// slang-artifact.h #ifndef SLANG_ARTIFACT_H #define SLANG_ARTIFACT_H #include "../core/slang-basic.h" #include "slang-com-helper.h" #include 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 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 SLANG_FORCE_INLINE Slice makeSlice(const T* inData, Count inCount) { return Slice(inData, inCount); } struct CharSlice : public Slice { typedef CharSlice ThisType; typedef Slice 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); 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); /* 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 WGSL, ///< WGSL 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 WGSL_SPIRV, ///< SPIR-V derived via WebGPU shading language 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. This also uses the ICompileResult interface to more easily allow the Slang API to retrieve multiple associated artifacts in cases where both base and debug spirv are needed. */ class IArtifact : public slang::ICompileResult { 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 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 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 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 SLANG_FORCE_INLINE T* findRepresentation(IArtifact* artifact) { return reinterpret_cast( artifact->findRepresentation(IArtifact::ContainedKind::Representation, T::getTypeGuid())); } template SLANG_FORCE_INLINE T* findAssociatedRepresentation(IArtifact* artifact) { return reinterpret_cast( artifact->findRepresentation(IArtifact::ContainedKind::Associated, T::getTypeGuid())); } template SLANG_FORCE_INLINE T* findChildRepresentation(IArtifact* artifact) { return reinterpret_cast( 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