summaryrefslogtreecommitdiff
path: root/source/compiler-core/slang-artifact.h
blob: 400c85b2eb9da5cc1b77febecdb0cdbb2303eb94 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
// slang-artifact.h
#ifndef SLANG_ARTIFACT_H
#define SLANG_ARTIFACT_H

#include "../core/slang-basic.h"
#include "slang-com-helper.h"

#include <type_traits>

namespace Slang
{

/* Simplest slice types. We can't use UnownedStringSlice etc, because they implement functionality in libraries,
and we want to use these types in headers.
If we wanted a C implementation it would be easy to use a macro to generate the functionality */

template <typename T>
struct Slice
{
    const T* begin() const { return data; }
    const T* end() const { return data + count; }

    const T& operator[](Index index) const { SLANG_ASSERT(index >= 0 && index < count); return data[index]; }

    Slice() :count(0), data(nullptr) {}
    Slice(const T* inData, Count inCount) :
        data(inData),
        count(inCount)
    {}

    const T* data;
    Count count;
};

template <typename T>
SLANG_FORCE_INLINE Slice<T> makeSlice(const T* inData, Count inCount) 
{ 
    return Slice<T>(inData, inCount); 
}

struct CharSlice : public Slice<char>
{
    typedef CharSlice ThisType;
    typedef Slice<char> Super;

    bool operator==(const ThisType& rhs) const { return count == rhs.count && (data == rhs.data || ::memcmp(data, rhs.data, count) == 0); }
    bool operator!=(const ThisType& rhs) const { return !(*this == rhs); }

    explicit CharSlice(const char* in) :Super(in, ::strlen(in)) {}
    CharSlice(const char* in, Count inCount) :Super(in, inCount) {}
    CharSlice() :Super(nullptr, 0) {}
    explicit CharSlice(const String& s) :CharSlice(s.begin(), s.getLength()){};
};
static_assert(std::is_trivially_copyable_v<CharSlice>);

struct TerminatedCharSlice : public CharSlice
{
    typedef TerminatedCharSlice ThisType;
    typedef CharSlice Super;

    SLANG_FORCE_INLINE bool operator==(const ThisType& rhs) const { return Super::operator==(rhs); }
    SLANG_FORCE_INLINE bool operator!=(const ThisType& rhs) const { return !(*this == rhs); }

        /// Make convertable to char*
    SLANG_FORCE_INLINE operator const char* () const { return data; }

    explicit TerminatedCharSlice(const char* in) :Super(in) {}
    TerminatedCharSlice(const char* in, Count inCount) :Super(in, inCount) { SLANG_ASSERT(in[inCount] == 0); }
    TerminatedCharSlice() :Super("", 0) {}
};
static_assert(std::is_trivially_copyable_v<TerminatedCharSlice>);

/* As a rule of thumb, if we can define some aspect in a hierarchy then we should do so at the highest level. 
If some aspect can apply to multiple items identically we move that to a separate enum. 

NOTE!
New Kinds must be added at the end. Values can be deprecated, or disabled
but never removed, without breaking binary compatability.

Any change requires a change to SLANG_ARTIFACT_KIND
*/
enum class ArtifactKind : uint8_t
{ 
    Invalid,                    ///< Invalid
    Base,                       ///< Base kind of all valid kinds

    None,                       ///< Doesn't contain anything
    Unknown,                    ///< Unknown

    BinaryFormat,               ///< A generic binary format. 

    Container,                  ///< Container like types
    Zip,                        ///< Zip container
    RiffContainer,              ///< Riff container
    RiffLz4Container,           ///< Riff container using Lz4 compression
    RiffDeflateContainer,       ///< Riff container using deflate compression

    Text,                       ///< Representation is text. Encoding is utf8, unless prefixed with 'encoding'.
    
    Source,                     ///< Source (Source type is in payload)
    Assembly,                   ///< Assembly (Type is in payload)
    HumanText,                  ///< Text for human consumption

