summaryrefslogtreecommitdiffstats
path: root/source/compiler-core
diff options
context:
space:
mode:
authorjsmall-nvidia <jsmall@nvidia.com>2022-04-26 12:09:32 -0400
committerGitHub <noreply@github.com>2022-04-26 12:09:32 -0400
commit79dd12c21e8f5c5ce01051a280679cf6ac8ffe97 (patch)
tree705df0ab9047e4418a6218993cd0ddb2a46dffbc /source/compiler-core
parent66ad0072821b58318c6dc5d2d64c966e312951dd (diff)
Linking in DXC (#2190)
* #include an absolute path didn't work - because paths were taken to always be relative. * Compile to a dxil library. * Added CompileProduct. * Support handling of ModuleLibrary. * CacheBehavior -> Cache * Use CompileProduct for -r references. * CompileProduct -> Artifact. * Determining an artifact type on binding. * Determine binary linkability. * Added Artifact::exists. * Added ArtifactKeep. * Small fixes. * Small improvements to Artifact. * Add zip extension. * Fix some comments. * Fix multiple adding of PublicDecoration. Make public output export for DXIL/lib. Add checking for simpleDecorations such that only added once. * Use 'whole program' to identify library build. * Move slang-artifact into compiler-core. * Split out Keep free functions. * Artifact::Keep -> ArtifactKeep. * Handle libraries as artifacts. * Add -target dxil so test infrastructure knows it needs DXC. * Linking working in DXC. * Improve handling around emit for 'export'. * Add comment around Artifact name. * Render test working with linking. Co-authored-by: Theresa Foley <10618364+tangent-vector@users.noreply.github.com>
Diffstat (limited to 'source/compiler-core')
-rw-r--r--source/compiler-core/slang-artifact.cpp459
-rw-r--r--source/compiler-core/slang-artifact.h366
-rw-r--r--source/compiler-core/slang-downstream-compiler.h4
-rw-r--r--source/compiler-core/slang-dxc-compiler.cpp236
-rw-r--r--source/compiler-core/slang-gcc-compiler-util.cpp51
-rw-r--r--source/compiler-core/slang-visual-studio-compiler-util.cpp14
6 files changed, 1093 insertions, 37 deletions
diff --git a/source/compiler-core/slang-artifact.cpp b/source/compiler-core/slang-artifact.cpp
new file mode 100644
index 000000000..5d9f8692c
--- /dev/null
+++ b/source/compiler-core/slang-artifact.cpp
@@ -0,0 +1,459 @@
+// slang-artifact.cpp
+#include "slang-artifact.h"
+
+#include "../core/slang-type-text-util.h"
+#include "../core/slang-io.h"
+
+namespace Slang {
+
+namespace { // anonymous
+struct KindExtension
+{
+ ArtifactKind kind;
+ UnownedStringSlice ext;
+};
+} // anonymous
+
+#define SLANG_KIND_EXTENSION(kind, ext) \
+ { ArtifactKind::kind, UnownedStringSlice::fromLiteral(ext) },
+
+static const KindExtension g_cpuKindExts[] =
+{
+#if SLANG_WINDOWS_FAMILY
+ SLANG_KIND_EXTENSION(Library, "lib")
+ SLANG_KIND_EXTENSION(ObjectCode, "obj")
+ SLANG_KIND_EXTENSION(Executable, "exe")
+ SLANG_KIND_EXTENSION(SharedLibrary, "dll")
+#else
+ SLANG_KIND_EXTENSION(Library, "a")
+ SLANG_KIND_EXTENSION(ObjectCode, "o")
+ SLANG_KIND_EXTENSION(Executable, "")
+
+#if __CYGWIN__
+ SLANG_KIND_EXTENSION(SharedLibrary, "dll")
+#elif SLANG_APPLE_FAMILY
+ SLANG_KIND_EXTENSION(SharedLibrary, "dylib")
+#else
+ SLANG_KIND_EXTENSION(SharedLibrary, "so")
+#endif
+
+#endif
+};
+
+/* static */ArtifactDesc ArtifactDesc::fromPath(const UnownedStringSlice& slice)
+{
+ auto extension = Path::getPathExt(slice);
+ return fromExtension(extension);
+}
+
+/* static */ ArtifactDesc ArtifactDesc::fromExtension(const UnownedStringSlice& slice)
+{
+ if (slice == "slang-module" ||
+ slice == "slang-lib")
+ {
+ return make(ArtifactKind::Library, ArtifactPayload::SlangIR, ArtifactStyle::Unknown);
+ }
+
+ for (const auto& kindExt : g_cpuKindExts)
+ {
+ if (slice == kindExt.ext)
+ {
+ // We'll assume it's for the host CPU for now..
+ return make(kindExt.kind, Payload::HostCPU, Style::Unknown);
+ }
+ }
+
+ const auto target = TypeTextUtil::findCompileTargetFromExtension(slice);
+
+ return makeFromCompileTarget(target);
+}
+
+/* static */ArtifactDesc ArtifactDesc::makeFromCompileTarget(SlangCompileTarget target)
+{
+ switch (target)
+ {
+ case SLANG_TARGET_UNKNOWN: return make(Kind::Unknown, Payload::None, Style::Unknown, 0);
+ case SLANG_TARGET_NONE: return make(Kind::None, Payload::None, Style::Unknown, 0);
+ case SLANG_GLSL_VULKAN:
+ case SLANG_GLSL_VULKAN_ONE_DESC:
+ case SLANG_GLSL:
+ {
+ // For the moment we make all just map to GLSL, but we could use flags
+ // or some other mechanism to distinguish the types
+ return make(Kind::Text, Payload::GLSL, Style::Kernel, 0);
+ }
+ case SLANG_HLSL: return make(Kind::Text, Payload::HLSL, Style::Kernel, 0);
+ case SLANG_SPIRV: return make(Kind::Executable, Payload::SPIRV, Style::Kernel, 0);
+ case SLANG_SPIRV_ASM: return make(Kind::Text, Payload::SPIRVAssembly, Style::Kernel, 0);
+ case SLANG_DXBC: return make(Kind::Executable, Payload::DXBC, Style::Kernel, 0);
+ case SLANG_DXBC_ASM: return make(Kind::Text, Payload::DXBCAssembly, Style::Kernel, 0);
+ case SLANG_DXIL: return make(Kind::Executable, Payload::DXIL, Style::Kernel, 0);
+ case SLANG_DXIL_ASM: return make(Kind::Text, Payload::DXILAssembly, Style::Kernel, 0);
+ case SLANG_C_SOURCE: return make(Kind::Text, Payload::C, Style::Kernel, 0);
+ case SLANG_CPP_SOURCE: return make(Kind::Text, Payload::CPP, Style::Kernel, 0);
+ case SLANG_HOST_CPP_SOURCE: return make(Kind::Text, Payload::CPP, Style::Host, 0);
+ case SLANG_HOST_EXECUTABLE: return make(Kind::Executable, Payload::HostCPU, Style::Host, 0);
+ case SLANG_SHADER_SHARED_LIBRARY: return make(Kind::SharedLibrary, Payload::HostCPU, Style::Kernel, 0);
+ case SLANG_SHADER_HOST_CALLABLE: return make(Kind::Callable, Payload::HostCPU, Style::Kernel, 0);
+ case SLANG_CUDA_SOURCE: return make(Kind::Text, Payload::CUDA, Style::Kernel, 0);
+ case SLANG_PTX: return make(Kind::Executable, Payload::PTX, Style::Kernel, 0);
+ case SLANG_OBJECT_CODE: return make(Kind::ObjectCode, Payload::HostCPU, Style::Kernel, 0);
+ default: break;
+ }
+
+ SLANG_UNEXPECTED("Unhandled type");
+}
+
+/* static */bool ArtifactDesc::isPayloadGpuBinary(Payload payloadType)
+{
+ switch (payloadType)
+ {
+ case Payload::DXIL:
+ case Payload::DXBC:
+ case Payload::SPIRV:
+ case Payload::PTX:
+ {
+ return true;
+ }
+ default: break;
+ }
+ return false;
+}
+
+/* static */bool ArtifactDesc::isPayloadCpuBinary(Payload payloadType)
+{
+ switch (payloadType)
+ {
+ case Payload::X86:
+ case Payload::X86_64:
+ case Payload::AARCH:
+ case Payload::AARCH64:
+ case Payload::HostCPU:
+ {
+ return true;
+ }
+ default: break;
+ }
+ return false;
+}
+
+/* static */bool ArtifactDesc::isPayloadGpuBinaryLinkable(Payload payload)
+{
+ switch (payload)
+ {
+ case Payload::DXBC:
+ {
+ // It seems as if DXBC is potentially linkable from
+ // https://docs.microsoft.com/en-us/windows/win32/direct3dhlsl/dx-graphics-hlsl-appendix-keywords#export
+ return true;
+ }
+
+ case Payload::DXIL:
+ case Payload::PTX:
+ case Payload::SPIRV:
+ {
+ // We can't *actually* link PTX or SPIR-V currently but it is in principal possible
+ // so let's say we accept for now
+ return true;
+ }
+ default: break;
+ }
+ return false;
+}
+
+bool ArtifactDesc::isBinaryLinkable() const
+{
+ if (isKindBinaryLinkable(kind))
+ {
+ return isPayloadCpuBinary(payload) || isPayloadGpuBinaryLinkable(payload) || payload == ArtifactPayload::SlangIR;
+ }
+
+ return false;
+}
+
+/* static */bool ArtifactDesc::isKindBinaryLinkable(Kind kind)
+{
+ switch (kind)
+ {
+ case Kind::Library:
+ case Kind::ObjectCode:
+ {
+ return true;
+ }
+ default: break;
+ }
+ return false;
+}
+
+/* static*/ UnownedStringSlice ArtifactDesc::getCpuExtensionForKind(Kind kind)
+{
+ for (const auto& kindExt : g_cpuKindExts)
+ {
+ if (kind == kindExt.kind)
+ {
+ return kindExt.ext;
+ }
+ }
+ return UnownedStringSlice();
+}
+
+UnownedStringSlice ArtifactDesc::getDefaultExtension()
+{
+ if (isPayloadCpuBinary(payload))
+ {
+ return getCpuExtensionForKind(kind);
+ }
+ else
+ {
+ return getDefaultExtensionForPayload(payload);
+ }
+}
+
+/* static */UnownedStringSlice ArtifactDesc::getDefaultExtensionForPayload(Payload payload)
+{
+ switch (payload)
+ {
+ case Payload::None: return UnownedStringSlice();
+ case Payload::Unknown: return UnownedStringSlice::fromLiteral("unknown");
+
+ case Payload::DXIL: return UnownedStringSlice::fromLiteral("dxil");
+ case Payload::DXBC: return UnownedStringSlice::fromLiteral("dxbc");
+ case Payload::SPIRV: return UnownedStringSlice::fromLiteral("spirv");
+
+ case Payload::PTX: return UnownedStringSlice::fromLiteral("ptx");
+
+ case Payload::X86:
+ case Payload::X86_64:
+ case Payload::AARCH:
+ case Payload::AARCH64:
+ case Payload::HostCPU:
+ {
+ return UnownedStringSlice();
+ }
+
+ case Payload::SlangIR: return UnownedStringSlice::fromLiteral("slang-ir");
+ case Payload::LLVMIR: return UnownedStringSlice::fromLiteral("llvm-ir");
+
+ case Payload::HLSL: return UnownedStringSlice::fromLiteral("hlsl");
+ case Payload::GLSL: return UnownedStringSlice::fromLiteral("glsl");
+
+ case Payload::CPP: return UnownedStringSlice::fromLiteral("cpp");
+ case Payload::C: return UnownedStringSlice::fromLiteral("c");
+
+ case Payload::CUDA: return UnownedStringSlice::fromLiteral("cu");
+
+ case Payload::Slang: return UnownedStringSlice::fromLiteral("slang");
+
+ case Payload::Zip: return UnownedStringSlice::fromLiteral("zip");
+
+ default: break;
+ }
+
+ SLANG_UNEXPECTED("Unknown content type");
+}
+
+/* !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! Artifact !!!!!!!!!!!!!!!!!!!!!!!!!!! */
+
+Artifact::~Artifact()
+{
+ if (m_pathType == PathType::Temporary)
+ {
+ File::remove(m_path);
+ }
+
+ for (const auto& entry : m_entries)
+ {
+ // Release the associated data
+ switch (entry.type)
+ {
+ case Entry::Type::ObjectInstance:
+ {
+ entry.object->releaseReference();
+ break;
+ }
+ case Entry::Type::InterfaceInstance:
+ {
+ entry.intf->release();
+ break;
+ }
+ default: break;
+ }
+ }
+}
+
+Index Artifact::indexOf(Entry::Type type) const
+{
+ return m_entries.findFirstIndex([&](const Entry& entry) -> bool { return entry.type == type; });
+}
+
+void Artifact::add(Entry::Style style, RefObject* obj)
+{
+ SLANG_ASSERT(obj);
+
+ Entry entry;
+ entry.type = Entry::Type::ObjectInstance;
+ entry.style = style;
+ entry.object = obj;
+
+ obj->addReference();
+
+ m_entries.add(entry);
+}
+
+void Artifact::add(Entry::Style style, ISlangUnknown* intf)
+{
+ // Can't be nullptr
+ SLANG_ASSERT(intf);
+
+ Entry entry;
+ entry.type = Entry::Type::InterfaceInstance;
+ entry.style = style;
+
+ intf->addRef();
+
+ entry.intf = intf;
+ m_entries.add(entry);
+}
+
+bool Artifact::exists() const
+{
+ // If we have a blob it exists
+ if (m_blob)
+ {
+ return true;
+ }
+
+ // If we have an associated entry that represents the artifact it exists
+ for (const auto& entry : m_entries)
+ {
+ if (entry.style == Entry::Style::Artifact)
+ {
+ // There is a representation that 'is' the artifact
+ return true;
+ }
+ }
+
+ // If we don't have a path then it can't exist
+ if (m_pathType == PathType::None)
+ {
+ return false;
+ }
+
+ // If the file exists we assume it exists
+ return File::exists(m_path);
+}
+
+ISlangUnknown* Artifact::findInterfaceInstance(const Guid& guid)
+{
+ for (const auto& entry : m_entries)
+ {
+ if (entry.type == Entry::Type::InterfaceInstance)
+ {
+ ISlangUnknown* intf = nullptr;
+ if (SLANG_SUCCEEDED(entry.intf->queryInterface(guid, (void**)&intf)) && intf)
+ {
+ // NOTE! This assumes we *DONT* need to ref count to keep an interface in scope
+ // (as strict COM requires so as to allow on demand interfaces).
+ intf->release();
+
+ return intf;
+ }
+ }
+ }
+
+ return nullptr;
+}
+
+SlangResult Artifact::requireFilePath(Keep keep, String& outFilePath)
+{
+ if (m_pathType != PathType::None)
+ {
+ outFilePath = m_path;
+ return SLANG_OK;
+ }
+
+ ComPtr<ISlangBlob> blob;
+
+ // Get the contents as a blob. If we can't do that, then we can't write anything...
+ SLANG_RETURN_ON_FAIL(loadBlob(getIntermediateKeep(keep), blob));
+
+ const UnownedStringSlice ext = m_desc.getDefaultExtension();
+
+ // TODO(JS): NOTE! This isn't strictly correct, as the generated filename is not guarenteed to be unique
+ // if we change it with an extension (or prefix).
+ // This doesn't change the previous behavior though.
+ String path;
+ SLANG_RETURN_ON_FAIL(File::generateTemporary(UnownedStringSlice::fromLiteral("slang-generated"), path));
+
+ if (m_desc.isCpuBinary() && m_desc.kind == ArtifactKind::SharedLibrary)
+ {
+ const bool isSharedLibraryPrefixPlatform = SLANG_LINUX_FAMILY || SLANG_APPLE_FAMILY;
+ if (isSharedLibraryPrefixPlatform)
+ {
+ StringBuilder buf;
+ buf << "lib";
+ buf << Path::getFileName(path);
+
+ auto parentDir = Path::getParentDirectory(path);
+ if (parentDir.getLength())
+ {
+ // Combine the name with path if their is a parent
+ path = Path::combine(parentDir, buf);
+ }
+ else
+ {
+ // Just use the name as is
+ path = buf;
+ }
+ }
+ }
+
+ // If there is an extension append it
+ if (ext.getLength())
+ {
+ path.appendChar('.');
+ path.append(ext);
+ }
+
+ SLANG_RETURN_ON_FAIL(File::writeAllBytes(path, blob->getBufferPointer(), blob->getBufferSize()));
+
+ // Okay we can now add this as temporary path too
+ setPath(PathType::Temporary, path);
+
+ return SLANG_OK;
+}
+
+SlangResult Artifact::loadBlob(Keep keep, ComPtr<ISlangBlob>& outBlob)
+{
+ if (m_blob)
+ {
+ outBlob = m_blob;
+ return SLANG_OK;
+ }
+
+ // TODO(JS):
+ // Strictly speaking we could *potentially* convert some other representation into
+ // a blob by serializing it, but we don't worry about any of that here
+ if (m_pathType == PathType::None)
+ {
+ return SLANG_E_NOT_FOUND;
+ }
+
+ // Read into a blob
+ ScopedAllocation alloc;
+ SLANG_RETURN_ON_FAIL(File::readAllBytes(m_path, alloc));
+
+ // Create as a blob
+ RefPtr<RawBlob> blob = RawBlob::moveCreate(alloc);
+
+ // Put in cache
+ if (canKeep(keep))
+ {
+ setBlob(blob);
+ }
+
+ outBlob = blob;
+ return SLANG_OK;
+}
+
+} // namespace Slang
diff --git a/source/compiler-core/slang-artifact.h b/source/compiler-core/slang-artifact.h
new file mode 100644
index 000000000..232e9a8eb
--- /dev/null
+++ b/source/compiler-core/slang-artifact.h
@@ -0,0 +1,366 @@
+// slang-artifact.h
+#ifndef SLANG_ARTIFACT_H
+#define SLANG_ARTIFACT_H
+
+#include "../core/slang-basic.h"
+
+#include "../../slang-com-helper.h"
+#include "../../slang-com-ptr.h"
+
+namespace Slang
+{
+
+enum class ArtifactKind : uint8_t
+{
+ None, ///< There is no container
+
+ Unknown, ///< There is a container of unknown type
+
+ Library, ///< Library of object code (typically made up multiple ObjectCode)
+ ObjectCode, ///< Object code (for CPU typically .o or .obj file types)
+
+ Executable, ///< Self contained such it can exectuted. On GPU this would be a kernel.
+ SharedLibrary, ///< Shared library/dll
+ Callable, ///< Callable directly (typically means there isn't a binary artifact)
+
+ Text, ///< Text
+
+ Container, ///< A container holding other things
+
+ CountOf,
+};
+
+enum class ArtifactPayload : uint8_t
+{
+ None, ///< There is no payload
+
+ Unknown, ///< Has payload but its unknown variety
+
+ DXIL,
+ DXBC,
+ SPIRV,
+ PTX,
+
+ DXILAssembly,
+ DXBCAssembly,
+ SPIRVAssembly,
+ PTXAssembly,
+
+ HostCPU, ///< The host CPU architecture
+
+ SlangIR, ///< Slang IR
+ LLVMIR, ///< LLVM IR
+
+ SlangAST, ///< Slang AST
+
+ X86,
+ X86_64,
+ AARCH,
+ AARCH64,
+
+ HLSL, ///< HLSL
+ GLSL, ///< GLSL
+ CPP, ///< C++
+ C, ///< C Language
+ CUDA, ///< CUDA
+ Slang, ///< Slang
+
+ DebugInfo, ///< Debug information
+
+ Zip, ///< It's a zip
+
+ CountOf,
+};
+
+enum class ArtifactStyle : uint8_t
+{
+ Unknown, ///< Unknown
+
+ Kernel, ///< Compiled as `GPU kernel` style.
+ Host, ///< Compiled in `host` style
+
+ CountOf,
+};
+
+typedef uint8_t ArtifactFlags;
+struct ArtifactFlag
+{
+ enum Enum : ArtifactFlags
+ {
+ // Don't currently have any flags
+ };
+};
+
+// 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
+};
+
+
+/**
+A value type to describe aspects of the contents of an Artifact.
+**/
+struct ArtifactDesc
+{
+public:
+ typedef ArtifactDesc This;
+
+ 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;
+
+ /// True if the container appears to be binary linkable
+ bool isBinaryLinkable() const;
+ /// True if is a CPU binary
+ bool isCpuBinary() const { return isPayloadCpuBinary(payload); }
+ /// True if is a GPU binary
+ bool isGpuBinary() const { return isPayloadGpuBinary(payload); }
+
+ /// Gets the default file extension for the artifact type. Returns empty slice if not known
+ UnownedStringSlice getDefaultExtension();
+
+ static UnownedStringSlice getDefaultExtensionForPayload(Payload payload);
+
+ /// Get the extension for CPU/Host for a kind
+ static UnownedStringSlice getCpuExtensionForKind(Kind kind);
+
+ /// Returns true if the kind is binary linkable
+ static bool isKindBinaryLinkable(Kind kind);
+
+ /// Returns true if the payload type is CPU
+ static bool isPayloadCpuBinary(Payload payload);
+ /// Returns true if the payload type is applicable to the GPU
+ static bool isPayloadGpuBinary(Payload payload);
+
+ /// True if the payload type is in principal binary linkable
+ static bool isPayloadGpuBinaryLinkable(Payload payload);
+
+ /// Try to determine the desc from a path
+ static This fromPath(const UnownedStringSlice& slice);
+ /// Try to determine the desc from just a file extension (passed without .)
+ static This fromExtension(const UnownedStringSlice& slice);
+
+ bool operator==(const This& rhs) const { return kind == rhs.kind && payload == rhs.payload && style == rhs.style && flags == rhs.flags; }
+ bool operator!=(const This& rhs) const { return !(*this == rhs); }
+
+ /// Given a code gen target, get the equivalent ArtifactDesc
+ static This makeFromCompileTarget(SlangCompileTarget target);
+
+ /// Construct from the elements
+ static This make(Kind inKind, Payload inPayload, Style inStyle = Style::Kernel, Flags flags = 0)
+ {
+ return This{ inKind, inPayload, inStyle, flags };
+ }
+ /// Construct from the packed format
+ inline static This 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);
+
+ This 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;
+}
+
+/* The Artifact type is a type 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
+
+The artifact uses the Blob as the standard representation of in memory data.
+
+Some downstream compilers require the artifact to be available as a file system file, or to produce
+artifacts that are files. The Artifact type allows to abstract away this difference, including the
+ability to turn an in memory representation into a temporary file on the system file.
+
+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)
+
+A more long term goal would be to
+
+* Make Artifact an interface (such that it can work long term over binary boundaries)
+* Make Diagnostics into an interface (such it can be added to a Artifact result)
+* Use Artifact and related types for downstream compiler
+
+TODO(JS): There is an issue here around libraries in that downstream compilers can use
+named libraries, but the name doesn't directly relate to a file. If it is a file it may
+not be easily possible to determine it's location. So there is a desire to indicate the
+`name` as opposed to the path.
+
+As a second related issue. Lets say we have a blob (and not a file).
+
+*/
+class Artifact : public RefObject
+{
+public:
+
+ typedef ArtifactDesc Desc;
+
+ typedef ArtifactKind Kind;
+ typedef ArtifactPayload Payload;
+ typedef ArtifactStyle Style;
+ typedef ArtifactFlags Flags;
+ typedef ArtifactKeep Keep;
+
+ enum PathType
+ {
+ None,
+ Temporary,
+ Existing,
+ };
+
+ /* A compile product can be made up of multiple representations.
+ */
+ struct Entry
+ {
+ /// NOTE! Only interface innstances work across dll/shared library boundaries
+ /// because casting other types does not work across those boundaries.
+
+ // The Type of the entry
+ enum class Type : uint8_t
+ {
+ InterfaceInstance, ///< An interface instance
+ ObjectInstance, ///< An object instance
+ RawInstance,
+ };
+ enum class Style : uint8_t
+ {
+ Artifact, ///< Means this entry *can* represent the whole artifact
+ Child, ///< Some part of the artifact
+ Info, ///< Informational
+ Other, ///< Other
+ };
+
+ Type type;
+ Style style;
+ union
+ {
+ RefObject* object;
+ ISlangUnknown* intf;
+ void* raw;
+ };
+ };
+
+ /// Given a type T find the associated instance
+ template <typename T>
+ T* findObjectInstance();
+
+ /// Finds an instance of that has the guid.
+ ISlangUnknown* findInterfaceInstance(const Guid& guid);
+
+ /// Returns true if the artifact in principal exists (it could be invalid)
+ bool exists() const;
+
+ /// Load as a blob
+ SlangResult loadBlob(Keep keep, ComPtr<ISlangBlob>& outBlob);
+
+ /// Get as a file. May need to serialize and write as a temporary file.
+ SlangResult requireFilePath(Keep keep, String& outPath);
+
+ SLANG_FORCE_INLINE const Desc& getDesc() { return m_desc; }
+
+ /// Returns the index of the entry
+ Index indexOf(Entry::Type type) const;
+
+ /// Add items
+ void setPath(PathType pathType, const String& filePath) { m_pathType = pathType; m_path = filePath; }
+ void setBlob(ISlangBlob* blob) { m_blob = blob; }
+
+ void add(Entry::Style style, RefObject* obj);
+ void add(Entry::Style style, ISlangUnknown* intf);
+
+ PathType getPathType() const { return m_pathType; }
+ const String& getPath() const { return m_path; }
+
+ const List<Entry>& getEntries() const { return m_entries; }
+
+ /// Ctor
+ Artifact(const Desc& desc) :m_desc(desc) {}
+ /// Dtor
+ ~Artifact();
+
+protected:
+ Desc m_desc;
+
+ PathType m_pathType = PathType::None; ///< What the path indicates
+ String m_path; ///< The path
+
+ ComPtr<ISlangBlob> m_blob; ///< Blob to store result in memory
+
+ List<Entry> m_entries;
+};
+
+// ----------------------------------------------------------------------
+template <typename T>
+T* Artifact::findObjectInstance()
+{
+ RefObject* check = static_cast<T*>(nullptr);
+ SLANG_UNUSED(check);
+
+ // Check if we already have it
+ for (const auto& entry : m_entries)
+ {
+ if (entry.type == Entry::Type::ObjectInstance)
+ {
+ auto obj = as<T>(entry.object);
+ if (obj)
+ {
+ return obj;
+ }
+ }
+ }
+ return nullptr;
+}
+
+/// 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; }
+
+} // namespace Slang
+
+#endif
diff --git a/source/compiler-core/slang-downstream-compiler.h b/source/compiler-core/slang-downstream-compiler.h
index e40ffc13a..e6c1f7a40 100644
--- a/source/compiler-core/slang-downstream-compiler.h
+++ b/source/compiler-core/slang-downstream-compiler.h
@@ -13,6 +13,8 @@
#include "../../slang-com-ptr.h"
+#include "slang-artifact.h"
+
namespace Slang
{
@@ -303,7 +305,7 @@ public:
List<String> libraryPaths;
/// Libraries to link against.
- List<String> libraries;
+ List<RefPtr<Artifact>> libraries;
List<CapabilityVersion> requiredCapabilityVersions;
diff --git a/source/compiler-core/slang-dxc-compiler.cpp b/source/compiler-core/slang-dxc-compiler.cpp
index f5db290b1..eb26dd3cc 100644
--- a/source/compiler-core/slang-dxc-compiler.cpp
+++ b/source/compiler-core/slang-dxc-compiler.cpp
@@ -50,6 +50,52 @@ static UnownedStringSlice _getSlice(IDxcBlob* blob) { return StringUtil::getSlic
// 7f61fc7d-950d-467f-b3e3-3c02fb49187c
static const Guid IID_IDxcIncludeHandler = { 0x7f61fc7d, 0x950d, 0x467f, { 0x3c, 0x02, 0xfb, 0x49, 0x18, 0x7c } };
+namespace { // anonymous
+
+class LibraryNameList
+{
+public:
+ void addName(Artifact* library)
+ {
+ String name;
+ if (library->getPathType() == Artifact::Existing)
+ {
+ name = Path::getFileNameWithoutExt(library->getPath());
+ }
+ addName(name);
+ }
+ void addName(const String& inName)
+ {
+ String name(inName);
+ if (name.getLength() == 0)
+ {
+ name = "unnamed";
+ }
+
+ if (m_names.indexOf(name) >= 0)
+ {
+ StringBuilder buf;
+ for (Index i = 1; ; ++i)
+ {
+ buf.Clear();
+ buf << name << "_" << i;
+
+ if (m_names.indexOf(buf) < 0)
+ {
+ name = buf;
+ break;
+ }
+ }
+ }
+
+ m_names.add(name);
+ }
+
+ List<String> m_names;
+};
+
+} // anonymous
+
class DxcIncludeHandler : public IDxcIncludeHandler
{
public:
@@ -137,6 +183,8 @@ public:
protected:
DxcCreateInstanceProc m_createInstance = nullptr;
+
+
ComPtr<ISlangSharedLibrary> m_sharedLibrary;
};
@@ -183,6 +231,60 @@ static SlangResult _parseDiagnosticLine(const UnownedStringSlice& line, List<Uno
return SLANG_OK;
}
+static SlangResult _handleOperationResult(IDxcOperationResult* dxcResult, DownstreamDiagnostics& ioDiagnostics, ComPtr<IDxcBlob>& outBlob)
+{
+ // Retrieve result.
+ HRESULT resultCode = S_OK;
+ SLANG_RETURN_ON_FAIL(dxcResult->GetStatus(&resultCode));
+
+ // Note: it seems like the dxcompiler interface
+ // doesn't support querying diagnostic output
+ // *unless* the compile failed (no way to get
+ // warnings out!?).
+
+ if (SLANG_SUCCEEDED(ioDiagnostics.result))
+ {
+ ioDiagnostics.result = resultCode;
+ }
+
+ // Try getting the error/diagnostics blob
+ ComPtr<IDxcBlobEncoding> dxcErrorBlob;
+ dxcResult->GetErrorBuffer(dxcErrorBlob.writeRef());
+
+ if (dxcErrorBlob)
+ {
+ const UnownedStringSlice diagnosticsSlice = _getSlice(dxcErrorBlob);
+ if (diagnosticsSlice.getLength())
+ {
+ if (ioDiagnostics.rawDiagnostics.getLength() > 0)
+ {
+ ioDiagnostics.rawDiagnostics.append("\n");
+ }
+ ioDiagnostics.rawDiagnostics.append(diagnosticsSlice);
+
+ SlangResult diagnosticParseRes = DownstreamDiagnostic::parseColonDelimitedDiagnostics(diagnosticsSlice, 0, _parseDiagnosticLine, ioDiagnostics.diagnostics);
+
+ SLANG_UNUSED(diagnosticParseRes);
+ SLANG_ASSERT(SLANG_SUCCEEDED(diagnosticParseRes));
+ }
+ }
+
+ // If it failed, make sure we have an error in the diagnostics
+ if (SLANG_FAILED(resultCode))
+ {
+ // In case the parsing failed, we still have an error -> so require there is one in the diagnostics
+ ioDiagnostics.requireErrorDiagnostic();
+ }
+ else
+ {
+ // Okay, the compile supposedly succeeded, so we
+ // just need to grab the buffer with the output DXIL.
+ SLANG_RETURN_ON_FAIL(dxcResult->GetResult(outBlob.writeRef()));
+ }
+
+ return SLANG_OK;
+}
+
SlangResult DXCDownstreamCompiler::compile(const CompileOptions& options, RefPtr<DownstreamCompileResult>& outResult)
{
// This compiler doesn't read files, they should be read externally and stored in sourceContents/sourceContentsPath
@@ -197,6 +299,22 @@ SlangResult DXCDownstreamCompiler::compile(const CompileOptions& options, RefPtr
return SLANG_FAIL;
}
+ // Find all of the libraries
+ List<Artifact*> libraries;
+ for (Artifact* library : options.libraries)
+ {
+ const auto desc = library->getDesc();
+
+ if (desc.kind == ArtifactKind::Library && desc.payload == ArtifactPayload::DXIL)
+ {
+ // Make sure they all have blobs
+ ComPtr<ISlangBlob> libraryBlob;
+ SLANG_RETURN_ON_FAIL(library->loadBlob(ArtifactKeep::Yes, libraryBlob));
+
+ libraries.add(library);
+ }
+ }
+
ComPtr<IDxcCompiler> dxcCompiler;
SLANG_RETURN_ON_FAIL(m_createInstance(CLSID_DxcCompiler, __uuidof(dxcCompiler), (LPVOID*)dxcCompiler.writeRef()));
ComPtr<IDxcLibrary> dxcLibrary;
@@ -289,8 +407,28 @@ SlangResult DXCDownstreamCompiler::compile(const CompileOptions& options, RefPtr
//
args.add(L"-no-warnings");
+ String profileName = options.profileName;
+ // If we are going to link we have to compile in the lib profile style
+ if (libraries.getCount())
+ {
+ if (!profileName.startsWith("lib"))
+ {
+ const Index index = profileName.indexOf('_');
+ if (index < 0)
+ {
+ profileName = "lib_6_3";
+ }
+ else
+ {
+ StringBuilder buf;
+ buf << "lib" << profileName.getUnownedSlice().tail(index);
+ profileName = buf;
+ }
+ }
+ }
+
OSString wideEntryPointName = options.entryPointName.toWString();
- OSString wideProfileName = options.profileName.toWString();
+ OSString wideProfileName = profileName.toWString();
if (options.flags & CompileOptions::Flag::EnableFloat16)
{
@@ -319,50 +457,80 @@ SlangResult DXCDownstreamCompiler::compile(const CompileOptions& options, RefPtr
&includeHandler, // `#include` handler
dxcResult.writeRef()));
- // Retrieve result.
- HRESULT resultCode = S_OK;
- SLANG_RETURN_ON_FAIL(dxcResult->GetStatus(&resultCode));
-
- // Note: it seems like the dxcompiler interface
- // doesn't support querying diagnostic output
- // *unless* the compile failed (no way to get
- // warnings out!?).
-
DownstreamDiagnostics diagnostics;
- diagnostics.result = resultCode;
- // Try getting the error/diagnostics blob
- ComPtr<IDxcBlobEncoding> dxcErrorBlob;
- dxcResult->GetErrorBuffer(dxcErrorBlob.writeRef());
+ ComPtr<IDxcBlob> dxcResultBlob;
- if (dxcErrorBlob)
+ SLANG_RETURN_ON_FAIL(_handleOperationResult(dxcResult, diagnostics, dxcResultBlob));
+
+ // If we have libraries then we need to link...
+ if (libraries.getCount())
{
- const UnownedStringSlice diagnosticsSlice = _getSlice(dxcErrorBlob);
- if (diagnosticsSlice.getLength())
+ ComPtr<IDxcLinker> linker;
+ SLANG_RETURN_ON_FAIL(m_createInstance(CLSID_DxcLinker, __uuidof(linker), (void**)linker.writeRef()));
+
+ List<ComPtr<ISlangBlob>> libraryBlobs;
+
+ LibraryNameList libraryNames;
+
+ for (Artifact* library : libraries)
{
- diagnostics.rawDiagnostics = String(diagnosticsSlice);
+ ComPtr<ISlangBlob> blob;
+ SLANG_RETURN_ON_FAIL(library->loadBlob(ArtifactKeep::Yes, blob));
- SlangResult diagnosticParseRes = DownstreamDiagnostic::parseColonDelimitedDiagnostics(diagnosticsSlice, 0, _parseDiagnosticLine, diagnostics.diagnostics);
+ libraryBlobs.add(blob);
+ libraryNames.addName(library);
+ }
- SLANG_UNUSED(diagnosticParseRes);
- SLANG_ASSERT(SLANG_SUCCEEDED(diagnosticParseRes));
+ // Add the compiled blob name
+
+ {
+ auto blob = (ISlangBlob*)dxcResultBlob.get();
+ libraryBlobs.add(ComPtr<ISlangBlob>(blob));
}
- }
- ComPtr<IDxcBlob> dxcResultBlob;
+ if (options.modulePath.getLength())
+ {
+ libraryNames.addName(Path::getFileNameWithoutExt(options.modulePath));
+ }
+ else if (options.sourceContentsPath.getLength())
+ {
+ libraryNames.addName(Path::getFileNameWithoutExt(options.sourceContentsPath));
+ }
+ else
+ {
+ libraryNames.addName("");
+ }
- // If it failed, make sure we have an error in the diagnostics
- if (SLANG_FAILED(resultCode))
- {
- // In case the parsing failed, we still have an error -> so require there is one in the diagnostics
- diagnostics.requireErrorDiagnostic();
+ const Index librariesCount = libraryNames.m_names.getCount();
+ SLANG_ASSERT(libraryBlobs.getCount() == librariesCount);
+
+ List<const wchar_t*> linkLibraryNames;
+ List<OSString> wideLibraryNames;
+
+ linkLibraryNames.setCount(librariesCount);
+ wideLibraryNames.setCount(librariesCount);
+
+ for (Index i = 0; i < librariesCount; ++i)
+ {
+ wideLibraryNames[i] = libraryNames.m_names[i].toWString();
+ linkLibraryNames[i] = wideLibraryNames[i].begin();
+
+ // Register the library
+ SLANG_RETURN_ON_FAIL(linker->RegisterLibrary(linkLibraryNames[i], (IDxcBlob*)libraryBlobs[i].get()));
+ }
+
+ // Use the original profile name
+ wideProfileName = options.profileName.toWString();
+
+ ComPtr<IDxcOperationResult> linkDxcResult;
+ SLANG_RETURN_ON_FAIL(linker->Link(wideEntryPointName.begin(), wideProfileName.begin(), linkLibraryNames.getBuffer(), UINT32(librariesCount), nullptr, 0, linkDxcResult.writeRef()));
+
+ ComPtr<IDxcBlob> linkedBlob;
+ SLANG_RETURN_ON_FAIL(_handleOperationResult(linkDxcResult, diagnostics, linkedBlob));
+
+ dxcResultBlob = linkedBlob;
}
- else
- {
- // Okay, the compile supposedly succeeded, so we
- // just need to grab the buffer with the output DXIL.
- SLANG_RETURN_ON_FAIL(dxcResult->GetResult(dxcResultBlob.writeRef()));
- }
outResult = new BlobDownstreamCompileResult(diagnostics, (ISlangBlob*)dxcResultBlob.get());
return SLANG_OK;
diff --git a/source/compiler-core/slang-gcc-compiler-util.cpp b/source/compiler-core/slang-gcc-compiler-util.cpp
index 8d1b87b68..7d1b7b86a 100644
--- a/source/compiler-core/slang-gcc-compiler-util.cpp
+++ b/source/compiler-core/slang-gcc-compiler-util.cpp
@@ -619,6 +619,56 @@ static SlangResult _parseGCCFamilyLine(const UnownedStringSlice& line, LineParse
cmdLine.addArg(sourceFile);
}
+ // Add the library paths
+ List<String> libraryPaths;
+ libraryPaths.addRange(options.libraryPaths.getBuffer(), options.libraryPaths.getCount());
+
+ // Artifacts might add library paths
+ for (Artifact* artifact : options.libraries)
+ {
+ const auto desc = artifact->getDesc();
+ // If it's a library for CPU types, try and use it
+ if (desc.isCpuBinary())
+ {
+ if (desc.kind == ArtifactKind::Library)
+ {
+ String path;
+ SLANG_RETURN_ON_FAIL(artifact->requireFilePath(ArtifactKeep::No, path));
+
+ String parentDir = Path::getParentDirectory(parentDir);
+ if (parentDir.getLength())
+ {
+ // Check if we already have the library path, only add it if it's not found
+ if (libraryPaths.indexOf(parentDir) < 0)
+ {
+ libraryPaths.add(parentDir);
+ }
+ path = Path::getFileName(path);
+ }
+
+ // If it starts with lib strip it
+ if (path.startsWith("lib"))
+ {
+ const String stripLib = path.getUnownedSlice().tail(3);
+ path = stripLib;
+ }
+
+ // Strip the extension if it's a match
+ auto extension = Path::getPathExt(path);
+ if (extension.getLength())
+ {
+ auto libExt = ArtifactDesc::make(ArtifactKind::Library, ArtifactPayload::HostCPU).getDefaultExtension();
+ if (extension == libExt)
+ {
+ path = Path::getFileNameWithoutExt(path);
+ }
+ }
+
+ cmdLine.addPrefixPathArg("-l", path);
+ }
+ }
+ }
+
for (const auto& libPath : options.libraryPaths)
{
// Note that any escaping of the path is handled in the ProcessUtil::
@@ -628,6 +678,7 @@ static SlangResult _parseGCCFamilyLine(const UnownedStringSlice& line, LineParse
cmdLine.addArg(libPath);
}
+
if (options.sourceLanguage == SLANG_SOURCE_LANGUAGE_CPP && !PlatformUtil::isFamily(PlatformFamily::Windows, platformKind))
{
// Make STD libs available
diff --git a/source/compiler-core/slang-visual-studio-compiler-util.cpp b/source/compiler-core/slang-visual-studio-compiler-util.cpp
index 2ba69c1ce..df64ed821 100644
--- a/source/compiler-core/slang-visual-studio-compiler-util.cpp
+++ b/source/compiler-core/slang-visual-studio-compiler-util.cpp
@@ -257,9 +257,19 @@ namespace Slang
}
// Link libraries.
- for (const auto& lib : options.libraries)
+ for (Artifact* artifact : options.libraries)
{
- cmdLine.addPrefixPathArg("", lib, ".lib");
+ if (artifact->getDesc().isCpuBinary())
+ {
+ String path;
+ SLANG_RETURN_ON_FAIL(artifact->requireFilePath(ArtifactKeep::No, path));
+
+ if (Path::getPathExt(path).getLength() == 0)
+ {
+ path.append(".lib");
+ }
+ cmdLine.addArg(path);
+ }
}
return SLANG_OK;