    CompileBinary,              ///< Kinds which are 'binary like' - can be executed, linked with and so forth. 
    
    ObjectCode,                 ///< Object file
    Library,                    ///< Library (collection of object code)
    Executable,                 ///< Executable
    SharedLibrary,              ///< Shared library - can be dynamically linked
    HostCallable,               ///< Code can be executed directly on the host

    Instance,                   ///< Primary representation is an interface/class instance 
    
    Json,                       ///< It's JSON

    CountOf,
};

/* Payload. 

SlangIR and LLVMIR can be GPU or CPU orientated, so put in own category.

NOTE!
New Payloads must be added at the end. Values can be deprecated, or disabled
but never removed, without breaking binary compatability.

Any change requires a change to SLANG_ARTIFACT_PAYLOAD
*/
enum class ArtifactPayload : uint8_t
{
    Invalid,        ///< Is invalid - indicates some kind of problem
    Base,           ///< The base of the hierarchy

    None,           ///< Doesn't have a payload
    Unknown,        ///< Unknown but probably valid
    
    Source,         ///< Source code
    
    C,              ///< C source
    Cpp,            ///< C++ source
    HLSL,           ///< HLSL source
    GLSL,           ///< GLSL source
    CUDA,           ///< CUDA source
    Metal,          ///< Metal source
    Slang,          ///< Slang source

    KernelLike,     ///< GPU Kernel like

    DXIL,           ///< DXIL 
    DXBC,           ///< DXBC
    SPIRV,          ///< SPIR-V
    PTX,            ///< PTX. NOTE! PTX is a text format, but is handable to CUDA API.
    MetalAIR,       ///< Metal AIR 
    CuBin,          ///< CUDA binary

    CPULike,        ///< CPU code
    
    UnknownCPU,     ///< CPU code for unknown/undetermined type
    X86,            ///< X86
    X86_64,         ///< X86_64
    Aarch,          ///< 32 bit arm
    Aarch64,        ///< Aarch64
    HostCPU,        ///< HostCPU
    UniversalCPU,   ///< CPU code for multiple CPU types 

    GeneralIR,      ///< General purpose IR representation (IR)

    SlangIR,        ///< Slang IR
    LLVMIR,         ///< LLVM IR

    AST,            ///< Abstract syntax tree (AST)

    SlangAST,       ///< Slang AST

    CompileResults, ///< Payload is a collection of compilation results

    Metadata,       ///< Metadata

    DebugInfo,      ///< Debugging information
    Diagnostics,    ///< Diagnostics information

    Miscellaneous,  ///< Category for miscellaneous payloads (like Log/Lock)

    Log,            ///< Log file
    Lock,           ///< Typically some kind of 'lock' file. Contents is typically not important.

    PdbDebugInfo,   ///< PDB debug info

    SourceMap,      ///< A source map

    PostEmitMetadata,   ///< Metadata from post emit (binding information)

    CountOf,
};

/* Style.

NOTE!
New Styles must be added at the end. Values can be deprecated, or disabled
but never removed, without breaking binary compatability.

Any change requires a change to SLANG_ARTIFACT_STYLE
*/
enum class ArtifactStyle : uint8_t
{
    Invalid,            ///< Invalid style (indicating an error)
    Base,
        
    None,               ///< A style is not applicable

    Unknown,            ///< Unknown

    CodeLike,           ///< For styles that are 'code like' such as 'kernel' or 'host'.

    Kernel,             ///< Compiled as `GPU kernel` style.        
    Host,               ///< Compiled in `host` style
    Obfuscated,         ///< Holds something specific to obfuscation, such as an obfuscated source map

    CountOf,
};

typedef uint8_t ArtifactFlags;
struct ArtifactFlag
{
    enum Enum : ArtifactFlags
    {
        // Don't currently have any flags
    };
};

/**
A value type to describe aspects of the contents of an Artifact.
**/
struct ArtifactDesc
{
public:
    typedef ArtifactDesc ThisType;

    typedef ArtifactKind Kind;
    typedef ArtifactPayload Payload;
    typedef ArtifactStyle Style;
    typedef ArtifactFlags Flags;
   
    typedef uint32_t PackedBacking;
    enum class Packed : PackedBacking;
    
        /// Get in packed format
    inline Packed getPacked() const;

    bool operator==(const ThisType& rhs) const { return kind == rhs.kind && payload == rhs.payload && style == rhs.style && flags == rhs.flags;  }
    bool operator!=(const ThisType& rhs) const { return !(*this == rhs); }

        /// Construct from the elements
    static ThisType make(Kind inKind, Payload inPayload, Style inStyle = Style::Unknown, Flags flags = 0) { return ThisType{ inKind, inPayload, inStyle, flags }; }
    static ThisType make(Kind inKind, Payload inPayload, const ThisType& base) { return ThisType{ inKind, inPayload, base.style, base.flags }; }

        /// Construct from the packed format
    inline static ThisType make(Packed inPacked);

    Kind kind;
    Payload payload;
    Style style;
    Flags flags;
};

// --------------------------------------------------------------------------
inline ArtifactDesc::Packed ArtifactDesc::getPacked() const
{
    typedef PackedBacking IntType;
    return Packed((IntType(kind) << 24) |
        (IntType(payload) << 16) |
        (IntType(style) << 8) |
        flags);
}

// --------------------------------------------------------------------------
inline /* static */ArtifactDesc ArtifactDesc::make(Packed inPacked)
{
    const PackedBacking packed = PackedBacking(inPacked);

    ThisType r;
    r.kind = Kind(packed >> 24);
    r.payload = Payload(uint8_t(packed >> 16));
    r.style = Style(uint8_t(packed >> 8));
    r.flags = uint8_t(packed);

    return r;
}

// Forward declare
class IOSFileArtifactRepresentation;
class IPathArtifactRepresentation;

class IArtifactRepresentation;

// Controls what items can be kept. 
enum class ArtifactKeep
{
    No,         ///< Don't keep the item
    Yes,        ///< Yes keep the final item
    All,        ///< Keep the final item and any intermediataries
};

/// True if can keep an intermediate item
SLANG_INLINE  bool canKeepIntermediate(ArtifactKeep keep) { return keep == ArtifactKeep::All; }
/// True if can keep
SLANG_INLINE bool canKeep(ArtifactKeep keep) { return Index(keep) >= Index(ArtifactKeep::Yes); }
/// Returns the keep type for an intermediate
SLANG_INLINE ArtifactKeep getIntermediateKeep(ArtifactKeep keep) { return (keep == ArtifactKeep::All) ? ArtifactKeep::All : ArtifactKeep::No; }

/* Forward define */
class IArtifactHandler;

/* The IArtifact interface is designed to represent some Artifact of compilation. It could be input to or output from a compilation.

An abstraction is desirable here, because depending on the compiler the artifact/s could be

* A file on the file system
* A blob
* Multiple files
* Some other (perhaps multiple) in memory representations 
* A name 

The artifact uses the Blob as the canonical in memory representation. 

Some downstream compilers require the artifact to be available as a file system file, or to produce
artifacts that are files. The IArtifact type allows to abstract away this difference, including the
ability to turn an in memory representation into a temporary file on the file system. 

The mechanism also allows for 'Containers' which allow for Artifacts to contain other Artifacts (amongst other things).
Those artifacts may be other files. For example a downstream compilation that produces results as well as temporary
files could be a Container containing artifacts for

* Diagnostics
* Temporary files (of known and unknown types)
* Files that contain known types
* Callable interface (an ISlangSharedLibrary)

There are several types of ways to associate data with an artifact:

* A representation
* An associated artifact
* A child artifact

A `representation` has to wholly represent the artifact. That representation could be a blob, a file on the file system,
an in memory representation. There are two classes of `Representation` - ones that can be turned into blobs (and therefore 
derive from IArtifactRepresentation) and ones that are in of themselves a representation (such as a blob or or ISlangSharedLibrary).

`Associated artifacts` hold information that is associated with the artifact. It could be part 
of the representation, or useful for the implementation of a representation. Could also be considered as a kind of side channel
to associate arbitrary data including temporary data with an artifact.

A `child artifact` belongs to the artifact, within the hierarchy of artifacts.
*/
class IArtifact : public ICastable
{
public:
    SLANG_COM_INTERFACE(0xf90acdb0, 0x9a4a, 0x414e, { 0x85, 0x45, 0x8b, 0x26, 0xc9, 0x2d, 0x94, 0x42 })

    enum class ContainedKind
    {
        Representation,
        Associated,
        Children,
    };

    typedef ArtifactDesc Desc;

    typedef ArtifactKind Kind;
    typedef ArtifactPayload Payload;
    typedef ArtifactStyle Style;
    typedef ArtifactFlags Flags;

    typedef ArtifactKeep Keep;
    
        /// Get the Desc defining the contents of the artifact
    virtual SLANG_NO_THROW Desc SLANG_MCALL getDesc() = 0;

        /// Returns true if the artifact in principal exists
    virtual SLANG_NO_THROW bool SLANG_MCALL exists() = 0;

        /// Load as a blob
    virtual SLANG_NO_THROW SlangResult SLANG_MCALL loadBlob(Keep keep, ISlangBlob** outBlob) = 0;
    
        /// Require artifact is available as a file.
        /// NOTE! May need to serialize and write as a temporary file.
    virtual SLANG_NO_THROW SlangResult SLANG_MCALL requireFile(Keep keep, IOSFileArtifactRepresentation** outFileRep) = 0;

        /// Load the artifact as a shared library
    virtual SLANG_NO_THROW SlangResult SLANG_MCALL loadSharedLibrary(ArtifactKeep keep, ISlangSharedLibrary** outSharedLibrary) = 0;

        /// Get the name of the artifact. This can be empty.
    virtual SLANG_NO_THROW const char* SLANG_MCALL getName() = 0;
        /// Set the name associated with the artifact
    virtual SLANG_NO_THROW void SLANG_MCALL setName(const char* name) = 0;

        /// Add associated artifacts with this artifact
    virtual SLANG_NO_THROW void SLANG_MCALL addAssociated(IArtifact* artifact) = 0;
        /// Get the list of associated items
    virtual SLANG_NO_THROW Slice<IArtifact*> SLANG_MCALL getAssociated() = 0;
    
        /// Add a representation 
    virtual SLANG_NO_THROW void SLANG_MCALL addRepresentation(ICastable* castable) = 0;
        /// Add a representation that doesn't derive from IArtifactRepresentation
    virtual SLANG_NO_THROW void SLANG_MCALL addRepresentationUnknown(ISlangUnknown* rep) = 0;
        /// Get all the representations
    virtual SLANG_NO_THROW Slice<ICastable*> SLANG_MCALL getRepresentations() = 0;

        /// Given a typeGuid representing the desired type get or create the representation.
        /// If found outCastable holds an entity that *must* be castable to typeGuid
        /// Use the keep parameter to determine if the representation should be cached on the artifact/s or not.
    virtual SLANG_NO_THROW SlangResult SLANG_MCALL getOrCreateRepresentation(const Guid& typeGuid, ArtifactKeep keep, ICastable** outCastable) = 0;
    
        /// Get the handler used for this artifact. If nullptr means the default handler will be used.
    virtual SLANG_NO_THROW IArtifactHandler* SLANG_MCALL getHandler() = 0;
        /// Set the handler associated with this artifact. Setting nullptr will use the default handler.
    virtual SLANG_NO_THROW void SLANG_MCALL setHandler(IArtifactHandler* handler) = 0;

        /// Returns the result of expansion. Will return SLANG_E_UNINITIALIZED if expansion hasn't happened
    virtual SLANG_NO_THROW SlangResult SLANG_MCALL getExpandChildrenResult() = 0;
        /// Sets all of the children, will set the expansion state to SLANG_OK
    virtual SLANG_NO_THROW void SLANG_MCALL setChildren(IArtifact*const* children, Count count) = 0;
        /// Will be called implicitly on access to children
    virtual SLANG_NO_THROW SlangResult SLANG_MCALL expandChildren() = 0;

        /// Add the artifact to the list
    virtual SLANG_NO_THROW void SLANG_MCALL addChild(IArtifact* artifact) = 0;
        /// Get the children, will only remain valid if no mutation of children list
    virtual SLANG_NO_THROW Slice<IArtifact*> SLANG_MCALL getChildren() = 0;

        /// Find a represention from the specified list 
    virtual SLANG_NO_THROW void* SLANG_MCALL findRepresentation(ContainedKind kind, const Guid& guid) = 0;
        /// Clear all of the contained kind
    virtual SLANG_NO_THROW void SLANG_MCALL clear(ContainedKind kind) = 0;
        /// Remove entry at index for the specified kind
    virtual SLANG_NO_THROW void SLANG_MCALL removeAt(ContainedKind kind, Index i) = 0;
};

template <typename T>
SLANG_FORCE_INLINE T* findRepresentation(IArtifact* artifact)
{
    return reinterpret_cast<T*>(artifact->findRepresentation(IArtifact::ContainedKind::Representation, T::getTypeGuid()));
}

template <typename T>
SLANG_FORCE_INLINE T* findAssociatedRepresentation(IArtifact* artifact)
{
    return reinterpret_cast<T*>(artifact->findRepresentation(IArtifact::ContainedKind::Associated, T::getTypeGuid()));
}

template <typename T>
SLANG_FORCE_INLINE T* findChildRepresentation(IArtifact* artifact)
{
    return reinterpret_cast<T*>(artifact->findRepresentation(IArtifact::ContainedKind::Children, T::getTypeGuid()));
}

/* The IArtifactRepresentation interface represents a single representation that can be part of an artifact. It's special in so far
as

* IArtifactRepresentation can be queried for it's underlying object class
* Can determine if the representation exists (for example if it's on the file system)
* Can optionally serialize into a blob
*/
class IArtifactRepresentation : public ICastable
{
    SLANG_COM_INTERFACE(0xa3790eb, 0x22b9, 0x430e, { 0xbf, 0xc6, 0x24, 0x6c, 0x5b, 0x5c, 0xcd, 0x0 })

        /// Create a representation of the specified typeGuid interface. 
        /// Calling castAs on the castable will return the specific type
        /// Returns SLANG_E_NOT_IMPLEMENTED if an implementation doesn't implement
    virtual SLANG_NO_THROW SlangResult SLANG_MCALL createRepresentation(const Guid& typeGuid, ICastable** outCastable) = 0;

        /// Returns true if this representation exists and is available for use.
    virtual SLANG_NO_THROW bool SLANG_MCALL exists() = 0;
};

/* Handler provides functionality external to the artifact */
class IArtifactHandler : public ICastable
{
    SLANG_COM_INTERFACE(0x6a646f57, 0xb3ac, 0x4c6a, { 0xb6, 0xf1, 0x33, 0xb6, 0xef, 0x60, 0xa6, 0xae });

        /// Given an artifact expands children
    virtual SLANG_NO_THROW SlangResult SLANG_MCALL expandChildren(IArtifact* container) = 0;
        /// Given an artifact gets or creates a representation. 
    virtual SLANG_NO_THROW SlangResult SLANG_MCALL getOrCreateRepresentation(IArtifact* artifact, const Guid& guid, ArtifactKeep keep, ICastable** outCastable) = 0;
};

} // namespace Slang

#endif