summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorTheresa Foley <10618364+tangent-vector@users.noreply.github.com>2025-07-24 12:59:58 -0700
committerGitHub <noreply@github.com>2025-07-24 19:59:58 +0000
commit8ccd495d5eaa82cb831378c28dd190e657b6c999 (patch)
treec39dc364d95f78984fd4ba2776d259ff944d2596
parent2d23a962766a97cbb11bcee5483a66aec923da49 (diff)
Organize code better by splitting some big files (#7890)
* Organize code better by splitting some big files The basic change here is that the majority of the declarations in `slang-compiler.h` have been split out into a set of smaller and more focused files. As a result, the implement of those declarations have been moved from `slang-compiler.cpp` and `slang.cpp` over to those new files when the proper home for code is obvious. I have tried as much as possible to *not* make any edits to the code along the way, and just copy-paste declarations from one place to another as-is. The exceptions I am aware of are: * In some cases a function that used to be file-scope `static` was used by code that landed in two or more different `.cpp` files. In these cases, I changed the function to be non-`static` (removing the `_` prefix from its name, if it had one, per our naming conventions), and put a declaration for the function into the most appropriate header I could identify. * I added a few comments in places where I saw ugly or unfortunate things in the code I was moving, and wanted to tag them with `TODO`s so we can hopefully get to them in the fullness of time. * I added top-level comments to each of the new `.h` files that was introduced to try to explain the logic for what goes into that file. * In cases where one of the new header files mostly existed to declare a single type, I sometimes added more detail to the doc comment on that type, to better explain the type and its role in the compiler (this is text that otherwise might have gone into the comment at the top leve lof the file, but I figured that the doc comment would have higher discoverability). I expect that the most contentious choice here is that the `Session` class lands in `slang-global-session.h` while `slang-session.h` holds the `Linkage` class. The names used in this change are consistent with how the relevant concepts in the public Slang API are named, and are consistent with how we *intend* to rename the classes themselves in time. * format code * fixup --------- Co-authored-by: slangbot <186143334+slangbot@users.noreply.github.com>
-rw-r--r--source/compiler-core/slang-diagnostic-sink.cpp41
-rw-r--r--source/compiler-core/slang-diagnostic-sink.h13
-rw-r--r--source/core/slang-type-convert-util.h8
-rw-r--r--source/core/slang-type-text-util.h8
-rw-r--r--source/slang/slang-base-type-info.cpp95
-rw-r--r--source/slang/slang-base-type-info.h50
-rw-r--r--source/slang/slang-check-impl.h15
-rw-r--r--source/slang/slang-code-gen.cpp1405
-rw-r--r--source/slang/slang-code-gen.h263
-rw-r--r--source/slang/slang-compile-request.cpp703
-rw-r--r--source/slang/slang-compile-request.h362
-rw-r--r--source/slang/slang-compiler-api.cpp2
-rw-r--r--source/slang/slang-compiler-api.h124
-rw-r--r--source/slang/slang-compiler-fwd.h31
-rw-r--r--source/slang/slang-compiler.cpp3097
-rw-r--r--source/slang/slang-compiler.h3854
-rw-r--r--source/slang/slang-container-pool.cpp0
-rw-r--r--source/slang/slang-emit-dependency-file.cpp125
-rw-r--r--source/slang/slang-emit-dependency-file.h20
-rw-r--r--source/slang/slang-end-to-end-request.cpp1999
-rw-r--r--source/slang/slang-end-to-end-request.h416
-rw-r--r--source/slang/slang-entry-point.cpp159
-rw-r--r--source/slang/slang-entry-point.h333
-rw-r--r--source/slang/slang-global-session.cpp1217
-rw-r--r--source/slang/slang-global-session.h366
-rw-r--r--source/slang/slang-linkable-impls.cpp752
-rw-r--r--source/slang/slang-linkable-impls.h566
-rw-r--r--source/slang/slang-linkable.cpp1037
-rw-r--r--source/slang/slang-linkable.h430
-rw-r--r--source/slang/slang-module.cpp420
-rw-r--r--source/slang/slang-module.h525
-rw-r--r--source/slang/slang-pass-through.cpp275
-rw-r--r--source/slang/slang-pass-through.h91
-rw-r--r--source/slang/slang-profile.cpp272
-rw-r--r--source/slang/slang-profile.h2
-rw-r--r--source/slang/slang-serialize-ast.cpp1
-rw-r--r--source/slang/slang-session.cpp2068
-rw-r--r--source/slang/slang-session.h476
-rw-r--r--source/slang/slang-target-program.cpp113
-rw-r--r--source/slang/slang-target-program.h143
-rw-r--r--source/slang/slang-target.cpp248
-rw-r--r--source/slang/slang-target.h141
-rw-r--r--source/slang/slang-translation-unit.cpp251
-rw-r--r--source/slang/slang-translation-unit.h117
-rw-r--r--source/slang/slang.cpp8070
45 files changed, 15713 insertions, 14991 deletions
diff --git a/source/compiler-core/slang-diagnostic-sink.cpp b/source/compiler-core/slang-diagnostic-sink.cpp
index 28a98266a..de8bdf52c 100644
--- a/source/compiler-core/slang-diagnostic-sink.cpp
+++ b/source/compiler-core/slang-diagnostic-sink.cpp
@@ -828,4 +828,45 @@ DiagnosticsLookup::DiagnosticsLookup(
add(diagnostics, diagnosticsCount);
}
+void outputExceptionDiagnostic(
+ const AbortCompilationException& exception,
+ DiagnosticSink& sink,
+ slang::IBlob** outDiagnostics)
+{
+ sink.diagnoseRaw(Severity::Error, exception.Message.getUnownedSlice());
+ sink.getBlobIfNeeded(outDiagnostics);
+}
+
+void outputExceptionDiagnostic(
+ const Exception& exception,
+ DiagnosticSink& sink,
+ slang::IBlob** outDiagnostics)
+{
+ try
+ {
+ sink.diagnoseRaw(Severity::Internal, exception.Message.getUnownedSlice());
+ }
+ catch (const AbortCompilationException&)
+ {
+ // Catch and ignore the AbortCompilationException that diagnoseRaw throws
+ // for Internal severity to prevent exception leak from loadModule
+ }
+ sink.getBlobIfNeeded(outDiagnostics);
+}
+
+void outputExceptionDiagnostic(DiagnosticSink& sink, slang::IBlob** outDiagnostics)
+{
+ try
+ {
+ sink.diagnoseRaw(Severity::Fatal, "An unknown exception occurred");
+ }
+ catch (const AbortCompilationException&)
+ {
+ // Catch and ignore the AbortCompilationException that diagnoseRaw throws
+ // for Fatal severity to prevent exception leak from loadModule
+ }
+ sink.getBlobIfNeeded(outDiagnostics);
+}
+
+
} // namespace Slang
diff --git a/source/compiler-core/slang-diagnostic-sink.h b/source/compiler-core/slang-diagnostic-sink.h
index 2d60747d9..fe05d953b 100644
--- a/source/compiler-core/slang-diagnostic-sink.h
+++ b/source/compiler-core/slang-diagnostic-sink.h
@@ -393,6 +393,19 @@ protected:
MemoryArena m_arena;
};
+
+void outputExceptionDiagnostic(
+ const AbortCompilationException& exception,
+ DiagnosticSink& sink,
+ slang::IBlob** outDiagnostics);
+
+void outputExceptionDiagnostic(
+ const Exception& exception,
+ DiagnosticSink& sink,
+ slang::IBlob** outDiagnostics);
+
+void outputExceptionDiagnostic(DiagnosticSink& sink, slang::IBlob** outDiagnostics);
+
} // namespace Slang
#endif
diff --git a/source/core/slang-type-convert-util.h b/source/core/slang-type-convert-util.h
index d4c80abf8..45cb4b82c 100644
--- a/source/core/slang-type-convert-util.h
+++ b/source/core/slang-type-convert-util.h
@@ -1,6 +1,14 @@
#ifndef SLANG_CORE_TYPE_CONVERT_UTIL_H
#define SLANG_CORE_TYPE_CONVERT_UTIL_H
+// TODO: This file's name is not obvious for what it contains.
+// Either the file should be renamed to be more obviously related
+// to what it does, or (better yet) the functionality should be
+// moved to reside in places that are more logically related
+// to each of the given types.
+//
+// Also: this doesn't belong in `core` for a bunch of reasons.
+
#include "slang.h"
namespace Slang
diff --git a/source/core/slang-type-text-util.h b/source/core/slang-type-text-util.h
index 684d109c3..075b9e2a0 100644
--- a/source/core/slang-type-text-util.h
+++ b/source/core/slang-type-text-util.h
@@ -1,6 +1,14 @@
#ifndef SLANG_CORE_TYPE_TEXT_UTIL_H
#define SLANG_CORE_TYPE_TEXT_UTIL_H
+// TODO: This file's name is not obvious for what it contains.
+// Either the file should be renamed to be more obviously related
+// to what it does, or (better yet) the functionality should be
+// moved to reside in places that are more logically related
+// to each of the given types.
+//
+// Also: this doesn't belong in `core` for a bunch of reasons.
+
#include "slang-array-view.h"
#include "slang-name-value.h"
#include "slang-string.h"
diff --git a/source/slang/slang-base-type-info.cpp b/source/slang/slang-base-type-info.cpp
new file mode 100644
index 000000000..9072e34e4
--- /dev/null
+++ b/source/slang/slang-base-type-info.cpp
@@ -0,0 +1,95 @@
+// slang-base-type-info.cpp
+#include "slang-base-type-info.h"
+
+namespace Slang
+{
+
+/* static */ const BaseTypeInfo BaseTypeInfo::s_info[Index(BaseType::CountOf)] = {
+ {0, 0, uint8_t(BaseType::Void)},
+ {uint8_t(sizeof(bool)), 0, uint8_t(BaseType::Bool)},
+ {uint8_t(sizeof(int8_t)),
+ BaseTypeInfo::Flag::Signed | BaseTypeInfo::Flag::Integer,
+ uint8_t(BaseType::Int8)},
+ {uint8_t(sizeof(int16_t)),
+ BaseTypeInfo::Flag::Signed | BaseTypeInfo::Flag::Integer,
+ uint8_t(BaseType::Int16)},
+ {uint8_t(sizeof(int32_t)),
+ BaseTypeInfo::Flag::Signed | BaseTypeInfo::Flag::Integer,
+ uint8_t(BaseType::Int)},
+ {uint8_t(sizeof(int64_t)),
+ BaseTypeInfo::Flag::Signed | BaseTypeInfo::Flag::Integer,
+ uint8_t(BaseType::Int64)},
+ {uint8_t(sizeof(uint8_t)), BaseTypeInfo::Flag::Integer, uint8_t(BaseType::UInt8)},
+ {uint8_t(sizeof(uint16_t)), BaseTypeInfo::Flag::Integer, uint8_t(BaseType::UInt16)},
+ {uint8_t(sizeof(uint32_t)), BaseTypeInfo::Flag::Integer, uint8_t(BaseType::UInt)},
+ {uint8_t(sizeof(uint64_t)), BaseTypeInfo::Flag::Integer, uint8_t(BaseType::UInt64)},
+ {uint8_t(sizeof(uint16_t)), BaseTypeInfo::Flag::FloatingPoint, uint8_t(BaseType::Half)},
+ {uint8_t(sizeof(float)), BaseTypeInfo::Flag::FloatingPoint, uint8_t(BaseType::Float)},
+ {uint8_t(sizeof(double)), BaseTypeInfo::Flag::FloatingPoint, uint8_t(BaseType::Double)},
+ {uint8_t(sizeof(char)),
+ BaseTypeInfo::Flag::Signed | BaseTypeInfo::Flag::Integer,
+ uint8_t(BaseType::Char)},
+ {uint8_t(sizeof(intptr_t)),
+ BaseTypeInfo::Flag::Signed | BaseTypeInfo::Flag::Integer,
+ uint8_t(BaseType::IntPtr)},
+ {uint8_t(sizeof(uintptr_t)), BaseTypeInfo::Flag::Integer, uint8_t(BaseType::UIntPtr)},
+};
+
+/* static */ bool BaseTypeInfo::check()
+{
+ for (Index i = 0; i < SLANG_COUNT_OF(s_info); ++i)
+ {
+ if (s_info[i].baseType != i)
+ {
+ SLANG_ASSERT(!"Inconsistency between the s_info table and BaseInfo");
+ return false;
+ }
+ }
+ return true;
+}
+
+/* static */ UnownedStringSlice BaseTypeInfo::asText(BaseType baseType)
+{
+ switch (baseType)
+ {
+ case BaseType::Void:
+ return UnownedStringSlice::fromLiteral("void");
+ case BaseType::Bool:
+ return UnownedStringSlice::fromLiteral("bool");
+ case BaseType::Int8:
+ return UnownedStringSlice::fromLiteral("int8_t");
+ case BaseType::Int16:
+ return UnownedStringSlice::fromLiteral("int16_t");
+ case BaseType::Int:
+ return UnownedStringSlice::fromLiteral("int");
+ case BaseType::Int64:
+ return UnownedStringSlice::fromLiteral("int64_t");
+ case BaseType::UInt8:
+ return UnownedStringSlice::fromLiteral("uint8_t");
+ case BaseType::UInt16:
+ return UnownedStringSlice::fromLiteral("uint16_t");
+ case BaseType::UInt:
+ return UnownedStringSlice::fromLiteral("uint");
+ case BaseType::UInt64:
+ return UnownedStringSlice::fromLiteral("uint64_t");
+ case BaseType::Half:
+ return UnownedStringSlice::fromLiteral("half");
+ case BaseType::Float:
+ return UnownedStringSlice::fromLiteral("float");
+ case BaseType::Double:
+ return UnownedStringSlice::fromLiteral("double");
+ case BaseType::Char:
+ return UnownedStringSlice::fromLiteral("char");
+ case BaseType::IntPtr:
+ return UnownedStringSlice::fromLiteral("intptr_t");
+ case BaseType::UIntPtr:
+ return UnownedStringSlice::fromLiteral("uintptr_t");
+ default:
+ {
+ SLANG_ASSERT(!"Unknown basic type");
+ return UnownedStringSlice();
+ }
+ }
+}
+
+} // namespace Slang
diff --git a/source/slang/slang-base-type-info.h b/source/slang/slang-base-type-info.h
new file mode 100644
index 000000000..4b96af18f
--- /dev/null
+++ b/source/slang/slang-base-type-info.h
@@ -0,0 +1,50 @@
+// slang-base-type-info.h
+#pragma once
+
+//
+// This file defines the `BaseTypeInfo` type, which encodes
+// information (such as size in bits) about the base types
+// supported by the Slang language. That information is used
+// for things like checking if a literal is in the representible
+// range of a given type, and for determining the relative
+// cost of implicit conversions between the base types.
+//
+
+#include "../core/slang-basic.h"
+#include "slang-type-system-shared.h"
+
+namespace Slang
+{
+
+// Information about BaseType that's useful for checking literals
+struct BaseTypeInfo
+{
+ typedef uint8_t Flags;
+ struct Flag
+ {
+ enum Enum : Flags
+ {
+ Signed = 0x1,
+ FloatingPoint = 0x2,
+ Integer = 0x4,
+ };
+ };
+
+ SLANG_FORCE_INLINE static const BaseTypeInfo& getInfo(BaseType baseType)
+ {
+ return s_info[Index(baseType)];
+ }
+
+ static UnownedStringSlice asText(BaseType baseType);
+
+ uint8_t sizeInBytes; ///< Size of type in bytes
+ Flags flags;
+ uint8_t baseType;
+
+ static bool check();
+
+private:
+ static const BaseTypeInfo s_info[Index(BaseType::CountOf)];
+};
+
+} // namespace Slang
diff --git a/source/slang/slang-check-impl.h b/source/slang/slang-check-impl.h
index 3145c9454..a82278054 100644
--- a/source/slang/slang-check-impl.h
+++ b/source/slang/slang-check-impl.h
@@ -3261,4 +3261,19 @@ bool getExtensionTargetDeclList(
ExtensionDecl* extDeclRef,
ShortList<AggTypeDecl*>& targetDecls);
+void validateEntryPoint(EntryPoint* entryPoint, DiagnosticSink* sink);
+
+RefPtr<ComponentType> createUnspecializedGlobalComponentType(
+ FrontEndCompileRequest* compileRequest);
+
+RefPtr<ComponentType> createUnspecializedGlobalAndEntryPointsComponentType(
+ FrontEndCompileRequest* compileRequest,
+ List<RefPtr<ComponentType>>& outUnspecializedEntryPoints);
+
+RefPtr<ComponentType> createSpecializedGlobalComponentType(EndToEndCompileRequest* endToEndReq);
+
+RefPtr<ComponentType> createSpecializedGlobalAndEntryPointsComponentType(
+ EndToEndCompileRequest* endToEndReq,
+ List<RefPtr<ComponentType>>& outSpecializedEntryPoints);
+
} // namespace Slang
diff --git a/source/slang/slang-code-gen.cpp b/source/slang/slang-code-gen.cpp
new file mode 100644
index 000000000..cd47147e2
--- /dev/null
+++ b/source/slang/slang-code-gen.cpp
@@ -0,0 +1,1405 @@
+// slang-code-gen.cpp
+#include "slang-code-gen.h"
+
+#include "../compiler-core/slang-slice-allocator.h"
+#include "../core/slang-type-convert-util.h"
+#include "../core/slang-type-text-util.h"
+#include "slang-compiler.h"
+#include "slang-emit-cuda.h" // for `CUDAExtensionTracker`
+#include "slang-extension-tracker.h" // for `ShaderExtensionTracker`
+
+// TODO: The "artifact" system is a scourge.
+#include "../compiler-core/slang-artifact-desc-util.h"
+#include "../compiler-core/slang-artifact-impl.h"
+#include "../compiler-core/slang-artifact-util.h"
+#include "slang-artifact-output-util.h"
+
+namespace Slang
+{
+
+EndToEndCompileRequest* CodeGenContext::isPassThroughEnabled()
+{
+ auto endToEndReq = isEndToEndCompile();
+
+ // If there isn't an end-to-end compile going on,
+ // there can be no pass-through.
+ //
+ if (!endToEndReq)
+ return nullptr;
+
+ // And if pass-through isn't set on that end-to-end compile,
+ // then we clearly areb't doing a pass-through compile.
+ //
+ if (endToEndReq->m_passThrough == PassThroughMode::None)
+ return nullptr;
+
+ // If we have confirmed that pass-through compilation is going on,
+ // we return the end-to-end request, because it has all the
+ // relevant state that we need to implement pass-through mode.
+ //
+ return endToEndReq;
+}
+
+/// If there is a pass-through compile going on, find the translation unit for the given entry
+/// point. Assumes isPassThroughEnabled has already been called
+TranslationUnitRequest* getPassThroughTranslationUnit(
+ EndToEndCompileRequest* endToEndReq,
+ Int entryPointIndex)
+{
+ SLANG_ASSERT(endToEndReq);
+ SLANG_ASSERT(endToEndReq->m_passThrough != PassThroughMode::None);
+ auto frontEndReq = endToEndReq->getFrontEndReq();
+ auto entryPointReq = frontEndReq->getEntryPointReq(entryPointIndex);
+ auto translationUnit = entryPointReq->getTranslationUnit();
+ return translationUnit;
+}
+
+TranslationUnitRequest* CodeGenContext::findPassThroughTranslationUnit(Int entryPointIndex)
+{
+ if (auto endToEndReq = isPassThroughEnabled())
+ return getPassThroughTranslationUnit(endToEndReq, entryPointIndex);
+ return nullptr;
+}
+
+static void _appendCodeWithPath(
+ const UnownedStringSlice& filePath,
+ const UnownedStringSlice& fileContent,
+ StringBuilder& outCodeBuilder)
+{
+ outCodeBuilder << "#line 1 \"";
+ auto handler = StringEscapeUtil::getHandler(StringEscapeUtil::Style::Cpp);
+ handler->appendEscaped(filePath, outCodeBuilder);
+ outCodeBuilder << "\"\n";
+ outCodeBuilder << fileContent << "\n";
+}
+
+#if SLANG_VC
+// TODO(JS): This is a workaround
+// In debug VS builds there is a warning on line about it being unreachable.
+// for (auto entryPointIndex : getEntryPointIndices())
+// It's not clear how that could possibly be unreachable
+//
+// Note(tfoley): The diagnostic noted above arises because the `for`
+// loop in question unconditionally exits on its first iteration.
+// As a result the automatically-generated code for the "continue clause"
+// (more or less the `operator++` on the iterator) is identified
+// as unreachable code.
+//
+// The actual fix would be to make this code more explicit about its
+// expectations. Either it expects there to be only a single entry
+// point (in which case it should diagnose if that expectation is
+// not met), or it just wants to query for the *first* entry point
+// more explicitly, and include a comment explaining why that is valid.
+//
+#pragma warning(push)
+#pragma warning(disable : 4702)
+#endif
+SlangResult CodeGenContext::emitEntryPointsSource(ComPtr<IArtifact>& outArtifact)
+{
+ outArtifact.setNull();
+
+ SLANG_RETURN_ON_FAIL(requireTranslationUnitSourceFiles());
+
+ auto endToEndReq = isPassThroughEnabled();
+ if (endToEndReq)
+ {
+ for (auto entryPointIndex : getEntryPointIndices())
+ {
+ auto translationUnit = getPassThroughTranslationUnit(endToEndReq, entryPointIndex);
+ SLANG_ASSERT(translationUnit);
+
+ /// Make sure we have the source files
+ SLANG_RETURN_ON_FAIL(translationUnit->requireSourceFiles());
+
+ // Generate a string that includes the content of
+ // the source file(s), along with a line directive
+ // to ensure that we get reasonable messages
+ // from the downstream compiler when in pass-through
+ // mode.
+
+ StringBuilder codeBuilder;
+ if (getTargetFormat() == CodeGenTarget::GLSL)
+ {
+ // Special case GLSL
+ int translationUnitCounter = 0;
+ for (auto sourceFile : translationUnit->getSourceFiles())
+ {
+ int translationUnitIndex = translationUnitCounter++;
+
+ // We want to output `#line` directives, but we need
+ // to skip this for the first file, since otherwise
+ // some GLSL implementations will get tripped up by
+ // not having the `#version` directive be the first
+ // thing in the file.
+ if (translationUnitIndex != 0)
+ {
+ codeBuilder << "#line 1 " << translationUnitIndex << "\n";
+ }
+ codeBuilder << sourceFile->getContent() << "\n";
+ }
+ }
+ else
+ {
+ for (auto sourceFile : translationUnit->getSourceFiles())
+ {
+ _appendCodeWithPath(
+ sourceFile->getPathInfo().foundPath.getUnownedSlice(),
+ sourceFile->getContent(),
+ codeBuilder);
+ }
+ }
+
+ auto artifact =
+ ArtifactUtil::createArtifactForCompileTarget(asExternal(getTargetFormat()));
+ artifact->addRepresentationUnknown(StringBlob::moveCreate(codeBuilder));
+
+ outArtifact.swap(artifact);
+ return SLANG_OK;
+ }
+ return SLANG_OK;
+ }
+ else
+ {
+ return emitEntryPointsSourceFromIR(outArtifact);
+ }
+}
+#if SLANG_VC
+#pragma warning(pop)
+#endif
+
+SlangResult CodeGenContext::emitPrecompiledDownstreamIR(ComPtr<IArtifact>& outArtifact)
+{
+ return _emitEntryPoints(outArtifact);
+}
+
+static String _getDisplayPath(DiagnosticSink* sink, SourceFile* sourceFile)
+{
+ if (sink->isFlagSet(DiagnosticSink::Flag::VerbosePath))
+ {
+ return sourceFile->calcVerbosePath();
+ }
+ else
+ {
+ return sourceFile->getPathInfo().foundPath;
+ }
+}
+
+String CodeGenContext::calcSourcePathForEntryPoints()
+{
+ String failureMode = "slang-generated";
+ if (getEntryPointCount() != 1)
+ return failureMode;
+ auto entryPointIndex = getSingleEntryPointIndex();
+ auto translationUnitRequest = findPassThroughTranslationUnit(entryPointIndex);
+ if (!translationUnitRequest)
+ return failureMode;
+
+ const auto& sourceFiles = translationUnitRequest->getSourceFiles();
+
+ auto sink = getSink();
+
+ const Index numSourceFiles = sourceFiles.getCount();
+
+ switch (numSourceFiles)
+ {
+ case 0:
+ return "unknown";
+ case 1:
+ return _getDisplayPath(sink, sourceFiles[0]);
+ default:
+ {
+ StringBuilder builder;
+ builder << _getDisplayPath(sink, sourceFiles[0]);
+ for (int i = 1; i < numSourceFiles; ++i)
+ {
+ builder << ";" << _getDisplayPath(sink, sourceFiles[i]);
+ }
+ return builder;
+ }
+ }
+}
+
+static RefPtr<ExtensionTracker> _newExtensionTracker(CodeGenTarget target)
+{
+ switch (target)
+ {
+ case CodeGenTarget::PTX:
+ case CodeGenTarget::CUDASource:
+ {
+ return new CUDAExtensionTracker;
+ }
+ case CodeGenTarget::SPIRV:
+ case CodeGenTarget::GLSL:
+ case CodeGenTarget::WGSL:
+ case CodeGenTarget::WGSLSPIRV:
+ case CodeGenTarget::WGSLSPIRVAssembly:
+ {
+ return new ShaderExtensionTracker;
+ }
+ default:
+ return nullptr;
+ }
+}
+
+static CodeGenTarget _getDefaultSourceForTarget(CodeGenTarget target)
+{
+ switch (target)
+ {
+ case CodeGenTarget::ShaderHostCallable:
+ case CodeGenTarget::ShaderSharedLibrary:
+ {
+ return CodeGenTarget::CPPSource;
+ }
+ case CodeGenTarget::HostHostCallable:
+ case CodeGenTarget::HostExecutable:
+ case CodeGenTarget::HostSharedLibrary:
+ {
+ return CodeGenTarget::HostCPPSource;
+ }
+ case CodeGenTarget::PTX:
+ return CodeGenTarget::CUDASource;
+ case CodeGenTarget::DXBytecode:
+ return CodeGenTarget::HLSL;
+ case CodeGenTarget::DXIL:
+ return CodeGenTarget::HLSL;
+ case CodeGenTarget::SPIRV:
+ return CodeGenTarget::GLSL;
+ case CodeGenTarget::MetalLib:
+ return CodeGenTarget::Metal;
+ case CodeGenTarget::WGSLSPIRV:
+ return CodeGenTarget::WGSL;
+ default:
+ break;
+ }
+ return CodeGenTarget::Unknown;
+}
+
+void trackGLSLTargetCaps(ShaderExtensionTracker* extensionTracker, CapabilitySet const& caps)
+{
+ for (auto& conjunctions : caps.getAtomSets())
+ {
+ for (auto atom : conjunctions)
+ {
+ switch (asAtom(atom))
+ {
+ default:
+ break;
+
+ case CapabilityAtom::glsl_spirv_1_0:
+ extensionTracker->requireSPIRVVersion(SemanticVersion(1, 0));
+ break;
+ case CapabilityAtom::glsl_spirv_1_1:
+ extensionTracker->requireSPIRVVersion(SemanticVersion(1, 1));
+ break;
+ case CapabilityAtom::glsl_spirv_1_2:
+ extensionTracker->requireSPIRVVersion(SemanticVersion(1, 2));
+ break;
+ case CapabilityAtom::glsl_spirv_1_3:
+ extensionTracker->requireSPIRVVersion(SemanticVersion(1, 3));
+ break;
+ case CapabilityAtom::glsl_spirv_1_4:
+ extensionTracker->requireSPIRVVersion(SemanticVersion(1, 4));
+ break;
+ case CapabilityAtom::glsl_spirv_1_5:
+ extensionTracker->requireSPIRVVersion(SemanticVersion(1, 5));
+ break;
+ case CapabilityAtom::glsl_spirv_1_6:
+ extensionTracker->requireSPIRVVersion(SemanticVersion(1, 6));
+ break;
+ }
+ }
+ }
+}
+
+// True if it's best to use 'emitted' source for complication. For a downstream compiler
+// that is not file based, this is always ok.
+///
+/// If the downstream compiler is file system based, we may want to just use the file that was
+/// passed to be compiled. That the downstream compiler can determine if it will then save the file
+/// or not based on if it's a match - and generally there will not be a match with emitted source.
+///
+/// This test is only used for pass through mode.
+static bool _useEmittedSource(
+ IDownstreamCompiler* compiler,
+ TranslationUnitRequest* translationUnit)
+{
+ // We only bother if it's a file based compiler.
+ if (compiler->isFileBased())
+ {
+ // It can only have *one* source file as otherwise we have to combine to make a new source
+ // file anyway
+ return translationUnit->getSourceArtifacts().getCount() != 1;
+ }
+ return true;
+}
+
+static bool _shouldSetEntryPointName(TargetProgram* targetProgram)
+{
+ if (!isKhronosTarget(targetProgram->getTargetReq()))
+ return true;
+ if (targetProgram->getOptionSet().getBoolOption(CompilerOptionName::VulkanUseEntryPointName))
+ return true;
+ return false;
+}
+
+static bool _isCPUHostTarget(CodeGenTarget target)
+{
+ auto desc = ArtifactDescUtil::makeDescForCompileTarget(asExternal(target));
+ return desc.style == ArtifactStyle::Host;
+}
+
+SlangResult CodeGenContext::emitWithDownstreamForEntryPoints(ComPtr<IArtifact>& outArtifact)
+{
+ outArtifact.setNull();
+
+ auto sink = getSink();
+ auto session = getSession();
+
+ CodeGenTarget sourceTarget = CodeGenTarget::None;
+ SourceLanguage sourceLanguage = SourceLanguage::Unknown;
+
+ auto target = getTargetFormat();
+ RefPtr<ExtensionTracker> extensionTracker = _newExtensionTracker(target);
+ PassThroughMode compilerType;
+
+ SliceAllocator allocator;
+
+ if (auto endToEndReq = isPassThroughEnabled())
+ {
+ compilerType = endToEndReq->m_passThrough;
+ }
+ else
+ {
+ // If we are not in pass through, lookup the default compiler for the emitted source type
+
+ // Get the default source codegen type for a given target
+ sourceTarget = _getDefaultSourceForTarget(target);
+ compilerType = (PassThroughMode)session->getDownstreamCompilerForTransition(
+ (SlangCompileTarget)sourceTarget,
+ (SlangCompileTarget)target);
+ // We should have a downstream compiler set at this point
+ if (compilerType == PassThroughMode::None)
+ {
+ auto sourceName = TypeTextUtil::getCompileTargetName(SlangCompileTarget(sourceTarget));
+ auto targetName = TypeTextUtil::getCompileTargetName(SlangCompileTarget(target));
+
+ sink->diagnose(
+ SourceLoc(),
+ Diagnostics::compilerNotDefinedForTransition,
+ sourceName,
+ targetName);
+ return SLANG_FAIL;
+ }
+ }
+
+ SLANG_ASSERT(compilerType != PassThroughMode::None);
+
+ // Get the required downstream compiler
+ IDownstreamCompiler* compiler = session->getOrLoadDownstreamCompiler(compilerType, sink);
+ if (!compiler)
+ {
+ auto compilerName = TypeTextUtil::getPassThroughAsHumanText((SlangPassThrough)compilerType);
+ sink->diagnose(SourceLoc(), Diagnostics::passThroughCompilerNotFound, compilerName);
+ return SLANG_FAIL;
+ }
+
+ Dictionary<String, String> preprocessorDefinitions;
+ List<String> includePaths;
+
+ typedef DownstreamCompileOptions CompileOptions;
+ CompileOptions options;
+
+ List<DownstreamCompileOptions::CapabilityVersion> requiredCapabilityVersions;
+ List<String> compilerSpecificArguments;
+ List<ComPtr<IArtifact>> libraries;
+ List<String> libraryPaths;
+
+ // Set compiler specific args
+ {
+ auto name = TypeTextUtil::getPassThroughName((SlangPassThrough)compilerType);
+ List<String> downstreamArgs = getTargetProgram()->getOptionSet().getDownstreamArgs(name);
+ for (const auto& arg : downstreamArgs)
+ {
+ // We special case some kinds of args, that can be handled directly
+ if (arg.startsWith("-I"))
+ {
+ // We handle the -I option, by just adding to the include paths
+ includePaths.add(arg.getUnownedSlice().tail(2));
+ }
+ else
+ {
+ compilerSpecificArguments.add(arg);
+ }
+ }
+ }
+
+ ComPtr<IArtifact> sourceArtifact;
+
+ /* This is more convoluted than the other scenarios, because when we invoke C/C++ compiler we
+ would ideally like to use the original file. We want to do this because we want includes
+ relative to the source file to work, and for that to work most easily we want to use the
+ original file, if there is one */
+ if (auto endToEndReq = isPassThroughEnabled())
+ {
+ // If we are pass through, we may need to set extension tracker state.
+ if (ShaderExtensionTracker* glslTracker = as<ShaderExtensionTracker>(extensionTracker))
+ {
+ trackGLSLTargetCaps(glslTracker, getTargetCaps());
+ }
+
+ auto translationUnit =
+ getPassThroughTranslationUnit(endToEndReq, getSingleEntryPointIndex());
+
+ // We are just passing thru, so it's whatever it originally was
+ sourceLanguage = translationUnit->sourceLanguage;
+
+ // TODO(JS): This seems like a bit of a hack
+ // That if a pass-through is being performed and the source language is Slang
+ // no downstream compiler knows how to deal with that, so probably means 'HLSL'
+ sourceLanguage =
+ (sourceLanguage == SourceLanguage::Slang) ? SourceLanguage::HLSL : sourceLanguage;
+ sourceTarget = CodeGenTarget(TypeConvertUtil::getCompileTargetFromSourceLanguage(
+ (SlangSourceLanguage)sourceLanguage));
+
+ // If it's pass through we accumulate the preprocessor definitions.
+ for (const auto& define :
+ endToEndReq->getOptionSet().getArray(CompilerOptionName::MacroDefine))
+ preprocessorDefinitions.add(define.stringValue, define.stringValue2);
+ for (const auto& define : translationUnit->preprocessorDefinitions)
+ preprocessorDefinitions.add(define);
+
+ {
+ /* TODO(JS): Not totally clear what options should be set here. If we are using the pass
+ through - then using say the defines/includes all makes total sense. If we are
+ generating C++ code from slang, then should we really be using these values -> aren't
+ they what is being set for the *slang* source, not for the C++ generated code. That
+ being the case it implies that there needs to be a mechanism (if there isn't already) to
+ specify such information on a particular pass/pass through etc.
+
+ On invoking DXC for example include paths do not appear to be set at all (even with
+ pass-through).
+ */
+
+ auto linkage = getLinkage();
+
+ // Add all the search paths
+
+ const auto searchDirectories = linkage->getSearchDirectories();
+ const SearchDirectoryList* searchList = &searchDirectories;
+ while (searchList)
+ {
+ for (const auto& searchDirectory : searchList->searchDirectories)
+ {
+ includePaths.add(searchDirectory.path);
+ }
+ searchList = searchList->parent;
+ }
+ }
+
+ // If emitted source is required, emit and set the path
+ if (_useEmittedSource(compiler, translationUnit))
+ {
+ CodeGenContext sourceCodeGenContext(this, sourceTarget, extensionTracker);
+
+ SLANG_RETURN_ON_FAIL(sourceCodeGenContext.emitEntryPointsSource(sourceArtifact));
+
+ // If it's not file based we can set an appropriate path name, and it doesn't matter if
+ // it doesn't exist on the file system. We set the name to the path as this will be used
+ // for downstream reporting.
+ auto sourcePath = calcSourcePathForEntryPoints();
+ sourceArtifact->setName(sourcePath.getBuffer());
+
+ sourceCodeGenContext.maybeDumpIntermediate(sourceArtifact);
+ }
+ else
+ {
+ // Special case if we have a single file, so that we pass the path, and the contents as
+ // is.
+ const auto& sourceArtifacts = translationUnit->getSourceArtifacts();
+ SLANG_ASSERT(sourceArtifacts.getCount() == 1);
+
+ sourceArtifact = sourceArtifacts[0];
+ SLANG_ASSERT(sourceArtifact);
+ }
+ }
+ else
+ {
+ CodeGenContext sourceCodeGenContext(this, sourceTarget, extensionTracker);
+
+ sourceCodeGenContext.removeAvailableInDownstreamIR = true;
+
+ SLANG_RETURN_ON_FAIL(sourceCodeGenContext.emitEntryPointsSource(sourceArtifact));
+ sourceCodeGenContext.maybeDumpIntermediate(sourceArtifact);
+
+ sourceLanguage = (SourceLanguage)TypeConvertUtil::getSourceLanguageFromTarget(
+ (SlangCompileTarget)sourceTarget);
+ }
+
+ if (sourceArtifact)
+ {
+ // Set the source artifacts
+ options.sourceArtifacts = makeSlice(sourceArtifact.readRef(), 1);
+ }
+
+ // Add any preprocessor definitions associated with the linkage
+ {
+ // TODO(JS): This is somewhat arguable - should defines passed to Slang really be
+ // passed to downstream compilers? It does appear consistent with the behavior if
+ // there is an endToEndReq.
+ //
+ // That said it's very convenient and provides way to control aspects
+ // of downstream compilation.
+
+ for (const auto& define :
+ getTargetProgram()->getOptionSet().getArray(CompilerOptionName::MacroDefine))
+ {
+ preprocessorDefinitions.addIfNotExists(define.stringValue, define.stringValue2);
+ }
+ }
+
+
+ // If we have an extension tracker, we may need to set options such as SPIR-V version
+ // and CUDA Shader Model.
+ if (extensionTracker)
+ {
+ // Look for the version
+ if (auto cudaTracker = as<CUDAExtensionTracker>(extensionTracker))
+ {
+ cudaTracker->finalize();
+
+ if (cudaTracker->m_smVersion.isSet())
+ {
+ DownstreamCompileOptions::CapabilityVersion version;
+ version.kind = DownstreamCompileOptions::CapabilityVersion::Kind::CUDASM;
+ version.version = cudaTracker->m_smVersion;
+
+ requiredCapabilityVersions.add(version);
+ }
+
+ if (cudaTracker->isBaseTypeRequired(BaseType::Half))
+ {
+ options.flags |= CompileOptions::Flag::EnableFloat16;
+ }
+ }
+ else if (ShaderExtensionTracker* glslTracker = as<ShaderExtensionTracker>(extensionTracker))
+ {
+ DownstreamCompileOptions::CapabilityVersion version;
+ version.kind = DownstreamCompileOptions::CapabilityVersion::Kind::SPIRV;
+ version.version = glslTracker->getSPIRVVersion();
+
+ requiredCapabilityVersions.add(version);
+ }
+ }
+
+ CapabilitySet targetCaps = getTargetCaps();
+ for (auto atomSets : targetCaps.getAtomSets())
+ {
+ for (auto atomVal : atomSets)
+ {
+ auto atom = CapabilityAtom(atomVal);
+ switch (atom)
+ {
+ default:
+ break;
+
+#define CASE(KIND, NAME, VERSION) \
+ case CapabilityAtom::NAME: \
+ requiredCapabilityVersions.add(DownstreamCompileOptions::CapabilityVersion{ \
+ DownstreamCompileOptions::CapabilityVersion::Kind::KIND, \
+ VERSION}); \
+ break
+
+ CASE(CUDASM, _cuda_sm_1_0, SemanticVersion(1, 0));
+ CASE(CUDASM, _cuda_sm_2_0, SemanticVersion(2, 0));
+ CASE(CUDASM, _cuda_sm_3_0, SemanticVersion(3, 0));
+ CASE(CUDASM, _cuda_sm_4_0, SemanticVersion(4, 0));
+ CASE(CUDASM, _cuda_sm_5_0, SemanticVersion(5, 0));
+ CASE(CUDASM, _cuda_sm_6_0, SemanticVersion(6, 0));
+ CASE(CUDASM, _cuda_sm_7_0, SemanticVersion(7, 0));
+ CASE(CUDASM, _cuda_sm_8_0, SemanticVersion(8, 0));
+ CASE(CUDASM, _cuda_sm_9_0, SemanticVersion(9, 0));
+
+#undef CASE
+ }
+ }
+ }
+
+ // Set the file sytem and source manager, as *may* be used by downstream compiler
+ options.fileSystemExt = getFileSystemExt();
+ options.sourceManager = getSourceManager();
+
+ // Set the source type
+ options.sourceLanguage = SlangSourceLanguage(sourceLanguage);
+
+ switch (target)
+ {
+ case CodeGenTarget::ShaderHostCallable:
+ case CodeGenTarget::ShaderSharedLibrary:
+ // Disable exceptions and security checks
+ options.flags &=
+ ~(CompileOptions::Flag::EnableExceptionHandling |
+ CompileOptions::Flag::EnableSecurityChecks);
+ break;
+ }
+
+ Profile profile;
+
+ if (compilerType == PassThroughMode::Fxc || compilerType == PassThroughMode::Dxc ||
+ compilerType == PassThroughMode::Glslang)
+ {
+ const auto entryPointIndices = getEntryPointIndices();
+ auto targetReq = getTargetReq();
+
+ const auto entryPointIndicesCount = entryPointIndices.getCount();
+
+ // Whole program means
+ // * can have 0-N entry points
+ // * 'doesn't build into an executable/kernel'
+ //
+ // So in some sense it is a library
+ if (getTargetProgram()->getOptionSet().getBoolOption(
+ CompilerOptionName::GenerateWholeProgram))
+ {
+ if (compilerType == PassThroughMode::Dxc)
+ {
+ // Can support no entry points on DXC because we can build libraries
+ profile =
+ Profile(getTargetProgram()->getOptionSet().getEnumOption<Profile::RawEnum>(
+ CompilerOptionName::Profile));
+ }
+ else
+ {
+ auto downstreamCompilerName =
+ TypeTextUtil::getPassThroughName((SlangPassThrough)compilerType);
+
+ sink->diagnose(
+ SourceLoc(),
+ Diagnostics::downstreamCompilerDoesntSupportWholeProgramCompilation,
+ downstreamCompilerName);
+ return SLANG_FAIL;
+ }
+ }
+ else if (entryPointIndicesCount == 1)
+ {
+ // All support a single entry point
+ const Index entryPointIndex = entryPointIndices[0];
+
+ auto entryPoint = getEntryPoint(entryPointIndex);
+ profile = getEffectiveProfile(entryPoint, targetReq);
+
+ if (_shouldSetEntryPointName(getTargetProgram()))
+ {
+ options.entryPointName = allocator.allocate(getText(entryPoint->getName()));
+ auto entryPointNameOverride =
+ getProgram()->getEntryPointNameOverride(entryPointIndex);
+ if (entryPointNameOverride.getLength() != 0)
+ {
+ options.entryPointName = allocator.allocate(entryPointNameOverride);
+ }
+ }
+ }
+ else
+ {
+ // We only support a single entry point on this target
+ SLANG_ASSERT(!"Can only compile with a single entry point on this target");
+ return SLANG_FAIL;
+ }
+
+ options.stage = SlangStage(profile.getStage());
+
+ if (compilerType == PassThroughMode::Dxc)
+ {
+ // We will enable the flag to generate proper code for 16 - bit types
+ // by default, as long as the user is requesting a sufficiently
+ // high shader model.
+ //
+ // TODO: Need to check that this is safe to enable in all cases,
+ // or if it will make a shader demand hardware features that
+ // aren't always present.
+ //
+ // TODO: Ideally the dxc back-end should be passed some information
+ // on the "capabilities" that were used and/or requested in the code.
+ //
+ if (profile.getVersion() >= ProfileVersion::DX_6_2)
+ {
+ options.flags |= CompileOptions::Flag::EnableFloat16;
+ }
+
+ // Set the matrix layout
+ options.matrixLayout =
+ (SlangMatrixLayoutMode)getTargetProgram()->getOptionSet().getMatrixLayoutMode();
+ }
+
+ // Set the profile
+ options.profileName = allocator.allocate(getHLSLProfileName(profile));
+ }
+
+ // If we aren't using LLVM 'host callable', we want downstream compile to produce a shared
+ // library
+ if (compilerType != PassThroughMode::LLVM &&
+ ArtifactDescUtil::makeDescForCompileTarget(asExternal(target)).kind ==
+ ArtifactKind::HostCallable)
+ {
+ target = CodeGenTarget::ShaderSharedLibrary;
+ }
+
+ if (!isPassThroughEnabled())
+ {
+ if (_isCPUHostTarget(target))
+ {
+ libraryPaths.add(Path::getParentDirectory(Path::getExecutablePath()));
+ libraryPaths.add(
+ Path::combine(Path::getParentDirectory(Path::getExecutablePath()), "../lib"));
+
+ // Set up the library artifact
+ auto artifact = Artifact::create(
+ ArtifactDesc::make(ArtifactKind::Library, Artifact::Payload::HostCPU),
+ toSlice("slang-rt"));
+
+ ComPtr<IOSFileArtifactRepresentation> fileRep(new OSFileArtifactRepresentation(
+ IOSFileArtifactRepresentation::Kind::NameOnly,
+ toSlice("slang-rt"),
+ nullptr));
+ artifact->addRepresentation(fileRep);
+
+ libraries.add(artifact);
+ }
+ }
+
+ options.targetType = (SlangCompileTarget)target;
+
+ // Need to configure for the compilation
+
+ {
+ auto linkage = getLinkage();
+
+ switch (getTargetProgram()->getOptionSet().getEnumOption<OptimizationLevel>(
+ CompilerOptionName::Optimization))
+ {
+ case OptimizationLevel::None:
+ options.optimizationLevel = DownstreamCompileOptions::OptimizationLevel::None;
+ break;
+ case OptimizationLevel::Default:
+ options.optimizationLevel = DownstreamCompileOptions::OptimizationLevel::Default;
+ break;
+ case OptimizationLevel::High:
+ options.optimizationLevel = DownstreamCompileOptions::OptimizationLevel::High;
+ break;
+ case OptimizationLevel::Maximal:
+ options.optimizationLevel = DownstreamCompileOptions::OptimizationLevel::Maximal;
+ break;
+ default:
+ SLANG_ASSERT(!"Unhandled optimization level");
+ break;
+ }
+
+ switch (getTargetProgram()->getOptionSet().getEnumOption<DebugInfoLevel>(
+ CompilerOptionName::DebugInformation))
+ {
+ case DebugInfoLevel::None:
+ options.debugInfoType = DownstreamCompileOptions::DebugInfoType::None;
+ break;
+ case DebugInfoLevel::Minimal:
+ options.debugInfoType = DownstreamCompileOptions::DebugInfoType::Minimal;
+ break;
+
+ case DebugInfoLevel::Standard:
+ options.debugInfoType = DownstreamCompileOptions::DebugInfoType::Standard;
+ break;
+ case DebugInfoLevel::Maximal:
+ options.debugInfoType = DownstreamCompileOptions::DebugInfoType::Maximal;
+ break;
+ default:
+ SLANG_ASSERT(!"Unhandled debug level");
+ break;
+ }
+
+ switch (getTargetProgram()->getOptionSet().getEnumOption<FloatingPointMode>(
+ CompilerOptionName::FloatingPointMode))
+ {
+ case FloatingPointMode::Default:
+ options.floatingPointMode = DownstreamCompileOptions::FloatingPointMode::Default;
+ break;
+ case FloatingPointMode::Precise:
+ options.floatingPointMode = DownstreamCompileOptions::FloatingPointMode::Precise;
+ break;
+ case FloatingPointMode::Fast:
+ options.floatingPointMode = DownstreamCompileOptions::FloatingPointMode::Fast;
+ break;
+ default:
+ SLANG_ASSERT(!"Unhandled floating point mode");
+ }
+
+ if (getTargetProgram()->getOptionSet().hasOption(CompilerOptionName::DenormalModeFp16))
+ {
+ switch (getTargetProgram()->getOptionSet().getEnumOption<FloatingPointDenormalMode>(
+ CompilerOptionName::DenormalModeFp16))
+ {
+ case FloatingPointDenormalMode::Any:
+ options.denormalModeFp16 = DownstreamCompileOptions::FloatingPointDenormalMode::Any;
+ break;
+ case FloatingPointDenormalMode::Preserve:
+ options.denormalModeFp16 =
+ DownstreamCompileOptions::FloatingPointDenormalMode::Preserve;
+ break;
+ case FloatingPointDenormalMode::FlushToZero:
+ options.denormalModeFp16 =
+ DownstreamCompileOptions::FloatingPointDenormalMode::FlushToZero;
+ break;
+ default:
+ SLANG_ASSERT(!"Unhandled fp16 denormal handling mode");
+ }
+ }
+
+ if (getTargetProgram()->getOptionSet().hasOption(CompilerOptionName::DenormalModeFp32))
+ {
+ switch (getTargetProgram()->getOptionSet().getEnumOption<FloatingPointDenormalMode>(
+ CompilerOptionName::DenormalModeFp32))
+ {
+ case FloatingPointDenormalMode::Any:
+ options.denormalModeFp32 = DownstreamCompileOptions::FloatingPointDenormalMode::Any;
+ break;
+ case FloatingPointDenormalMode::Preserve:
+ options.denormalModeFp32 =
+ DownstreamCompileOptions::FloatingPointDenormalMode::Preserve;
+ break;
+ case FloatingPointDenormalMode::FlushToZero:
+ options.denormalModeFp32 =
+ DownstreamCompileOptions::FloatingPointDenormalMode::FlushToZero;
+ break;
+ default:
+ SLANG_ASSERT(!"Unhandled fp32 denormal handling mode");
+ }
+ }
+
+ if (getTargetProgram()->getOptionSet().hasOption(CompilerOptionName::DenormalModeFp64))
+ {
+ switch (getTargetProgram()->getOptionSet().getEnumOption<FloatingPointDenormalMode>(
+ CompilerOptionName::DenormalModeFp64))
+ {
+ case FloatingPointDenormalMode::Any:
+ options.denormalModeFp64 = DownstreamCompileOptions::FloatingPointDenormalMode::Any;
+ break;
+ case FloatingPointDenormalMode::Preserve:
+ options.denormalModeFp64 =
+ DownstreamCompileOptions::FloatingPointDenormalMode::Preserve;
+ break;
+ case FloatingPointDenormalMode::FlushToZero:
+ options.denormalModeFp64 =
+ DownstreamCompileOptions::FloatingPointDenormalMode::FlushToZero;
+ break;
+ default:
+ SLANG_ASSERT(!"Unhandled fp64 denormal handling mode");
+ }
+ }
+
+ {
+ // We need to look at the stage of the entry point(s) we are
+ // being asked to compile, since this will determine the
+ // "pipeline" that the result should be compiled for (e.g.,
+ // compute vs. ray tracing).
+ //
+ // TODO: This logic is kind of messy in that it assumes
+ // a program to be compiled will only contain kernels for
+ // a single pipeline type, but that invariant isn't expressed
+ // at all in the front-end today. It also has no error
+ // checking for the case where there are conflicts.
+ //
+ // HACK: Right now none of the above concerns matter
+ // because we always perform code generation on a single
+ // entry point at a time.
+ //
+ Index entryPointCount = getEntryPointCount();
+ for (Index ee = 0; ee < entryPointCount; ++ee)
+ {
+ auto stage = getEntryPoint(ee)->getStage();
+ switch (stage)
+ {
+ default:
+ break;
+
+ case Stage::Compute:
+ options.pipelineType = DownstreamCompileOptions::PipelineType::Compute;
+ break;
+
+ case Stage::Vertex:
+ case Stage::Hull:
+ case Stage::Domain:
+ case Stage::Geometry:
+ case Stage::Fragment:
+ options.pipelineType = DownstreamCompileOptions::PipelineType::Rasterization;
+ break;
+
+ case Stage::RayGeneration:
+ case Stage::Intersection:
+ case Stage::AnyHit:
+ case Stage::ClosestHit:
+ case Stage::Miss:
+ case Stage::Callable:
+ options.pipelineType = DownstreamCompileOptions::PipelineType::RayTracing;
+ break;
+ }
+ }
+ }
+
+ // Add all the search paths (as calculated earlier - they will only be set if this is a pass
+ // through else will be empty)
+ options.includePaths = allocator.allocate(includePaths);
+
+ // Add the specified defines (as calculated earlier - they will only be set if this is a
+ // pass through else will be empty)
+ {
+ const auto count = preprocessorDefinitions.getCount();
+ auto dst = allocator.getArena().allocateArray<DownstreamCompileOptions::Define>(count);
+
+ Index i = 0;
+
+ for (const auto& [defKey, defValue] : preprocessorDefinitions)
+ {
+ auto& define = dst[i];
+
+ define.nameWithSig = allocator.allocate(defKey);
+ define.value = allocator.allocate(defValue);
+
+ ++i;
+ }
+ options.defines = makeSlice(dst, count);
+ }
+
+ // Add all of the module libraries
+ libraries.addRange(linkage->m_libModules.getBuffer(), linkage->m_libModules.getCount());
+ }
+
+ auto program = getProgram();
+
+ // Load embedded precompiled libraries from IR into library artifacts
+ program->enumerateIRModules(
+ [&](IRModule* irModule)
+ {
+ for (auto globalInst : irModule->getModuleInst()->getChildren())
+ {
+ if (target == CodeGenTarget::DXILAssembly || target == CodeGenTarget::DXIL)
+ {
+ if (auto inst = as<IREmbeddedDownstreamIR>(globalInst))
+ {
+ if (inst->getTarget() == CodeGenTarget::DXIL)
+ {
+ auto slice = inst->getBlob()->getStringSlice();
+ ArtifactDesc desc =
+ ArtifactDescUtil::makeDescForCompileTarget(SLANG_DXIL);
+ desc.kind = ArtifactKind::Library;
+
+ auto library = ArtifactUtil::createArtifact(desc);
+
+ library->addRepresentationUnknown(StringBlob::create(slice));
+ libraries.add(library);
+ }
+ }
+ }
+ }
+ });
+
+ options.compilerSpecificArguments = allocator.allocate(compilerSpecificArguments);
+ options.requiredCapabilityVersions = SliceUtil::asSlice(requiredCapabilityVersions);
+ options.libraries = SliceUtil::asSlice(libraries);
+ options.libraryPaths = allocator.allocate(libraryPaths);
+
+ if (m_targetProfile.getFamily() == ProfileFamily::DX)
+ {
+ options.enablePAQ = m_targetProfile.getVersion() >= ProfileVersion::DX_6_7;
+ }
+
+ // Compile
+ ComPtr<IArtifact> artifact;
+ auto downstreamStartTime = std::chrono::high_resolution_clock::now();
+ SLANG_RETURN_ON_FAIL(compiler->compile(options, artifact.writeRef()));
+ auto downstreamElapsedTime =
+ (std::chrono::high_resolution_clock::now() - downstreamStartTime).count() * 0.000000001;
+ getSession()->addDownstreamCompileTime(downstreamElapsedTime);
+
+ SLANG_RETURN_ON_FAIL(passthroughDownstreamDiagnostics(getSink(), compiler, artifact));
+
+ // Copy over all of the information associated with the source into the output
+ if (sourceArtifact)
+ {
+ for (auto associatedArtifact : sourceArtifact->getAssociated())
+ {
+ artifact->addAssociated(associatedArtifact);
+ }
+ }
+
+ // Set the artifact
+ outArtifact.swap(artifact);
+ return SLANG_OK;
+}
+
+SlangResult emitSPIRVForEntryPointsDirectly(
+ CodeGenContext* codeGenContext,
+ ComPtr<IArtifact>& outArtifact);
+
+SlangResult emitHostVMCode(CodeGenContext* codeGenContext, ComPtr<IArtifact>& outArtifact);
+
+static CodeGenTarget _getIntermediateTarget(CodeGenTarget target)
+{
+ switch (target)
+ {
+ case CodeGenTarget::DXBytecodeAssembly:
+ return CodeGenTarget::DXBytecode;
+ case CodeGenTarget::DXILAssembly:
+ return CodeGenTarget::DXIL;
+ case CodeGenTarget::SPIRVAssembly:
+ return CodeGenTarget::SPIRV;
+ case CodeGenTarget::WGSLSPIRVAssembly:
+ return CodeGenTarget::WGSLSPIRV;
+ default:
+ return CodeGenTarget::None;
+ }
+}
+
+IArtifact* getSeparateDbgArtifact(IArtifact* artifact)
+{
+ if (!artifact)
+ return nullptr;
+
+ // The first associated artifact of kind ObjectCode and SPIRV payload should be the debug
+ // artifact.
+ for (auto* associated : artifact->getAssociated())
+ {
+ auto desc = associated->getDesc();
+ if (desc.kind == ArtifactKind::ObjectCode && desc.payload == ArtifactPayload::SPIRV)
+ return associated;
+ }
+
+ return nullptr;
+}
+
+/// Function to simplify the logic around emitting, and dissassembling
+SlangResult CodeGenContext::_emitEntryPoints(ComPtr<IArtifact>& outArtifact)
+{
+ auto target = getTargetFormat();
+ switch (target)
+ {
+ case CodeGenTarget::SPIRVAssembly:
+ case CodeGenTarget::DXBytecodeAssembly:
+ case CodeGenTarget::DXILAssembly:
+ case CodeGenTarget::MetalLibAssembly:
+ case CodeGenTarget::WGSLSPIRVAssembly:
+ {
+ // First compile to an intermediate target for the corresponding binary format.
+ const CodeGenTarget intermediateTarget = _getIntermediateTarget(target);
+ CodeGenContext intermediateContext(this, intermediateTarget);
+
+ ComPtr<IArtifact> intermediateArtifact;
+
+ SLANG_RETURN_ON_FAIL(intermediateContext._emitEntryPoints(intermediateArtifact));
+ intermediateContext.maybeDumpIntermediate(intermediateArtifact);
+
+ // Then disassemble the intermediate binary result to get the desired output
+ // Output the disassemble
+ ComPtr<IArtifact> disassemblyArtifact;
+ SLANG_RETURN_ON_FAIL(ArtifactOutputUtil::dissassembleWithDownstream(
+ getSession(),
+ intermediateArtifact,
+ getSink(),
+ disassemblyArtifact.writeRef()));
+
+ // Also disassemble the debug artifact if one exists.
+ auto debugArtifact = getSeparateDbgArtifact(intermediateArtifact);
+ ComPtr<IArtifact> disassemblyDebugArtifact;
+ if (debugArtifact)
+ {
+ SLANG_RETURN_ON_FAIL(ArtifactOutputUtil::dissassembleWithDownstream(
+ getSession(),
+ debugArtifact,
+ getSink(),
+ disassemblyDebugArtifact.writeRef()));
+ disassemblyDebugArtifact->setName(debugArtifact->getName());
+
+ // The disassembly needs both the metadata for the debug build identifier
+ // and the debug spirv to be associated with is.
+ for (auto associated : intermediateArtifact->getAssociated())
+ {
+ if (associated->getDesc().payload == ArtifactPayload::Metadata ||
+ associated->getDesc().payload == ArtifactPayload::PostEmitMetadata)
+ {
+ disassemblyArtifact->addAssociated(associated);
+ break;
+ }
+ }
+ disassemblyArtifact->addAssociated(disassemblyDebugArtifact);
+ }
+
+ outArtifact.swap(disassemblyArtifact);
+ return SLANG_OK;
+ }
+ case CodeGenTarget::SPIRV:
+ if (getTargetProgram()->getOptionSet().shouldEmitSPIRVDirectly())
+ {
+ SLANG_RETURN_ON_FAIL(emitSPIRVForEntryPointsDirectly(this, outArtifact));
+ return SLANG_OK;
+ }
+ [[fallthrough]];
+ case CodeGenTarget::DXIL:
+ case CodeGenTarget::DXBytecode:
+ case CodeGenTarget::MetalLib:
+ case CodeGenTarget::PTX:
+ case CodeGenTarget::ShaderHostCallable:
+ case CodeGenTarget::ShaderSharedLibrary:
+ case CodeGenTarget::HostExecutable:
+ case CodeGenTarget::HostHostCallable:
+ case CodeGenTarget::HostSharedLibrary:
+ case CodeGenTarget::WGSLSPIRV:
+ SLANG_RETURN_ON_FAIL(emitWithDownstreamForEntryPoints(outArtifact));
+ return SLANG_OK;
+ case CodeGenTarget::HostVM:
+ SLANG_RETURN_ON_FAIL(emitHostVMCode(this, outArtifact));
+ return SLANG_OK;
+ default:
+ break;
+ }
+
+ return SLANG_FAIL;
+}
+
+// Helper class for recording compile time.
+struct CompileTimerRAII
+{
+ std::chrono::high_resolution_clock::time_point startTime;
+ Session* session;
+ CompileTimerRAII(Session* inSession)
+ {
+ startTime = std::chrono::high_resolution_clock::now();
+ session = inSession;
+ }
+ ~CompileTimerRAII()
+ {
+ double elapsedTime = std::chrono::duration_cast<std::chrono::microseconds>(
+ std::chrono::high_resolution_clock::now() - startTime)
+ .count() /
+ 1e6;
+ session->addTotalCompileTime(elapsedTime);
+ }
+};
+
+// Do emit logic for a zero or more entry points
+SlangResult CodeGenContext::emitEntryPoints(ComPtr<IArtifact>& outArtifact)
+{
+ CompileTimerRAII recordCompileTime(getSession());
+
+ auto target = getTargetFormat();
+
+ switch (target)
+ {
+ case CodeGenTarget::SPIRVAssembly:
+ case CodeGenTarget::DXBytecodeAssembly:
+ case CodeGenTarget::DXILAssembly:
+ case CodeGenTarget::SPIRV:
+ case CodeGenTarget::DXIL:
+ case CodeGenTarget::DXBytecode:
+ case CodeGenTarget::MetalLib:
+ case CodeGenTarget::MetalLibAssembly:
+ case CodeGenTarget::PTX:
+ case CodeGenTarget::HostHostCallable:
+ case CodeGenTarget::ShaderHostCallable:
+ case CodeGenTarget::ShaderSharedLibrary:
+ case CodeGenTarget::HostExecutable:
+ case CodeGenTarget::HostSharedLibrary:
+ case CodeGenTarget::WGSLSPIRVAssembly:
+ case CodeGenTarget::HostVM:
+ {
+ SLANG_RETURN_ON_FAIL(_emitEntryPoints(outArtifact));
+
+ maybeDumpIntermediate(outArtifact);
+ return SLANG_OK;
+ }
+ break;
+ case CodeGenTarget::GLSL:
+ case CodeGenTarget::HLSL:
+ case CodeGenTarget::CUDASource:
+ case CodeGenTarget::CPPSource:
+ case CodeGenTarget::HostCPPSource:
+ case CodeGenTarget::PyTorchCppBinding:
+ case CodeGenTarget::CSource:
+ case CodeGenTarget::Metal:
+ case CodeGenTarget::WGSL:
+ {
+ RefPtr<ExtensionTracker> extensionTracker = _newExtensionTracker(target);
+
+ CodeGenContext subContext(this, target, extensionTracker);
+
+ ComPtr<IArtifact> sourceArtifact;
+
+ SLANG_RETURN_ON_FAIL(subContext.emitEntryPointsSource(sourceArtifact));
+
+ subContext.maybeDumpIntermediate(sourceArtifact);
+ outArtifact = sourceArtifact;
+ return SLANG_OK;
+ }
+ break;
+
+ case CodeGenTarget::None:
+ // The user requested no output
+ return SLANG_OK;
+
+ // Note(tfoley): We currently hit this case when compiling the core module
+ case CodeGenTarget::Unknown:
+ return SLANG_OK;
+
+ default:
+ SLANG_UNEXPECTED("unhandled code generation target");
+ break;
+ }
+ return SLANG_FAIL;
+}
+
+void CodeGenContext::_dumpIntermediateMaybeWithAssembly(IArtifact* artifact)
+{
+ _dumpIntermediate(artifact);
+
+ ComPtr<IArtifact> assembly;
+ ArtifactOutputUtil::maybeDisassemble(getSession(), artifact, nullptr, assembly);
+
+ if (assembly)
+ {
+ _dumpIntermediate(assembly);
+ }
+}
+
+void CodeGenContext::_dumpIntermediate(IArtifact* artifact)
+{
+ ComPtr<ISlangBlob> blob;
+ if (SLANG_FAILED(artifact->loadBlob(ArtifactKeep::No, blob.writeRef())))
+ {
+ return;
+ }
+ _dumpIntermediate(artifact->getDesc(), blob->getBufferPointer(), blob->getBufferSize());
+}
+
+void CodeGenContext::_dumpIntermediate(const ArtifactDesc& desc, void const* data, size_t size)
+{
+ // Try to generate a unique ID for the file to dump,
+ // even in cases where there might be multiple threads
+ // doing compilation.
+ //
+ // This is primarily a debugging aid, so we don't
+ // really need/want to do anything too elaborate
+
+ static std::atomic<uint32_t> counter(0);
+
+ const uint32_t id = ++counter;
+
+ // Just use the counter for the 'base name'
+ StringBuilder basename;
+
+ // Add the prefix
+ basename << getIntermediateDumpPrefix();
+
+ // Add the id
+ basename << int(id);
+
+ // Work out the filename based on the desc and the basename
+ StringBuilder filename;
+ ArtifactDescUtil::calcNameForDesc(desc, basename.getUnownedSlice(), filename);
+
+ // If didn't produce a filename, use basename with .unknown extension
+ if (filename.getLength() == 0)
+ {
+ filename = basename;
+ filename << ".unknown";
+ }
+
+ // Write to a file
+ ArtifactOutputUtil::writeToFile(desc, data, size, filename);
+}
+
+void CodeGenContext::maybeDumpIntermediate(IArtifact* artifact)
+{
+ if (!shouldDumpIntermediates())
+ return;
+
+
+ _dumpIntermediateMaybeWithAssembly(artifact);
+}
+
+IRDumpOptions CodeGenContext::getIRDumpOptions()
+{
+ if (auto endToEndReq = isEndToEndCompile())
+ {
+ return endToEndReq->getFrontEndReq()->m_irDumpOptions;
+ }
+ return IRDumpOptions();
+}
+
+bool CodeGenContext::shouldValidateIR()
+{
+ return getTargetProgram()->getOptionSet().getBoolOption(CompilerOptionName::ValidateIr);
+}
+
+bool CodeGenContext::shouldSkipSPIRVValidation()
+{
+ return getTargetProgram()->getOptionSet().getBoolOption(
+ CompilerOptionName::SkipSPIRVValidation);
+}
+
+bool CodeGenContext::shouldDumpIR()
+{
+ return getTargetProgram()->getOptionSet().getBoolOption(CompilerOptionName::DumpIr);
+}
+
+bool CodeGenContext::shouldSkipDownstreamLinking()
+{
+ return getTargetProgram()->getOptionSet().getBoolOption(
+ CompilerOptionName::SkipDownstreamLinking);
+}
+
+bool CodeGenContext::shouldReportCheckpointIntermediates()
+{
+ return getTargetProgram()->getOptionSet().getBoolOption(
+ CompilerOptionName::ReportCheckpointIntermediates);
+}
+
+bool CodeGenContext::shouldDumpIntermediates()
+{
+ return getTargetProgram()->getOptionSet().getBoolOption(CompilerOptionName::DumpIntermediates);
+}
+
+bool CodeGenContext::shouldTrackLiveness()
+{
+ return getTargetProgram()->getOptionSet().getBoolOption(CompilerOptionName::TrackLiveness);
+}
+
+String CodeGenContext::getIntermediateDumpPrefix()
+{
+ return getTargetProgram()->getOptionSet().getStringOption(
+ CompilerOptionName::DumpIntermediatePrefix);
+}
+
+bool CodeGenContext::getUseUnknownImageFormatAsDefault()
+{
+ return getTargetProgram()->getOptionSet().getBoolOption(
+ CompilerOptionName::DefaultImageFormatUnknown);
+}
+
+bool CodeGenContext::isSpecializationDisabled()
+{
+ return getTargetProgram()->getOptionSet().getBoolOption(
+ CompilerOptionName::DisableSpecialization);
+}
+
+SlangResult CodeGenContext::requireTranslationUnitSourceFiles()
+{
+ if (auto endToEndReq = isPassThroughEnabled())
+ {
+ for (auto entryPointIndex : getEntryPointIndices())
+ {
+ auto translationUnit = getPassThroughTranslationUnit(endToEndReq, entryPointIndex);
+ SLANG_ASSERT(translationUnit);
+ /// Make sure we have the source files
+ SLANG_RETURN_ON_FAIL(translationUnit->requireSourceFiles());
+ }
+ }
+
+ return SLANG_OK;
+}
+
+} // namespace Slang
diff --git a/source/slang/slang-code-gen.h b/source/slang/slang-code-gen.h
new file mode 100644
index 000000000..81271abad
--- /dev/null
+++ b/source/slang/slang-code-gen.h
@@ -0,0 +1,263 @@
+// slang-code-gen.h
+#pragma once
+
+//
+// This file defines the `CodeGenContext` type and related
+// utilities. The `CodeGenContext` is used to bundle together
+// the information needed by the back-end of the Slang
+// compiler, and to help ensure that the back-end is not able
+// to access (and thus rely on) information that should only
+// be available to the front-end. Maintaining that split
+// ensures that results are consistent between end-to-end
+// and seaprate-compilation scenarios.
+//
+
+#include "slang-entry-point.h"
+#include "slang-session.h"
+#include "slang-target-program.h"
+
+namespace Slang
+{
+
+/// A back-end-specific object to track optional feaures/capabilities/extensions
+/// that are discovered to be used by a program/kernel as part of code generation.
+class ExtensionTracker : public RefObject
+{
+ // TODO: The existence of this type is evidence of a design/architecture problem.
+ //
+ // A better formulation of things requires a few key changes:
+ //
+ // 1. All optional capabilities need to be enumerated as part of the `CapabilitySet`
+ // system, so that they can be reasoned about uniformly across different targets
+ // and different layers of the compiler.
+ //
+ // 2. The front-end should be responsible for either or both of:
+ //
+ // * Checking that `public` or otherwise externally-visible items (declarations/definitions)
+ // explicitly declare the capabilities they require, and that they only ever
+ // make use of items that are comatible with those required capabilities.
+ //
+ // * Inferring the capabilities required by items that are not externally visible,
+ // and attaching those capabilities explicit as a modifier or other synthesized AST node.
+ //
+ // 3. The capabilities required by a given `ComponentType` and its entry points should be
+ // explicitly know-able, and they should be something we can compare to the capabilities
+ // of a code generation target *before* back-end code generation is started. We should be
+ // able to issue error messages around lacking capabilities in a way the user can understand,
+ // in terms of the high-level-language entities.
+
+public:
+};
+
+struct RequiredLoweringPassSet
+{
+ bool debugInfo;
+ bool resultType;
+ bool optionalType;
+ bool enumType;
+ bool combinedTextureSamplers;
+ bool reinterpret;
+ bool generics;
+ bool bindExistential;
+ bool autodiff;
+ bool derivativePyBindWrapper;
+ bool bitcast;
+ bool existentialTypeLayout;
+ bool bindingQuery;
+ bool meshOutput;
+ bool higherOrderFunc;
+ bool globalVaryingVar;
+ bool glslSSBO;
+ bool byteAddressBuffer;
+ bool dynamicResource;
+ bool dynamicResourceHeap;
+ bool resolveVaryingInputRef;
+ bool specializeStageSwitch;
+ bool missingReturn;
+ bool nonVectorCompositeSelect;
+};
+
+/// A context for code generation in the compiler back-end
+struct CodeGenContext
+{
+public:
+ typedef List<Index> EntryPointIndices;
+
+ struct Shared
+ {
+ public:
+ Shared(
+ TargetProgram* targetProgram,
+ EntryPointIndices const& entryPointIndices,
+ DiagnosticSink* sink,
+ EndToEndCompileRequest* endToEndReq)
+ : targetProgram(targetProgram)
+ , entryPointIndices(entryPointIndices)
+ , sink(sink)
+ , endToEndReq(endToEndReq)
+ {
+ }
+
+ // Shared(
+ // TargetProgram* targetProgram,
+ // EndToEndCompileRequest* endToEndReq);
+
+ TargetProgram* targetProgram = nullptr;
+ EntryPointIndices entryPointIndices;
+ DiagnosticSink* sink = nullptr;
+ EndToEndCompileRequest* endToEndReq = nullptr;
+ };
+
+ CodeGenContext(Shared* shared)
+ : m_shared(shared)
+ , m_targetFormat(shared->targetProgram->getTargetReq()->getTarget())
+ , m_targetProfile(shared->targetProgram->getOptionSet().getProfile())
+ {
+ }
+
+ CodeGenContext(
+ CodeGenContext* base,
+ CodeGenTarget targetFormat,
+ ExtensionTracker* extensionTracker = nullptr)
+ : m_shared(base->m_shared)
+ , m_targetFormat(targetFormat)
+ , m_extensionTracker(extensionTracker)
+ {
+ }
+
+ /// Get the diagnostic sink
+ DiagnosticSink* getSink() { return m_shared->sink; }
+
+ TargetProgram* getTargetProgram() { return m_shared->targetProgram; }
+
+ EntryPointIndices const& getEntryPointIndices() { return m_shared->entryPointIndices; }
+
+ CodeGenTarget getTargetFormat() { return m_targetFormat; }
+
+ ExtensionTracker* getExtensionTracker() { return m_extensionTracker; }
+
+ TargetRequest* getTargetReq() { return getTargetProgram()->getTargetReq(); }
+
+ CapabilitySet getTargetCaps() { return getTargetReq()->getTargetCaps(); }
+
+ CodeGenTarget getFinalTargetFormat() { return getTargetReq()->getTarget(); }
+
+ ComponentType* getProgram() { return getTargetProgram()->getProgram(); }
+
+ Linkage* getLinkage() { return getProgram()->getLinkage(); }
+
+ Session* getSession() { return getLinkage()->getSessionImpl(); }
+
+ /// Get the source manager
+ SourceManager* getSourceManager() { return getLinkage()->getSourceManager(); }
+
+ ISlangFileSystemExt* getFileSystemExt() { return getLinkage()->getFileSystemExt(); }
+
+ EndToEndCompileRequest* isEndToEndCompile() { return m_shared->endToEndReq; }
+
+ EndToEndCompileRequest* isPassThroughEnabled();
+
+ Count getEntryPointCount() { return getEntryPointIndices().getCount(); }
+
+ EntryPoint* getEntryPoint(Index index) { return getProgram()->getEntryPoint(index); }
+
+ Index getSingleEntryPointIndex()
+ {
+ SLANG_ASSERT(getEntryPointCount() == 1);
+ return getEntryPointIndices()[0];
+ }
+
+ //
+
+ IRDumpOptions getIRDumpOptions();
+
+ bool shouldValidateIR();
+ bool shouldDumpIR();
+ bool shouldReportCheckpointIntermediates();
+
+ bool shouldTrackLiveness();
+
+ bool shouldDumpIntermediates();
+ String getIntermediateDumpPrefix();
+
+ bool getUseUnknownImageFormatAsDefault();
+
+ bool isSpecializationDisabled();
+
+ bool shouldSkipSPIRVValidation();
+
+ SlangResult requireTranslationUnitSourceFiles();
+
+ //
+
+ SlangResult emitEntryPoints(ComPtr<IArtifact>& outArtifact);
+
+ SlangResult emitPrecompiledDownstreamIR(ComPtr<IArtifact>& outArtifact);
+
+ void maybeDumpIntermediate(IArtifact* artifact);
+
+ // Used to cause instructions available in precompiled blobs to be
+ // removed between IR linking and target source generation.
+ bool removeAvailableInDownstreamIR = false;
+
+ // Determines if program level compilation like getTargetCode() or getEntryPointCode()
+ // should return a fully linked downstream program or just the glue SPIR-V/DXIL that
+ // imports and uses the precompiled SPIR-V/DXIL from constituent modules.
+ // This is a no-op if modules are not precompiled.
+ bool shouldSkipDownstreamLinking();
+
+ RequiredLoweringPassSet& getRequiredLoweringPassSet() { return m_requiredLoweringPassSet; }
+
+protected:
+ CodeGenTarget m_targetFormat = CodeGenTarget::Unknown;
+ Profile m_targetProfile;
+ ExtensionTracker* m_extensionTracker = nullptr;
+
+ // To improve the performance of our backend, we will try to avoid running
+ // passes related to features not used in the user code.
+ // To do so, we will scan the IR module once, and determine which passes are needed
+ // based on the instructions used in the IR module.
+ // This will allow us to skip running passes that are not needed, without having to
+ // run all the passes only to find out that no work is needed.
+ // This is especially important for the performance of the backend, as some passes
+ // have an initialization cost (such as building reference graphs or DOM trees) that
+ // can be expensive.
+ RequiredLoweringPassSet m_requiredLoweringPassSet;
+
+ /// Will output assembly as well as the artifact if appropriate for the artifact type for
+ /// assembly output and conversion is possible
+ void _dumpIntermediateMaybeWithAssembly(IArtifact* artifact);
+
+ void _dumpIntermediate(IArtifact* artifact);
+ void _dumpIntermediate(const ArtifactDesc& desc, void const* data, size_t size);
+
+ /* Emits entry point source taking into account if a pass-through or not. Uses 'targetFormat' to
+ determine the target (not targetReq) */
+ SlangResult emitEntryPointsSource(ComPtr<IArtifact>& outArtifact);
+
+ SlangResult emitEntryPointsSourceFromIR(ComPtr<IArtifact>& outArtifact);
+
+ SlangResult emitWithDownstreamForEntryPoints(ComPtr<IArtifact>& outArtifact);
+
+ /* Determines a suitable filename to identify the input for a given entry point being compiled.
+ If the end-to-end compile is a pass-through case, will attempt to find the (unique) source file
+ pathname for the translation unit containing the entry point at `entryPointIndex.
+ If the compilation is not in a pass-through case, then always returns `"slang-generated"`.
+ @param endToEndReq The end-to-end compile request which might be using pass-through compilation
+ @param entryPointIndex The index of the entry point to compute a filename for.
+ @return the appropriate source filename */
+ String calcSourcePathForEntryPoints();
+
+ TranslationUnitRequest* findPassThroughTranslationUnit(Int entryPointIndex);
+
+
+ SlangResult _emitEntryPoints(ComPtr<IArtifact>& outArtifact);
+
+private:
+ Shared* m_shared = nullptr;
+};
+
+// TODO: The "artifact" system is a scourge.
+IArtifact* getSeparateDbgArtifact(IArtifact* artifact);
+
+} // namespace Slang
diff --git a/source/slang/slang-compile-request.cpp b/source/slang/slang-compile-request.cpp
new file mode 100644
index 000000000..6cbf79f96
--- /dev/null
+++ b/source/slang/slang-compile-request.cpp
@@ -0,0 +1,703 @@
+// slang-compile-request.cpp
+#include "slang-compile-request.h"
+
+#include "../core/slang-performance-profiler.h"
+#include "compiler-core/slang-artifact-desc-util.h"
+#include "compiler-core/slang-artifact-util.h"
+#include "slang-ast-dump.h"
+#include "slang-check-impl.h"
+#include "slang-compiler.h"
+#include "slang-emit-source-writer.h"
+#include "slang-lower-to-ir.h"
+#include "slang-parser.h"
+#include "slang-serialize-container.h"
+
+namespace Slang
+{
+//
+// FrontEndEntryPointRequest
+//
+
+FrontEndEntryPointRequest::FrontEndEntryPointRequest(
+ FrontEndCompileRequest* compileRequest,
+ int translationUnitIndex,
+ Name* name,
+ Profile profile)
+ : m_compileRequest(compileRequest)
+ , m_translationUnitIndex(translationUnitIndex)
+ , m_name(name)
+ , m_profile(profile)
+{
+}
+
+TranslationUnitRequest* FrontEndEntryPointRequest::getTranslationUnit()
+{
+ return getCompileRequest()->translationUnits[m_translationUnitIndex];
+}
+
+//
+// CompileRequestBase
+//
+
+CompileRequestBase::CompileRequestBase(Linkage* linkage, DiagnosticSink* sink)
+ : m_linkage(linkage), m_sink(sink)
+{
+}
+
+Session* CompileRequestBase::getSession()
+{
+ return getLinkage()->getSessionImpl();
+}
+
+//
+// FrontEndCompileRequest
+//
+
+FrontEndCompileRequest::FrontEndCompileRequest(
+ Linkage* linkage,
+ StdWriters* writers,
+ DiagnosticSink* sink)
+ : CompileRequestBase(linkage, sink), m_writers(writers)
+{
+ optionSet.inheritFrom(linkage->m_optionSet);
+}
+
+// Holds the hierarchy of views, the children being views that were 'initiated' (have an initiating
+// SourceLoc) in the parent.
+typedef Dictionary<SourceView*, List<SourceView*>> ViewInitiatingHierarchy;
+
+// Calculate the hierarchy from the sourceManager
+static void _calcViewInitiatingHierarchy(
+ SourceManager* sourceManager,
+ ViewInitiatingHierarchy& outHierarchy)
+{
+ const List<SourceView*> emptyList;
+ outHierarchy.clear();
+
+ // Iterate over all managers
+ for (SourceManager* curManager = sourceManager; curManager;
+ curManager = curManager->getParent())
+ {
+ // Iterate over all views
+ for (SourceView* view : curManager->getSourceViews())
+ {
+ if (view->getInitiatingSourceLoc().isValid())
+ {
+ // Look up the view it came from
+ SourceView* parentView =
+ sourceManager->findSourceViewRecursively(view->getInitiatingSourceLoc());
+ if (parentView)
+ {
+ List<SourceView*>& children = outHierarchy.getOrAddValue(parentView, emptyList);
+ // It shouldn't have already been added
+ SLANG_ASSERT(children.indexOf(view) < 0);
+ children.add(view);
+ }
+ }
+ }
+ }
+
+ // Order all the children, by their raw SourceLocs. This is desirable, so that a trivial
+ // traversal will traverse children in the order they are initiated in the parent source. This
+ // assumes they increase in SourceLoc implies an later within a source file - this is true
+ // currently.
+ for (auto& [_, value] : outHierarchy)
+ {
+ value.sort(
+ [](SourceView* a, SourceView* b) -> bool {
+ return a->getInitiatingSourceLoc().getRaw() < b->getInitiatingSourceLoc().getRaw();
+ });
+ }
+}
+
+// Given a source file, find the view that is the initial SourceView use of the source. It must have
+// an initiating SourceLoc that is not valid.
+static SourceView* _findInitialSourceView(SourceFile* sourceFile)
+{
+ // TODO(JS):
+ // This might be overkill - presumably the SourceView would belong to the same manager as it's
+ // SourceFile? That is not enforced by the SourceManager in any way though so we just search all
+ // managers, and all views.
+ for (SourceManager* sourceManager = sourceFile->getSourceManager(); sourceManager;
+ sourceManager = sourceManager->getParent())
+ {
+ for (SourceView* view : sourceManager->getSourceViews())
+ {
+ if (view->getSourceFile() == sourceFile && !view->getInitiatingSourceLoc().isValid())
+ {
+ return view;
+ }
+ }
+ }
+
+ return nullptr;
+}
+
+static void _outputInclude(SourceFile* sourceFile, Index depth, DiagnosticSink* sink)
+{
+ StringBuilder buf;
+
+ for (Index i = 0; i < depth; ++i)
+ {
+ buf << " ";
+ }
+
+ // Output the found path for now
+ // TODO(JS). We could use the verbose paths flag to control what path is output -> as it may be
+ // useful to output the full path for example
+
+ const PathInfo& pathInfo = sourceFile->getPathInfo();
+ buf << "'" << pathInfo.foundPath << "'";
+
+ // TODO(JS)?
+ // You might want to know where this include was from.
+ // If I output this though there will be a problem... as the indenting won't be clearly shown.
+ // Perhaps I output in two sections, one the hierarchy and the other the locations of the
+ // includes?
+
+ sink->diagnose(SourceLoc(), Diagnostics::includeOutput, buf);
+}
+
+static void _outputIncludesRec(
+ SourceView* sourceView,
+ Index depth,
+ ViewInitiatingHierarchy& hierarchy,
+ DiagnosticSink* sink)
+{
+ SourceFile* sourceFile = sourceView->getSourceFile();
+ const PathInfo& pathInfo = sourceFile->getPathInfo();
+
+ switch (pathInfo.type)
+ {
+ case PathInfo::Type::TokenPaste:
+ case PathInfo::Type::CommandLine:
+ case PathInfo::Type::TypeParse:
+ {
+ // If any of these types we don't output
+ return;
+ }
+ default:
+ break;
+ }
+
+ // Okay output this file at the current depth
+ _outputInclude(sourceFile, depth, sink);
+
+ // Now recurse to all of the children at the next depth
+ List<SourceView*>* children = hierarchy.tryGetValue(sourceView);
+ if (children)
+ {
+ for (SourceView* child : *children)
+ {
+ _outputIncludesRec(child, depth + 1, hierarchy, sink);
+ }
+ }
+}
+
+static void _outputPreprocessorTokens(const TokenList& toks, ISlangWriter* writer)
+{
+ if (writer == nullptr)
+ {
+ return;
+ }
+
+ StringBuilder buf;
+ for (const auto& tok : toks)
+ {
+ buf << tok.getContent();
+ // We'll separate tokens with space for now
+ buf.appendChar(' ');
+ }
+
+ buf.appendChar('\n');
+
+ writer->write(buf.getBuffer(), buf.getLength());
+}
+
+static void _outputIncludes(
+ const List<SourceFile*>& sourceFiles,
+ SourceManager* sourceManager,
+ DiagnosticSink* sink)
+{
+ // Set up the hierarchy to know how all the source views relate. This could be argued as
+ // overkill, but makes recursive output pretty simple
+ ViewInitiatingHierarchy hierarchy;
+ _calcViewInitiatingHierarchy(sourceManager, hierarchy);
+
+ // For all the source files
+ for (SourceFile* sourceFile : sourceFiles)
+ {
+ if (sourceFile->isIncludedFile())
+ continue;
+
+ // Find an initial view (this is the view of this file, that doesn't have an initiating loc)
+ SourceView* sourceView = _findInitialSourceView(sourceFile);
+ if (!sourceView)
+ {
+ // Okay, didn't find one, so just output the file
+ _outputInclude(sourceFile, 0, sink);
+ }
+ else
+ {
+ // Output from this view recursively
+ _outputIncludesRec(sourceView, 0, hierarchy, sink);
+ }
+ }
+}
+
+void FrontEndCompileRequest::parseTranslationUnit(TranslationUnitRequest* translationUnit)
+{
+ SLANG_PROFILE;
+ if (translationUnit->isChecked)
+ return;
+
+ auto linkage = getLinkage();
+
+ SLANG_AST_BUILDER_RAII(linkage->getASTBuilder());
+
+ // TODO(JS): NOTE! Here we are using the searchDirectories on the linkage. This is because
+ // currently the API only allows the setting search paths on linkage.
+ //
+ // Here we should probably be using the searchDirectories on the FrontEndCompileRequest.
+ // If searchDirectories.parent pointed to the one in the Linkage would mean linkage paths
+ // would be checked too (after those on the FrontEndCompileRequest).
+ IncludeSystem includeSystem(
+ &linkage->getSearchDirectories(),
+ linkage->getFileSystemExt(),
+ linkage->getSourceManager());
+
+ auto combinedPreprocessorDefinitions = translationUnit->getCombinedPreprocessorDefinitions();
+
+ auto module = translationUnit->getModule();
+
+ ASTBuilder* astBuilder = module->getASTBuilder();
+
+ ModuleDecl* translationUnitSyntax = astBuilder->create<ModuleDecl>();
+
+ translationUnitSyntax->nameAndLoc.name = translationUnit->moduleName;
+ translationUnitSyntax->module = module;
+ module->setModuleDecl(translationUnitSyntax);
+
+ // When compiling a module of code that belongs to the Slang
+ // core module, we add a modifier to the module to act
+ // as a marker, so that downstream code can detect declarations
+ // that came from the core module (by walking up their
+ // chain of ancestors and looking for the marker), and treat
+ // them differently from user declarations.
+ //
+ // We are adding the marker here, before we even parse the
+ // code in the module, in case the subsequent steps would
+ // like to treat the core module differently. Alternatively
+ // we could pass down the `m_isStandardLibraryCode` flag to
+ // these passes.
+ //
+ if (m_isCoreModuleCode)
+ {
+ translationUnitSyntax->modifiers.first = astBuilder->create<FromCoreModuleModifier>();
+ }
+
+ // We use a custom handler for preprocessor callbacks, to
+ // ensure that relevant state that is only visible during
+ // preprocessoing can be communicated to later phases of
+ // compilation.
+ //
+ FrontEndPreprocessorHandler preprocessorHandler(module, astBuilder, getSink(), translationUnit);
+
+ for (auto sourceFile : translationUnit->getSourceFiles())
+ {
+ module->getIncludedSourceFileMap().addIfNotExists(sourceFile, nullptr);
+ }
+
+ for (auto sourceFile : translationUnit->getSourceFiles())
+ {
+ SourceLanguage sourceLanguage = translationUnit->sourceLanguage;
+ SlangLanguageVersion languageVersion =
+ translationUnit->compileRequest->optionSet.getLanguageVersion();
+ auto tokens = preprocessSource(
+ sourceFile,
+ getSink(),
+ &includeSystem,
+ combinedPreprocessorDefinitions,
+ getLinkage(),
+ sourceLanguage,
+ languageVersion,
+ &preprocessorHandler);
+
+ translationUnitSyntax->languageVersion = languageVersion;
+
+ if (sourceLanguage == SourceLanguage::Unknown)
+ sourceLanguage = translationUnit->sourceLanguage;
+
+ Scope* languageScope = nullptr;
+ switch (sourceLanguage)
+ {
+ case SourceLanguage::HLSL:
+ languageScope = getSession()->hlslLanguageScope;
+ break;
+ case SourceLanguage::GLSL:
+ languageScope = getSession()->glslLanguageScope;
+ break;
+ case SourceLanguage::Slang:
+ default:
+ languageScope = getSession()->slangLanguageScope;
+ break;
+ }
+
+ if (optionSet.getBoolOption(CompilerOptionName::OutputIncludes))
+ {
+ _outputIncludes(
+ translationUnit->getSourceFiles(),
+ getSink()->getSourceManager(),
+ getSink());
+ }
+
+ if (optionSet.getBoolOption(CompilerOptionName::PreprocessorOutput))
+ {
+ if (m_writers)
+ {
+ _outputPreprocessorTokens(
+ tokens,
+ m_writers->getWriter(SLANG_WRITER_CHANNEL_STD_OUTPUT));
+ }
+ // If we output the preprocessor output then we are done doing anything else
+ return;
+ }
+
+ parseSourceFile(
+ astBuilder,
+ translationUnit,
+ sourceLanguage,
+ tokens,
+ getSink(),
+ languageScope,
+ translationUnitSyntax);
+
+ // Let's try dumping
+
+ if (optionSet.getBoolOption(CompilerOptionName::DumpAst))
+ {
+ StringBuilder buf;
+ SourceWriter writer(linkage->getSourceManager(), LineDirectiveMode::None, nullptr);
+
+ ASTDumpUtil::dump(
+ translationUnit->getModuleDecl(),
+ ASTDumpUtil::Style::Flat,
+ 0,
+ &writer);
+
+ const String& path = sourceFile->getPathInfo().foundPath;
+ if (path.getLength())
+ {
+ String fileName = Path::getFileNameWithoutExt(path);
+ fileName.append(".slang-ast");
+
+ File::writeAllText(fileName, writer.getContent());
+ }
+ }
+
+#if 0
+ // Test serialization
+ {
+ ASTSerialTestUtil::testSerialize(translationUnit->getModuleDecl(), getSession()->getNamePool(), getLinkage()->getASTBuilder()->getSharedASTBuilder(), getSourceManager());
+ }
+#endif
+ }
+}
+
+void FrontEndCompileRequest::checkAllTranslationUnits()
+{
+ SLANG_PROFILE;
+
+ LoadedModuleDictionary loadedModules;
+ if (additionalLoadedModules)
+ loadedModules = *additionalLoadedModules;
+
+ // Iterate over all translation units and
+ // apply the semantic checking logic.
+ for (auto& translationUnit : translationUnits)
+ {
+ if (translationUnit->isChecked)
+ continue;
+
+ checkTranslationUnit(translationUnit.Ptr(), loadedModules);
+
+ // Add the checked module to list of loadedModules so that they can be
+ // discovered by `findOrImportModule` when processing future `import` decls.
+ // TODO: this does not handle the case where a translation unit to discover
+ // another translation unit added later to the compilation request.
+ // We should output an error message when we detect such a case, or support
+ // this scenario with a recursive style checking.
+ loadedModules.add(translationUnit->moduleName, translationUnit->getModule());
+ }
+ checkEntryPoints();
+}
+
+void FrontEndCompileRequest::generateIR()
+{
+ SLANG_PROFILE;
+ SLANG_AST_BUILDER_RAII(getLinkage()->getASTBuilder());
+
+ // Our task in this function is to generate IR code
+ // for all of the declarations in the translation
+ // units that were loaded.
+
+ // Each translation unit is its own little world
+ // for code generation (we are not trying to
+ // replicate the GLSL linkage model), and so
+ // we will generate IR for each (if needed)
+ // in isolation.
+ for (auto& translationUnit : translationUnits)
+ {
+ // Skip if the module is precompiled.
+ if (translationUnit->getModule()->getIRModule())
+ continue;
+
+ // We want to only run generateIRForTranslationUnit once here. This is for two side effects:
+ // * it can dump ir
+ // * it can generate diagnostics
+
+ /// Generate IR for translation unit.
+ RefPtr<IRModule> irModule(
+ generateIRForTranslationUnit(getLinkage()->getASTBuilder(), translationUnit));
+
+ if (verifyDebugSerialization)
+ {
+ SerialContainerUtil::WriteOptions options;
+
+ options.sourceManagerToUseWhenSerializingSourceLocs = getSourceManager();
+
+ // Verify debug information
+ if (SLANG_FAILED(
+ SerialContainerUtil::verifyIRSerialize(irModule, getSession(), options)))
+ {
+ getSink()->diagnose(
+ irModule->getModuleInst()->sourceLoc,
+ Diagnostics::serialDebugVerificationFailed);
+ }
+ }
+
+ // Set the module on the translation unit
+ translationUnit->getModule()->setIRModule(irModule);
+ }
+}
+
+SlangResult FrontEndCompileRequest::executeActionsInner()
+{
+ SLANG_PROFILE_SECTION(frontEndExecute);
+ SLANG_AST_BUILDER_RAII(getLinkage()->getASTBuilder());
+
+ for (TranslationUnitRequest* translationUnit : translationUnits)
+ {
+ // Make sure SourceFile representation is available for all translationUnits
+ SLANG_RETURN_ON_FAIL(translationUnit->requireSourceFiles());
+ }
+
+
+ // Parse everything from the input files requested
+ for (TranslationUnitRequest* translationUnit : translationUnits)
+ {
+ parseTranslationUnit(translationUnit);
+ }
+
+ if (optionSet.getBoolOption(CompilerOptionName::PreprocessorOutput))
+ {
+ // If doing pre-processor output, then we are done
+ return SLANG_OK;
+ }
+
+ if (getSink()->getErrorCount() != 0)
+ return SLANG_FAIL;
+
+ // Perform semantic checking on the whole collection
+ {
+ SLANG_PROFILE_SECTION(SemanticChecking);
+ checkAllTranslationUnits();
+ }
+
+ if (getSink()->getErrorCount() != 0)
+ return SLANG_FAIL;
+
+ // After semantic checking is performed we can try and output doc information for this
+ if (optionSet.getBoolOption(CompilerOptionName::Doc))
+ {
+ // TODO: implement the logic to output generated documents to target directory/zip file.
+ }
+
+ // Look up all the entry points that are expected,
+ // and use them to populate the `program` member.
+ //
+ m_globalComponentType = createUnspecializedGlobalComponentType(this);
+ if (getSink()->getErrorCount() != 0)
+ return SLANG_FAIL;
+
+ m_globalAndEntryPointsComponentType =
+ createUnspecializedGlobalAndEntryPointsComponentType(this, m_unspecializedEntryPoints);
+ if (getSink()->getErrorCount() != 0)
+ return SLANG_FAIL;
+
+ // We always generate IR for all the translation units.
+ //
+ // TODO: We may eventually have a mode where we skip
+ // IR codegen and only produce an AST (e.g., for use when
+ // debugging problems in the parser or semantic checking),
+ // but for now there are no cases where not having IR
+ // makes sense.
+ //
+ generateIR();
+ if (getSink()->getErrorCount() != 0)
+ return SLANG_FAIL;
+
+ // Do parameter binding generation, for each compilation target.
+ //
+ for (auto targetReq : getLinkage()->targets)
+ {
+ auto targetProgram = m_globalAndEntryPointsComponentType->getTargetProgram(targetReq);
+ targetProgram->getOrCreateLayout(getSink());
+ targetProgram->getOrCreateIRModuleForLayout(getSink());
+ }
+ if (getSink()->getErrorCount() != 0)
+ return SLANG_FAIL;
+
+ return SLANG_OK;
+}
+
+int FrontEndCompileRequest::addTranslationUnit(SourceLanguage language, Name* moduleName)
+{
+ RefPtr<TranslationUnitRequest> translationUnit = new TranslationUnitRequest(this);
+ translationUnit->compileRequest = this;
+ translationUnit->sourceLanguage = SourceLanguage(language);
+
+ translationUnit->setModuleName(moduleName);
+ return addTranslationUnit(translationUnit);
+}
+
+int FrontEndCompileRequest::addTranslationUnit(TranslationUnitRequest* translationUnit)
+{
+ Index result = translationUnits.getCount();
+ translationUnits.add(translationUnit);
+ return (int)result;
+}
+
+void FrontEndCompileRequest::addTranslationUnitSourceArtifact(
+ int translationUnitIndex,
+ IArtifact* sourceArtifact)
+{
+ auto translationUnit = translationUnits[translationUnitIndex];
+
+ // Add the source file
+ translationUnit->addSourceArtifact(sourceArtifact);
+
+ if (!translationUnit->moduleName)
+ {
+ translationUnit->setModuleName(
+ getNamePool()->getName(Path::getFileNameWithoutExt(sourceArtifact->getName())));
+ }
+ if (translationUnit->module->getFilePath() == nullptr)
+ translationUnit->module->setPathInfo(PathInfo::makePath(sourceArtifact->getName()));
+}
+
+void FrontEndCompileRequest::addTranslationUnitSourceBlob(
+ int translationUnitIndex,
+ String const& path,
+ ISlangBlob* sourceBlob)
+{
+ auto translationUnit = translationUnits[translationUnitIndex];
+ auto sourceDesc =
+ ArtifactDescUtil::makeDescForSourceLanguage(asExternal(translationUnit->sourceLanguage));
+
+ auto artifact = ArtifactUtil::createArtifact(sourceDesc, path.getBuffer());
+ artifact->addRepresentationUnknown(sourceBlob);
+
+ addTranslationUnitSourceArtifact(translationUnitIndex, artifact);
+}
+
+void FrontEndCompileRequest::addTranslationUnitSourceFile(
+ int translationUnitIndex,
+ String const& path)
+{
+ // TODO: We need to consider whether a relative `path` should cause
+ // us to look things up using the registered search paths.
+ //
+ // This behavior wouldn't make sense for command-line invocations
+ // of `slangc`, but at least one API user wondered by the search
+ // paths were not taken into account by this function.
+ //
+
+ auto fileSystemExt = getLinkage()->getFileSystemExt();
+ auto translationUnit = getTranslationUnit(translationUnitIndex);
+
+ auto sourceDesc =
+ ArtifactDescUtil::makeDescForSourceLanguage(asExternal(translationUnit->sourceLanguage));
+
+ auto sourceArtifact = ArtifactUtil::createArtifact(sourceDesc, path.getBuffer());
+
+ auto extRep = new ExtFileArtifactRepresentation(path.getUnownedSlice(), fileSystemExt);
+ sourceArtifact->addRepresentation(extRep);
+
+ SlangResult existsRes = SLANG_OK;
+
+ // If we require caching, we demand it's loaded here.
+ //
+ // In practice this probably means repro capture is enabled. So we want to
+ // load the blob such that it's in the cache, even if it doesn't actually
+ // have to be loaded for the compilation.
+ if (getLinkage()->m_requireCacheFileSystem)
+ {
+ ComPtr<ISlangBlob> blob;
+ // If we can load the blob, then it exists
+ existsRes = sourceArtifact->loadBlob(ArtifactKeep::Yes, blob.writeRef());
+ }
+ else
+ {
+ existsRes = sourceArtifact->exists() ? SLANG_OK : SLANG_E_NOT_FOUND;
+ }
+
+ if (SLANG_FAILED(existsRes))
+ {
+ // Emit a diagnostic!
+ getSink()->diagnose(SourceLoc(), Diagnostics::cannotOpenFile, path);
+ return;
+ }
+
+ addTranslationUnitSourceArtifact(translationUnitIndex, sourceArtifact);
+}
+
+int FrontEndCompileRequest::addEntryPoint(
+ int translationUnitIndex,
+ String const& name,
+ Profile entryPointProfile)
+{
+ auto translationUnitReq = translationUnits[translationUnitIndex];
+
+ Index result = m_entryPointReqs.getCount();
+
+ RefPtr<FrontEndEntryPointRequest> entryPointReq = new FrontEndEntryPointRequest(
+ this,
+ translationUnitIndex,
+ getNamePool()->getName(name),
+ entryPointProfile);
+
+ m_entryPointReqs.add(entryPointReq);
+ // translationUnitReq->entryPoints.add(entryPointReq);
+
+ return int(result);
+}
+
+int EndToEndCompileRequest::addEntryPoint(
+ int translationUnitIndex,
+ String const& name,
+ Profile entryPointProfile,
+ List<String> const& genericTypeNames)
+{
+ getFrontEndReq()->addEntryPoint(translationUnitIndex, name, entryPointProfile);
+
+ EntryPointInfo entryPointInfo;
+ for (auto typeName : genericTypeNames)
+ entryPointInfo.specializationArgStrings.add(typeName);
+
+ Index result = m_entryPoints.getCount();
+ m_entryPoints.add(_Move(entryPointInfo));
+ return (int)result;
+}
+
+} // namespace Slang
diff --git a/source/slang/slang-compile-request.h b/source/slang/slang-compile-request.h
new file mode 100644
index 000000000..4dee026aa
--- /dev/null
+++ b/source/slang/slang-compile-request.h
@@ -0,0 +1,362 @@
+// slang-compile-request.h
+#pragma once
+
+//
+// This file contains the `FrontEndCompileRequest` type
+// and the types that it is built from (such as
+// `TranslationUnitRequest`). These types are used
+// whenever the Slang front-end is invoked to compile
+// a module (or, in some cases, one or more modules)
+// from source code to a checked AST and Slang IR.
+//
+// Note that the `EndToEndCompileRequest` type has its
+// own header: `slang-end-to-end-request.h`.
+//
+
+#include "../compiler-core/slang-artifact.h"
+#include "../compiler-core/slang-source-loc.h"
+#include "../core/slang-smart-pointer.h"
+#include "../core/slang-std-writers.h"
+#include "slang-compiler-fwd.h"
+#include "slang-diagnostics.h"
+#include "slang-module.h"
+#include "slang-preprocessor.h"
+#include "slang-profile.h"
+#include "slang-session.h"
+#include "slang-translation-unit.h"
+
+namespace Slang
+{
+
+/// Shared functionality between front- and back-end compile requests.
+///
+/// This is the base class for both `FrontEndCompileRequest` and
+/// `BackEndCompileRequest`, and allows a small number of parts of
+/// the compiler to be easily invocable from either front-end or
+/// back-end work.
+///
+class CompileRequestBase : public RefObject
+{
+ // TODO: We really shouldn't need this type in the long run.
+ // The few places that rely on it should be refactored to just
+ // depend on the underlying information (a linkage and a diagnostic
+ // sink) directly.
+ //
+ // The flags to control dumping and validation of IR should be
+ // moved to some kind of shared settings/options `struct` that
+ // both front-end and back-end requests can store.
+
+public:
+ Session* getSession();
+ Linkage* getLinkage() { return m_linkage; }
+ DiagnosticSink* getSink() { return m_sink; }
+ SourceManager* getSourceManager() { return getLinkage()->getSourceManager(); }
+ NamePool* getNamePool() { return getLinkage()->getNamePool(); }
+ ISlangFileSystemExt* getFileSystemExt() { return getLinkage()->getFileSystemExt(); }
+ SlangResult loadFile(String const& path, PathInfo& outPathInfo, ISlangBlob** outBlob)
+ {
+ return getLinkage()->loadFile(path, outPathInfo, outBlob);
+ }
+
+protected:
+ CompileRequestBase(Linkage* linkage, DiagnosticSink* sink);
+
+private:
+ Linkage* m_linkage = nullptr;
+ DiagnosticSink* m_sink = nullptr;
+};
+
+/// A request for the front-end to find and validate an entry-point function
+struct FrontEndEntryPointRequest : RefObject
+{
+public:
+ /// Create a request for an entry point.
+ FrontEndEntryPointRequest(
+ FrontEndCompileRequest* compileRequest,
+ int translationUnitIndex,
+ Name* name,
+ Profile profile);
+
+ /// Get the parent front-end compile request.
+ FrontEndCompileRequest* getCompileRequest() { return m_compileRequest; }
+
+ /// Get the translation unit that contains the entry point.
+ TranslationUnitRequest* getTranslationUnit();
+
+ /// Get the name of the entry point to find.
+ Name* getName() { return m_name; }
+
+ /// Get the stage that the entry point is to be compiled for
+ Stage getStage() { return m_profile.getStage(); }
+
+ /// Get the profile that the entry point is to be compiled for
+ Profile getProfile() { return m_profile; }
+
+ /// Get the index to the translation unit
+ int getTranslationUnitIndex() const { return m_translationUnitIndex; }
+
+private:
+ // The parent compile request
+ FrontEndCompileRequest* m_compileRequest;
+
+ // The index of the translation unit that will hold the entry point
+ int m_translationUnitIndex;
+
+ // The name of the entry point function to look for
+ Name* m_name;
+
+ // The profile to compile for (including stage)
+ Profile m_profile;
+};
+
+/// A request to compile source code to an AST + IR.
+class FrontEndCompileRequest : public CompileRequestBase
+{
+public:
+ /// Note that writers can be parsed as nullptr to disable output,
+ /// and individual channels set to null to disable them
+ FrontEndCompileRequest(Linkage* linkage, StdWriters* writers, DiagnosticSink* sink);
+
+ int addEntryPoint(int translationUnitIndex, String const& name, Profile entryPointProfile);
+
+ // Translation units we are being asked to compile
+ List<RefPtr<TranslationUnitRequest>> translationUnits;
+
+ // Additional modules that needs to be made visible to `import` while checking.
+ const LoadedModuleDictionary* additionalLoadedModules = nullptr;
+
+ RefPtr<TranslationUnitRequest> getTranslationUnit(UInt index)
+ {
+ return translationUnits[index];
+ }
+
+ // If true will serialize and de-serialize with debug information
+ bool verifyDebugSerialization = false;
+
+ CompilerOptionSet optionSet;
+
+ List<RefPtr<FrontEndEntryPointRequest>> m_entryPointReqs;
+
+ List<RefPtr<FrontEndEntryPointRequest>> const& getEntryPointReqs() { return m_entryPointReqs; }
+ UInt getEntryPointReqCount() { return m_entryPointReqs.getCount(); }
+ FrontEndEntryPointRequest* getEntryPointReq(UInt index) { return m_entryPointReqs[index]; }
+
+ void parseTranslationUnit(TranslationUnitRequest* translationUnit);
+
+ // Perform primary semantic checking on all
+ // of the translation units in the program
+ void checkAllTranslationUnits();
+
+ void checkEntryPoints();
+
+ void generateIR();
+
+ SlangResult executeActionsInner();
+
+ /// Add a translation unit to be compiled.
+ ///
+ /// @param language The source language that the translation unit will use (e.g.,
+ /// `SourceLanguage::Slang`
+ /// @param moduleName The name that will be used for the module compile from the translation
+ /// unit.
+ ///
+ /// If moduleName is passed as nullptr a module name is generated.
+ /// If all translation units in a compile request use automatically generated
+ /// module names, then they are guaranteed not to conflict with one another.
+ ///
+ /// @return The zero-based index of the translation unit in this compile request.
+ int addTranslationUnit(SourceLanguage language, Name* moduleName);
+
+ int addTranslationUnit(TranslationUnitRequest* translationUnit);
+
+ void addTranslationUnitSourceArtifact(int translationUnitIndex, IArtifact* sourceArtifact);
+
+ void addTranslationUnitSourceBlob(
+ int translationUnitIndex,
+ String const& path,
+ ISlangBlob* sourceBlob);
+
+ void addTranslationUnitSourceFile(int translationUnitIndex, String const& path);
+
+ /// Get a component type that represents the global scope of the compile request.
+ ComponentType* getGlobalComponentType() { return m_globalComponentType; }
+
+ /// Get a component type that represents the global scope of the compile request, plus the
+ /// requested entry points.
+ ComponentType* getGlobalAndEntryPointsComponentType()
+ {
+ return m_globalAndEntryPointsComponentType;
+ }
+
+ List<RefPtr<ComponentType>> const& getUnspecializedEntryPoints()
+ {
+ return m_unspecializedEntryPoints;
+ }
+
+ /// Does the code we are compiling represent part of the Slang core module?
+ bool m_isCoreModuleCode = false;
+
+ Name* m_defaultModuleName = nullptr;
+
+ /// The irDumpOptions
+ IRDumpOptions m_irDumpOptions;
+
+ /// An "extra" entry point that was added via a library reference
+ struct ExtraEntryPointInfo
+ {
+ Name* name;
+ Profile profile;
+ String mangledName;
+ };
+
+ /// A list of "extra" entry points added via a library reference
+ List<ExtraEntryPointInfo> m_extraEntryPoints;
+
+private:
+ /// A component type that includes only the global scopes of the translation unit(s) that were
+ /// compiled.
+ RefPtr<ComponentType> m_globalComponentType;
+
+ /// A component type that extends the global scopes with all of the entry points that were
+ /// specified.
+ RefPtr<ComponentType> m_globalAndEntryPointsComponentType;
+
+ List<RefPtr<ComponentType>> m_unspecializedEntryPoints;
+
+ RefPtr<StdWriters> m_writers;
+};
+
+/// Handlers for preprocessor callbacks to use when doing ordinary front-end compilation
+struct FrontEndPreprocessorHandler : PreprocessorHandler
+{
+public:
+ FrontEndPreprocessorHandler(
+ Module* module,
+ ASTBuilder* astBuilder,
+ DiagnosticSink* sink,
+ TranslationUnitRequest* translationUnit)
+ : m_module(module)
+ , m_astBuilder(astBuilder)
+ , m_sink(sink)
+ , m_translationUnit(translationUnit)
+ {
+ }
+
+protected:
+ Module* m_module;
+ ASTBuilder* m_astBuilder;
+ DiagnosticSink* m_sink;
+ TranslationUnitRequest* m_translationUnit = nullptr;
+
+ // The first task that this handler tries to deal with is
+ // capturing all the files on which a module is dependent.
+ //
+ // That information is exposed through public APIs and used
+ // by applications to decide when they need to "hot reload"
+ // their shader code.
+ //
+ void handleFileDependency(SourceFile* sourceFile) SLANG_OVERRIDE
+ {
+ m_module->addFileDependency(sourceFile);
+ m_translationUnit->addIncludedSourceFileIfNotExist(sourceFile);
+ }
+
+ // The second task that this handler deals with is detecting
+ // whether any macro values were set in a given source file
+ // that are semantically relevant to other stages of compilation.
+ //
+ void handleEndOfTranslationUnit(Preprocessor* preprocessor) SLANG_OVERRIDE
+ {
+ // We look at the preprocessor state after reading the entire
+ // source file/string, in order to see if any macros have been
+ // set that should be considered semantically relevant for
+ // later stages of compilation.
+ //
+ // Note: Checking the macro environment *after* preprocessing is complete
+ // means that we can treat macros introduced via `-D` options or the API
+ // equivalently to macros introduced via `#define`s in user code.
+ //
+ // For now, the only case of semantically-relevant macros we need to worrry
+ // about are the NVAPI macros used to establish the register/space to use.
+ //
+ static const char* kNVAPIRegisterMacroName = "NV_SHADER_EXTN_SLOT";
+ static const char* kNVAPISpaceMacroName = "NV_SHADER_EXTN_REGISTER_SPACE";
+
+ // For NVAPI use, the `NV_SHADER_EXTN_SLOT` macro is required to be defined.
+ //
+ String nvapiRegister;
+ SourceLoc nvapiRegisterLoc;
+ if (!SLANG_FAILED(findMacroValue(
+ preprocessor,
+ kNVAPIRegisterMacroName,
+ nvapiRegister,
+ nvapiRegisterLoc)))
+ {
+ // In contrast, NVAPI can be used without defining `NV_SHADER_EXTN_REGISTER_SPACE`,
+ // which effectively defaults to `space0`.
+ //
+ String nvapiSpace = "space0";
+ SourceLoc nvapiSpaceLoc;
+ findMacroValue(preprocessor, kNVAPISpaceMacroName, nvapiSpace, nvapiSpaceLoc);
+
+ // We are going to store the values of these macros on the AST-level `ModuleDecl`
+ // so that they will be available to later processing stages.
+ //
+ auto moduleDecl = m_module->getModuleDecl();
+
+ if (auto existingModifier = moduleDecl->findModifier<NVAPISlotModifier>())
+ {
+ // If there is already a modifier attached to the module (perhaps
+ // because of preprocessing a different source file, or because
+ // of settings established via command-line options), then we
+ // need to validate that the values being set in this file
+ // match those already set (or else there is likely to be
+ // some kind of error in the user's code).
+ //
+ _validateNVAPIMacroMatch(
+ kNVAPIRegisterMacroName,
+ existingModifier->registerName,
+ nvapiRegister,
+ nvapiRegisterLoc);
+ _validateNVAPIMacroMatch(
+ kNVAPISpaceMacroName,
+ existingModifier->spaceName,
+ nvapiSpace,
+ nvapiSpaceLoc);
+ }
+ else
+ {
+ // If there is no existing modifier on the module, then we
+ // take responsibility for adding one, based on the macro
+ // values we saw.
+ //
+ auto modifier = m_astBuilder->create<NVAPISlotModifier>();
+ modifier->loc = nvapiRegisterLoc;
+ modifier->registerName = nvapiRegister;
+ modifier->spaceName = nvapiSpace;
+
+ addModifier(moduleDecl, modifier);
+ }
+ }
+ }
+
+ /// Validate that a re-defintion of an NVAPI-related macro matches any previous definition
+ void _validateNVAPIMacroMatch(
+ char const* macroName,
+ String const& existingValue,
+ String const& newValue,
+ SourceLoc loc)
+ {
+ if (existingValue != newValue)
+ {
+ m_sink->diagnose(
+ loc,
+ Diagnostics::nvapiMacroMismatch,
+ macroName,
+ existingValue,
+ newValue);
+ }
+ }
+};
+
+} // namespace Slang
diff --git a/source/slang/slang-compiler-api.cpp b/source/slang/slang-compiler-api.cpp
new file mode 100644
index 000000000..6e3e8615f
--- /dev/null
+++ b/source/slang/slang-compiler-api.cpp
@@ -0,0 +1,2 @@
+// slang-compiler-api.cpp
+#include "slang-compiler-api.h"
diff --git a/source/slang/slang-compiler-api.h b/source/slang/slang-compiler-api.h
new file mode 100644
index 000000000..cfcf65a7f
--- /dev/null
+++ b/source/slang/slang-compiler-api.h
@@ -0,0 +1,124 @@
+// slang-compiler-api.h
+#pragma once
+
+//
+// This file provides utilities that are needed at the boundary
+// between the public Slang API (the interfaces declared in
+// `slang.h`) and the code that implements that API.
+//
+
+#include "slang-end-to-end-request.h"
+#include "slang-global-session.h"
+#include "slang-module.h"
+#include "slang-session.h"
+
+#include <slang.h>
+
+namespace Slang
+{
+
+//
+// The following functions are utilties to convert between
+// matching "external" (public API) and "internal" (implementation)
+// types. They are favored over explicit casts because they
+// help avoid making incorrect conversions (e.g., when using
+// `reinterpret_cast` or C-style casts), and because they
+// abstract over the conversion required for each pair of types.
+//
+
+SLANG_FORCE_INLINE slang::IGlobalSession* asExternal(Session* session)
+{
+ return static_cast<slang::IGlobalSession*>(session);
+}
+
+SLANG_FORCE_INLINE ComPtr<Session> asInternal(slang::IGlobalSession* session)
+{
+ Slang::Session* internalSession = nullptr;
+ session->queryInterface(SLANG_IID_PPV_ARGS(&internalSession));
+ return ComPtr<Session>(INIT_ATTACH, static_cast<Session*>(internalSession));
+}
+
+SLANG_FORCE_INLINE slang::ISession* asExternal(Linkage* linkage)
+{
+ return static_cast<slang::ISession*>(linkage);
+}
+
+SLANG_FORCE_INLINE Module* asInternal(slang::IModule* module)
+{
+ return static_cast<Module*>(module);
+}
+
+SLANG_FORCE_INLINE slang::IModule* asExternal(Module* module)
+{
+ return static_cast<slang::IModule*>(module);
+}
+
+ComponentType* asInternal(slang::IComponentType* inComponentType);
+
+SLANG_FORCE_INLINE slang::IComponentType* asExternal(ComponentType* componentType)
+{
+ return static_cast<slang::IComponentType*>(componentType);
+}
+
+SLANG_FORCE_INLINE slang::ProgramLayout* asExternal(ProgramLayout* programLayout)
+{
+ return (slang::ProgramLayout*)programLayout;
+}
+
+SLANG_FORCE_INLINE Type* asInternal(slang::TypeReflection* type)
+{
+ return reinterpret_cast<Type*>(type);
+}
+
+SLANG_FORCE_INLINE slang::TypeReflection* asExternal(Type* type)
+{
+ return reinterpret_cast<slang::TypeReflection*>(type);
+}
+
+SLANG_FORCE_INLINE DeclRef<Decl> asInternal(slang::GenericReflection* generic)
+{
+ return DeclRef<Decl>(reinterpret_cast<DeclRefBase*>(generic));
+}
+
+SLANG_FORCE_INLINE slang::GenericReflection* asExternal(DeclRef<Decl> generic)
+{
+ return reinterpret_cast<slang::GenericReflection*>(generic.declRefBase);
+}
+
+SLANG_FORCE_INLINE TypeLayout* asInternal(slang::TypeLayoutReflection* type)
+{
+ return reinterpret_cast<TypeLayout*>(type);
+}
+
+SLANG_FORCE_INLINE slang::TypeLayoutReflection* asExternal(TypeLayout* type)
+{
+ return reinterpret_cast<slang::TypeLayoutReflection*>(type);
+}
+
+SLANG_FORCE_INLINE SlangCompileRequest* asExternal(EndToEndCompileRequest* request)
+{
+ return static_cast<SlangCompileRequest*>(request);
+}
+
+SLANG_FORCE_INLINE EndToEndCompileRequest* asInternal(SlangCompileRequest* request)
+{
+ // Converts to the internal type -- does a runtime type check through queryInterfae
+ SLANG_ASSERT(request);
+ EndToEndCompileRequest* endToEndRequest = nullptr;
+ // NOTE! We aren't using to access an interface, so *doesn't* return with a refcount
+ request->queryInterface(SLANG_IID_PPV_ARGS(&endToEndRequest));
+ SLANG_ASSERT(endToEndRequest);
+ return endToEndRequest;
+}
+
+SLANG_FORCE_INLINE SlangCompileTarget asExternal(CodeGenTarget target)
+{
+ return (SlangCompileTarget)target;
+}
+
+SLANG_FORCE_INLINE SlangSourceLanguage asExternal(SourceLanguage sourceLanguage)
+{
+ return (SlangSourceLanguage)sourceLanguage;
+}
+
+} // namespace Slang
diff --git a/source/slang/slang-compiler-fwd.h b/source/slang/slang-compiler-fwd.h
new file mode 100644
index 000000000..91f3e1236
--- /dev/null
+++ b/source/slang/slang-compiler-fwd.h
@@ -0,0 +1,31 @@
+// slang-compiler-fwd.h
+#pragma once
+
+//
+// This file provides forward declarations that are
+// commonly used by the files that get included by
+// as part of the umbrella header `slang-compiler.h`.
+//
+
+namespace Slang
+{
+class ASTBuilder;
+class EndToEndCompileRequest;
+class FrontEndCompileRequest;
+struct IRModule;
+class Linkage;
+class Module;
+struct ModuleChunk;
+class ProgramLayout;
+class Session;
+class SharedASTBuilder;
+struct SharedSemanticsContext;
+class TargetProgram;
+class TargetRequest;
+class TranslationUnitRequest;
+struct TypeCheckingCache;
+class TypeLayout;
+
+using LoadedModule = Module;
+
+} // namespace Slang
diff --git a/source/slang/slang-compiler.cpp b/source/slang/slang-compiler.cpp
index b13624aac..4b689455b 100644
--- a/source/slang/slang-compiler.cpp
+++ b/source/slang/slang-compiler.cpp
@@ -1,1223 +1,9 @@
-// Compiler.cpp : Defines the entry point for the console application.
-//
+// slang-compiler.cpp
#include "slang-compiler.h"
-#include "../compiler-core/slang-lexer.h"
-#include "../core/slang-basic.h"
-#include "../core/slang-castable.h"
-#include "../core/slang-hex-dump-util.h"
-#include "../core/slang-io.h"
-#include "../core/slang-performance-profiler.h"
-#include "../core/slang-platform.h"
-#include "../core/slang-riff.h"
-#include "../core/slang-string-util.h"
-#include "../core/slang-type-convert-util.h"
-#include "../core/slang-type-text-util.h"
-#include "slang-check-impl.h"
-#include "slang-check.h"
-
-#include <chrono>
-
-// Artifact
-#include "../compiler-core/slang-artifact-associated.h"
-#include "../compiler-core/slang-artifact-container-util.h"
-#include "../compiler-core/slang-artifact-desc-util.h"
-#include "../compiler-core/slang-artifact-diagnostic-util.h"
-#include "../compiler-core/slang-artifact-impl.h"
-#include "../compiler-core/slang-artifact-representation-impl.h"
-#include "../compiler-core/slang-artifact-util.h"
-
-// Artifact output
-#include "slang-artifact-output-util.h"
-#include "slang-emit-cuda.h"
-#include "slang-extension-tracker.h"
-#include "slang-lower-to-ir.h"
-#include "slang-mangle.h"
-#include "slang-parameter-binding.h"
-#include "slang-parser.h"
-#include "slang-preprocessor.h"
-#include "slang-serialize-ast.h"
-#include "slang-serialize-container.h"
-#include "slang-type-layout.h"
-
namespace Slang
{
-// !!!!!!!!!!!!!!!!!!!!!! free functions for DiagnosicSink !!!!!!!!!!!!!!!!!!!!!!!!!!!!!
-
-bool isHeterogeneousTarget(CodeGenTarget target)
-{
- return ArtifactDescUtil::makeDescForCompileTarget(asExternal(target)).style ==
- ArtifactStyle::Host;
-}
-
-void printDiagnosticArg(StringBuilder& sb, CodeGenTarget val)
-{
- UnownedStringSlice name = TypeTextUtil::getCompileTargetName(asExternal(val));
- name = name.getLength() ? name : toSlice("<unknown>");
- sb << name;
-}
-
-void printDiagnosticArg(StringBuilder& sb, PassThroughMode val)
-{
- sb << TypeTextUtil::getPassThroughName(SlangPassThrough(val));
-}
-
-//
-// FrontEndEntryPointRequest
-//
-
-FrontEndEntryPointRequest::FrontEndEntryPointRequest(
- FrontEndCompileRequest* compileRequest,
- int translationUnitIndex,
- Name* name,
- Profile profile)
- : m_compileRequest(compileRequest)
- , m_translationUnitIndex(translationUnitIndex)
- , m_name(name)
- , m_profile(profile)
-{
-}
-
-
-TranslationUnitRequest* FrontEndEntryPointRequest::getTranslationUnit()
-{
- return getCompileRequest()->translationUnits[m_translationUnitIndex];
-}
-
-//
-// EntryPoint
-//
-
-ISlangUnknown* EntryPoint::getInterface(const Guid& guid)
-{
- if (guid == slang::IEntryPoint::getTypeGuid())
- return static_cast<slang::IEntryPoint*>(this);
-
- return Super::getInterface(guid);
-}
-
-RefPtr<EntryPoint> EntryPoint::create(
- Linkage* linkage,
- DeclRef<FuncDecl> funcDeclRef,
- Profile profile)
-{
- RefPtr<EntryPoint> entryPoint =
- new EntryPoint(linkage, funcDeclRef.getName(), profile, funcDeclRef);
- entryPoint->m_mangledName = getMangledName(linkage->getASTBuilder(), funcDeclRef);
- return entryPoint;
-}
-
-RefPtr<EntryPoint> EntryPoint::createDummyForPassThrough(
- Linkage* linkage,
- Name* name,
- Profile profile)
-{
- RefPtr<EntryPoint> entryPoint = new EntryPoint(linkage, name, profile, DeclRef<FuncDecl>());
- return entryPoint;
-}
-
-RefPtr<EntryPoint> EntryPoint::createDummyForDeserialize(
- Linkage* linkage,
- Name* name,
- Profile profile,
- String mangledName)
-{
- RefPtr<EntryPoint> entryPoint = new EntryPoint(linkage, name, profile, DeclRef<FuncDecl>());
- entryPoint->m_mangledName = mangledName;
- return entryPoint;
-}
-
-EntryPoint::EntryPoint(Linkage* linkage, Name* name, Profile profile, DeclRef<FuncDecl> funcDeclRef)
- : ComponentType(linkage), m_name(name), m_profile(profile), m_funcDeclRef(funcDeclRef)
-{
- // Collect any specialization parameters used by the entry point
- //
- _collectShaderParams();
-}
-
-Module* EntryPoint::getModule()
-{
- return Slang::getModule(getFuncDecl());
-}
-
-Index EntryPoint::getSpecializationParamCount()
-{
- return m_genericSpecializationParams.getCount() + m_existentialSpecializationParams.getCount();
-}
-
-SpecializationParam const& EntryPoint::getSpecializationParam(Index index)
-{
- auto genericParamCount = m_genericSpecializationParams.getCount();
- if (index < genericParamCount)
- {
- return m_genericSpecializationParams[index];
- }
- else
- {
- return m_existentialSpecializationParams[index - genericParamCount];
- }
-}
-
-Index EntryPoint::getRequirementCount()
-{
- // The only requirement of an entry point is the module that contains it.
- //
- // TODO: We will eventually want to support the case of an entry
- // point nested in a `struct` type, in which case there should be
- // a single requirement representing that outer type (so that multiple
- // entry points nested under the same type can share the storage
- // for parameters at that scope).
-
- // Note: the defensive coding is here because the
- // "dummy" entry points we create for pass-through
- // compilation will not have an associated module.
- //
- if (const auto module = getModule())
- {
- return 1;
- }
- return 0;
-}
-
-RefPtr<ComponentType> EntryPoint::getRequirement(Index index)
-{
- SLANG_UNUSED(index);
- SLANG_ASSERT(index == 0);
- SLANG_ASSERT(getModule());
- return getModule();
-}
-
-String EntryPoint::getEntryPointMangledName(Index index)
-{
- SLANG_UNUSED(index);
- SLANG_ASSERT(index == 0);
-
- return m_mangledName;
-}
-
-String EntryPoint::getEntryPointNameOverride(Index index)
-{
- SLANG_UNUSED(index);
- SLANG_ASSERT(index == 0);
-
- return m_name ? m_name->text : "";
-}
-
-void EntryPoint::acceptVisitor(
- ComponentTypeVisitor* visitor,
- SpecializationInfo* specializationInfo)
-{
- visitor->visitEntryPoint(this, as<EntryPointSpecializationInfo>(specializationInfo));
-}
-
-void EntryPoint::buildHash(DigestBuilder<SHA1>& builder)
-{
- SLANG_UNUSED(builder);
-}
-
-List<Module*> const& EntryPoint::getModuleDependencies()
-{
- if (auto module = getModule())
- return module->getModuleDependencies();
-
- static List<Module*> empty;
- return empty;
-}
-
-List<SourceFile*> const& EntryPoint::getFileDependencies()
-{
- if (const auto module = getModule())
- return getModule()->getFileDependencies();
-
- static List<SourceFile*> empty;
- return empty;
-}
-
-TypeConformance::TypeConformance(
- Linkage* linkage,
- SubtypeWitness* witness,
- Int confomrmanceIdOverride,
- DiagnosticSink* sink)
- : ComponentType(linkage)
- , m_subtypeWitness(witness)
- , m_conformanceIdOverride(confomrmanceIdOverride)
-{
- addDepedencyFromWitness(witness);
- m_irModule = generateIRForTypeConformance(this, m_conformanceIdOverride, sink);
-}
-
-void TypeConformance::addDepedencyFromWitness(SubtypeWitness* witness)
-{
- if (auto declaredWitness = as<DeclaredSubtypeWitness>(witness))
- {
- auto declModule = getModule(declaredWitness->getDeclRef().getDecl());
- m_moduleDependencyList.addDependency(declModule);
- m_fileDependencyList.addDependency(declModule);
- if (m_requirementSet.add(declModule))
- {
- m_requirements.add(declModule);
- }
- // TODO: handle the specialization arguments in declaredWitness->declRef.substitutions.
- }
- else if (auto transitiveWitness = as<TransitiveSubtypeWitness>(witness))
- {
- addDepedencyFromWitness(transitiveWitness->getMidToSup());
- addDepedencyFromWitness(transitiveWitness->getSubToMid());
- }
- else if (auto conjunctionWitness = as<ConjunctionSubtypeWitness>(witness))
- {
- auto componentCount = conjunctionWitness->getComponentCount();
- for (Index i = 0; i < componentCount; ++i)
- {
- auto w = as<SubtypeWitness>(conjunctionWitness->getComponentWitness(i));
- if (w)
- addDepedencyFromWitness(w);
- }
- }
-}
-
-ISlangUnknown* TypeConformance::getInterface(const Guid& guid)
-{
- if (guid == slang::ITypeConformance::getTypeGuid())
- return static_cast<slang::ITypeConformance*>(this);
-
- return Super::getInterface(guid);
-}
-
-void TypeConformance::buildHash(DigestBuilder<SHA1>& builder)
-{
- // TODO: Implement some kind of hashInto for Val then replace this
- auto subtypeWitness = m_subtypeWitness->toString();
-
- builder.append(subtypeWitness);
- builder.append(m_conformanceIdOverride);
-}
-
-List<Module*> const& TypeConformance::getModuleDependencies()
-{
- return m_moduleDependencyList.getModuleList();
-}
-
-List<SourceFile*> const& TypeConformance::getFileDependencies()
-{
- return m_fileDependencyList.getFileList();
-}
-
-Index TypeConformance::getRequirementCount()
-{
- return m_requirements.getCount();
-}
-
-RefPtr<ComponentType> TypeConformance::getRequirement(Index index)
-{
- return m_requirements[index];
-}
-
-void TypeConformance::acceptVisitor(
- ComponentTypeVisitor* visitor,
- ComponentType::SpecializationInfo* specializationInfo)
-{
- SLANG_UNUSED(specializationInfo);
- visitor->visitTypeConformance(this);
-}
-
-RefPtr<ComponentType::SpecializationInfo> TypeConformance::_validateSpecializationArgsImpl(
- SpecializationArg const* args,
- Index argCount,
- DiagnosticSink* sink)
-{
- SLANG_UNUSED(args);
- SLANG_UNUSED(argCount);
- SLANG_UNUSED(sink);
- return nullptr;
-}
-
-//
-
-Profile Profile::lookUp(UnownedStringSlice const& name)
-{
-#define PROFILE(TAG, NAME, STAGE, VERSION) \
- if (name == UnownedTerminatedStringSlice(#NAME)) \
- return Profile::TAG;
-#define PROFILE_ALIAS(TAG, DEF, NAME) \
- if (name == UnownedTerminatedStringSlice(#NAME)) \
- return Profile::TAG;
-#include "slang-profile-defs.h"
-
- return Profile::Unknown;
-}
-
-Profile Profile::lookUp(char const* name)
-{
- return lookUp(UnownedTerminatedStringSlice(name));
-}
-
-CapabilitySet Profile::getCapabilityName()
-{
- List<CapabilityName> result;
- switch (getVersion())
- {
-#define PROFILE_VERSION(TAG, NAME) \
- case ProfileVersion::TAG: \
- result.add(CapabilityName::TAG); \
- break;
-#include "slang-profile-defs.h"
- default:
- break;
- }
- switch (getStage())
- {
-#define PROFILE_STAGE(TAG, NAME, VAL) \
- case Stage::TAG: \
- result.add(CapabilityName::NAME); \
- break;
-#include "slang-profile-defs.h"
- default:
- break;
- }
-
- CapabilitySet resultSet = CapabilitySet(result);
- for (auto i : this->additionalCapabilities)
- resultSet.join(i);
- return resultSet;
-}
-
-char const* Profile::getName()
-{
- switch (raw)
- {
- default:
- return "unknown";
-
-#define PROFILE(TAG, NAME, STAGE, VERSION) \
- case Profile::TAG: \
- return #NAME;
-#define PROFILE_ALIAS(TAG, DEF, NAME) /* empty */
-#include "slang-profile-defs.h"
- }
-}
-
-static const StageInfo kStages[] = {
-#define PROFILE_STAGE(ID, NAME, ENUM) {#NAME, Stage::ID},
-
-#define PROFILE_STAGE_ALIAS(ID, NAME, VAL) {#NAME, Stage::ID},
-
-#include "slang-profile-defs.h"
-};
-
-ConstArrayView<StageInfo> getStageInfos()
-{
- return makeConstArrayView(kStages);
-}
-
-Stage findStageByName(String const& name)
-{
- for (auto entry : kStages)
- {
- if (name == entry.name)
- {
- return entry.stage;
- }
- }
-
- return Stage::Unknown;
-}
-
-UnownedStringSlice getStageText(Stage stage)
-{
- for (auto entry : kStages)
- {
- if (stage == entry.stage)
- {
- return UnownedStringSlice(entry.name);
- }
- }
- return UnownedStringSlice();
-}
-
-Stage getStageFromAtom(CapabilityAtom atom)
-{
- switch (atom)
- {
- case CapabilityAtom::vertex:
- return Stage::Vertex;
- case CapabilityAtom::hull:
- return Stage::Hull;
- case CapabilityAtom::domain:
- return Stage::Domain;
- case CapabilityAtom::geometry:
- return Stage::Geometry;
- case CapabilityAtom::fragment:
- return Stage::Fragment;
- case CapabilityAtom::compute:
- return Stage::Compute;
- case CapabilityAtom::_mesh:
- return Stage::Mesh;
- case CapabilityAtom::_amplification:
- return Stage::Amplification;
- case CapabilityAtom::_anyhit:
- return Stage::AnyHit;
- case CapabilityAtom::_closesthit:
- return Stage::ClosestHit;
- case CapabilityAtom::_intersection:
- return Stage::Intersection;
- case CapabilityAtom::_raygen:
- return Stage::RayGeneration;
- case CapabilityAtom::_miss:
- return Stage::Miss;
- case CapabilityAtom::_callable:
- return Stage::Callable;
- case CapabilityAtom::dispatch:
- return Stage::Dispatch;
- default:
- SLANG_UNEXPECTED("unknown stage atom");
- UNREACHABLE_RETURN(Stage::Unknown);
- }
-}
-
-CapabilityAtom getAtomFromStage(Stage stage)
-{
- // Convert Slang::Stage to CapabilityAtom.
- // Note that capabilities do not share the same values as Slang::Stage
- // and must be explicitly converted.
- switch (stage)
- {
- case Stage::Compute:
- return CapabilityAtom::compute;
- case Stage::Vertex:
- return CapabilityAtom::vertex;
- case Stage::Fragment:
- return CapabilityAtom::fragment;
- case Stage::Geometry:
- return CapabilityAtom::geometry;
- case Stage::Hull:
- return CapabilityAtom::hull;
- case Stage::Domain:
- return CapabilityAtom::domain;
- case Stage::Mesh:
- return CapabilityAtom::_mesh;
- case Stage::Amplification:
- return CapabilityAtom::_amplification;
- case Stage::RayGeneration:
- return CapabilityAtom::_raygen;
- case Stage::AnyHit:
- return CapabilityAtom::_anyhit;
- case Stage::ClosestHit:
- return CapabilityAtom::_closesthit;
- case Stage::Miss:
- return CapabilityAtom::_miss;
- case Stage::Intersection:
- return CapabilityAtom::_intersection;
- case Stage::Callable:
- return CapabilityAtom::_callable;
- case Stage::Dispatch:
- return CapabilityAtom::dispatch;
- default:
- SLANG_UNEXPECTED("unknown stage");
- UNREACHABLE_RETURN(CapabilityAtom::Invalid);
- }
-}
-
-SlangResult checkExternalCompilerSupport(Session* session, PassThroughMode passThrough)
-{
- // Check if the type is supported on this compile
- if (passThrough == PassThroughMode::None)
- {
- // If no pass through -> that will always work!
- return SLANG_OK;
- }
-
- return session->getOrLoadDownstreamCompiler(passThrough, nullptr) ? SLANG_OK
- : SLANG_E_NOT_FOUND;
-}
-
-SourceLanguage getDefaultSourceLanguageForDownstreamCompiler(PassThroughMode compiler)
-{
- switch (compiler)
- {
- case PassThroughMode::None:
- {
- return SourceLanguage::Unknown;
- }
- case PassThroughMode::Fxc:
- case PassThroughMode::Dxc:
- {
- return SourceLanguage::HLSL;
- }
- case PassThroughMode::Glslang:
- {
- return SourceLanguage::GLSL;
- }
- case PassThroughMode::LLVM:
- case PassThroughMode::Clang:
- case PassThroughMode::VisualStudio:
- case PassThroughMode::Gcc:
- case PassThroughMode::GenericCCpp:
- {
- // These could ingest C, but we only have this function to work out a
- // 'default' language to ingest.
- return SourceLanguage::CPP;
- }
- case PassThroughMode::NVRTC:
- {
- return SourceLanguage::CUDA;
- }
- case PassThroughMode::Tint:
- {
- return SourceLanguage::WGSL;
- }
- case PassThroughMode::SpirvDis:
- {
- return SourceLanguage::SPIRV;
- }
- case PassThroughMode::MetalC:
- {
- return SourceLanguage::Metal;
- }
- default:
- break;
- }
- SLANG_ASSERT(!"Unknown compiler");
- return SourceLanguage::Unknown;
-}
-
-PassThroughMode getDownstreamCompilerRequiredForTarget(CodeGenTarget target)
-{
- switch (target)
- {
- // Don't *require* a downstream compiler for source output
- case CodeGenTarget::GLSL:
- case CodeGenTarget::HLSL:
- case CodeGenTarget::CUDASource:
- case CodeGenTarget::CPPSource:
- case CodeGenTarget::HostCPPSource:
- case CodeGenTarget::PyTorchCppBinding:
- case CodeGenTarget::CSource:
- case CodeGenTarget::Metal:
- case CodeGenTarget::WGSL:
- {
- return PassThroughMode::None;
- }
- case CodeGenTarget::None:
- {
- return PassThroughMode::None;
- }
- case CodeGenTarget::WGSLSPIRVAssembly:
- case CodeGenTarget::SPIRVAssembly:
- case CodeGenTarget::SPIRV:
- {
- return PassThroughMode::SpirvDis;
- }
- case CodeGenTarget::DXBytecode:
- case CodeGenTarget::DXBytecodeAssembly:
- {
- return PassThroughMode::Fxc;
- }
- case CodeGenTarget::DXIL:
- case CodeGenTarget::DXILAssembly:
- {
- return PassThroughMode::Dxc;
- }
- case CodeGenTarget::MetalLib:
- case CodeGenTarget::MetalLibAssembly:
- {
- return PassThroughMode::MetalC;
- }
- case CodeGenTarget::ShaderHostCallable:
- case CodeGenTarget::ShaderSharedLibrary:
- case CodeGenTarget::HostExecutable:
- case CodeGenTarget::HostHostCallable:
- case CodeGenTarget::HostSharedLibrary:
- {
- // We need some C/C++ compiler
- return PassThroughMode::GenericCCpp;
- }
- case CodeGenTarget::PTX:
- {
- return PassThroughMode::NVRTC;
- }
- case CodeGenTarget::WGSLSPIRV:
- {
- return PassThroughMode::Tint;
- }
- default:
- break;
- }
-
- SLANG_ASSERT(!"Unhandled target");
- return PassThroughMode::None;
-}
-
-EndToEndCompileRequest* CodeGenContext::isPassThroughEnabled()
-{
- auto endToEndReq = isEndToEndCompile();
-
- // If there isn't an end-to-end compile going on,
- // there can be no pass-through.
- //
- if (!endToEndReq)
- return nullptr;
-
- // And if pass-through isn't set on that end-to-end compile,
- // then we clearly areb't doing a pass-through compile.
- //
- if (endToEndReq->m_passThrough == PassThroughMode::None)
- return nullptr;
-
- // If we have confirmed that pass-through compilation is going on,
- // we return the end-to-end request, because it has all the
- // relevant state that we need to implement pass-through mode.
- //
- return endToEndReq;
-}
-
-/// If there is a pass-through compile going on, find the translation unit for the given entry
-/// point. Assumes isPassThroughEnabled has already been called
-TranslationUnitRequest* getPassThroughTranslationUnit(
- EndToEndCompileRequest* endToEndReq,
- Int entryPointIndex)
-{
- SLANG_ASSERT(endToEndReq);
- SLANG_ASSERT(endToEndReq->m_passThrough != PassThroughMode::None);
- auto frontEndReq = endToEndReq->getFrontEndReq();
- auto entryPointReq = frontEndReq->getEntryPointReq(entryPointIndex);
- auto translationUnit = entryPointReq->getTranslationUnit();
- return translationUnit;
-}
-
-TranslationUnitRequest* CodeGenContext::findPassThroughTranslationUnit(Int entryPointIndex)
-{
- if (auto endToEndReq = isPassThroughEnabled())
- return getPassThroughTranslationUnit(endToEndReq, entryPointIndex);
- return nullptr;
-}
-
-static void _appendCodeWithPath(
- const UnownedStringSlice& filePath,
- const UnownedStringSlice& fileContent,
- StringBuilder& outCodeBuilder)
-{
- outCodeBuilder << "#line 1 \"";
- auto handler = StringEscapeUtil::getHandler(StringEscapeUtil::Style::Cpp);
- handler->appendEscaped(filePath, outCodeBuilder);
- outCodeBuilder << "\"\n";
- outCodeBuilder << fileContent << "\n";
-}
-
-void trackGLSLTargetCaps(ShaderExtensionTracker* extensionTracker, CapabilitySet const& caps)
-{
- for (auto& conjunctions : caps.getAtomSets())
- {
- for (auto atom : conjunctions)
- {
- switch (asAtom(atom))
- {
- default:
- break;
-
- case CapabilityAtom::glsl_spirv_1_0:
- extensionTracker->requireSPIRVVersion(SemanticVersion(1, 0));
- break;
- case CapabilityAtom::glsl_spirv_1_1:
- extensionTracker->requireSPIRVVersion(SemanticVersion(1, 1));
- break;
- case CapabilityAtom::glsl_spirv_1_2:
- extensionTracker->requireSPIRVVersion(SemanticVersion(1, 2));
- break;
- case CapabilityAtom::glsl_spirv_1_3:
- extensionTracker->requireSPIRVVersion(SemanticVersion(1, 3));
- break;
- case CapabilityAtom::glsl_spirv_1_4:
- extensionTracker->requireSPIRVVersion(SemanticVersion(1, 4));
- break;
- case CapabilityAtom::glsl_spirv_1_5:
- extensionTracker->requireSPIRVVersion(SemanticVersion(1, 5));
- break;
- case CapabilityAtom::glsl_spirv_1_6:
- extensionTracker->requireSPIRVVersion(SemanticVersion(1, 6));
- break;
- }
- }
- }
-}
-
-SlangResult CodeGenContext::requireTranslationUnitSourceFiles()
-{
- if (auto endToEndReq = isPassThroughEnabled())
- {
- for (auto entryPointIndex : getEntryPointIndices())
- {
- auto translationUnit = getPassThroughTranslationUnit(endToEndReq, entryPointIndex);
- SLANG_ASSERT(translationUnit);
- /// Make sure we have the source files
- SLANG_RETURN_ON_FAIL(translationUnit->requireSourceFiles());
- }
- }
-
- return SLANG_OK;
-}
-
-#if SLANG_VC
-// TODO(JS): This is a workaround
-// In debug VS builds there is a warning on line about it being unreachable.
-// for (auto entryPointIndex : getEntryPointIndices())
-// It's not clear how that could possibly be unreachable
-#pragma warning(push)
-#pragma warning(disable : 4702)
-#endif
-SlangResult CodeGenContext::emitEntryPointsSource(ComPtr<IArtifact>& outArtifact)
-{
- outArtifact.setNull();
-
- SLANG_RETURN_ON_FAIL(requireTranslationUnitSourceFiles());
-
- auto endToEndReq = isPassThroughEnabled();
- if (endToEndReq)
- {
- for (auto entryPointIndex : getEntryPointIndices())
- {
- auto translationUnit = getPassThroughTranslationUnit(endToEndReq, entryPointIndex);
- SLANG_ASSERT(translationUnit);
-
- /// Make sure we have the source files
- SLANG_RETURN_ON_FAIL(translationUnit->requireSourceFiles());
-
- // Generate a string that includes the content of
- // the source file(s), along with a line directive
- // to ensure that we get reasonable messages
- // from the downstream compiler when in pass-through
- // mode.
-
- StringBuilder codeBuilder;
- if (getTargetFormat() == CodeGenTarget::GLSL)
- {
- // Special case GLSL
- int translationUnitCounter = 0;
- for (auto sourceFile : translationUnit->getSourceFiles())
- {
- int translationUnitIndex = translationUnitCounter++;
-
- // We want to output `#line` directives, but we need
- // to skip this for the first file, since otherwise
- // some GLSL implementations will get tripped up by
- // not having the `#version` directive be the first
- // thing in the file.
- if (translationUnitIndex != 0)
- {
- codeBuilder << "#line 1 " << translationUnitIndex << "\n";
- }
- codeBuilder << sourceFile->getContent() << "\n";
- }
- }
- else
- {
- for (auto sourceFile : translationUnit->getSourceFiles())
- {
- _appendCodeWithPath(
- sourceFile->getPathInfo().foundPath.getUnownedSlice(),
- sourceFile->getContent(),
- codeBuilder);
- }
- }
-
- auto artifact =
- ArtifactUtil::createArtifactForCompileTarget(asExternal(getTargetFormat()));
- artifact->addRepresentationUnknown(StringBlob::moveCreate(codeBuilder));
-
- outArtifact.swap(artifact);
- return SLANG_OK;
- }
- return SLANG_OK;
- }
- else
- {
- return emitEntryPointsSourceFromIR(outArtifact);
- }
-}
-#if SLANG_VC
-#pragma warning(pop)
-#endif
-
-SlangResult CodeGenContext::emitPrecompiledDownstreamIR(ComPtr<IArtifact>& outArtifact)
-{
- return _emitEntryPoints(outArtifact);
-}
-
-String GetHLSLProfileName(Profile profile)
-{
- switch (profile.getFamily())
- {
- case ProfileFamily::DX:
- // Profile version is a DX one, so stick with it.
- break;
-
- default:
- // Profile is a non-DX profile family, so we need to try
- // to clobber it with something to get a default.
- //
- // TODO: This is a huge hack...
- profile.setVersion(ProfileVersion::DX_5_1);
- break;
- }
-
- char const* stagePrefix = nullptr;
- switch (profile.getStage())
- {
- // Note: All of the raytracing-related stages require
- // compiling for a `lib_*` profile, even when only a
- // single entry point is present.
- //
- // We also go ahead and use this target in any case
- // where we don't know the actual stage to compiel for,
- // as a fallback option.
- //
- // TODO: We also want to use this option when compiling
- // multiple entry points to a DXIL library.
- //
- default:
- stagePrefix = "lib";
- break;
-
- // The traditional rasterization pipeline and compute
- // shaders all have custom profile names that identify
- // both the stage and shader model, which need to be
- // used when compiling a single entry point.
- //
-#define CASE(NAME, PREFIX) \
- case Stage::NAME: \
- stagePrefix = #PREFIX; \
- break
- CASE(Vertex, vs);
- CASE(Hull, hs);
- CASE(Domain, ds);
- CASE(Geometry, gs);
- CASE(Fragment, ps);
- CASE(Compute, cs);
- CASE(Amplification, as);
- CASE(Mesh, ms);
-#undef CASE
- }
-
- char const* versionSuffix = nullptr;
- switch (profile.getVersion())
- {
-#define CASE(TAG, SUFFIX) \
- case ProfileVersion::TAG: \
- versionSuffix = #SUFFIX; \
- break
- CASE(DX_4_0, _4_0);
- CASE(DX_4_1, _4_1);
- CASE(DX_5_0, _5_0);
- CASE(DX_5_1, _5_1);
- CASE(DX_6_0, _6_0);
- CASE(DX_6_1, _6_1);
- CASE(DX_6_2, _6_2);
- CASE(DX_6_3, _6_3);
- CASE(DX_6_4, _6_4);
- CASE(DX_6_5, _6_5);
- CASE(DX_6_6, _6_6);
- CASE(DX_6_7, _6_7);
- CASE(DX_6_8, _6_8);
- CASE(DX_6_9, _6_9);
-#undef CASE
-
- default:
- return "unknown";
- }
-
- String result;
- result.append(stagePrefix);
- result.append(versionSuffix);
- return result;
-}
-
-void reportExternalCompileError(
- const char* compilerName,
- Severity severity,
- SlangResult res,
- const UnownedStringSlice& diagnostic,
- DiagnosticSink* sink)
-{
- StringBuilder builder;
- if (compilerName)
- {
- builder << compilerName << ": ";
- }
-
- if (SLANG_FAILED(res) && res != SLANG_FAIL)
- {
- {
- char tmp[17];
- sprintf_s(tmp, SLANG_COUNT_OF(tmp), "0x%08x", uint32_t(res));
- builder << "Result(" << tmp << ") ";
- }
-
- PlatformUtil::appendResult(res, builder);
- }
-
- if (diagnostic.getLength() > 0)
- {
- builder.append(diagnostic);
- if (!diagnostic.endsWith("\n"))
- {
- builder.append("\n");
- }
- }
-
- sink->diagnoseRaw(severity, builder.getUnownedSlice());
-}
-
-void reportExternalCompileError(
- const char* compilerName,
- SlangResult res,
- const UnownedStringSlice& diagnostic,
- DiagnosticSink* sink)
-{
- // TODO(tfoley): need a better policy for how we translate diagnostics
- // back into the Slang world (although we should always try to generate
- // HLSL that doesn't produce any diagnostics...)
- reportExternalCompileError(
- compilerName,
- SLANG_FAILED(res) ? Severity::Error : Severity::Warning,
- res,
- diagnostic,
- sink);
-}
-
-static String _getDisplayPath(DiagnosticSink* sink, SourceFile* sourceFile)
-{
- if (sink->isFlagSet(DiagnosticSink::Flag::VerbosePath))
- {
- return sourceFile->calcVerbosePath();
- }
- else
- {
- return sourceFile->getPathInfo().foundPath;
- }
-}
-
-String CodeGenContext::calcSourcePathForEntryPoints()
-{
- String failureMode = "slang-generated";
- if (getEntryPointCount() != 1)
- return failureMode;
- auto entryPointIndex = getSingleEntryPointIndex();
- auto translationUnitRequest = findPassThroughTranslationUnit(entryPointIndex);
- if (!translationUnitRequest)
- return failureMode;
-
- const auto& sourceFiles = translationUnitRequest->getSourceFiles();
-
- auto sink = getSink();
-
- const Index numSourceFiles = sourceFiles.getCount();
-
- switch (numSourceFiles)
- {
- case 0:
- return "unknown";
- case 1:
- return _getDisplayPath(sink, sourceFiles[0]);
- default:
- {
- StringBuilder builder;
- builder << _getDisplayPath(sink, sourceFiles[0]);
- for (int i = 1; i < numSourceFiles; ++i)
- {
- builder << ";" << _getDisplayPath(sink, sourceFiles[i]);
- }
- return builder;
- }
- }
-}
-
-// Helper function for cases where we can assume a single entry point
-Int assertSingleEntryPoint(List<Int> const& entryPointIndices)
-{
- SLANG_ASSERT(entryPointIndices.getCount() == 1);
- return *entryPointIndices.begin();
-}
-
-// True if it's best to use 'emitted' source for complication. For a downstream compiler
-// that is not file based, this is always ok.
-///
-/// If the downstream compiler is file system based, we may want to just use the file that was
-/// passed to be compiled. That the downstream compiler can determine if it will then save the file
-/// or not based on if it's a match - and generally there will not be a match with emitted source.
-///
-/// This test is only used for pass through mode.
-static bool _useEmittedSource(
- IDownstreamCompiler* compiler,
- TranslationUnitRequest* translationUnit)
-{
- // We only bother if it's a file based compiler.
- if (compiler->isFileBased())
- {
- // It can only have *one* source file as otherwise we have to combine to make a new source
- // file anyway
- return translationUnit->getSourceArtifacts().getCount() != 1;
- }
- return true;
-}
-
-static Severity _getDiagnosticSeverity(ArtifactDiagnostic::Severity severity)
-{
- switch (severity)
- {
- case ArtifactDiagnostic::Severity::Warning:
- return Severity::Warning;
- case ArtifactDiagnostic::Severity::Info:
- return Severity::Note;
- default:
- return Severity::Error;
- }
-}
-
-static RefPtr<ExtensionTracker> _newExtensionTracker(CodeGenTarget target)
-{
- switch (target)
- {
- case CodeGenTarget::PTX:
- case CodeGenTarget::CUDASource:
- {
- return new CUDAExtensionTracker;
- }
- case CodeGenTarget::SPIRV:
- case CodeGenTarget::GLSL:
- case CodeGenTarget::WGSL:
- case CodeGenTarget::WGSLSPIRV:
- case CodeGenTarget::WGSLSPIRVAssembly:
- {
- return new ShaderExtensionTracker;
- }
- default:
- return nullptr;
- }
-}
-
-static CodeGenTarget _getDefaultSourceForTarget(CodeGenTarget target)
-{
- switch (target)
- {
- case CodeGenTarget::ShaderHostCallable:
- case CodeGenTarget::ShaderSharedLibrary:
- {
- return CodeGenTarget::CPPSource;
- }
- case CodeGenTarget::HostHostCallable:
- case CodeGenTarget::HostExecutable:
- case CodeGenTarget::HostSharedLibrary:
- {
- return CodeGenTarget::HostCPPSource;
- }
- case CodeGenTarget::PTX:
- return CodeGenTarget::CUDASource;
- case CodeGenTarget::DXBytecode:
- return CodeGenTarget::HLSL;
- case CodeGenTarget::DXIL:
- return CodeGenTarget::HLSL;
- case CodeGenTarget::SPIRV:
- return CodeGenTarget::GLSL;
- case CodeGenTarget::MetalLib:
- return CodeGenTarget::Metal;
- case CodeGenTarget::WGSLSPIRV:
- return CodeGenTarget::WGSL;
- default:
- break;
- }
- return CodeGenTarget::Unknown;
-}
-
-static bool _isCPUHostTarget(CodeGenTarget target)
-{
- auto desc = ArtifactDescUtil::makeDescForCompileTarget(asExternal(target));
- return desc.style == ArtifactStyle::Host;
-}
-
-static bool _shouldSetEntryPointName(TargetProgram* targetProgram)
-{
- if (!isKhronosTarget(targetProgram->getTargetReq()))
- return true;
- if (targetProgram->getOptionSet().getBoolOption(CompilerOptionName::VulkanUseEntryPointName))
- return true;
- return false;
-}
-
-SlangResult passthroughDownstreamDiagnostics(
- DiagnosticSink* sink,
- IDownstreamCompiler* compiler,
- IArtifact* artifact)
-{
- auto diagnostics = findAssociatedRepresentation<IArtifactDiagnostics>(artifact);
-
- if (!diagnostics)
- return SLANG_OK;
-
- if (diagnostics->getCount())
- {
- StringBuilder compilerText;
- DownstreamCompilerUtil::appendAsText(compiler->getDesc(), compilerText);
-
- StringBuilder builder;
-
- auto const diagnosticCount = diagnostics->getCount();
- for (Index i = 0; i < diagnosticCount; ++i)
- {
- const auto& diagnostic = *diagnostics->getAt(i);
-
- builder.clear();
-
- const Severity severity = _getDiagnosticSeverity(diagnostic.severity);
-
- if (diagnostic.filePath.count == 0 && diagnostic.location.line == 0 &&
- severity == Severity::Note)
- {
- // If theres no filePath line number and it's info, output severity and text alone
- builder << getSeverityName(severity) << " : ";
- }
- else
- {
- if (diagnostic.filePath.count)
- {
- builder << asStringSlice(diagnostic.filePath);
- }
-
- if (diagnostic.location.line)
- {
- builder << "(" << diagnostic.location.line << ")";
- }
-
- builder << ": ";
-
- if (diagnostic.stage == ArtifactDiagnostic::Stage::Link)
- {
- builder << "link ";
- }
-
- builder << getSeverityName(severity);
- builder << " " << asStringSlice(diagnostic.code) << ": ";
- }
-
- builder << asStringSlice(diagnostic.text);
- reportExternalCompileError(
- compilerText.getBuffer(),
- severity,
- SLANG_OK,
- builder.getUnownedSlice(),
- sink);
- }
- }
-
- // If any errors are emitted, then we are done
- if (diagnostics->hasOfAtLeastSeverity(ArtifactDiagnostic::Severity::Error))
- {
- return SLANG_FAIL;
- }
-
- return SLANG_OK;
-}
-
bool isValidSlangLanguageVersion(SlangLanguageVersion version)
{
switch (version)
@@ -1258,1885 +44,4 @@ bool isValidGLSLVersion(int version)
}
}
-SlangResult CodeGenContext::emitWithDownstreamForEntryPoints(ComPtr<IArtifact>& outArtifact)
-{
- outArtifact.setNull();
-
- auto sink = getSink();
- auto session = getSession();
-
- CodeGenTarget sourceTarget = CodeGenTarget::None;
- SourceLanguage sourceLanguage = SourceLanguage::Unknown;
-
- auto target = getTargetFormat();
- RefPtr<ExtensionTracker> extensionTracker = _newExtensionTracker(target);
- PassThroughMode compilerType;
-
- SliceAllocator allocator;
-
- if (auto endToEndReq = isPassThroughEnabled())
- {
- compilerType = endToEndReq->m_passThrough;
- }
- else
- {
- // If we are not in pass through, lookup the default compiler for the emitted source type
-
- // Get the default source codegen type for a given target
- sourceTarget = _getDefaultSourceForTarget(target);
- compilerType = (PassThroughMode)session->getDownstreamCompilerForTransition(
- (SlangCompileTarget)sourceTarget,
- (SlangCompileTarget)target);
- // We should have a downstream compiler set at this point
- if (compilerType == PassThroughMode::None)
- {
- auto sourceName = TypeTextUtil::getCompileTargetName(SlangCompileTarget(sourceTarget));
- auto targetName = TypeTextUtil::getCompileTargetName(SlangCompileTarget(target));
-
- sink->diagnose(
- SourceLoc(),
- Diagnostics::compilerNotDefinedForTransition,
- sourceName,
- targetName);
- return SLANG_FAIL;
- }
- }
-
- SLANG_ASSERT(compilerType != PassThroughMode::None);
-
- // Get the required downstream compiler
- IDownstreamCompiler* compiler = session->getOrLoadDownstreamCompiler(compilerType, sink);
- if (!compiler)
- {
- auto compilerName = TypeTextUtil::getPassThroughAsHumanText((SlangPassThrough)compilerType);
- sink->diagnose(SourceLoc(), Diagnostics::passThroughCompilerNotFound, compilerName);
- return SLANG_FAIL;
- }
-
- Dictionary<String, String> preprocessorDefinitions;
- List<String> includePaths;
-
- typedef DownstreamCompileOptions CompileOptions;
- CompileOptions options;
-
- List<DownstreamCompileOptions::CapabilityVersion> requiredCapabilityVersions;
- List<String> compilerSpecificArguments;
- List<ComPtr<IArtifact>> libraries;
- List<String> libraryPaths;
-
- // Set compiler specific args
- {
- auto name = TypeTextUtil::getPassThroughName((SlangPassThrough)compilerType);
- List<String> downstreamArgs = getTargetProgram()->getOptionSet().getDownstreamArgs(name);
- for (const auto& arg : downstreamArgs)
- {
- // We special case some kinds of args, that can be handled directly
- if (arg.startsWith("-I"))
- {
- // We handle the -I option, by just adding to the include paths
- includePaths.add(arg.getUnownedSlice().tail(2));
- }
- else
- {
- compilerSpecificArguments.add(arg);
- }
- }
- }
-
- ComPtr<IArtifact> sourceArtifact;
-
- /* This is more convoluted than the other scenarios, because when we invoke C/C++ compiler we
- would ideally like to use the original file. We want to do this because we want includes
- relative to the source file to work, and for that to work most easily we want to use the
- original file, if there is one */
- if (auto endToEndReq = isPassThroughEnabled())
- {
- // If we are pass through, we may need to set extension tracker state.
- if (ShaderExtensionTracker* glslTracker = as<ShaderExtensionTracker>(extensionTracker))
- {
- trackGLSLTargetCaps(glslTracker, getTargetCaps());
- }
-
- auto translationUnit =
- getPassThroughTranslationUnit(endToEndReq, getSingleEntryPointIndex());
-
- // We are just passing thru, so it's whatever it originally was
- sourceLanguage = translationUnit->sourceLanguage;
-
- // TODO(JS): This seems like a bit of a hack
- // That if a pass-through is being performed and the source language is Slang
- // no downstream compiler knows how to deal with that, so probably means 'HLSL'
- sourceLanguage =
- (sourceLanguage == SourceLanguage::Slang) ? SourceLanguage::HLSL : sourceLanguage;
- sourceTarget = CodeGenTarget(TypeConvertUtil::getCompileTargetFromSourceLanguage(
- (SlangSourceLanguage)sourceLanguage));
-
- // If it's pass through we accumulate the preprocessor definitions.
- for (const auto& define :
- endToEndReq->getOptionSet().getArray(CompilerOptionName::MacroDefine))
- preprocessorDefinitions.add(define.stringValue, define.stringValue2);
- for (const auto& define : translationUnit->preprocessorDefinitions)
- preprocessorDefinitions.add(define);
-
- {
- /* TODO(JS): Not totally clear what options should be set here. If we are using the pass
- through - then using say the defines/includes all makes total sense. If we are
- generating C++ code from slang, then should we really be using these values -> aren't
- they what is being set for the *slang* source, not for the C++ generated code. That
- being the case it implies that there needs to be a mechanism (if there isn't already) to
- specify such information on a particular pass/pass through etc.
-
- On invoking DXC for example include paths do not appear to be set at all (even with
- pass-through).
- */
-
- auto linkage = getLinkage();
-
- // Add all the search paths
-
- const auto searchDirectories = linkage->getSearchDirectories();
- const SearchDirectoryList* searchList = &searchDirectories;
- while (searchList)
- {
- for (const auto& searchDirectory : searchList->searchDirectories)
- {
- includePaths.add(searchDirectory.path);
- }
- searchList = searchList->parent;
- }
- }
-
- // If emitted source is required, emit and set the path
- if (_useEmittedSource(compiler, translationUnit))
- {
- CodeGenContext sourceCodeGenContext(this, sourceTarget, extensionTracker);
-
- SLANG_RETURN_ON_FAIL(sourceCodeGenContext.emitEntryPointsSource(sourceArtifact));
-
- // If it's not file based we can set an appropriate path name, and it doesn't matter if
- // it doesn't exist on the file system. We set the name to the path as this will be used
- // for downstream reporting.
- auto sourcePath = calcSourcePathForEntryPoints();
- sourceArtifact->setName(sourcePath.getBuffer());
-
- sourceCodeGenContext.maybeDumpIntermediate(sourceArtifact);
- }
- else
- {
- // Special case if we have a single file, so that we pass the path, and the contents as
- // is.
- const auto& sourceArtifacts = translationUnit->getSourceArtifacts();
- SLANG_ASSERT(sourceArtifacts.getCount() == 1);
-
- sourceArtifact = sourceArtifacts[0];
- SLANG_ASSERT(sourceArtifact);
- }
- }
- else
- {
- CodeGenContext sourceCodeGenContext(this, sourceTarget, extensionTracker);
-
- sourceCodeGenContext.removeAvailableInDownstreamIR = true;
-
- SLANG_RETURN_ON_FAIL(sourceCodeGenContext.emitEntryPointsSource(sourceArtifact));
- sourceCodeGenContext.maybeDumpIntermediate(sourceArtifact);
-
- sourceLanguage = (SourceLanguage)TypeConvertUtil::getSourceLanguageFromTarget(
- (SlangCompileTarget)sourceTarget);
- }
-
- if (sourceArtifact)
- {
- // Set the source artifacts
- options.sourceArtifacts = makeSlice(sourceArtifact.readRef(), 1);
- }
-
- // Add any preprocessor definitions associated with the linkage
- {
- // TODO(JS): This is somewhat arguable - should defines passed to Slang really be
- // passed to downstream compilers? It does appear consistent with the behavior if
- // there is an endToEndReq.
- //
- // That said it's very convenient and provides way to control aspects
- // of downstream compilation.
-
- for (const auto& define :
- getTargetProgram()->getOptionSet().getArray(CompilerOptionName::MacroDefine))
- {
- preprocessorDefinitions.addIfNotExists(define.stringValue, define.stringValue2);
- }
- }
-
-
- // If we have an extension tracker, we may need to set options such as SPIR-V version
- // and CUDA Shader Model.
- if (extensionTracker)
- {
- // Look for the version
- if (auto cudaTracker = as<CUDAExtensionTracker>(extensionTracker))
- {
- cudaTracker->finalize();
-
- if (cudaTracker->m_smVersion.isSet())
- {
- DownstreamCompileOptions::CapabilityVersion version;
- version.kind = DownstreamCompileOptions::CapabilityVersion::Kind::CUDASM;
- version.version = cudaTracker->m_smVersion;
-
- requiredCapabilityVersions.add(version);
- }
-
- if (cudaTracker->isBaseTypeRequired(BaseType::Half))
- {
- options.flags |= CompileOptions::Flag::EnableFloat16;
- }
- }
- else if (ShaderExtensionTracker* glslTracker = as<ShaderExtensionTracker>(extensionTracker))
- {
- DownstreamCompileOptions::CapabilityVersion version;
- version.kind = DownstreamCompileOptions::CapabilityVersion::Kind::SPIRV;
- version.version = glslTracker->getSPIRVVersion();
-
- requiredCapabilityVersions.add(version);
- }
- }
-
- CapabilitySet targetCaps = getTargetCaps();
- for (auto atomSets : targetCaps.getAtomSets())
- {
- for (auto atomVal : atomSets)
- {
- auto atom = CapabilityAtom(atomVal);
- switch (atom)
- {
- default:
- break;
-
-#define CASE(KIND, NAME, VERSION) \
- case CapabilityAtom::NAME: \
- requiredCapabilityVersions.add(DownstreamCompileOptions::CapabilityVersion{ \
- DownstreamCompileOptions::CapabilityVersion::Kind::KIND, \
- VERSION}); \
- break
-
- CASE(CUDASM, _cuda_sm_1_0, SemanticVersion(1, 0));
- CASE(CUDASM, _cuda_sm_2_0, SemanticVersion(2, 0));
- CASE(CUDASM, _cuda_sm_3_0, SemanticVersion(3, 0));
- CASE(CUDASM, _cuda_sm_4_0, SemanticVersion(4, 0));
- CASE(CUDASM, _cuda_sm_5_0, SemanticVersion(5, 0));
- CASE(CUDASM, _cuda_sm_6_0, SemanticVersion(6, 0));
- CASE(CUDASM, _cuda_sm_7_0, SemanticVersion(7, 0));
- CASE(CUDASM, _cuda_sm_8_0, SemanticVersion(8, 0));
- CASE(CUDASM, _cuda_sm_9_0, SemanticVersion(9, 0));
-
-#undef CASE
- }
- }
- }
-
- // Set the file sytem and source manager, as *may* be used by downstream compiler
- options.fileSystemExt = getFileSystemExt();
- options.sourceManager = getSourceManager();
-
- // Set the source type
- options.sourceLanguage = SlangSourceLanguage(sourceLanguage);
-
- switch (target)
- {
- case CodeGenTarget::ShaderHostCallable:
- case CodeGenTarget::ShaderSharedLibrary:
- // Disable exceptions and security checks
- options.flags &=
- ~(CompileOptions::Flag::EnableExceptionHandling |
- CompileOptions::Flag::EnableSecurityChecks);
- break;
- }
-
- Profile profile;
-
- if (compilerType == PassThroughMode::Fxc || compilerType == PassThroughMode::Dxc ||
- compilerType == PassThroughMode::Glslang)
- {
- const auto entryPointIndices = getEntryPointIndices();
- auto targetReq = getTargetReq();
-
- const auto entryPointIndicesCount = entryPointIndices.getCount();
-
- // Whole program means
- // * can have 0-N entry points
- // * 'doesn't build into an executable/kernel'
- //
- // So in some sense it is a library
- if (getTargetProgram()->getOptionSet().getBoolOption(
- CompilerOptionName::GenerateWholeProgram))
- {
- if (compilerType == PassThroughMode::Dxc)
- {
- // Can support no entry points on DXC because we can build libraries
- profile =
- Profile(getTargetProgram()->getOptionSet().getEnumOption<Profile::RawEnum>(
- CompilerOptionName::Profile));
- }
- else
- {
- auto downstreamCompilerName =
- TypeTextUtil::getPassThroughName((SlangPassThrough)compilerType);
-
- sink->diagnose(
- SourceLoc(),
- Diagnostics::downstreamCompilerDoesntSupportWholeProgramCompilation,
- downstreamCompilerName);
- return SLANG_FAIL;
- }
- }
- else if (entryPointIndicesCount == 1)
- {
- // All support a single entry point
- const Index entryPointIndex = entryPointIndices[0];
-
- auto entryPoint = getEntryPoint(entryPointIndex);
- profile = getEffectiveProfile(entryPoint, targetReq);
-
- if (_shouldSetEntryPointName(getTargetProgram()))
- {
- options.entryPointName = allocator.allocate(getText(entryPoint->getName()));
- auto entryPointNameOverride =
- getProgram()->getEntryPointNameOverride(entryPointIndex);
- if (entryPointNameOverride.getLength() != 0)
- {
- options.entryPointName = allocator.allocate(entryPointNameOverride);
- }
- }
- }
- else
- {
- // We only support a single entry point on this target
- SLANG_ASSERT(!"Can only compile with a single entry point on this target");
- return SLANG_FAIL;
- }
-
- options.stage = SlangStage(profile.getStage());
-
- if (compilerType == PassThroughMode::Dxc)
- {
- // We will enable the flag to generate proper code for 16 - bit types
- // by default, as long as the user is requesting a sufficiently
- // high shader model.
- //
- // TODO: Need to check that this is safe to enable in all cases,
- // or if it will make a shader demand hardware features that
- // aren't always present.
- //
- // TODO: Ideally the dxc back-end should be passed some information
- // on the "capabilities" that were used and/or requested in the code.
- //
- if (profile.getVersion() >= ProfileVersion::DX_6_2)
- {
- options.flags |= CompileOptions::Flag::EnableFloat16;
- }
-
- // Set the matrix layout
- options.matrixLayout =
- (SlangMatrixLayoutMode)getTargetProgram()->getOptionSet().getMatrixLayoutMode();
- }
-
- // Set the profile
- options.profileName = allocator.allocate(GetHLSLProfileName(profile));
- }
-
- // If we aren't using LLVM 'host callable', we want downstream compile to produce a shared
- // library
- if (compilerType != PassThroughMode::LLVM &&
- ArtifactDescUtil::makeDescForCompileTarget(asExternal(target)).kind ==
- ArtifactKind::HostCallable)
- {
- target = CodeGenTarget::ShaderSharedLibrary;
- }
-
- if (!isPassThroughEnabled())
- {
- if (_isCPUHostTarget(target))
- {
- libraryPaths.add(Path::getParentDirectory(Path::getExecutablePath()));
- libraryPaths.add(
- Path::combine(Path::getParentDirectory(Path::getExecutablePath()), "../lib"));
-
- // Set up the library artifact
- auto artifact = Artifact::create(
- ArtifactDesc::make(ArtifactKind::Library, Artifact::Payload::HostCPU),
- toSlice("slang-rt"));
-
- ComPtr<IOSFileArtifactRepresentation> fileRep(new OSFileArtifactRepresentation(
- IOSFileArtifactRepresentation::Kind::NameOnly,
- toSlice("slang-rt"),
- nullptr));
- artifact->addRepresentation(fileRep);
-
- libraries.add(artifact);
- }
- }
-
- options.targetType = (SlangCompileTarget)target;
-
- // Need to configure for the compilation
-
- {
- auto linkage = getLinkage();
-
- switch (getTargetProgram()->getOptionSet().getEnumOption<OptimizationLevel>(
- CompilerOptionName::Optimization))
- {
- case OptimizationLevel::None:
- options.optimizationLevel = DownstreamCompileOptions::OptimizationLevel::None;
- break;
- case OptimizationLevel::Default:
- options.optimizationLevel = DownstreamCompileOptions::OptimizationLevel::Default;
- break;
- case OptimizationLevel::High:
- options.optimizationLevel = DownstreamCompileOptions::OptimizationLevel::High;
- break;
- case OptimizationLevel::Maximal:
- options.optimizationLevel = DownstreamCompileOptions::OptimizationLevel::Maximal;
- break;
- default:
- SLANG_ASSERT(!"Unhandled optimization level");
- break;
- }
-
- switch (getTargetProgram()->getOptionSet().getEnumOption<DebugInfoLevel>(
- CompilerOptionName::DebugInformation))
- {
- case DebugInfoLevel::None:
- options.debugInfoType = DownstreamCompileOptions::DebugInfoType::None;
- break;
- case DebugInfoLevel::Minimal:
- options.debugInfoType = DownstreamCompileOptions::DebugInfoType::Minimal;
- break;
-
- case DebugInfoLevel::Standard:
- options.debugInfoType = DownstreamCompileOptions::DebugInfoType::Standard;
- break;
- case DebugInfoLevel::Maximal:
- options.debugInfoType = DownstreamCompileOptions::DebugInfoType::Maximal;
- break;
- default:
- SLANG_ASSERT(!"Unhandled debug level");
- break;
- }
-
- switch (getTargetProgram()->getOptionSet().getEnumOption<FloatingPointMode>(
- CompilerOptionName::FloatingPointMode))
- {
- case FloatingPointMode::Default:
- options.floatingPointMode = DownstreamCompileOptions::FloatingPointMode::Default;
- break;
- case FloatingPointMode::Precise:
- options.floatingPointMode = DownstreamCompileOptions::FloatingPointMode::Precise;
- break;
- case FloatingPointMode::Fast:
- options.floatingPointMode = DownstreamCompileOptions::FloatingPointMode::Fast;
- break;
- default:
- SLANG_ASSERT(!"Unhandled floating point mode");
- }
-
- if (getTargetProgram()->getOptionSet().hasOption(CompilerOptionName::DenormalModeFp16))
- {
- switch (getTargetProgram()->getOptionSet().getEnumOption<FloatingPointDenormalMode>(
- CompilerOptionName::DenormalModeFp16))
- {
- case FloatingPointDenormalMode::Any:
- options.denormalModeFp16 = DownstreamCompileOptions::FloatingPointDenormalMode::Any;
- break;
- case FloatingPointDenormalMode::Preserve:
- options.denormalModeFp16 =
- DownstreamCompileOptions::FloatingPointDenormalMode::Preserve;
- break;
- case FloatingPointDenormalMode::FlushToZero:
- options.denormalModeFp16 =
- DownstreamCompileOptions::FloatingPointDenormalMode::FlushToZero;
- break;
- default:
- SLANG_ASSERT(!"Unhandled fp16 denormal handling mode");
- }
- }
-
- if (getTargetProgram()->getOptionSet().hasOption(CompilerOptionName::DenormalModeFp32))
- {
- switch (getTargetProgram()->getOptionSet().getEnumOption<FloatingPointDenormalMode>(
- CompilerOptionName::DenormalModeFp32))
- {
- case FloatingPointDenormalMode::Any:
- options.denormalModeFp32 = DownstreamCompileOptions::FloatingPointDenormalMode::Any;
- break;
- case FloatingPointDenormalMode::Preserve:
- options.denormalModeFp32 =
- DownstreamCompileOptions::FloatingPointDenormalMode::Preserve;
- break;
- case FloatingPointDenormalMode::FlushToZero:
- options.denormalModeFp32 =
- DownstreamCompileOptions::FloatingPointDenormalMode::FlushToZero;
- break;
- default:
- SLANG_ASSERT(!"Unhandled fp32 denormal handling mode");
- }
- }
-
- if (getTargetProgram()->getOptionSet().hasOption(CompilerOptionName::DenormalModeFp64))
- {
- switch (getTargetProgram()->getOptionSet().getEnumOption<FloatingPointDenormalMode>(
- CompilerOptionName::DenormalModeFp64))
- {
- case FloatingPointDenormalMode::Any:
- options.denormalModeFp64 = DownstreamCompileOptions::FloatingPointDenormalMode::Any;
- break;
- case FloatingPointDenormalMode::Preserve:
- options.denormalModeFp64 =
- DownstreamCompileOptions::FloatingPointDenormalMode::Preserve;
- break;
- case FloatingPointDenormalMode::FlushToZero:
- options.denormalModeFp64 =
- DownstreamCompileOptions::FloatingPointDenormalMode::FlushToZero;
- break;
- default:
- SLANG_ASSERT(!"Unhandled fp64 denormal handling mode");
- }
- }
-
- {
- // We need to look at the stage of the entry point(s) we are
- // being asked to compile, since this will determine the
- // "pipeline" that the result should be compiled for (e.g.,
- // compute vs. ray tracing).
- //
- // TODO: This logic is kind of messy in that it assumes
- // a program to be compiled will only contain kernels for
- // a single pipeline type, but that invariant isn't expressed
- // at all in the front-end today. It also has no error
- // checking for the case where there are conflicts.
- //
- // HACK: Right now none of the above concerns matter
- // because we always perform code generation on a single
- // entry point at a time.
- //
- Index entryPointCount = getEntryPointCount();
- for (Index ee = 0; ee < entryPointCount; ++ee)
- {
- auto stage = getEntryPoint(ee)->getStage();
- switch (stage)
- {
- default:
- break;
-
- case Stage::Compute:
- options.pipelineType = DownstreamCompileOptions::PipelineType::Compute;
- break;
-
- case Stage::Vertex:
- case Stage::Hull:
- case Stage::Domain:
- case Stage::Geometry:
- case Stage::Fragment:
- options.pipelineType = DownstreamCompileOptions::PipelineType::Rasterization;
- break;
-
- case Stage::RayGeneration:
- case Stage::Intersection:
- case Stage::AnyHit:
- case Stage::ClosestHit:
- case Stage::Miss:
- case Stage::Callable:
- options.pipelineType = DownstreamCompileOptions::PipelineType::RayTracing;
- break;
- }
- }
- }
-
- // Add all the search paths (as calculated earlier - they will only be set if this is a pass
- // through else will be empty)
- options.includePaths = allocator.allocate(includePaths);
-
- // Add the specified defines (as calculated earlier - they will only be set if this is a
- // pass through else will be empty)
- {
- const auto count = preprocessorDefinitions.getCount();
- auto dst = allocator.getArena().allocateArray<DownstreamCompileOptions::Define>(count);
-
- Index i = 0;
-
- for (const auto& [defKey, defValue] : preprocessorDefinitions)
- {
- auto& define = dst[i];
-
- define.nameWithSig = allocator.allocate(defKey);
- define.value = allocator.allocate(defValue);
-
- ++i;
- }
- options.defines = makeSlice(dst, count);
- }
-
- // Add all of the module libraries
- libraries.addRange(linkage->m_libModules.getBuffer(), linkage->m_libModules.getCount());
- }
-
- auto program = getProgram();
-
- // Load embedded precompiled libraries from IR into library artifacts
- program->enumerateIRModules(
- [&](IRModule* irModule)
- {
- for (auto globalInst : irModule->getModuleInst()->getChildren())
- {
- if (target == CodeGenTarget::DXILAssembly || target == CodeGenTarget::DXIL)
- {
- if (auto inst = as<IREmbeddedDownstreamIR>(globalInst))
- {
- if (inst->getTarget() == CodeGenTarget::DXIL)
- {
- auto slice = inst->getBlob()->getStringSlice();
- ArtifactDesc desc =
- ArtifactDescUtil::makeDescForCompileTarget(SLANG_DXIL);
- desc.kind = ArtifactKind::Library;
-
- auto library = ArtifactUtil::createArtifact(desc);
-
- library->addRepresentationUnknown(StringBlob::create(slice));
- libraries.add(library);
- }
- }
- }
- }
- });
-
- options.compilerSpecificArguments = allocator.allocate(compilerSpecificArguments);
- options.requiredCapabilityVersions = SliceUtil::asSlice(requiredCapabilityVersions);
- options.libraries = SliceUtil::asSlice(libraries);
- options.libraryPaths = allocator.allocate(libraryPaths);
-
- if (m_targetProfile.getFamily() == ProfileFamily::DX)
- {
- options.enablePAQ = m_targetProfile.getVersion() >= ProfileVersion::DX_6_7;
- }
-
- // Compile
- ComPtr<IArtifact> artifact;
- auto downstreamStartTime = std::chrono::high_resolution_clock::now();
- SLANG_RETURN_ON_FAIL(compiler->compile(options, artifact.writeRef()));
- auto downstreamElapsedTime =
- (std::chrono::high_resolution_clock::now() - downstreamStartTime).count() * 0.000000001;
- getSession()->addDownstreamCompileTime(downstreamElapsedTime);
-
- SLANG_RETURN_ON_FAIL(passthroughDownstreamDiagnostics(getSink(), compiler, artifact));
-
- // Copy over all of the information associated with the source into the output
- if (sourceArtifact)
- {
- for (auto associatedArtifact : sourceArtifact->getAssociated())
- {
- artifact->addAssociated(associatedArtifact);
- }
- }
-
- // Set the artifact
- outArtifact.swap(artifact);
- return SLANG_OK;
-}
-
-SlangResult emitSPIRVForEntryPointsDirectly(
- CodeGenContext* codeGenContext,
- ComPtr<IArtifact>& outArtifact);
-
-SlangResult emitHostVMCode(CodeGenContext* codeGenContext, ComPtr<IArtifact>& outArtifact);
-
-static CodeGenTarget _getIntermediateTarget(CodeGenTarget target)
-{
- switch (target)
- {
- case CodeGenTarget::DXBytecodeAssembly:
- return CodeGenTarget::DXBytecode;
- case CodeGenTarget::DXILAssembly:
- return CodeGenTarget::DXIL;
- case CodeGenTarget::SPIRVAssembly:
- return CodeGenTarget::SPIRV;
- case CodeGenTarget::WGSLSPIRVAssembly:
- return CodeGenTarget::WGSLSPIRV;
- default:
- return CodeGenTarget::None;
- }
-}
-
-static IArtifact* _getSeparateDbgArtifact(IArtifact* artifact)
-{
- if (!artifact)
- return nullptr;
-
- // The first associated artifact of kind ObjectCode and SPIRV payload should be the debug
- // artifact.
- for (auto* associated : artifact->getAssociated())
- {
- auto desc = associated->getDesc();
- if (desc.kind == ArtifactKind::ObjectCode && desc.payload == ArtifactPayload::SPIRV)
- return associated;
- }
-
- return nullptr;
-}
-
-/// Function to simplify the logic around emitting, and dissassembling
-SlangResult CodeGenContext::_emitEntryPoints(ComPtr<IArtifact>& outArtifact)
-{
- auto target = getTargetFormat();
- switch (target)
- {
- case CodeGenTarget::SPIRVAssembly:
- case CodeGenTarget::DXBytecodeAssembly:
- case CodeGenTarget::DXILAssembly:
- case CodeGenTarget::MetalLibAssembly:
- case CodeGenTarget::WGSLSPIRVAssembly:
- {
- // First compile to an intermediate target for the corresponding binary format.
- const CodeGenTarget intermediateTarget = _getIntermediateTarget(target);
- CodeGenContext intermediateContext(this, intermediateTarget);
-
- ComPtr<IArtifact> intermediateArtifact;
-
- SLANG_RETURN_ON_FAIL(intermediateContext._emitEntryPoints(intermediateArtifact));
- intermediateContext.maybeDumpIntermediate(intermediateArtifact);
-
- // Then disassemble the intermediate binary result to get the desired output
- // Output the disassemble
- ComPtr<IArtifact> disassemblyArtifact;
- SLANG_RETURN_ON_FAIL(ArtifactOutputUtil::dissassembleWithDownstream(
- getSession(),
- intermediateArtifact,
- getSink(),
- disassemblyArtifact.writeRef()));
-
- // Also disassemble the debug artifact if one exists.
- auto debugArtifact = _getSeparateDbgArtifact(intermediateArtifact);
- ComPtr<IArtifact> disassemblyDebugArtifact;
- if (debugArtifact)
- {
- SLANG_RETURN_ON_FAIL(ArtifactOutputUtil::dissassembleWithDownstream(
- getSession(),
- debugArtifact,
- getSink(),
- disassemblyDebugArtifact.writeRef()));
- disassemblyDebugArtifact->setName(debugArtifact->getName());
-
- // The disassembly needs both the metadata for the debug build identifier
- // and the debug spirv to be associated with is.
- for (auto associated : intermediateArtifact->getAssociated())
- {
- if (associated->getDesc().payload == ArtifactPayload::Metadata ||
- associated->getDesc().payload == ArtifactPayload::PostEmitMetadata)
- {
- disassemblyArtifact->addAssociated(associated);
- break;
- }
- }
- disassemblyArtifact->addAssociated(disassemblyDebugArtifact);
- }
-
- outArtifact.swap(disassemblyArtifact);
- return SLANG_OK;
- }
- case CodeGenTarget::SPIRV:
- if (getTargetProgram()->getOptionSet().shouldEmitSPIRVDirectly())
- {
- SLANG_RETURN_ON_FAIL(emitSPIRVForEntryPointsDirectly(this, outArtifact));
- return SLANG_OK;
- }
- [[fallthrough]];
- case CodeGenTarget::DXIL:
- case CodeGenTarget::DXBytecode:
- case CodeGenTarget::MetalLib:
- case CodeGenTarget::PTX:
- case CodeGenTarget::ShaderHostCallable:
- case CodeGenTarget::ShaderSharedLibrary:
- case CodeGenTarget::HostExecutable:
- case CodeGenTarget::HostHostCallable:
- case CodeGenTarget::HostSharedLibrary:
- case CodeGenTarget::WGSLSPIRV:
- SLANG_RETURN_ON_FAIL(emitWithDownstreamForEntryPoints(outArtifact));
- return SLANG_OK;
- case CodeGenTarget::HostVM:
- SLANG_RETURN_ON_FAIL(emitHostVMCode(this, outArtifact));
- return SLANG_OK;
- default:
- break;
- }
-
- return SLANG_FAIL;
-}
-
-// Helper class for recording compile time.
-struct CompileTimerRAII
-{
- std::chrono::high_resolution_clock::time_point startTime;
- Session* session;
- CompileTimerRAII(Session* inSession)
- {
- startTime = std::chrono::high_resolution_clock::now();
- session = inSession;
- }
- ~CompileTimerRAII()
- {
- double elapsedTime = std::chrono::duration_cast<std::chrono::microseconds>(
- std::chrono::high_resolution_clock::now() - startTime)
- .count() /
- 1e6;
- session->addTotalCompileTime(elapsedTime);
- }
-};
-
-// Do emit logic for a zero or more entry points
-SlangResult CodeGenContext::emitEntryPoints(ComPtr<IArtifact>& outArtifact)
-{
- CompileTimerRAII recordCompileTime(getSession());
-
- auto target = getTargetFormat();
-
- switch (target)
- {
- case CodeGenTarget::SPIRVAssembly:
- case CodeGenTarget::DXBytecodeAssembly:
- case CodeGenTarget::DXILAssembly:
- case CodeGenTarget::SPIRV:
- case CodeGenTarget::DXIL:
- case CodeGenTarget::DXBytecode:
- case CodeGenTarget::MetalLib:
- case CodeGenTarget::MetalLibAssembly:
- case CodeGenTarget::PTX:
- case CodeGenTarget::HostHostCallable:
- case CodeGenTarget::ShaderHostCallable:
- case CodeGenTarget::ShaderSharedLibrary:
- case CodeGenTarget::HostExecutable:
- case CodeGenTarget::HostSharedLibrary:
- case CodeGenTarget::WGSLSPIRVAssembly:
- case CodeGenTarget::HostVM:
- {
- SLANG_RETURN_ON_FAIL(_emitEntryPoints(outArtifact));
-
- maybeDumpIntermediate(outArtifact);
- return SLANG_OK;
- }
- break;
- case CodeGenTarget::GLSL:
- case CodeGenTarget::HLSL:
- case CodeGenTarget::CUDASource:
- case CodeGenTarget::CPPSource:
- case CodeGenTarget::HostCPPSource:
- case CodeGenTarget::PyTorchCppBinding:
- case CodeGenTarget::CSource:
- case CodeGenTarget::Metal:
- case CodeGenTarget::WGSL:
- {
- RefPtr<ExtensionTracker> extensionTracker = _newExtensionTracker(target);
-
- CodeGenContext subContext(this, target, extensionTracker);
-
- ComPtr<IArtifact> sourceArtifact;
-
- SLANG_RETURN_ON_FAIL(subContext.emitEntryPointsSource(sourceArtifact));
-
- subContext.maybeDumpIntermediate(sourceArtifact);
- outArtifact = sourceArtifact;
- return SLANG_OK;
- }
- break;
-
- case CodeGenTarget::None:
- // The user requested no output
- return SLANG_OK;
-
- // Note(tfoley): We currently hit this case when compiling the core module
- case CodeGenTarget::Unknown:
- return SLANG_OK;
-
- default:
- SLANG_UNEXPECTED("unhandled code generation target");
- break;
- }
- return SLANG_FAIL;
-}
-
-void EndToEndCompileRequest::writeArtifactToStandardOutput(
- IArtifact* artifact,
- DiagnosticSink* sink)
-{
- // If it's host callable it's not available to write to output
- if (isDerivedFrom(artifact->getDesc().kind, ArtifactKind::HostCallable))
- {
- return;
- }
-
- auto session = getSession();
- ArtifactOutputUtil::maybeConvertAndWrite(
- session,
- artifact,
- sink,
- toSlice("stdout"),
- getWriter(WriterChannel::StdOutput));
-}
-
-String EndToEndCompileRequest::_getWholeProgramPath(TargetRequest* targetReq)
-{
- RefPtr<EndToEndCompileRequest::TargetInfo> targetInfo;
- if (m_targetInfos.tryGetValue(targetReq, targetInfo))
- {
- return targetInfo->wholeTargetOutputPath;
- }
- return String();
-}
-
-String EndToEndCompileRequest::_getEntryPointPath(TargetRequest* targetReq, Index entryPointIndex)
-{
- // It is possible that we are dynamically discovering entry
- // points (using `[shader(...)]` attributes), so that there
- // might be entry points added to the program that did not
- // get paths specified via command-line options.
- //
- RefPtr<EndToEndCompileRequest::TargetInfo> targetInfo;
- if (m_targetInfos.tryGetValue(targetReq, targetInfo))
- {
- String outputPath;
- if (targetInfo->entryPointOutputPaths.tryGetValue(entryPointIndex, outputPath))
- {
- return outputPath;
- }
- }
-
- return String();
-}
-
-SlangResult EndToEndCompileRequest::_writeArtifact(const String& path, IArtifact* artifact)
-{
- if (path.getLength() > 0)
- {
- SLANG_RETURN_ON_FAIL(ArtifactOutputUtil::writeToFile(artifact, getSink(), path));
- }
- else if (m_containerFormat == ContainerFormat::None)
- {
- // If we aren't writing to a container and we didn't write to a file, we can output to
- // standard output
- writeArtifactToStandardOutput(artifact, getSink());
- }
- return SLANG_OK;
-}
-
-SlangResult EndToEndCompileRequest::_maybeWriteArtifact(const String& path, IArtifact* artifact)
-{
- // We don't have to do anything if there is no artifact
- if (!artifact)
- {
- return SLANG_OK;
- }
-
- // If embedding is enabled...
- if (m_sourceEmbedStyle != SourceEmbedUtil::Style::None)
- {
- SourceEmbedUtil::Options options;
-
- options.style = m_sourceEmbedStyle;
- options.variableName = m_sourceEmbedName;
- options.language = (SlangSourceLanguage)m_sourceEmbedLanguage;
-
- ComPtr<IArtifact> embeddedArtifact;
- SLANG_RETURN_ON_FAIL(SourceEmbedUtil::createEmbedded(artifact, options, embeddedArtifact));
-
- if (!embeddedArtifact)
- {
- return SLANG_FAIL;
- }
- SLANG_RETURN_ON_FAIL(
- _writeArtifact(SourceEmbedUtil::getPath(path, options), embeddedArtifact));
- return SLANG_OK;
- }
- else
- {
- SLANG_RETURN_ON_FAIL(_writeArtifact(path, artifact));
- }
-
- return SLANG_OK;
-}
-
-// These helper functions are used by the -separate-debug-info command line
-// arg to extract the associated artifact containing the debug SPIRV data
-// and save it to a file with a .dbg.spv extension.
-static String _getDebugSpvPath(const String& basePath)
-{
- // Find the last occurrence of ".spv" at the end of the string.
- static const char ext[] = ".spv";
- static const char dbgExt[] = ".dbg.spv";
- Index extLen = 4;
- if (basePath.getLength() >= extLen && basePath.endsWith(ext))
- {
- // Replace the ".spv" extension with ".dbg.spv"
- String prefix = String(basePath.subString(0, basePath.getLength() - extLen));
- return prefix + dbgExt;
- }
- // If it doesn't end with .spv, just append .dbg.spv
- return basePath + dbgExt;
-}
-
-SlangResult EndToEndCompileRequest::_maybeWriteDebugArtifact(
- TargetProgram* targetProgram,
- const String& path,
- IArtifact* artifact)
-{
- if (targetProgram->getOptionSet().getBoolOption(CompilerOptionName::EmitSeparateDebug))
- {
- const auto dbgArtifact = _getSeparateDbgArtifact(artifact);
- // Check if a debug artifact was actually created (only for SPIR-V targets)
- if (dbgArtifact)
- {
- // The artifact's name may have been set to the debug build id hash, use
- // it as the filename if it exists.
- String dbgPath = dbgArtifact->getName();
- if (dbgPath.getLength() == 0)
- dbgPath = _getDebugSpvPath(path);
- else
- dbgPath.append(".dbg.spv");
- return _maybeWriteArtifact(dbgPath, dbgArtifact);
- }
- // If no debug artifact exists (e.g., for non-SPIR-V targets), just silently succeed
- // The warning about unsupported targets is already issued during option parsing
- }
-
- return SLANG_OK;
-}
-
-IArtifact* TargetProgram::_createWholeProgramResult(
- DiagnosticSink* sink,
- EndToEndCompileRequest* endToEndReq)
-{
- // We want to call `emitEntryPoints` function to generate code that contains
- // all the entrypoints defined in `m_program`.
- // The current logic of `emitEntryPoints` takes a list of entry-point indices to
- // emit code for, so we construct such a list first.
- List<Int> entryPointIndices;
-
- m_entryPointResults.setCount(m_program->getEntryPointCount());
- entryPointIndices.setCount(m_program->getEntryPointCount());
- for (Index i = 0; i < entryPointIndices.getCount(); i++)
- entryPointIndices[i] = i;
-
- CodeGenContext::Shared sharedCodeGenContext(this, entryPointIndices, sink, endToEndReq);
- CodeGenContext codeGenContext(&sharedCodeGenContext);
-
- if (SLANG_FAILED(codeGenContext.emitEntryPoints(m_wholeProgramResult)))
- {
- return nullptr;
- }
-
- return m_wholeProgramResult;
-}
-
-IArtifact* TargetProgram::_createEntryPointResult(
- Int entryPointIndex,
- DiagnosticSink* sink,
- EndToEndCompileRequest* endToEndReq)
-{
- // It is possible that entry points got added to the `Program`
- // *after* we created this `TargetProgram`, so there might be
- // a request for an entry point that we didn't allocate space for.
- //
- // TODO: Change the construction logic so that a `Program` is
- // constructed all at once rather than incrementally, to avoid
- // this problem.
- //
- if (entryPointIndex >= m_entryPointResults.getCount())
- m_entryPointResults.setCount(entryPointIndex + 1);
-
-
- CodeGenContext::EntryPointIndices entryPointIndices;
- entryPointIndices.add(entryPointIndex);
-
- CodeGenContext::Shared sharedCodeGenContext(this, entryPointIndices, sink, endToEndReq);
- CodeGenContext codeGenContext(&sharedCodeGenContext);
-
- codeGenContext.emitEntryPoints(m_entryPointResults[entryPointIndex]);
-
- return m_entryPointResults[entryPointIndex];
-}
-
-IArtifact* TargetProgram::getOrCreateWholeProgramResult(DiagnosticSink* sink)
-{
- if (m_wholeProgramResult)
- return m_wholeProgramResult;
-
- // If we haven't yet computed a layout for this target
- // program, we need to make sure that is done before
- // code generation.
- //
- if (!getOrCreateIRModuleForLayout(sink))
- {
- return nullptr;
- }
-
- return _createWholeProgramResult(sink);
-}
-
-IArtifact* TargetProgram::getOrCreateEntryPointResult(Int entryPointIndex, DiagnosticSink* sink)
-{
- if (entryPointIndex >= m_entryPointResults.getCount())
- m_entryPointResults.setCount(entryPointIndex + 1);
-
- if (IArtifact* artifact = m_entryPointResults[entryPointIndex])
- return artifact;
-
- // If we haven't yet computed a layout for this target
- // program, we need to make sure that is done before
- // code generation.
- //
- if (!getOrCreateIRModuleForLayout(sink))
- {
- return nullptr;
- }
-
- return _createEntryPointResult(entryPointIndex, sink);
-}
-
-void EndToEndCompileRequest::generateOutput(TargetProgram* targetProgram)
-{
- auto program = targetProgram->getProgram();
-
- // Generate target code any entry points that
- // have been requested for compilation.
- auto entryPointCount = program->getEntryPointCount();
- if (targetProgram->getOptionSet().getBoolOption(CompilerOptionName::GenerateWholeProgram))
- {
- targetProgram->_createWholeProgramResult(getSink(), this);
- }
- else
- {
- for (Index ii = 0; ii < entryPointCount; ++ii)
- {
- targetProgram->_createEntryPointResult(ii, getSink(), this);
- }
- }
-}
-
-
-bool _shouldWriteSourceLocs(Linkage* linkage)
-{
- // If debug information or source manager are not avaiable we can't/shouldn't write out locs
- if (linkage->m_optionSet.getEnumOption<DebugInfoLevel>(CompilerOptionName::DebugInformation) ==
- DebugInfoLevel::None ||
- linkage->getSourceManager() == nullptr)
- {
- return false;
- }
-
- // Otherwise we do want to write out the locs
- return true;
-}
-
-SlangResult EndToEndCompileRequest::writeContainerToStream(Stream* stream)
-{
- auto linkage = getLinkage();
-
- // Set up options
- SerialContainerUtil::WriteOptions options;
-
- // If debug information is enabled, enable writing out source locs
- if (_shouldWriteSourceLocs(linkage))
- {
- options.sourceManagerToUseWhenSerializingSourceLocs = linkage->getSourceManager();
- }
-
- SLANG_RETURN_ON_FAIL(SerialContainerUtil::write(this, options, stream));
-
- return SLANG_OK;
-}
-
-static IBoxValue<SourceMap>* _getObfuscatedSourceMap(TranslationUnitRequest* translationUnit)
-{
- if (auto module = translationUnit->getModule())
- {
- if (auto irModule = module->getIRModule())
- {
- return irModule->getObfuscatedSourceMap();
- }
- }
- return nullptr;
-}
-
-SlangResult EndToEndCompileRequest::maybeCreateContainer()
-{
- m_containerArtifact.setNull();
-
- List<ComPtr<IArtifact>> artifacts;
-
- auto linkage = getLinkage();
-
- auto program = getSpecializedGlobalAndEntryPointsComponentType();
-
- for (auto targetReq : linkage->targets)
- {
- auto targetProgram = program->getTargetProgram(targetReq);
-
- if (targetProgram->getOptionSet().getBoolOption(CompilerOptionName::GenerateWholeProgram))
- {
- if (auto artifact = targetProgram->getExistingWholeProgramResult())
- {
- if (!targetProgram->getOptionSet().getBoolOption(
- CompilerOptionName::EmbedDownstreamIR))
- {
- artifacts.add(ComPtr<IArtifact>(artifact));
- }
- }
- }
- else
- {
- Index entryPointCount = program->getEntryPointCount();
- for (Index ee = 0; ee < entryPointCount; ++ee)
- {
- if (auto artifact = targetProgram->getExistingEntryPointResult(ee))
- {
- artifacts.add(ComPtr<IArtifact>(artifact));
- }
- }
- }
- }
-
- // If IR emitting is enabled, add IR to the artifacts
- if (m_emitIr && (m_containerFormat == ContainerFormat::SlangModule))
- {
- OwnedMemoryStream stream(FileAccess::Write);
- SlangResult res = writeContainerToStream(&stream);
- if (SLANG_FAILED(res))
- {
- getSink()->diagnose(SourceLoc(), Diagnostics::unableToCreateModuleContainer);
- return res;
- }
-
- // Need to turn into a blob
- List<uint8_t> blobData;
- stream.swapContents(blobData);
-
- auto containerBlob = ListBlob::moveCreate(blobData);
-
- auto irArtifact = Artifact::create(ArtifactDesc::make(
- Artifact::Kind::CompileBinary,
- ArtifactPayload::SlangIR,
- ArtifactStyle::Unknown));
- irArtifact->addRepresentationUnknown(containerBlob);
-
- // Add the IR artifact
- artifacts.add(irArtifact);
- }
-
- // If there is only one artifact we can use that as the container
- if (artifacts.getCount() == 1)
- {
- m_containerArtifact = artifacts[0];
- }
- else
- {
- m_containerArtifact = ArtifactUtil::createArtifact(
- ArtifactDesc::make(ArtifactKind::Container, ArtifactPayload::CompileResults));
-
- for (IArtifact* childArtifact : artifacts)
- {
- m_containerArtifact->addChild(childArtifact);
- }
- }
-
- // Get all of the source obfuscated source maps and add those
- if (m_containerArtifact)
- {
- auto frontEndReq = getFrontEndReq();
-
- for (auto translationUnit : frontEndReq->translationUnits)
- {
- // Hmmm do I have to therefore add a map for all translation units(!)
- // I guess this is okay in so far as an association can always be looked up by name
- if (auto sourceMap = _getObfuscatedSourceMap(translationUnit))
- {
- auto artifactDesc = ArtifactDesc::make(
- ArtifactKind::Json,
- ArtifactPayload::SourceMap,
- ArtifactStyle::Obfuscated);
-
- // Create the source map artifact
- auto sourceMapArtifact =
- Artifact::create(artifactDesc, sourceMap->get().m_file.getUnownedSlice());
-
- // Add the repesentation
- sourceMapArtifact->addRepresentation(sourceMap);
-
- // Associate with the container
- m_containerArtifact->addAssociated(sourceMapArtifact);
- }
- }
- }
-
- return SLANG_OK;
-}
-
-CompilerOptionSet& EndToEndCompileRequest::getTargetOptionSet(TargetRequest* req)
-{
- return req->getOptionSet();
-}
-
-CompilerOptionSet& EndToEndCompileRequest::getTargetOptionSet(Index targetIndex)
-{
- return m_linkage->targets[targetIndex]->getOptionSet();
-}
-
-SlangResult EndToEndCompileRequest::maybeWriteContainer(const String& fileName)
-{
- // If there is no container, or filename, don't write anything
- if (fileName.getLength() == 0 || !m_containerArtifact)
- {
- return SLANG_OK;
- }
-
- // Filter the containerArtifact into things that can be written
- ComPtr<IArtifact> writeArtifact;
- SLANG_RETURN_ON_FAIL(ArtifactContainerUtil::filter(m_containerArtifact, writeArtifact));
-
- // Only write if there is something to write
- if (writeArtifact)
- {
- SLANG_RETURN_ON_FAIL(ArtifactContainerUtil::writeContainer(writeArtifact, fileName));
- }
-
- return SLANG_OK;
-}
-
-static void _writeString(Stream& stream, const char* string)
-{
- stream.write(string, strlen(string));
-}
-
-static void _escapeDependencyString(const char* string, StringBuilder& outBuilder)
-{
- // make has unusual escaping rules, but we only care about characters that are acceptable in a
- // path
- for (const char* p = string; *p; ++p)
- {
- char c = *p;
- switch (c)
- {
- case ' ':
- case ':':
- case '#':
- case '[':
- case ']':
- case '\\':
- outBuilder.appendChar('\\');
- break;
-
- case '$':
- outBuilder.appendChar('$');
- break;
- }
-
- outBuilder.appendChar(c);
- }
-}
-
-// Writes a line to the file stream, formatted like this:
-// <output-file>: <dependency-file> <dependency-file...>
-static void _writeDependencyStatement(
- Stream& stream,
- EndToEndCompileRequest* compileRequest,
- const String& outputPath)
-{
- if (outputPath.getLength() == 0)
- return;
-
- StringBuilder builder;
- _escapeDependencyString(outputPath.begin(), builder);
- _writeString(stream, builder.begin());
- _writeString(stream, ": ");
-
- int dependencyCount = compileRequest->getDependencyFileCount();
- for (int dependencyIndex = 0; dependencyIndex < dependencyCount; ++dependencyIndex)
- {
- builder.clear();
- _escapeDependencyString(compileRequest->getDependencyFilePath(dependencyIndex), builder);
- _writeString(stream, builder.begin());
- _writeString(stream, (dependencyIndex + 1 < dependencyCount) ? " " : "\n");
- }
-}
-
-// Writes a file with dependency info, with one line in the output file per compile product.
-static SlangResult _writeDependencyFile(EndToEndCompileRequest* compileRequest)
-{
- if (compileRequest->m_dependencyOutputPath.getLength() == 0)
- return SLANG_OK;
-
- FileStream stream;
- SLANG_RETURN_ON_FAIL(stream.init(
- compileRequest->m_dependencyOutputPath,
- FileMode::Create,
- FileAccess::Write,
- FileShare::ReadWrite));
-
- auto linkage = compileRequest->getLinkage();
- auto program = compileRequest->getSpecializedGlobalAndEntryPointsComponentType();
-
- // Iterate over all the targets and their outputs
- for (const auto& targetReq : linkage->targets)
- {
- if (compileRequest->getTargetOptionSet(targetReq).getBoolOption(
- CompilerOptionName::GenerateWholeProgram))
- {
- RefPtr<EndToEndCompileRequest::TargetInfo> targetInfo;
- if (compileRequest->m_targetInfos.tryGetValue(targetReq, targetInfo))
- {
- _writeDependencyStatement(
- stream,
- compileRequest,
- targetInfo->wholeTargetOutputPath);
- }
- }
- else
- {
- Index entryPointCount = program->getEntryPointCount();
- for (Index entryPointIndex = 0; entryPointIndex < entryPointCount; ++entryPointIndex)
- {
- RefPtr<EndToEndCompileRequest::TargetInfo> targetInfo;
- if (compileRequest->m_targetInfos.tryGetValue(targetReq, targetInfo))
- {
- String outputPath;
- if (targetInfo->entryPointOutputPaths.tryGetValue(entryPointIndex, outputPath))
- {
- _writeDependencyStatement(stream, compileRequest, outputPath);
- }
- }
- }
- }
- }
-
- // When the output is a binary module, linkage->targets can be empty. So
- // we need to do their dependencies separately.
- if (compileRequest->m_containerFormat == ContainerFormat::SlangModule)
- {
- _writeDependencyStatement(stream, compileRequest, compileRequest->m_containerOutputPath);
- }
-
- return SLANG_OK;
-}
-
-
-void EndToEndCompileRequest::generateOutput(ComponentType* program)
-{
- // When dynamic dispatch is disabled, the program must
- // be fully specialized by now. So we check if we still
- // have unspecialized generic/existential parameters,
- // and report them as an error.
- //
- auto specializationParamCount = program->getSpecializationParamCount();
- if (getOptionSet().getBoolOption(CompilerOptionName::DisableDynamicDispatch) &&
- specializationParamCount != 0)
- {
- auto sink = getSink();
-
- for (Index ii = 0; ii < specializationParamCount; ++ii)
- {
- auto specializationParam = program->getSpecializationParam(ii);
- if (auto decl = as<Decl>(specializationParam.object))
- {
- sink->diagnose(
- specializationParam.loc,
- Diagnostics::specializationParameterOfNameNotSpecialized,
- decl);
- }
- else if (auto type = as<Type>(specializationParam.object))
- {
- sink->diagnose(
- specializationParam.loc,
- Diagnostics::specializationParameterOfNameNotSpecialized,
- type);
- }
- else
- {
- sink->diagnose(
- specializationParam.loc,
- Diagnostics::specializationParameterNotSpecialized);
- }
- }
-
- return;
- }
-
-
- // Go through the code-generation targets that the user
- // has specified, and generate code for each of them.
- //
- auto linkage = getLinkage();
- for (auto targetReq : linkage->targets)
- {
- if (targetReq->getOptionSet().getBoolOption(CompilerOptionName::EmbedDownstreamIR))
- continue;
-
- auto targetProgram = program->getTargetProgram(targetReq);
- generateOutput(targetProgram);
- }
-}
-
-void EndToEndCompileRequest::generateOutput()
-{
- SLANG_PROFILE;
- generateOutput(getSpecializedGlobalAndEntryPointsComponentType());
-
- // If we are in command-line mode, we might be expected to actually
- // write output to one or more files here.
-
- if (m_isCommandLineCompile && m_containerFormat == ContainerFormat::None)
- {
- auto linkage = getLinkage();
- auto program = getSpecializedGlobalAndEntryPointsComponentType();
-
- for (auto targetReq : linkage->targets)
- {
- auto targetProgram = program->getTargetProgram(targetReq);
-
- if (targetProgram->getOptionSet().getBoolOption(
- CompilerOptionName::GenerateWholeProgram))
- {
- if (const auto artifact = targetProgram->getExistingWholeProgramResult())
- {
- const auto path = _getWholeProgramPath(targetReq);
-
- _maybeWriteArtifact(path, artifact);
-
- // If we are compiling separate debug info, check for the additional
- // SPIRV artifact and write that if needed.
- _maybeWriteDebugArtifact(targetProgram, path, artifact);
- }
- }
- else
- {
- Index entryPointCount = program->getEntryPointCount();
- for (Index ee = 0; ee < entryPointCount; ++ee)
- {
- if (const auto artifact = targetProgram->getExistingEntryPointResult(ee))
- {
- const auto path = _getEntryPointPath(targetReq, ee);
-
- _maybeWriteArtifact(path, artifact);
-
- // If we are compiling separate debug info, check for the additional
- // SPIRV artifact and write that if needed.
- _maybeWriteDebugArtifact(targetProgram, path, artifact);
- }
- }
- }
- }
- }
-
- // Maybe create the container
- maybeCreateContainer();
-
- // If it's a command line compile we may need to write the container to a file
- if (m_isCommandLineCompile)
- {
- // TODO(JS):
- // We could write the container into a source embedded format potentially
-
- maybeWriteContainer(m_containerOutputPath);
-
- _writeDependencyFile(this);
- }
-}
-
-// Debug logic for dumping intermediate outputs
-
-
-void CodeGenContext::_dumpIntermediateMaybeWithAssembly(IArtifact* artifact)
-{
- _dumpIntermediate(artifact);
-
- ComPtr<IArtifact> assembly;
- ArtifactOutputUtil::maybeDisassemble(getSession(), artifact, nullptr, assembly);
-
- if (assembly)
- {
- _dumpIntermediate(assembly);
- }
-}
-
-void CodeGenContext::_dumpIntermediate(IArtifact* artifact)
-{
- ComPtr<ISlangBlob> blob;
- if (SLANG_FAILED(artifact->loadBlob(ArtifactKeep::No, blob.writeRef())))
- {
- return;
- }
- _dumpIntermediate(artifact->getDesc(), blob->getBufferPointer(), blob->getBufferSize());
-}
-
-void CodeGenContext::_dumpIntermediate(const ArtifactDesc& desc, void const* data, size_t size)
-{
- // Try to generate a unique ID for the file to dump,
- // even in cases where there might be multiple threads
- // doing compilation.
- //
- // This is primarily a debugging aid, so we don't
- // really need/want to do anything too elaborate
-
- static std::atomic<uint32_t> counter(0);
-
- const uint32_t id = ++counter;
-
- // Just use the counter for the 'base name'
- StringBuilder basename;
-
- // Add the prefix
- basename << getIntermediateDumpPrefix();
-
- // Add the id
- basename << int(id);
-
- // Work out the filename based on the desc and the basename
- StringBuilder filename;
- ArtifactDescUtil::calcNameForDesc(desc, basename.getUnownedSlice(), filename);
-
- // If didn't produce a filename, use basename with .unknown extension
- if (filename.getLength() == 0)
- {
- filename = basename;
- filename << ".unknown";
- }
-
- // Write to a file
- ArtifactOutputUtil::writeToFile(desc, data, size, filename);
-}
-
-void CodeGenContext::maybeDumpIntermediate(IArtifact* artifact)
-{
- if (!shouldDumpIntermediates())
- return;
-
-
- _dumpIntermediateMaybeWithAssembly(artifact);
-}
-
-IRDumpOptions CodeGenContext::getIRDumpOptions()
-{
- if (auto endToEndReq = isEndToEndCompile())
- {
- return endToEndReq->getFrontEndReq()->m_irDumpOptions;
- }
- return IRDumpOptions();
-}
-
-bool CodeGenContext::shouldValidateIR()
-{
- return getTargetProgram()->getOptionSet().getBoolOption(CompilerOptionName::ValidateIr);
-}
-
-bool CodeGenContext::shouldSkipSPIRVValidation()
-{
- return getTargetProgram()->getOptionSet().getBoolOption(
- CompilerOptionName::SkipSPIRVValidation);
-}
-
-bool CodeGenContext::shouldDumpIR()
-{
- return getTargetProgram()->getOptionSet().getBoolOption(CompilerOptionName::DumpIr);
-}
-
-bool CodeGenContext::shouldSkipDownstreamLinking()
-{
- return getTargetProgram()->getOptionSet().getBoolOption(
- CompilerOptionName::SkipDownstreamLinking);
-}
-
-bool CodeGenContext::shouldReportCheckpointIntermediates()
-{
- return getTargetProgram()->getOptionSet().getBoolOption(
- CompilerOptionName::ReportCheckpointIntermediates);
-}
-
-bool CodeGenContext::shouldDumpIntermediates()
-{
- return getTargetProgram()->getOptionSet().getBoolOption(CompilerOptionName::DumpIntermediates);
-}
-
-bool CodeGenContext::shouldTrackLiveness()
-{
- return getTargetProgram()->getOptionSet().getBoolOption(CompilerOptionName::TrackLiveness);
-}
-
-String CodeGenContext::getIntermediateDumpPrefix()
-{
- return getTargetProgram()->getOptionSet().getStringOption(
- CompilerOptionName::DumpIntermediatePrefix);
-}
-
-bool CodeGenContext::getUseUnknownImageFormatAsDefault()
-{
- return getTargetProgram()->getOptionSet().getBoolOption(
- CompilerOptionName::DefaultImageFormatUnknown);
-}
-
-bool CodeGenContext::isSpecializationDisabled()
-{
- return getTargetProgram()->getOptionSet().getBoolOption(
- CompilerOptionName::DisableSpecialization);
-}
-
-SLANG_NO_THROW SlangResult SLANG_MCALL Module::serialize(ISlangBlob** outSerializedBlob)
-{
- SerialContainerUtil::WriteOptions writeOptions;
- OwnedMemoryStream memoryStream(FileAccess::Write);
- SLANG_RETURN_ON_FAIL(SerialContainerUtil::write(this, writeOptions, &memoryStream));
- *outSerializedBlob = RawBlob::create(
- memoryStream.getContents().getBuffer(),
- (size_t)memoryStream.getContents().getCount())
- .detach();
- return SLANG_OK;
-}
-
-SLANG_NO_THROW SlangResult SLANG_MCALL Module::writeToFile(char const* fileName)
-{
- SerialContainerUtil::WriteOptions writeOptions;
- FileStream fileStream;
- SLANG_RETURN_ON_FAIL(fileStream.init(fileName, FileMode::Create));
- return SerialContainerUtil::write(this, writeOptions, &fileStream);
-}
-
-SLANG_NO_THROW const char* SLANG_MCALL Module::getName()
-{
- if (m_name)
- return m_name->text.getBuffer();
- return nullptr;
-}
-
-SLANG_NO_THROW const char* SLANG_MCALL Module::getFilePath()
-{
- if (m_pathInfo.hasFoundPath())
- return m_pathInfo.foundPath.getBuffer();
- return nullptr;
-}
-
-SLANG_NO_THROW const char* SLANG_MCALL Module::getUniqueIdentity()
-{
- if (m_pathInfo.hasUniqueIdentity())
- return m_pathInfo.getMostUniqueIdentity().getBuffer();
- return nullptr;
-}
-
-SLANG_NO_THROW SlangInt32 SLANG_MCALL Module::getDependencyFileCount()
-{
- return (SlangInt32)getFileDependencies().getCount();
-}
-
-SLANG_NO_THROW char const* SLANG_MCALL Module::getDependencyFilePath(SlangInt32 index)
-{
- SourceFile* sourceFile = getFileDependencies()[index];
- return sourceFile->getPathInfo().hasFoundPath()
- ? sourceFile->getPathInfo().getMostUniqueIdentity().getBuffer()
- : nullptr;
-}
-
-void validateEntryPoint(EntryPoint* entryPoint, DiagnosticSink* sink);
-
-void Module::_discoverEntryPoints(DiagnosticSink* sink, const List<RefPtr<TargetRequest>>& targets)
-{
- if (m_entryPoints.getCount() > 0)
- return;
- _discoverEntryPointsImpl(m_moduleDecl, sink, targets);
-}
-void Module::_discoverEntryPointsImpl(
- ContainerDecl* containerDecl,
- DiagnosticSink* sink,
- const List<RefPtr<TargetRequest>>& targets)
-{
- for (auto globalDecl : containerDecl->getDirectMemberDecls())
- {
- auto maybeFuncDecl = globalDecl;
- if (auto genericDecl = as<GenericDecl>(maybeFuncDecl))
- {
- maybeFuncDecl = genericDecl->inner;
- }
-
- if (as<NamespaceDeclBase>(globalDecl) || as<FileDecl>(globalDecl) ||
- as<StructDecl>(globalDecl))
- {
- _discoverEntryPointsImpl(as<ContainerDecl>(globalDecl), sink, targets);
- continue;
- }
-
- auto funcDecl = as<FuncDecl>(maybeFuncDecl);
- if (!funcDecl)
- continue;
-
- Profile profile;
- bool resolvedStageOfProfileWithEntryPoint = resolveStageOfProfileWithEntryPoint(
- profile,
- getLinkage()->m_optionSet,
- targets,
- funcDecl,
- sink);
- if (!resolvedStageOfProfileWithEntryPoint)
- {
- // If there isn't a [shader] attribute, look for a [numthreads] attribute
- // since that implicitly means a compute shader. We'll not do this when compiling for
- // CUDA/Torch since [numthreads] attributes are utilized differently for those targets.
- //
-
- bool allTargetsCUDARelated = true;
- for (auto target : targets)
- {
- if (!isCUDATarget(target) &&
- target->getTarget() != CodeGenTarget::PyTorchCppBinding)
- {
- allTargetsCUDARelated = false;
- break;
- }
- }
-
- if (allTargetsCUDARelated && targets.getCount() > 0)
- continue;
-
- bool canDetermineStage = false;
- for (auto modifier : funcDecl->modifiers)
- {
- if (as<NumThreadsAttribute>(modifier))
- {
- if (funcDecl->findModifier<OutputTopologyAttribute>())
- profile.setStage(Stage::Mesh);
- else
- profile.setStage(Stage::Compute);
- canDetermineStage = true;
- break;
- }
- else if (as<PatchConstantFuncAttribute>(modifier))
- {
- profile.setStage(Stage::Hull);
- canDetermineStage = true;
- break;
- }
- }
- if (!canDetermineStage)
- continue;
- }
-
- RefPtr<EntryPoint> entryPoint =
- EntryPoint::create(getLinkage(), makeDeclRef(funcDecl), profile);
-
- validateEntryPoint(entryPoint, sink);
-
- // Note: in the case that the user didn't explicitly
- // specify entry points and we are instead compiling
- // a shader "library," then we do not want to automatically
- // combine the entry points into groups in the generated
- // `Program`, since that would be slightly too magical.
- //
- // Instead, each entry point will end up in a singleton
- // group, so that its entry-point parameters lay out
- // independent of the others.
- //
- _addEntryPoint(entryPoint);
- }
-}
} // namespace Slang
diff --git a/source/slang/slang-compiler.h b/source/slang/slang-compiler.h
index d45e796d9..934f86096 100644
--- a/source/slang/slang-compiler.h
+++ b/source/slang/slang-compiler.h
@@ -1,6 +1,23 @@
+// slang-compiler.h
#ifndef SLANG_COMPILER_H_INCLUDED
#define SLANG_COMPILER_H_INCLUDED
+//
+// This file provides an umbrella header that ties together
+// the headers for a bunch of the core types used by the
+// Slang compiler implementation: the global session, session,
+// modules, entry points, etc.
+//
+// Note: this file used to be a kind of kitchen-sink header
+// with thousands of lines of declarations, and even though
+// those declarations have migrated to their own files, this
+// header has been otherwise left as-is to avoid breaking
+// all of the code that `#include`s it.
+//
+// Please avoid adding new declarations in here without a clear
+// motivation for *why* they belong here.
+//
+
#include "../compiler-core/slang-artifact-representation-impl.h"
#include "../compiler-core/slang-command-line-args.h"
#include "../compiler-core/slang-downstream-compiler-util.h"
@@ -15,16 +32,29 @@
#include "../core/slang-file-system.h"
#include "../core/slang-shared-library.h"
#include "../core/slang-std-writers.h"
+#include "slang-base-type-info.h"
#include "slang-capability.h"
+#include "slang-code-gen.h"
#include "slang-com-ptr.h"
+#include "slang-compile-request.h"
+#include "slang-compiler-api.h"
#include "slang-compiler-options.h"
#include "slang-content-assist-info.h"
#include "slang-diagnostics.h"
+#include "slang-end-to-end-request.h"
+#include "slang-global-session.h"
#include "slang-hlsl-to-vulkan-layout-options.h"
+#include "slang-linkable-impls.h"
+#include "slang-linkable.h"
+#include "slang-module.h"
+#include "slang-pass-through.h"
#include "slang-preprocessor.h"
#include "slang-profile.h"
#include "slang-serialize-ir-types.h"
+#include "slang-session.h"
#include "slang-syntax.h"
+#include "slang-target.h"
+#include "slang-translation-unit.h"
#include "slang.h"
#include <chrono>
@@ -33,7 +63,6 @@ namespace Slang
{
struct PathInfo;
struct IncludeHandler;
-struct SharedSemanticsContext;
struct ModuleChunk;
class ProgramLayout;
@@ -50,61 +79,6 @@ enum class CompilerMode
GenerateChoice
};
-enum class StageTarget
-{
- Unknown,
- VertexShader,
- HullShader,
- DomainShader,
- GeometryShader,
- FragmentShader,
- ComputeShader,
-};
-
-enum class CodeGenTarget : SlangCompileTargetIntegral
-{
- Unknown = SLANG_TARGET_UNKNOWN,
- None = SLANG_TARGET_NONE,
- GLSL = SLANG_GLSL,
- HLSL = SLANG_HLSL,
- SPIRV = SLANG_SPIRV,
- SPIRVAssembly = SLANG_SPIRV_ASM,
- DXBytecode = SLANG_DXBC,
- DXBytecodeAssembly = SLANG_DXBC_ASM,
- DXIL = SLANG_DXIL,
- DXILAssembly = SLANG_DXIL_ASM,
- CSource = SLANG_C_SOURCE,
- CPPSource = SLANG_CPP_SOURCE,
- PyTorchCppBinding = SLANG_CPP_PYTORCH_BINDING,
- HostCPPSource = SLANG_HOST_CPP_SOURCE,
- HostExecutable = SLANG_HOST_EXECUTABLE,
- HostSharedLibrary = SLANG_HOST_SHARED_LIBRARY,
- ShaderSharedLibrary = SLANG_SHADER_SHARED_LIBRARY,
- ShaderHostCallable = SLANG_SHADER_HOST_CALLABLE,
- CUDASource = SLANG_CUDA_SOURCE,
- PTX = SLANG_PTX,
- CUDAObjectCode = SLANG_CUDA_OBJECT_CODE,
- ObjectCode = SLANG_OBJECT_CODE,
- HostHostCallable = SLANG_HOST_HOST_CALLABLE,
- Metal = SLANG_METAL,
- MetalLib = SLANG_METAL_LIB,
- MetalLibAssembly = SLANG_METAL_LIB_ASM,
- WGSL = SLANG_WGSL,
- WGSLSPIRVAssembly = SLANG_WGSL_SPIRV_ASM,
- WGSLSPIRV = SLANG_WGSL_SPIRV,
- HostVM = SLANG_HOST_VM,
- CountOf = SLANG_TARGET_COUNT_OF,
-};
-
-bool isHeterogeneousTarget(CodeGenTarget target);
-
-void printDiagnosticArg(StringBuilder& sb, CodeGenTarget val);
-
-enum class ContainerFormat : SlangContainerFormatIntegral
-{
- None = SLANG_CONTAINER_FORMAT_NONE,
- SlangModule = SLANG_CONTAINER_FORMAT_SLANG_MODULE,
-};
enum class LineDirectiveMode : SlangLineDirectiveModeIntegral
{
@@ -169,1866 +143,9 @@ class Linkage;
class Module;
class TranslationUnitRequest;
-/// Information collected about global or entry-point shader parameters
-struct ShaderParamInfo
-{
- DeclRef<VarDeclBase> paramDeclRef;
- Int firstSpecializationParamIndex = 0;
- Int specializationParamCount = 0;
-};
-
-/// A request for the front-end to find and validate an entry-point function
-struct FrontEndEntryPointRequest : RefObject
-{
-public:
- /// Create a request for an entry point.
- FrontEndEntryPointRequest(
- FrontEndCompileRequest* compileRequest,
- int translationUnitIndex,
- Name* name,
- Profile profile);
-
- /// Get the parent front-end compile request.
- FrontEndCompileRequest* getCompileRequest() { return m_compileRequest; }
-
- /// Get the translation unit that contains the entry point.
- TranslationUnitRequest* getTranslationUnit();
-
- /// Get the name of the entry point to find.
- Name* getName() { return m_name; }
-
- /// Get the stage that the entry point is to be compiled for
- Stage getStage() { return m_profile.getStage(); }
-
- /// Get the profile that the entry point is to be compiled for
- Profile getProfile() { return m_profile; }
-
- /// Get the index to the translation unit
- int getTranslationUnitIndex() const { return m_translationUnitIndex; }
-
-private:
- // The parent compile request
- FrontEndCompileRequest* m_compileRequest;
-
- // The index of the translation unit that will hold the entry point
- int m_translationUnitIndex;
-
- // The name of the entry point function to look for
- Name* m_name;
-
- // The profile to compile for (including stage)
- Profile m_profile;
-};
-
-/// Tracks an ordered list of modules that something depends on.
-/// TODO: Shader caching currently relies on this being in well defined order.
-struct ModuleDependencyList
-{
-public:
- /// Get the list of modules that are depended on.
- List<Module*> const& getModuleList() { return m_moduleList; }
-
- /// Add a module and everything it depends on to the list.
- void addDependency(Module* module);
-
- /// Add a module to the list, but not the modules it depends on.
- void addLeafDependency(Module* module);
-
-private:
- void _addDependency(Module* module);
-
- List<Module*> m_moduleList;
- HashSet<Module*> m_moduleSet;
-};
-
-/// Tracks an unordered list of source files that something depends on
-/// TODO: Shader caching currently relies on this being in well defined order.
-struct FileDependencyList
-{
-public:
- /// Get the list of files that are depended on.
- List<SourceFile*> const& getFileList() { return m_fileList; }
-
- /// Add a file to the list, if it is not already present
- void addDependency(SourceFile* sourceFile);
-
- /// Add all of the paths that `module` depends on to the list
- void addDependency(Module* module);
-
- void clear()
- {
- m_fileList.clear();
- m_fileSet.clear();
- }
-
-private:
- // TODO: We are using a `HashSet` here to deduplicate
- // the paths so that we don't return the same path
- // multiple times from `getFilePathList`, but because
- // order isn't important, we could potentially do better
- // in terms of memory (at some cost in performance) by
- // just sorting the `m_fileList` every once in
- // a while and then deduplicating.
-
- List<SourceFile*> m_fileList;
- HashSet<SourceFile*> m_fileSet;
-};
-
-
-class EntryPoint;
-
-class ComponentType;
-class ComponentTypeVisitor;
-
-/// Base class for "component types" that represent the pieces a final
-/// shader program gets linked together from.
-///
-class ComponentType : public RefObject,
- public slang::IComponentType,
- public slang::IComponentType2,
- public slang::IModulePrecompileService_Experimental
-{
-public:
- //
- // ISlangUnknown interface
- //
-
- SLANG_REF_OBJECT_IUNKNOWN_ALL;
- ISlangUnknown* getInterface(Guid const& guid);
-
- //
- // slang::IComponentType interface
- //
-
- SLANG_NO_THROW slang::ISession* SLANG_MCALL getSession() SLANG_OVERRIDE;
- SLANG_NO_THROW slang::ProgramLayout* SLANG_MCALL
- getLayout(SlangInt targetIndex, slang::IBlob** outDiagnostics) SLANG_OVERRIDE;
- SLANG_NO_THROW SlangResult SLANG_MCALL getEntryPointCode(
- SlangInt entryPointIndex,
- SlangInt targetIndex,
- slang::IBlob** outCode,
- slang::IBlob** outDiagnostics) SLANG_OVERRIDE;
-
- IArtifact* getTargetArtifact(SlangInt targetIndex, slang::IBlob** outDiagnostics);
-
- SLANG_NO_THROW SlangResult SLANG_MCALL getTargetCode(
- SlangInt targetIndex,
- slang::IBlob** outCode,
- slang::IBlob** outDiagnostics = nullptr) SLANG_OVERRIDE;
- SLANG_NO_THROW SlangResult SLANG_MCALL getEntryPointMetadata(
- SlangInt entryPointIndex,
- SlangInt targetIndex,
- slang::IMetadata** outMetadata,
- slang::IBlob** outDiagnostics) SLANG_OVERRIDE;
- SLANG_NO_THROW SlangResult SLANG_MCALL getTargetMetadata(
- SlangInt targetIndex,
- slang::IMetadata** outMetadata,
- slang::IBlob** outDiagnostics = nullptr) SLANG_OVERRIDE;
-
- SLANG_NO_THROW SlangResult SLANG_MCALL getResultAsFileSystem(
- SlangInt entryPointIndex,
- SlangInt targetIndex,
- ISlangMutableFileSystem** outFileSystem) SLANG_OVERRIDE;
-
- SLANG_NO_THROW SlangResult SLANG_MCALL specialize(
- slang::SpecializationArg const* specializationArgs,
- SlangInt specializationArgCount,
- slang::IComponentType** outSpecializedComponentType,
- ISlangBlob** outDiagnostics) SLANG_OVERRIDE;
- SLANG_NO_THROW SlangResult SLANG_MCALL
- renameEntryPoint(const char* newName, slang::IComponentType** outEntryPoint) SLANG_OVERRIDE;
- SLANG_NO_THROW SlangResult SLANG_MCALL link(
- slang::IComponentType** outLinkedComponentType,
- ISlangBlob** outDiagnostics) SLANG_OVERRIDE;
- SLANG_NO_THROW SlangResult SLANG_MCALL getEntryPointHostCallable(
- int entryPointIndex,
- int targetIndex,
- ISlangSharedLibrary** outSharedLibrary,
- slang::IBlob** outDiagnostics) SLANG_OVERRIDE;
-
- /// ComponentType is the only class inheriting from IComponentType that provides a
- /// meaningful implementation for this function. All others should forward these and
- /// implement `buildHash`.
- SLANG_NO_THROW void SLANG_MCALL getEntryPointHash(
- SlangInt entryPointIndex,
- SlangInt targetIndex,
- slang::IBlob** outHash) SLANG_OVERRIDE;
-
- SLANG_NO_THROW SlangResult SLANG_MCALL linkWithOptions(
- slang::IComponentType** outLinkedComponentType,
- uint32_t count,
- slang::CompilerOptionEntry* entries,
- ISlangBlob** outDiagnostics) override;
-
- //
- // slang::IComponentType2 interface
- //
-
- SLANG_NO_THROW SlangResult SLANG_MCALL getEntryPointCompileResult(
- SlangInt entryPointIndex,
- SlangInt targetIndex,
- slang::ICompileResult** outCompileResult,
- slang::IBlob** outDiagnostics) SLANG_OVERRIDE;
- SLANG_NO_THROW SlangResult SLANG_MCALL getTargetCompileResult(
- SlangInt targetIndex,
- slang::ICompileResult** outCompileResult,
- slang::IBlob** outDiagnostics = nullptr) SLANG_OVERRIDE;
-
- //
- // slang::IModulePrecompileService interface
- //
- SLANG_NO_THROW SlangResult SLANG_MCALL
- precompileForTarget(SlangCompileTarget target, slang::IBlob** outDiagnostics) SLANG_OVERRIDE;
-
- SLANG_NO_THROW SlangResult SLANG_MCALL getPrecompiledTargetCode(
- SlangCompileTarget target,
- slang::IBlob** outCode,
- slang::IBlob** outDiagnostics = nullptr) SLANG_OVERRIDE;
-
- SLANG_NO_THROW SlangInt SLANG_MCALL getModuleDependencyCount() SLANG_OVERRIDE;
-
- SLANG_NO_THROW SlangResult SLANG_MCALL getModuleDependency(
- SlangInt dependencyIndex,
- slang::IModule** outModule,
- slang::IBlob** outDiagnostics = nullptr) SLANG_OVERRIDE;
-
- CompilerOptionSet& getOptionSet() { return m_optionSet; }
-
- /// Get the linkage (aka "session" in the public API) for this component type.
- Linkage* getLinkage() { return m_linkage; }
-
- /// Get the target-specific version of this program for the given `target`.
- ///
- /// The `target` must be a target on the `Linkage` that was used to create this program.
- TargetProgram* getTargetProgram(TargetRequest* target);
-
- /// Update the hash builder with the dependencies for this component type.
- virtual void buildHash(DigestBuilder<SHA1>& builder) = 0;
-
- /// Get the number of entry points linked into this component type.
- virtual Index getEntryPointCount() = 0;
-
- /// Get one of the entry points linked into this component type.
- virtual RefPtr<EntryPoint> getEntryPoint(Index index) = 0;
-
- /// Get the mangled name of one of the entry points linked into this component type.
- virtual String getEntryPointMangledName(Index index) = 0;
-
- /// Get the name override of one of the entry points linked into this component type.
- virtual String getEntryPointNameOverride(Index index) = 0;
-
- /// Get the number of global shader parameters linked into this component type.
- virtual Index getShaderParamCount() = 0;
-
- /// Get one of the global shader parametesr linked into this component type.
- virtual ShaderParamInfo getShaderParam(Index index) = 0;
-
- /// Get the specialization parameter at `index`.
- virtual SpecializationParam const& getSpecializationParam(Index index) = 0;
-
- /// Get the number of "requirements" that this component type has.
- ///
- /// A requirement represents another component type that this component
- /// needs in order to function correctly. For example, the dependency
- /// of one module on another module that it `import`s is represented
- /// as a requirement, as is the dependency of an entry point on the
- /// module that defines it.
- ///
- virtual Index getRequirementCount() = 0;
-
- /// Get the requirement at `index`.
- virtual RefPtr<ComponentType> getRequirement(Index index) = 0;
-
- /// Parse a type from a string, in the context of this component type.
- ///
- /// Any names in the string will be resolved using the modules
- /// referenced by the program.
- ///
- /// On an error, returns null and reports diagnostic messages
- /// to the provided `sink`.
- ///
- /// TODO: This function shouldn't be on the base class, since
- /// it only really makes sense on `Module`.
- ///
- Type* getTypeFromString(String const& typeStr, DiagnosticSink* sink);
-
- Expr* findDeclFromString(String const& name, DiagnosticSink* sink);
-
- Expr* findDeclFromStringInType(
- Type* type,
- String const& name,
- LookupMask mask,
- DiagnosticSink* sink);
-
- bool isSubType(Type* subType, Type* superType);
-
- Dictionary<String, IntVal*>& getMangledNameToIntValMap();
- ConstantIntVal* tryFoldIntVal(IntVal* intVal);
-
- /// Get a list of modules that this component type depends on.
- ///
- virtual List<Module*> const& getModuleDependencies() = 0;
-
- /// Get the full list of source files this component type depends on.
- ///
- virtual List<SourceFile*> const& getFileDependencies() = 0;
-
- /// Callback for use with `enumerateIRModules`
- typedef void (*EnumerateIRModulesCallback)(IRModule* irModule, void* userData);
-
- /// Invoke `callback` on all the IR modules that are (transitively) linked into this component
- /// type.
- void enumerateIRModules(EnumerateIRModulesCallback callback, void* userData);
-
- /// Invoke `callback` on all the IR modules that are (transitively) linked into this component
- /// type.
- template<typename F>
- void enumerateIRModules(F const& callback)
- {
- struct Helper
- {
- static void helper(IRModule* irModule, void* userData) { (*(F*)userData)(irModule); }
- };
- enumerateIRModules(&Helper::helper, (void*)&callback);
- }
-
- /// Callback for use with `enumerateModules`
- typedef void (*EnumerateModulesCallback)(Module* module, void* userData);
-
- /// Invoke `callback` on all the modules that are (transitively) linked into this component
- /// type.
- void enumerateModules(EnumerateModulesCallback callback, void* userData);
-
- /// Invoke `callback` on all the modules that are (transitively) linked into this component
- /// type.
- template<typename F>
- void enumerateModules(F const& callback)
- {
- struct Helper
- {
- static void helper(Module* module, void* userData) { (*(F*)userData)(module); }
- };
- enumerateModules(&Helper::helper, (void*)&callback);
- }
-
- /// Side-band information generated when specializing this component type.
- ///
- /// Difference subclasses of `ComponentType` are expected to create their
- /// own subclass of `SpecializationInfo` as the output of `_validateSpecializationArgs`.
- /// Later, whenever we want to use a specialized component type we will
- /// also have the `SpecializationInfo` available and will expect it to
- /// have the correct (subclass-specific) type.
- ///
- class SpecializationInfo : public RefObject
- {
- };
-
- /// Validate the given specialization `args` and compute any side-band specialization info.
- ///
- /// Any errors will be reported to `sink`, which can thus be used to test
- /// if the operation was successful.
- ///
- /// A null return value is allowed, since not all subclasses require
- /// custom side-band specialization information.
- ///
- /// This function is an implementation detail of `specialize()`.
- ///
- virtual RefPtr<SpecializationInfo> _validateSpecializationArgsImpl(
- SpecializationArg const* args,
- Index argCount,
- DiagnosticSink* sink) = 0;
-
- /// Validate the given specialization `args` and compute any side-band specialization info.
- ///
- /// Any errors will be reported to `sink`, which can thus be used to test
- /// if the operation was successful.
- ///
- /// A null return value is allowed, since not all subclasses require
- /// custom side-band specialization information.
- ///
- /// This function is an implementation detail of `specialize()`.
- ///
- RefPtr<SpecializationInfo> _validateSpecializationArgs(
- SpecializationArg const* args,
- Index argCount,
- DiagnosticSink* sink)
- {
- if (argCount == 0)
- return nullptr;
- return _validateSpecializationArgsImpl(args, argCount, sink);
- }
-
- /// Specialize this component type given `specializationArgs`
- ///
- /// Any diagnostics will be reported to `sink`, which can be used
- /// to determine if the operation was successful. It is allowed
- /// for this operation to have a non-null return even when an
- /// error is ecnountered.
- ///
- RefPtr<ComponentType> specialize(
- SpecializationArg const* specializationArgs,
- SlangInt specializationArgCount,
- DiagnosticSink* sink);
-
- /// Invoke `visitor` on this component type, using the appropriate dynamic type.
- ///
- /// This function implements the "visitor pattern" for `ComponentType`.
- ///
- /// If the `specializationInfo` argument is non-null, it must be specialization
- /// information generated for this specific component type by `_validateSpecializationArgs`.
- /// In that case, appropriately-typed specialization information will be passed
- /// when invoking the `visitor`.
- ///
- virtual void acceptVisitor(
- ComponentTypeVisitor* visitor,
- SpecializationInfo* specializationInfo) = 0;
-
- /// Create a scope suitable for looking up names or parsing specialization arguments.
- ///
- /// This facility is only needed to support legacy APIs for string-based lookup
- /// and parsing via Slang reflection, and is not recommended for future APIs to use.
- ///
- Scope* _getOrCreateScopeForLegacyLookup(ASTBuilder* astBuilder);
-
-protected:
- ComponentType(Linkage* linkage);
-
-protected:
- Linkage* m_linkage;
-
- CompilerOptionSet m_optionSet;
-
- // Cache of target-specific programs for each target.
- Dictionary<TargetRequest*, RefPtr<TargetProgram>> m_targetPrograms;
-
- // Any types looked up dynamically using `getTypeFromString`
- //
- // TODO: Remove this. Type lookup should only be supported on `Module`s.
- //
- Dictionary<String, Type*> m_types;
-
- // Any decls looked up dynamically using `findDeclFromString`.
- Dictionary<String, Expr*> m_decls;
-
- Scope* m_lookupScope = nullptr;
- std::unique_ptr<Dictionary<String, IntVal*>> m_mapMangledNameToIntVal;
-
- Dictionary<Int, ComPtr<IArtifact>> m_targetArtifacts;
-};
-
-/// A component type built up from other component types.
-class CompositeComponentType : public ComponentType
-{
-public:
- static RefPtr<ComponentType> create(
- Linkage* linkage,
- List<RefPtr<ComponentType>> const& childComponents);
-
- virtual void buildHash(DigestBuilder<SHA1>& builder) SLANG_OVERRIDE;
-
- List<RefPtr<ComponentType>> const& getChildComponents() { return m_childComponents; };
- Index getChildComponentCount() { return m_childComponents.getCount(); }
- RefPtr<ComponentType> getChildComponent(Index index) { return m_childComponents[index]; }
-
- Index getEntryPointCount() SLANG_OVERRIDE;
- RefPtr<EntryPoint> getEntryPoint(Index index) SLANG_OVERRIDE;
- String getEntryPointMangledName(Index index) SLANG_OVERRIDE;
- String getEntryPointNameOverride(Index index) SLANG_OVERRIDE;
-
- Index getShaderParamCount() SLANG_OVERRIDE;
- ShaderParamInfo getShaderParam(Index index) SLANG_OVERRIDE;
-
- SLANG_NO_THROW Index SLANG_MCALL getSpecializationParamCount() SLANG_OVERRIDE;
- SpecializationParam const& getSpecializationParam(Index index) SLANG_OVERRIDE;
-
- Index getRequirementCount() SLANG_OVERRIDE;
- RefPtr<ComponentType> getRequirement(Index index) SLANG_OVERRIDE;
-
- List<Module*> const& getModuleDependencies() SLANG_OVERRIDE;
- List<SourceFile*> const& getFileDependencies() SLANG_OVERRIDE;
-
- class CompositeSpecializationInfo : public SpecializationInfo
- {
- public:
- List<RefPtr<SpecializationInfo>> childInfos;
- };
-
-protected:
- void acceptVisitor(ComponentTypeVisitor* visitor, SpecializationInfo* specializationInfo)
- SLANG_OVERRIDE;
-
-
- RefPtr<SpecializationInfo> _validateSpecializationArgsImpl(
- SpecializationArg const* args,
- Index argCount,
- DiagnosticSink* sink) SLANG_OVERRIDE;
-
-public:
- CompositeComponentType(Linkage* linkage, List<RefPtr<ComponentType>> const& childComponents);
-
-private:
- List<RefPtr<ComponentType>> m_childComponents;
-
- // The following arrays hold the concatenated entry points, parameters,
- // etc. from the child components. This approach allows for reasonably
- // fast (constant time) access through operations like `getShaderParam`,
- // but means that the memory usage of a composite is proportional to
- // the sum of the memory usage of the children, rather than being fixed
- // by the number of children (as it would be if we just stored
- // `m_childComponents`).
- //
- // TODO: We could conceivably build some O(numChildren) arrays that
- // support binary-search to provide logarithmic-time access to entry
- // points, parameters, etc. while giving a better overall memory usage.
- //
- List<EntryPoint*> m_entryPoints;
- List<String> m_entryPointMangledNames;
- List<String> m_entryPointNameOverrides;
- List<ShaderParamInfo> m_shaderParams;
- List<SpecializationParam> m_specializationParams;
- List<ComponentType*> m_requirements;
-
- ModuleDependencyList m_moduleDependencyList;
- FileDependencyList m_fileDependencyList;
-};
-
-/// A component type created by specializing another component type.
-class SpecializedComponentType : public ComponentType
-{
-public:
- SpecializedComponentType(
- ComponentType* base,
- SpecializationInfo* specializationInfo,
- List<SpecializationArg> const& specializationArgs,
- DiagnosticSink* sink);
-
- virtual void buildHash(DigestBuilder<SHA1>& builer) SLANG_OVERRIDE;
-
- /// Get the base (unspecialized) component type that is being specialized.
- RefPtr<ComponentType> getBaseComponentType() { return m_base; }
-
- RefPtr<SpecializationInfo> getSpecializationInfo() { return m_specializationInfo; }
-
- /// Get the number of arguments supplied for existential type parameters.
- ///
- /// Note that the number of arguments may not match the number of parameters.
- /// In particular, an unspecialized entry point may have many parameters, but zero arguments.
- Index getSpecializationArgCount() { return m_specializationArgs.getCount(); }
-
- /// Get the existential type argument (type and witness table) at `index`.
- SpecializationArg const& getSpecializationArg(Index index)
- {
- return m_specializationArgs[index];
- }
-
- /// Get an array of all existential type arguments.
- SpecializationArg const* getSpecializationArgs() { return m_specializationArgs.getBuffer(); }
-
- Index getEntryPointCount() SLANG_OVERRIDE { return m_base->getEntryPointCount(); }
- RefPtr<EntryPoint> getEntryPoint(Index index) SLANG_OVERRIDE
- {
- return m_base->getEntryPoint(index);
- }
- String getEntryPointMangledName(Index index) SLANG_OVERRIDE;
- String getEntryPointNameOverride(Index index) SLANG_OVERRIDE;
-
- Index getShaderParamCount() SLANG_OVERRIDE { return m_base->getShaderParamCount(); }
- ShaderParamInfo getShaderParam(Index index) SLANG_OVERRIDE
- {
- return m_base->getShaderParam(index);
- }
-
- SLANG_NO_THROW Index SLANG_MCALL getSpecializationParamCount() SLANG_OVERRIDE { return 0; }
- SpecializationParam const& getSpecializationParam(Index index) SLANG_OVERRIDE
- {
- SLANG_UNUSED(index);
- static SpecializationParam dummy;
- return dummy;
- }
-
- Index getRequirementCount() SLANG_OVERRIDE;
- RefPtr<ComponentType> getRequirement(Index index) SLANG_OVERRIDE;
-
- List<Module*> const& getModuleDependencies() SLANG_OVERRIDE { return m_moduleDependencies; }
- List<SourceFile*> const& getFileDependencies() SLANG_OVERRIDE { return m_fileDependencies; }
-
- RefPtr<IRModule> getIRModule() { return m_irModule; }
-
- void acceptVisitor(ComponentTypeVisitor* visitor, SpecializationInfo* specializationInfo)
- SLANG_OVERRIDE;
-
-protected:
- RefPtr<SpecializationInfo> _validateSpecializationArgsImpl(
- SpecializationArg const* args,
- Index argCount,
- DiagnosticSink* sink) SLANG_OVERRIDE
- {
- SLANG_UNUSED(args);
- SLANG_UNUSED(argCount);
- SLANG_UNUSED(sink);
- return nullptr;
- }
-
-private:
- RefPtr<ComponentType> m_base;
- RefPtr<SpecializationInfo> m_specializationInfo;
- SpecializationArgs m_specializationArgs;
- RefPtr<IRModule> m_irModule;
-
- List<String> m_entryPointMangledNames;
- List<String> m_entryPointNameOverrides;
-
- List<Module*> m_moduleDependencies;
- List<SourceFile*> m_fileDependencies;
- List<RefPtr<ComponentType>> m_requirements;
-};
-
-class RenamedEntryPointComponentType : public ComponentType
-{
-public:
- using Super = ComponentType;
-
- RenamedEntryPointComponentType(ComponentType* base, String newName);
-
- ComponentType* getBase() { return m_base.Ptr(); }
-
- // Forward `IComponentType` methods
-
- SLANG_NO_THROW slang::ISession* SLANG_MCALL getSession() SLANG_OVERRIDE
- {
- return Super::getSession();
- }
-
- SLANG_NO_THROW slang::ProgramLayout* SLANG_MCALL
- getLayout(SlangInt targetIndex, slang::IBlob** outDiagnostics) SLANG_OVERRIDE
- {
- return Super::getLayout(targetIndex, outDiagnostics);
- }
-
- SLANG_NO_THROW SlangResult SLANG_MCALL getEntryPointCode(
- SlangInt entryPointIndex,
- SlangInt targetIndex,
- slang::IBlob** outCode,
- slang::IBlob** outDiagnostics) SLANG_OVERRIDE
- {
- return Super::getEntryPointCode(entryPointIndex, targetIndex, outCode, outDiagnostics);
- }
-
- SLANG_NO_THROW SlangResult SLANG_MCALL specialize(
- slang::SpecializationArg const* specializationArgs,
- SlangInt specializationArgCount,
- slang::IComponentType** outSpecializedComponentType,
- ISlangBlob** outDiagnostics) SLANG_OVERRIDE
- {
- return Super::specialize(
- specializationArgs,
- specializationArgCount,
- outSpecializedComponentType,
- outDiagnostics);
- }
-
- SLANG_NO_THROW SlangResult SLANG_MCALL
- renameEntryPoint(const char* newName, slang::IComponentType** outEntryPoint) SLANG_OVERRIDE
- {
- return Super::renameEntryPoint(newName, outEntryPoint);
- }
-
- SLANG_NO_THROW SlangResult SLANG_MCALL
- link(slang::IComponentType** outLinkedComponentType, ISlangBlob** outDiagnostics) SLANG_OVERRIDE
- {
- return Super::link(outLinkedComponentType, outDiagnostics);
- }
-
- SLANG_NO_THROW SlangResult SLANG_MCALL getEntryPointHostCallable(
- int entryPointIndex,
- int targetIndex,
- ISlangSharedLibrary** outSharedLibrary,
- slang::IBlob** outDiagnostics) SLANG_OVERRIDE
- {
- return Super::getEntryPointHostCallable(
- entryPointIndex,
- targetIndex,
- outSharedLibrary,
- outDiagnostics);
- }
-
- List<Module*> const& getModuleDependencies() SLANG_OVERRIDE
- {
- return m_base->getModuleDependencies();
- }
- List<SourceFile*> const& getFileDependencies() SLANG_OVERRIDE
- {
- return m_base->getFileDependencies();
- }
-
- SLANG_NO_THROW Index SLANG_MCALL getSpecializationParamCount() SLANG_OVERRIDE
- {
- return m_base->getSpecializationParamCount();
- }
-
- SpecializationParam const& getSpecializationParam(Index index) SLANG_OVERRIDE
- {
- return m_base->getSpecializationParam(index);
- }
-
- Index getRequirementCount() SLANG_OVERRIDE { return m_base->getRequirementCount(); }
- RefPtr<ComponentType> getRequirement(Index index) SLANG_OVERRIDE
- {
- return m_base->getRequirement(index);
- }
- Index getEntryPointCount() SLANG_OVERRIDE { return m_base->getEntryPointCount(); }
- RefPtr<EntryPoint> getEntryPoint(Index index) SLANG_OVERRIDE
- {
- return m_base->getEntryPoint(index);
- }
- String getEntryPointMangledName(Index index) SLANG_OVERRIDE
- {
- return m_base->getEntryPointMangledName(index);
- }
- String getEntryPointNameOverride(Index index) SLANG_OVERRIDE
- {
- SLANG_UNUSED(index);
- SLANG_ASSERT(index == 0);
- return m_entryPointNameOverride;
- }
-
- Index getShaderParamCount() SLANG_OVERRIDE { return m_base->getShaderParamCount(); }
- ShaderParamInfo getShaderParam(Index index) SLANG_OVERRIDE
- {
- return m_base->getShaderParam(index);
- }
-
- void acceptVisitor(ComponentTypeVisitor* visitor, SpecializationInfo* specializationInfo)
- SLANG_OVERRIDE;
-
- virtual void buildHash(DigestBuilder<SHA1>& builder) SLANG_OVERRIDE;
-
-private:
- RefPtr<ComponentType> m_base;
- String m_entryPointNameOverride;
-
-protected:
- RefPtr<SpecializationInfo> _validateSpecializationArgsImpl(
- SpecializationArg const* args,
- Index argCount,
- DiagnosticSink* sink) SLANG_OVERRIDE
- {
- return m_base->_validateSpecializationArgsImpl(args, argCount, sink);
- }
-};
-
-/// Describes an entry point for the purposes of layout and code generation.
-///
-/// This class also tracks any generic arguments to the entry point,
-/// in the case that it is a specialization of a generic entry point.
-///
-/// There is also a provision for creating a "dummy" entry point for
-/// the purposes of pass-through compilation modes. Only the
-/// `getName()` and `getProfile()` methods should be expected to
-/// return useful data on pass-through entry points.
-///
-class EntryPoint : public ComponentType, public slang::IEntryPoint
-{
- typedef ComponentType Super;
-
-public:
- SLANG_REF_OBJECT_IUNKNOWN_ALL
-
- ISlangUnknown* getInterface(const Guid& guid);
-
-
- // Forward `IComponentType` methods
-
- SLANG_NO_THROW slang::ISession* SLANG_MCALL getSession() SLANG_OVERRIDE
- {
- return Super::getSession();
- }
-
- SLANG_NO_THROW slang::ProgramLayout* SLANG_MCALL
- getLayout(SlangInt targetIndex, slang::IBlob** outDiagnostics) SLANG_OVERRIDE
- {
- return Super::getLayout(targetIndex, outDiagnostics);
- }
-
- SLANG_NO_THROW SlangResult SLANG_MCALL getEntryPointCode(
- SlangInt entryPointIndex,
- SlangInt targetIndex,
- slang::IBlob** outCode,
- slang::IBlob** outDiagnostics) SLANG_OVERRIDE
- {
- return Super::getEntryPointCode(entryPointIndex, targetIndex, outCode, outDiagnostics);
- }
-
- SLANG_NO_THROW SlangResult SLANG_MCALL getTargetCode(
- SlangInt targetIndex,
- slang::IBlob** outCode,
- slang::IBlob** outDiagnostics) SLANG_OVERRIDE
- {
- return Super::getTargetCode(targetIndex, outCode, outDiagnostics);
- }
-
- SLANG_NO_THROW SlangResult SLANG_MCALL getEntryPointMetadata(
- SlangInt entryPointIndex,
- SlangInt targetIndex,
- slang::IMetadata** outMetadata,
- slang::IBlob** outDiagnostics) SLANG_OVERRIDE
- {
- return Super::getEntryPointMetadata(
- entryPointIndex,
- targetIndex,
- outMetadata,
- outDiagnostics);
- }
-
- SLANG_NO_THROW SlangResult SLANG_MCALL getTargetMetadata(
- SlangInt targetIndex,
- slang::IMetadata** outMetadata,
- slang::IBlob** outDiagnostics) SLANG_OVERRIDE
- {
- return Super::getTargetMetadata(targetIndex, outMetadata, outDiagnostics);
- }
-
- SLANG_NO_THROW SlangResult SLANG_MCALL getEntryPointCompileResult(
- SlangInt entryPointIndex,
- SlangInt targetIndex,
- slang::ICompileResult** outCompileResult,
- slang::IBlob** outDiagnostics) SLANG_OVERRIDE
- {
- return Super::getEntryPointCompileResult(
- entryPointIndex,
- targetIndex,
- outCompileResult,
- outDiagnostics);
- }
-
- SLANG_NO_THROW SlangResult SLANG_MCALL getTargetCompileResult(
- SlangInt targetIndex,
- slang::ICompileResult** outCompileResult,
- slang::IBlob** outDiagnostics) SLANG_OVERRIDE
- {
- return Super::getTargetCompileResult(targetIndex, outCompileResult, outDiagnostics);
- }
-
- SLANG_NO_THROW SlangResult SLANG_MCALL getResultAsFileSystem(
- SlangInt entryPointIndex,
- SlangInt targetIndex,
- ISlangMutableFileSystem** outFileSystem) SLANG_OVERRIDE
- {
- return Super::getResultAsFileSystem(entryPointIndex, targetIndex, outFileSystem);
- }
-
- SLANG_NO_THROW SlangResult SLANG_MCALL specialize(
- slang::SpecializationArg const* specializationArgs,
- SlangInt specializationArgCount,
- slang::IComponentType** outSpecializedComponentType,
- ISlangBlob** outDiagnostics) SLANG_OVERRIDE
- {
- return Super::specialize(
- specializationArgs,
- specializationArgCount,
- outSpecializedComponentType,
- outDiagnostics);
- }
-
- SLANG_NO_THROW SlangResult SLANG_MCALL
- renameEntryPoint(const char* newName, slang::IComponentType** outEntryPoint) SLANG_OVERRIDE
- {
- return Super::renameEntryPoint(newName, outEntryPoint);
- }
-
- SLANG_NO_THROW SlangResult SLANG_MCALL
- link(slang::IComponentType** outLinkedComponentType, ISlangBlob** outDiagnostics) SLANG_OVERRIDE
- {
- return Super::link(outLinkedComponentType, outDiagnostics);
- }
-
- virtual SLANG_NO_THROW SlangResult SLANG_MCALL linkWithOptions(
- slang::IComponentType** outLinkedComponentType,
- uint32_t count,
- slang::CompilerOptionEntry* entries,
- ISlangBlob** outDiagnostics) override
- {
- return Super::linkWithOptions(outLinkedComponentType, count, entries, outDiagnostics);
- }
-
- SLANG_NO_THROW SlangResult SLANG_MCALL getEntryPointHostCallable(
- int entryPointIndex,
- int targetIndex,
- ISlangSharedLibrary** outSharedLibrary,
- slang::IBlob** outDiagnostics) SLANG_OVERRIDE
- {
- return Super::getEntryPointHostCallable(
- entryPointIndex,
- targetIndex,
- outSharedLibrary,
- outDiagnostics);
- }
-
- SLANG_NO_THROW void SLANG_MCALL getEntryPointHash(
- SlangInt entryPointIndex,
- SlangInt targetIndex,
- slang::IBlob** outHash) SLANG_OVERRIDE
- {
- return Super::getEntryPointHash(entryPointIndex, targetIndex, outHash);
- }
-
- virtual void buildHash(DigestBuilder<SHA1>& builder) SLANG_OVERRIDE;
-
- /// Create an entry point that refers to the given function.
- static RefPtr<EntryPoint> create(
- Linkage* linkage,
- DeclRef<FuncDecl> funcDeclRef,
- Profile profile);
-
- /// Get the function decl-ref, including any generic arguments.
- DeclRef<FuncDecl> getFuncDeclRef() { return m_funcDeclRef; }
-
- /// Get the function declaration (without generic arguments).
- FuncDecl* getFuncDecl() { return m_funcDeclRef.getDecl(); }
-
- /// Get the name of the entry point
- Name* getName() { return m_name; }
-
- /// Get the profile associated with the entry point
- ///
- /// Note: only the stage part of the profile is expected
- /// to contain useful data, but certain legacy code paths
- /// allow for "shader model" information to come via this path.
- ///
- Profile getProfile() { return m_profile; }
-
- /// Get the stage that the entry point is for.
- Stage getStage() { return m_profile.getStage(); }
-
- /// Get the module that contains the entry point.
- Module* getModule();
-
- /// Get a list of modules that this entry point depends on.
- ///
- /// This will include the module that defines the entry point (see `getModule()`),
- /// but may also include modules that are required by its generic type arguments.
- ///
- List<Module*> const& getModuleDependencies()
- SLANG_OVERRIDE; // { return getModule()->getModuleDependencies(); }
- List<SourceFile*> const& getFileDependencies()
- SLANG_OVERRIDE; // { return getModule()->getFileDependencies(); }
-
- /// Create a dummy `EntryPoint` that is only usable for pass-through compilation.
- static RefPtr<EntryPoint> createDummyForPassThrough(
- Linkage* linkage,
- Name* name,
- Profile profile);
-
- /// Create a dummy `EntryPoint` that stands in for a serialized entry point
- static RefPtr<EntryPoint> createDummyForDeserialize(
- Linkage* linkage,
- Name* name,
- Profile profile,
- String mangledName);
-
- /// Get the number of existential type parameters for the entry point.
- SLANG_NO_THROW Index SLANG_MCALL getSpecializationParamCount() SLANG_OVERRIDE;
-
- /// Get the existential type parameter at `index`.
- SpecializationParam const& getSpecializationParam(Index index) SLANG_OVERRIDE;
-
- Index getRequirementCount() SLANG_OVERRIDE;
- RefPtr<ComponentType> getRequirement(Index index) SLANG_OVERRIDE;
-
- SpecializationParams const& getExistentialSpecializationParams()
- {
- return m_existentialSpecializationParams;
- }
-
- Index getGenericSpecializationParamCount() { return m_genericSpecializationParams.getCount(); }
- Index getExistentialSpecializationParamCount()
- {
- return m_existentialSpecializationParams.getCount();
- }
-
- /// Get an array of all entry-point shader parameters.
- List<ShaderParamInfo> const& getShaderParams() { return m_shaderParams; }
-
- Index getEntryPointCount() SLANG_OVERRIDE { return 1; };
- RefPtr<EntryPoint> getEntryPoint(Index index) SLANG_OVERRIDE
- {
- SLANG_UNUSED(index);
- return this;
- }
- String getEntryPointMangledName(Index index) SLANG_OVERRIDE;
- String getEntryPointNameOverride(Index index) SLANG_OVERRIDE;
-
- Index getShaderParamCount() SLANG_OVERRIDE { return 0; }
- ShaderParamInfo getShaderParam(Index index) SLANG_OVERRIDE
- {
- SLANG_UNUSED(index);
- return ShaderParamInfo();
- }
-
- class EntryPointSpecializationInfo : public SpecializationInfo
- {
- public:
- DeclRef<FuncDecl> specializedFuncDeclRef;
- List<ExpandedSpecializationArg> existentialSpecializationArgs;
- };
-
- SLANG_NO_THROW slang::FunctionReflection* SLANG_MCALL getFunctionReflection() SLANG_OVERRIDE
- {
- return (slang::FunctionReflection*)m_funcDeclRef.declRefBase;
- }
-
-protected:
- void acceptVisitor(ComponentTypeVisitor* visitor, SpecializationInfo* specializationInfo)
- SLANG_OVERRIDE;
-
- RefPtr<SpecializationInfo> _validateSpecializationArgsImpl(
- SpecializationArg const* args,
- Index argCount,
- DiagnosticSink* sink) SLANG_OVERRIDE;
-
-private:
- EntryPoint(Linkage* linkage, Name* name, Profile profile, DeclRef<FuncDecl> funcDeclRef);
-
- void _collectGenericSpecializationParamsRec(Decl* decl);
- void _collectShaderParams();
-
- // The name of the entry point function (e.g., `main`)
- //
- Name* m_name = nullptr;
-
- // The declaration of the entry-point function itself.
- //
- DeclRef<FuncDecl> m_funcDeclRef;
-
- /// The mangled name of the entry point function
- String m_mangledName;
-
- SpecializationParams m_genericSpecializationParams;
- SpecializationParams m_existentialSpecializationParams;
-
- /// Information about entry-point parameters
- List<ShaderParamInfo> m_shaderParams;
-
- // The profile that the entry point will be compiled for
- // (this is a combination of the target stage, and also
- // a feature level that sets capabilities)
- //
- // Note: the profile-version part of this should probably
- // be moving towards deprecation, in favor of the version
- // information (e.g., "Shader Model 5.1") always coming
- // from the target, while the stage part is all that is
- // intrinsic to the entry point.
- //
- Profile m_profile;
-};
-
-class TypeConformance : public ComponentType, public slang::ITypeConformance
-{
- typedef ComponentType Super;
-
-public:
- SLANG_REF_OBJECT_IUNKNOWN_ALL
-
- ISlangUnknown* getInterface(const Guid& guid);
-
- TypeConformance(
- Linkage* linkage,
- SubtypeWitness* witness,
- Int confomrmanceIdOverride,
- DiagnosticSink* sink);
-
- // Forward `IComponentType` methods
-
- SLANG_NO_THROW slang::ISession* SLANG_MCALL getSession() SLANG_OVERRIDE
- {
- return Super::getSession();
- }
-
- SLANG_NO_THROW slang::ProgramLayout* SLANG_MCALL
- getLayout(SlangInt targetIndex, slang::IBlob** outDiagnostics) SLANG_OVERRIDE
- {
- return Super::getLayout(targetIndex, outDiagnostics);
- }
-
- SLANG_NO_THROW SlangResult SLANG_MCALL getEntryPointCode(
- SlangInt entryPointIndex,
- SlangInt targetIndex,
- slang::IBlob** outCode,
- slang::IBlob** outDiagnostics) SLANG_OVERRIDE
- {
- return Super::getEntryPointCode(entryPointIndex, targetIndex, outCode, outDiagnostics);
- }
-
- SLANG_NO_THROW SlangResult SLANG_MCALL getTargetCode(
- SlangInt targetIndex,
- slang::IBlob** outCode,
- slang::IBlob** outDiagnostics) SLANG_OVERRIDE
- {
- return Super::getTargetCode(targetIndex, outCode, outDiagnostics);
- }
-
- SLANG_NO_THROW SlangResult SLANG_MCALL getEntryPointMetadata(
- SlangInt entryPointIndex,
- SlangInt targetIndex,
- slang::IMetadata** outMetadata,
- slang::IBlob** outDiagnostics) SLANG_OVERRIDE
- {
- return Super::getEntryPointMetadata(
- entryPointIndex,
- targetIndex,
- outMetadata,
- outDiagnostics);
- }
-
- SLANG_NO_THROW SlangResult SLANG_MCALL getTargetMetadata(
- SlangInt targetIndex,
- slang::IMetadata** outMetadata,
- slang::IBlob** outDiagnostics) SLANG_OVERRIDE
- {
- return Super::getTargetMetadata(targetIndex, outMetadata, outDiagnostics);
- }
-
- SLANG_NO_THROW SlangResult SLANG_MCALL getEntryPointCompileResult(
- SlangInt entryPointIndex,
- SlangInt targetIndex,
- slang::ICompileResult** outCompileResult,
- slang::IBlob** outDiagnostics) SLANG_OVERRIDE
- {
- return Super::getEntryPointCompileResult(
- entryPointIndex,
- targetIndex,
- outCompileResult,
- outDiagnostics);
- }
-
- SLANG_NO_THROW SlangResult SLANG_MCALL getTargetCompileResult(
- SlangInt targetIndex,
- slang::ICompileResult** outCompileResult,
- slang::IBlob** outDiagnostics) SLANG_OVERRIDE
- {
- return Super::getTargetCompileResult(targetIndex, outCompileResult, outDiagnostics);
- }
-
- SLANG_NO_THROW SlangResult SLANG_MCALL getResultAsFileSystem(
- SlangInt entryPointIndex,
- SlangInt targetIndex,
- ISlangMutableFileSystem** outFileSystem) SLANG_OVERRIDE
- {
- return Super::getResultAsFileSystem(entryPointIndex, targetIndex, outFileSystem);
- }
-
- SLANG_NO_THROW SlangResult SLANG_MCALL specialize(
- slang::SpecializationArg const* specializationArgs,
- SlangInt specializationArgCount,
- slang::IComponentType** outSpecializedComponentType,
- ISlangBlob** outDiagnostics) SLANG_OVERRIDE
- {
- return Super::specialize(
- specializationArgs,
- specializationArgCount,
- outSpecializedComponentType,
- outDiagnostics);
- }
-
- SLANG_NO_THROW SlangResult SLANG_MCALL
- renameEntryPoint(const char* newName, slang::IComponentType** outEntryPoint) SLANG_OVERRIDE
- {
- return Super::renameEntryPoint(newName, outEntryPoint);
- }
-
- SLANG_NO_THROW SlangResult SLANG_MCALL
- link(slang::IComponentType** outLinkedComponentType, ISlangBlob** outDiagnostics) SLANG_OVERRIDE
- {
- return Super::link(outLinkedComponentType, outDiagnostics);
- }
-
- virtual SLANG_NO_THROW SlangResult SLANG_MCALL linkWithOptions(
- slang::IComponentType** outLinkedComponentType,
- uint32_t count,
- slang::CompilerOptionEntry* entries,
- ISlangBlob** outDiagnostics) override
- {
- return Super::linkWithOptions(outLinkedComponentType, count, entries, outDiagnostics);
- }
-
- SLANG_NO_THROW SlangResult SLANG_MCALL getEntryPointHostCallable(
- int entryPointIndex,
- int targetIndex,
- ISlangSharedLibrary** outSharedLibrary,
- slang::IBlob** outDiagnostics) SLANG_OVERRIDE
- {
- return Super::getEntryPointHostCallable(
- entryPointIndex,
- targetIndex,
- outSharedLibrary,
- outDiagnostics);
- }
-
- SLANG_NO_THROW void SLANG_MCALL getEntryPointHash(
- SlangInt entryPointIndex,
- SlangInt targetIndex,
- slang::IBlob** outHash) SLANG_OVERRIDE
- {
- return Super::getEntryPointHash(entryPointIndex, targetIndex, outHash);
- }
-
- virtual void buildHash(DigestBuilder<SHA1>& builder) SLANG_OVERRIDE;
-
- List<Module*> const& getModuleDependencies() SLANG_OVERRIDE;
- List<SourceFile*> const& getFileDependencies() SLANG_OVERRIDE;
-
- SLANG_NO_THROW Index SLANG_MCALL getSpecializationParamCount() SLANG_OVERRIDE { return 0; }
-
- /// Get the existential type parameter at `index`.
- SpecializationParam const& getSpecializationParam(Index /*index*/) SLANG_OVERRIDE
- {
- static SpecializationParam emptyParam;
- return emptyParam;
- }
-
- Index getRequirementCount() SLANG_OVERRIDE;
- RefPtr<ComponentType> getRequirement(Index index) SLANG_OVERRIDE;
- Index getEntryPointCount() SLANG_OVERRIDE { return 0; };
- RefPtr<EntryPoint> getEntryPoint(Index index) SLANG_OVERRIDE
- {
- SLANG_UNUSED(index);
- return nullptr;
- }
- String getEntryPointMangledName(Index /*index*/) SLANG_OVERRIDE { return ""; }
- String getEntryPointNameOverride(Index /*index*/) SLANG_OVERRIDE { return ""; }
-
- Index getShaderParamCount() SLANG_OVERRIDE { return 0; }
- ShaderParamInfo getShaderParam(Index index) SLANG_OVERRIDE
- {
- SLANG_UNUSED(index);
- return ShaderParamInfo();
- }
-
- SubtypeWitness* getSubtypeWitness() { return m_subtypeWitness; }
- IRModule* getIRModule() { return m_irModule.Ptr(); }
-
-protected:
- void acceptVisitor(ComponentTypeVisitor* visitor, SpecializationInfo* specializationInfo)
- SLANG_OVERRIDE;
-
- RefPtr<SpecializationInfo> _validateSpecializationArgsImpl(
- SpecializationArg const* args,
- Index argCount,
- DiagnosticSink* sink) SLANG_OVERRIDE;
-
-private:
- SubtypeWitness* m_subtypeWitness;
- ModuleDependencyList m_moduleDependencyList;
- FileDependencyList m_fileDependencyList;
- List<RefPtr<Module>> m_requirements;
- HashSet<Module*> m_requirementSet;
- RefPtr<IRModule> m_irModule;
- Int m_conformanceIdOverride;
- void addDepedencyFromWitness(SubtypeWitness* witness);
-};
-
-enum class PassThroughMode : SlangPassThroughIntegral
-{
- None = SLANG_PASS_THROUGH_NONE, ///< don't pass through: use Slang compiler
- Fxc = SLANG_PASS_THROUGH_FXC, ///< pass through HLSL to `D3DCompile` API
- Dxc = SLANG_PASS_THROUGH_DXC, ///< pass through HLSL to `IDxcCompiler` API
- Glslang = SLANG_PASS_THROUGH_GLSLANG, ///< pass through GLSL to `glslang` library
- SpirvDis = SLANG_PASS_THROUGH_SPIRV_DIS, ///< pass through spirv-dis
- Clang = SLANG_PASS_THROUGH_CLANG, ///< Pass through clang compiler
- VisualStudio = SLANG_PASS_THROUGH_VISUAL_STUDIO, ///< Visual studio compiler
- Gcc = SLANG_PASS_THROUGH_GCC, ///< Gcc compiler
- GenericCCpp = SLANG_PASS_THROUGH_GENERIC_C_CPP, ///< Generic C/C++ compiler
- NVRTC = SLANG_PASS_THROUGH_NVRTC, ///< NVRTC CUDA compiler
- LLVM = SLANG_PASS_THROUGH_LLVM, ///< LLVM 'compiler'
- SpirvOpt = SLANG_PASS_THROUGH_SPIRV_OPT, ///< pass thorugh spirv to spirv-opt
- MetalC = SLANG_PASS_THROUGH_METAL,
- Tint = SLANG_PASS_THROUGH_TINT, ///< pass through spirv to Tint API
- SpirvLink = SLANG_PASS_THROUGH_SPIRV_LINK, ///< pass through spirv to spirv-link
- CountOf = SLANG_PASS_THROUGH_COUNT_OF,
-};
-void printDiagnosticArg(StringBuilder& sb, PassThroughMode val);
class SourceFile;
-/// A module of code that has been compiled through the front-end
-///
-/// A module comprises all the code from one translation unit (which
-/// may span multiple Slang source files), and provides access
-/// to both the AST and IR representations of that code.
-///
-class Module : public ComponentType, public slang::IModule
-{
- typedef ComponentType Super;
-
-public:
- SLANG_REF_OBJECT_IUNKNOWN_ALL
-
- ISlangUnknown* getInterface(const Guid& guid);
-
-
- // Forward `IComponentType` methods
-
- SLANG_NO_THROW slang::ISession* SLANG_MCALL getSession() SLANG_OVERRIDE
- {
- return Super::getSession();
- }
-
- SLANG_NO_THROW slang::ProgramLayout* SLANG_MCALL
- getLayout(SlangInt targetIndex, slang::IBlob** outDiagnostics) SLANG_OVERRIDE
- {
- return Super::getLayout(targetIndex, outDiagnostics);
- }
-
- SLANG_NO_THROW SlangResult SLANG_MCALL getEntryPointCode(
- SlangInt entryPointIndex,
- SlangInt targetIndex,
- slang::IBlob** outCode,
- slang::IBlob** outDiagnostics) SLANG_OVERRIDE
- {
- return Super::getEntryPointCode(entryPointIndex, targetIndex, outCode, outDiagnostics);
- }
-
- SLANG_NO_THROW SlangResult SLANG_MCALL getTargetCode(
- SlangInt targetIndex,
- slang::IBlob** outCode,
- slang::IBlob** outDiagnostics) SLANG_OVERRIDE
- {
- return Super::getTargetCode(targetIndex, outCode, outDiagnostics);
- }
-
- SLANG_NO_THROW SlangResult SLANG_MCALL getResultAsFileSystem(
- SlangInt entryPointIndex,
- SlangInt targetIndex,
- ISlangMutableFileSystem** outFileSystem) SLANG_OVERRIDE
- {
- return Super::getResultAsFileSystem(entryPointIndex, targetIndex, outFileSystem);
- }
-
- SLANG_NO_THROW SlangResult SLANG_MCALL specialize(
- slang::SpecializationArg const* specializationArgs,
- SlangInt specializationArgCount,
- slang::IComponentType** outSpecializedComponentType,
- ISlangBlob** outDiagnostics) SLANG_OVERRIDE
- {
- return Super::specialize(
- specializationArgs,
- specializationArgCount,
- outSpecializedComponentType,
- outDiagnostics);
- }
-
- SLANG_NO_THROW SlangResult SLANG_MCALL
- renameEntryPoint(const char* newName, slang::IComponentType** outEntryPoint) SLANG_OVERRIDE
- {
- return Super::renameEntryPoint(newName, outEntryPoint);
- }
-
- SLANG_NO_THROW SlangResult SLANG_MCALL
- link(slang::IComponentType** outLinkedComponentType, ISlangBlob** outDiagnostics) SLANG_OVERRIDE
- {
- return Super::link(outLinkedComponentType, outDiagnostics);
- }
-
- SLANG_NO_THROW SlangResult SLANG_MCALL getEntryPointHostCallable(
- int entryPointIndex,
- int targetIndex,
- ISlangSharedLibrary** outSharedLibrary,
- slang::IBlob** outDiagnostics) SLANG_OVERRIDE
- {
- return Super::getEntryPointHostCallable(
- entryPointIndex,
- targetIndex,
- outSharedLibrary,
- outDiagnostics);
- }
-
- SLANG_NO_THROW SlangResult SLANG_MCALL
- findEntryPointByName(char const* name, slang::IEntryPoint** outEntryPoint) SLANG_OVERRIDE
- {
- if (outEntryPoint == nullptr)
- {
- return SLANG_E_INVALID_ARG;
- }
- SLANG_AST_BUILDER_RAII(m_astBuilder);
- ComPtr<slang::IEntryPoint> entryPoint(findEntryPointByName(UnownedStringSlice(name)));
- if ((!entryPoint))
- return SLANG_FAIL;
-
- *outEntryPoint = entryPoint.detach();
- return SLANG_OK;
- }
-
- virtual SLANG_NO_THROW SlangResult SLANG_MCALL findAndCheckEntryPoint(
- char const* name,
- SlangStage stage,
- slang::IEntryPoint** outEntryPoint,
- ISlangBlob** outDiagnostics) override
- {
- if (outEntryPoint == nullptr)
- {
- return SLANG_E_INVALID_ARG;
- }
- ComPtr<slang::IEntryPoint> entryPoint(
- findAndCheckEntryPoint(UnownedStringSlice(name), stage, outDiagnostics));
- if ((!entryPoint))
- return SLANG_FAIL;
-
- *outEntryPoint = entryPoint.detach();
- return SLANG_OK;
- }
-
- virtual SLANG_NO_THROW SlangInt32 SLANG_MCALL getDefinedEntryPointCount() override
- {
- return (SlangInt32)m_entryPoints.getCount();
- }
-
- virtual SLANG_NO_THROW SlangResult SLANG_MCALL
- getDefinedEntryPoint(SlangInt32 index, slang::IEntryPoint** outEntryPoint) override
- {
- if (index < 0 || index >= m_entryPoints.getCount())
- return SLANG_E_INVALID_ARG;
-
- if (outEntryPoint == nullptr)
- {
- return SLANG_E_INVALID_ARG;
- }
-
- ComPtr<slang::IEntryPoint> entryPoint(m_entryPoints[index].Ptr());
- *outEntryPoint = entryPoint.detach();
- return SLANG_OK;
- }
-
- virtual SLANG_NO_THROW SlangResult SLANG_MCALL linkWithOptions(
- slang::IComponentType** outLinkedComponentType,
- uint32_t count,
- slang::CompilerOptionEntry* entries,
- ISlangBlob** outDiagnostics) override
- {
- return Super::linkWithOptions(outLinkedComponentType, count, entries, outDiagnostics);
- }
- //
-
- SLANG_NO_THROW void SLANG_MCALL getEntryPointHash(
- SlangInt entryPointIndex,
- SlangInt targetIndex,
- slang::IBlob** outHash) SLANG_OVERRIDE
- {
- return Super::getEntryPointHash(entryPointIndex, targetIndex, outHash);
- }
-
- SLANG_NO_THROW SlangResult SLANG_MCALL getEntryPointMetadata(
- SlangInt entryPointIndex,
- SlangInt targetIndex,
- slang::IMetadata** outMetadata,
- slang::IBlob** outDiagnostics) SLANG_OVERRIDE
- {
- return Super::getEntryPointMetadata(
- entryPointIndex,
- targetIndex,
- outMetadata,
- outDiagnostics);
- }
-
- SLANG_NO_THROW SlangResult SLANG_MCALL getTargetMetadata(
- SlangInt targetIndex,
- slang::IMetadata** outMetadata,
- slang::IBlob** outDiagnostics) SLANG_OVERRIDE
- {
- return Super::getTargetMetadata(targetIndex, outMetadata, outDiagnostics);
- }
-
- SLANG_NO_THROW SlangResult SLANG_MCALL getEntryPointCompileResult(
- SlangInt entryPointIndex,
- SlangInt targetIndex,
- slang::ICompileResult** outCompileResult,
- slang::IBlob** outDiagnostics) SLANG_OVERRIDE
- {
- return Super::getEntryPointCompileResult(
- entryPointIndex,
- targetIndex,
- outCompileResult,
- outDiagnostics);
- }
-
- SLANG_NO_THROW SlangResult SLANG_MCALL getTargetCompileResult(
- SlangInt targetIndex,
- slang::ICompileResult** outCompileResult,
- slang::IBlob** outDiagnostics) SLANG_OVERRIDE
- {
- return Super::getTargetCompileResult(targetIndex, outCompileResult, outDiagnostics);
- }
-
- /// Get a serialized representation of the checked module.
- virtual SLANG_NO_THROW SlangResult SLANG_MCALL
- serialize(ISlangBlob** outSerializedBlob) override;
-
- /// Write the serialized representation of this module to a file.
- virtual SLANG_NO_THROW SlangResult SLANG_MCALL writeToFile(char const* fileName) override;
-
- /// Get the name of the module.
- virtual SLANG_NO_THROW const char* SLANG_MCALL getName() override;
-
- /// Get the path of the module.
- virtual SLANG_NO_THROW const char* SLANG_MCALL getFilePath() override;
-
- /// Get the unique identity of the module.
- virtual SLANG_NO_THROW const char* SLANG_MCALL getUniqueIdentity() override;
-
- /// Get the number of dependency files that this module depends on.
- /// This includes both the explicit source files, as well as any
- /// additional files that were transitively referenced (e.g., via
- /// a `#include` directive).
- virtual SLANG_NO_THROW SlangInt32 SLANG_MCALL getDependencyFileCount() override;
-
- /// Get the path to a file this module depends on.
- virtual SLANG_NO_THROW char const* SLANG_MCALL getDependencyFilePath(SlangInt32 index) override;
-
-
- // IModulePrecompileService_Experimental
- /// Precompile TU to target language
- virtual SLANG_NO_THROW SlangResult SLANG_MCALL
- precompileForTarget(SlangCompileTarget target, slang::IBlob** outDiagnostics) override;
-
- virtual SLANG_NO_THROW SlangResult SLANG_MCALL getPrecompiledTargetCode(
- SlangCompileTarget target,
- slang::IBlob** outCode,
- slang::IBlob** outDiagnostics = nullptr) override;
-
- virtual SLANG_NO_THROW SlangInt SLANG_MCALL getModuleDependencyCount() SLANG_OVERRIDE;
-
- virtual SLANG_NO_THROW SlangResult SLANG_MCALL getModuleDependency(
- SlangInt dependencyIndex,
- slang::IModule** outModule,
- slang::IBlob** outDiagnostics = nullptr) SLANG_OVERRIDE;
-
- virtual void buildHash(DigestBuilder<SHA1>& builder) SLANG_OVERRIDE;
-
- virtual SLANG_NO_THROW slang::DeclReflection* SLANG_MCALL getModuleReflection() SLANG_OVERRIDE;
-
- void setDigest(SHA1::Digest const& digest) { m_digest = digest; }
- SHA1::Digest computeDigest();
-
- /// Create a module (initially empty).
- Module(Linkage* linkage, ASTBuilder* astBuilder = nullptr);
-
- /// Get the AST for the module (if it has been parsed)
- ModuleDecl* getModuleDecl() { return m_moduleDecl; }
-
- /// The the IR for the module (if it has been generated)
- IRModule* getIRModule() { return m_irModule; }
-
- /// Get the list of other modules this module depends on
- List<Module*> const& getModuleDependencyList()
- {
- return m_moduleDependencyList.getModuleList();
- }
-
- /// Get the list of files this module depends on
- List<SourceFile*> const& getFileDependencyList() { return m_fileDependencyList.getFileList(); }
-
- /// Register a module that this module depends on
- void addModuleDependency(Module* module);
-
- /// Register a source file that this module depends on
- void addFileDependency(SourceFile* sourceFile);
-
- void clearFileDependency() { m_fileDependencyList.clear(); }
- /// Set the AST for this module.
- ///
- /// This should only be called once, during creation of the module.
- ///
- void setModuleDecl(ModuleDecl* moduleDecl); // { m_moduleDecl = moduleDecl; }
-
- void setName(String name);
- void setName(Name* name) { m_name = name; }
- Name* getNameObj() { return m_name; }
-
- void setPathInfo(PathInfo pathInfo) { m_pathInfo = pathInfo; }
-
- /// Set the IR for this module.
- ///
- /// This should only be called once, during creation of the module.
- ///
- void setIRModule(IRModule* irModule) { m_irModule = irModule; }
-
- Index getEntryPointCount() SLANG_OVERRIDE { return 0; }
- RefPtr<EntryPoint> getEntryPoint(Index index) SLANG_OVERRIDE
- {
- SLANG_UNUSED(index);
- return nullptr;
- }
- String getEntryPointMangledName(Index index) SLANG_OVERRIDE
- {
- SLANG_UNUSED(index);
- return String();
- }
- String getEntryPointNameOverride(Index index) SLANG_OVERRIDE
- {
- SLANG_UNUSED(index);
- return String();
- }
-
- Index getShaderParamCount() SLANG_OVERRIDE { return m_shaderParams.getCount(); }
- ShaderParamInfo getShaderParam(Index index) SLANG_OVERRIDE { return m_shaderParams[index]; }
-
- SLANG_NO_THROW Index SLANG_MCALL getSpecializationParamCount() SLANG_OVERRIDE
- {
- return m_specializationParams.getCount();
- }
- SpecializationParam const& getSpecializationParam(Index index) SLANG_OVERRIDE
- {
- return m_specializationParams[index];
- }
-
- Index getRequirementCount() SLANG_OVERRIDE;
- RefPtr<ComponentType> getRequirement(Index index) SLANG_OVERRIDE;
-
- List<Module*> const& getModuleDependencies() SLANG_OVERRIDE
- {
- return m_moduleDependencyList.getModuleList();
- }
- List<SourceFile*> const& getFileDependencies() SLANG_OVERRIDE
- {
- return m_fileDependencyList.getFileList();
- }
-
- /// Given a mangled name finds the exported NodeBase associated with this module.
- /// If not found returns nullptr.
- Decl* findExportedDeclByMangledName(const UnownedStringSlice& mangledName);
-
- /// Ensure that the any accelerator(s) used for `findExportedDeclByMangledName`
- /// have already been built.
- ///
- void ensureExportLookupAcceleratorBuilt();
-
- Count getExportedDeclCount();
- Decl* getExportedDecl(Index index);
- UnownedStringSlice getExportedDeclMangledName(Index index);
-
- /// Get the ASTBuilder
- ASTBuilder* getASTBuilder() { return m_astBuilder; }
-
- /// Collect information on the shader parameters of the module.
- ///
- /// This method should only be called once, after the core
- /// structured of the module (its AST and IR) have been created,
- /// and before any of the `ComponentType` APIs are used.
- ///
- /// TODO: We might eventually consider a non-stateful approach
- /// to constructing a `Module`.
- ///
- void _collectShaderParams();
-
- void _discoverEntryPoints(DiagnosticSink* sink, const List<RefPtr<TargetRequest>>& targets);
- void _discoverEntryPointsImpl(
- ContainerDecl* containerDecl,
- DiagnosticSink* sink,
- const List<RefPtr<TargetRequest>>& targets);
-
-
- class ModuleSpecializationInfo : public SpecializationInfo
- {
- public:
- struct GenericArgInfo
- {
- Decl* paramDecl = nullptr;
- Val* argVal = nullptr;
- };
-
- List<GenericArgInfo> genericArgs;
- List<ExpandedSpecializationArg> existentialArgs;
- };
-
- RefPtr<EntryPoint> findEntryPointByName(UnownedStringSlice const& name);
- RefPtr<EntryPoint> findAndCheckEntryPoint(
- UnownedStringSlice const& name,
- SlangStage stage,
- ISlangBlob** outDiagnostics);
-
- List<RefPtr<EntryPoint>>& getEntryPoints() { return m_entryPoints; }
- void _addEntryPoint(EntryPoint* entryPoint);
- void _processFindDeclsExportSymbolsRec(Decl* decl);
-
- // Gets the files that has been included into the module.
- Dictionary<SourceFile*, FileDecl*>& getIncludedSourceFileMap()
- {
- return m_mapSourceFileToFileDecl;
- }
-
-protected:
- void acceptVisitor(ComponentTypeVisitor* visitor, SpecializationInfo* specializationInfo)
- SLANG_OVERRIDE;
-
- RefPtr<SpecializationInfo> _validateSpecializationArgsImpl(
- SpecializationArg const* args,
- Index argCount,
- DiagnosticSink* sink) SLANG_OVERRIDE;
-
-private:
- Name* m_name = nullptr;
- PathInfo m_pathInfo;
-
- // The AST for the module
- ModuleDecl* m_moduleDecl = nullptr;
-
- // The IR for the module
- RefPtr<IRModule> m_irModule = nullptr;
-
- List<ShaderParamInfo> m_shaderParams;
- SpecializationParams m_specializationParams;
-
- List<Module*> m_requirements;
-
- // A digest that uniquely identifies the contents of the module.
- SHA1::Digest m_digest;
-
- // List of modules this module depends on
- ModuleDependencyList m_moduleDependencyList;
-
- // List of source files this module depends on
- FileDependencyList m_fileDependencyList;
-
- // Entry points that were defined in this module
- //
- // Note: the entry point defined in the module are *not*
- // part of the memory image/layout of the module when
- // it is considered as an IComponentType. This can be
- // a bit confusing, but if all the entry points in the
- // module were automatically linked into the component
- // type, we'd need a way to access just the global
- // scope of the module without the entry points, in
- // case we wanted to link a single entry point against
- // the global scope. The `Module` type provides exactly
- // that "module without its entry points" unit of
- // granularity for linking.
- //
- // This list only exists for lookup purposes, so that
- // the user can find an existing entry-point function
- // that was defined as part of the module.
- //
- List<RefPtr<EntryPoint>> m_entryPoints;
-
- // The builder that owns all of the AST nodes from parsing the source of
- // this module.
- RefPtr<ASTBuilder> m_astBuilder;
-
- // Holds map of exported mangled names to symbols. m_mangledExportPool maps names to indices,
- // and m_mangledExportSymbols holds the NodeBase* values for each index.
- StringSlicePool m_mangledExportPool;
- List<Decl*> m_mangledExportSymbols;
-
- // Source files that have been pulled into the module with `__include`.
- Dictionary<SourceFile*, FileDecl*> m_mapSourceFileToFileDecl;
-
-public:
- SLANG_NO_THROW SlangResult SLANG_MCALL disassemble(slang::IBlob** outDisassembledBlob) override
- {
- if (!outDisassembledBlob)
- return SLANG_E_INVALID_ARG;
- String disassembly;
- this->getIRModule()->getModuleInst()->dump(disassembly);
- auto blob = StringUtil::createStringBlob(disassembly);
- *outDisassembledBlob = blob.detach();
- return SLANG_OK;
- }
-};
-typedef Module LoadedModule;
-
-/// A request for the front-end to compile a translation unit.
-class TranslationUnitRequest : public RefObject
-{
-public:
- TranslationUnitRequest(FrontEndCompileRequest* compileRequest);
- TranslationUnitRequest(FrontEndCompileRequest* compileRequest, Module* m);
-
- // The parent compile request
- FrontEndCompileRequest* compileRequest = nullptr;
-
- // The language in which the source file(s)
- // are assumed to be written
- SourceLanguage sourceLanguage = SourceLanguage::Unknown;
-
- /// Makes any source artifact available as a SourceFile.
- /// If successful any of the source artifacts will be represented by the same index
- /// of sourceArtifacts
- SlangResult requireSourceFiles();
-
- /// Get the source files.
- /// Since lazily evaluated requires calling requireSourceFiles to know it's in sync
- /// with sourceArtifacts.
- List<SourceFile*> const& getSourceFiles();
-
- /// Get the source artifacts associated
- const List<ComPtr<IArtifact>>& getSourceArtifacts() const { return m_sourceArtifacts; }
-
- /// Clear all of the source
- void clearSource()
- {
- m_sourceArtifacts.clear();
- m_sourceFiles.clear();
- m_includedFileSet.clear();
- }
-
- /// Add a source artifact
- void addSourceArtifact(IArtifact* sourceArtifact);
-
- /// Add both the artifact and the sourceFile.
- void addSource(IArtifact* sourceArtifact, SourceFile* sourceFile);
-
- void addIncludedSourceFileIfNotExist(SourceFile* sourceFile);
-
- // The entry points associated with this translation unit
- List<RefPtr<EntryPoint>> const& getEntryPoints() { return module->getEntryPoints(); }
-
- void _addEntryPoint(EntryPoint* entryPoint) { module->_addEntryPoint(entryPoint); }
-
- // Preprocessor definitions to use for this translation unit only
- // (whereas the ones on `compileRequest` will be shared)
- Dictionary<String, String> preprocessorDefinitions;
-
- /// The name that will be used for the module this translation unit produces.
- Name* moduleName = nullptr;
-
- /// Result of compiling this translation unit (a module)
- RefPtr<Module> module;
-
- bool isChecked = false;
-
- Module* getModule() { return module; }
- ModuleDecl* getModuleDecl() { return module->getModuleDecl(); }
-
- Session* getSession();
- NamePool* getNamePool();
- SourceManager* getSourceManager();
-
- Scope* getLanguageScope();
-
- Dictionary<String, String> getCombinedPreprocessorDefinitions();
-
- void setModuleName(Name* name)
- {
- moduleName = name;
- if (module)
- module->setName(name);
- }
-
-protected:
- void _addSourceFile(SourceFile* sourceFile);
- /* Given an artifact, find a PathInfo.
- If no PathInfo can be found will return an unknown PathInfo */
- PathInfo _findSourcePathInfo(IArtifact* artifact);
-
- List<ComPtr<IArtifact>> m_sourceArtifacts;
- // The source file(s) that will be compiled to form this translation unit
- //
- // Usually, for HLSL or GLSL there will be only one file.
- // NOTE! This member is generated lazily from m_sourceArtifacts
- // it is *necessary* to call requireSourceFiles to ensure it's in sync.
- List<SourceFile*> m_sourceFiles;
-
- // Track all the included source files added in m_sourceFiles
- HashSet<SourceFile*> m_includedFileSet;
-};
enum class FloatingPointMode : SlangFloatingPointModeIntegral
{
@@ -2044,105 +161,6 @@ enum class FloatingPointDenormalMode : SlangFpDenormalModeIntegral
FlushToZero = SLANG_FP_DENORM_MODE_FTZ,
};
-enum class WriterChannel : SlangWriterChannelIntegral
-{
- Diagnostic = SLANG_WRITER_CHANNEL_DIAGNOSTIC,
- StdOutput = SLANG_WRITER_CHANNEL_STD_OUTPUT,
- StdError = SLANG_WRITER_CHANNEL_STD_ERROR,
- CountOf = SLANG_WRITER_CHANNEL_COUNT_OF,
-};
-
-enum class WriterMode : SlangWriterModeIntegral
-{
- Text = SLANG_WRITER_MODE_TEXT,
- Binary = SLANG_WRITER_MODE_BINARY,
-};
-
-class TargetRequest;
-
-/// Are we generating code for a D3D API?
-bool isD3DTarget(TargetRequest* targetReq);
-
-// Are we generating code for Metal?
-bool isMetalTarget(TargetRequest* targetReq);
-
-/// Are we generating code for a Khronos API (OpenGL or Vulkan)?
-bool isKhronosTarget(TargetRequest* targetReq);
-bool isKhronosTarget(CodeGenTarget target);
-
-/// Are we generating code for a CUDA API (CUDA / OptiX)?
-bool isCUDATarget(TargetRequest* targetReq);
-
-// Are we generating code for a CPU target
-bool isCPUTarget(TargetRequest* targetReq);
-
-/// Are we generating code for the WebGPU API?
-bool isWGPUTarget(TargetRequest* targetReq);
-bool isWGPUTarget(CodeGenTarget target);
-
-/// A request to generate output in some target format.
-class TargetRequest : public RefObject
-{
-public:
- TargetRequest(Linkage* linkage, CodeGenTarget format);
-
- TargetRequest(const TargetRequest& other);
-
- Linkage* getLinkage() { return linkage; }
-
- Session* getSession();
-
- CodeGenTarget getTarget()
- {
- return optionSet.getEnumOption<CodeGenTarget>(CompilerOptionName::Target);
- }
-
- // TypeLayouts created on the fly by reflection API
- struct TypeLayoutKey
- {
- Type* type;
- slang::LayoutRules rules;
- HashCode getHashCode() const
- {
- Hasher hasher;
- hasher.hashValue(type);
- hasher.hashValue(rules);
- return hasher.getResult();
- }
- bool operator==(TypeLayoutKey other) const
- {
- return type == other.type && rules == other.rules;
- }
- };
- Dictionary<TypeLayoutKey, RefPtr<TypeLayout>> typeLayouts;
-
- Dictionary<TypeLayoutKey, RefPtr<TypeLayout>>& getTypeLayouts() { return typeLayouts; }
-
- TypeLayout* getTypeLayout(Type* type, slang::LayoutRules rules);
-
- CompilerOptionSet& getOptionSet() { return optionSet; }
-
- CapabilitySet getTargetCaps();
-
- void setTargetCaps(CapabilitySet capSet);
-
- HLSLToVulkanLayoutOptions* getHLSLToVulkanLayoutOptions();
-
-private:
- Linkage* linkage = nullptr;
- CompilerOptionSet optionSet;
- CapabilitySet cookedCapabilities;
- RefPtr<HLSLToVulkanLayoutOptions> hlslToVulkanOptions;
-};
-
-/// Given a target request returns which (if any) intermediate source language is required
-/// to produce it.
-///
-/// If no intermediate source language is required, will return SourceLanguage::Unknown
-SourceLanguage getIntermediateSourceLanguageForTarget(TargetProgram* req);
-
-/// Are resource types "bindless" (implemented as ordinary data) on the given `target`?
-bool areResourceTypesBindlessOnTarget(TargetRequest* target);
// Compute the "effective" profile to use when outputting the given entry point
// for the chosen code-generation target.
@@ -2160,1721 +178,12 @@ bool areResourceTypesBindlessOnTarget(TargetRequest* target);
Profile getEffectiveProfile(EntryPoint* entryPoint, TargetRequest* target);
-/// Given a target returns the required downstream compiler
-PassThroughMode getDownstreamCompilerRequiredForTarget(CodeGenTarget target);
-/// Given a target returns a downstream compiler the prelude should be taken from.
-SourceLanguage getDefaultSourceLanguageForDownstreamCompiler(PassThroughMode compiler);
-
/// Get the build tag string
const char* getBuildTagString();
-struct TypeCheckingCache;
-
-struct ContainerTypeKey
-{
- slang::TypeReflection* elementType;
- slang::ContainerType containerType;
- bool operator==(ContainerTypeKey other) const
- {
- return elementType == other.elementType && containerType == other.containerType;
- }
- Slang::HashCode getHashCode() const
- {
- return Slang::combineHash(
- Slang::getHashCode(elementType),
- Slang::getHashCode(containerType));
- }
-};
-
-/// A dictionary of modules to be considered when resolving `import`s,
-/// beyond those that would normally be found through a `Linkage`.
-///
-/// Checking of an `import` declaration will bottleneck through
-/// `Linkage::findOrImportModule`, which would usually just check for
-/// any module that had been previously loaded into the same `Linkage`
-/// (e.g., by a call to `Linkage::loadModule()`).
-///
-/// In the case where compilation is being done through an
-/// explicit `FrontEndCompileRequest` or `EndToEndCompileRequest`,
-/// the modules being compiled by that request do not get added to
-/// the surrounding `Linkage`.
-///
-/// There is a corner case when an explicit compile request has
-/// multiple `TranslationUnitRequest`s, because the user (reasonably)
-/// expects that if they compile `A.slang` and `B.slang` as two
-/// distinct translation units in the same compile request, then
-/// an `import B` inside of `A.slang` should resolve to reference
-/// the code of `B.slang`. But because neither `A` nor `B` gets
-/// added to the `Linkage`, and the `Linkage` is what usually
-/// determines what is or isn't loaded, that intuition will
-/// be wrong, without a bit of help.
-///
-/// The `LoadedModuleDictionary` is thus filled in by a
-/// `FrontEndCompileRequest` to collect the modules it is compiling,
-/// so that they can cross-reference one another (albeit with
-/// a current implementation restriction that modules in the
-/// request can only `import` those earlier in the request...).
-///
-/// The dictionary then gets passed around between nearly all of
-/// the operations that deal with loading modules, to make sure
-/// that they can detect a previously loaded module.
-///
-typedef Dictionary<Name*, Module*> LoadedModuleDictionary;
-
-enum ModuleBlobType
-{
- Source,
- IR
-};
-
-/// A context for loading and re-using code modules.
-class Linkage : public RefObject, public slang::ISession
-{
-public:
- SLANG_REF_OBJECT_IUNKNOWN_ALL
-
- CompilerOptionSet m_optionSet;
-
- ISlangUnknown* getInterface(const Guid& guid);
-
- SLANG_NO_THROW slang::IGlobalSession* SLANG_MCALL getGlobalSession() override;
- SLANG_NO_THROW slang::IModule* SLANG_MCALL
- loadModule(const char* moduleName, slang::IBlob** outDiagnostics = nullptr) override;
- slang::IModule* loadModuleFromBlob(
- const char* moduleName,
- const char* path,
- slang::IBlob* source,
- ModuleBlobType blobType,
- slang::IBlob** outDiagnostics = nullptr);
- SLANG_NO_THROW slang::IModule* SLANG_MCALL loadModuleFromIRBlob(
- const char* moduleName,
- const char* path,
- slang::IBlob* source,
- slang::IBlob** outDiagnostics = nullptr) override;
- SLANG_NO_THROW SlangResult SLANG_MCALL loadModuleInfoFromIRBlob(
- slang::IBlob* source,
- SlangInt& outModuleVersion,
- const char*& outModuleCompilerVersion,
- const char*& outModuleName) override;
- SLANG_NO_THROW slang::IModule* SLANG_MCALL loadModuleFromSource(
- const char* moduleName,
- const char* path,
- slang::IBlob* source,
- slang::IBlob** outDiagnostics = nullptr) override;
- SLANG_NO_THROW slang::IModule* SLANG_MCALL loadModuleFromSourceString(
- const char* moduleName,
- const char* path,
- const char* string,
- slang::IBlob** outDiagnostics = nullptr) override;
- SLANG_NO_THROW SlangResult SLANG_MCALL createCompositeComponentType(
- slang::IComponentType* const* componentTypes,
- SlangInt componentTypeCount,
- slang::IComponentType** outCompositeComponentType,
- ISlangBlob** outDiagnostics = nullptr) override;
- SLANG_NO_THROW slang::TypeReflection* SLANG_MCALL specializeType(
- slang::TypeReflection* type,
- slang::SpecializationArg const* specializationArgs,
- SlangInt specializationArgCount,
- ISlangBlob** outDiagnostics = nullptr) override;
- SLANG_NO_THROW slang::TypeLayoutReflection* SLANG_MCALL getTypeLayout(
- slang::TypeReflection* type,
- SlangInt targetIndex = 0,
- slang::LayoutRules rules = slang::LayoutRules::Default,
- ISlangBlob** outDiagnostics = nullptr) override;
- SLANG_NO_THROW slang::TypeReflection* SLANG_MCALL getContainerType(
- slang::TypeReflection* elementType,
- slang::ContainerType containerType,
- ISlangBlob** outDiagnostics = nullptr) override;
- SLANG_NO_THROW slang::TypeReflection* SLANG_MCALL getDynamicType() override;
- SLANG_NO_THROW SlangResult SLANG_MCALL
- getTypeRTTIMangledName(slang::TypeReflection* type, ISlangBlob** outNameBlob) override;
- SLANG_NO_THROW SlangResult SLANG_MCALL getTypeConformanceWitnessMangledName(
- slang::TypeReflection* type,
- slang::TypeReflection* interfaceType,
- ISlangBlob** outNameBlob) override;
- SLANG_NO_THROW SlangResult SLANG_MCALL getTypeConformanceWitnessSequentialID(
- slang::TypeReflection* type,
- slang::TypeReflection* interfaceType,
- uint32_t* outId) override;
- SLANG_NO_THROW SlangResult SLANG_MCALL getDynamicObjectRTTIBytes(
- slang::TypeReflection* type,
- slang::TypeReflection* interfaceType,
- uint32_t* outBytes,
- uint32_t bufferSize) override;
- SLANG_NO_THROW SlangResult SLANG_MCALL createTypeConformanceComponentType(
- slang::TypeReflection* type,
- slang::TypeReflection* interfaceType,
- slang::ITypeConformance** outConformance,
- SlangInt conformanceIdOverride,
- ISlangBlob** outDiagnostics) override;
- SLANG_NO_THROW SlangResult SLANG_MCALL
- createCompileRequest(SlangCompileRequest** outCompileRequest) override;
- virtual SLANG_NO_THROW SlangInt SLANG_MCALL getLoadedModuleCount() override;
- virtual SLANG_NO_THROW slang::IModule* SLANG_MCALL getLoadedModule(SlangInt index) override;
- virtual SLANG_NO_THROW bool SLANG_MCALL
- isBinaryModuleUpToDate(const char* modulePath, slang::IBlob* binaryModuleBlob) override;
-
- // Updates the supplied builder with linkage-related information, which includes preprocessor
- // defines, the compiler version, and other compiler options. This is then merged with the hash
- // produced for the program to produce a key that can be used with the shader cache.
- void buildHash(DigestBuilder<SHA1>& builder, SlangInt targetIndex = -1);
-
- void addTarget(slang::TargetDesc const& desc);
- SlangResult addSearchPath(char const* path);
- SlangResult addPreprocessorDefine(char const* name, char const* value);
- SlangResult setMatrixLayoutMode(SlangMatrixLayoutMode mode);
- /// Create an initially-empty linkage
- Linkage(Session* session, ASTBuilder* astBuilder, Linkage* builtinLinkage);
-
- /// Dtor
- ~Linkage();
-
- bool isInLanguageServer()
- {
- return contentAssistInfo.checkingMode != ContentAssistCheckingMode::None;
- }
-
- /// Get the parent session for this linkage
- Session* getSessionImpl() { return m_session; }
-
- // Information on the targets we are being asked to
- // generate code for.
- List<RefPtr<TargetRequest>> targets;
-
- // Directories to search for `#include` files or `import`ed modules
- SearchDirectoryList& getSearchDirectories();
-
- // Source manager to help track files loaded
- SourceManager m_defaultSourceManager;
- SourceManager* m_sourceManager = nullptr;
- RefPtr<CommandLineContext> m_cmdLineContext;
-
- // Used to store strings returned by the api as const char*
- StringSlicePool m_stringSlicePool;
-
- // Name pool for looking up names
- NamePool* namePool = nullptr;
-
- NamePool* getNamePool() { return namePool; }
-
- ASTBuilder* getASTBuilder() { return m_astBuilder; }
-
- RefPtr<ASTBuilder> m_astBuilder;
-
- // Cache for container types.
- Dictionary<ContainerTypeKey, Type*> m_containerTypes;
-
- // cache used by type checking, implemented in check.cpp
- TypeCheckingCache* getTypeCheckingCache();
- void destroyTypeCheckingCache();
-
- RefPtr<RefObject> m_typeCheckingCache = nullptr;
-
- // Modules that have been dynamically loaded via `import`
- //
- // This is a list of unique modules loaded, in the order they were encountered.
- List<RefPtr<LoadedModule>> loadedModulesList;
-
- // Map from the path (or uniqueIdentity if available) of a module file to its definition
- Dictionary<String, RefPtr<LoadedModule>> mapPathToLoadedModule;
-
- // Map from the logical name of a module to its definition
- Dictionary<Name*, RefPtr<LoadedModule>> mapNameToLoadedModules;
-
- // Map from the mangled name of RTTI objects to sequential IDs
- // used by `switch`-based dynamic dispatch.
- Dictionary<String, uint32_t> mapMangledNameToRTTIObjectIndex;
-
- // Counters for allocating sequential IDs to witness tables conforming to each interface type.
- Dictionary<String, uint32_t> mapInterfaceMangledNameToSequentialIDCounters;
-
- SearchDirectoryList searchDirectoryCache;
-
- // The resulting specialized IR module for each entry point request
- List<RefPtr<IRModule>> compiledModules;
-
- ContentAssistInfo contentAssistInfo;
-
- /// File system implementation to use when loading files from disk.
- ///
- /// If this member is `null`, a default implementation that tries
- /// to use the native OS filesystem will be used instead.
- ///
- ComPtr<ISlangFileSystem> m_fileSystem;
-
- /// The extended file system implementation. Will be set to a default implementation
- /// if fileSystem is nullptr. Otherwise it will either be fileSystem's interface,
- /// or a wrapped impl that makes fileSystem operate as fileSystemExt
- ComPtr<ISlangFileSystemExt> m_fileSystemExt;
-
- /// Get the currenly set file system
- ISlangFileSystemExt* getFileSystemExt() { return m_fileSystemExt; }
-
- /// Load a file into memory using the configured file system.
- ///
- /// @param path The path to attempt to load from
- /// @param outBlob A destination pointer to receive the loaded blob
- /// @returns A `SlangResult` to indicate success or failure.
- ///
- SlangResult loadFile(String const& path, PathInfo& outPathInfo, ISlangBlob** outBlob);
-
- Expr* parseTermString(String str, Scope* scope);
-
- Type* specializeType(
- Type* unspecializedType,
- Int argCount,
- Type* const* args,
- DiagnosticSink* sink);
-
- /// Add a new target and return its index.
- UInt addTarget(CodeGenTarget target);
-
- /// "Bottleneck" routine for loading a module.
- ///
- /// All attempts to load a module, whether through
- /// Slang API calls, `import` operations, or other
- /// means, should bottleneck through `loadModuleImpl`,
- /// or one of the specialized cases `loadSourceModuleImpl`
- /// and `loadBinaryModuleImpl`.
- ///
- RefPtr<Module> loadModuleImpl(
- Name* name,
- const PathInfo& filePathInfo,
- ISlangBlob* fileContentsBlob,
- SourceLoc const& loc,
- DiagnosticSink* sink,
- const LoadedModuleDictionary* additionalLoadedModules,
- ModuleBlobType blobType);
-
- RefPtr<Module> loadSourceModuleImpl(
- Name* name,
- const PathInfo& filePathInfo,
- ISlangBlob* fileContentsBlob,
- SourceLoc const& loc,
- DiagnosticSink* sink,
- const LoadedModuleDictionary* additionalLoadedModules);
-
- RefPtr<Module> loadBinaryModuleImpl(
- Name* name,
- const PathInfo& filePathInfo,
- ISlangBlob* fileContentsBlob,
- SourceLoc const& loc,
- DiagnosticSink* sink);
-
- /// Either finds a previously-loaded module matching what
- /// was serialized into `moduleChunk`, or else attempts
- /// to load the serialized module.
- ///
- /// If a previously-loaded module is found that matches the
- /// name or path information in `moduleChunk`, then that
- /// previously-loaded module is returned.
- ///
- /// Othwerise, attempts to load a module from `moduleChunk`
- /// and, if successful, returns the freshly loaded module.
- ///
- /// Otherwise, return null.
- ///
- RefPtr<Module> findOrLoadSerializedModuleForModuleLibrary(
- ISlangBlob* blobHoldingSerializedData,
- ModuleChunk const* moduleChunk,
- RIFF::ListChunk const* libraryChunk,
- DiagnosticSink* sink);
-
- RefPtr<Module> loadSerializedModule(
- Name* moduleName,
- const PathInfo& moduleFilePathInfo,
- ISlangBlob* blobHoldingSerializedData,
- ModuleChunk const* moduleChunk,
- RIFF::ListChunk const* containerChunk, //< The outer container, if there is one.
- SourceLoc const& requestingLoc,
- DiagnosticSink* sink);
-
- SlangResult loadSerializedModuleContents(
- Module* module,
- const PathInfo& moduleFilePathInfo,
- ISlangBlob* blobHoldingSerializedData,
- ModuleChunk const* moduleChunk,
- RIFF::ListChunk const* containerChunk, //< The outer container, if there is one.
- DiagnosticSink* sink);
-
- SourceFile* loadSourceFile(String pathFrom, String path);
-
- void loadParsedModule(
- RefPtr<FrontEndCompileRequest> compileRequest,
- RefPtr<TranslationUnitRequest> translationUnit,
- Name* name,
- PathInfo const& pathInfo);
-
- bool isBinaryModuleUpToDate(String fromPath, RIFF::ListChunk const* baseChunk);
-
- RefPtr<Module> findOrImportModule(
- Name* name,
- SourceLoc const& loc,
- DiagnosticSink* sink,
- const LoadedModuleDictionary* loadedModules = nullptr);
-
- SourceFile* findFile(Name* name, SourceLoc loc, IncludeSystem& outIncludeSystem);
- struct IncludeResult
- {
- FileDecl* fileDecl;
- bool isNew;
- };
- IncludeResult findAndIncludeFile(
- Module* module,
- TranslationUnitRequest* translationUnit,
- Name* name,
- SourceLoc const& loc,
- DiagnosticSink* sink);
-
- SourceManager* getSourceManager() { return m_sourceManager; }
-
- /// Override the source manager for the linkage.
- ///
- /// This is only used to install a temporary override when
- /// parsing stuff from strings (where we don't want to retain
- /// full source files for the parsed result).
- ///
- /// TODO: We should remove the need for this hack.
- ///
- void setSourceManager(SourceManager* sourceManager) { m_sourceManager = sourceManager; }
-
- void setRequireCacheFileSystem(bool requireCacheFileSystem);
-
- void setFileSystem(ISlangFileSystem* fileSystem);
-
- DeclRef<Decl> specializeGeneric(
- DeclRef<Decl> declRef,
- List<Expr*> argExprs,
- DiagnosticSink* sink);
-
- DeclRef<Decl> specializeWithArgTypes(
- Expr* funcExpr,
- List<Type*> argTypes,
- DiagnosticSink* sink);
-
- bool isSpecialized(DeclRef<Decl> declRef);
-
- DiagnosticSink::Flags diagnosticSinkFlags = 0;
-
- bool m_requireCacheFileSystem = false;
-
- // Modules that have been read in with the -r option
- List<ComPtr<IArtifact>> m_libModules;
-
- void _stopRetainingParentSession() { m_retainedSession = nullptr; }
-
- // Get shared semantics information for reflection purposes.
- SharedSemanticsContext* getSemanticsForReflection();
-
-private:
- /// The global Slang library session that this linkage is a child of
- Session* m_session = nullptr;
-
- RefPtr<Session> m_retainedSession;
-
- /// Tracks state of modules currently being loaded.
- ///
- /// This information is used to diagnose cases where
- /// a user tries to recursively import the same module
- /// (possibly along a transitive chain of `import`s).
- ///
- struct ModuleBeingImportedRAII
- {
- public:
- ModuleBeingImportedRAII(
- Linkage* linkage,
- Module* module,
- Name* name,
- SourceLoc const& importLoc)
- : linkage(linkage), module(module), name(name), importLoc(importLoc)
- {
- next = linkage->m_modulesBeingImported;
- linkage->m_modulesBeingImported = this;
- }
-
- ~ModuleBeingImportedRAII() { linkage->m_modulesBeingImported = next; }
-
- Linkage* linkage;
- Module* module;
- Name* name;
- SourceLoc importLoc;
- ModuleBeingImportedRAII* next;
- };
-
- // Any modules currently being imported will be listed here
- ModuleBeingImportedRAII* m_modulesBeingImported = nullptr;
-
- /// Is the given module in the middle of being imported?
- bool isBeingImported(Module* module);
-
- /// Diagnose that an error occured in the process of importing a module
- void _diagnoseErrorInImportedModule(DiagnosticSink* sink);
-
- List<Type*> m_specializedTypes;
-
- RefPtr<SharedSemanticsContext> m_semanticsForReflection;
-};
-
-/// Shared functionality between front- and back-end compile requests.
-///
-/// This is the base class for both `FrontEndCompileRequest` and
-/// `BackEndCompileRequest`, and allows a small number of parts of
-/// the compiler to be easily invocable from either front-end or
-/// back-end work.
-///
-class CompileRequestBase : public RefObject
-{
- // TODO: We really shouldn't need this type in the long run.
- // The few places that rely on it should be refactored to just
- // depend on the underlying information (a linkage and a diagnostic
- // sink) directly.
- //
- // The flags to control dumping and validation of IR should be
- // moved to some kind of shared settings/options `struct` that
- // both front-end and back-end requests can store.
-
-public:
- Session* getSession();
- Linkage* getLinkage() { return m_linkage; }
- DiagnosticSink* getSink() { return m_sink; }
- SourceManager* getSourceManager() { return getLinkage()->getSourceManager(); }
- NamePool* getNamePool() { return getLinkage()->getNamePool(); }
- ISlangFileSystemExt* getFileSystemExt() { return getLinkage()->getFileSystemExt(); }
- SlangResult loadFile(String const& path, PathInfo& outPathInfo, ISlangBlob** outBlob)
- {
- return getLinkage()->loadFile(path, outPathInfo, outBlob);
- }
-
-protected:
- CompileRequestBase(Linkage* linkage, DiagnosticSink* sink);
-
-private:
- Linkage* m_linkage = nullptr;
- DiagnosticSink* m_sink = nullptr;
-};
-
-/// A request to compile source code to an AST + IR.
-class FrontEndCompileRequest : public CompileRequestBase
-{
-public:
- /// Note that writers can be parsed as nullptr to disable output,
- /// and individual channels set to null to disable them
- FrontEndCompileRequest(Linkage* linkage, StdWriters* writers, DiagnosticSink* sink);
-
- int addEntryPoint(int translationUnitIndex, String const& name, Profile entryPointProfile);
-
- // Translation units we are being asked to compile
- List<RefPtr<TranslationUnitRequest>> translationUnits;
-
- // Additional modules that needs to be made visible to `import` while checking.
- const LoadedModuleDictionary* additionalLoadedModules = nullptr;
-
- RefPtr<TranslationUnitRequest> getTranslationUnit(UInt index)
- {
- return translationUnits[index];
- }
-
- // If true will serialize and de-serialize with debug information
- bool verifyDebugSerialization = false;
-
- CompilerOptionSet optionSet;
-
- List<RefPtr<FrontEndEntryPointRequest>> m_entryPointReqs;
-
- List<RefPtr<FrontEndEntryPointRequest>> const& getEntryPointReqs() { return m_entryPointReqs; }
- UInt getEntryPointReqCount() { return m_entryPointReqs.getCount(); }
- FrontEndEntryPointRequest* getEntryPointReq(UInt index) { return m_entryPointReqs[index]; }
-
- void parseTranslationUnit(TranslationUnitRequest* translationUnit);
-
- // Perform primary semantic checking on all
- // of the translation units in the program
- void checkAllTranslationUnits();
-
- void checkEntryPoints();
-
- void generateIR();
-
- SlangResult executeActionsInner();
-
- /// Add a translation unit to be compiled.
- ///
- /// @param language The source language that the translation unit will use (e.g.,
- /// `SourceLanguage::Slang`
- /// @param moduleName The name that will be used for the module compile from the translation
- /// unit.
- ///
- /// If moduleName is passed as nullptr a module name is generated.
- /// If all translation units in a compile request use automatically generated
- /// module names, then they are guaranteed not to conflict with one another.
- ///
- /// @return The zero-based index of the translation unit in this compile request.
- int addTranslationUnit(SourceLanguage language, Name* moduleName);
-
- int addTranslationUnit(TranslationUnitRequest* translationUnit);
-
- void addTranslationUnitSourceArtifact(int translationUnitIndex, IArtifact* sourceArtifact);
-
- void addTranslationUnitSourceBlob(
- int translationUnitIndex,
- String const& path,
- ISlangBlob* sourceBlob);
-
- void addTranslationUnitSourceFile(int translationUnitIndex, String const& path);
-
- /// Get a component type that represents the global scope of the compile request.
- ComponentType* getGlobalComponentType() { return m_globalComponentType; }
-
- /// Get a component type that represents the global scope of the compile request, plus the
- /// requested entry points.
- ComponentType* getGlobalAndEntryPointsComponentType()
- {
- return m_globalAndEntryPointsComponentType;
- }
-
- List<RefPtr<ComponentType>> const& getUnspecializedEntryPoints()
- {
- return m_unspecializedEntryPoints;
- }
-
- /// Does the code we are compiling represent part of the Slang core module?
- bool m_isCoreModuleCode = false;
-
- Name* m_defaultModuleName = nullptr;
-
- /// The irDumpOptions
- IRDumpOptions m_irDumpOptions;
-
- /// An "extra" entry point that was added via a library reference
- struct ExtraEntryPointInfo
- {
- Name* name;
- Profile profile;
- String mangledName;
- };
-
- /// A list of "extra" entry points added via a library reference
- List<ExtraEntryPointInfo> m_extraEntryPoints;
-
-private:
- /// A component type that includes only the global scopes of the translation unit(s) that were
- /// compiled.
- RefPtr<ComponentType> m_globalComponentType;
-
- /// A component type that extends the global scopes with all of the entry points that were
- /// specified.
- RefPtr<ComponentType> m_globalAndEntryPointsComponentType;
-
- List<RefPtr<ComponentType>> m_unspecializedEntryPoints;
-
- RefPtr<StdWriters> m_writers;
-};
-
-/// A visitor for use with `ComponentType`s, allowing dispatch over the concrete subclasses.
-class ComponentTypeVisitor
-{
-public:
- // The following methods should be overriden in a concrete subclass
- // to customize how it acts on each of the concrete types of component.
- //
- // In cases where the application wants to simply "recurse" on a
- // composite, specialized, or legacy component type it can use
- // the `visitChildren` methods below.
- //
- virtual void visitEntryPoint(
- EntryPoint* entryPoint,
- EntryPoint::EntryPointSpecializationInfo* specializationInfo) = 0;
- virtual void visitModule(
- Module* module,
- Module::ModuleSpecializationInfo* specializationInfo) = 0;
- virtual void visitComposite(
- CompositeComponentType* composite,
- CompositeComponentType::CompositeSpecializationInfo* specializationInfo) = 0;
- virtual void visitSpecialized(SpecializedComponentType* specialized) = 0;
- virtual void visitTypeConformance(TypeConformance* conformance) = 0;
- virtual void visitRenamedEntryPoint(
- RenamedEntryPointComponentType* renamedEntryPoint,
- EntryPoint::EntryPointSpecializationInfo* specializationInfo) = 0;
-
-protected:
- // These helpers can be used to recurse into the logical children of a
- // component type, and are useful for the common case where a visitor
- // only cares about a few leaf cases.
- //
- void visitChildren(
- CompositeComponentType* composite,
- CompositeComponentType::CompositeSpecializationInfo* specializationInfo);
- void visitChildren(SpecializedComponentType* specialized);
-};
-
-/// A `TargetProgram` represents a `ComponentType` specialized for a particular `TargetRequest`
-///
-/// TODO: This should probably be renamed to `TargetComponentType`.
-///
-/// By binding a component type to a specific target, a `TargetProgram` allows
-/// for things like layout to be computed, that fundamentally depend on
-/// the choice of target.
-///
-/// A `TargetProgram` handles request for compiled kernel code for
-/// entry point functions. In practice, kernel code can only be
-/// correctly generated when the underlying `ComponentType` is "fully linked"
-/// (has no remaining unsatisfied requirements).
-///
-class TargetProgram : public RefObject
-{
-public:
- TargetProgram(ComponentType* componentType, TargetRequest* targetReq);
-
- /// Get the underlying program
- ComponentType* getProgram() { return m_program; }
-
- /// Get the underlying target
- TargetRequest* getTargetReq() { return m_targetReq; }
-
- /// Get the layout for the program on the target.
- ///
- /// If this is the first time the layout has been
- /// requested, report any errors that arise during
- /// layout to the given `sink`.
- ///
- ProgramLayout* getOrCreateLayout(DiagnosticSink* sink);
-
- /// Get the layout for the program on the target.
- ///
- /// This routine assumes that `getOrCreateLayout`
- /// has already been called previously.
- ///
- ProgramLayout* getExistingLayout()
- {
- SLANG_ASSERT(m_layout);
- return m_layout;
- }
-
- /// Get the compiled code for an entry point on the target.
- ///
- /// If this is the first time that code generation has
- /// been requested, report any errors that arise during
- /// code generation to the given `sink`.
- ///
- IArtifact* getOrCreateEntryPointResult(Int entryPointIndex, DiagnosticSink* sink);
- IArtifact* getOrCreateWholeProgramResult(DiagnosticSink* sink);
-
- IArtifact* getExistingWholeProgramResult() { return m_wholeProgramResult; }
- /// Get the compiled code for an entry point on the target.
- ///
- /// This routine assumes that `getOrCreateEntryPointResult`
- /// has already been called previously.
- ///
- IArtifact* getExistingEntryPointResult(Int entryPointIndex)
- {
- return m_entryPointResults[entryPointIndex];
- }
-
- IArtifact* _createWholeProgramResult(
- DiagnosticSink* sink,
- EndToEndCompileRequest* endToEndReq = nullptr);
-
- /// Internal helper for `getOrCreateEntryPointResult`.
- ///
- /// This is used so that command-line and API-based
- /// requests for code can bottleneck through the same place.
- ///
- /// Shouldn't be called directly by most code.
- ///
- IArtifact* _createEntryPointResult(
- Int entryPointIndex,
- DiagnosticSink* sink,
- EndToEndCompileRequest* endToEndReq = nullptr);
-
- RefPtr<IRModule> getOrCreateIRModuleForLayout(DiagnosticSink* sink);
-
- RefPtr<IRModule> getExistingIRModuleForLayout() { return m_irModuleForLayout; }
-
- CompilerOptionSet& getOptionSet() { return m_optionSet; }
-
- HLSLToVulkanLayoutOptions* getHLSLToVulkanLayoutOptions()
- {
- return m_targetReq->getHLSLToVulkanLayoutOptions();
- }
-
- bool shouldEmitSPIRVDirectly()
- {
- return isKhronosTarget(m_targetReq) && getOptionSet().shouldEmitSPIRVDirectly();
- }
-
-private:
- RefPtr<IRModule> createIRModuleForLayout(DiagnosticSink* sink);
-
- // The program being compiled or laid out
- ComponentType* m_program;
-
- // The target that code/layout will be generated for
- TargetRequest* m_targetReq;
-
- // The computed layout, if it has been generated yet
- RefPtr<ProgramLayout> m_layout;
-
- CompilerOptionSet m_optionSet;
-
- // Generated compile results for each entry point
- // in the parent `Program` (indexing matches
- // the order they are given in the `Program`)
- ComPtr<IArtifact> m_wholeProgramResult;
- List<ComPtr<IArtifact>> m_entryPointResults;
-
- RefPtr<IRModule> m_irModuleForLayout;
-};
-
-/// A back-end-specific object to track optional feaures/capabilities/extensions
-/// that are discovered to be used by a program/kernel as part of code generation.
-class ExtensionTracker : public RefObject
-{
- // TODO: The existence of this type is evidence of a design/architecture problem.
- //
- // A better formulation of things requires a few key changes:
- //
- // 1. All optional capabilities need to be enumerated as part of the `CapabilitySet`
- // system, so that they can be reasoned about uniformly across different targets
- // and different layers of the compiler.
- //
- // 2. The front-end should be responsible for either or both of:
- //
- // * Checking that `public` or otherwise externally-visible items (declarations/definitions)
- // explicitly declare the capabilities they require, and that they only ever
- // make use of items that are comatible with those required capabilities.
- //
- // * Inferring the capabilities required by items that are not externally visible,
- // and attaching those capabilities explicit as a modifier or other synthesized AST node.
- //
- // 3. The capabilities required by a given `ComponentType` and its entry points should be
- // explicitly know-able, and they should be something we can compare to the capabilities
- // of a code generation target *before* back-end code generation is started. We should be
- // able to issue error messages around lacking capabilities in a way the user can understand,
- // in terms of the high-level-language entities.
-
-public:
-};
-
-struct RequiredLoweringPassSet
-{
- bool debugInfo;
- bool resultType;
- bool optionalType;
- bool enumType;
- bool combinedTextureSamplers;
- bool reinterpret;
- bool generics;
- bool bindExistential;
- bool autodiff;
- bool derivativePyBindWrapper;
- bool bitcast;
- bool existentialTypeLayout;
- bool bindingQuery;
- bool meshOutput;
- bool higherOrderFunc;
- bool globalVaryingVar;
- bool glslSSBO;
- bool byteAddressBuffer;
- bool dynamicResource;
- bool dynamicResourceHeap;
- bool resolveVaryingInputRef;
- bool specializeStageSwitch;
- bool missingReturn;
- bool nonVectorCompositeSelect;
-};
-
-/// A context for code generation in the compiler back-end
-struct CodeGenContext
-{
-public:
- typedef List<Index> EntryPointIndices;
-
- struct Shared
- {
- public:
- Shared(
- TargetProgram* targetProgram,
- EntryPointIndices const& entryPointIndices,
- DiagnosticSink* sink,
- EndToEndCompileRequest* endToEndReq)
- : targetProgram(targetProgram)
- , entryPointIndices(entryPointIndices)
- , sink(sink)
- , endToEndReq(endToEndReq)
- {
- }
-
- // Shared(
- // TargetProgram* targetProgram,
- // EndToEndCompileRequest* endToEndReq);
-
- TargetProgram* targetProgram = nullptr;
- EntryPointIndices entryPointIndices;
- DiagnosticSink* sink = nullptr;
- EndToEndCompileRequest* endToEndReq = nullptr;
- };
-
- CodeGenContext(Shared* shared)
- : m_shared(shared)
- , m_targetFormat(shared->targetProgram->getTargetReq()->getTarget())
- , m_targetProfile(shared->targetProgram->getOptionSet().getProfile())
- {
- }
-
- CodeGenContext(
- CodeGenContext* base,
- CodeGenTarget targetFormat,
- ExtensionTracker* extensionTracker = nullptr)
- : m_shared(base->m_shared)
- , m_targetFormat(targetFormat)
- , m_extensionTracker(extensionTracker)
- {
- }
-
- /// Get the diagnostic sink
- DiagnosticSink* getSink() { return m_shared->sink; }
-
- TargetProgram* getTargetProgram() { return m_shared->targetProgram; }
-
- EntryPointIndices const& getEntryPointIndices() { return m_shared->entryPointIndices; }
-
- CodeGenTarget getTargetFormat() { return m_targetFormat; }
-
- ExtensionTracker* getExtensionTracker() { return m_extensionTracker; }
-
- TargetRequest* getTargetReq() { return getTargetProgram()->getTargetReq(); }
-
- CapabilitySet getTargetCaps() { return getTargetReq()->getTargetCaps(); }
-
- CodeGenTarget getFinalTargetFormat() { return getTargetReq()->getTarget(); }
-
- ComponentType* getProgram() { return getTargetProgram()->getProgram(); }
-
- Linkage* getLinkage() { return getProgram()->getLinkage(); }
-
- Session* getSession() { return getLinkage()->getSessionImpl(); }
-
- /// Get the source manager
- SourceManager* getSourceManager() { return getLinkage()->getSourceManager(); }
-
- ISlangFileSystemExt* getFileSystemExt() { return getLinkage()->getFileSystemExt(); }
-
- EndToEndCompileRequest* isEndToEndCompile() { return m_shared->endToEndReq; }
-
- EndToEndCompileRequest* isPassThroughEnabled();
-
- Count getEntryPointCount() { return getEntryPointIndices().getCount(); }
-
- EntryPoint* getEntryPoint(Index index) { return getProgram()->getEntryPoint(index); }
-
- Index getSingleEntryPointIndex()
- {
- SLANG_ASSERT(getEntryPointCount() == 1);
- return getEntryPointIndices()[0];
- }
-
- //
-
- IRDumpOptions getIRDumpOptions();
-
- bool shouldValidateIR();
- bool shouldDumpIR();
- bool shouldReportCheckpointIntermediates();
-
- bool shouldTrackLiveness();
-
- bool shouldDumpIntermediates();
- String getIntermediateDumpPrefix();
-
- bool getUseUnknownImageFormatAsDefault();
-
- bool isSpecializationDisabled();
-
- bool shouldSkipSPIRVValidation();
-
- SlangResult requireTranslationUnitSourceFiles();
-
- //
-
- SlangResult emitEntryPoints(ComPtr<IArtifact>& outArtifact);
-
- SlangResult emitPrecompiledDownstreamIR(ComPtr<IArtifact>& outArtifact);
-
- void maybeDumpIntermediate(IArtifact* artifact);
-
- // Used to cause instructions available in precompiled blobs to be
- // removed between IR linking and target source generation.
- bool removeAvailableInDownstreamIR = false;
-
- // Determines if program level compilation like getTargetCode() or getEntryPointCode()
- // should return a fully linked downstream program or just the glue SPIR-V/DXIL that
- // imports and uses the precompiled SPIR-V/DXIL from constituent modules.
- // This is a no-op if modules are not precompiled.
- bool shouldSkipDownstreamLinking();
-
- RequiredLoweringPassSet& getRequiredLoweringPassSet() { return m_requiredLoweringPassSet; }
-
-protected:
- CodeGenTarget m_targetFormat = CodeGenTarget::Unknown;
- Profile m_targetProfile;
- ExtensionTracker* m_extensionTracker = nullptr;
-
- // To improve the performance of our backend, we will try to avoid running
- // passes related to features not used in the user code.
- // To do so, we will scan the IR module once, and determine which passes are needed
- // based on the instructions used in the IR module.
- // This will allow us to skip running passes that are not needed, without having to
- // run all the passes only to find out that no work is needed.
- // This is especially important for the performance of the backend, as some passes
- // have an initialization cost (such as building reference graphs or DOM trees) that
- // can be expensive.
- RequiredLoweringPassSet m_requiredLoweringPassSet;
-
- /// Will output assembly as well as the artifact if appropriate for the artifact type for
- /// assembly output and conversion is possible
- void _dumpIntermediateMaybeWithAssembly(IArtifact* artifact);
-
- void _dumpIntermediate(IArtifact* artifact);
- void _dumpIntermediate(const ArtifactDesc& desc, void const* data, size_t size);
-
- /* Emits entry point source taking into account if a pass-through or not. Uses 'targetFormat' to
- determine the target (not targetReq) */
- SlangResult emitEntryPointsSource(ComPtr<IArtifact>& outArtifact);
-
- SlangResult emitEntryPointsSourceFromIR(ComPtr<IArtifact>& outArtifact);
-
- SlangResult emitWithDownstreamForEntryPoints(ComPtr<IArtifact>& outArtifact);
-
- /* Determines a suitable filename to identify the input for a given entry point being compiled.
- If the end-to-end compile is a pass-through case, will attempt to find the (unique) source file
- pathname for the translation unit containing the entry point at `entryPointIndex.
- If the compilation is not in a pass-through case, then always returns `"slang-generated"`.
- @param endToEndReq The end-to-end compile request which might be using pass-through compilation
- @param entryPointIndex The index of the entry point to compute a filename for.
- @return the appropriate source filename */
- String calcSourcePathForEntryPoints();
-
- TranslationUnitRequest* findPassThroughTranslationUnit(Int entryPointIndex);
-
-
- SlangResult _emitEntryPoints(ComPtr<IArtifact>& outArtifact);
-
-private:
- Shared* m_shared = nullptr;
-};
-
-/// A compile request that spans the front and back ends of the compiler
-///
-/// This is what the command-line `slangc` uses, as well as the legacy
-/// C API. It ties together the functionality of `Linkage`,
-/// `FrontEndCompileRequest`, and `BackEndCompileRequest`, plus a small
-/// number of additional features that primarily make sense for
-/// command-line usage.
-///
-class EndToEndCompileRequest : public RefObject, public slang::ICompileRequest
-{
-public:
- SLANG_CLASS_GUID(0xce6d2383, 0xee1b, 0x4fd7, {0xa0, 0xf, 0xb8, 0xb6, 0x33, 0x12, 0x95, 0xc8})
-
- // ISlangUnknown
- SLANG_NO_THROW SlangResult SLANG_MCALL queryInterface(SlangUUID const& uuid, void** outObject)
- SLANG_OVERRIDE;
- SLANG_REF_OBJECT_IUNKNOWN_ADD_REF
- SLANG_REF_OBJECT_IUNKNOWN_RELEASE
-
- // slang::ICompileRequest
- virtual SLANG_NO_THROW void SLANG_MCALL setFileSystem(ISlangFileSystem* fileSystem)
- SLANG_OVERRIDE;
- virtual SLANG_NO_THROW void SLANG_MCALL setCompileFlags(SlangCompileFlags flags) SLANG_OVERRIDE;
- virtual SLANG_NO_THROW SlangCompileFlags SLANG_MCALL getCompileFlags() SLANG_OVERRIDE;
- virtual SLANG_NO_THROW void SLANG_MCALL setDumpIntermediates(int enable) SLANG_OVERRIDE;
- virtual SLANG_NO_THROW void SLANG_MCALL setDumpIntermediatePrefix(const char* prefix)
- SLANG_OVERRIDE;
- virtual SLANG_NO_THROW void SLANG_MCALL setEnableEffectAnnotations(bool value) SLANG_OVERRIDE;
- virtual SLANG_NO_THROW void SLANG_MCALL setLineDirectiveMode(SlangLineDirectiveMode mode)
- SLANG_OVERRIDE;
- virtual SLANG_NO_THROW void SLANG_MCALL setCodeGenTarget(SlangCompileTarget target)
- SLANG_OVERRIDE;
- virtual SLANG_NO_THROW int SLANG_MCALL addCodeGenTarget(SlangCompileTarget target)
- SLANG_OVERRIDE;
- virtual SLANG_NO_THROW void SLANG_MCALL
- setTargetProfile(int targetIndex, SlangProfileID profile) SLANG_OVERRIDE;
- virtual SLANG_NO_THROW void SLANG_MCALL setTargetFlags(int targetIndex, SlangTargetFlags flags)
- SLANG_OVERRIDE;
- virtual SLANG_NO_THROW void SLANG_MCALL
- setTargetFloatingPointMode(int targetIndex, SlangFloatingPointMode mode) SLANG_OVERRIDE;
- virtual SLANG_NO_THROW void SLANG_MCALL
- setTargetMatrixLayoutMode(int targetIndex, SlangMatrixLayoutMode mode) SLANG_OVERRIDE;
- virtual SLANG_NO_THROW void SLANG_MCALL
- setTargetForceGLSLScalarBufferLayout(int targetIndex, bool value) SLANG_OVERRIDE;
- virtual SLANG_NO_THROW void SLANG_MCALL setTargetForceDXLayout(int targetIndex, bool value)
- SLANG_OVERRIDE;
- virtual SLANG_NO_THROW void SLANG_MCALL
- setTargetGenerateWholeProgram(int targetIndex, bool value) SLANG_OVERRIDE;
- virtual SLANG_NO_THROW void SLANG_MCALL setTargetEmbedDownstreamIR(int targetIndex, bool value)
- SLANG_OVERRIDE;
- virtual SLANG_NO_THROW void SLANG_MCALL setMatrixLayoutMode(SlangMatrixLayoutMode mode)
- SLANG_OVERRIDE;
- virtual SLANG_NO_THROW void SLANG_MCALL setDebugInfoLevel(SlangDebugInfoLevel level)
- SLANG_OVERRIDE;
- virtual SLANG_NO_THROW void SLANG_MCALL setOptimizationLevel(SlangOptimizationLevel level)
- SLANG_OVERRIDE;
- virtual SLANG_NO_THROW void SLANG_MCALL setOutputContainerFormat(SlangContainerFormat format)
- SLANG_OVERRIDE;
- virtual SLANG_NO_THROW void SLANG_MCALL setPassThrough(SlangPassThrough passThrough)
- SLANG_OVERRIDE;
- virtual SLANG_NO_THROW void SLANG_MCALL
- setDiagnosticCallback(SlangDiagnosticCallback callback, void const* userData) SLANG_OVERRIDE;
- virtual SLANG_NO_THROW void SLANG_MCALL
- setWriter(SlangWriterChannel channel, ISlangWriter* writer) SLANG_OVERRIDE;
- virtual SLANG_NO_THROW ISlangWriter* SLANG_MCALL getWriter(SlangWriterChannel channel)
- SLANG_OVERRIDE;
- virtual SLANG_NO_THROW void SLANG_MCALL addSearchPath(const char* searchDir) SLANG_OVERRIDE;
- virtual SLANG_NO_THROW void SLANG_MCALL
- addPreprocessorDefine(const char* key, const char* value) SLANG_OVERRIDE;
- virtual SLANG_NO_THROW SlangResult SLANG_MCALL
- processCommandLineArguments(char const* const* args, int argCount) SLANG_OVERRIDE;
- virtual SLANG_NO_THROW int SLANG_MCALL
- addTranslationUnit(SlangSourceLanguage language, char const* name) SLANG_OVERRIDE;
- virtual SLANG_NO_THROW void SLANG_MCALL setDefaultModuleName(const char* defaultModuleName)
- SLANG_OVERRIDE;
- virtual SLANG_NO_THROW void SLANG_MCALL addTranslationUnitPreprocessorDefine(
- int translationUnitIndex,
- const char* key,
- const char* value) SLANG_OVERRIDE;
- virtual SLANG_NO_THROW void SLANG_MCALL
- addTranslationUnitSourceFile(int translationUnitIndex, char const* path) SLANG_OVERRIDE;
- virtual SLANG_NO_THROW void SLANG_MCALL addTranslationUnitSourceString(
- int translationUnitIndex,
- char const* path,
- char const* source) SLANG_OVERRIDE;
- virtual SLANG_NO_THROW SlangResult SLANG_MCALL addLibraryReference(
- const char* basePath,
- const void* libData,
- size_t libDataSize) SLANG_OVERRIDE;
- virtual SLANG_NO_THROW void SLANG_MCALL addTranslationUnitSourceStringSpan(
- int translationUnitIndex,
- char const* path,
- char const* sourceBegin,
- char const* sourceEnd) SLANG_OVERRIDE;
- virtual SLANG_NO_THROW void SLANG_MCALL addTranslationUnitSourceBlob(
- int translationUnitIndex,
- char const* path,
- ISlangBlob* sourceBlob) SLANG_OVERRIDE;
- virtual SLANG_NO_THROW int SLANG_MCALL
- addEntryPoint(int translationUnitIndex, char const* name, SlangStage stage) SLANG_OVERRIDE;
- virtual SLANG_NO_THROW int SLANG_MCALL addEntryPointEx(
- int translationUnitIndex,
- char const* name,
- SlangStage stage,
- int genericArgCount,
- char const** genericArgs) SLANG_OVERRIDE;
- virtual SLANG_NO_THROW SlangResult SLANG_MCALL
- setGlobalGenericArgs(int genericArgCount, char const** genericArgs) SLANG_OVERRIDE;
- virtual SLANG_NO_THROW SlangResult SLANG_MCALL
- setTypeNameForGlobalExistentialTypeParam(int slotIndex, char const* typeName) SLANG_OVERRIDE;
- virtual SLANG_NO_THROW SlangResult SLANG_MCALL setTypeNameForEntryPointExistentialTypeParam(
- int entryPointIndex,
- int slotIndex,
- char const* typeName) SLANG_OVERRIDE;
- virtual SLANG_NO_THROW void SLANG_MCALL setAllowGLSLInput(bool value) SLANG_OVERRIDE;
- virtual SLANG_NO_THROW SlangResult SLANG_MCALL compile() SLANG_OVERRIDE;
- virtual SLANG_NO_THROW char const* SLANG_MCALL getDiagnosticOutput() SLANG_OVERRIDE;
- virtual SLANG_NO_THROW SlangResult SLANG_MCALL getDiagnosticOutputBlob(ISlangBlob** outBlob)
- SLANG_OVERRIDE;
- virtual SLANG_NO_THROW int SLANG_MCALL getDependencyFileCount() SLANG_OVERRIDE;
- virtual SLANG_NO_THROW char const* SLANG_MCALL getDependencyFilePath(int index) SLANG_OVERRIDE;
- virtual SLANG_NO_THROW int SLANG_MCALL getTranslationUnitCount() SLANG_OVERRIDE;
- virtual SLANG_NO_THROW char const* SLANG_MCALL getEntryPointSource(int entryPointIndex)
- SLANG_OVERRIDE;
- virtual SLANG_NO_THROW void const* SLANG_MCALL
- getEntryPointCode(int entryPointIndex, size_t* outSize) SLANG_OVERRIDE;
- virtual SLANG_NO_THROW SlangResult SLANG_MCALL getEntryPointCodeBlob(
- int entryPointIndex,
- int targetIndex,
- ISlangBlob** outBlob) SLANG_OVERRIDE;
- virtual SLANG_NO_THROW SlangResult SLANG_MCALL getEntryPointHostCallable(
- int entryPointIndex,
- int targetIndex,
- ISlangSharedLibrary** outSharedLibrary) SLANG_OVERRIDE;
- virtual SLANG_NO_THROW SlangResult SLANG_MCALL
- getTargetCodeBlob(int targetIndex, ISlangBlob** outBlob) SLANG_OVERRIDE;
- virtual SLANG_NO_THROW SlangResult SLANG_MCALL
- getTargetHostCallable(int targetIndex, ISlangSharedLibrary** outSharedLibrary) SLANG_OVERRIDE;
- virtual SLANG_NO_THROW void const* SLANG_MCALL getCompileRequestCode(size_t* outSize)
- SLANG_OVERRIDE;
- virtual SLANG_NO_THROW ISlangMutableFileSystem* SLANG_MCALL
- getCompileRequestResultAsFileSystem() SLANG_OVERRIDE;
- virtual SLANG_NO_THROW SlangResult SLANG_MCALL getContainerCode(ISlangBlob** outBlob)
- SLANG_OVERRIDE;
- virtual SLANG_NO_THROW SlangResult SLANG_MCALL
- loadRepro(ISlangFileSystem* fileSystem, const void* data, size_t size) SLANG_OVERRIDE;
- virtual SLANG_NO_THROW SlangResult SLANG_MCALL saveRepro(ISlangBlob** outBlob) SLANG_OVERRIDE;
- virtual SLANG_NO_THROW SlangResult SLANG_MCALL enableReproCapture() SLANG_OVERRIDE;
- virtual SLANG_NO_THROW SlangResult SLANG_MCALL getProgram(slang::IComponentType** outProgram)
- SLANG_OVERRIDE;
- virtual SLANG_NO_THROW SlangResult SLANG_MCALL
- getEntryPoint(SlangInt entryPointIndex, slang::IComponentType** outEntryPoint) SLANG_OVERRIDE;
- virtual SLANG_NO_THROW SlangResult SLANG_MCALL
- getModule(SlangInt translationUnitIndex, slang::IModule** outModule) SLANG_OVERRIDE;
- virtual SLANG_NO_THROW SlangResult SLANG_MCALL getSession(slang::ISession** outSession)
- SLANG_OVERRIDE;
- virtual SLANG_NO_THROW SlangReflection* SLANG_MCALL getReflection() SLANG_OVERRIDE;
- virtual SLANG_NO_THROW void SLANG_MCALL setCommandLineCompilerMode() SLANG_OVERRIDE;
- virtual SLANG_NO_THROW SlangResult SLANG_MCALL
- addTargetCapability(SlangInt targetIndex, SlangCapabilityID capability) SLANG_OVERRIDE;
- virtual SLANG_NO_THROW SlangResult SLANG_MCALL
- getProgramWithEntryPoints(slang::IComponentType** outProgram) SLANG_OVERRIDE;
- virtual SLANG_NO_THROW SlangResult SLANG_MCALL isParameterLocationUsed(
- SlangInt entryPointIndex,
- SlangInt targetIndex,
- SlangParameterCategory category,
- SlangUInt spaceIndex,
- SlangUInt registerIndex,
- bool& outUsed) SLANG_OVERRIDE;
- virtual SLANG_NO_THROW void SLANG_MCALL
- setTargetLineDirectiveMode(SlangInt targetIndex, SlangLineDirectiveMode mode) SLANG_OVERRIDE;
- virtual SLANG_NO_THROW void SLANG_MCALL
- overrideDiagnosticSeverity(SlangInt messageID, SlangSeverity overrideSeverity) SLANG_OVERRIDE;
- virtual SLANG_NO_THROW SlangDiagnosticFlags SLANG_MCALL getDiagnosticFlags() SLANG_OVERRIDE;
- virtual SLANG_NO_THROW void SLANG_MCALL setDiagnosticFlags(SlangDiagnosticFlags flags)
- SLANG_OVERRIDE;
- virtual SLANG_NO_THROW void SLANG_MCALL setDebugInfoFormat(SlangDebugInfoFormat format)
- SLANG_OVERRIDE;
- virtual SLANG_NO_THROW void SLANG_MCALL setReportDownstreamTime(bool value) SLANG_OVERRIDE;
- virtual SLANG_NO_THROW void SLANG_MCALL setReportPerfBenchmark(bool value) SLANG_OVERRIDE;
- virtual SLANG_NO_THROW void SLANG_MCALL setSkipSPIRVValidation(bool value) SLANG_OVERRIDE;
- virtual SLANG_NO_THROW void SLANG_MCALL
- setTargetUseMinimumSlangOptimization(int targetIndex, bool value) SLANG_OVERRIDE;
- virtual SLANG_NO_THROW void SLANG_MCALL setIgnoreCapabilityCheck(bool value) SLANG_OVERRIDE;
- virtual SLANG_NO_THROW SlangResult SLANG_MCALL
- getCompileTimeProfile(ISlangProfiler** compileTimeProfile, bool isClear) SLANG_OVERRIDE;
-
- void setTrackLiveness(bool v);
-
- EndToEndCompileRequest(Session* session);
-
- EndToEndCompileRequest(Linkage* linkage);
-
- ~EndToEndCompileRequest();
-
- // If enabled will emit IR
- bool m_emitIr = false;
-
- // What container format are we being asked to generate?
- // If it's set to a format, the container blob will be calculated during compile
- ContainerFormat m_containerFormat = ContainerFormat::None;
-
- /// Where the container is stored. This is calculated as part of compile if m_containerFormat is
- /// set to a supported format.
- ComPtr<IArtifact> m_containerArtifact;
- /// Holds the container as a file system
- ComPtr<ISlangMutableFileSystem> m_containerFileSystem;
-
- /// File system used by repro system if a file couldn't be found within the repro (or associated
- /// directory)
- ComPtr<ISlangFileSystem> m_reproFallbackFileSystem =
- ComPtr<ISlangFileSystem>(OSFileSystem::getExtSingleton());
-
- // Path to output container to
- String m_containerOutputPath;
-
- // Should we just pass the input to another compiler?
- PassThroughMode m_passThrough = PassThroughMode::None;
-
- /// If output should be source embedded, define the style of the embedding
- SourceEmbedUtil::Style m_sourceEmbedStyle = SourceEmbedUtil::Style::None;
- /// The language to be used for source embedding
- SourceLanguage m_sourceEmbedLanguage = SourceLanguage::C;
- /// Source embed variable name. Note may be used as a basis for names if multiple items written
- String m_sourceEmbedName;
-
- /// Source code for the specialization arguments to use for the global specialization parameters
- /// of the program.
- List<String> m_globalSpecializationArgStrings;
-
- // Are we being driven by the command-line `slangc`, and should act accordingly?
- bool m_isCommandLineCompile = false;
-
- String m_diagnosticOutput;
-
- /// A blob holding the diagnostic output
- ComPtr<ISlangBlob> m_diagnosticOutputBlob;
-
- /// Per-entry-point information not tracked by other compile requests
- class EntryPointInfo : public RefObject
- {
- public:
- /// Source code for the specialization arguments to use for the specialization parameters of
- /// the entry point.
- List<String> specializationArgStrings;
- };
- List<EntryPointInfo> m_entryPoints;
-
- /// Per-target information only needed for command-line compiles
- class TargetInfo : public RefObject
- {
- public:
- // Requested output paths for each entry point.
- // An empty string indices no output desired for
- // the given entry point.
- Dictionary<Int, String> entryPointOutputPaths;
- String wholeTargetOutputPath;
- CompilerOptionSet targetOptions;
- };
- Dictionary<TargetRequest*, RefPtr<TargetInfo>> m_targetInfos;
-
- CompilerOptionSet m_optionSetForDefaultTarget;
-
- CompilerOptionSet& getTargetOptionSet(TargetRequest* req);
-
- CompilerOptionSet& getTargetOptionSet(Index targetIndex);
-
- String m_dependencyOutputPath;
-
- /// Writes the modules in a container to the stream
- SlangResult writeContainerToStream(Stream* stream);
-
- /// If a container format has been specified produce a container (stored in m_containerBlob)
- SlangResult maybeCreateContainer();
- /// If a container has been constructed and the filename/path has contents will try to write
- /// the container contents to the file
- SlangResult maybeWriteContainer(const String& fileName);
-
- Linkage* getLinkage() { return m_linkage; }
-
- int addEntryPoint(
- int translationUnitIndex,
- String const& name,
- Profile profile,
- List<String> const& genericTypeNames);
-
- void setWriter(WriterChannel chan, ISlangWriter* writer);
- ISlangWriter* getWriter(WriterChannel chan) const
- {
- return m_writers->getWriter(SlangWriterChannel(chan));
- }
-
- /// The end to end request can be passed as nullptr, if not driven by one
- SlangResult executeActionsInner();
- SlangResult executeActions();
-
- Session* getSession() { return m_session; }
- DiagnosticSink* getSink() { return &m_sink; }
- NamePool* getNamePool() { return getLinkage()->getNamePool(); }
-
- FrontEndCompileRequest* getFrontEndReq() { return m_frontEndReq; }
-
- ComponentType* getUnspecializedGlobalComponentType()
- {
- return getFrontEndReq()->getGlobalComponentType();
- }
- ComponentType* getUnspecializedGlobalAndEntryPointsComponentType()
- {
- return getFrontEndReq()->getGlobalAndEntryPointsComponentType();
- }
-
- ComponentType* getSpecializedGlobalComponentType() { return m_specializedGlobalComponentType; }
- ComponentType* getSpecializedGlobalAndEntryPointsComponentType()
- {
- return m_specializedGlobalAndEntryPointsComponentType;
- }
-
- ComponentType* getSpecializedEntryPointComponentType(Index index)
- {
- return m_specializedEntryPoints[index];
- }
-
- void writeArtifactToStandardOutput(IArtifact* artifact, DiagnosticSink* sink);
-
- void generateOutput();
-
- CompilerOptionSet& getOptionSet() { return m_linkage->m_optionSet; }
-
-private:
- String _getWholeProgramPath(TargetRequest* targetReq);
- String _getEntryPointPath(TargetRequest* targetReq, Index entryPointIndex);
-
- /// Maybe write the artifact to the path (if set), or stdout (if there is no container or path)
- SlangResult _maybeWriteArtifact(const String& path, IArtifact* artifact);
- SlangResult _maybeWriteDebugArtifact(
- TargetProgram* targetProgram,
- const String& path,
- IArtifact* artifact);
- SlangResult _writeArtifact(const String& path, IArtifact* artifact);
-
- /// Adds any extra settings to complete a targetRequest
- void _completeTargetRequest(UInt targetIndex);
-
- ISlangUnknown* getInterface(const Guid& guid);
-
- void generateOutput(ComponentType* program);
- void generateOutput(TargetProgram* targetProgram);
-
- void init();
-
- Session* m_session = nullptr;
- RefPtr<Linkage> m_linkage;
- DiagnosticSink m_sink;
- RefPtr<FrontEndCompileRequest> m_frontEndReq;
- RefPtr<ComponentType> m_specializedGlobalComponentType;
- RefPtr<ComponentType> m_specializedGlobalAndEntryPointsComponentType;
- List<RefPtr<ComponentType>> m_specializedEntryPoints;
-
- // For output
-
- RefPtr<StdWriters> m_writers;
-};
-
-/* Returns SLANG_OK if pass through support is available */
-SlangResult checkExternalCompilerSupport(Session* session, PassThroughMode passThrough);
-/* Report an error appearing from external compiler to the diagnostic sink error to the diagnostic
-sink.
-@param compilerName The name of the compiler the error came for (or nullptr if not known)
-@param res Result associated with the error. The error code will be reported. (Can take HRESULT -
-and will expand to string if known)
-@param diagnostic The diagnostic string associated with the compile failure
-@param sink The diagnostic sink to report to */
-void reportExternalCompileError(
- const char* compilerName,
- SlangResult res,
- const UnownedStringSlice& diagnostic,
- DiagnosticSink* sink);
//
-// Information about BaseType that's useful for checking literals
-struct BaseTypeInfo
-{
- typedef uint8_t Flags;
- struct Flag
- {
- enum Enum : Flags
- {
- Signed = 0x1,
- FloatingPoint = 0x2,
- Integer = 0x4,
- };
- };
-
- SLANG_FORCE_INLINE static const BaseTypeInfo& getInfo(BaseType baseType)
- {
- return s_info[Index(baseType)];
- }
-
- static UnownedStringSlice asText(BaseType baseType);
-
- uint8_t sizeInBytes; ///< Size of type in bytes
- Flags flags;
- uint8_t baseType;
-
- static bool check();
-
-private:
- static const BaseTypeInfo s_info[Index(BaseType::CountOf)];
-};
-
-class CodeGenTransitionMap
-{
-public:
- struct Pair
- {
- typedef Pair ThisType;
- SLANG_FORCE_INLINE bool operator==(const ThisType& rhs) const
- {
- return source == rhs.source && target == rhs.target;
- }
- SLANG_FORCE_INLINE bool operator!=(const ThisType& rhs) const { return !(*this == rhs); }
-
- SLANG_FORCE_INLINE HashCode getHashCode() const
- {
- return combineHash(HashCode(source), HashCode(target));
- }
-
- CodeGenTarget source;
- CodeGenTarget target;
- };
-
- void removeTransition(CodeGenTarget source, CodeGenTarget target)
- {
- m_map.remove(Pair{source, target});
- }
- void addTransition(CodeGenTarget source, CodeGenTarget target, PassThroughMode compiler)
- {
- SLANG_ASSERT(source != target);
- m_map.set(Pair{source, target}, compiler);
- }
- bool hasTransition(CodeGenTarget source, CodeGenTarget target) const
- {
- return m_map.containsKey(Pair{source, target});
- }
- PassThroughMode getTransition(CodeGenTarget source, CodeGenTarget target) const
- {
- const Pair pair{source, target};
- auto value = m_map.tryGetValue(pair);
- return value ? *value : PassThroughMode::None;
- }
-
-protected:
- Dictionary<Pair, PassThroughMode> m_map;
-};
-
-class Session : public RefObject, public slang::IGlobalSession
-{
-public:
- SLANG_COM_INTERFACE(
- 0xd6b767eb,
- 0xd786,
- 0x4343,
- {0x2a, 0x8c, 0x6d, 0xa0, 0x3d, 0x5a, 0xb4, 0x4a})
-
- SLANG_NO_THROW SlangResult SLANG_MCALL queryInterface(SlangUUID const& uuid, void** outObject)
- SLANG_OVERRIDE;
- SLANG_REF_OBJECT_IUNKNOWN_ADD_REF
- SLANG_REF_OBJECT_IUNKNOWN_RELEASE
-
- // slang::IGlobalSession
- SLANG_NO_THROW SlangResult SLANG_MCALL
- createSession(slang::SessionDesc const& desc, slang::ISession** outSession) override;
- SLANG_NO_THROW SlangProfileID SLANG_MCALL findProfile(char const* name) override;
- SLANG_NO_THROW void SLANG_MCALL
- setDownstreamCompilerPath(SlangPassThrough passThrough, char const* path) override;
- SLANG_NO_THROW void SLANG_MCALL
- setDownstreamCompilerPrelude(SlangPassThrough inPassThrough, char const* prelude) override;
- SLANG_NO_THROW void SLANG_MCALL
- getDownstreamCompilerPrelude(SlangPassThrough inPassThrough, ISlangBlob** outPrelude) override;
- SLANG_NO_THROW const char* SLANG_MCALL getBuildTagString() override;
- SLANG_NO_THROW SlangResult SLANG_MCALL setDefaultDownstreamCompiler(
- SlangSourceLanguage sourceLanguage,
- SlangPassThrough defaultCompiler) override;
- SLANG_NO_THROW SlangPassThrough SLANG_MCALL
- getDefaultDownstreamCompiler(SlangSourceLanguage sourceLanguage) override;
-
- SLANG_NO_THROW void SLANG_MCALL
- setLanguagePrelude(SlangSourceLanguage inSourceLanguage, char const* prelude) override;
- SLANG_NO_THROW void SLANG_MCALL
- getLanguagePrelude(SlangSourceLanguage inSourceLanguage, ISlangBlob** outPrelude) override;
-
- SLANG_NO_THROW SlangResult SLANG_MCALL
- createCompileRequest(slang::ICompileRequest** outCompileRequest) override;
-
- SLANG_NO_THROW void SLANG_MCALL
- addBuiltins(char const* sourcePath, char const* sourceString) override;
- SLANG_NO_THROW void SLANG_MCALL
- setSharedLibraryLoader(ISlangSharedLibraryLoader* loader) override;
- SLANG_NO_THROW ISlangSharedLibraryLoader* SLANG_MCALL getSharedLibraryLoader() override;
- SLANG_NO_THROW SlangResult SLANG_MCALL
- checkCompileTargetSupport(SlangCompileTarget target) override;
- SLANG_NO_THROW SlangResult SLANG_MCALL
- checkPassThroughSupport(SlangPassThrough passThrough) override;
-
- void writeCoreModuleDoc(String config);
- SLANG_NO_THROW SlangResult SLANG_MCALL
- compileCoreModule(slang::CompileCoreModuleFlags flags) override;
- SLANG_NO_THROW SlangResult SLANG_MCALL
- loadCoreModule(const void* coreModule, size_t coreModuleSizeInBytes) override;
- SLANG_NO_THROW SlangResult SLANG_MCALL
- saveCoreModule(SlangArchiveType archiveType, ISlangBlob** outBlob) override;
-
- SLANG_NO_THROW SlangResult SLANG_MCALL compileBuiltinModule(
- slang::BuiltinModuleName moduleName,
- slang::CompileCoreModuleFlags flags) override;
- SLANG_NO_THROW SlangResult SLANG_MCALL loadBuiltinModule(
- slang::BuiltinModuleName moduleName,
- const void* coreModule,
- size_t coreModuleSizeInBytes) override;
- SLANG_NO_THROW SlangResult SLANG_MCALL saveBuiltinModule(
- slang::BuiltinModuleName moduleName,
- SlangArchiveType archiveType,
- ISlangBlob** outBlob) override;
-
- SLANG_NO_THROW SlangCapabilityID SLANG_MCALL findCapability(char const* name) override;
-
- SLANG_NO_THROW void SLANG_MCALL setDownstreamCompilerForTransition(
- SlangCompileTarget source,
- SlangCompileTarget target,
- SlangPassThrough compiler) override;
- SLANG_NO_THROW SlangPassThrough SLANG_MCALL getDownstreamCompilerForTransition(
- SlangCompileTarget source,
- SlangCompileTarget target) override;
- SLANG_NO_THROW void SLANG_MCALL
- getCompilerElapsedTime(double* outTotalTime, double* outDownstreamTime) override
- {
- *outDownstreamTime = m_downstreamCompileTime;
- *outTotalTime = m_totalCompileTime;
- }
-
- SLANG_NO_THROW SlangResult SLANG_MCALL setSPIRVCoreGrammar(char const* jsonPath) override;
-
- SLANG_NO_THROW SlangResult SLANG_MCALL parseCommandLineArguments(
- int argc,
- const char* const* argv,
- slang::SessionDesc* outSessionDesc,
- ISlangUnknown** outAllocation) override;
-
- SLANG_NO_THROW SlangResult SLANG_MCALL
- getSessionDescDigest(slang::SessionDesc* sessionDesc, ISlangBlob** outBlob) override;
-
- /// Get the downstream compiler for a transition
- IDownstreamCompiler* getDownstreamCompiler(CodeGenTarget source, CodeGenTarget target);
-
- // This needs to be atomic not because of contention between threads as `Session` is
- // *not* multithreaded, but can be used exclusively on one thread at a time.
- // The need for atomic is purely for visibility. If the session is used on a different
- // thread we need to be sure any changes to m_epochId are visible to this thread.
- std::atomic<Index> m_epochId = 1;
-
- Scope* baseLanguageScope = nullptr;
- Scope* coreLanguageScope = nullptr;
- Scope* hlslLanguageScope = nullptr;
- Scope* slangLanguageScope = nullptr;
- Scope* glslLanguageScope = nullptr;
- Name* glslModuleName = nullptr;
-
- ModuleDecl* baseModuleDecl = nullptr;
- List<RefPtr<Module>> coreModules;
-
- SourceManager builtinSourceManager;
-
- SourceManager* getBuiltinSourceManager() { return &builtinSourceManager; }
-
- // Name pool stuff for unique-ing identifiers
-
- NamePool namePool;
-
- NamePool* getNamePool() { return &namePool; }
- Name* getNameObj(String name) { return namePool.getName(name); }
- Name* tryGetNameObj(String name) { return namePool.tryGetName(name); }
- //
-
- /// This AST Builder should only be used for creating AST nodes that are global across requests
- /// not doing so could lead to memory being consumed but not used.
- ASTBuilder* getGlobalASTBuilder() { return globalAstBuilder; }
- void finalizeSharedASTBuilder();
-
- RefPtr<ASTBuilder> globalAstBuilder;
-
- // Generated code for core module, etc.
- String coreModulePath;
-
- ComPtr<ISlangBlob> coreLibraryCode;
- // ComPtr<ISlangBlob> slangLibraryCode;
- ComPtr<ISlangBlob> hlslLibraryCode;
- ComPtr<ISlangBlob> glslLibraryCode;
- ComPtr<ISlangBlob> autodiffLibraryCode;
-
- String getCoreModulePath();
-
- ComPtr<ISlangBlob> getCoreLibraryCode();
- ComPtr<ISlangBlob> getHLSLLibraryCode();
- ComPtr<ISlangBlob> getAutodiffLibraryCode();
- ComPtr<ISlangBlob> getGLSLLibraryCode();
-
- void getBuiltinModuleSource(StringBuilder& sb, slang::BuiltinModuleName moduleName);
-
- RefPtr<SharedASTBuilder> m_sharedASTBuilder;
-
- SPIRVCoreGrammarInfo& getSPIRVCoreGrammarInfo()
- {
- if (!spirvCoreGrammarInfo)
- setSPIRVCoreGrammar(nullptr);
- SLANG_ASSERT(spirvCoreGrammarInfo);
- return *spirvCoreGrammarInfo;
- }
- RefPtr<SPIRVCoreGrammarInfo> spirvCoreGrammarInfo;
-
- //
-
- void _setSharedLibraryLoader(ISlangSharedLibraryLoader* loader);
-
- /// Will try to load the library by specified name (using the set loader), if not one already
- /// available.
- IDownstreamCompiler* getOrLoadDownstreamCompiler(PassThroughMode type, DiagnosticSink* sink);
- /// Will unload the specified shared library if it's currently loaded
- void resetDownstreamCompiler(PassThroughMode type);
-
- /// Get the prelude associated with the language
- const String& getPreludeForLanguage(SourceLanguage language)
- {
- return m_languagePreludes[int(language)];
- }
-
- /// Get the built in linkage -> handy to get the core module from
- Linkage* getBuiltinLinkage() const { return m_builtinLinkage; }
-
- Module* getBuiltinModule(slang::BuiltinModuleName builtinModuleName);
-
- Name* getCompletionRequestTokenName() const { return m_completionTokenName; }
-
- void init();
-
- void addBuiltinSource(
- Scope* scope,
- String const& path,
- ISlangBlob* sourceBlob,
- Module*& outModule);
- ~Session();
-
- void addDownstreamCompileTime(double time) { m_downstreamCompileTime += time; }
- void addTotalCompileTime(double time) { m_totalCompileTime += time; }
-
- ComPtr<ISlangSharedLibraryLoader>
- m_sharedLibraryLoader; ///< The shared library loader (never null)
-
- int m_downstreamCompilerInitialized = 0;
-
- RefPtr<DownstreamCompilerSet>
- m_downstreamCompilerSet; ///< Information about all available downstream compilers.
- ComPtr<IDownstreamCompiler> m_downstreamCompilers[int(
- PassThroughMode::CountOf)]; ///< A downstream compiler for a pass through
- DownstreamCompilerLocatorFunc m_downstreamCompilerLocators[int(PassThroughMode::CountOf)];
- Name* m_completionTokenName = nullptr; ///< The name of a completion request token.
-
- /// For parsing command line options
- CommandOptions m_commandOptions;
-
- int m_typeDictionarySize = 0;
-
- RefPtr<RefObject> m_typeCheckingCache;
- TypeCheckingCache* getTypeCheckingCache();
- std::mutex m_typeCheckingCacheMutex;
-
-private:
- struct BuiltinModuleInfo
- {
- const char* name;
- Scope* languageScope;
- };
-
- BuiltinModuleInfo getBuiltinModuleInfo(slang::BuiltinModuleName name);
-
- void _initCodeGenTransitionMap();
-
- SlangResult _readBuiltinModule(
- ISlangFileSystem* fileSystem,
- Scope* scope,
- String moduleName,
- Module*& outModule);
-
- SlangResult _loadRequest(EndToEndCompileRequest* request, const void* data, size_t size);
-
- /// Linkage used for all built-in (core module) code.
- RefPtr<Linkage> m_builtinLinkage;
-
- String
- m_downstreamCompilerPaths[int(PassThroughMode::CountOf)]; ///< Paths for each pass through
- String m_languagePreludes[int(SourceLanguage::CountOf)]; ///< Prelude for each source language
- PassThroughMode m_defaultDownstreamCompilers[int(SourceLanguage::CountOf)];
-
- // Describes a conversion from one code gen target (source) to another (target)
- CodeGenTransitionMap m_codeGenTransitionMap;
-
- double m_downstreamCompileTime = 0.0;
- double m_totalCompileTime = 0.0;
-};
-
-const char* getBuiltinModuleNameStr(slang::BuiltinModuleName name);
-
void checkTranslationUnit(
TranslationUnitRequest* translationUnit,
LoadedModuleDictionary& loadedModules);
@@ -3897,109 +206,6 @@ SlangResult passthroughDownstreamDiagnostics(
IDownstreamCompiler* compiler,
IArtifact* artifact);
-//
-// The following functions are utilties to convert between
-// matching "external" (public API) and "internal" (implementation)
-// types. They are favored over explicit casts because they
-// help avoid making incorrect conversions (e.g., when using
-// `reinterpret_cast` or C-style casts), and because they
-// abstract over the conversion required for each pair of types.
-//
-
-SLANG_FORCE_INLINE slang::IGlobalSession* asExternal(Session* session)
-{
- return static_cast<slang::IGlobalSession*>(session);
-}
-
-SLANG_FORCE_INLINE ComPtr<Session> asInternal(slang::IGlobalSession* session)
-{
- Slang::Session* internalSession = nullptr;
- session->queryInterface(SLANG_IID_PPV_ARGS(&internalSession));
- return ComPtr<Session>(INIT_ATTACH, static_cast<Session*>(internalSession));
-}
-
-SLANG_FORCE_INLINE slang::ISession* asExternal(Linkage* linkage)
-{
- return static_cast<slang::ISession*>(linkage);
-}
-
-SLANG_FORCE_INLINE Module* asInternal(slang::IModule* module)
-{
- return static_cast<Module*>(module);
-}
-
-SLANG_FORCE_INLINE slang::IModule* asExternal(Module* module)
-{
- return static_cast<slang::IModule*>(module);
-}
-
-ComponentType* asInternal(slang::IComponentType* inComponentType);
-
-SLANG_FORCE_INLINE slang::IComponentType* asExternal(ComponentType* componentType)
-{
- return static_cast<slang::IComponentType*>(componentType);
-}
-
-SLANG_FORCE_INLINE slang::ProgramLayout* asExternal(ProgramLayout* programLayout)
-{
- return (slang::ProgramLayout*)programLayout;
-}
-
-SLANG_FORCE_INLINE Type* asInternal(slang::TypeReflection* type)
-{
- return reinterpret_cast<Type*>(type);
-}
-
-SLANG_FORCE_INLINE slang::TypeReflection* asExternal(Type* type)
-{
- return reinterpret_cast<slang::TypeReflection*>(type);
-}
-
-SLANG_FORCE_INLINE DeclRef<Decl> asInternal(slang::GenericReflection* generic)
-{
- return DeclRef<Decl>(reinterpret_cast<DeclRefBase*>(generic));
-}
-
-SLANG_FORCE_INLINE slang::GenericReflection* asExternal(DeclRef<Decl> generic)
-{
- return reinterpret_cast<slang::GenericReflection*>(generic.declRefBase);
-}
-
-SLANG_FORCE_INLINE TypeLayout* asInternal(slang::TypeLayoutReflection* type)
-{
- return reinterpret_cast<TypeLayout*>(type);
-}
-
-SLANG_FORCE_INLINE slang::TypeLayoutReflection* asExternal(TypeLayout* type)
-{
- return reinterpret_cast<slang::TypeLayoutReflection*>(type);
-}
-
-SLANG_FORCE_INLINE SlangCompileRequest* asExternal(EndToEndCompileRequest* request)
-{
- return static_cast<SlangCompileRequest*>(request);
-}
-
-SLANG_FORCE_INLINE EndToEndCompileRequest* asInternal(SlangCompileRequest* request)
-{
- // Converts to the internal type -- does a runtime type check through queryInterfae
- SLANG_ASSERT(request);
- EndToEndCompileRequest* endToEndRequest = nullptr;
- // NOTE! We aren't using to access an interface, so *doesn't* return with a refcount
- request->queryInterface(SLANG_IID_PPV_ARGS(&endToEndRequest));
- SLANG_ASSERT(endToEndRequest);
- return endToEndRequest;
-}
-
-SLANG_FORCE_INLINE SlangCompileTarget asExternal(CodeGenTarget target)
-{
- return (SlangCompileTarget)target;
-}
-
-SLANG_FORCE_INLINE SlangSourceLanguage asExternal(SourceLanguage sourceLanguage)
-{
- return (SlangSourceLanguage)sourceLanguage;
-}
// helpers for error/warning reporting
enum class DiagnosticCategory
diff --git a/source/slang/slang-container-pool.cpp b/source/slang/slang-container-pool.cpp
deleted file mode 100644
index e69de29bb..000000000
--- a/source/slang/slang-container-pool.cpp
+++ /dev/null
diff --git a/source/slang/slang-emit-dependency-file.cpp b/source/slang/slang-emit-dependency-file.cpp
new file mode 100644
index 000000000..a482b08de
--- /dev/null
+++ b/source/slang/slang-emit-dependency-file.cpp
@@ -0,0 +1,125 @@
+// slang-emit-dependency-file.cpp
+#include "slang-emit-dependency-file.h"
+
+#include "slang-compiler.h"
+
+namespace Slang
+{
+
+static void _writeString(Stream& stream, const char* string)
+{
+ stream.write(string, strlen(string));
+}
+
+static void _escapeDependencyString(const char* string, StringBuilder& outBuilder)
+{
+ // make has unusual escaping rules, but we only care about characters that are acceptable in a
+ // path
+ for (const char* p = string; *p; ++p)
+ {
+ char c = *p;
+ switch (c)
+ {
+ case ' ':
+ case ':':
+ case '#':
+ case '[':
+ case ']':
+ case '\\':
+ outBuilder.appendChar('\\');
+ break;
+
+ case '$':
+ outBuilder.appendChar('$');
+ break;
+ }
+
+ outBuilder.appendChar(c);
+ }
+}
+
+// Writes a line to the file stream, formatted like this:
+// <output-file>: <dependency-file> <dependency-file...>
+static void _writeDependencyStatement(
+ Stream& stream,
+ EndToEndCompileRequest* compileRequest,
+ const String& outputPath)
+{
+ if (outputPath.getLength() == 0)
+ return;
+
+ StringBuilder builder;
+ _escapeDependencyString(outputPath.begin(), builder);
+ _writeString(stream, builder.begin());
+ _writeString(stream, ": ");
+
+ int dependencyCount = compileRequest->getDependencyFileCount();
+ for (int dependencyIndex = 0; dependencyIndex < dependencyCount; ++dependencyIndex)
+ {
+ builder.clear();
+ _escapeDependencyString(compileRequest->getDependencyFilePath(dependencyIndex), builder);
+ _writeString(stream, builder.begin());
+ _writeString(stream, (dependencyIndex + 1 < dependencyCount) ? " " : "\n");
+ }
+}
+
+// Writes a file with dependency info, with one line in the output file per compile product.
+SlangResult writeDependencyFile(EndToEndCompileRequest* compileRequest)
+{
+ if (compileRequest->m_dependencyOutputPath.getLength() == 0)
+ return SLANG_OK;
+
+ FileStream stream;
+ SLANG_RETURN_ON_FAIL(stream.init(
+ compileRequest->m_dependencyOutputPath,
+ FileMode::Create,
+ FileAccess::Write,
+ FileShare::ReadWrite));
+
+ auto linkage = compileRequest->getLinkage();
+ auto program = compileRequest->getSpecializedGlobalAndEntryPointsComponentType();
+
+ // Iterate over all the targets and their outputs
+ for (const auto& targetReq : linkage->targets)
+ {
+ if (compileRequest->getTargetOptionSet(targetReq).getBoolOption(
+ CompilerOptionName::GenerateWholeProgram))
+ {
+ RefPtr<EndToEndCompileRequest::TargetInfo> targetInfo;
+ if (compileRequest->m_targetInfos.tryGetValue(targetReq, targetInfo))
+ {
+ _writeDependencyStatement(
+ stream,
+ compileRequest,
+ targetInfo->wholeTargetOutputPath);
+ }
+ }
+ else
+ {
+ Index entryPointCount = program->getEntryPointCount();
+ for (Index entryPointIndex = 0; entryPointIndex < entryPointCount; ++entryPointIndex)
+ {
+ RefPtr<EndToEndCompileRequest::TargetInfo> targetInfo;
+ if (compileRequest->m_targetInfos.tryGetValue(targetReq, targetInfo))
+ {
+ String outputPath;
+ if (targetInfo->entryPointOutputPaths.tryGetValue(entryPointIndex, outputPath))
+ {
+ _writeDependencyStatement(stream, compileRequest, outputPath);
+ }
+ }
+ }
+ }
+ }
+
+ // When the output is a binary module, linkage->targets can be empty. So
+ // we need to do their dependencies separately.
+ if (compileRequest->m_containerFormat == ContainerFormat::SlangModule)
+ {
+ _writeDependencyStatement(stream, compileRequest, compileRequest->m_containerOutputPath);
+ }
+
+ return SLANG_OK;
+}
+
+} // namespace Slang
diff --git a/source/slang/slang-emit-dependency-file.h b/source/slang/slang-emit-dependency-file.h
new file mode 100644
index 000000000..27e584508
--- /dev/null
+++ b/source/slang/slang-emit-dependency-file.h
@@ -0,0 +1,20 @@
+// slang-emit-dependency-file.h
+#pragma once
+
+//
+// This file defines the interface for emitting a
+// dependency file (in the same format used by `make`,
+// `gcc`, and various other tools) based on a compile
+// request using the `slangc` tool.
+//
+
+#include <slang.h>
+
+namespace Slang
+{
+class EndToEndCompileRequest;
+
+SlangResult writeDependencyFile(EndToEndCompileRequest* compileRequest);
+
+
+} // namespace Slang
diff --git a/source/slang/slang-end-to-end-request.cpp b/source/slang/slang-end-to-end-request.cpp
new file mode 100644
index 000000000..d0f0a6f53
--- /dev/null
+++ b/source/slang/slang-end-to-end-request.cpp
@@ -0,0 +1,1999 @@
+// slang-end-to-end-request.cpp
+#include "slang-end-to-end-request.h"
+
+#include "compiler-core/slang-pretty-writer.h"
+#include "core/slang-memory-file-system.h"
+#include "core/slang-performance-profiler.h"
+#include "slang-check-impl.h"
+#include "slang-compiler.h"
+#include "slang-emit-dependency-file.h"
+#include "slang-module-library.h"
+#include "slang-options.h"
+#include "slang-reflection-json.h"
+#include "slang-repro.h"
+#include "slang-serialize-container.h"
+
+// TODO: The "artifact" system is a scourge.
+#include "compiler-core/slang-artifact-associated-impl.h"
+#include "compiler-core/slang-artifact-container-util.h"
+#include "compiler-core/slang-artifact-desc-util.h"
+#include "compiler-core/slang-artifact-impl.h"
+#include "compiler-core/slang-artifact-util.h"
+#include "slang-artifact-output-util.h"
+
+namespace Slang
+{
+
+EndToEndCompileRequest::EndToEndCompileRequest(Session* session)
+ : m_session(session), m_sink(nullptr, Lexer::sourceLocationLexer)
+{
+ RefPtr<ASTBuilder> astBuilder(
+ new ASTBuilder(session->m_sharedASTBuilder, "EndToEnd::Linkage::astBuilder"));
+ m_linkage = new Linkage(session, astBuilder, session->getBuiltinLinkage());
+ init();
+}
+
+EndToEndCompileRequest::EndToEndCompileRequest(Linkage* linkage)
+ : m_session(linkage->getSessionImpl())
+ , m_linkage(linkage)
+ , m_sink(nullptr, Lexer::sourceLocationLexer)
+{
+ init();
+}
+
+void EndToEndCompileRequest::init()
+{
+ m_sink.setSourceManager(m_linkage->getSourceManager());
+
+ m_writers = new StdWriters;
+
+ // Set all the default writers
+ for (int i = 0; i < int(WriterChannel::CountOf); ++i)
+ {
+ setWriter(WriterChannel(i), nullptr);
+ }
+
+ m_frontEndReq = new FrontEndCompileRequest(getLinkage(), m_writers, getSink());
+}
+
+EndToEndCompileRequest::~EndToEndCompileRequest()
+{
+ // Flush any writers associated with the request
+ m_writers->flushWriters();
+
+ m_linkage.setNull();
+ m_frontEndReq.setNull();
+}
+
+SLANG_NO_THROW SlangResult SLANG_MCALL
+EndToEndCompileRequest::queryInterface(SlangUUID const& uuid, void** outObject)
+{
+ if (uuid == EndToEndCompileRequest::getTypeGuid())
+ {
+ // Special case to cast directly into internal type
+ // NOTE! No addref(!)
+ *outObject = this;
+ return SLANG_OK;
+ }
+
+ if (uuid == ISlangUnknown::getTypeGuid() && uuid == ICompileRequest::getTypeGuid())
+ {
+ addReference();
+ *outObject = static_cast<slang::ICompileRequest*>(this);
+ return SLANG_OK;
+ }
+
+ return SLANG_E_NO_INTERFACE;
+}
+
+// Try to infer a single common source language for a request
+static SourceLanguage inferSourceLanguage(FrontEndCompileRequest* request)
+{
+ SourceLanguage language = SourceLanguage::Unknown;
+ for (auto& translationUnit : request->translationUnits)
+ {
+ // Allow any other language to overide Slang as a choice
+ if (language == SourceLanguage::Unknown || language == SourceLanguage::Slang)
+ {
+ language = translationUnit->sourceLanguage;
+ }
+ else if (language == translationUnit->sourceLanguage)
+ {
+ // same language as we currently have, so keep going
+ }
+ else
+ {
+ // we found a mismatch, so inference fails
+ return SourceLanguage::Unknown;
+ }
+ }
+ return language;
+}
+
+SlangResult EndToEndCompileRequest::executeActionsInner()
+{
+ SLANG_PROFILE_SECTION(endToEndActions);
+ // If no code-generation target was specified, then try to infer one from the source language,
+ // just to make sure we can do something reasonable when invoked from the command line.
+ //
+ // TODO: This logic should be moved into `options.cpp` or somewhere else
+ // specific to the command-line tool.
+ //
+ if (getLinkage()->targets.getCount() == 0)
+ {
+ auto language = inferSourceLanguage(getFrontEndReq());
+ switch (language)
+ {
+ case SourceLanguage::HLSL:
+ getLinkage()->addTarget(CodeGenTarget::DXBytecode);
+ break;
+
+ case SourceLanguage::GLSL:
+ getLinkage()->addTarget(CodeGenTarget::SPIRV);
+ break;
+
+ default:
+ break;
+ }
+ }
+
+ // Update compiler settings in target requests.
+ for (auto target : getLinkage()->targets)
+ target->getOptionSet().inheritFrom(getOptionSet());
+ m_frontEndReq->optionSet = getOptionSet();
+
+ // We only do parsing and semantic checking if we *aren't* doing
+ // a pass-through compilation.
+ //
+ if (m_passThrough == PassThroughMode::None)
+ {
+ SLANG_RETURN_ON_FAIL(getFrontEndReq()->executeActionsInner());
+ }
+
+ if (getOptionSet().getBoolOption(CompilerOptionName::PreprocessorOutput))
+ {
+ return SLANG_OK;
+ }
+
+ // If command line specifies to skip codegen, we exit here.
+ // Note: this is a debugging option.
+ //
+ if (getOptionSet().getBoolOption(CompilerOptionName::SkipCodeGen))
+ {
+ // We will use the program (and matching layout information)
+ // that was computed in the front-end for all subsequent
+ // reflection queries, etc.
+ //
+ m_specializedGlobalComponentType = getUnspecializedGlobalComponentType();
+ m_specializedGlobalAndEntryPointsComponentType =
+ getUnspecializedGlobalAndEntryPointsComponentType();
+ m_specializedEntryPoints = getFrontEndReq()->getUnspecializedEntryPoints();
+
+ SLANG_RETURN_ON_FAIL(maybeCreateContainer());
+
+ SLANG_RETURN_ON_FAIL(maybeWriteContainer(m_containerOutputPath));
+
+ return SLANG_OK;
+ }
+
+ // If requested, attempt to compile the translation unit all the way down to the target
+ // language(s) and stash the result blobs in IR.
+ for (auto target : getLinkage()->targets)
+ {
+ SlangCompileTarget targetEnum = SlangCompileTarget(target->getTarget());
+ if (target->getOptionSet().getBoolOption(CompilerOptionName::EmbedDownstreamIR))
+ {
+ auto frontEndReq = getFrontEndReq();
+
+ for (auto translationUnit : frontEndReq->translationUnits)
+ {
+ SLANG_RETURN_ON_FAIL(
+ translationUnit->getModule()->precompileForTarget(targetEnum, nullptr));
+
+ if (frontEndReq->optionSet.shouldDumpIR())
+ {
+ DiagnosticSinkWriter writer(frontEndReq->getSink());
+
+ dumpIR(
+ translationUnit->getModule()->getIRModule(),
+ frontEndReq->m_irDumpOptions,
+ "PRECOMPILE_FOR_TARGET_COMPLETE_ALL",
+ frontEndReq->getSourceManager(),
+ &writer);
+
+ dumpIR(
+ translationUnit->getModule()->getIRModule()->getModuleInst(),
+ frontEndReq->m_irDumpOptions,
+ frontEndReq->getSourceManager(),
+ &writer);
+ }
+ }
+ }
+ }
+
+ // If codegen is enabled, we need to move along to
+ // apply any generic specialization that the user asked for.
+ //
+ if (m_passThrough == PassThroughMode::None)
+ {
+ m_specializedGlobalComponentType = createSpecializedGlobalComponentType(this);
+ if (getSink()->getErrorCount() != 0)
+ return SLANG_FAIL;
+
+ m_specializedGlobalAndEntryPointsComponentType =
+ createSpecializedGlobalAndEntryPointsComponentType(this, m_specializedEntryPoints);
+ if (getSink()->getErrorCount() != 0)
+ return SLANG_FAIL;
+
+ // For each code generation target, we will generate specialized
+ // parameter binding information (taking global generic
+ // arguments into account at this time).
+ //
+ for (auto targetReq : getLinkage()->targets)
+ {
+ auto targetProgram =
+ m_specializedGlobalAndEntryPointsComponentType->getTargetProgram(targetReq);
+ targetProgram->getOrCreateLayout(getSink());
+ }
+ if (getSink()->getErrorCount() != 0)
+ return SLANG_FAIL;
+ }
+ else
+ {
+ // We need to create dummy `EntryPoint` objects
+ // to make sure that the logic in `generateOutput`
+ // sees something worth processing.
+ //
+ List<RefPtr<ComponentType>> dummyEntryPoints;
+ for (auto entryPointReq : getFrontEndReq()->getEntryPointReqs())
+ {
+ RefPtr<EntryPoint> dummyEntryPoint = EntryPoint::createDummyForPassThrough(
+ getLinkage(),
+ entryPointReq->getName(),
+ entryPointReq->getProfile());
+
+ dummyEntryPoints.add(dummyEntryPoint);
+ }
+
+ RefPtr<ComponentType> composedProgram =
+ CompositeComponentType::create(getLinkage(), dummyEntryPoints);
+
+ m_specializedGlobalComponentType = getUnspecializedGlobalComponentType();
+ m_specializedGlobalAndEntryPointsComponentType = composedProgram;
+ m_specializedEntryPoints = getFrontEndReq()->getUnspecializedEntryPoints();
+ }
+
+ // Generate output code, in whatever format was requested
+ generateOutput();
+ if (getSink()->getErrorCount() != 0)
+ return SLANG_FAIL;
+
+ return SLANG_OK;
+}
+
+// Act as expected of the API-based compiler
+SlangResult EndToEndCompileRequest::executeActions()
+{
+ SlangResult res = executeActionsInner();
+
+ m_diagnosticOutput = getSink()->outputBuffer.produceString();
+ return res;
+}
+
+
+static ISlangWriter* _getDefaultWriter(WriterChannel chan)
+{
+ static FileWriter stdOut(stdout, WriterFlag::IsStatic | WriterFlag::IsUnowned);
+ static FileWriter stdError(stderr, WriterFlag::IsStatic | WriterFlag::IsUnowned);
+ static NullWriter nullWriter(WriterFlag::IsStatic | WriterFlag::IsConsole);
+
+ switch (chan)
+ {
+ case WriterChannel::StdError:
+ return &stdError;
+ case WriterChannel::StdOutput:
+ return &stdOut;
+ case WriterChannel::Diagnostic:
+ return &nullWriter;
+ default:
+ {
+ SLANG_ASSERT(!"Unknown type");
+ return &stdError;
+ }
+ }
+}
+
+void EndToEndCompileRequest::setWriter(WriterChannel chan, ISlangWriter* writer)
+{
+ // If the user passed in null, we will use the default writer on that channel
+ m_writers->setWriter(SlangWriterChannel(chan), writer ? writer : _getDefaultWriter(chan));
+
+ // For diagnostic output, if the user passes in nullptr, we set on m_sink.writer as that enables
+ // buffering on DiagnosticSink
+ if (chan == WriterChannel::Diagnostic)
+ {
+ m_sink.writer = writer;
+ }
+}
+
+void EndToEndCompileRequest::writeArtifactToStandardOutput(
+ IArtifact* artifact,
+ DiagnosticSink* sink)
+{
+ // If it's host callable it's not available to write to output
+ if (isDerivedFrom(artifact->getDesc().kind, ArtifactKind::HostCallable))
+ {
+ return;
+ }
+
+ auto session = getSession();
+ ArtifactOutputUtil::maybeConvertAndWrite(
+ session,
+ artifact,
+ sink,
+ toSlice("stdout"),
+ getWriter(WriterChannel::StdOutput));
+}
+
+String EndToEndCompileRequest::_getWholeProgramPath(TargetRequest* targetReq)
+{
+ RefPtr<EndToEndCompileRequest::TargetInfo> targetInfo;
+ if (m_targetInfos.tryGetValue(targetReq, targetInfo))
+ {
+ return targetInfo->wholeTargetOutputPath;
+ }
+ return String();
+}
+
+String EndToEndCompileRequest::_getEntryPointPath(TargetRequest* targetReq, Index entryPointIndex)
+{
+ // It is possible that we are dynamically discovering entry
+ // points (using `[shader(...)]` attributes), so that there
+ // might be entry points added to the program that did not
+ // get paths specified via command-line options.
+ //
+ RefPtr<EndToEndCompileRequest::TargetInfo> targetInfo;
+ if (m_targetInfos.tryGetValue(targetReq, targetInfo))
+ {
+ String outputPath;
+ if (targetInfo->entryPointOutputPaths.tryGetValue(entryPointIndex, outputPath))
+ {
+ return outputPath;
+ }
+ }
+
+ return String();
+}
+
+SlangResult EndToEndCompileRequest::_writeArtifact(const String& path, IArtifact* artifact)
+{
+ if (path.getLength() > 0)
+ {
+ SLANG_RETURN_ON_FAIL(ArtifactOutputUtil::writeToFile(artifact, getSink(), path));
+ }
+ else if (m_containerFormat == ContainerFormat::None)
+ {
+ // If we aren't writing to a container and we didn't write to a file, we can output to
+ // standard output
+ writeArtifactToStandardOutput(artifact, getSink());
+ }
+ return SLANG_OK;
+}
+
+SlangResult EndToEndCompileRequest::_maybeWriteArtifact(const String& path, IArtifact* artifact)
+{
+ // We don't have to do anything if there is no artifact
+ if (!artifact)
+ {
+ return SLANG_OK;
+ }
+
+ // If embedding is enabled...
+ if (m_sourceEmbedStyle != SourceEmbedUtil::Style::None)
+ {
+ SourceEmbedUtil::Options options;
+
+ options.style = m_sourceEmbedStyle;
+ options.variableName = m_sourceEmbedName;
+ options.language = (SlangSourceLanguage)m_sourceEmbedLanguage;
+
+ ComPtr<IArtifact> embeddedArtifact;
+ SLANG_RETURN_ON_FAIL(SourceEmbedUtil::createEmbedded(artifact, options, embeddedArtifact));
+
+ if (!embeddedArtifact)
+ {
+ return SLANG_FAIL;
+ }
+ SLANG_RETURN_ON_FAIL(
+ _writeArtifact(SourceEmbedUtil::getPath(path, options), embeddedArtifact));
+ return SLANG_OK;
+ }
+ else
+ {
+ SLANG_RETURN_ON_FAIL(_writeArtifact(path, artifact));
+ }
+
+ return SLANG_OK;
+}
+
+// These helper functions are used by the -separate-debug-info command line
+// arg to extract the associated artifact containing the debug SPIRV data
+// and save it to a file with a .dbg.spv extension.
+static String _getDebugSpvPath(const String& basePath)
+{
+ // Find the last occurrence of ".spv" at the end of the string.
+ static const char ext[] = ".spv";
+ static const char dbgExt[] = ".dbg.spv";
+ Index extLen = 4;
+ if (basePath.getLength() >= extLen && basePath.endsWith(ext))
+ {
+ // Replace the ".spv" extension with ".dbg.spv"
+ String prefix = String(basePath.subString(0, basePath.getLength() - extLen));
+ return prefix + dbgExt;
+ }
+ // If it doesn't end with .spv, just append .dbg.spv
+ return basePath + dbgExt;
+}
+
+SlangResult EndToEndCompileRequest::_maybeWriteDebugArtifact(
+ TargetProgram* targetProgram,
+ const String& path,
+ IArtifact* artifact)
+{
+ if (targetProgram->getOptionSet().getBoolOption(CompilerOptionName::EmitSeparateDebug))
+ {
+ const auto dbgArtifact = getSeparateDbgArtifact(artifact);
+ // Check if a debug artifact was actually created (only for SPIR-V targets)
+ if (dbgArtifact)
+ {
+ // The artifact's name may have been set to the debug build id hash, use
+ // it as the filename if it exists.
+ String dbgPath = dbgArtifact->getName();
+ if (dbgPath.getLength() == 0)
+ dbgPath = _getDebugSpvPath(path);
+ else
+ dbgPath.append(".dbg.spv");
+ return _maybeWriteArtifact(dbgPath, dbgArtifact);
+ }
+ // If no debug artifact exists (e.g., for non-SPIR-V targets), just silently succeed
+ // The warning about unsupported targets is already issued during option parsing
+ }
+
+ return SLANG_OK;
+}
+
+void EndToEndCompileRequest::generateOutput(TargetProgram* targetProgram)
+{
+ auto program = targetProgram->getProgram();
+
+ // Generate target code any entry points that
+ // have been requested for compilation.
+ auto entryPointCount = program->getEntryPointCount();
+ if (targetProgram->getOptionSet().getBoolOption(CompilerOptionName::GenerateWholeProgram))
+ {
+ targetProgram->_createWholeProgramResult(getSink(), this);
+ }
+ else
+ {
+ for (Index ii = 0; ii < entryPointCount; ++ii)
+ {
+ targetProgram->_createEntryPointResult(ii, getSink(), this);
+ }
+ }
+}
+
+bool _shouldWriteSourceLocs(Linkage* linkage)
+{
+ // If debug information or source manager are not avaiable we can't/shouldn't write out locs
+ if (linkage->m_optionSet.getEnumOption<DebugInfoLevel>(CompilerOptionName::DebugInformation) ==
+ DebugInfoLevel::None ||
+ linkage->getSourceManager() == nullptr)
+ {
+ return false;
+ }
+
+ // Otherwise we do want to write out the locs
+ return true;
+}
+
+SlangResult EndToEndCompileRequest::writeContainerToStream(Stream* stream)
+{
+ auto linkage = getLinkage();
+
+ // Set up options
+ SerialContainerUtil::WriteOptions options;
+
+ // If debug information is enabled, enable writing out source locs
+ if (_shouldWriteSourceLocs(linkage))
+ {
+ options.sourceManagerToUseWhenSerializingSourceLocs = linkage->getSourceManager();
+ }
+
+ SLANG_RETURN_ON_FAIL(SerialContainerUtil::write(this, options, stream));
+
+ return SLANG_OK;
+}
+
+static IBoxValue<SourceMap>* _getObfuscatedSourceMap(TranslationUnitRequest* translationUnit)
+{
+ if (auto module = translationUnit->getModule())
+ {
+ if (auto irModule = module->getIRModule())
+ {
+ return irModule->getObfuscatedSourceMap();
+ }
+ }
+ return nullptr;
+}
+
+SlangResult EndToEndCompileRequest::maybeCreateContainer()
+{
+ m_containerArtifact.setNull();
+
+ List<ComPtr<IArtifact>> artifacts;
+
+ auto linkage = getLinkage();
+
+ auto program = getSpecializedGlobalAndEntryPointsComponentType();
+
+ for (auto targetReq : linkage->targets)
+ {
+ auto targetProgram = program->getTargetProgram(targetReq);
+
+ if (targetProgram->getOptionSet().getBoolOption(CompilerOptionName::GenerateWholeProgram))
+ {
+ if (auto artifact = targetProgram->getExistingWholeProgramResult())
+ {
+ if (!targetProgram->getOptionSet().getBoolOption(
+ CompilerOptionName::EmbedDownstreamIR))
+ {
+ artifacts.add(ComPtr<IArtifact>(artifact));
+ }
+ }
+ }
+ else
+ {
+ Index entryPointCount = program->getEntryPointCount();
+ for (Index ee = 0; ee < entryPointCount; ++ee)
+ {
+ if (auto artifact = targetProgram->getExistingEntryPointResult(ee))
+ {
+ artifacts.add(ComPtr<IArtifact>(artifact));
+ }
+ }
+ }
+ }
+
+ // If IR emitting is enabled, add IR to the artifacts
+ if (m_emitIr && (m_containerFormat == ContainerFormat::SlangModule))
+ {
+ OwnedMemoryStream stream(FileAccess::Write);
+ SlangResult res = writeContainerToStream(&stream);
+ if (SLANG_FAILED(res))
+ {
+ getSink()->diagnose(SourceLoc(), Diagnostics::unableToCreateModuleContainer);
+ return res;
+ }
+
+ // Need to turn into a blob
+ List<uint8_t> blobData;
+ stream.swapContents(blobData);
+
+ auto containerBlob = ListBlob::moveCreate(blobData);
+
+ auto irArtifact = Artifact::create(ArtifactDesc::make(
+ Artifact::Kind::CompileBinary,
+ ArtifactPayload::SlangIR,
+ ArtifactStyle::Unknown));
+ irArtifact->addRepresentationUnknown(containerBlob);
+
+ // Add the IR artifact
+ artifacts.add(irArtifact);
+ }
+
+ // If there is only one artifact we can use that as the container
+ if (artifacts.getCount() == 1)
+ {
+ m_containerArtifact = artifacts[0];
+ }
+ else
+ {
+ m_containerArtifact = ArtifactUtil::createArtifact(
+ ArtifactDesc::make(ArtifactKind::Container, ArtifactPayload::CompileResults));
+
+ for (IArtifact* childArtifact : artifacts)
+ {
+ m_containerArtifact->addChild(childArtifact);
+ }
+ }
+
+ // Get all of the source obfuscated source maps and add those
+ if (m_containerArtifact)
+ {
+ auto frontEndReq = getFrontEndReq();
+
+ for (auto translationUnit : frontEndReq->translationUnits)
+ {
+ // Hmmm do I have to therefore add a map for all translation units(!)
+ // I guess this is okay in so far as an association can always be looked up by name
+ if (auto sourceMap = _getObfuscatedSourceMap(translationUnit))
+ {
+ auto artifactDesc = ArtifactDesc::make(
+ ArtifactKind::Json,
+ ArtifactPayload::SourceMap,
+ ArtifactStyle::Obfuscated);
+
+ // Create the source map artifact
+ auto sourceMapArtifact =
+ Artifact::create(artifactDesc, sourceMap->get().m_file.getUnownedSlice());
+
+ // Add the repesentation
+ sourceMapArtifact->addRepresentation(sourceMap);
+
+ // Associate with the container
+ m_containerArtifact->addAssociated(sourceMapArtifact);
+ }
+ }
+ }
+
+ return SLANG_OK;
+}
+
+CompilerOptionSet& EndToEndCompileRequest::getTargetOptionSet(TargetRequest* req)
+{
+ return req->getOptionSet();
+}
+
+CompilerOptionSet& EndToEndCompileRequest::getTargetOptionSet(Index targetIndex)
+{
+ return m_linkage->targets[targetIndex]->getOptionSet();
+}
+
+SlangResult EndToEndCompileRequest::maybeWriteContainer(const String& fileName)
+{
+ // If there is no container, or filename, don't write anything
+ if (fileName.getLength() == 0 || !m_containerArtifact)
+ {
+ return SLANG_OK;
+ }
+
+ // Filter the containerArtifact into things that can be written
+ ComPtr<IArtifact> writeArtifact;
+ SLANG_RETURN_ON_FAIL(ArtifactContainerUtil::filter(m_containerArtifact, writeArtifact));
+
+ // Only write if there is something to write
+ if (writeArtifact)
+ {
+ SLANG_RETURN_ON_FAIL(ArtifactContainerUtil::writeContainer(writeArtifact, fileName));
+ }
+
+ return SLANG_OK;
+}
+
+void EndToEndCompileRequest::generateOutput(ComponentType* program)
+{
+ // When dynamic dispatch is disabled, the program must
+ // be fully specialized by now. So we check if we still
+ // have unspecialized generic/existential parameters,
+ // and report them as an error.
+ //
+ auto specializationParamCount = program->getSpecializationParamCount();
+ if (getOptionSet().getBoolOption(CompilerOptionName::DisableDynamicDispatch) &&
+ specializationParamCount != 0)
+ {
+ auto sink = getSink();
+
+ for (Index ii = 0; ii < specializationParamCount; ++ii)
+ {
+ auto specializationParam = program->getSpecializationParam(ii);
+ if (auto decl = as<Decl>(specializationParam.object))
+ {
+ sink->diagnose(
+ specializationParam.loc,
+ Diagnostics::specializationParameterOfNameNotSpecialized,
+ decl);
+ }
+ else if (auto type = as<Type>(specializationParam.object))
+ {
+ sink->diagnose(
+ specializationParam.loc,
+ Diagnostics::specializationParameterOfNameNotSpecialized,
+ type);
+ }
+ else
+ {
+ sink->diagnose(
+ specializationParam.loc,
+ Diagnostics::specializationParameterNotSpecialized);
+ }
+ }
+
+ return;
+ }
+
+
+ // Go through the code-generation targets that the user
+ // has specified, and generate code for each of them.
+ //
+ auto linkage = getLinkage();
+ for (auto targetReq : linkage->targets)
+ {
+ if (targetReq->getOptionSet().getBoolOption(CompilerOptionName::EmbedDownstreamIR))
+ continue;
+
+ auto targetProgram = program->getTargetProgram(targetReq);
+ generateOutput(targetProgram);
+ }
+}
+
+void EndToEndCompileRequest::generateOutput()
+{
+ SLANG_PROFILE;
+ generateOutput(getSpecializedGlobalAndEntryPointsComponentType());
+
+ // If we are in command-line mode, we might be expected to actually
+ // write output to one or more files here.
+
+ if (m_isCommandLineCompile && m_containerFormat == ContainerFormat::None)
+ {
+ auto linkage = getLinkage();
+ auto program = getSpecializedGlobalAndEntryPointsComponentType();
+
+ for (auto targetReq : linkage->targets)
+ {
+ auto targetProgram = program->getTargetProgram(targetReq);
+
+ if (targetProgram->getOptionSet().getBoolOption(
+ CompilerOptionName::GenerateWholeProgram))
+ {
+ if (const auto artifact = targetProgram->getExistingWholeProgramResult())
+ {
+ const auto path = _getWholeProgramPath(targetReq);
+
+ _maybeWriteArtifact(path, artifact);
+
+ // If we are compiling separate debug info, check for the additional
+ // SPIRV artifact and write that if needed.
+ _maybeWriteDebugArtifact(targetProgram, path, artifact);
+ }
+ }
+ else
+ {
+ Index entryPointCount = program->getEntryPointCount();
+ for (Index ee = 0; ee < entryPointCount; ++ee)
+ {
+ if (const auto artifact = targetProgram->getExistingEntryPointResult(ee))
+ {
+ const auto path = _getEntryPointPath(targetReq, ee);
+
+ _maybeWriteArtifact(path, artifact);
+
+ // If we are compiling separate debug info, check for the additional
+ // SPIRV artifact and write that if needed.
+ _maybeWriteDebugArtifact(targetProgram, path, artifact);
+ }
+ }
+ }
+ }
+ }
+
+ // Maybe create the container
+ maybeCreateContainer();
+
+ // If it's a command line compile we may need to write the container to a file
+ if (m_isCommandLineCompile)
+ {
+ // TODO(JS):
+ // We could write the container into a source embedded format potentially
+
+ maybeWriteContainer(m_containerOutputPath);
+
+ writeDependencyFile(this);
+ }
+}
+
+void EndToEndCompileRequest::setFileSystem(ISlangFileSystem* fileSystem)
+{
+ getLinkage()->setFileSystem(fileSystem);
+}
+
+void EndToEndCompileRequest::setCompileFlags(SlangCompileFlags flags)
+{
+ if (flags & SLANG_COMPILE_FLAG_NO_MANGLING)
+ getOptionSet().set(CompilerOptionName::NoMangle, true);
+ if (flags & SLANG_COMPILE_FLAG_NO_CODEGEN)
+ getOptionSet().set(CompilerOptionName::SkipCodeGen, true);
+ if (flags & SLANG_COMPILE_FLAG_OBFUSCATE)
+ getOptionSet().set(CompilerOptionName::Obfuscate, true);
+}
+
+SlangCompileFlags EndToEndCompileRequest::getCompileFlags()
+{
+ SlangCompileFlags result = 0;
+ if (getOptionSet().getBoolOption(CompilerOptionName::NoMangle))
+ result |= SLANG_COMPILE_FLAG_NO_MANGLING;
+ if (getOptionSet().getBoolOption(CompilerOptionName::SkipCodeGen))
+ result |= SLANG_COMPILE_FLAG_NO_CODEGEN;
+ if (getOptionSet().getBoolOption(CompilerOptionName::Obfuscate))
+ result |= SLANG_COMPILE_FLAG_OBFUSCATE;
+ return result;
+}
+
+void EndToEndCompileRequest::setDumpIntermediates(int enable)
+{
+ getOptionSet().set(CompilerOptionName::DumpIntermediates, enable);
+}
+
+void EndToEndCompileRequest::setTrackLiveness(bool v)
+{
+ getOptionSet().set(CompilerOptionName::TrackLiveness, v);
+}
+
+void EndToEndCompileRequest::setDumpIntermediatePrefix(const char* prefix)
+{
+ getOptionSet().set(CompilerOptionName::DumpIntermediatePrefix, String(prefix));
+}
+
+void EndToEndCompileRequest::setLineDirectiveMode(SlangLineDirectiveMode mode)
+{
+ getOptionSet().set(CompilerOptionName::LineDirectiveMode, mode);
+}
+
+void EndToEndCompileRequest::setCommandLineCompilerMode()
+{
+ m_isCommandLineCompile = true;
+
+ // legacy slangc tool defaults to column major layout.
+ if (!getOptionSet().hasOption(CompilerOptionName::MatrixLayoutRow))
+ getOptionSet().setMatrixLayoutMode(kMatrixLayoutMode_ColumnMajor);
+}
+
+void EndToEndCompileRequest::_completeTargetRequest(UInt targetIndex)
+{
+ auto linkage = getLinkage();
+
+ TargetRequest* targetRequest = linkage->targets[Index(targetIndex)];
+
+ targetRequest->getOptionSet().inheritFrom(getLinkage()->m_optionSet);
+ targetRequest->getOptionSet().inheritFrom(m_optionSetForDefaultTarget);
+}
+
+void EndToEndCompileRequest::setCodeGenTarget(SlangCompileTarget target)
+{
+ auto linkage = getLinkage();
+ linkage->targets.clear();
+ const auto targetIndex = linkage->addTarget(CodeGenTarget(target));
+ SLANG_ASSERT(targetIndex == 0);
+ _completeTargetRequest(0);
+}
+
+int EndToEndCompileRequest::addCodeGenTarget(SlangCompileTarget target)
+{
+ const auto targetIndex = getLinkage()->addTarget(CodeGenTarget(target));
+ _completeTargetRequest(targetIndex);
+ return int(targetIndex);
+}
+
+void EndToEndCompileRequest::setTargetProfile(int targetIndex, SlangProfileID profile)
+{
+ getTargetOptionSet(targetIndex).setProfile(Profile(profile));
+}
+
+void EndToEndCompileRequest::setTargetFlags(int targetIndex, SlangTargetFlags flags)
+{
+ getTargetOptionSet(targetIndex).setTargetFlags(flags);
+}
+
+void EndToEndCompileRequest::setTargetForceGLSLScalarBufferLayout(int targetIndex, bool value)
+{
+ getTargetOptionSet(targetIndex).set(CompilerOptionName::GLSLForceScalarLayout, value);
+}
+
+void EndToEndCompileRequest::setTargetForceDXLayout(int targetIndex, bool value)
+{
+ getTargetOptionSet(targetIndex).set(CompilerOptionName::ForceDXLayout, value);
+}
+
+void EndToEndCompileRequest::setTargetFloatingPointMode(
+ int targetIndex,
+ SlangFloatingPointMode mode)
+{
+ getTargetOptionSet(targetIndex)
+ .set(CompilerOptionName::FloatingPointMode, FloatingPointMode(mode));
+}
+
+void EndToEndCompileRequest::setMatrixLayoutMode(SlangMatrixLayoutMode mode)
+{
+ getOptionSet().setMatrixLayoutMode((MatrixLayoutMode)mode);
+}
+
+void EndToEndCompileRequest::setTargetMatrixLayoutMode(int targetIndex, SlangMatrixLayoutMode mode)
+{
+ getTargetOptionSet(targetIndex).setMatrixLayoutMode(MatrixLayoutMode(mode));
+}
+
+void EndToEndCompileRequest::setTargetGenerateWholeProgram(int targetIndex, bool value)
+{
+ getTargetOptionSet(targetIndex).set(CompilerOptionName::GenerateWholeProgram, value);
+}
+
+void EndToEndCompileRequest::setTargetEmbedDownstreamIR(int targetIndex, bool value)
+{
+ getTargetOptionSet(targetIndex).set(CompilerOptionName::EmbedDownstreamIR, value);
+}
+
+void EndToEndCompileRequest::setTargetLineDirectiveMode(
+ SlangInt targetIndex,
+ SlangLineDirectiveMode mode)
+{
+ getTargetOptionSet(targetIndex)
+ .set(CompilerOptionName::LineDirectiveMode, LineDirectiveMode(mode));
+}
+
+void EndToEndCompileRequest::overrideDiagnosticSeverity(
+ SlangInt messageID,
+ SlangSeverity overrideSeverity)
+{
+ getSink()->overrideDiagnosticSeverity(int(messageID), Severity(overrideSeverity));
+}
+
+SlangDiagnosticFlags EndToEndCompileRequest::getDiagnosticFlags()
+{
+ DiagnosticSink::Flags sinkFlags = getSink()->getFlags();
+
+ SlangDiagnosticFlags flags = 0;
+
+ if (sinkFlags & DiagnosticSink::Flag::VerbosePath)
+ flags |= SLANG_DIAGNOSTIC_FLAG_VERBOSE_PATHS;
+
+ if (sinkFlags & DiagnosticSink::Flag::TreatWarningsAsErrors)
+ flags |= SLANG_DIAGNOSTIC_FLAG_TREAT_WARNINGS_AS_ERRORS;
+
+ return flags;
+}
+
+void EndToEndCompileRequest::setDiagnosticFlags(SlangDiagnosticFlags flags)
+{
+ DiagnosticSink::Flags sinkFlags = getSink()->getFlags();
+
+ if (flags & SLANG_DIAGNOSTIC_FLAG_VERBOSE_PATHS)
+ sinkFlags |= DiagnosticSink::Flag::VerbosePath;
+ else
+ sinkFlags &= ~DiagnosticSink::Flag::VerbosePath;
+
+ if (flags & SLANG_DIAGNOSTIC_FLAG_TREAT_WARNINGS_AS_ERRORS)
+ sinkFlags |= DiagnosticSink::Flag::TreatWarningsAsErrors;
+ else
+ sinkFlags &= ~DiagnosticSink::Flag::TreatWarningsAsErrors;
+
+ getSink()->setFlags(sinkFlags);
+}
+
+SlangResult EndToEndCompileRequest::addTargetCapability(
+ SlangInt targetIndex,
+ SlangCapabilityID capability)
+{
+ auto& targets = getLinkage()->targets;
+ if (targetIndex < 0 || targetIndex >= targets.getCount())
+ return SLANG_E_INVALID_ARG;
+ getTargetOptionSet(targetIndex).addCapabilityAtom(CapabilityName(capability));
+ return SLANG_OK;
+}
+
+void EndToEndCompileRequest::setDebugInfoLevel(SlangDebugInfoLevel level)
+{
+ getOptionSet().set(CompilerOptionName::DebugInformation, DebugInfoLevel(level));
+}
+
+void EndToEndCompileRequest::setDebugInfoFormat(SlangDebugInfoFormat format)
+{
+ getOptionSet().set(CompilerOptionName::DebugInformationFormat, DebugInfoFormat(format));
+}
+
+void EndToEndCompileRequest::setOptimizationLevel(SlangOptimizationLevel level)
+{
+ getOptionSet().set(CompilerOptionName::Optimization, OptimizationLevel(level));
+}
+
+void EndToEndCompileRequest::setOutputContainerFormat(SlangContainerFormat format)
+{
+ m_containerFormat = ContainerFormat(format);
+}
+
+void EndToEndCompileRequest::setPassThrough(SlangPassThrough inPassThrough)
+{
+ m_passThrough = PassThroughMode(inPassThrough);
+}
+
+void EndToEndCompileRequest::setReportDownstreamTime(bool value)
+{
+ getOptionSet().set(CompilerOptionName::ReportDownstreamTime, value);
+}
+
+void EndToEndCompileRequest::setReportPerfBenchmark(bool value)
+{
+ getOptionSet().set(CompilerOptionName::ReportPerfBenchmark, value);
+}
+
+void EndToEndCompileRequest::setSkipSPIRVValidation(bool value)
+{
+ getOptionSet().set(CompilerOptionName::SkipSPIRVValidation, value);
+}
+
+void EndToEndCompileRequest::setTargetUseMinimumSlangOptimization(int targetIndex, bool value)
+{
+ getTargetOptionSet(targetIndex).set(CompilerOptionName::MinimumSlangOptimization, value);
+}
+
+void EndToEndCompileRequest::setIgnoreCapabilityCheck(bool value)
+{
+ getOptionSet().set(CompilerOptionName::IgnoreCapabilities, value);
+}
+
+void EndToEndCompileRequest::setDiagnosticCallback(
+ SlangDiagnosticCallback callback,
+ void const* userData)
+{
+ ComPtr<ISlangWriter> writer(new CallbackWriter(callback, userData, WriterFlag::IsConsole));
+ setWriter(WriterChannel::Diagnostic, writer);
+}
+
+void EndToEndCompileRequest::setWriter(SlangWriterChannel chan, ISlangWriter* writer)
+{
+ setWriter(WriterChannel(chan), writer);
+}
+
+ISlangWriter* EndToEndCompileRequest::getWriter(SlangWriterChannel chan)
+{
+ return getWriter(WriterChannel(chan));
+}
+
+void EndToEndCompileRequest::addSearchPath(const char* path)
+{
+ getOptionSet().addSearchPath(path);
+}
+
+void EndToEndCompileRequest::addPreprocessorDefine(const char* key, const char* value)
+{
+ getOptionSet().addPreprocessorDefine(key, value);
+}
+
+void EndToEndCompileRequest::setEnableEffectAnnotations(bool value)
+{
+ getOptionSet().set(CompilerOptionName::EnableEffectAnnotations, value);
+}
+
+char const* EndToEndCompileRequest::getDiagnosticOutput()
+{
+ return m_diagnosticOutput.begin();
+}
+
+SlangResult EndToEndCompileRequest::getDiagnosticOutputBlob(ISlangBlob** outBlob)
+{
+ if (!outBlob)
+ return SLANG_E_INVALID_ARG;
+
+ if (!m_diagnosticOutputBlob)
+ {
+ m_diagnosticOutputBlob = StringUtil::createStringBlob(m_diagnosticOutput);
+ }
+
+ ComPtr<ISlangBlob> resultBlob = m_diagnosticOutputBlob;
+ *outBlob = resultBlob.detach();
+ return SLANG_OK;
+}
+
+int EndToEndCompileRequest::addTranslationUnit(SlangSourceLanguage language, char const* inName)
+{
+ auto frontEndReq = getFrontEndReq();
+ NamePool* namePool = frontEndReq->getNamePool();
+
+ // Work out a module name. Can be nullptr if so will generate a name
+ Name* moduleName = inName ? namePool->getName(inName) : frontEndReq->m_defaultModuleName;
+
+ // If moduleName is nullptr a name will be generated
+ return frontEndReq->addTranslationUnit(Slang::SourceLanguage(language), moduleName);
+}
+
+void EndToEndCompileRequest::setDefaultModuleName(const char* defaultModuleName)
+{
+ auto frontEndReq = getFrontEndReq();
+ NamePool* namePool = frontEndReq->getNamePool();
+ frontEndReq->m_defaultModuleName = namePool->getName(defaultModuleName);
+}
+
+SlangResult _addLibraryReference(
+ EndToEndCompileRequest* req,
+ ModuleLibrary* moduleLibrary,
+ bool includeEntryPoint)
+{
+ FrontEndCompileRequest* frontEndRequest = req->getFrontEndReq();
+
+ if (includeEntryPoint)
+ {
+ frontEndRequest->m_extraEntryPoints.addRange(
+ moduleLibrary->m_entryPoints.getBuffer(),
+ moduleLibrary->m_entryPoints.getCount());
+ }
+
+ for (auto m : moduleLibrary->m_modules)
+ {
+ RefPtr<TranslationUnitRequest> tu = new TranslationUnitRequest(frontEndRequest, m);
+ frontEndRequest->translationUnits.add(tu);
+ // For modules loaded for EndToEndCompileRequest,
+ // we don't need the automatically discovered entrypoints.
+ if (!includeEntryPoint)
+ m->getEntryPoints().clear();
+ }
+ return SLANG_OK;
+}
+
+SlangResult _addLibraryReference(
+ EndToEndCompileRequest* req,
+ String path,
+ IArtifact* artifact,
+ bool includeEntryPoint)
+{
+ auto desc = artifact->getDesc();
+
+ // TODO(JS):
+ // This isn't perhaps the best way to handle this scenario, as IArtifact can
+ // support lazy evaluation, with suitable hander.
+ // For now we just read in and strip out the bits we want.
+ if (isDerivedFrom(desc.kind, ArtifactKind::Container) &&
+ isDerivedFrom(desc.payload, ArtifactPayload::CompileResults))
+ {
+ // We want to read as a file system
+ ComPtr<IArtifact> container;
+
+ SLANG_RETURN_ON_FAIL(ArtifactContainerUtil::readContainer(artifact, container));
+
+ // Find the payload... It should be linkable
+ if (!ArtifactDescUtil::isLinkable(container->getDesc()))
+ {
+ return SLANG_FAIL;
+ }
+
+ ComPtr<IModuleLibrary> libraryIntf;
+ SLANG_RETURN_ON_FAIL(
+ loadModuleLibrary(ArtifactKeep::Yes, container, path, req, libraryIntf));
+
+ auto library = as<ModuleLibrary>(libraryIntf);
+
+ // Look for source maps
+ for (auto associated : container->getAssociated())
+ {
+ auto assocDesc = associated->getDesc();
+
+ // If we find an obfuscated source map load it and associate
+ if (isDerivedFrom(assocDesc.kind, ArtifactKind::Json) &&
+ isDerivedFrom(assocDesc.payload, ArtifactPayload::SourceMap) &&
+ isDerivedFrom(assocDesc.style, ArtifactStyle::Obfuscated))
+ {
+ ComPtr<ICastable> castable;
+ SLANG_RETURN_ON_FAIL(associated->getOrCreateRepresentation(
+ SourceMap::getTypeGuid(),
+ ArtifactKeep::Yes,
+ castable.writeRef()));
+ auto sourceMapBox = asBoxValue<SourceMap>(castable);
+ SLANG_ASSERT(sourceMapBox);
+
+ // TODO(JS):
+ // There is perhaps (?) a risk here that we might copy the obfuscated map
+ // into some output container. Currently that only happens for source maps
+ // that are from translation units.
+ //
+ // On the other hand using "import" is a way that such source maps *would* be
+ // copied into the output, and that is something that could be a vector
+ // for leaking.
+ //
+ // That isn't a risk from -r though because, it doesn't create a translation
+ // unit(s).
+ for (auto module : library->m_modules)
+ {
+ module->getIRModule()->setObfuscatedSourceMap(sourceMapBox);
+ }
+
+ // Look up the source file
+ auto sourceManager = req->getSink()->getSourceManager();
+
+ auto name = Path::getFileNameWithoutExt(associated->getName());
+
+ if (name.getLength())
+ {
+ // Note(tfoley): There is a subtle requirement here, that any
+ // source file `name` that might be searched for here *must*
+ // have been added to the `sourceManager` already, as a
+ // byproduct of debug source location information getting
+ // deserialized as part of the call to `loadModuleLibrary()` above.
+ //
+ // The implicit dependency is frustrating, and could potentially
+ // break if somehow the debug info chunk was stripped from a binary,
+ // while the source map was left in (which should be valid, even if
+ // it is unlikely to be what a user wants).
+ //
+ // Ideally the source map would either be made an integral part of
+ // the debug source location chunk, so they are loaded together,
+ // or the `SourceManager` would be adapted so that it can store
+ // registered source maps independent of whether or not the
+ // corresponding source file(s) have been loaded.
+
+ auto sourceFile = sourceManager->findSourceFileByPathRecursively(name);
+ SLANG_ASSERT(sourceFile);
+ sourceFile->setSourceMap(sourceMapBox, SourceMapKind::Obfuscated);
+ }
+ }
+ }
+
+ SLANG_RETURN_ON_FAIL(_addLibraryReference(req, library, includeEntryPoint));
+ return SLANG_OK;
+ }
+
+ if (desc.kind == ArtifactKind::Library && desc.payload == ArtifactPayload::SlangIR)
+ {
+ ComPtr<IModuleLibrary> libraryIntf;
+
+ SLANG_RETURN_ON_FAIL(
+ loadModuleLibrary(ArtifactKeep::Yes, artifact, path, req, libraryIntf));
+
+ auto library = as<ModuleLibrary>(libraryIntf);
+ if (!library)
+ {
+ return SLANG_FAIL;
+ }
+
+ SLANG_RETURN_ON_FAIL(_addLibraryReference(req, library, includeEntryPoint));
+ return SLANG_OK;
+ }
+
+ // TODO(JS):
+ // Do we want to check the path exists?
+
+ // Add to the m_libModules
+ auto linkage = req->getLinkage();
+ linkage->m_libModules.add(ComPtr<IArtifact>(artifact));
+
+ return SLANG_OK;
+}
+
+SlangResult EndToEndCompileRequest::addLibraryReference(
+ const char* basePath,
+ const void* libData,
+ size_t libDataSize)
+{
+ // We need to deserialize and add the modules
+ ComPtr<IModuleLibrary> library;
+
+ auto libBlob = RawBlob::create((const Byte*)libData, libDataSize);
+
+ SLANG_RETURN_ON_FAIL(
+ loadModuleLibrary(libBlob, (const Byte*)libData, libDataSize, basePath, this, library));
+
+ // Create an artifact without any name (as one is not provided)
+ auto artifact =
+ Artifact::create(ArtifactDesc::make(ArtifactKind::Library, ArtifactPayload::SlangIR));
+ artifact->addRepresentation(library);
+
+ return _addLibraryReference(this, basePath, artifact, true);
+}
+
+void EndToEndCompileRequest::addTranslationUnitPreprocessorDefine(
+ int translationUnitIndex,
+ const char* key,
+ const char* value)
+{
+ getFrontEndReq()->translationUnits[translationUnitIndex]->preprocessorDefinitions[key] = value;
+}
+
+void EndToEndCompileRequest::addTranslationUnitSourceFile(
+ int translationUnitIndex,
+ char const* path)
+{
+ auto frontEndReq = getFrontEndReq();
+ if (!path)
+ return;
+ if (translationUnitIndex < 0)
+ return;
+ if (Index(translationUnitIndex) >= frontEndReq->translationUnits.getCount())
+ return;
+
+ frontEndReq->addTranslationUnitSourceFile(translationUnitIndex, path);
+}
+
+void EndToEndCompileRequest::addTranslationUnitSourceString(
+ int translationUnitIndex,
+ char const* path,
+ char const* source)
+{
+ if (!source)
+ return;
+ addTranslationUnitSourceStringSpan(translationUnitIndex, path, source, source + strlen(source));
+}
+
+void EndToEndCompileRequest::addTranslationUnitSourceStringSpan(
+ int translationUnitIndex,
+ char const* path,
+ char const* sourceBegin,
+ char const* sourceEnd)
+{
+ auto frontEndReq = getFrontEndReq();
+ if (!sourceBegin)
+ return;
+ if (translationUnitIndex < 0)
+ return;
+ if (Index(translationUnitIndex) >= frontEndReq->translationUnits.getCount())
+ return;
+
+ if (!path)
+ path = "";
+
+ const auto slice = UnownedStringSlice(sourceBegin, sourceEnd);
+
+ auto blob = RawBlob::create(slice.begin(), slice.getLength());
+
+ frontEndReq->addTranslationUnitSourceBlob(translationUnitIndex, path, blob);
+}
+
+void EndToEndCompileRequest::addTranslationUnitSourceBlob(
+ int translationUnitIndex,
+ char const* path,
+ ISlangBlob* sourceBlob)
+{
+ auto frontEndReq = getFrontEndReq();
+ if (!sourceBlob)
+ return;
+ if (translationUnitIndex < 0)
+ return;
+ if (Slang::Index(translationUnitIndex) >= frontEndReq->translationUnits.getCount())
+ return;
+
+ if (!path)
+ path = "";
+
+ frontEndReq->addTranslationUnitSourceBlob(translationUnitIndex, path, sourceBlob);
+}
+
+
+int EndToEndCompileRequest::addEntryPoint(
+ int translationUnitIndex,
+ char const* name,
+ SlangStage stage)
+{
+ return addEntryPointEx(translationUnitIndex, name, stage, 0, nullptr);
+}
+
+int EndToEndCompileRequest::addEntryPointEx(
+ int translationUnitIndex,
+ char const* name,
+ SlangStage stage,
+ int genericParamTypeNameCount,
+ char const** genericParamTypeNames)
+{
+ auto frontEndReq = getFrontEndReq();
+ if (!name)
+ return -1;
+ if (translationUnitIndex < 0)
+ return -1;
+ if (Index(translationUnitIndex) >= frontEndReq->translationUnits.getCount())
+ return -1;
+
+ List<String> typeNames;
+ for (int i = 0; i < genericParamTypeNameCount; i++)
+ typeNames.add(genericParamTypeNames[i]);
+
+ return addEntryPoint(translationUnitIndex, name, Profile(Stage(stage)), typeNames);
+}
+
+SlangResult EndToEndCompileRequest::setGlobalGenericArgs(
+ int genericArgCount,
+ char const** genericArgs)
+{
+ auto& argStrings = m_globalSpecializationArgStrings;
+ argStrings.clear();
+ for (int i = 0; i < genericArgCount; i++)
+ argStrings.add(genericArgs[i]);
+
+ return SLANG_OK;
+}
+
+SlangResult EndToEndCompileRequest::setTypeNameForGlobalExistentialTypeParam(
+ int slotIndex,
+ char const* typeName)
+{
+ if (slotIndex < 0)
+ return SLANG_FAIL;
+ if (!typeName)
+ return SLANG_FAIL;
+
+ auto& typeArgStrings = m_globalSpecializationArgStrings;
+ if (Index(slotIndex) >= typeArgStrings.getCount())
+ typeArgStrings.setCount(slotIndex + 1);
+ typeArgStrings[slotIndex] = String(typeName);
+ return SLANG_OK;
+}
+
+SlangResult EndToEndCompileRequest::setTypeNameForEntryPointExistentialTypeParam(
+ int entryPointIndex,
+ int slotIndex,
+ char const* typeName)
+{
+ if (entryPointIndex < 0)
+ return SLANG_FAIL;
+ if (slotIndex < 0)
+ return SLANG_FAIL;
+ if (!typeName)
+ return SLANG_FAIL;
+
+ if (Index(entryPointIndex) >= m_entryPoints.getCount())
+ return SLANG_FAIL;
+
+ auto& entryPointInfo = m_entryPoints[entryPointIndex];
+ auto& typeArgStrings = entryPointInfo.specializationArgStrings;
+ if (Index(slotIndex) >= typeArgStrings.getCount())
+ typeArgStrings.setCount(slotIndex + 1);
+ typeArgStrings[slotIndex] = String(typeName);
+ return SLANG_OK;
+}
+
+void EndToEndCompileRequest::setAllowGLSLInput(bool value)
+{
+ getOptionSet().set(CompilerOptionName::AllowGLSL, value);
+}
+
+SlangResult EndToEndCompileRequest::compile()
+{
+ SlangResult res = SLANG_FAIL;
+ double downstreamStartTime = 0.0;
+ double totalStartTime = 0.0;
+
+ if (getOptionSet().getBoolOption(CompilerOptionName::ReportDownstreamTime))
+ {
+ getSession()->getCompilerElapsedTime(&totalStartTime, &downstreamStartTime);
+ PerformanceProfiler::getProfiler()->clear();
+ }
+#if !defined(SLANG_DEBUG_INTERNAL_ERROR)
+ // By default we'd like to catch as many internal errors as possible,
+ // and report them to the user nicely (rather than just crash their
+ // application). Internally Slang currently uses exceptions for this.
+ //
+ // TODO: Consider using `setjmp()`-style escape so that we can work
+ // with applications that disable exceptions.
+ //
+ // TODO: Consider supporting Windows "Structured Exception Handling"
+ // so that we can also recover from a wider class of crashes.
+
+ try
+ {
+ SLANG_PROFILE_SECTION(compileInner);
+ res = executeActions();
+ }
+ catch (const AbortCompilationException& e)
+ {
+ // This situation indicates a fatal (but not necessarily internal) error
+ // that forced compilation to terminate. There should already have been
+ // a diagnostic produced, so we don't need to add one here.
+ if (getSink()->getErrorCount() == 0)
+ {
+ // If for some reason we didn't output any diagnostic, something is
+ // going wrong, but we want to make sure we at least output something.
+ getSink()->diagnose(
+ SourceLoc(),
+ Diagnostics::compilationAbortedDueToException,
+ typeid(e).name(),
+ e.Message);
+ }
+ }
+ catch (const Exception& e)
+ {
+ // The compiler failed due to an internal error that was detected.
+ // We will print out information on the exception to help out the user
+ // in either filing a bug, or locating what in their code created
+ // a problem.
+ getSink()->diagnose(
+ SourceLoc(),
+ Diagnostics::compilationAbortedDueToException,
+ typeid(e).name(),
+ e.Message);
+ }
+ catch (...)
+ {
+ // The compiler failed due to some exception that wasn't a sublass of
+ // `Exception`, so something really fishy is going on. We want to
+ // let the user know that we messed up, so they know to blame Slang
+ // and not some other component in their system.
+ getSink()->diagnose(SourceLoc(), Diagnostics::compilationAborted);
+ }
+ m_diagnosticOutput = getSink()->outputBuffer.produceString();
+
+#else
+ // When debugging, we probably don't want to filter out any errors, since
+ // we are probably trying to root-cause and *fix* those errors.
+ {
+ res = req->executeActions();
+ }
+#endif
+
+ if (getOptionSet().getBoolOption(CompilerOptionName::ReportDownstreamTime))
+ {
+ double downstreamEndTime = 0;
+ double totalEndTime = 0;
+ getSession()->getCompilerElapsedTime(&totalEndTime, &downstreamEndTime);
+ double downstreamTime = downstreamEndTime - downstreamStartTime;
+ String downstreamTimeStr = String(downstreamTime, "%.2f");
+ getSink()->diagnose(SourceLoc(), Diagnostics::downstreamCompileTime, downstreamTimeStr);
+ }
+ if (getOptionSet().getBoolOption(CompilerOptionName::ReportPerfBenchmark))
+ {
+ StringBuilder perfResult;
+ PerformanceProfiler::getProfiler()->getResult(perfResult);
+ perfResult << "\nType Dictionary Size: " << getSession()->m_typeDictionarySize << "\n";
+ getSink()->diagnose(
+ SourceLoc(),
+ Diagnostics::performanceBenchmarkResult,
+ perfResult.produceString());
+ }
+
+ // Repro dump handling
+ {
+ auto dumpRepro = getOptionSet().getStringOption(CompilerOptionName::DumpRepro);
+ auto dumpReproOnError = getOptionSet().getBoolOption(CompilerOptionName::DumpReproOnError);
+
+ if (dumpRepro.getLength())
+ {
+ SlangResult saveRes = ReproUtil::saveState(this, dumpRepro);
+ if (SLANG_FAILED(saveRes))
+ {
+ getSink()->diagnose(SourceLoc(), Diagnostics::unableToWriteReproFile, dumpRepro);
+ return saveRes;
+ }
+ }
+ else if (dumpReproOnError && SLANG_FAILED(res))
+ {
+ String reproFileName;
+ SlangResult saveRes = SLANG_FAIL;
+
+ RefPtr<Stream> stream;
+ if (SLANG_SUCCEEDED(ReproUtil::findUniqueReproDumpStream(this, reproFileName, stream)))
+ {
+ saveRes = ReproUtil::saveState(this, stream);
+ }
+
+ if (SLANG_FAILED(saveRes))
+ {
+ getSink()->diagnose(
+ SourceLoc(),
+ Diagnostics::unableToWriteReproFile,
+ reproFileName);
+ }
+ }
+ }
+
+ auto reflectionPath = getOptionSet().getStringOption(CompilerOptionName::EmitReflectionJSON);
+ if (reflectionPath.getLength() != 0)
+ {
+ auto bufferWriter = PrettyWriter();
+ emitReflectionJSON(this, this->getReflection(), bufferWriter);
+ if (reflectionPath == "-")
+ {
+ auto builder = bufferWriter.getBuilder();
+ StdWriters::getOut().write(builder.getBuffer(), builder.getLength());
+ }
+ else if (SLANG_FAILED(File::writeAllText(reflectionPath, bufferWriter.getBuilder())))
+ {
+ getSink()->diagnose(SourceLoc(), Diagnostics::unableToWriteFile, reflectionPath);
+ }
+ }
+
+ return res;
+}
+
+int EndToEndCompileRequest::getDependencyFileCount()
+{
+ auto frontEndReq = getFrontEndReq();
+ auto program = frontEndReq->getGlobalAndEntryPointsComponentType();
+ return (int)program->getFileDependencies().getCount();
+}
+
+char const* EndToEndCompileRequest::getDependencyFilePath(int index)
+{
+ auto frontEndReq = getFrontEndReq();
+ auto program = frontEndReq->getGlobalAndEntryPointsComponentType();
+ SourceFile* sourceFile = program->getFileDependencies()[index];
+ return sourceFile->getPathInfo().hasFoundPath()
+ ? sourceFile->getPathInfo().getMostUniqueIdentity().getBuffer()
+ : "unknown";
+}
+
+int EndToEndCompileRequest::getTranslationUnitCount()
+{
+ return (int)getFrontEndReq()->translationUnits.getCount();
+}
+
+void const* EndToEndCompileRequest::getEntryPointCode(int entryPointIndex, size_t* outSize)
+{
+ // Zero the size initially, in case need to return nullptr for error.
+ if (outSize)
+ {
+ *outSize = 0;
+ }
+
+ auto linkage = getLinkage();
+ auto program = getSpecializedGlobalAndEntryPointsComponentType();
+
+ // TODO: We should really accept a target index in this API
+ Index targetIndex = 0;
+ auto targetCount = linkage->targets.getCount();
+ if (targetIndex >= targetCount)
+ return nullptr;
+ auto targetReq = linkage->targets[targetIndex];
+
+
+ if (entryPointIndex < 0)
+ return nullptr;
+ if (Index(entryPointIndex) >= program->getEntryPointCount())
+ return nullptr;
+ auto entryPoint = program->getEntryPoint(entryPointIndex);
+
+ auto targetProgram = program->getTargetProgram(targetReq);
+ if (!targetProgram)
+ return nullptr;
+ IArtifact* artifact = targetProgram->getExistingEntryPointResult(entryPointIndex);
+ if (!artifact)
+ {
+ return nullptr;
+ }
+
+ ComPtr<ISlangBlob> blob;
+ SLANG_RETURN_NULL_ON_FAIL(artifact->loadBlob(ArtifactKeep::Yes, blob.writeRef()));
+
+ if (outSize)
+ {
+ *outSize = blob->getBufferSize();
+ }
+
+ return (void*)blob->getBufferPointer();
+}
+
+SlangResult EndToEndCompileRequest::getCompileTimeProfile(
+ ISlangProfiler** compileTimeProfile,
+ bool shouldClear)
+{
+ if (compileTimeProfile == nullptr)
+ {
+ return SLANG_E_INVALID_ARG;
+ }
+
+ SlangProfiler* profiler = new SlangProfiler(PerformanceProfiler::getProfiler());
+
+ if (shouldClear)
+ {
+ PerformanceProfiler::getProfiler()->clear();
+ }
+
+ ComPtr<ISlangProfiler> result(profiler);
+ *compileTimeProfile = result.detach();
+ return SLANG_OK;
+}
+
+static SlangResult _getEntryPointResult(
+ EndToEndCompileRequest* req,
+ int entryPointIndex,
+ int targetIndex,
+ ComPtr<IArtifact>& outArtifact)
+{
+ auto linkage = req->getLinkage();
+ auto program = req->getSpecializedGlobalAndEntryPointsComponentType();
+
+ Index targetCount = linkage->targets.getCount();
+ if ((targetIndex < 0) || (targetIndex >= targetCount))
+ {
+ return SLANG_E_INVALID_ARG;
+ }
+ auto targetReq = linkage->targets[targetIndex];
+
+ // Get the entry point count on the program, rather than (say) req->m_entryPoints.getCount()
+ // because
+ // 1) The entry point is fetched from the program anyway so must be consistent
+ // 2) The req may not have all entry points (for example when an entry point is in a module)
+ const Index entryPointCount = program->getEntryPointCount();
+
+ if ((entryPointIndex < 0) || (entryPointIndex >= entryPointCount))
+ {
+ return SLANG_E_INVALID_ARG;
+ }
+ auto entryPointReq = program->getEntryPoint(entryPointIndex);
+
+ auto targetProgram = program->getTargetProgram(targetReq);
+ if (!targetProgram)
+ return SLANG_FAIL;
+
+ outArtifact = targetProgram->getExistingEntryPointResult(entryPointIndex);
+ return SLANG_OK;
+}
+
+static SlangResult _getWholeProgramResult(
+ EndToEndCompileRequest* req,
+ int targetIndex,
+ ComPtr<IArtifact>& outArtifact)
+{
+ auto linkage = req->getLinkage();
+ auto program = req->getSpecializedGlobalAndEntryPointsComponentType();
+
+ if (!program)
+ {
+ return SLANG_FAIL;
+ }
+
+ Index targetCount = linkage->targets.getCount();
+ if ((targetIndex < 0) || (targetIndex >= targetCount))
+ {
+ return SLANG_E_INVALID_ARG;
+ }
+ auto targetReq = linkage->targets[targetIndex];
+
+ auto targetProgram = program->getTargetProgram(targetReq);
+ if (!targetProgram)
+ return SLANG_FAIL;
+ outArtifact = targetProgram->getExistingWholeProgramResult();
+ return SLANG_OK;
+}
+
+SlangResult EndToEndCompileRequest::getEntryPointCodeBlob(
+ int entryPointIndex,
+ int targetIndex,
+ ISlangBlob** outBlob)
+{
+ if (!outBlob)
+ return SLANG_E_INVALID_ARG;
+ ComPtr<IArtifact> artifact;
+ SLANG_RETURN_ON_FAIL(_getEntryPointResult(this, entryPointIndex, targetIndex, artifact));
+ SLANG_RETURN_ON_FAIL(artifact->loadBlob(ArtifactKeep::Yes, outBlob));
+
+ return SLANG_OK;
+}
+
+SlangResult EndToEndCompileRequest::getEntryPointHostCallable(
+ int entryPointIndex,
+ int targetIndex,
+ ISlangSharedLibrary** outSharedLibrary)
+{
+ if (!outSharedLibrary)
+ return SLANG_E_INVALID_ARG;
+ ComPtr<IArtifact> artifact;
+ SLANG_RETURN_ON_FAIL(_getEntryPointResult(this, entryPointIndex, targetIndex, artifact));
+ SLANG_RETURN_ON_FAIL(artifact->loadSharedLibrary(ArtifactKeep::Yes, outSharedLibrary));
+ return SLANG_OK;
+}
+
+SlangResult EndToEndCompileRequest::getTargetCodeBlob(int targetIndex, ISlangBlob** outBlob)
+{
+ if (!outBlob)
+ return SLANG_E_INVALID_ARG;
+
+ ComPtr<IArtifact> artifact;
+ SLANG_RETURN_ON_FAIL(_getWholeProgramResult(this, targetIndex, artifact));
+ SLANG_RETURN_ON_FAIL(artifact->loadBlob(ArtifactKeep::Yes, outBlob));
+ return SLANG_OK;
+}
+
+SlangResult EndToEndCompileRequest::getTargetHostCallable(
+ int targetIndex,
+ ISlangSharedLibrary** outSharedLibrary)
+{
+ if (!outSharedLibrary)
+ return SLANG_E_INVALID_ARG;
+
+ ComPtr<IArtifact> artifact;
+ SLANG_RETURN_ON_FAIL(_getWholeProgramResult(this, targetIndex, artifact));
+ SLANG_RETURN_ON_FAIL(artifact->loadSharedLibrary(ArtifactKeep::Yes, outSharedLibrary));
+ return SLANG_OK;
+}
+
+char const* EndToEndCompileRequest::getEntryPointSource(int entryPointIndex)
+{
+ return (char const*)getEntryPointCode(entryPointIndex, nullptr);
+}
+
+ISlangMutableFileSystem* EndToEndCompileRequest::getCompileRequestResultAsFileSystem()
+{
+ if (!m_containerFileSystem)
+ {
+ if (m_containerArtifact)
+ {
+ ComPtr<ISlangMutableFileSystem> fileSystem(new MemoryFileSystem);
+
+ // Filter the containerArtifact into things that can be written
+ ComPtr<IArtifact> writeArtifact;
+ if (SLANG_SUCCEEDED(
+ ArtifactContainerUtil::filter(m_containerArtifact, writeArtifact)) &&
+ writeArtifact)
+ {
+ if (SLANG_SUCCEEDED(
+ ArtifactContainerUtil::writeContainer(writeArtifact, "", fileSystem)))
+ {
+ m_containerFileSystem.swap(fileSystem);
+ }
+ }
+ }
+ }
+
+ return m_containerFileSystem;
+}
+
+void const* EndToEndCompileRequest::getCompileRequestCode(size_t* outSize)
+{
+ if (m_containerArtifact)
+ {
+ ComPtr<ISlangBlob> containerBlob;
+ if (SLANG_SUCCEEDED(
+ m_containerArtifact->loadBlob(ArtifactKeep::Yes, containerBlob.writeRef())))
+ {
+ *outSize = containerBlob->getBufferSize();
+ return containerBlob->getBufferPointer();
+ }
+ }
+
+ // Container blob does not have any contents
+ *outSize = 0;
+ return nullptr;
+}
+
+SlangResult EndToEndCompileRequest::getContainerCode(ISlangBlob** outBlob)
+{
+ if (m_containerArtifact)
+ {
+ ComPtr<ISlangBlob> containerBlob;
+ if (SLANG_SUCCEEDED(
+ m_containerArtifact->loadBlob(ArtifactKeep::Yes, containerBlob.writeRef())))
+ {
+ *outBlob = containerBlob.detach();
+ return SLANG_OK;
+ }
+ }
+ return SLANG_FAIL;
+}
+
+SlangResult EndToEndCompileRequest::loadRepro(
+ ISlangFileSystem* fileSystem,
+ const void* data,
+ size_t size)
+{
+ List<uint8_t> buffer;
+ SLANG_RETURN_ON_FAIL(ReproUtil::loadState((const uint8_t*)data, size, getSink(), buffer));
+
+ MemoryOffsetBase base;
+ base.set(buffer.getBuffer(), buffer.getCount());
+
+ ReproUtil::RequestState* requestState = ReproUtil::getRequest(buffer);
+
+ SLANG_RETURN_ON_FAIL(ReproUtil::load(base, requestState, fileSystem, this));
+ return SLANG_OK;
+}
+
+SlangResult EndToEndCompileRequest::saveRepro(ISlangBlob** outBlob)
+{
+ OwnedMemoryStream stream(FileAccess::Write);
+
+ SLANG_RETURN_ON_FAIL(ReproUtil::saveState(this, &stream));
+
+ // Put the content of the stream in the blob
+
+ List<uint8_t> data;
+ stream.swapContents(data);
+
+ *outBlob = ListBlob::moveCreate(data).detach();
+ return SLANG_OK;
+}
+
+SlangResult EndToEndCompileRequest::enableReproCapture()
+{
+ getLinkage()->setRequireCacheFileSystem(true);
+ return SLANG_OK;
+}
+
+SlangResult EndToEndCompileRequest::processCommandLineArguments(
+ char const* const* args,
+ int argCount)
+{
+ return parseOptions(this, argCount, args);
+}
+
+SlangReflection* EndToEndCompileRequest::getReflection()
+{
+ auto linkage = getLinkage();
+ auto program = getSpecializedGlobalAndEntryPointsComponentType();
+
+ // Note(tfoley): The API signature doesn't let the client
+ // specify which target they want to access reflection
+ // information for, so for now we default to the first one.
+ //
+ // TODO: Add a new `spGetReflectionForTarget(req, targetIndex)`
+ // so that we can do this better, and make it clear that
+ // `spGetReflection()` is shorthand for `targetIndex == 0`.
+ //
+ Slang::Index targetIndex = 0;
+ auto targetCount = linkage->targets.getCount();
+ if (targetIndex >= targetCount)
+ return nullptr;
+
+ auto targetReq = linkage->targets[targetIndex];
+ auto targetProgram = program->getTargetProgram(targetReq);
+
+
+ DiagnosticSink sink(linkage->getSourceManager(), Lexer::sourceLocationLexer);
+ auto programLayout = targetProgram->getOrCreateLayout(&sink);
+
+ return (SlangReflection*)programLayout;
+}
+
+SlangResult EndToEndCompileRequest::getProgram(slang::IComponentType** outProgram)
+{
+ auto program = getSpecializedGlobalComponentType();
+ *outProgram = Slang::ComPtr<slang::IComponentType>(program).detach();
+ return SLANG_OK;
+}
+
+SlangResult EndToEndCompileRequest::getProgramWithEntryPoints(slang::IComponentType** outProgram)
+{
+ auto program = getSpecializedGlobalAndEntryPointsComponentType();
+ *outProgram = Slang::ComPtr<slang::IComponentType>(program).detach();
+ return SLANG_OK;
+}
+
+SlangResult EndToEndCompileRequest::getModule(
+ SlangInt translationUnitIndex,
+ slang::IModule** outModule)
+{
+ auto module = getFrontEndReq()->getTranslationUnit(translationUnitIndex)->getModule();
+
+ *outModule = Slang::ComPtr<slang::IModule>(module).detach();
+ return SLANG_OK;
+}
+
+SlangResult EndToEndCompileRequest::getSession(slang::ISession** outSession)
+{
+ auto session = getLinkage();
+ *outSession = Slang::ComPtr<slang::ISession>(session).detach();
+ return SLANG_OK;
+}
+
+SlangResult EndToEndCompileRequest::getEntryPoint(
+ SlangInt entryPointIndex,
+ slang::IComponentType** outEntryPoint)
+{
+ auto entryPoint = getSpecializedEntryPointComponentType(entryPointIndex);
+ *outEntryPoint = Slang::ComPtr<slang::IComponentType>(entryPoint).detach();
+ return SLANG_OK;
+}
+
+SlangResult EndToEndCompileRequest::isParameterLocationUsed(
+ Int entryPointIndex,
+ Int targetIndex,
+ SlangParameterCategory category,
+ UInt spaceIndex,
+ UInt registerIndex,
+ bool& outUsed)
+{
+ if (!ShaderBindingRange::isUsageTracked((slang::ParameterCategory)category))
+ return SLANG_E_NOT_AVAILABLE;
+
+ ComPtr<IArtifact> artifact;
+ if (SLANG_FAILED(_getEntryPointResult(
+ this,
+ static_cast<int>(entryPointIndex),
+ static_cast<int>(targetIndex),
+ artifact)))
+ return SLANG_E_INVALID_ARG;
+
+ if (!artifact)
+ return SLANG_E_NOT_AVAILABLE;
+
+ // Find a rep
+ auto metadata = findAssociatedRepresentation<IArtifactPostEmitMetadata>(artifact);
+ if (!metadata)
+ return SLANG_E_NOT_AVAILABLE;
+
+ return metadata->isParameterLocationUsed(category, spaceIndex, registerIndex, outUsed);
+}
+
+} // namespace Slang
diff --git a/source/slang/slang-end-to-end-request.h b/source/slang/slang-end-to-end-request.h
new file mode 100644
index 000000000..af04eac63
--- /dev/null
+++ b/source/slang/slang-end-to-end-request.h
@@ -0,0 +1,416 @@
+// slang-end-to-end-request.h
+#pragma once
+
+//
+// This file provides the `EndToEndCompileRequest` type and
+// related utilities.
+//
+// The primary purpose of `EndToEndCompileRequest` is to
+// implement the overall flow of compilation for the
+// `slangc` command-line tool. Command-line compiles need
+// to deal with various details that aren't relevant to
+// API-based compiles (e.g., writing output to files),
+// and also need to implement a lot of complicated
+// "do what I mean" policy that is expected by users of
+// `slangc` but that can be dangerously implicit when
+// that policy is enshrined in an API.
+//
+// In addition to serving the needs of `slangc`, the
+// `EndToEndCompileRequest` type also implements the
+// deprecated `slang::ICompileRequest` interface from
+// the public API.
+//
+
+#include "../compiler-core/slang-source-embed-util.h"
+#include "../core/slang-file-system.h"
+#include "slang-compile-request.h"
+
+namespace Slang
+{
+
+enum class ContainerFormat : SlangContainerFormatIntegral
+{
+ None = SLANG_CONTAINER_FORMAT_NONE,
+ SlangModule = SLANG_CONTAINER_FORMAT_SLANG_MODULE,
+};
+
+// TODO: everything related to `StdWriters` should be removed.
+enum class WriterChannel : SlangWriterChannelIntegral
+{
+ Diagnostic = SLANG_WRITER_CHANNEL_DIAGNOSTIC,
+ StdOutput = SLANG_WRITER_CHANNEL_STD_OUTPUT,
+ StdError = SLANG_WRITER_CHANNEL_STD_ERROR,
+ CountOf = SLANG_WRITER_CHANNEL_COUNT_OF,
+};
+
+// TODO: everything related to `StdWriters` should be removed.
+enum class WriterMode : SlangWriterModeIntegral
+{
+ Text = SLANG_WRITER_MODE_TEXT,
+ Binary = SLANG_WRITER_MODE_BINARY,
+};
+
+/// A compile request that spans the front and back ends of the compiler
+///
+/// This is what the command-line `slangc` uses, as well as the legacy
+/// C API. It ties together the functionality of `Linkage`,
+/// `FrontEndCompileRequest`, and `BackEndCompileRequest`, plus a small
+/// number of additional features that primarily make sense for
+/// command-line usage.
+///
+class EndToEndCompileRequest : public RefObject, public slang::ICompileRequest
+{
+public:
+ SLANG_CLASS_GUID(0xce6d2383, 0xee1b, 0x4fd7, {0xa0, 0xf, 0xb8, 0xb6, 0x33, 0x12, 0x95, 0xc8})
+
+ // ISlangUnknown
+ SLANG_NO_THROW SlangResult SLANG_MCALL queryInterface(SlangUUID const& uuid, void** outObject)
+ SLANG_OVERRIDE;
+ SLANG_REF_OBJECT_IUNKNOWN_ADD_REF
+ SLANG_REF_OBJECT_IUNKNOWN_RELEASE
+
+ // slang::ICompileRequest
+ virtual SLANG_NO_THROW void SLANG_MCALL setFileSystem(ISlangFileSystem* fileSystem)
+ SLANG_OVERRIDE;
+ virtual SLANG_NO_THROW void SLANG_MCALL setCompileFlags(SlangCompileFlags flags) SLANG_OVERRIDE;
+ virtual SLANG_NO_THROW SlangCompileFlags SLANG_MCALL getCompileFlags() SLANG_OVERRIDE;
+ virtual SLANG_NO_THROW void SLANG_MCALL setDumpIntermediates(int enable) SLANG_OVERRIDE;
+ virtual SLANG_NO_THROW void SLANG_MCALL setDumpIntermediatePrefix(const char* prefix)
+ SLANG_OVERRIDE;
+ virtual SLANG_NO_THROW void SLANG_MCALL setEnableEffectAnnotations(bool value) SLANG_OVERRIDE;
+ virtual SLANG_NO_THROW void SLANG_MCALL setLineDirectiveMode(SlangLineDirectiveMode mode)
+ SLANG_OVERRIDE;
+ virtual SLANG_NO_THROW void SLANG_MCALL setCodeGenTarget(SlangCompileTarget target)
+ SLANG_OVERRIDE;
+ virtual SLANG_NO_THROW int SLANG_MCALL addCodeGenTarget(SlangCompileTarget target)
+ SLANG_OVERRIDE;
+ virtual SLANG_NO_THROW void SLANG_MCALL
+ setTargetProfile(int targetIndex, SlangProfileID profile) SLANG_OVERRIDE;
+ virtual SLANG_NO_THROW void SLANG_MCALL setTargetFlags(int targetIndex, SlangTargetFlags flags)
+ SLANG_OVERRIDE;
+ virtual SLANG_NO_THROW void SLANG_MCALL
+ setTargetFloatingPointMode(int targetIndex, SlangFloatingPointMode mode) SLANG_OVERRIDE;
+ virtual SLANG_NO_THROW void SLANG_MCALL
+ setTargetMatrixLayoutMode(int targetIndex, SlangMatrixLayoutMode mode) SLANG_OVERRIDE;
+ virtual SLANG_NO_THROW void SLANG_MCALL
+ setTargetForceGLSLScalarBufferLayout(int targetIndex, bool value) SLANG_OVERRIDE;
+ virtual SLANG_NO_THROW void SLANG_MCALL setTargetForceDXLayout(int targetIndex, bool value)
+ SLANG_OVERRIDE;
+ virtual SLANG_NO_THROW void SLANG_MCALL
+ setTargetGenerateWholeProgram(int targetIndex, bool value) SLANG_OVERRIDE;
+ virtual SLANG_NO_THROW void SLANG_MCALL setTargetEmbedDownstreamIR(int targetIndex, bool value)
+ SLANG_OVERRIDE;
+ virtual SLANG_NO_THROW void SLANG_MCALL setMatrixLayoutMode(SlangMatrixLayoutMode mode)
+ SLANG_OVERRIDE;
+ virtual SLANG_NO_THROW void SLANG_MCALL setDebugInfoLevel(SlangDebugInfoLevel level)
+ SLANG_OVERRIDE;
+ virtual SLANG_NO_THROW void SLANG_MCALL setOptimizationLevel(SlangOptimizationLevel level)
+ SLANG_OVERRIDE;
+ virtual SLANG_NO_THROW void SLANG_MCALL setOutputContainerFormat(SlangContainerFormat format)
+ SLANG_OVERRIDE;
+ virtual SLANG_NO_THROW void SLANG_MCALL setPassThrough(SlangPassThrough passThrough)
+ SLANG_OVERRIDE;
+ virtual SLANG_NO_THROW void SLANG_MCALL
+ setDiagnosticCallback(SlangDiagnosticCallback callback, void const* userData) SLANG_OVERRIDE;
+ virtual SLANG_NO_THROW void SLANG_MCALL
+ setWriter(SlangWriterChannel channel, ISlangWriter* writer) SLANG_OVERRIDE;
+ virtual SLANG_NO_THROW ISlangWriter* SLANG_MCALL getWriter(SlangWriterChannel channel)
+ SLANG_OVERRIDE;
+ virtual SLANG_NO_THROW void SLANG_MCALL addSearchPath(const char* searchDir) SLANG_OVERRIDE;
+ virtual SLANG_NO_THROW void SLANG_MCALL
+ addPreprocessorDefine(const char* key, const char* value) SLANG_OVERRIDE;
+ virtual SLANG_NO_THROW SlangResult SLANG_MCALL
+ processCommandLineArguments(char const* const* args, int argCount) SLANG_OVERRIDE;
+ virtual SLANG_NO_THROW int SLANG_MCALL
+ addTranslationUnit(SlangSourceLanguage language, char const* name) SLANG_OVERRIDE;
+ virtual SLANG_NO_THROW void SLANG_MCALL setDefaultModuleName(const char* defaultModuleName)
+ SLANG_OVERRIDE;
+ virtual SLANG_NO_THROW void SLANG_MCALL addTranslationUnitPreprocessorDefine(
+ int translationUnitIndex,
+ const char* key,
+ const char* value) SLANG_OVERRIDE;
+ virtual SLANG_NO_THROW void SLANG_MCALL
+ addTranslationUnitSourceFile(int translationUnitIndex, char const* path) SLANG_OVERRIDE;
+ virtual SLANG_NO_THROW void SLANG_MCALL addTranslationUnitSourceString(
+ int translationUnitIndex,
+ char const* path,
+ char const* source) SLANG_OVERRIDE;
+ virtual SLANG_NO_THROW SlangResult SLANG_MCALL addLibraryReference(
+ const char* basePath,
+ const void* libData,
+ size_t libDataSize) SLANG_OVERRIDE;
+ virtual SLANG_NO_THROW void SLANG_MCALL addTranslationUnitSourceStringSpan(
+ int translationUnitIndex,
+ char const* path,
+ char const* sourceBegin,
+ char const* sourceEnd) SLANG_OVERRIDE;
+ virtual SLANG_NO_THROW void SLANG_MCALL addTranslationUnitSourceBlob(
+ int translationUnitIndex,
+ char const* path,
+ ISlangBlob* sourceBlob) SLANG_OVERRIDE;
+ virtual SLANG_NO_THROW int SLANG_MCALL
+ addEntryPoint(int translationUnitIndex, char const* name, SlangStage stage) SLANG_OVERRIDE;
+ virtual SLANG_NO_THROW int SLANG_MCALL addEntryPointEx(
+ int translationUnitIndex,
+ char const* name,
+ SlangStage stage,
+ int genericArgCount,
+ char const** genericArgs) SLANG_OVERRIDE;
+ virtual SLANG_NO_THROW SlangResult SLANG_MCALL
+ setGlobalGenericArgs(int genericArgCount, char const** genericArgs) SLANG_OVERRIDE;
+ virtual SLANG_NO_THROW SlangResult SLANG_MCALL
+ setTypeNameForGlobalExistentialTypeParam(int slotIndex, char const* typeName) SLANG_OVERRIDE;
+ virtual SLANG_NO_THROW SlangResult SLANG_MCALL setTypeNameForEntryPointExistentialTypeParam(
+ int entryPointIndex,
+ int slotIndex,
+ char const* typeName) SLANG_OVERRIDE;
+ virtual SLANG_NO_THROW void SLANG_MCALL setAllowGLSLInput(bool value) SLANG_OVERRIDE;
+ virtual SLANG_NO_THROW SlangResult SLANG_MCALL compile() SLANG_OVERRIDE;
+ virtual SLANG_NO_THROW char const* SLANG_MCALL getDiagnosticOutput() SLANG_OVERRIDE;
+ virtual SLANG_NO_THROW SlangResult SLANG_MCALL getDiagnosticOutputBlob(ISlangBlob** outBlob)
+ SLANG_OVERRIDE;
+ virtual SLANG_NO_THROW int SLANG_MCALL getDependencyFileCount() SLANG_OVERRIDE;
+ virtual SLANG_NO_THROW char const* SLANG_MCALL getDependencyFilePath(int index) SLANG_OVERRIDE;
+ virtual SLANG_NO_THROW int SLANG_MCALL getTranslationUnitCount() SLANG_OVERRIDE;
+ virtual SLANG_NO_THROW char const* SLANG_MCALL getEntryPointSource(int entryPointIndex)
+ SLANG_OVERRIDE;
+ virtual SLANG_NO_THROW void const* SLANG_MCALL
+ getEntryPointCode(int entryPointIndex, size_t* outSize) SLANG_OVERRIDE;
+ virtual SLANG_NO_THROW SlangResult SLANG_MCALL getEntryPointCodeBlob(
+ int entryPointIndex,
+ int targetIndex,
+ ISlangBlob** outBlob) SLANG_OVERRIDE;
+ virtual SLANG_NO_THROW SlangResult SLANG_MCALL getEntryPointHostCallable(
+ int entryPointIndex,
+ int targetIndex,
+ ISlangSharedLibrary** outSharedLibrary) SLANG_OVERRIDE;
+ virtual SLANG_NO_THROW SlangResult SLANG_MCALL
+ getTargetCodeBlob(int targetIndex, ISlangBlob** outBlob) SLANG_OVERRIDE;
+ virtual SLANG_NO_THROW SlangResult SLANG_MCALL
+ getTargetHostCallable(int targetIndex, ISlangSharedLibrary** outSharedLibrary) SLANG_OVERRIDE;
+ virtual SLANG_NO_THROW void const* SLANG_MCALL getCompileRequestCode(size_t* outSize)
+ SLANG_OVERRIDE;
+ virtual SLANG_NO_THROW ISlangMutableFileSystem* SLANG_MCALL
+ getCompileRequestResultAsFileSystem() SLANG_OVERRIDE;
+ virtual SLANG_NO_THROW SlangResult SLANG_MCALL getContainerCode(ISlangBlob** outBlob)
+ SLANG_OVERRIDE;
+ virtual SLANG_NO_THROW SlangResult SLANG_MCALL
+ loadRepro(ISlangFileSystem* fileSystem, const void* data, size_t size) SLANG_OVERRIDE;
+ virtual SLANG_NO_THROW SlangResult SLANG_MCALL saveRepro(ISlangBlob** outBlob) SLANG_OVERRIDE;
+ virtual SLANG_NO_THROW SlangResult SLANG_MCALL enableReproCapture() SLANG_OVERRIDE;
+ virtual SLANG_NO_THROW SlangResult SLANG_MCALL getProgram(slang::IComponentType** outProgram)
+ SLANG_OVERRIDE;
+ virtual SLANG_NO_THROW SlangResult SLANG_MCALL
+ getEntryPoint(SlangInt entryPointIndex, slang::IComponentType** outEntryPoint) SLANG_OVERRIDE;
+ virtual SLANG_NO_THROW SlangResult SLANG_MCALL
+ getModule(SlangInt translationUnitIndex, slang::IModule** outModule) SLANG_OVERRIDE;
+ virtual SLANG_NO_THROW SlangResult SLANG_MCALL getSession(slang::ISession** outSession)
+ SLANG_OVERRIDE;
+ virtual SLANG_NO_THROW SlangReflection* SLANG_MCALL getReflection() SLANG_OVERRIDE;
+ virtual SLANG_NO_THROW void SLANG_MCALL setCommandLineCompilerMode() SLANG_OVERRIDE;
+ virtual SLANG_NO_THROW SlangResult SLANG_MCALL
+ addTargetCapability(SlangInt targetIndex, SlangCapabilityID capability) SLANG_OVERRIDE;
+ virtual SLANG_NO_THROW SlangResult SLANG_MCALL
+ getProgramWithEntryPoints(slang::IComponentType** outProgram) SLANG_OVERRIDE;
+ virtual SLANG_NO_THROW SlangResult SLANG_MCALL isParameterLocationUsed(
+ SlangInt entryPointIndex,
+ SlangInt targetIndex,
+ SlangParameterCategory category,
+ SlangUInt spaceIndex,
+ SlangUInt registerIndex,
+ bool& outUsed) SLANG_OVERRIDE;
+ virtual SLANG_NO_THROW void SLANG_MCALL
+ setTargetLineDirectiveMode(SlangInt targetIndex, SlangLineDirectiveMode mode) SLANG_OVERRIDE;
+ virtual SLANG_NO_THROW void SLANG_MCALL
+ overrideDiagnosticSeverity(SlangInt messageID, SlangSeverity overrideSeverity) SLANG_OVERRIDE;
+ virtual SLANG_NO_THROW SlangDiagnosticFlags SLANG_MCALL getDiagnosticFlags() SLANG_OVERRIDE;
+ virtual SLANG_NO_THROW void SLANG_MCALL setDiagnosticFlags(SlangDiagnosticFlags flags)
+ SLANG_OVERRIDE;
+ virtual SLANG_NO_THROW void SLANG_MCALL setDebugInfoFormat(SlangDebugInfoFormat format)
+ SLANG_OVERRIDE;
+ virtual SLANG_NO_THROW void SLANG_MCALL setReportDownstreamTime(bool value) SLANG_OVERRIDE;
+ virtual SLANG_NO_THROW void SLANG_MCALL setReportPerfBenchmark(bool value) SLANG_OVERRIDE;
+ virtual SLANG_NO_THROW void SLANG_MCALL setSkipSPIRVValidation(bool value) SLANG_OVERRIDE;
+ virtual SLANG_NO_THROW void SLANG_MCALL
+ setTargetUseMinimumSlangOptimization(int targetIndex, bool value) SLANG_OVERRIDE;
+ virtual SLANG_NO_THROW void SLANG_MCALL setIgnoreCapabilityCheck(bool value) SLANG_OVERRIDE;
+ virtual SLANG_NO_THROW SlangResult SLANG_MCALL
+ getCompileTimeProfile(ISlangProfiler** compileTimeProfile, bool isClear) SLANG_OVERRIDE;
+
+ void setTrackLiveness(bool v);
+
+ EndToEndCompileRequest(Session* session);
+
+ EndToEndCompileRequest(Linkage* linkage);
+
+ ~EndToEndCompileRequest();
+
+ // If enabled will emit IR
+ bool m_emitIr = false;
+
+ // What container format are we being asked to generate?
+ // If it's set to a format, the container blob will be calculated during compile
+ ContainerFormat m_containerFormat = ContainerFormat::None;
+
+ /// Where the container is stored. This is calculated as part of compile if m_containerFormat is
+ /// set to a supported format.
+ ComPtr<IArtifact> m_containerArtifact;
+ /// Holds the container as a file system
+ ComPtr<ISlangMutableFileSystem> m_containerFileSystem;
+
+ /// File system used by repro system if a file couldn't be found within the repro (or associated
+ /// directory)
+ ComPtr<ISlangFileSystem> m_reproFallbackFileSystem =
+ ComPtr<ISlangFileSystem>(OSFileSystem::getExtSingleton());
+
+ // Path to output container to
+ String m_containerOutputPath;
+
+ // Should we just pass the input to another compiler?
+ PassThroughMode m_passThrough = PassThroughMode::None;
+
+ /// If output should be source embedded, define the style of the embedding
+ SourceEmbedUtil::Style m_sourceEmbedStyle = SourceEmbedUtil::Style::None;
+ /// The language to be used for source embedding
+ SourceLanguage m_sourceEmbedLanguage = SourceLanguage::C;
+ /// Source embed variable name. Note may be used as a basis for names if multiple items written
+ String m_sourceEmbedName;
+
+ /// Source code for the specialization arguments to use for the global specialization parameters
+ /// of the program.
+ List<String> m_globalSpecializationArgStrings;
+
+ // Are we being driven by the command-line `slangc`, and should act accordingly?
+ bool m_isCommandLineCompile = false;
+
+ String m_diagnosticOutput;
+
+ /// A blob holding the diagnostic output
+ ComPtr<ISlangBlob> m_diagnosticOutputBlob;
+
+ /// Per-entry-point information not tracked by other compile requests
+ class EntryPointInfo : public RefObject
+ {
+ public:
+ /// Source code for the specialization arguments to use for the specialization parameters of
+ /// the entry point.
+ List<String> specializationArgStrings;
+ };
+ List<EntryPointInfo> m_entryPoints;
+
+ /// Per-target information only needed for command-line compiles
+ class TargetInfo : public RefObject
+ {
+ public:
+ // Requested output paths for each entry point.
+ // An empty string indices no output desired for
+ // the given entry point.
+ Dictionary<Int, String> entryPointOutputPaths;
+ String wholeTargetOutputPath;
+ CompilerOptionSet targetOptions;
+ };
+ Dictionary<TargetRequest*, RefPtr<TargetInfo>> m_targetInfos;
+
+ CompilerOptionSet m_optionSetForDefaultTarget;
+
+ CompilerOptionSet& getTargetOptionSet(TargetRequest* req);
+
+ CompilerOptionSet& getTargetOptionSet(Index targetIndex);
+
+ String m_dependencyOutputPath;
+
+ /// Writes the modules in a container to the stream
+ SlangResult writeContainerToStream(Stream* stream);
+
+ /// If a container format has been specified produce a container (stored in m_containerBlob)
+ SlangResult maybeCreateContainer();
+ /// If a container has been constructed and the filename/path has contents will try to write
+ /// the container contents to the file
+ SlangResult maybeWriteContainer(const String& fileName);
+
+ Linkage* getLinkage() { return m_linkage; }
+
+ int addEntryPoint(
+ int translationUnitIndex,
+ String const& name,
+ Profile profile,
+ List<String> const& genericTypeNames);
+
+ void setWriter(WriterChannel chan, ISlangWriter* writer);
+ ISlangWriter* getWriter(WriterChannel chan) const
+ {
+ return m_writers->getWriter(SlangWriterChannel(chan));
+ }
+
+ /// The end to end request can be passed as nullptr, if not driven by one
+ SlangResult executeActionsInner();
+ SlangResult executeActions();
+
+ Session* getSession() { return m_session; }
+ DiagnosticSink* getSink() { return &m_sink; }
+ NamePool* getNamePool() { return getLinkage()->getNamePool(); }
+
+ FrontEndCompileRequest* getFrontEndReq() { return m_frontEndReq; }
+
+ ComponentType* getUnspecializedGlobalComponentType()
+ {
+ return getFrontEndReq()->getGlobalComponentType();
+ }
+ ComponentType* getUnspecializedGlobalAndEntryPointsComponentType()
+ {
+ return getFrontEndReq()->getGlobalAndEntryPointsComponentType();
+ }
+
+ ComponentType* getSpecializedGlobalComponentType() { return m_specializedGlobalComponentType; }
+ ComponentType* getSpecializedGlobalAndEntryPointsComponentType()
+ {
+ return m_specializedGlobalAndEntryPointsComponentType;
+ }
+
+ ComponentType* getSpecializedEntryPointComponentType(Index index)
+ {
+ return m_specializedEntryPoints[index];
+ }
+
+ void writeArtifactToStandardOutput(IArtifact* artifact, DiagnosticSink* sink);
+
+ void generateOutput();
+
+ CompilerOptionSet& getOptionSet() { return m_linkage->m_optionSet; }
+
+private:
+ String _getWholeProgramPath(TargetRequest* targetReq);
+ String _getEntryPointPath(TargetRequest* targetReq, Index entryPointIndex);
+
+ /// Maybe write the artifact to the path (if set), or stdout (if there is no container or path)
+ SlangResult _maybeWriteArtifact(const String& path, IArtifact* artifact);
+ SlangResult _maybeWriteDebugArtifact(
+ TargetProgram* targetProgram,
+ const String& path,
+ IArtifact* artifact);
+ SlangResult _writeArtifact(const String& path, IArtifact* artifact);
+
+ /// Adds any extra settings to complete a targetRequest
+ void _completeTargetRequest(UInt targetIndex);
+
+ ISlangUnknown* getInterface(const Guid& guid);
+
+ void generateOutput(ComponentType* program);
+ void generateOutput(TargetProgram* targetProgram);
+
+ void init();
+
+ Session* m_session = nullptr;
+ RefPtr<Linkage> m_linkage;
+ DiagnosticSink m_sink;
+ RefPtr<FrontEndCompileRequest> m_frontEndReq;
+ RefPtr<ComponentType> m_specializedGlobalComponentType;
+ RefPtr<ComponentType> m_specializedGlobalAndEntryPointsComponentType;
+ List<RefPtr<ComponentType>> m_specializedEntryPoints;
+
+ // For output
+
+ RefPtr<StdWriters> m_writers;
+};
+
+} // namespace Slang
diff --git a/source/slang/slang-entry-point.cpp b/source/slang/slang-entry-point.cpp
new file mode 100644
index 000000000..0cce90646
--- /dev/null
+++ b/source/slang/slang-entry-point.cpp
@@ -0,0 +1,159 @@
+// slang-entry-point.cpp
+#include "slang-entry-point.h"
+
+#include "slang-compiler.h"
+#include "slang-mangle.h"
+
+namespace Slang
+{
+
+//
+// EntryPoint
+//
+
+ISlangUnknown* EntryPoint::getInterface(const Guid& guid)
+{
+ if (guid == slang::IEntryPoint::getTypeGuid())
+ return static_cast<slang::IEntryPoint*>(this);
+
+ return Super::getInterface(guid);
+}
+
+RefPtr<EntryPoint> EntryPoint::create(
+ Linkage* linkage,
+ DeclRef<FuncDecl> funcDeclRef,
+ Profile profile)
+{
+ RefPtr<EntryPoint> entryPoint =
+ new EntryPoint(linkage, funcDeclRef.getName(), profile, funcDeclRef);
+ entryPoint->m_mangledName = getMangledName(linkage->getASTBuilder(), funcDeclRef);
+ return entryPoint;
+}
+
+RefPtr<EntryPoint> EntryPoint::createDummyForPassThrough(
+ Linkage* linkage,
+ Name* name,
+ Profile profile)
+{
+ RefPtr<EntryPoint> entryPoint = new EntryPoint(linkage, name, profile, DeclRef<FuncDecl>());
+ return entryPoint;
+}
+
+RefPtr<EntryPoint> EntryPoint::createDummyForDeserialize(
+ Linkage* linkage,
+ Name* name,
+ Profile profile,
+ String mangledName)
+{
+ RefPtr<EntryPoint> entryPoint = new EntryPoint(linkage, name, profile, DeclRef<FuncDecl>());
+ entryPoint->m_mangledName = mangledName;
+ return entryPoint;
+}
+
+EntryPoint::EntryPoint(Linkage* linkage, Name* name, Profile profile, DeclRef<FuncDecl> funcDeclRef)
+ : ComponentType(linkage), m_name(name), m_profile(profile), m_funcDeclRef(funcDeclRef)
+{
+ // Collect any specialization parameters used by the entry point
+ //
+ _collectShaderParams();
+}
+
+Module* EntryPoint::getModule()
+{
+ return Slang::getModule(getFuncDecl());
+}
+
+Index EntryPoint::getSpecializationParamCount()
+{
+ return m_genericSpecializationParams.getCount() + m_existentialSpecializationParams.getCount();
+}
+
+SpecializationParam const& EntryPoint::getSpecializationParam(Index index)
+{
+ auto genericParamCount = m_genericSpecializationParams.getCount();
+ if (index < genericParamCount)
+ {
+ return m_genericSpecializationParams[index];
+ }
+ else
+ {
+ return m_existentialSpecializationParams[index - genericParamCount];
+ }
+}
+
+Index EntryPoint::getRequirementCount()
+{
+ // The only requirement of an entry point is the module that contains it.
+ //
+ // TODO: We will eventually want to support the case of an entry
+ // point nested in a `struct` type, in which case there should be
+ // a single requirement representing that outer type (so that multiple
+ // entry points nested under the same type can share the storage
+ // for parameters at that scope).
+
+ // Note: the defensive coding is here because the
+ // "dummy" entry points we create for pass-through
+ // compilation will not have an associated module.
+ //
+ if (const auto module = getModule())
+ {
+ return 1;
+ }
+ return 0;
+}
+
+RefPtr<ComponentType> EntryPoint::getRequirement(Index index)
+{
+ SLANG_UNUSED(index);
+ SLANG_ASSERT(index == 0);
+ SLANG_ASSERT(getModule());
+ return getModule();
+}
+
+String EntryPoint::getEntryPointMangledName(Index index)
+{
+ SLANG_UNUSED(index);
+ SLANG_ASSERT(index == 0);
+
+ return m_mangledName;
+}
+
+String EntryPoint::getEntryPointNameOverride(Index index)
+{
+ SLANG_UNUSED(index);
+ SLANG_ASSERT(index == 0);
+
+ return m_name ? m_name->text : "";
+}
+
+void EntryPoint::acceptVisitor(
+ ComponentTypeVisitor* visitor,
+ SpecializationInfo* specializationInfo)
+{
+ visitor->visitEntryPoint(this, as<EntryPointSpecializationInfo>(specializationInfo));
+}
+
+void EntryPoint::buildHash(DigestBuilder<SHA1>& builder)
+{
+ SLANG_UNUSED(builder);
+}
+
+List<Module*> const& EntryPoint::getModuleDependencies()
+{
+ if (auto module = getModule())
+ return module->getModuleDependencies();
+
+ static List<Module*> empty;
+ return empty;
+}
+
+List<SourceFile*> const& EntryPoint::getFileDependencies()
+{
+ if (const auto module = getModule())
+ return getModule()->getFileDependencies();
+
+ static List<SourceFile*> empty;
+ return empty;
+}
+
+} // namespace Slang
diff --git a/source/slang/slang-entry-point.h b/source/slang/slang-entry-point.h
new file mode 100644
index 000000000..16499a542
--- /dev/null
+++ b/source/slang/slang-entry-point.h
@@ -0,0 +1,333 @@
+// slang-entry-point.h
+#pragma once
+
+//
+// This file provides the `EntryPoint` type, which is used
+// to represent an entry point that has been identified
+// and validated by the compiler front-end inside of some
+// `Module`.
+//
+// Note that the `FrontEndEntryPointRequest` type, corresponding
+// to use of the `-entry` command-line option, is not
+// declared here, and instead comes from `slang-compile-request.h`.
+//
+
+#include "slang-linkable.h"
+
+namespace Slang
+{
+
+/// Describes an entry point for the purposes of layout and code generation.
+///
+/// This type intentionally does not distinguish between entry
+/// points that were discovered because of a `[shader(...)]` attribute
+/// and those that were identified by a `FrontEndEntryPointRequest`
+/// (e.g., as a result of a `-entry` command-line option). The
+/// intention is that *how* an `EntryPoint` came to be is a purely
+/// front-end consideration, and the back-end of the compiler should
+/// never depend on that information.
+///
+/// This class also tracks any generic arguments to the entry point,
+/// in the case that it is a specialization of a generic entry point.
+///
+/// There is also a provision for creating a "dummy" entry point for
+/// the purposes of pass-through compilation modes. Only the
+/// `getName()` and `getProfile()` methods should be expected to
+/// return useful data on pass-through entry points.
+///
+class EntryPoint : public ComponentType, public slang::IEntryPoint
+{
+ typedef ComponentType Super;
+
+public:
+ SLANG_REF_OBJECT_IUNKNOWN_ALL
+
+ ISlangUnknown* getInterface(const Guid& guid);
+
+
+ // Forward `IComponentType` methods
+
+ SLANG_NO_THROW slang::ISession* SLANG_MCALL getSession() SLANG_OVERRIDE
+ {
+ return Super::getSession();
+ }
+
+ SLANG_NO_THROW slang::ProgramLayout* SLANG_MCALL
+ getLayout(SlangInt targetIndex, slang::IBlob** outDiagnostics) SLANG_OVERRIDE
+ {
+ return Super::getLayout(targetIndex, outDiagnostics);
+ }
+
+ SLANG_NO_THROW SlangResult SLANG_MCALL getEntryPointCode(
+ SlangInt entryPointIndex,
+ SlangInt targetIndex,
+ slang::IBlob** outCode,
+ slang::IBlob** outDiagnostics) SLANG_OVERRIDE
+ {
+ return Super::getEntryPointCode(entryPointIndex, targetIndex, outCode, outDiagnostics);
+ }
+
+ SLANG_NO_THROW SlangResult SLANG_MCALL getTargetCode(
+ SlangInt targetIndex,
+ slang::IBlob** outCode,
+ slang::IBlob** outDiagnostics) SLANG_OVERRIDE
+ {
+ return Super::getTargetCode(targetIndex, outCode, outDiagnostics);
+ }
+
+ SLANG_NO_THROW SlangResult SLANG_MCALL getEntryPointMetadata(
+ SlangInt entryPointIndex,
+ SlangInt targetIndex,
+ slang::IMetadata** outMetadata,
+ slang::IBlob** outDiagnostics) SLANG_OVERRIDE
+ {
+ return Super::getEntryPointMetadata(
+ entryPointIndex,
+ targetIndex,
+ outMetadata,
+ outDiagnostics);
+ }
+
+ SLANG_NO_THROW SlangResult SLANG_MCALL getTargetMetadata(
+ SlangInt targetIndex,
+ slang::IMetadata** outMetadata,
+ slang::IBlob** outDiagnostics) SLANG_OVERRIDE
+ {
+ return Super::getTargetMetadata(targetIndex, outMetadata, outDiagnostics);
+ }
+
+ SLANG_NO_THROW SlangResult SLANG_MCALL getEntryPointCompileResult(
+ SlangInt entryPointIndex,
+ SlangInt targetIndex,
+ slang::ICompileResult** outCompileResult,
+ slang::IBlob** outDiagnostics) SLANG_OVERRIDE
+ {
+ return Super::getEntryPointCompileResult(
+ entryPointIndex,
+ targetIndex,
+ outCompileResult,
+ outDiagnostics);
+ }
+
+ SLANG_NO_THROW SlangResult SLANG_MCALL getTargetCompileResult(
+ SlangInt targetIndex,
+ slang::ICompileResult** outCompileResult,
+ slang::IBlob** outDiagnostics) SLANG_OVERRIDE
+ {
+ return Super::getTargetCompileResult(targetIndex, outCompileResult, outDiagnostics);
+ }
+
+ SLANG_NO_THROW SlangResult SLANG_MCALL getResultAsFileSystem(
+ SlangInt entryPointIndex,
+ SlangInt targetIndex,
+ ISlangMutableFileSystem** outFileSystem) SLANG_OVERRIDE
+ {
+ return Super::getResultAsFileSystem(entryPointIndex, targetIndex, outFileSystem);
+ }
+
+ SLANG_NO_THROW SlangResult SLANG_MCALL specialize(
+ slang::SpecializationArg const* specializationArgs,
+ SlangInt specializationArgCount,
+ slang::IComponentType** outSpecializedComponentType,
+ ISlangBlob** outDiagnostics) SLANG_OVERRIDE
+ {
+ return Super::specialize(
+ specializationArgs,
+ specializationArgCount,
+ outSpecializedComponentType,
+ outDiagnostics);
+ }
+
+ SLANG_NO_THROW SlangResult SLANG_MCALL
+ renameEntryPoint(const char* newName, slang::IComponentType** outEntryPoint) SLANG_OVERRIDE
+ {
+ return Super::renameEntryPoint(newName, outEntryPoint);
+ }
+
+ SLANG_NO_THROW SlangResult SLANG_MCALL
+ link(slang::IComponentType** outLinkedComponentType, ISlangBlob** outDiagnostics) SLANG_OVERRIDE
+ {
+ return Super::link(outLinkedComponentType, outDiagnostics);
+ }
+
+ virtual SLANG_NO_THROW SlangResult SLANG_MCALL linkWithOptions(
+ slang::IComponentType** outLinkedComponentType,
+ uint32_t count,
+ slang::CompilerOptionEntry* entries,
+ ISlangBlob** outDiagnostics) override
+ {
+ return Super::linkWithOptions(outLinkedComponentType, count, entries, outDiagnostics);
+ }
+
+ SLANG_NO_THROW SlangResult SLANG_MCALL getEntryPointHostCallable(
+ int entryPointIndex,
+ int targetIndex,
+ ISlangSharedLibrary** outSharedLibrary,
+ slang::IBlob** outDiagnostics) SLANG_OVERRIDE
+ {
+ return Super::getEntryPointHostCallable(
+ entryPointIndex,
+ targetIndex,
+ outSharedLibrary,
+ outDiagnostics);
+ }
+
+ SLANG_NO_THROW void SLANG_MCALL getEntryPointHash(
+ SlangInt entryPointIndex,
+ SlangInt targetIndex,
+ slang::IBlob** outHash) SLANG_OVERRIDE
+ {
+ return Super::getEntryPointHash(entryPointIndex, targetIndex, outHash);
+ }
+
+ virtual void buildHash(DigestBuilder<SHA1>& builder) SLANG_OVERRIDE;
+
+ /// Create an entry point that refers to the given function.
+ static RefPtr<EntryPoint> create(
+ Linkage* linkage,
+ DeclRef<FuncDecl> funcDeclRef,
+ Profile profile);
+
+ /// Get the function decl-ref, including any generic arguments.
+ DeclRef<FuncDecl> getFuncDeclRef() { return m_funcDeclRef; }
+
+ /// Get the function declaration (without generic arguments).
+ FuncDecl* getFuncDecl() { return m_funcDeclRef.getDecl(); }
+
+ /// Get the name of the entry point
+ Name* getName() { return m_name; }
+
+ /// Get the profile associated with the entry point
+ ///
+ /// Note: only the stage part of the profile is expected
+ /// to contain useful data, but certain legacy code paths
+ /// allow for "shader model" information to come via this path.
+ ///
+ Profile getProfile() { return m_profile; }
+
+ /// Get the stage that the entry point is for.
+ Stage getStage() { return m_profile.getStage(); }
+
+ /// Get the module that contains the entry point.
+ Module* getModule();
+
+ /// Get a list of modules that this entry point depends on.
+ ///
+ /// This will include the module that defines the entry point (see `getModule()`),
+ /// but may also include modules that are required by its generic type arguments.
+ ///
+ List<Module*> const& getModuleDependencies()
+ SLANG_OVERRIDE; // { return getModule()->getModuleDependencies(); }
+ List<SourceFile*> const& getFileDependencies()
+ SLANG_OVERRIDE; // { return getModule()->getFileDependencies(); }
+
+ /// Create a dummy `EntryPoint` that is only usable for pass-through compilation.
+ static RefPtr<EntryPoint> createDummyForPassThrough(
+ Linkage* linkage,
+ Name* name,
+ Profile profile);
+
+ /// Create a dummy `EntryPoint` that stands in for a serialized entry point
+ static RefPtr<EntryPoint> createDummyForDeserialize(
+ Linkage* linkage,
+ Name* name,
+ Profile profile,
+ String mangledName);
+
+ /// Get the number of existential type parameters for the entry point.
+ SLANG_NO_THROW Index SLANG_MCALL getSpecializationParamCount() SLANG_OVERRIDE;
+
+ /// Get the existential type parameter at `index`.
+ SpecializationParam const& getSpecializationParam(Index index) SLANG_OVERRIDE;
+
+ Index getRequirementCount() SLANG_OVERRIDE;
+ RefPtr<ComponentType> getRequirement(Index index) SLANG_OVERRIDE;
+
+ SpecializationParams const& getExistentialSpecializationParams()
+ {
+ return m_existentialSpecializationParams;
+ }
+
+ Index getGenericSpecializationParamCount() { return m_genericSpecializationParams.getCount(); }
+ Index getExistentialSpecializationParamCount()
+ {
+ return m_existentialSpecializationParams.getCount();
+ }
+
+ /// Get an array of all entry-point shader parameters.
+ List<ShaderParamInfo> const& getShaderParams() { return m_shaderParams; }
+
+ Index getEntryPointCount() SLANG_OVERRIDE { return 1; };
+ RefPtr<EntryPoint> getEntryPoint(Index index) SLANG_OVERRIDE
+ {
+ SLANG_UNUSED(index);
+ return this;
+ }
+ String getEntryPointMangledName(Index index) SLANG_OVERRIDE;
+ String getEntryPointNameOverride(Index index) SLANG_OVERRIDE;
+
+ Index getShaderParamCount() SLANG_OVERRIDE { return 0; }
+ ShaderParamInfo getShaderParam(Index index) SLANG_OVERRIDE
+ {
+ SLANG_UNUSED(index);
+ return ShaderParamInfo();
+ }
+
+ class EntryPointSpecializationInfo : public SpecializationInfo
+ {
+ public:
+ DeclRef<FuncDecl> specializedFuncDeclRef;
+ List<ExpandedSpecializationArg> existentialSpecializationArgs;
+ };
+
+ SLANG_NO_THROW slang::FunctionReflection* SLANG_MCALL getFunctionReflection() SLANG_OVERRIDE
+ {
+ return (slang::FunctionReflection*)m_funcDeclRef.declRefBase;
+ }
+
+protected:
+ void acceptVisitor(ComponentTypeVisitor* visitor, SpecializationInfo* specializationInfo)
+ SLANG_OVERRIDE;
+
+ RefPtr<SpecializationInfo> _validateSpecializationArgsImpl(
+ SpecializationArg const* args,
+ Index argCount,
+ DiagnosticSink* sink) SLANG_OVERRIDE;
+
+private:
+ EntryPoint(Linkage* linkage, Name* name, Profile profile, DeclRef<FuncDecl> funcDeclRef);
+
+ void _collectGenericSpecializationParamsRec(Decl* decl);
+ void _collectShaderParams();
+
+ // The name of the entry point function (e.g., `main`)
+ //
+ Name* m_name = nullptr;
+
+ // The declaration of the entry-point function itself.
+ //
+ DeclRef<FuncDecl> m_funcDeclRef;
+
+ /// The mangled name of the entry point function
+ String m_mangledName;
+
+ SpecializationParams m_genericSpecializationParams;
+ SpecializationParams m_existentialSpecializationParams;
+
+ /// Information about entry-point parameters
+ List<ShaderParamInfo> m_shaderParams;
+
+ // The profile that the entry point will be compiled for
+ // (this is a combination of the target stage, and also
+ // a feature level that sets capabilities)
+ //
+ // Note: the profile-version part of this should probably
+ // be moving towards deprecation, in favor of the version
+ // information (e.g., "Shader Model 5.1") always coming
+ // from the target, while the stage part is all that is
+ // intrinsic to the entry point.
+ //
+ Profile m_profile;
+};
+
+} // namespace Slang
diff --git a/source/slang/slang-global-session.cpp b/source/slang/slang-global-session.cpp
new file mode 100644
index 000000000..7b922dcf0
--- /dev/null
+++ b/source/slang/slang-global-session.cpp
@@ -0,0 +1,1217 @@
+// slang-global-session.cpp
+#include "slang-global-session.h"
+
+#include "compiler-core/slang-artifact-desc-util.h"
+#include "core/slang-archive-file-system.h"
+#include "core/slang-performance-profiler.h"
+#include "core/slang-type-convert-util.h"
+#include "slang-check-impl.h"
+#include "slang-compiler.h"
+#include "slang-doc-ast.h"
+#include "slang-doc-markdown-writer.h"
+#include "slang-options.h"
+#include "slang-parser.h"
+#include "slang-serialize-ast.h"
+#include "slang-serialize-container.h"
+#include "slang-serialize-ir.h"
+
+extern Slang::String get_slang_cuda_prelude();
+extern Slang::String get_slang_cpp_prelude();
+extern Slang::String get_slang_hlsl_prelude();
+
+namespace Slang
+{
+
+void Session::init()
+{
+ SLANG_ASSERT(BaseTypeInfo::check());
+
+#if SLANG_ENABLE_IR_BREAK_ALLOC
+ // Read environment variable for IR debugging
+ StringBuilder irBreakEnv;
+ if (SLANG_SUCCEEDED(PlatformUtil::getEnvironmentVariable(
+ UnownedStringSlice("SLANG_DEBUG_IR_BREAK"),
+ irBreakEnv)))
+ {
+ String envValue = irBreakEnv.produceString();
+ if (envValue.getLength())
+ {
+ _slangIRAllocBreak = stringToInt(envValue);
+ _slangIRPrintStackAtBreak = true;
+ }
+ }
+#endif
+
+ _initCodeGenTransitionMap();
+
+ ::memset(m_downstreamCompilerLocators, 0, sizeof(m_downstreamCompilerLocators));
+ DownstreamCompilerUtil::setDefaultLocators(m_downstreamCompilerLocators);
+ m_downstreamCompilerSet = new DownstreamCompilerSet;
+
+ m_completionTokenName = getNamePool()->getName("#?");
+
+ m_sharedLibraryLoader = DefaultSharedLibraryLoader::getSingleton();
+
+ // Set up the command line options
+ initCommandOptions(m_commandOptions);
+
+ // Set up shared AST builder
+ m_sharedASTBuilder = new SharedASTBuilder;
+ m_sharedASTBuilder->init(this);
+
+ // And the global ASTBuilder
+ auto builtinAstBuilder = m_sharedASTBuilder->getInnerASTBuilder();
+ globalAstBuilder = builtinAstBuilder;
+
+ // Make sure our source manager is initialized
+ builtinSourceManager.initialize(nullptr, nullptr);
+
+ // Built in linkage uses the built in builder
+ m_builtinLinkage = new Linkage(this, builtinAstBuilder, nullptr);
+ m_builtinLinkage->m_optionSet.set(CompilerOptionName::DebugInformation, DebugInfoLevel::None);
+
+ // Because the `Session` retains the builtin `Linkage`,
+ // we need to make sure that the parent pointer inside
+ // `Linkage` doesn't create a retain cycle.
+ //
+ // This operation ensures that the parent pointer will
+ // just be a raw pointer, so that the builtin linkage
+ // doesn't keep the parent session alive.
+ //
+ m_builtinLinkage->_stopRetainingParentSession();
+
+ // Create scopes for various language builtins.
+ //
+ // TODO: load these on-demand to avoid parsing
+ // the core module code for languages the user won't use.
+
+ baseLanguageScope = builtinAstBuilder->create<Scope>();
+
+ // Will stay in scope as long as ASTBuilder
+ baseModuleDecl =
+ populateBaseLanguageModule(m_builtinLinkage->getASTBuilder(), baseLanguageScope);
+
+ coreLanguageScope = builtinAstBuilder->create<Scope>();
+ coreLanguageScope->nextSibling = baseLanguageScope;
+
+ hlslLanguageScope = builtinAstBuilder->create<Scope>();
+ hlslLanguageScope->nextSibling = coreLanguageScope;
+
+ slangLanguageScope = builtinAstBuilder->create<Scope>();
+ slangLanguageScope->nextSibling = hlslLanguageScope;
+
+ glslLanguageScope = builtinAstBuilder->create<Scope>();
+ glslLanguageScope->nextSibling = slangLanguageScope;
+
+ glslModuleName = getNameObj("glsl");
+
+ {
+ for (Index i = 0; i < Index(SourceLanguage::CountOf); ++i)
+ {
+ m_defaultDownstreamCompilers[i] = PassThroughMode::None;
+ }
+ m_defaultDownstreamCompilers[Index(SourceLanguage::C)] = PassThroughMode::GenericCCpp;
+ m_defaultDownstreamCompilers[Index(SourceLanguage::CPP)] = PassThroughMode::GenericCCpp;
+ m_defaultDownstreamCompilers[Index(SourceLanguage::CUDA)] = PassThroughMode::NVRTC;
+ }
+
+ // Set up default prelude code for target languages that need a prelude
+ m_languagePreludes[Index(SourceLanguage::CUDA)] = get_slang_cuda_prelude();
+ m_languagePreludes[Index(SourceLanguage::CPP)] = get_slang_cpp_prelude();
+ m_languagePreludes[Index(SourceLanguage::HLSL)] = get_slang_hlsl_prelude();
+
+ if (!spirvCoreGrammarInfo)
+ spirvCoreGrammarInfo = SPIRVCoreGrammarInfo::getEmbeddedVersion();
+}
+
+Session::~Session()
+{
+ // This is necessary because this ASTBuilder uses the SharedASTBuilder also owned by the
+ // session. If the SharedASTBuilder gets dtored before the globalASTBuilder it has a dangling
+ // pointer, which is referenced in the ASTBuilder dtor (likely) causing a crash.
+ //
+ // By destroying first we know it is destroyed, before the SharedASTBuilder.
+ globalAstBuilder.setNull();
+
+ // destroy modules next
+ coreModules = decltype(coreModules)();
+}
+
+
+Module* Session::getBuiltinModule(slang::BuiltinModuleName name)
+{
+ auto info = getBuiltinModuleInfo(name);
+ auto builtinLinkage = getBuiltinLinkage();
+ auto moduleNameObj = builtinLinkage->getNamePool()->getName(info.name);
+ RefPtr<Module> module;
+ if (builtinLinkage->mapNameToLoadedModules.tryGetValue(moduleNameObj, module))
+ return module.get();
+ return nullptr;
+}
+
+void Session::_initCodeGenTransitionMap()
+{
+ // TODO(JS): Might want to do something about these in the future...
+
+ // PassThroughMode getDownstreamCompilerRequiredForTarget(CodeGenTarget target);
+ // SourceLanguage getDefaultSourceLanguageForDownstreamCompiler(PassThroughMode compiler);
+
+ // Set up the default ways to do compilations between code gen targets
+ auto& map = m_codeGenTransitionMap;
+
+ // TODO(JS): There currently isn't a 'downstream compiler' for direct spirv output. If we did
+ // it would presumably a transition from SlangIR to SPIRV.
+
+ // For C and C++ we default to use the 'genericCCpp' compiler
+ {
+ const CodeGenTarget sources[] = {CodeGenTarget::CSource, CodeGenTarget::CPPSource};
+ for (auto source : sources)
+ {
+ // We *don't* add a default for host callable, as we will determine what is suitable
+ // depending on what is available. We prefer LLVM if that's available. If it's not we
+ // can use generic C/C++ compiler
+
+ map.addTransition(
+ source,
+ CodeGenTarget::ShaderSharedLibrary,
+ PassThroughMode::GenericCCpp);
+ map.addTransition(
+ source,
+ CodeGenTarget::HostSharedLibrary,
+ PassThroughMode::GenericCCpp);
+ map.addTransition(source, CodeGenTarget::HostExecutable, PassThroughMode::GenericCCpp);
+ map.addTransition(source, CodeGenTarget::ObjectCode, PassThroughMode::GenericCCpp);
+ }
+ }
+
+
+ // Add all the straightforward transitions
+ map.addTransition(CodeGenTarget::CUDASource, CodeGenTarget::PTX, PassThroughMode::NVRTC);
+ map.addTransition(CodeGenTarget::HLSL, CodeGenTarget::DXBytecode, PassThroughMode::Fxc);
+ map.addTransition(CodeGenTarget::HLSL, CodeGenTarget::DXIL, PassThroughMode::Dxc);
+ map.addTransition(CodeGenTarget::GLSL, CodeGenTarget::SPIRV, PassThroughMode::Glslang);
+ map.addTransition(CodeGenTarget::Metal, CodeGenTarget::MetalLib, PassThroughMode::MetalC);
+ map.addTransition(CodeGenTarget::WGSL, CodeGenTarget::WGSLSPIRV, PassThroughMode::Tint);
+ // To assembly
+ map.addTransition(CodeGenTarget::SPIRV, CodeGenTarget::SPIRVAssembly, PassThroughMode::Glslang);
+ // We use glslang to turn SPIR-V into SPIR-V assembly.
+ map.addTransition(
+ CodeGenTarget::WGSLSPIRV,
+ CodeGenTarget::WGSLSPIRVAssembly,
+ PassThroughMode::Glslang);
+ map.addTransition(CodeGenTarget::DXIL, CodeGenTarget::DXILAssembly, PassThroughMode::Dxc);
+ map.addTransition(
+ CodeGenTarget::DXBytecode,
+ CodeGenTarget::DXBytecodeAssembly,
+ PassThroughMode::Fxc);
+ map.addTransition(
+ CodeGenTarget::MetalLib,
+ CodeGenTarget::MetalLibAssembly,
+ PassThroughMode::MetalC);
+}
+
+void Session::addBuiltins(char const* sourcePath, char const* source)
+{
+ auto sourceBlob = StringBlob::moveCreate(String(source));
+
+ // TODO(tfoley): Add ability to directly new builtins to the appropriate scope
+ Module* module = nullptr;
+ addBuiltinSource(coreLanguageScope, sourcePath, sourceBlob, module);
+ if (module)
+ coreModules.add(module);
+}
+
+void Session::setSharedLibraryLoader(ISlangSharedLibraryLoader* loader)
+{
+ // External API allows passing of nullptr to reset the loader
+ loader = loader ? loader : DefaultSharedLibraryLoader::getSingleton();
+
+ _setSharedLibraryLoader(loader);
+}
+
+ISlangSharedLibraryLoader* Session::getSharedLibraryLoader()
+{
+ return (m_sharedLibraryLoader == DefaultSharedLibraryLoader::getSingleton())
+ ? nullptr
+ : m_sharedLibraryLoader.get();
+}
+
+SlangResult Session::checkCompileTargetSupport(SlangCompileTarget inTarget)
+{
+ auto target = CodeGenTarget(inTarget);
+
+ const PassThroughMode mode = getDownstreamCompilerRequiredForTarget(target);
+ return (mode != PassThroughMode::None) ? checkPassThroughSupport(SlangPassThrough(mode))
+ : SLANG_OK;
+}
+
+SlangResult Session::checkPassThroughSupport(SlangPassThrough inPassThrough)
+{
+ return checkExternalCompilerSupport(this, PassThroughMode(inPassThrough));
+}
+
+void Session::writeCoreModuleDoc(String config)
+{
+ ASTBuilder* astBuilder = getBuiltinLinkage()->getASTBuilder();
+ SourceManager* sourceManager = getBuiltinSourceManager();
+
+ DiagnosticSink sink(sourceManager, Lexer::sourceLocationLexer);
+
+ List<String> docStrings;
+
+ // For all the modules add their doc output to docStrings
+ for (Module* m : coreModules)
+ {
+ RefPtr<ASTMarkup> markup(new ASTMarkup);
+ ASTMarkupUtil::extract(m->getModuleDecl(), sourceManager, &sink, markup);
+
+ DocMarkdownWriter writer(markup, astBuilder, &sink);
+ auto rootPage = writer.writeAll(config.getUnownedSlice());
+ File::writeAllText("toc.html", writer.writeTOC());
+ rootPage->writeToDisk();
+ rootPage->writeSummary(toSlice("summary.txt"));
+ }
+ ComPtr<ISlangBlob> diagnosticBlob;
+ sink.getBlobIfNeeded(diagnosticBlob.writeRef());
+ if (diagnosticBlob && diagnosticBlob->getBufferSize() != 0)
+ {
+ // Write the diagnostic blob to stdout.
+ fprintf(stderr, "%s", (const char*)diagnosticBlob->getBufferPointer());
+ }
+}
+
+const char* getBuiltinModuleNameStr(slang::BuiltinModuleName name)
+{
+ const char* result = nullptr;
+ switch (name)
+ {
+ case slang::BuiltinModuleName::Core:
+ result = "core";
+ break;
+ case slang::BuiltinModuleName::GLSL:
+ result = "glsl";
+ break;
+ default:
+ SLANG_UNEXPECTED("Unknown builtin module");
+ }
+ return result;
+}
+
+TypeCheckingCache* Session::getTypeCheckingCache()
+{
+ return static_cast<TypeCheckingCache*>(m_typeCheckingCache.get());
+}
+
+Session::BuiltinModuleInfo Session::getBuiltinModuleInfo(slang::BuiltinModuleName name)
+{
+ Session::BuiltinModuleInfo result;
+
+ result.name = getBuiltinModuleNameStr(name);
+
+ switch (name)
+ {
+ case slang::BuiltinModuleName::Core:
+ result.languageScope = coreLanguageScope;
+ break;
+ case slang::BuiltinModuleName::GLSL:
+ result.name = "glsl";
+ result.languageScope = glslLanguageScope;
+ break;
+ default:
+ SLANG_UNEXPECTED("Unknown builtin module");
+ }
+ return result;
+}
+
+SlangResult Session::compileCoreModule(slang::CompileCoreModuleFlags compileFlags)
+{
+ return compileBuiltinModule(slang::BuiltinModuleName::Core, compileFlags);
+}
+
+void Session::getBuiltinModuleSource(StringBuilder& sb, slang::BuiltinModuleName moduleName)
+{
+ switch (moduleName)
+ {
+ case slang::BuiltinModuleName::Core:
+ sb << (const char*)getCoreLibraryCode()->getBufferPointer()
+ << (const char*)getHLSLLibraryCode()->getBufferPointer()
+ << (const char*)getAutodiffLibraryCode()->getBufferPointer();
+ break;
+ case slang::BuiltinModuleName::GLSL:
+ sb << (const char*)getGLSLLibraryCode()->getBufferPointer();
+ break;
+ }
+}
+
+SlangResult Session::compileBuiltinModule(
+ slang::BuiltinModuleName moduleName,
+ slang::CompileCoreModuleFlags compileFlags)
+{
+ SLANG_AST_BUILDER_RAII(m_builtinLinkage->getASTBuilder());
+
+#ifdef _DEBUG
+ time_t beginTime = 0;
+ if (moduleName == slang::BuiltinModuleName::Core)
+ {
+ // Print a message in debug builds to notice the user that compiling the core module
+ // can take a while.
+ time(&beginTime);
+ fprintf(stderr, "Compiling core module on debug build, this can take a while.\n");
+ }
+#endif
+ BuiltinModuleInfo builtinModuleInfo = getBuiltinModuleInfo(moduleName);
+ auto moduleNameObj = m_builtinLinkage->getNamePool()->getName(builtinModuleInfo.name);
+ if (m_builtinLinkage->mapNameToLoadedModules.tryGetValue(moduleNameObj))
+ {
+ // Already have the builtin module loaded
+ return SLANG_FAIL;
+ }
+
+ StringBuilder moduleSrcBuilder;
+ getBuiltinModuleSource(moduleSrcBuilder, moduleName);
+
+ // TODO(JS): Could make this return a SlangResult as opposed to exception
+ auto moduleSrcBlob = StringBlob::moveCreate(moduleSrcBuilder.produceString());
+ Module* compiledModule = nullptr;
+ addBuiltinSource(
+ builtinModuleInfo.languageScope,
+ builtinModuleInfo.name,
+ moduleSrcBlob,
+ compiledModule);
+
+ if (moduleName == slang::BuiltinModuleName::Core)
+ {
+ // We need to retain this AST so that we can use it in other code
+ // (Note that the `Scope` type does not retain the AST it points to)
+ coreModules.add(compiledModule);
+ }
+
+ if (compileFlags & slang::CompileCoreModuleFlag::WriteDocumentation)
+ {
+ // Load config file first.
+ String configText;
+ if (SLANG_FAILED(File::readAllText("config.txt", configText)))
+ {
+ fprintf(
+ stderr,
+ "Error writing documentation: config file not found on current working "
+ "directory.\n");
+ }
+ else
+ {
+ writeCoreModuleDoc(configText);
+ }
+ }
+
+ finalizeSharedASTBuilder();
+
+#ifdef _DEBUG
+ if (moduleName == slang::BuiltinModuleName::Core)
+ {
+ time_t endTime;
+ time(&endTime);
+ fprintf(stderr, "Compiling core module took %.2f seconds.\n", difftime(endTime, beginTime));
+ }
+#endif
+ return SLANG_OK;
+}
+
+SlangResult Session::loadCoreModule(const void* coreModule, size_t coreModuleSizeInBytes)
+{
+ return loadBuiltinModule(slang::BuiltinModuleName::Core, coreModule, coreModuleSizeInBytes);
+}
+
+SlangResult Session::loadBuiltinModule(
+ slang::BuiltinModuleName moduleName,
+ const void* moduleData,
+ size_t sizeInBytes)
+{
+ SLANG_PROFILE;
+
+
+ SLANG_AST_BUILDER_RAII(m_builtinLinkage->getASTBuilder());
+
+ BuiltinModuleInfo builtinModuleInfo = getBuiltinModuleInfo(moduleName);
+ auto nameObj = m_builtinLinkage->getNamePool()->getName(builtinModuleInfo.name);
+ if (m_builtinLinkage->mapNameToLoadedModules.containsKey(nameObj))
+ {
+ // Already have a core module loaded
+ return SLANG_FAIL;
+ }
+
+ // Make a file system to read it from
+ ComPtr<ISlangFileSystemExt> fileSystem;
+ SLANG_RETURN_ON_FAIL(loadArchiveFileSystem(moduleData, sizeInBytes, fileSystem));
+
+ // Let's try loading serialized modules and adding them
+ Module* module = nullptr;
+ SLANG_RETURN_ON_FAIL(_readBuiltinModule(
+ fileSystem,
+ builtinModuleInfo.languageScope,
+ builtinModuleInfo.name,
+ module));
+
+ if (moduleName == slang::BuiltinModuleName::Core)
+ {
+ // We need to retain this AST so that we can use it in other code
+ // (Note that the `Scope` type does not retain the AST it points to)
+ coreModules.add(module);
+ }
+
+ finalizeSharedASTBuilder();
+ return SLANG_OK;
+}
+
+SlangResult Session::saveCoreModule(SlangArchiveType archiveType, ISlangBlob** outBlob)
+{
+ return saveBuiltinModule(slang::BuiltinModuleName::Core, archiveType, outBlob);
+}
+
+SlangResult Session::saveBuiltinModule(
+ slang::BuiltinModuleName moduleTag,
+ SlangArchiveType archiveType,
+ ISlangBlob** outBlob)
+{
+ // If no builtin modules have been loaded, then there is
+ // nothing to save, and we fail immediately.
+ //
+ if (m_builtinLinkage->mapNameToLoadedModules.getCount() == 0)
+ {
+ return SLANG_FAIL;
+ }
+
+ // The module will need to be looked up by its name, and
+ // will also be serialized out to a path with a matching name.
+ //
+ BuiltinModuleInfo moduleInfo = getBuiltinModuleInfo(moduleTag);
+ const char* moduleName = moduleInfo.name;
+
+ // If we cannot find a loaded module in the linkage with
+ // the appropriate name, then for some reason it hasn't
+ // been loaded, and we fail.
+ //
+ RefPtr<Module> module;
+ m_builtinLinkage->mapNameToLoadedModules.tryGetValue(
+ getNameObj(UnownedStringSlice(moduleName)),
+ module);
+ if (!module)
+ {
+ return SLANG_FAIL;
+ }
+
+ // AST serialization needs access to an AST builder, so
+ // we establish a current builder for the duration of
+ // the serialization process.
+ //
+ SLANG_AST_BUILDER_RAII(m_builtinLinkage->getASTBuilder());
+
+ // The serialized module will be represented as a logical
+ // file in an archive, so we create a logical file system
+ // to represent that archive.
+ //
+ ComPtr<ISlangMutableFileSystem> fileSystem;
+ SLANG_RETURN_ON_FAIL(createArchiveFileSystem(archiveType, fileSystem));
+ //
+ // The created file system must support the `IArchiveFileSystem`
+ // interface (since we created it with `createArchiveFileSystem`).
+ //
+ auto archiveFileSystem = as<IArchiveFileSystem>(fileSystem);
+ if (!archiveFileSystem)
+ {
+ return SLANG_FAIL;
+ }
+
+ // The output file name that we'll write to in that file system
+ // is just the builtin module name with a `.slang-module` suffix.
+ //
+ StringBuilder moduleFileName;
+ moduleFileName << moduleName << ".slang-module";
+
+ // The module serialization step has some options that we need
+ // to configure appropriately.
+ //
+ SerialContainerUtil::WriteOptions options;
+ //
+ // We want builtin modules to be saved with their source location
+ // information.
+ //
+ // And in order to work with source locations, the serialization
+ // process will also need access to the source manager that
+ // can translate locations into their humane format.
+ //
+ options.sourceManagerToUseWhenSerializingSourceLocs = m_builtinLinkage->getSourceManager();
+
+ // At this point we can finally delegate down to the next level,
+ // which handles the serialization of a Slang module into a
+ // byte stream.
+ //
+ OwnedMemoryStream stream(FileAccess::Write);
+ SLANG_RETURN_ON_FAIL(SerialContainerUtil::write(module, options, &stream));
+ auto contents = stream.getContents();
+
+ // Once the stream that represents the module has been written, we can
+ // write it to a file in the logical file system.
+ //
+ // TODO(tfoley): why can't the file system let us open the file for output?
+ //
+ SLANG_RETURN_ON_FAIL(fileSystem->saveFile(
+ moduleFileName.getBuffer(),
+ contents.getBuffer(),
+ contents.getCount()));
+
+ // And finally, we can ask the archive file system to serialize itself
+ // out as a blob of bytes, which yields the final serialized representation
+ // of the module.
+ //
+ SLANG_RETURN_ON_FAIL(archiveFileSystem->storeArchive(
+ // The `true` here indicates that the blob that gets created should own
+ // its content, independent from the file system object itself; otherwise
+ // the file system might return a blob that shares storage with itself.
+ true,
+ outBlob));
+
+ return SLANG_OK;
+}
+
+SlangResult Session::_readBuiltinModule(
+ ISlangFileSystem* fileSystem,
+ Scope* scope,
+ String moduleName,
+ Module*& outModule)
+{
+ // Get the name of the module
+ StringBuilder moduleFilename;
+ moduleFilename << moduleName << ".slang-module";
+
+ // Load it
+ ComPtr<ISlangBlob> fileContents;
+ SLANG_RETURN_ON_FAIL(fileSystem->loadFile(moduleFilename.getBuffer(), fileContents.writeRef()));
+
+ RIFF::RootChunk const* rootChunk = RIFF::RootChunk::getFromBlob(fileContents);
+ if (!rootChunk)
+ {
+ return SLANG_FAIL;
+ }
+
+ Linkage* linkage = getBuiltinLinkage();
+ SourceManager* sourceManager = getBuiltinSourceManager();
+ NamePool* sessionNamePool = &namePool;
+
+ auto moduleChunk = ModuleChunk::find(rootChunk);
+ if (!moduleChunk)
+ return SLANG_FAIL;
+
+ SHA1::Digest moduleDigest = moduleChunk->getDigest();
+
+ auto irChunk = moduleChunk->findIR();
+ if (!irChunk)
+ return SLANG_FAIL;
+
+ auto astChunk = moduleChunk->findAST();
+ if (!astChunk)
+ return SLANG_FAIL;
+
+ // Source location information is stored as a distinct
+ // chunk from the IR and AST, so we need to search for
+ // that chunk and then set up the information for use
+ // in the IR and AST deserialization (if we find anything).
+ //
+ RefPtr<SerialSourceLocReader> sourceLocReader;
+ if (auto debugChunk = DebugChunk::find(moduleChunk))
+ {
+ SLANG_RETURN_ON_FAIL(
+ readSourceLocationsFromDebugChunk(debugChunk, sourceManager, sourceLocReader));
+ }
+
+ // At this point we create the `Module` object that will
+ // represent the builtin module we are reading, although
+ // it is still possible that deserialization will fail
+ // at one of the following steps.
+ //
+ auto astBuilder = linkage->getASTBuilder();
+ RefPtr<Module> module(new Module(linkage, astBuilder));
+ module->setName(moduleName);
+ module->setDigest(moduleDigest);
+
+
+ // Next, we set about deserializing the AST representation
+ // of the module.
+ //
+ auto moduleDecl = readSerializedModuleAST(
+ linkage,
+ astBuilder,
+ nullptr, // no sink
+ fileContents,
+ astChunk,
+ sourceLocReader,
+ SourceLoc());
+ if (!moduleDecl)
+ {
+ return SLANG_FAIL;
+ }
+ moduleDecl->module = module;
+ module->setModuleDecl(moduleDecl);
+
+ // After the AST module has been read in, we next look
+ // to deserialize the IR module.
+ //
+ RefPtr<IRModule> irModule;
+ SLANG_RETURN_ON_FAIL(readSerializedModuleIR(irChunk, this, sourceLocReader, irModule));
+
+ irModule->setName(module->getNameObj());
+ module->setIRModule(irModule);
+
+ // Put in the loaded module map
+ linkage->mapNameToLoadedModules.add(sessionNamePool->getName(moduleName), module);
+
+
+ // Add the resulting code to the appropriate scope
+ if (!scope->containerDecl)
+ {
+ // We are the first chunk of code to be loaded for this scope
+ scope->containerDecl = moduleDecl;
+ }
+ else
+ {
+ // We need to create a new scope to link into the whole thing
+ auto subScope = linkage->getASTBuilder()->create<Scope>();
+ subScope->containerDecl = moduleDecl;
+ subScope->nextSibling = scope->nextSibling;
+ scope->nextSibling = subScope;
+ }
+
+ outModule = module.get();
+
+ return SLANG_OK;
+}
+
+SLANG_NO_THROW SlangResult SLANG_MCALL
+Session::queryInterface(SlangUUID const& uuid, void** outObject)
+{
+ if (uuid == Session::getTypeGuid())
+ {
+ addReference();
+ *outObject = static_cast<Session*>(this);
+ return SLANG_OK;
+ }
+
+ if (uuid == ISlangUnknown::getTypeGuid() || uuid == IGlobalSession::getTypeGuid())
+ {
+ addReference();
+ *outObject = static_cast<slang::IGlobalSession*>(this);
+ return SLANG_OK;
+ }
+
+ return SLANG_E_NO_INTERFACE;
+}
+
+static size_t _getStructureSize(const uint8_t* src)
+{
+ size_t size = 0;
+ ::memcpy(&size, src, sizeof(size_t));
+ return size;
+}
+
+template<typename T>
+static T makeFromSizeVersioned(const uint8_t* src)
+{
+ // The structure size must be size_t
+ SLANG_COMPILE_TIME_ASSERT(sizeof(((T*)src)->structureSize) == sizeof(size_t));
+
+ // The structureSize field *must* be the first element of T
+ // Ideally would use SLANG_COMPILE_TIME_ASSERT, but that doesn't work on gcc.
+ // Can't just assert, because determined to be a constant expression
+ {
+ auto offset = SLANG_OFFSET_OF(T, structureSize);
+ SLANG_ASSERT(offset == 0);
+ // Needed because offset is only 'used' by an assert
+ SLANG_UNUSED(offset);
+ }
+
+ // The source size is held in the first element of T, and will be in the first bytes of src.
+ const size_t srcSize = _getStructureSize(src);
+ const size_t dstSize = sizeof(T);
+
+ // If they are the same size, and appropriate alignment we can just cast and return
+ if (srcSize == dstSize && (size_t(src) & (alignof(T) - 1)) == 0)
+ {
+ return *(const T*)src;
+ }
+
+ // Assumes T can default constructed sensibly
+ T dst;
+
+ // It's structure size should be setup and should be dstSize
+ SLANG_ASSERT(dst.structureSize == dstSize);
+
+ // The size to copy is the minimum on the two sizes
+ const auto copySize = std::min(srcSize, dstSize);
+ ::memcpy(&dst, src, copySize);
+
+ // The final struct size is the destination size
+ dst.structureSize = dstSize;
+
+ return dst;
+}
+
+SLANG_NO_THROW SlangResult SLANG_MCALL
+Session::createSession(slang::SessionDesc const& inDesc, slang::ISession** outSession)
+{
+ RefPtr<ASTBuilder> astBuilder(new ASTBuilder(m_sharedASTBuilder, "Session::astBuilder"));
+ slang::SessionDesc desc = makeFromSizeVersioned<slang::SessionDesc>((uint8_t*)&inDesc);
+
+ RefPtr<Linkage> linkage = new Linkage(this, astBuilder, getBuiltinLinkage());
+
+ if (desc.skipSPIRVValidation)
+ {
+ linkage->m_optionSet.set(CompilerOptionName::SkipSPIRVValidation, true);
+ }
+
+ {
+ std::lock_guard<std::mutex> lock(m_typeCheckingCacheMutex);
+ if (m_typeCheckingCache)
+ linkage->m_typeCheckingCache =
+ new TypeCheckingCache(*static_cast<TypeCheckingCache*>(m_typeCheckingCache.get()));
+ }
+
+ Int searchPathCount = desc.searchPathCount;
+ for (Int ii = 0; ii < searchPathCount; ++ii)
+ {
+ linkage->addSearchPath(desc.searchPaths[ii]);
+ }
+
+ Int macroCount = desc.preprocessorMacroCount;
+ for (Int ii = 0; ii < macroCount; ++ii)
+ {
+ auto& macro = desc.preprocessorMacros[ii];
+ linkage->addPreprocessorDefine(macro.name, macro.value);
+ }
+
+ if (desc.fileSystem)
+ {
+ linkage->setFileSystem(desc.fileSystem);
+ }
+
+ if (desc.structureSize >= offsetof(slang::SessionDesc, enableEffectAnnotations))
+ {
+ linkage->m_optionSet.set(
+ CompilerOptionName::EnableEffectAnnotations,
+ desc.enableEffectAnnotations);
+ }
+
+ linkage->m_optionSet.load(desc.compilerOptionEntryCount, desc.compilerOptionEntries);
+
+ if (!linkage->m_optionSet.hasOption(CompilerOptionName::MatrixLayoutColumn) &&
+ !linkage->m_optionSet.hasOption(CompilerOptionName::MatrixLayoutRow))
+ linkage->setMatrixLayoutMode(desc.defaultMatrixLayoutMode);
+
+ {
+ const Int targetCount = desc.targetCount;
+ const uint8_t* targetDescPtr = reinterpret_cast<const uint8_t*>(desc.targets);
+ for (Int ii = 0; ii < targetCount; ++ii, targetDescPtr += _getStructureSize(targetDescPtr))
+ {
+ const auto targetDesc = makeFromSizeVersioned<slang::TargetDesc>(targetDescPtr);
+ linkage->addTarget(targetDesc);
+ }
+ }
+
+ // If any target requires debug info, then we will need to enable debug info when lowering to
+ // target-agnostic IR. The target-agnostic IR will only include debug info if the linkage IR
+ // options specify that it should, so make sure the linkage debug info level is greater than or
+ // equal to that of any target.
+ DebugInfoLevel linkageDebugInfoLevel = linkage->m_optionSet.getDebugInfoLevel();
+ for (auto target : linkage->targets)
+ linkageDebugInfoLevel =
+ Math::Max(linkageDebugInfoLevel, target->getOptionSet().getDebugInfoLevel());
+ linkage->m_optionSet.set(CompilerOptionName::DebugInformation, linkageDebugInfoLevel);
+
+ // Add any referenced modules to the linkage
+ for (auto& option : linkage->m_optionSet.options)
+ {
+ if (option.key != CompilerOptionName::ReferenceModule)
+ continue;
+ for (auto& path : option.value)
+ {
+ DiagnosticSink sink;
+ ComPtr<IArtifact> artifact;
+ SlangResult result = createArtifactFromReferencedModule(
+ path.stringValue,
+ SourceLoc{},
+ &sink,
+ artifact.writeRef());
+ if (SLANG_FAILED(result))
+ {
+ sink.diagnose(SourceLoc{}, Diagnostics::unableToReadFile, path.stringValue);
+ return result;
+ }
+ linkage->m_libModules.add(artifact);
+ }
+ }
+
+ *outSession = asExternal(linkage.detach());
+ return SLANG_OK;
+}
+
+SLANG_NO_THROW SlangResult SLANG_MCALL
+Session::createCompileRequest(slang::ICompileRequest** outCompileRequest)
+{
+ auto req = new EndToEndCompileRequest(this);
+
+ // Give it a ref (for output)
+ req->addRef();
+ // Check it is what we think it should be
+ SLANG_ASSERT(req->debugGetReferenceCount() == 1);
+
+ *outCompileRequest = req;
+ return SLANG_OK;
+}
+
+SLANG_NO_THROW SlangProfileID SLANG_MCALL Session::findProfile(char const* name)
+{
+ return SlangProfileID(Slang::Profile::lookUp(name).raw);
+}
+
+SLANG_NO_THROW SlangCapabilityID SLANG_MCALL Session::findCapability(char const* name)
+{
+ return SlangCapabilityID(Slang::findCapabilityName(UnownedTerminatedStringSlice(name)));
+}
+
+SLANG_NO_THROW void SLANG_MCALL
+Session::setDownstreamCompilerPath(SlangPassThrough inPassThrough, char const* path)
+{
+ PassThroughMode passThrough = PassThroughMode(inPassThrough);
+ SLANG_ASSERT(
+ int(passThrough) > int(PassThroughMode::None) &&
+ int(passThrough) < int(PassThroughMode::CountOf));
+
+ if (m_downstreamCompilerPaths[int(passThrough)] != path)
+ {
+ // Make access redetermine compiler
+ resetDownstreamCompiler(passThrough);
+ // Set the path
+ m_downstreamCompilerPaths[int(passThrough)] = path;
+ }
+}
+
+SLANG_NO_THROW void SLANG_MCALL
+Session::setDownstreamCompilerPrelude(SlangPassThrough inPassThrough, char const* prelude)
+{
+ PassThroughMode downstreamCompiler = PassThroughMode(inPassThrough);
+ SLANG_ASSERT(
+ int(downstreamCompiler) > int(PassThroughMode::None) &&
+ int(downstreamCompiler) < int(PassThroughMode::CountOf));
+ const SourceLanguage sourceLanguage =
+ getDefaultSourceLanguageForDownstreamCompiler(downstreamCompiler);
+ setLanguagePrelude(SlangSourceLanguage(sourceLanguage), prelude);
+}
+
+SLANG_NO_THROW void SLANG_MCALL
+Session::getDownstreamCompilerPrelude(SlangPassThrough inPassThrough, ISlangBlob** outPrelude)
+{
+ PassThroughMode downstreamCompiler = PassThroughMode(inPassThrough);
+ SLANG_ASSERT(
+ int(downstreamCompiler) > int(PassThroughMode::None) &&
+ int(downstreamCompiler) < int(PassThroughMode::CountOf));
+ const SourceLanguage sourceLanguage =
+ getDefaultSourceLanguageForDownstreamCompiler(downstreamCompiler);
+ getLanguagePrelude(SlangSourceLanguage(sourceLanguage), outPrelude);
+}
+
+SLANG_NO_THROW void SLANG_MCALL
+Session::setLanguagePrelude(SlangSourceLanguage inSourceLanguage, char const* prelude)
+{
+ SourceLanguage sourceLanguage = SourceLanguage(inSourceLanguage);
+ SLANG_ASSERT(
+ int(sourceLanguage) > int(SourceLanguage::Unknown) &&
+ int(sourceLanguage) < int(SourceLanguage::CountOf));
+
+ SLANG_ASSERT(sourceLanguage != SourceLanguage::Unknown);
+
+ if (sourceLanguage != SourceLanguage::Unknown)
+ {
+ m_languagePreludes[int(sourceLanguage)] = prelude;
+ }
+}
+
+SLANG_NO_THROW void SLANG_MCALL
+Session::getLanguagePrelude(SlangSourceLanguage inSourceLanguage, ISlangBlob** outPrelude)
+{
+ SourceLanguage sourceLanguage = SourceLanguage(inSourceLanguage);
+
+ *outPrelude = nullptr;
+ if (sourceLanguage != SourceLanguage::Unknown)
+ {
+ SLANG_ASSERT(
+ int(sourceLanguage) > int(SourceLanguage::Unknown) &&
+ int(sourceLanguage) < int(SourceLanguage::CountOf));
+ *outPrelude =
+ Slang::StringUtil::createStringBlob(m_languagePreludes[int(sourceLanguage)]).detach();
+ }
+}
+
+SLANG_NO_THROW const char* SLANG_MCALL Session::getBuildTagString()
+{
+ return ::Slang::getBuildTagString();
+}
+
+SLANG_NO_THROW SlangResult SLANG_MCALL Session::setDefaultDownstreamCompiler(
+ SlangSourceLanguage sourceLanguage,
+ SlangPassThrough defaultCompiler)
+{
+ if (DownstreamCompilerInfo::canCompile(defaultCompiler, sourceLanguage))
+ {
+ m_defaultDownstreamCompilers[int(sourceLanguage)] = PassThroughMode(defaultCompiler);
+ return SLANG_OK;
+ }
+ return SLANG_FAIL;
+}
+
+SlangPassThrough SLANG_MCALL
+Session::getDefaultDownstreamCompiler(SlangSourceLanguage inSourceLanguage)
+{
+ SLANG_ASSERT(inSourceLanguage >= 0 && inSourceLanguage < SLANG_SOURCE_LANGUAGE_COUNT_OF);
+ auto sourceLanguage = SourceLanguage(inSourceLanguage);
+ return SlangPassThrough(m_defaultDownstreamCompilers[int(sourceLanguage)]);
+}
+
+void Session::setDownstreamCompilerForTransition(
+ SlangCompileTarget source,
+ SlangCompileTarget target,
+ SlangPassThrough compiler)
+{
+ if (compiler == SLANG_PASS_THROUGH_NONE)
+ {
+ // Removing the transition means a default can be used
+ m_codeGenTransitionMap.removeTransition(CodeGenTarget(source), CodeGenTarget(target));
+ }
+ else
+ {
+ m_codeGenTransitionMap.addTransition(
+ CodeGenTarget(source),
+ CodeGenTarget(target),
+ PassThroughMode(compiler));
+ }
+}
+
+SlangPassThrough Session::getDownstreamCompilerForTransition(
+ SlangCompileTarget inSource,
+ SlangCompileTarget inTarget)
+{
+ const CodeGenTarget source = CodeGenTarget(inSource);
+ const CodeGenTarget target = CodeGenTarget(inTarget);
+
+ if (m_codeGenTransitionMap.hasTransition(source, target))
+ {
+ return (SlangPassThrough)m_codeGenTransitionMap.getTransition(source, target);
+ }
+
+ const auto desc = ArtifactDescUtil::makeDescForCompileTarget(inTarget);
+
+ // Special case host-callable
+ if ((desc.kind == ArtifactKind::HostCallable) &&
+ (source == CodeGenTarget::CSource || source == CodeGenTarget::CPPSource))
+ {
+ // We prefer LLVM if it's available
+ if (const auto llvm = getOrLoadDownstreamCompiler(PassThroughMode::LLVM, nullptr))
+ {
+ return SLANG_PASS_THROUGH_LLVM;
+ }
+ }
+
+ // Use the legacy 'sourceLanguage' default mechanism.
+ // This says nothing about the target type, so it is *assumed* the target type is possible
+ // If not it will fail when trying to compile to an unknown target
+ const SourceLanguage sourceLanguage =
+ (SourceLanguage)TypeConvertUtil::getSourceLanguageFromTarget(inSource);
+ if (sourceLanguage != SourceLanguage::Unknown)
+ {
+ return getDefaultDownstreamCompiler(SlangSourceLanguage(sourceLanguage));
+ }
+
+ // Unknwon
+ return SLANG_PASS_THROUGH_NONE;
+}
+
+IDownstreamCompiler* Session::getDownstreamCompiler(CodeGenTarget source, CodeGenTarget target)
+{
+ PassThroughMode compilerType = (PassThroughMode)getDownstreamCompilerForTransition(
+ SlangCompileTarget(source),
+ SlangCompileTarget(target));
+ return getOrLoadDownstreamCompiler(compilerType, nullptr);
+}
+
+SLANG_NO_THROW SlangResult SLANG_MCALL Session::setSPIRVCoreGrammar(char const* jsonPath)
+{
+ if (!jsonPath)
+ {
+ spirvCoreGrammarInfo = SPIRVCoreGrammarInfo::getEmbeddedVersion();
+ SLANG_ASSERT(spirvCoreGrammarInfo);
+ }
+ else
+ {
+ SourceManager* sourceManager = getBuiltinSourceManager();
+ SLANG_ASSERT(sourceManager);
+ DiagnosticSink sink(sourceManager, Lexer::sourceLocationLexer);
+
+ String contents;
+ const auto readRes = File::readAllText(jsonPath, contents);
+ if (SLANG_FAILED(readRes))
+ {
+ sink.diagnose(SourceLoc{}, Diagnostics::unableToReadFile, jsonPath);
+ return readRes;
+ }
+ const auto pathInfo = PathInfo::makeFromString(jsonPath);
+ const auto sourceFile = sourceManager->createSourceFileWithString(pathInfo, contents);
+ const auto sourceView = sourceManager->createSourceView(sourceFile, nullptr, SourceLoc());
+ spirvCoreGrammarInfo = SPIRVCoreGrammarInfo::loadFromJSON(*sourceView, sink);
+ }
+ return spirvCoreGrammarInfo ? SLANG_OK : SLANG_FAIL;
+}
+
+struct ParsedCommandLineData : public ISlangUnknown, public ComObject
+{
+ SLANG_COM_OBJECT_IUNKNOWN_ALL
+
+ ISlangUnknown* getInterface(const Slang::Guid& guid)
+ {
+ if (guid == ISlangUnknown::getTypeGuid())
+ return this;
+ return nullptr;
+ }
+ List<SerializedOptionsData> options;
+ List<slang::TargetDesc> targets;
+};
+
+SLANG_NO_THROW SlangResult SLANG_MCALL Session::parseCommandLineArguments(
+ int argc,
+ const char* const* argv,
+ slang::SessionDesc* outDesc,
+ ISlangUnknown** outAllocation)
+{
+ if (outDesc->structureSize < sizeof(slang::SessionDesc))
+ return SLANG_E_BUFFER_TOO_SMALL;
+ RefPtr<ParsedCommandLineData> outData = new ParsedCommandLineData();
+ RefPtr<EndToEndCompileRequest> tempReq = new EndToEndCompileRequest(this);
+ tempReq->processCommandLineArguments(argv, argc);
+ outData->options.setCount(1 + tempReq->getLinkage()->targets.getCount());
+ int optionDataIndex = 0;
+ SerializedOptionsData& optionData = outData->options[optionDataIndex];
+ optionDataIndex++;
+ tempReq->getOptionSet().serialize(&optionData);
+ tempReq->m_optionSetForDefaultTarget.serialize(&optionData);
+ for (auto target : tempReq->getLinkage()->targets)
+ {
+ slang::TargetDesc tdesc;
+ SerializedOptionsData& targetOptionData = outData->options[optionDataIndex];
+ optionDataIndex++;
+ tempReq->getTargetOptionSet(target).serialize(&targetOptionData);
+ tdesc.compilerOptionEntryCount = (uint32_t)targetOptionData.entries.getCount();
+ tdesc.compilerOptionEntries = targetOptionData.entries.getBuffer();
+ outData->targets.add(tdesc);
+ }
+ outDesc->compilerOptionEntryCount = (uint32_t)optionData.entries.getCount();
+ outDesc->compilerOptionEntries = optionData.entries.getBuffer();
+ outDesc->targetCount = outData->targets.getCount();
+ outDesc->targets = outData->targets.getBuffer();
+ *outAllocation = outData.get();
+ outData->addRef();
+ return SLANG_OK;
+}
+
+SLANG_NO_THROW SlangResult SLANG_MCALL
+Session::getSessionDescDigest(slang::SessionDesc* sessionDesc, ISlangBlob** outBlob)
+{
+ ComPtr<slang::ISession> tempSession;
+ createSession(*sessionDesc, tempSession.writeRef());
+ auto linkage = static_cast<Linkage*>(tempSession.get());
+ DigestBuilder<SHA1> digestBuilder;
+ linkage->buildHash(digestBuilder, -1);
+ auto blob = digestBuilder.finalize().toBlob();
+ *outBlob = blob.detach();
+ return SLANG_OK;
+}
+
+void Session::addBuiltinSource(
+ Scope* scope,
+ String const& path,
+ ISlangBlob* sourceBlob,
+ Module*& outModule)
+{
+ SourceManager* sourceManager = getBuiltinSourceManager();
+
+ DiagnosticSink sink(sourceManager, Lexer::sourceLocationLexer);
+
+ RefPtr<FrontEndCompileRequest> compileRequest =
+ new FrontEndCompileRequest(m_builtinLinkage, nullptr, &sink);
+ compileRequest->m_isCoreModuleCode = true;
+
+ // Set the source manager on the sink
+ sink.setSourceManager(sourceManager);
+ // Make the linkage use the builtin source manager
+ Linkage* linkage = compileRequest->getLinkage();
+ linkage->setSourceManager(sourceManager);
+
+ Name* moduleName = getNamePool()->getName(path);
+ auto translationUnitIndex =
+ compileRequest->addTranslationUnit(SourceLanguage::Slang, moduleName);
+
+ compileRequest->addTranslationUnitSourceBlob(translationUnitIndex, path, sourceBlob);
+
+ SlangResult res = compileRequest->executeActionsInner();
+ if (SLANG_FAILED(res))
+ {
+ char const* diagnostics = sink.outputBuffer.getBuffer();
+ fprintf(stderr, "%s", diagnostics);
+
+ PlatformUtil::outputDebugMessage(diagnostics);
+
+ SLANG_UNEXPECTED("error in Slang core module");
+ }
+
+ // Compiling the core module should not yield any warnings.
+ SLANG_ASSERT(sink.outputBuffer.getLength() == 0);
+
+ // Extract the AST for the code we just parsed
+ auto module = compileRequest->translationUnits[translationUnitIndex]->getModule();
+ auto moduleDecl = module->getModuleDecl();
+
+ // Extact documentation markup.
+ ASTMarkup markup;
+ ASTMarkupUtil::extract(moduleDecl, sourceManager, &sink, &markup);
+ markup.attachToAST();
+
+ // Put in the loaded module map
+ linkage->mapNameToLoadedModules.add(moduleName, module);
+
+ // Add the resulting code to the appropriate scope
+ if (!scope->containerDecl)
+ {
+ // We are the first chunk of code to be loaded for this scope
+ scope->containerDecl = moduleDecl;
+ }
+ else
+ {
+ // We need to create a new scope to link into the whole thing
+ auto subScope = module->getASTBuilder()->create<Scope>();
+ subScope->containerDecl = moduleDecl;
+ subScope->nextSibling = scope->nextSibling;
+ scope->nextSibling = subScope;
+ }
+
+ outModule = module;
+}
+
+SlangResult checkExternalCompilerSupport(Session* session, PassThroughMode passThrough)
+{
+ // Check if the type is supported on this compile
+ if (passThrough == PassThroughMode::None)
+ {
+ // If no pass through -> that will always work!
+ return SLANG_OK;
+ }
+
+ return session->getOrLoadDownstreamCompiler(passThrough, nullptr) ? SLANG_OK
+ : SLANG_E_NOT_FOUND;
+}
+
+} // namespace Slang
diff --git a/source/slang/slang-global-session.h b/source/slang/slang-global-session.h
new file mode 100644
index 000000000..56984bdbd
--- /dev/null
+++ b/source/slang/slang-global-session.h
@@ -0,0 +1,366 @@
+// slang-global-session.h
+#pragma once
+
+//
+// This file provides the `Session` type, and the implementation
+// of the `slang::IGlobalSession` interface from the public API.
+//
+// TODO: there is an unfortunate and confusing situation
+// where the public Slang API `ISession` type is implemented
+// by the internal `Linkage` class, while the internal
+// `Session` class implements the `IGlobalSession` interface
+// from the public API.
+//
+
+#include "../compiler-core/slang-downstream-compiler-set.h"
+#include "../compiler-core/slang-downstream-compiler-util.h"
+#include "../compiler-core/slang-downstream-compiler.h"
+#include "../compiler-core/slang-spirv-core-grammar.h"
+#include "../core/slang-command-options.h"
+#include "slang-pass-through.h"
+#include "slang-target.h"
+
+namespace Slang
+{
+
+
+class CodeGenTransitionMap
+{
+public:
+ struct Pair
+ {
+ typedef Pair ThisType;
+ SLANG_FORCE_INLINE bool operator==(const ThisType& rhs) const
+ {
+ return source == rhs.source && target == rhs.target;
+ }
+ SLANG_FORCE_INLINE bool operator!=(const ThisType& rhs) const { return !(*this == rhs); }
+
+ SLANG_FORCE_INLINE HashCode getHashCode() const
+ {
+ return combineHash(HashCode(source), HashCode(target));
+ }
+
+ CodeGenTarget source;
+ CodeGenTarget target;
+ };
+
+ void removeTransition(CodeGenTarget source, CodeGenTarget target)
+ {
+ m_map.remove(Pair{source, target});
+ }
+ void addTransition(CodeGenTarget source, CodeGenTarget target, PassThroughMode compiler)
+ {
+ SLANG_ASSERT(source != target);
+ m_map.set(Pair{source, target}, compiler);
+ }
+ bool hasTransition(CodeGenTarget source, CodeGenTarget target) const
+ {
+ return m_map.containsKey(Pair{source, target});
+ }
+ PassThroughMode getTransition(CodeGenTarget source, CodeGenTarget target) const
+ {
+ const Pair pair{source, target};
+ auto value = m_map.tryGetValue(pair);
+ return value ? *value : PassThroughMode::None;
+ }
+
+protected:
+ Dictionary<Pair, PassThroughMode> m_map;
+};
+
+/// A global session for interaction with the Slang compiler.
+///
+/// A `Session` provides a context for ongoing interaction
+/// between a client application and the Slang API. Creating
+/// a `Session` has an up-front cost that then makes creation
+/// of one or more `Linkage`s cheaper.
+///
+/// The main services provided by a `Session` are:
+///
+/// * This class implements the `slang::IGobalSession` interface
+/// from the public Slang API.
+///
+/// * The global session provides a scope for a loading built-in
+/// modules (including the core module), such that the costs
+/// associated with those modules can be amortized across
+/// multiple `Linkage`s using the same global session.
+///
+/// * The global session provides a scope for interactions with
+/// the surrounding OS, and application-specific customizations
+/// related to it. This includes locating other tools such as
+/// downstream compilers, which may be implemented either as
+/// executables or shared libraries.
+///
+/// * The global session provides a scope for application-specific
+/// injection of custom source code to be treated like a builtin
+/// module, or text to be used as the "prelude" for particular
+/// downstream languages/compilers. This functionality should be
+/// considered as a legacy and/or deprecated feature.
+///
+/// * The global session provides various one-off services that
+/// are specific to the `slangc` tool or to the build process
+/// for Slang itself. Some of these have been exposed through the
+/// `slang::IGlobalSession` interface, but should be *not* be
+/// used by user applications.
+///
+class Session : public RefObject, public slang::IGlobalSession
+{
+public:
+ SLANG_COM_INTERFACE(
+ 0xd6b767eb,
+ 0xd786,
+ 0x4343,
+ {0x2a, 0x8c, 0x6d, 0xa0, 0x3d, 0x5a, 0xb4, 0x4a})
+
+ SLANG_NO_THROW SlangResult SLANG_MCALL queryInterface(SlangUUID const& uuid, void** outObject)
+ SLANG_OVERRIDE;
+ SLANG_REF_OBJECT_IUNKNOWN_ADD_REF
+ SLANG_REF_OBJECT_IUNKNOWN_RELEASE
+
+ // slang::IGlobalSession
+ SLANG_NO_THROW SlangResult SLANG_MCALL
+ createSession(slang::SessionDesc const& desc, slang::ISession** outSession) override;
+ SLANG_NO_THROW SlangProfileID SLANG_MCALL findProfile(char const* name) override;
+ SLANG_NO_THROW void SLANG_MCALL
+ setDownstreamCompilerPath(SlangPassThrough passThrough, char const* path) override;
+ SLANG_NO_THROW void SLANG_MCALL
+ setDownstreamCompilerPrelude(SlangPassThrough inPassThrough, char const* prelude) override;
+ SLANG_NO_THROW void SLANG_MCALL
+ getDownstreamCompilerPrelude(SlangPassThrough inPassThrough, ISlangBlob** outPrelude) override;
+ SLANG_NO_THROW const char* SLANG_MCALL getBuildTagString() override;
+ SLANG_NO_THROW SlangResult SLANG_MCALL setDefaultDownstreamCompiler(
+ SlangSourceLanguage sourceLanguage,
+ SlangPassThrough defaultCompiler) override;
+ SLANG_NO_THROW SlangPassThrough SLANG_MCALL
+ getDefaultDownstreamCompiler(SlangSourceLanguage sourceLanguage) override;
+
+ SLANG_NO_THROW void SLANG_MCALL
+ setLanguagePrelude(SlangSourceLanguage inSourceLanguage, char const* prelude) override;
+ SLANG_NO_THROW void SLANG_MCALL
+ getLanguagePrelude(SlangSourceLanguage inSourceLanguage, ISlangBlob** outPrelude) override;
+
+ SLANG_NO_THROW SlangResult SLANG_MCALL
+ createCompileRequest(slang::ICompileRequest** outCompileRequest) override;
+
+ SLANG_NO_THROW void SLANG_MCALL
+ addBuiltins(char const* sourcePath, char const* sourceString) override;
+ SLANG_NO_THROW void SLANG_MCALL
+ setSharedLibraryLoader(ISlangSharedLibraryLoader* loader) override;
+ SLANG_NO_THROW ISlangSharedLibraryLoader* SLANG_MCALL getSharedLibraryLoader() override;
+ SLANG_NO_THROW SlangResult SLANG_MCALL
+ checkCompileTargetSupport(SlangCompileTarget target) override;
+ SLANG_NO_THROW SlangResult SLANG_MCALL
+ checkPassThroughSupport(SlangPassThrough passThrough) override;
+
+ void writeCoreModuleDoc(String config);
+ SLANG_NO_THROW SlangResult SLANG_MCALL
+ compileCoreModule(slang::CompileCoreModuleFlags flags) override;
+ SLANG_NO_THROW SlangResult SLANG_MCALL
+ loadCoreModule(const void* coreModule, size_t coreModuleSizeInBytes) override;
+ SLANG_NO_THROW SlangResult SLANG_MCALL
+ saveCoreModule(SlangArchiveType archiveType, ISlangBlob** outBlob) override;
+
+ SLANG_NO_THROW SlangResult SLANG_MCALL compileBuiltinModule(
+ slang::BuiltinModuleName moduleName,
+ slang::CompileCoreModuleFlags flags) override;
+ SLANG_NO_THROW SlangResult SLANG_MCALL loadBuiltinModule(
+ slang::BuiltinModuleName moduleName,
+ const void* coreModule,
+ size_t coreModuleSizeInBytes) override;
+ SLANG_NO_THROW SlangResult SLANG_MCALL saveBuiltinModule(
+ slang::BuiltinModuleName moduleName,
+ SlangArchiveType archiveType,
+ ISlangBlob** outBlob) override;
+
+ SLANG_NO_THROW SlangCapabilityID SLANG_MCALL findCapability(char const* name) override;
+
+ SLANG_NO_THROW void SLANG_MCALL setDownstreamCompilerForTransition(
+ SlangCompileTarget source,
+ SlangCompileTarget target,
+ SlangPassThrough compiler) override;
+ SLANG_NO_THROW SlangPassThrough SLANG_MCALL getDownstreamCompilerForTransition(
+ SlangCompileTarget source,
+ SlangCompileTarget target) override;
+ SLANG_NO_THROW void SLANG_MCALL
+ getCompilerElapsedTime(double* outTotalTime, double* outDownstreamTime) override
+ {
+ *outDownstreamTime = m_downstreamCompileTime;
+ *outTotalTime = m_totalCompileTime;
+ }
+
+ SLANG_NO_THROW SlangResult SLANG_MCALL setSPIRVCoreGrammar(char const* jsonPath) override;
+
+ SLANG_NO_THROW SlangResult SLANG_MCALL parseCommandLineArguments(
+ int argc,
+ const char* const* argv,
+ slang::SessionDesc* outSessionDesc,
+ ISlangUnknown** outAllocation) override;
+
+ SLANG_NO_THROW SlangResult SLANG_MCALL
+ getSessionDescDigest(slang::SessionDesc* sessionDesc, ISlangBlob** outBlob) override;
+
+ /// Get the downstream compiler for a transition
+ IDownstreamCompiler* getDownstreamCompiler(CodeGenTarget source, CodeGenTarget target);
+
+ // This needs to be atomic not because of contention between threads as `Session` is
+ // *not* multithreaded, but can be used exclusively on one thread at a time.
+ // The need for atomic is purely for visibility. If the session is used on a different
+ // thread we need to be sure any changes to m_epochId are visible to this thread.
+ std::atomic<Index> m_epochId = 1;
+
+ Scope* baseLanguageScope = nullptr;
+ Scope* coreLanguageScope = nullptr;
+ Scope* hlslLanguageScope = nullptr;
+ Scope* slangLanguageScope = nullptr;
+ Scope* glslLanguageScope = nullptr;
+ Name* glslModuleName = nullptr;
+
+ ModuleDecl* baseModuleDecl = nullptr;
+ List<RefPtr<Module>> coreModules;
+
+ SourceManager builtinSourceManager;
+
+ SourceManager* getBuiltinSourceManager() { return &builtinSourceManager; }
+
+ // Name pool stuff for unique-ing identifiers
+
+ NamePool namePool;
+
+ NamePool* getNamePool() { return &namePool; }
+ Name* getNameObj(String name) { return namePool.getName(name); }
+ Name* tryGetNameObj(String name) { return namePool.tryGetName(name); }
+ //
+
+ /// This AST Builder should only be used for creating AST nodes that are global across requests
+ /// not doing so could lead to memory being consumed but not used.
+ ASTBuilder* getGlobalASTBuilder() { return globalAstBuilder; }
+ void finalizeSharedASTBuilder();
+
+ RefPtr<ASTBuilder> globalAstBuilder;
+
+ // Generated code for core module, etc.
+ String coreModulePath;
+
+ ComPtr<ISlangBlob> coreLibraryCode;
+ // ComPtr<ISlangBlob> slangLibraryCode;
+ ComPtr<ISlangBlob> hlslLibraryCode;
+ ComPtr<ISlangBlob> glslLibraryCode;
+ ComPtr<ISlangBlob> autodiffLibraryCode;
+
+ String getCoreModulePath();
+
+ ComPtr<ISlangBlob> getCoreLibraryCode();
+ ComPtr<ISlangBlob> getHLSLLibraryCode();
+ ComPtr<ISlangBlob> getAutodiffLibraryCode();
+ ComPtr<ISlangBlob> getGLSLLibraryCode();
+
+ void getBuiltinModuleSource(StringBuilder& sb, slang::BuiltinModuleName moduleName);
+
+ RefPtr<SharedASTBuilder> m_sharedASTBuilder;
+
+ SPIRVCoreGrammarInfo& getSPIRVCoreGrammarInfo()
+ {
+ if (!spirvCoreGrammarInfo)
+ setSPIRVCoreGrammar(nullptr);
+ SLANG_ASSERT(spirvCoreGrammarInfo);
+ return *spirvCoreGrammarInfo;
+ }
+ RefPtr<SPIRVCoreGrammarInfo> spirvCoreGrammarInfo;
+
+ //
+
+ void _setSharedLibraryLoader(ISlangSharedLibraryLoader* loader);
+
+ /// Will try to load the library by specified name (using the set loader), if not one already
+ /// available.
+ IDownstreamCompiler* getOrLoadDownstreamCompiler(PassThroughMode type, DiagnosticSink* sink);
+ /// Will unload the specified shared library if it's currently loaded
+ void resetDownstreamCompiler(PassThroughMode type);
+
+ /// Get the prelude associated with the language
+ const String& getPreludeForLanguage(SourceLanguage language)
+ {
+ return m_languagePreludes[int(language)];
+ }
+
+ /// Get the built in linkage -> handy to get the core module from
+ Linkage* getBuiltinLinkage() const { return m_builtinLinkage; }
+
+ Module* getBuiltinModule(slang::BuiltinModuleName builtinModuleName);
+
+ Name* getCompletionRequestTokenName() const { return m_completionTokenName; }
+
+ void init();
+
+ void addBuiltinSource(
+ Scope* scope,
+ String const& path,
+ ISlangBlob* sourceBlob,
+ Module*& outModule);
+ ~Session();
+
+ void addDownstreamCompileTime(double time) { m_downstreamCompileTime += time; }
+ void addTotalCompileTime(double time) { m_totalCompileTime += time; }
+
+ ComPtr<ISlangSharedLibraryLoader>
+ m_sharedLibraryLoader; ///< The shared library loader (never null)
+
+ int m_downstreamCompilerInitialized = 0;
+
+ RefPtr<DownstreamCompilerSet>
+ m_downstreamCompilerSet; ///< Information about all available downstream compilers.
+ ComPtr<IDownstreamCompiler> m_downstreamCompilers[int(
+ PassThroughMode::CountOf)]; ///< A downstream compiler for a pass through
+ DownstreamCompilerLocatorFunc m_downstreamCompilerLocators[int(PassThroughMode::CountOf)];
+ Name* m_completionTokenName = nullptr; ///< The name of a completion request token.
+
+ /// For parsing command line options
+ CommandOptions m_commandOptions;
+
+ int m_typeDictionarySize = 0;
+
+ RefPtr<RefObject> m_typeCheckingCache;
+ TypeCheckingCache* getTypeCheckingCache();
+ std::mutex m_typeCheckingCacheMutex;
+
+private:
+ struct BuiltinModuleInfo
+ {
+ const char* name;
+ Scope* languageScope;
+ };
+
+ BuiltinModuleInfo getBuiltinModuleInfo(slang::BuiltinModuleName name);
+
+ void _initCodeGenTransitionMap();
+
+ SlangResult _readBuiltinModule(
+ ISlangFileSystem* fileSystem,
+ Scope* scope,
+ String moduleName,
+ Module*& outModule);
+
+ SlangResult _loadRequest(EndToEndCompileRequest* request, const void* data, size_t size);
+
+ /// Linkage used for all built-in (core module) code.
+ RefPtr<Linkage> m_builtinLinkage;
+
+ String
+ m_downstreamCompilerPaths[int(PassThroughMode::CountOf)]; ///< Paths for each pass through
+ String m_languagePreludes[int(SourceLanguage::CountOf)]; ///< Prelude for each source language
+ PassThroughMode m_defaultDownstreamCompilers[int(SourceLanguage::CountOf)];
+
+ // Describes a conversion from one code gen target (source) to another (target)
+ CodeGenTransitionMap m_codeGenTransitionMap;
+
+ double m_downstreamCompileTime = 0.0;
+ double m_totalCompileTime = 0.0;
+};
+
+/* Returns SLANG_OK if pass through support is available */
+SlangResult checkExternalCompilerSupport(Session* session, PassThroughMode passThrough);
+
+const char* getBuiltinModuleNameStr(slang::BuiltinModuleName name);
+
+} // namespace Slang
diff --git a/source/slang/slang-linkable-impls.cpp b/source/slang/slang-linkable-impls.cpp
new file mode 100644
index 000000000..d03ecb3ca
--- /dev/null
+++ b/source/slang/slang-linkable-impls.cpp
@@ -0,0 +1,752 @@
+// slang-linkable-impls.cpp
+#include "slang-linkable-impls.h"
+
+#include "slang-lower-to-ir.h" // for `generateIRForTypeConformance`
+#include "slang-mangle.h"
+
+namespace Slang
+{
+
+//
+// CompositeComponentType
+//
+
+RefPtr<ComponentType> CompositeComponentType::create(
+ Linkage* linkage,
+ List<RefPtr<ComponentType>> const& childComponents)
+{
+ // TODO: We should ideally be caching the results of
+ // composition on the `linkage`, so that if we get
+ // asked for the same composite again later we re-use
+ // it rather than re-create it.
+ //
+ // Similarly, we might want to do some amount of
+ // work to "canonicalize" the input for composition.
+ // E.g., if the user does:
+ //
+ // X = compose(A,B);
+ // Y = compose(C,D);
+ // Z = compose(X,Y);
+ //
+ // W = compose(A, B, C, D);
+ //
+ // Then there is no observable difference between
+ // Z and W, so we might prefer to have them be identical.
+
+ // If there is only a single child, then we should
+ // just return that child rather than create a dummy composite.
+ //
+ if (childComponents.getCount() == 1)
+ {
+ return childComponents[0];
+ }
+
+ return new CompositeComponentType(linkage, childComponents);
+}
+
+
+CompositeComponentType::CompositeComponentType(
+ Linkage* linkage,
+ List<RefPtr<ComponentType>> const& childComponents)
+ : ComponentType(linkage), m_childComponents(childComponents)
+{
+ HashSet<ComponentType*> requirementsSet;
+ for (auto child : childComponents)
+ {
+ child->enumerateModules([&](Module* module) { requirementsSet.add(module); });
+ }
+
+ for (auto child : childComponents)
+ {
+ auto childEntryPointCount = child->getEntryPointCount();
+ for (Index cc = 0; cc < childEntryPointCount; ++cc)
+ {
+ m_entryPoints.add(child->getEntryPoint(cc));
+ m_entryPointMangledNames.add(child->getEntryPointMangledName(cc));
+ m_entryPointNameOverrides.add(child->getEntryPointNameOverride(cc));
+ }
+
+ auto childShaderParamCount = child->getShaderParamCount();
+ for (Index pp = 0; pp < childShaderParamCount; ++pp)
+ {
+ m_shaderParams.add(child->getShaderParam(pp));
+ }
+
+ auto childSpecializationParamCount = child->getSpecializationParamCount();
+ for (Index pp = 0; pp < childSpecializationParamCount; ++pp)
+ {
+ m_specializationParams.add(child->getSpecializationParam(pp));
+ }
+
+ for (auto module : child->getModuleDependencies())
+ {
+ m_moduleDependencyList.addDependency(module);
+ }
+ for (auto sourceFile : child->getFileDependencies())
+ {
+ m_fileDependencyList.addDependency(sourceFile);
+ }
+
+ auto childRequirementCount = child->getRequirementCount();
+ for (Index rr = 0; rr < childRequirementCount; ++rr)
+ {
+ auto childRequirement = child->getRequirement(rr);
+ if (!requirementsSet.contains(childRequirement))
+ {
+ requirementsSet.add(childRequirement);
+ m_requirements.add(childRequirement);
+ }
+ }
+ }
+}
+
+void CompositeComponentType::buildHash(DigestBuilder<SHA1>& builder)
+{
+ auto componentCount = getChildComponentCount();
+
+ for (Index i = 0; i < componentCount; ++i)
+ {
+ getChildComponent(i)->buildHash(builder);
+ }
+}
+
+Index CompositeComponentType::getEntryPointCount()
+{
+ return m_entryPoints.getCount();
+}
+
+RefPtr<EntryPoint> CompositeComponentType::getEntryPoint(Index index)
+{
+ return m_entryPoints[index];
+}
+
+String CompositeComponentType::getEntryPointMangledName(Index index)
+{
+ return m_entryPointMangledNames[index];
+}
+
+String CompositeComponentType::getEntryPointNameOverride(Index index)
+{
+ return m_entryPointNameOverrides[index];
+}
+
+Index CompositeComponentType::getShaderParamCount()
+{
+ return m_shaderParams.getCount();
+}
+
+ShaderParamInfo CompositeComponentType::getShaderParam(Index index)
+{
+ return m_shaderParams[index];
+}
+
+Index CompositeComponentType::getSpecializationParamCount()
+{
+ return m_specializationParams.getCount();
+}
+
+SpecializationParam const& CompositeComponentType::getSpecializationParam(Index index)
+{
+ return m_specializationParams[index];
+}
+
+Index CompositeComponentType::getRequirementCount()
+{
+ return m_requirements.getCount();
+}
+
+RefPtr<ComponentType> CompositeComponentType::getRequirement(Index index)
+{
+ return m_requirements[index];
+}
+
+List<Module*> const& CompositeComponentType::getModuleDependencies()
+{
+ return m_moduleDependencyList.getModuleList();
+}
+
+List<SourceFile*> const& CompositeComponentType::getFileDependencies()
+{
+ return m_fileDependencyList.getFileList();
+}
+
+void CompositeComponentType::acceptVisitor(
+ ComponentTypeVisitor* visitor,
+ SpecializationInfo* specializationInfo)
+{
+ visitor->visitComposite(this, as<CompositeSpecializationInfo>(specializationInfo));
+}
+
+RefPtr<ComponentType::SpecializationInfo> CompositeComponentType::_validateSpecializationArgsImpl(
+ SpecializationArg const* args,
+ Index argCount,
+ DiagnosticSink* sink)
+{
+ SLANG_UNUSED(argCount);
+
+ RefPtr<CompositeSpecializationInfo> specializationInfo = new CompositeSpecializationInfo();
+
+ Index offset = 0;
+ for (auto child : m_childComponents)
+ {
+ auto childParamCount = child->getSpecializationParamCount();
+ SLANG_ASSERT(offset + childParamCount <= argCount);
+
+ auto childInfo = child->_validateSpecializationArgs(args + offset, childParamCount, sink);
+
+ specializationInfo->childInfos.add(childInfo);
+
+ offset += childParamCount;
+ }
+ return specializationInfo;
+}
+
+//
+// SpecializedComponentType
+//
+
+/// Utility type for collecting modules references by types/declarations
+struct SpecializationArgModuleCollector : ComponentTypeVisitor
+{
+ HashSet<Module*> m_modulesSet;
+ List<Module*> m_modulesList;
+
+ void addModule(Module* module)
+ {
+ m_modulesList.add(module);
+ m_modulesSet.add(module);
+ }
+
+ void maybeAddModule(Module* module)
+ {
+ if (!module)
+ return;
+ if (m_modulesSet.contains(module))
+ return;
+
+ addModule(module);
+ }
+
+ void collectReferencedModules(Decl* decl)
+ {
+ auto module = getModule(decl);
+ maybeAddModule(module);
+ }
+
+ void collectReferencedModules(SubstitutionSet substitutions)
+ {
+ substitutions.forEachGenericSubstitution(
+ [this](GenericDecl*, Val::OperandView<Val> args)
+ {
+ for (auto arg : args)
+ {
+ collectReferencedModules(arg);
+ }
+ });
+ }
+
+ void collectReferencedModules(DeclRefBase* declRef)
+ {
+ collectReferencedModules(declRef->getDecl());
+ collectReferencedModules(SubstitutionSet(declRef));
+ }
+
+ void collectReferencedModules(Type* type)
+ {
+ if (auto declRefType = as<DeclRefType>(type))
+ {
+ collectReferencedModules(declRefType->getDeclRef());
+ }
+
+ // TODO: Handle non-decl-ref composite type cases
+ // (e.g., function types).
+ }
+
+ void collectReferencedModules(Val* val)
+ {
+ if (auto type = as<Type>(val))
+ {
+ collectReferencedModules(type);
+ }
+ else if (auto declRefVal = as<DeclRefIntVal>(val))
+ {
+ collectReferencedModules(declRefVal->getDeclRef());
+ }
+
+ // TODO: other cases of values that could reference
+ // a declaration.
+ }
+
+ void collectReferencedModules(List<ExpandedSpecializationArg> const& args)
+ {
+ for (auto arg : args)
+ {
+ collectReferencedModules(arg.val);
+ collectReferencedModules(arg.witness);
+ }
+ }
+
+ //
+ // ComponentTypeVisitor methods
+ //
+
+ void visitEntryPoint(
+ EntryPoint* entryPoint,
+ EntryPoint::EntryPointSpecializationInfo* specializationInfo) SLANG_OVERRIDE
+ {
+ SLANG_UNUSED(entryPoint);
+
+ if (!specializationInfo)
+ return;
+
+ collectReferencedModules(specializationInfo->specializedFuncDeclRef);
+ collectReferencedModules(specializationInfo->existentialSpecializationArgs);
+ }
+
+ void visitRenamedEntryPoint(
+ RenamedEntryPointComponentType* entryPoint,
+ EntryPoint::EntryPointSpecializationInfo* specializationInfo) SLANG_OVERRIDE
+ {
+ entryPoint->getBase()->acceptVisitor(this, specializationInfo);
+ }
+
+ void visitModule(Module* module, Module::ModuleSpecializationInfo* specializationInfo)
+ SLANG_OVERRIDE
+ {
+ SLANG_UNUSED(module);
+
+ if (!specializationInfo)
+ return;
+
+ for (auto arg : specializationInfo->genericArgs)
+ {
+ collectReferencedModules(arg.argVal);
+ }
+ collectReferencedModules(specializationInfo->existentialArgs);
+ }
+
+ void visitComposite(
+ CompositeComponentType* composite,
+ CompositeComponentType::CompositeSpecializationInfo* specializationInfo) SLANG_OVERRIDE
+ {
+ visitChildren(composite, specializationInfo);
+ }
+
+ void visitSpecialized(SpecializedComponentType* specialized) SLANG_OVERRIDE
+ {
+ visitChildren(specialized);
+ }
+
+ void visitTypeConformance(TypeConformance* conformance) SLANG_OVERRIDE
+ {
+ SLANG_UNUSED(conformance);
+ }
+};
+
+SpecializedComponentType::SpecializedComponentType(
+ ComponentType* base,
+ ComponentType::SpecializationInfo* specializationInfo,
+ List<SpecializationArg> const& specializationArgs,
+ DiagnosticSink* sink)
+ : ComponentType(base->getLinkage())
+ , m_base(base)
+ , m_specializationInfo(specializationInfo)
+ , m_specializationArgs(specializationArgs)
+{
+ m_optionSet.overrideWith(base->getOptionSet());
+
+ m_irModule = generateIRForSpecializedComponentType(this, sink);
+
+ // We need to account for the fact that a specialized
+ // entity like `myShader<SomeType>` needs to not only
+ // depend on the module(s) that `myShader` depends on,
+ // but also on any modules that `SomeType` depends on.
+ //
+ // We will set up a "collector" type that will be
+ // used to build a list of these additional modules.
+ //
+ SpecializationArgModuleCollector moduleCollector;
+
+ // We don't want to go adding additional requirements for
+ // modules that the base component type already includes,
+ // so we will add those to the set of modules in
+ // the collector before we starting trying to add others.
+ //
+ base->enumerateModules([&](Module* module) { moduleCollector.m_modulesSet.add(module); });
+
+ // In order to collect the additional modules, we need
+ // to inspect the specialization arguments and see what
+ // they depend on.
+ //
+ // Naively, it seems like we'd just want to iterate
+ // over `specializationArgs`, which gives the specialization
+ // arguments as the user supplied them. However, such
+ // an approach would have a subtle problem.
+ //
+ // If we have a generic entry point like:
+ //
+ // // In module A
+ // myShader<T : IThing>
+ //
+ //
+ // And the type `SomeType` that is being used as an argument doesn't
+ // directly conform to `IThing`:
+ //
+ // // In module B
+ // struct SomeType { ... }
+ //
+ // and the conformance of `SomeType` to `IThing` is
+ // coming from yet another module:
+ //
+ // // In module C
+ // import B;
+ // extension SomeType : IThing { ... }
+ //
+ // In this case, the specialized component for `myShader<SomeType>`
+ // needs to depend on all of:
+ //
+ // * Module A, because it defines `myShader`
+ // * Module B, because it defines `SomeType`
+ // * Module C, because it defines the conformance `SomeType : IThing`
+ //
+ // We thus need to iterate over a form of the specialization
+ // arguments that includes the "expanded" arguments like
+ // interface conformance witnesses that got added during
+ // semantic checking.
+ //
+ // The expanded arguments are being stored in the `specializationInfo`
+ // today (for use by downstream code generation), and the easiest
+ // way to walk that information and get to the leaf nodes where
+ // the expanded arguments are stored is to apply a visitor to
+ // the specialized component type we are in the middle of constructing.
+ //
+ moduleCollector.visitSpecialized(this);
+
+ // Now that we've collected our additional information, we can
+ // start to build up the final lists for the specialized component type.
+ //
+ // The starting point for our lists comes from the base component type.
+ //
+ m_moduleDependencies = base->getModuleDependencies();
+ m_fileDependencies = base->getFileDependencies();
+
+ Index baseRequirementCount = base->getRequirementCount();
+ for (Index r = 0; r < baseRequirementCount; r++)
+ {
+ m_requirements.add(base->getRequirement(r));
+ }
+
+ // The specialized component type will need to have additional
+ // dependencies and requirements based on the modules that
+ // were collected when looking at the specialization arguments.
+
+ // We want to avoid adding the same file dependency more than once.
+ //
+ HashSet<SourceFile*> fileDependencySet;
+ for (SourceFile* sourceFile : m_fileDependencies)
+ fileDependencySet.add(sourceFile);
+
+ for (auto module : moduleCollector.m_modulesList)
+ {
+ // The specialized component type will have an open (unsatisfied)
+ // requirement for each of the modules that its specialization
+ // arguments need.
+ //
+ // Note: what this means in practice is that the component type
+ // records that the given module(s) will need to be linked in
+ // before final code can be generated, but it importantly
+ // does not dictate the final placement of the parameters from
+ // those modules in the layout.
+ //
+ m_requirements.add(module);
+
+ // The speciialized component type will also have a dependency
+ // on all the files that any of the modules involved in
+ // it depend on (including those that are required but not
+ // yet linked in).
+ //
+ // The file path information is what a client would need to
+ // use to decide if kernel code is out of date compared to
+ // source files, so we want to include anything that could
+ // affect the validity of generated code.
+ //
+ for (SourceFile* sourceFile : module->getFileDependencies())
+ {
+ if (fileDependencySet.contains(sourceFile))
+ continue;
+ fileDependencySet.add(sourceFile);
+ m_fileDependencies.add(sourceFile);
+ }
+
+ // Finalyl we also add the module for the specialization arguments
+ // to the list of modules that would be used for legacy lookup
+ // operations where we need an implicit/default scope to use
+ // and want it to be expansive.
+ //
+ // TODO: This stuff really isn't worth keeping around long
+ // term, and we should ditch the entire "legacy lookup" idea.
+ //
+ m_moduleDependencies.add(module);
+ }
+
+ // Because we are specializing shader code, the mangled entry
+ // point names for this component type may be different than
+ // for the base component type (e.g., the mangled name for `f<int>`
+ // is different than that that of the generic `f` function
+ // itself).
+ //
+ // We will compute the mangled names of all the entry points and
+ // store them here, so that we don't have to do it on the fly.
+ // Because the `ComponentType` structure is hierarchical, we
+ // need to use a recursive visitor to compute the names,
+ // and we will define that visitor locally:
+ //
+ struct EntryPointMangledNameCollector : ComponentTypeVisitor
+ {
+ List<String>* mangledEntryPointNames;
+ List<String>* entryPointNameOverrides;
+
+ void visitEntryPoint(
+ EntryPoint* entryPoint,
+ EntryPoint::EntryPointSpecializationInfo* specializationInfo) SLANG_OVERRIDE
+ {
+ auto funcDeclRef = entryPoint->getFuncDeclRef();
+ if (specializationInfo)
+ funcDeclRef = specializationInfo->specializedFuncDeclRef;
+
+ (*mangledEntryPointNames).add(getMangledName(m_astBuilder, funcDeclRef));
+ (*entryPointNameOverrides).add(entryPoint->getEntryPointNameOverride(0));
+ }
+
+ void visitRenamedEntryPoint(
+ RenamedEntryPointComponentType* entryPoint,
+ EntryPoint::EntryPointSpecializationInfo* specializationInfo) SLANG_OVERRIDE
+ {
+ entryPoint->getBase()->acceptVisitor(this, specializationInfo);
+ (*entryPointNameOverrides).getLast() = entryPoint->getEntryPointNameOverride(0);
+ }
+
+ void visitModule(Module*, Module::ModuleSpecializationInfo*) SLANG_OVERRIDE {}
+ void visitComposite(
+ CompositeComponentType* composite,
+ CompositeComponentType::CompositeSpecializationInfo* specializationInfo) SLANG_OVERRIDE
+ {
+ visitChildren(composite, specializationInfo);
+ }
+ void visitSpecialized(SpecializedComponentType* specialized) SLANG_OVERRIDE
+ {
+ visitChildren(specialized);
+ }
+ void visitTypeConformance(TypeConformance* conformance) SLANG_OVERRIDE
+ {
+ SLANG_UNUSED(conformance);
+ }
+ EntryPointMangledNameCollector(ASTBuilder* astBuilder)
+ : m_astBuilder(astBuilder)
+ {
+ }
+ ASTBuilder* m_astBuilder;
+ };
+
+ // With the visitor defined, we apply it to ourself to compute
+ // and collect the mangled entry point names.
+ //
+ EntryPointMangledNameCollector collector(getLinkage()->getASTBuilder());
+ collector.mangledEntryPointNames = &m_entryPointMangledNames;
+ collector.entryPointNameOverrides = &m_entryPointNameOverrides;
+ collector.visitSpecialized(this);
+}
+
+void SpecializedComponentType::buildHash(DigestBuilder<SHA1>& builder)
+{
+ auto specializationArgCount = getSpecializationArgCount();
+ for (Index i = 0; i < specializationArgCount; ++i)
+ {
+ auto specializationArg = getSpecializationArg(i);
+ auto argString = specializationArg.val->toString();
+ builder.append(argString);
+ }
+
+ getBaseComponentType()->buildHash(builder);
+}
+
+void SpecializedComponentType::acceptVisitor(
+ ComponentTypeVisitor* visitor,
+ SpecializationInfo* specializationInfo)
+{
+ SLANG_ASSERT(specializationInfo == nullptr);
+ SLANG_UNUSED(specializationInfo);
+ visitor->visitSpecialized(this);
+}
+
+Index SpecializedComponentType::getRequirementCount()
+{
+ return m_requirements.getCount();
+}
+
+RefPtr<ComponentType> SpecializedComponentType::getRequirement(Index index)
+{
+ return m_requirements[index];
+}
+
+String SpecializedComponentType::getEntryPointMangledName(Index index)
+{
+ return m_entryPointMangledNames[index];
+}
+
+String SpecializedComponentType::getEntryPointNameOverride(Index index)
+{
+ return m_entryPointNameOverrides[index];
+}
+
+//
+// RenamedEntryPointComponentType
+//
+
+RenamedEntryPointComponentType::RenamedEntryPointComponentType(ComponentType* base, String newName)
+ : ComponentType(base->getLinkage()), m_base(base), m_entryPointNameOverride(newName)
+{
+}
+
+void RenamedEntryPointComponentType::acceptVisitor(
+ ComponentTypeVisitor* visitor,
+ SpecializationInfo* specializationInfo)
+{
+ visitor->visitRenamedEntryPoint(
+ this,
+ as<EntryPoint::EntryPointSpecializationInfo>(specializationInfo));
+}
+
+void RenamedEntryPointComponentType::buildHash(DigestBuilder<SHA1>& builder)
+{
+ SLANG_UNUSED(builder);
+}
+
+//
+// TypeConformance
+//
+
+TypeConformance::TypeConformance(
+ Linkage* linkage,
+ SubtypeWitness* witness,
+ Int confomrmanceIdOverride,
+ DiagnosticSink* sink)
+ : ComponentType(linkage)
+ , m_subtypeWitness(witness)
+ , m_conformanceIdOverride(confomrmanceIdOverride)
+{
+ addDepedencyFromWitness(witness);
+ m_irModule = generateIRForTypeConformance(this, m_conformanceIdOverride, sink);
+}
+
+void TypeConformance::addDepedencyFromWitness(SubtypeWitness* witness)
+{
+ if (auto declaredWitness = as<DeclaredSubtypeWitness>(witness))
+ {
+ auto declModule = getModule(declaredWitness->getDeclRef().getDecl());
+ m_moduleDependencyList.addDependency(declModule);
+ m_fileDependencyList.addDependency(declModule);
+ if (m_requirementSet.add(declModule))
+ {
+ m_requirements.add(declModule);
+ }
+ // TODO: handle the specialization arguments in declaredWitness->declRef.substitutions.
+ }
+ else if (auto transitiveWitness = as<TransitiveSubtypeWitness>(witness))
+ {
+ addDepedencyFromWitness(transitiveWitness->getMidToSup());
+ addDepedencyFromWitness(transitiveWitness->getSubToMid());
+ }
+ else if (auto conjunctionWitness = as<ConjunctionSubtypeWitness>(witness))
+ {
+ auto componentCount = conjunctionWitness->getComponentCount();
+ for (Index i = 0; i < componentCount; ++i)
+ {
+ auto w = as<SubtypeWitness>(conjunctionWitness->getComponentWitness(i));
+ if (w)
+ addDepedencyFromWitness(w);
+ }
+ }
+}
+
+ISlangUnknown* TypeConformance::getInterface(const Guid& guid)
+{
+ if (guid == slang::ITypeConformance::getTypeGuid())
+ return static_cast<slang::ITypeConformance*>(this);
+
+ return Super::getInterface(guid);
+}
+
+void TypeConformance::buildHash(DigestBuilder<SHA1>& builder)
+{
+ // TODO: Implement some kind of hashInto for Val then replace this
+ auto subtypeWitness = m_subtypeWitness->toString();
+
+ builder.append(subtypeWitness);
+ builder.append(m_conformanceIdOverride);
+}
+
+List<Module*> const& TypeConformance::getModuleDependencies()
+{
+ return m_moduleDependencyList.getModuleList();
+}
+
+List<SourceFile*> const& TypeConformance::getFileDependencies()
+{
+ return m_fileDependencyList.getFileList();
+}
+
+Index TypeConformance::getRequirementCount()
+{
+ return m_requirements.getCount();
+}
+
+RefPtr<ComponentType> TypeConformance::getRequirement(Index index)
+{
+ return m_requirements[index];
+}
+
+void TypeConformance::acceptVisitor(
+ ComponentTypeVisitor* visitor,
+ ComponentType::SpecializationInfo* specializationInfo)
+{
+ SLANG_UNUSED(specializationInfo);
+ visitor->visitTypeConformance(this);
+}
+
+RefPtr<ComponentType::SpecializationInfo> TypeConformance::_validateSpecializationArgsImpl(
+ SpecializationArg const* args,
+ Index argCount,
+ DiagnosticSink* sink)
+{
+ SLANG_UNUSED(args);
+ SLANG_UNUSED(argCount);
+ SLANG_UNUSED(sink);
+ return nullptr;
+}
+
+//
+// ComponentTypeVisitor
+//
+
+void ComponentTypeVisitor::visitChildren(
+ CompositeComponentType* composite,
+ CompositeComponentType::CompositeSpecializationInfo* specializationInfo)
+{
+ auto childCount = composite->getChildComponentCount();
+ for (Index ii = 0; ii < childCount; ++ii)
+ {
+ auto child = composite->getChildComponent(ii);
+ auto childSpecializationInfo =
+ specializationInfo ? specializationInfo->childInfos[ii] : nullptr;
+
+ child->acceptVisitor(this, childSpecializationInfo);
+ }
+}
+
+void ComponentTypeVisitor::visitChildren(SpecializedComponentType* specialized)
+{
+ specialized->getBaseComponentType()->acceptVisitor(this, specialized->getSpecializationInfo());
+}
+
+} // namespace Slang
diff --git a/source/slang/slang-linkable-impls.h b/source/slang/slang-linkable-impls.h
new file mode 100644
index 000000000..68a16587d
--- /dev/null
+++ b/source/slang/slang-linkable-impls.h
@@ -0,0 +1,566 @@
+// slang-linkable-impl.h
+#pragma once
+
+//
+// This file declares various implementations of linkable
+// objects (subclasses of `ComponentType`).
+//
+// Note that the base `ComponentType` class is declared
+// in `slang-linkable.h`.
+//
+// Note that the most important two classes of linkable
+// objects, `Module`s and `EntryPoint`s, have their own
+// headers: `slang-module.h` and `slang-entry-point.h`,
+// respectively.
+//
+
+#include "slang-entry-point.h"
+#include "slang-linkable.h"
+#include "slang-module.h"
+
+namespace Slang
+{
+/// A component type built up from other component types.
+class CompositeComponentType : public ComponentType
+{
+public:
+ static RefPtr<ComponentType> create(
+ Linkage* linkage,
+ List<RefPtr<ComponentType>> const& childComponents);
+
+ virtual void buildHash(DigestBuilder<SHA1>& builder) SLANG_OVERRIDE;
+
+ List<RefPtr<ComponentType>> const& getChildComponents() { return m_childComponents; };
+ Index getChildComponentCount() { return m_childComponents.getCount(); }
+ RefPtr<ComponentType> getChildComponent(Index index) { return m_childComponents[index]; }
+
+ Index getEntryPointCount() SLANG_OVERRIDE;
+ RefPtr<EntryPoint> getEntryPoint(Index index) SLANG_OVERRIDE;
+ String getEntryPointMangledName(Index index) SLANG_OVERRIDE;
+ String getEntryPointNameOverride(Index index) SLANG_OVERRIDE;
+
+ Index getShaderParamCount() SLANG_OVERRIDE;
+ ShaderParamInfo getShaderParam(Index index) SLANG_OVERRIDE;
+
+ SLANG_NO_THROW Index SLANG_MCALL getSpecializationParamCount() SLANG_OVERRIDE;
+ SpecializationParam const& getSpecializationParam(Index index) SLANG_OVERRIDE;
+
+ Index getRequirementCount() SLANG_OVERRIDE;
+ RefPtr<ComponentType> getRequirement(Index index) SLANG_OVERRIDE;
+
+ List<Module*> const& getModuleDependencies() SLANG_OVERRIDE;
+ List<SourceFile*> const& getFileDependencies() SLANG_OVERRIDE;
+
+ class CompositeSpecializationInfo : public SpecializationInfo
+ {
+ public:
+ List<RefPtr<SpecializationInfo>> childInfos;
+ };
+
+protected:
+ void acceptVisitor(ComponentTypeVisitor* visitor, SpecializationInfo* specializationInfo)
+ SLANG_OVERRIDE;
+
+
+ RefPtr<SpecializationInfo> _validateSpecializationArgsImpl(
+ SpecializationArg const* args,
+ Index argCount,
+ DiagnosticSink* sink) SLANG_OVERRIDE;
+
+public:
+ CompositeComponentType(Linkage* linkage, List<RefPtr<ComponentType>> const& childComponents);
+
+private:
+ List<RefPtr<ComponentType>> m_childComponents;
+
+ // The following arrays hold the concatenated entry points, parameters,
+ // etc. from the child components. This approach allows for reasonably
+ // fast (constant time) access through operations like `getShaderParam`,
+ // but means that the memory usage of a composite is proportional to
+ // the sum of the memory usage of the children, rather than being fixed
+ // by the number of children (as it would be if we just stored
+ // `m_childComponents`).
+ //
+ // TODO: We could conceivably build some O(numChildren) arrays that
+ // support binary-search to provide logarithmic-time access to entry
+ // points, parameters, etc. while giving a better overall memory usage.
+ //
+ List<EntryPoint*> m_entryPoints;
+ List<String> m_entryPointMangledNames;
+ List<String> m_entryPointNameOverrides;
+ List<ShaderParamInfo> m_shaderParams;
+ List<SpecializationParam> m_specializationParams;
+ List<ComponentType*> m_requirements;
+
+ ModuleDependencyList m_moduleDependencyList;
+ FileDependencyList m_fileDependencyList;
+};
+
+/// A component type created by specializing another component type.
+class SpecializedComponentType : public ComponentType
+{
+public:
+ SpecializedComponentType(
+ ComponentType* base,
+ SpecializationInfo* specializationInfo,
+ List<SpecializationArg> const& specializationArgs,
+ DiagnosticSink* sink);
+
+ virtual void buildHash(DigestBuilder<SHA1>& builer) SLANG_OVERRIDE;
+
+ /// Get the base (unspecialized) component type that is being specialized.
+ RefPtr<ComponentType> getBaseComponentType() { return m_base; }
+
+ RefPtr<SpecializationInfo> getSpecializationInfo() { return m_specializationInfo; }
+
+ /// Get the number of arguments supplied for existential type parameters.
+ ///
+ /// Note that the number of arguments may not match the number of parameters.
+ /// In particular, an unspecialized entry point may have many parameters, but zero arguments.
+ Index getSpecializationArgCount() { return m_specializationArgs.getCount(); }
+
+ /// Get the existential type argument (type and witness table) at `index`.
+ SpecializationArg const& getSpecializationArg(Index index)
+ {
+ return m_specializationArgs[index];
+ }
+
+ /// Get an array of all existential type arguments.
+ SpecializationArg const* getSpecializationArgs() { return m_specializationArgs.getBuffer(); }
+
+ Index getEntryPointCount() SLANG_OVERRIDE { return m_base->getEntryPointCount(); }
+ RefPtr<EntryPoint> getEntryPoint(Index index) SLANG_OVERRIDE
+ {
+ return m_base->getEntryPoint(index);
+ }
+ String getEntryPointMangledName(Index index) SLANG_OVERRIDE;
+ String getEntryPointNameOverride(Index index) SLANG_OVERRIDE;
+
+ Index getShaderParamCount() SLANG_OVERRIDE { return m_base->getShaderParamCount(); }
+ ShaderParamInfo getShaderParam(Index index) SLANG_OVERRIDE
+ {
+ return m_base->getShaderParam(index);
+ }
+
+ SLANG_NO_THROW Index SLANG_MCALL getSpecializationParamCount() SLANG_OVERRIDE { return 0; }
+ SpecializationParam const& getSpecializationParam(Index index) SLANG_OVERRIDE
+ {
+ SLANG_UNUSED(index);
+ static SpecializationParam dummy;
+ return dummy;
+ }
+
+ Index getRequirementCount() SLANG_OVERRIDE;
+ RefPtr<ComponentType> getRequirement(Index index) SLANG_OVERRIDE;
+
+ List<Module*> const& getModuleDependencies() SLANG_OVERRIDE { return m_moduleDependencies; }
+ List<SourceFile*> const& getFileDependencies() SLANG_OVERRIDE { return m_fileDependencies; }
+
+ RefPtr<IRModule> getIRModule() { return m_irModule; }
+
+ void acceptVisitor(ComponentTypeVisitor* visitor, SpecializationInfo* specializationInfo)
+ SLANG_OVERRIDE;
+
+protected:
+ RefPtr<SpecializationInfo> _validateSpecializationArgsImpl(
+ SpecializationArg const* args,
+ Index argCount,
+ DiagnosticSink* sink) SLANG_OVERRIDE
+ {
+ SLANG_UNUSED(args);
+ SLANG_UNUSED(argCount);
+ SLANG_UNUSED(sink);
+ return nullptr;
+ }
+
+private:
+ RefPtr<ComponentType> m_base;
+ RefPtr<SpecializationInfo> m_specializationInfo;
+ SpecializationArgs m_specializationArgs;
+ RefPtr<IRModule> m_irModule;
+
+ List<String> m_entryPointMangledNames;
+ List<String> m_entryPointNameOverrides;
+
+ List<Module*> m_moduleDependencies;
+ List<SourceFile*> m_fileDependencies;
+ List<RefPtr<ComponentType>> m_requirements;
+};
+
+class RenamedEntryPointComponentType : public ComponentType
+{
+public:
+ using Super = ComponentType;
+
+ RenamedEntryPointComponentType(ComponentType* base, String newName);
+
+ ComponentType* getBase() { return m_base.Ptr(); }
+
+ // Forward `IComponentType` methods
+
+ SLANG_NO_THROW slang::ISession* SLANG_MCALL getSession() SLANG_OVERRIDE
+ {
+ return Super::getSession();
+ }
+
+ SLANG_NO_THROW slang::ProgramLayout* SLANG_MCALL
+ getLayout(SlangInt targetIndex, slang::IBlob** outDiagnostics) SLANG_OVERRIDE
+ {
+ return Super::getLayout(targetIndex, outDiagnostics);
+ }
+
+ SLANG_NO_THROW SlangResult SLANG_MCALL getEntryPointCode(
+ SlangInt entryPointIndex,
+ SlangInt targetIndex,
+ slang::IBlob** outCode,
+ slang::IBlob** outDiagnostics) SLANG_OVERRIDE
+ {
+ return Super::getEntryPointCode(entryPointIndex, targetIndex, outCode, outDiagnostics);
+ }
+
+ SLANG_NO_THROW SlangResult SLANG_MCALL specialize(
+ slang::SpecializationArg const* specializationArgs,
+ SlangInt specializationArgCount,
+ slang::IComponentType** outSpecializedComponentType,
+ ISlangBlob** outDiagnostics) SLANG_OVERRIDE
+ {
+ return Super::specialize(
+ specializationArgs,
+ specializationArgCount,
+ outSpecializedComponentType,
+ outDiagnostics);
+ }
+
+ SLANG_NO_THROW SlangResult SLANG_MCALL
+ renameEntryPoint(const char* newName, slang::IComponentType** outEntryPoint) SLANG_OVERRIDE
+ {
+ return Super::renameEntryPoint(newName, outEntryPoint);
+ }
+
+ SLANG_NO_THROW SlangResult SLANG_MCALL
+ link(slang::IComponentType** outLinkedComponentType, ISlangBlob** outDiagnostics) SLANG_OVERRIDE
+ {
+ return Super::link(outLinkedComponentType, outDiagnostics);
+ }
+
+ SLANG_NO_THROW SlangResult SLANG_MCALL getEntryPointHostCallable(
+ int entryPointIndex,
+ int targetIndex,
+ ISlangSharedLibrary** outSharedLibrary,
+ slang::IBlob** outDiagnostics) SLANG_OVERRIDE
+ {
+ return Super::getEntryPointHostCallable(
+ entryPointIndex,
+ targetIndex,
+ outSharedLibrary,
+ outDiagnostics);
+ }
+
+ List<Module*> const& getModuleDependencies() SLANG_OVERRIDE
+ {
+ return m_base->getModuleDependencies();
+ }
+ List<SourceFile*> const& getFileDependencies() SLANG_OVERRIDE
+ {
+ return m_base->getFileDependencies();
+ }
+
+ SLANG_NO_THROW Index SLANG_MCALL getSpecializationParamCount() SLANG_OVERRIDE
+ {
+ return m_base->getSpecializationParamCount();
+ }
+
+ SpecializationParam const& getSpecializationParam(Index index) SLANG_OVERRIDE
+ {
+ return m_base->getSpecializationParam(index);
+ }
+
+ Index getRequirementCount() SLANG_OVERRIDE { return m_base->getRequirementCount(); }
+ RefPtr<ComponentType> getRequirement(Index index) SLANG_OVERRIDE
+ {
+ return m_base->getRequirement(index);
+ }
+ Index getEntryPointCount() SLANG_OVERRIDE { return m_base->getEntryPointCount(); }
+ RefPtr<EntryPoint> getEntryPoint(Index index) SLANG_OVERRIDE
+ {
+ return m_base->getEntryPoint(index);
+ }
+ String getEntryPointMangledName(Index index) SLANG_OVERRIDE
+ {
+ return m_base->getEntryPointMangledName(index);
+ }
+ String getEntryPointNameOverride(Index index) SLANG_OVERRIDE
+ {
+ SLANG_UNUSED(index);
+ SLANG_ASSERT(index == 0);
+ return m_entryPointNameOverride;
+ }
+
+ Index getShaderParamCount() SLANG_OVERRIDE { return m_base->getShaderParamCount(); }
+ ShaderParamInfo getShaderParam(Index index) SLANG_OVERRIDE
+ {
+ return m_base->getShaderParam(index);
+ }
+
+ void acceptVisitor(ComponentTypeVisitor* visitor, SpecializationInfo* specializationInfo)
+ SLANG_OVERRIDE;
+
+ virtual void buildHash(DigestBuilder<SHA1>& builder) SLANG_OVERRIDE;
+
+private:
+ RefPtr<ComponentType> m_base;
+ String m_entryPointNameOverride;
+
+protected:
+ RefPtr<SpecializationInfo> _validateSpecializationArgsImpl(
+ SpecializationArg const* args,
+ Index argCount,
+ DiagnosticSink* sink) SLANG_OVERRIDE
+ {
+ return m_base->_validateSpecializationArgsImpl(args, argCount, sink);
+ }
+};
+
+class TypeConformance : public ComponentType, public slang::ITypeConformance
+{
+ typedef ComponentType Super;
+
+public:
+ SLANG_REF_OBJECT_IUNKNOWN_ALL
+
+ ISlangUnknown* getInterface(const Guid& guid);
+
+ TypeConformance(
+ Linkage* linkage,
+ SubtypeWitness* witness,
+ Int confomrmanceIdOverride,
+ DiagnosticSink* sink);
+
+ // Forward `IComponentType` methods
+
+ SLANG_NO_THROW slang::ISession* SLANG_MCALL getSession() SLANG_OVERRIDE
+ {
+ return Super::getSession();
+ }
+
+ SLANG_NO_THROW slang::ProgramLayout* SLANG_MCALL
+ getLayout(SlangInt targetIndex, slang::IBlob** outDiagnostics) SLANG_OVERRIDE
+ {
+ return Super::getLayout(targetIndex, outDiagnostics);
+ }
+
+ SLANG_NO_THROW SlangResult SLANG_MCALL getEntryPointCode(
+ SlangInt entryPointIndex,
+ SlangInt targetIndex,
+ slang::IBlob** outCode,
+ slang::IBlob** outDiagnostics) SLANG_OVERRIDE
+ {
+ return Super::getEntryPointCode(entryPointIndex, targetIndex, outCode, outDiagnostics);
+ }
+
+ SLANG_NO_THROW SlangResult SLANG_MCALL getTargetCode(
+ SlangInt targetIndex,
+ slang::IBlob** outCode,
+ slang::IBlob** outDiagnostics) SLANG_OVERRIDE
+ {
+ return Super::getTargetCode(targetIndex, outCode, outDiagnostics);
+ }
+
+ SLANG_NO_THROW SlangResult SLANG_MCALL getEntryPointMetadata(
+ SlangInt entryPointIndex,
+ SlangInt targetIndex,
+ slang::IMetadata** outMetadata,
+ slang::IBlob** outDiagnostics) SLANG_OVERRIDE
+ {
+ return Super::getEntryPointMetadata(
+ entryPointIndex,
+ targetIndex,
+ outMetadata,
+ outDiagnostics);
+ }
+
+ SLANG_NO_THROW SlangResult SLANG_MCALL getTargetMetadata(
+ SlangInt targetIndex,
+ slang::IMetadata** outMetadata,
+ slang::IBlob** outDiagnostics) SLANG_OVERRIDE
+ {
+ return Super::getTargetMetadata(targetIndex, outMetadata, outDiagnostics);
+ }
+
+ SLANG_NO_THROW SlangResult SLANG_MCALL getEntryPointCompileResult(
+ SlangInt entryPointIndex,
+ SlangInt targetIndex,
+ slang::ICompileResult** outCompileResult,
+ slang::IBlob** outDiagnostics) SLANG_OVERRIDE
+ {
+ return Super::getEntryPointCompileResult(
+ entryPointIndex,
+ targetIndex,
+ outCompileResult,
+ outDiagnostics);
+ }
+
+ SLANG_NO_THROW SlangResult SLANG_MCALL getTargetCompileResult(
+ SlangInt targetIndex,
+ slang::ICompileResult** outCompileResult,
+ slang::IBlob** outDiagnostics) SLANG_OVERRIDE
+ {
+ return Super::getTargetCompileResult(targetIndex, outCompileResult, outDiagnostics);
+ }
+
+ SLANG_NO_THROW SlangResult SLANG_MCALL getResultAsFileSystem(
+ SlangInt entryPointIndex,
+ SlangInt targetIndex,
+ ISlangMutableFileSystem** outFileSystem) SLANG_OVERRIDE
+ {
+ return Super::getResultAsFileSystem(entryPointIndex, targetIndex, outFileSystem);
+ }
+
+ SLANG_NO_THROW SlangResult SLANG_MCALL specialize(
+ slang::SpecializationArg const* specializationArgs,
+ SlangInt specializationArgCount,
+ slang::IComponentType** outSpecializedComponentType,
+ ISlangBlob** outDiagnostics) SLANG_OVERRIDE
+ {
+ return Super::specialize(
+ specializationArgs,
+ specializationArgCount,
+ outSpecializedComponentType,
+ outDiagnostics);
+ }
+
+ SLANG_NO_THROW SlangResult SLANG_MCALL
+ renameEntryPoint(const char* newName, slang::IComponentType** outEntryPoint) SLANG_OVERRIDE
+ {
+ return Super::renameEntryPoint(newName, outEntryPoint);
+ }
+
+ SLANG_NO_THROW SlangResult SLANG_MCALL
+ link(slang::IComponentType** outLinkedComponentType, ISlangBlob** outDiagnostics) SLANG_OVERRIDE
+ {
+ return Super::link(outLinkedComponentType, outDiagnostics);
+ }
+
+ virtual SLANG_NO_THROW SlangResult SLANG_MCALL linkWithOptions(
+ slang::IComponentType** outLinkedComponentType,
+ uint32_t count,
+ slang::CompilerOptionEntry* entries,
+ ISlangBlob** outDiagnostics) override
+ {
+ return Super::linkWithOptions(outLinkedComponentType, count, entries, outDiagnostics);
+ }
+
+ SLANG_NO_THROW SlangResult SLANG_MCALL getEntryPointHostCallable(
+ int entryPointIndex,
+ int targetIndex,
+ ISlangSharedLibrary** outSharedLibrary,
+ slang::IBlob** outDiagnostics) SLANG_OVERRIDE
+ {
+ return Super::getEntryPointHostCallable(
+ entryPointIndex,
+ targetIndex,
+ outSharedLibrary,
+ outDiagnostics);
+ }
+
+ SLANG_NO_THROW void SLANG_MCALL getEntryPointHash(
+ SlangInt entryPointIndex,
+ SlangInt targetIndex,
+ slang::IBlob** outHash) SLANG_OVERRIDE
+ {
+ return Super::getEntryPointHash(entryPointIndex, targetIndex, outHash);
+ }
+
+ virtual void buildHash(DigestBuilder<SHA1>& builder) SLANG_OVERRIDE;
+
+ List<Module*> const& getModuleDependencies() SLANG_OVERRIDE;
+ List<SourceFile*> const& getFileDependencies() SLANG_OVERRIDE;
+
+ SLANG_NO_THROW Index SLANG_MCALL getSpecializationParamCount() SLANG_OVERRIDE { return 0; }
+
+ /// Get the existential type parameter at `index`.
+ SpecializationParam const& getSpecializationParam(Index /*index*/) SLANG_OVERRIDE
+ {
+ static SpecializationParam emptyParam;
+ return emptyParam;
+ }
+
+ Index getRequirementCount() SLANG_OVERRIDE;
+ RefPtr<ComponentType> getRequirement(Index index) SLANG_OVERRIDE;
+ Index getEntryPointCount() SLANG_OVERRIDE { return 0; };
+ RefPtr<EntryPoint> getEntryPoint(Index index) SLANG_OVERRIDE
+ {
+ SLANG_UNUSED(index);
+ return nullptr;
+ }
+ String getEntryPointMangledName(Index /*index*/) SLANG_OVERRIDE { return ""; }
+ String getEntryPointNameOverride(Index /*index*/) SLANG_OVERRIDE { return ""; }
+
+ Index getShaderParamCount() SLANG_OVERRIDE { return 0; }
+ ShaderParamInfo getShaderParam(Index index) SLANG_OVERRIDE
+ {
+ SLANG_UNUSED(index);
+ return ShaderParamInfo();
+ }
+
+ SubtypeWitness* getSubtypeWitness() { return m_subtypeWitness; }
+ IRModule* getIRModule() { return m_irModule.Ptr(); }
+
+protected:
+ void acceptVisitor(ComponentTypeVisitor* visitor, SpecializationInfo* specializationInfo)
+ SLANG_OVERRIDE;
+
+ RefPtr<SpecializationInfo> _validateSpecializationArgsImpl(
+ SpecializationArg const* args,
+ Index argCount,
+ DiagnosticSink* sink) SLANG_OVERRIDE;
+
+private:
+ SubtypeWitness* m_subtypeWitness;
+ ModuleDependencyList m_moduleDependencyList;
+ FileDependencyList m_fileDependencyList;
+ List<RefPtr<Module>> m_requirements;
+ HashSet<Module*> m_requirementSet;
+ RefPtr<IRModule> m_irModule;
+ Int m_conformanceIdOverride;
+ void addDepedencyFromWitness(SubtypeWitness* witness);
+};
+
+/// A visitor for use with `ComponentType`s, allowing dispatch over the concrete subclasses.
+class ComponentTypeVisitor
+{
+public:
+ // The following methods should be overriden in a concrete subclass
+ // to customize how it acts on each of the concrete types of component.
+ //
+ // In cases where the application wants to simply "recurse" on a
+ // composite, specialized, or legacy component type it can use
+ // the `visitChildren` methods below.
+ //
+ virtual void visitEntryPoint(
+ EntryPoint* entryPoint,
+ EntryPoint::EntryPointSpecializationInfo* specializationInfo) = 0;
+ virtual void visitModule(
+ Module* module,
+ Module::ModuleSpecializationInfo* specializationInfo) = 0;
+ virtual void visitComposite(
+ CompositeComponentType* composite,
+ CompositeComponentType::CompositeSpecializationInfo* specializationInfo) = 0;
+ virtual void visitSpecialized(SpecializedComponentType* specialized) = 0;
+ virtual void visitTypeConformance(TypeConformance* conformance) = 0;
+ virtual void visitRenamedEntryPoint(
+ RenamedEntryPointComponentType* renamedEntryPoint,
+ EntryPoint::EntryPointSpecializationInfo* specializationInfo) = 0;
+
+protected:
+ // These helpers can be used to recurse into the logical children of a
+ // component type, and are useful for the common case where a visitor
+ // only cares about a few leaf cases.
+ //
+ void visitChildren(
+ CompositeComponentType* composite,
+ CompositeComponentType::CompositeSpecializationInfo* specializationInfo);
+ void visitChildren(SpecializedComponentType* specialized);
+};
+
+} // namespace Slang
diff --git a/source/slang/slang-linkable.cpp b/source/slang/slang-linkable.cpp
new file mode 100644
index 000000000..da4cec823
--- /dev/null
+++ b/source/slang/slang-linkable.cpp
@@ -0,0 +1,1037 @@
+// slang-linkable.cpp
+#include "slang-linkable.h"
+
+#include "compiler-core/slang-artifact-container-util.h"
+#include "compiler-core/slang-artifact-desc-util.h"
+#include "compiler-core/slang-artifact-impl.h"
+#include "core/slang-char-util.h"
+#include "core/slang-memory-file-system.h"
+#include "slang-check-impl.h"
+#include "slang-compiler.h"
+#include "slang-mangle.h"
+
+namespace Slang
+{
+
+//
+// ModuleDependencyList
+//
+
+void ModuleDependencyList::addDependency(Module* module)
+{
+ // If we depend on a module, then we depend on everything it depends on.
+ //
+ // Note: We are processing these sub-depenencies before adding the
+ // `module` itself, so that in the common case a module will always
+ // appear *after* everything it depends on.
+ //
+ // However, this rule is being violated in the compiler right now because
+ // the modules for hte top-level translation units of a compile request
+ // will be added to the list first (using `addLeafDependency`) to
+ // maintain compatibility with old behavior. This may be fixed later.
+ //
+ for (auto subDependency : module->getModuleDependencyList())
+ {
+ _addDependency(subDependency);
+ }
+ _addDependency(module);
+}
+
+void ModuleDependencyList::addLeafDependency(Module* module)
+{
+ _addDependency(module);
+}
+
+void ModuleDependencyList::_addDependency(Module* module)
+{
+ if (m_moduleSet.contains(module))
+ return;
+
+ m_moduleList.add(module);
+ m_moduleSet.add(module);
+}
+
+//
+// FileDependencyList
+//
+
+void FileDependencyList::addDependency(SourceFile* sourceFile)
+{
+ if (m_fileSet.contains(sourceFile))
+ return;
+
+ m_fileList.add(sourceFile);
+ m_fileSet.add(sourceFile);
+}
+
+void FileDependencyList::addDependency(Module* module)
+{
+ for (SourceFile* sourceFile : module->getFileDependencyList())
+ {
+ addDependency(sourceFile);
+ }
+}
+
+//
+// ComponentType
+//
+
+ComponentType::ComponentType(Linkage* linkage)
+ : m_linkage(linkage)
+{
+}
+
+ComponentType* asInternal(slang::IComponentType* inComponentType)
+{
+ // Note: we use a `queryInterface` here instead of just a `static_cast`
+ // to ensure that the `IComponentType` we get is the preferred/canonical
+ // one, which shares its address with the `ComponentType`.
+ //
+ // TODO: An alternative choice here would be to have a "magic" IID that
+ // we pass into `queryInterface` that returns the `ComponentType` directly
+ // (without even `addRef`-ing it).
+ //
+ ComPtr<slang::IComponentType> componentType;
+ inComponentType->queryInterface(SLANG_IID_PPV_ARGS(componentType.writeRef()));
+ return static_cast<ComponentType*>(componentType.get());
+}
+
+ISlangUnknown* ComponentType::getInterface(Guid const& guid)
+{
+ if (guid == ISlangUnknown::getTypeGuid() || guid == slang::IComponentType::getTypeGuid())
+ {
+ return static_cast<slang::IComponentType*>(this);
+ }
+ if (guid == IModulePrecompileService_Experimental::getTypeGuid())
+ return static_cast<slang::IModulePrecompileService_Experimental*>(this);
+ if (guid == IComponentType2::getTypeGuid())
+ return static_cast<slang::IComponentType2*>(this);
+ return nullptr;
+}
+
+SLANG_NO_THROW slang::ISession* SLANG_MCALL ComponentType::getSession()
+{
+ return m_linkage;
+}
+
+SLANG_NO_THROW slang::ProgramLayout* SLANG_MCALL
+ComponentType::getLayout(Int targetIndex, slang::IBlob** outDiagnostics)
+{
+ auto linkage = getLinkage();
+ if (targetIndex < 0 || targetIndex >= linkage->targets.getCount())
+ return nullptr;
+ auto target = linkage->targets[targetIndex];
+
+ DiagnosticSink sink(linkage->getSourceManager(), Lexer::sourceLocationLexer);
+ auto programLayout = getTargetProgram(target)->getOrCreateLayout(&sink);
+ sink.getBlobIfNeeded(outDiagnostics);
+
+ return asExternal(programLayout);
+}
+
+static ICastable* _findDiagnosticRepresentation(IArtifact* artifact)
+{
+ if (auto rep = findAssociatedRepresentation<IArtifactDiagnostics>(artifact))
+ {
+ return rep;
+ }
+
+ for (auto associated : artifact->getAssociated())
+ {
+ if (isDerivedFrom(associated->getDesc().payload, ArtifactPayload::Diagnostics))
+ {
+ return associated;
+ }
+ }
+ return nullptr;
+}
+
+static IArtifact* _findObfuscatedSourceMap(IArtifact* artifact)
+{
+ // If we find any obfuscated source maps, we are done
+ for (auto associated : artifact->getAssociated())
+ {
+ const auto desc = associated->getDesc();
+
+ if (isDerivedFrom(desc.payload, ArtifactPayload::SourceMap) &&
+ isDerivedFrom(desc.style, ArtifactStyle::Obfuscated))
+ {
+ return associated;
+ }
+ }
+ return nullptr;
+}
+
+SLANG_NO_THROW SlangResult SLANG_MCALL ComponentType::getResultAsFileSystem(
+ SlangInt entryPointIndex,
+ Int targetIndex,
+ ISlangMutableFileSystem** outFileSystem)
+{
+ ComPtr<ISlangBlob> diagnostics;
+ ComPtr<ISlangBlob> code;
+
+ SLANG_RETURN_ON_FAIL(
+ getEntryPointCode(entryPointIndex, targetIndex, diagnostics.writeRef(), code.writeRef()));
+
+ auto linkage = getLinkage();
+
+ auto target = linkage->targets[targetIndex];
+
+ auto targetProgram = getTargetProgram(target);
+
+ IArtifact* artifact = targetProgram->getExistingEntryPointResult(entryPointIndex);
+
+ // Add diagnostics id needs be...
+ if (diagnostics && !_findDiagnosticRepresentation(artifact))
+ {
+ // Add as an associated
+
+ auto diagnosticsArtifact = Artifact::create(
+ ArtifactDesc::make(Artifact::Kind::HumanText, ArtifactPayload::Diagnostics));
+ diagnosticsArtifact->addRepresentationUnknown(diagnostics);
+
+ artifact->addAssociated(diagnosticsArtifact);
+
+ SLANG_ASSERT(diagnosticsArtifact == _findDiagnosticRepresentation(artifact));
+ }
+
+ // Add obfuscated source maps
+ if (!_findObfuscatedSourceMap(artifact))
+ {
+ List<IRModule*> irModules;
+ enumerateIRModules([&](IRModule* irModule) -> void { irModules.add(irModule); });
+
+ for (auto irModule : irModules)
+ {
+ if (auto obfuscatedSourceMap = irModule->getObfuscatedSourceMap())
+ {
+ auto artifactDesc = ArtifactDesc::make(
+ ArtifactKind::Json,
+ ArtifactPayload::SourceMap,
+ ArtifactStyle::Obfuscated);
+
+ // Create the source map artifact
+ auto sourceMapArtifact = Artifact::create(
+ artifactDesc,
+ obfuscatedSourceMap->get().m_file.getUnownedSlice());
+
+ sourceMapArtifact->addRepresentation(obfuscatedSourceMap);
+
+ // associate with the artifact
+ artifact->addAssociated(sourceMapArtifact);
+ }
+ }
+ }
+
+ // Turn into a file system and return
+ ComPtr<ISlangMutableFileSystem> fileSystem(new MemoryFileSystem);
+
+ // Filter the containerArtifact into things that can be written
+ ComPtr<IArtifact> writeArtifact;
+ SLANG_RETURN_ON_FAIL(ArtifactContainerUtil::filter(artifact, writeArtifact));
+ SLANG_RETURN_ON_FAIL(ArtifactContainerUtil::writeContainer(writeArtifact, "", fileSystem));
+
+ *outFileSystem = fileSystem.detach();
+
+ return SLANG_OK;
+}
+
+SLANG_NO_THROW SlangResult SLANG_MCALL ComponentType::getEntryPointCode(
+ SlangInt entryPointIndex,
+ Int targetIndex,
+ slang::IBlob** outCode,
+ slang::IBlob** outDiagnostics)
+{
+ auto linkage = getLinkage();
+ if (targetIndex < 0 || targetIndex >= linkage->targets.getCount())
+ return SLANG_E_INVALID_ARG;
+ auto target = linkage->targets[targetIndex];
+
+ auto targetProgram = getTargetProgram(target);
+
+ DiagnosticSink sink(linkage->getSourceManager(), Lexer::sourceLocationLexer);
+ applySettingsToDiagnosticSink(&sink, &sink, linkage->m_optionSet);
+ applySettingsToDiagnosticSink(&sink, &sink, m_optionSet);
+
+ IArtifact* artifact = targetProgram->getOrCreateEntryPointResult(entryPointIndex, &sink);
+ sink.getBlobIfNeeded(outDiagnostics);
+
+ if (artifact == nullptr)
+ return SLANG_FAIL;
+
+ return artifact->loadBlob(ArtifactKeep::Yes, outCode);
+}
+
+SLANG_NO_THROW void SLANG_MCALL ComponentType::getEntryPointHash(
+ SlangInt entryPointIndex,
+ SlangInt targetIndex,
+ slang::IBlob** outHash)
+{
+ DigestBuilder<SHA1> builder;
+
+ // A note on enums that may be hashed in as part of the following two function calls:
+ //
+ // While enums are not guaranteed to be encoded the same way across all versions of
+ // the compiler, part of hashing the linkage is hashing in the compiler version.
+ // Consequently, any encoding differences as a result of different compiler versions
+ // will already be reflected in the resulting hash.
+ getLinkage()->buildHash(builder, targetIndex);
+
+ buildHash(builder);
+
+ // Add the name and name override for the specified entry point to the hash.
+ auto entryPoint = getEntryPoint(entryPointIndex);
+ if (entryPoint)
+ {
+ auto entryPointName = entryPoint->getName()->text;
+ builder.append(entryPointName);
+ auto entryPointMangledName = getEntryPointMangledName(entryPointIndex);
+ builder.append(entryPointMangledName);
+ auto entryPointNameOverride = getEntryPointNameOverride(entryPointIndex);
+ builder.append(entryPointNameOverride);
+ }
+
+ auto hash = builder.finalize().toBlob();
+ *outHash = hash.detach();
+}
+
+SLANG_NO_THROW SlangResult SLANG_MCALL ComponentType::getEntryPointHostCallable(
+ int entryPointIndex,
+ int targetIndex,
+ ISlangSharedLibrary** outSharedLibrary,
+ slang::IBlob** outDiagnostics)
+{
+ auto linkage = getLinkage();
+ if (targetIndex < 0 || targetIndex >= linkage->targets.getCount())
+ return SLANG_E_INVALID_ARG;
+ auto target = linkage->targets[targetIndex];
+
+ auto targetProgram = getTargetProgram(target);
+
+ DiagnosticSink sink(linkage->getSourceManager(), Lexer::sourceLocationLexer);
+ applySettingsToDiagnosticSink(&sink, &sink, m_optionSet);
+
+ IArtifact* artifact = targetProgram->getOrCreateEntryPointResult(entryPointIndex, &sink);
+ sink.getBlobIfNeeded(outDiagnostics);
+
+ if (artifact == nullptr)
+ return SLANG_FAIL;
+
+ return artifact->loadSharedLibrary(ArtifactKeep::Yes, outSharedLibrary);
+}
+
+SLANG_NO_THROW SlangResult SLANG_MCALL ComponentType::getEntryPointMetadata(
+ SlangInt entryPointIndex,
+ Int targetIndex,
+ slang::IMetadata** outMetadata,
+ slang::IBlob** outDiagnostics)
+{
+ auto linkage = getLinkage();
+ if (targetIndex < 0 || targetIndex >= linkage->targets.getCount())
+ return SLANG_E_INVALID_ARG;
+ auto target = linkage->targets[targetIndex];
+
+ auto targetProgram = getTargetProgram(target);
+
+ DiagnosticSink sink(linkage->getSourceManager(), Lexer::sourceLocationLexer);
+ applySettingsToDiagnosticSink(&sink, &sink, linkage->m_optionSet);
+ applySettingsToDiagnosticSink(&sink, &sink, m_optionSet);
+
+ IArtifact* artifact = targetProgram->getOrCreateEntryPointResult(entryPointIndex, &sink);
+ sink.getBlobIfNeeded(outDiagnostics);
+
+ if (artifact == nullptr)
+ return SLANG_E_NOT_AVAILABLE;
+
+ auto metadata = findAssociatedRepresentation<IArtifactPostEmitMetadata>(artifact);
+ if (!metadata)
+ return SLANG_E_NOT_AVAILABLE;
+
+ *outMetadata = static_cast<slang::IMetadata*>(metadata);
+ (*outMetadata)->addRef();
+ return SLANG_OK;
+}
+
+RefPtr<ComponentType> ComponentType::specialize(
+ SpecializationArg const* inSpecializationArgs,
+ SlangInt specializationArgCount,
+ DiagnosticSink* sink)
+{
+ if (specializationArgCount == 0)
+ {
+ return this;
+ }
+
+ List<SpecializationArg> specializationArgs;
+ specializationArgs.addRange(inSpecializationArgs, specializationArgCount);
+
+ // We next need to validate that the specialization arguments
+ // make sense, and also expand them to include any derived data
+ // (e.g., interface conformance witnesses) that doesn't get
+ // passed explicitly through the API interface.
+ //
+ RefPtr<SpecializationInfo> specializationInfo =
+ _validateSpecializationArgs(specializationArgs.getBuffer(), specializationArgCount, sink);
+
+ return new SpecializedComponentType(this, specializationInfo, specializationArgs, sink);
+}
+
+SLANG_NO_THROW SlangResult SLANG_MCALL ComponentType::specialize(
+ slang::SpecializationArg const* specializationArgs,
+ SlangInt specializationArgCount,
+ slang::IComponentType** outSpecializedComponentType,
+ ISlangBlob** outDiagnostics)
+{
+ DiagnosticSink sink(getLinkage()->getSourceManager(), Lexer::sourceLocationLexer);
+
+ // First let's check if the number of arguments given matches
+ // the number of parameters that are present on this component type.
+ //
+ auto specializationParamCount = getSpecializationParamCount();
+ if (specializationArgCount != specializationParamCount)
+ {
+ sink.diagnose(
+ SourceLoc(),
+ Diagnostics::mismatchSpecializationArguments,
+ specializationParamCount,
+ specializationArgCount);
+ sink.getBlobIfNeeded(outDiagnostics);
+ return SLANG_FAIL;
+ }
+
+ List<SpecializationArg> expandedArgs;
+ for (Int aa = 0; aa < specializationArgCount; ++aa)
+ {
+ auto apiArg = specializationArgs[aa];
+
+ SpecializationArg expandedArg;
+ switch (apiArg.kind)
+ {
+ case slang::SpecializationArg::Kind::Type:
+ expandedArg.val = asInternal(apiArg.type);
+ break;
+
+ default:
+ sink.getBlobIfNeeded(outDiagnostics);
+ return SLANG_FAIL;
+ }
+ expandedArgs.add(expandedArg);
+ }
+
+ auto specializedComponentType =
+ specialize(expandedArgs.getBuffer(), expandedArgs.getCount(), &sink);
+
+ sink.getBlobIfNeeded(outDiagnostics);
+
+ *outSpecializedComponentType = specializedComponentType.detach();
+
+ return SLANG_OK;
+}
+
+SLANG_NO_THROW SlangResult SLANG_MCALL
+ComponentType::renameEntryPoint(const char* newName, IComponentType** outEntryPoint)
+{
+ RefPtr<RenamedEntryPointComponentType> result =
+ new RenamedEntryPointComponentType(this, newName);
+ *outEntryPoint = result.detach();
+ return SLANG_OK;
+}
+
+RefPtr<ComponentType> fillRequirements(ComponentType* inComponentType);
+
+SLANG_NO_THROW SlangResult SLANG_MCALL
+ComponentType::link(slang::IComponentType** outLinkedComponentType, ISlangBlob** outDiagnostics)
+{
+ // TODO: It should be possible for `fillRequirements` to fail,
+ // in cases where we have a dependency that can't be automatically
+ // resolved.
+ //
+ SLANG_UNUSED(outDiagnostics);
+
+ DiagnosticSink sink(getLinkage()->getSourceManager(), Lexer::sourceLocationLexer);
+
+ try
+ {
+ auto linked = fillRequirements(this);
+ if (!linked)
+ return SLANG_FAIL;
+
+ *outLinkedComponentType = ComPtr<slang::IComponentType>(linked).detach();
+ return SLANG_OK;
+ }
+ catch (const AbortCompilationException& e)
+ {
+ outputExceptionDiagnostic(e, sink, outDiagnostics);
+ return SLANG_FAIL;
+ }
+ catch (const Exception& e)
+ {
+ outputExceptionDiagnostic(e, sink, outDiagnostics);
+ return SLANG_FAIL;
+ }
+ catch (...)
+ {
+ outputExceptionDiagnostic(sink, outDiagnostics);
+ return SLANG_FAIL;
+ }
+}
+
+SLANG_NO_THROW SlangResult SLANG_MCALL ComponentType::linkWithOptions(
+ slang::IComponentType** outLinkedComponentType,
+ uint32_t count,
+ slang::CompilerOptionEntry* entries,
+ ISlangBlob** outDiagnostics)
+{
+ SLANG_RETURN_ON_FAIL(link(outLinkedComponentType, outDiagnostics));
+
+ auto linked = *outLinkedComponentType;
+
+ if (linked)
+ {
+ static_cast<ComponentType*>(linked)->getOptionSet().load(count, entries);
+ }
+
+ return SLANG_OK;
+}
+
+SLANG_NO_THROW SlangResult SLANG_MCALL ComponentType::getEntryPointCompileResult(
+ SlangInt entryPointIndex,
+ Int targetIndex,
+ slang::ICompileResult** outCompileResult,
+ slang::IBlob** outDiagnostics)
+{
+ auto linkage = getLinkage();
+ if (targetIndex < 0 || targetIndex >= linkage->targets.getCount())
+ return SLANG_E_INVALID_ARG;
+ auto target = linkage->targets[targetIndex];
+
+ auto targetProgram = getTargetProgram(target);
+
+ DiagnosticSink sink(linkage->getSourceManager(), Lexer::sourceLocationLexer);
+ applySettingsToDiagnosticSink(&sink, &sink, linkage->m_optionSet);
+ applySettingsToDiagnosticSink(&sink, &sink, m_optionSet);
+
+ IArtifact* artifact = targetProgram->getOrCreateEntryPointResult(entryPointIndex, &sink);
+ sink.getBlobIfNeeded(outDiagnostics);
+ if (artifact == nullptr)
+ return SLANG_E_NOT_AVAILABLE;
+
+ *outCompileResult = static_cast<slang::ICompileResult*>(artifact);
+ (*outCompileResult)->addRef();
+ return SLANG_OK;
+}
+
+SLANG_NO_THROW SlangResult SLANG_MCALL ComponentType::getTargetCompileResult(
+ Int targetIndex,
+ slang::ICompileResult** outCompileResult,
+ slang::IBlob** outDiagnostics)
+{
+ IArtifact* artifact = getTargetArtifact(targetIndex, outDiagnostics);
+ if (artifact == nullptr)
+ return SLANG_E_NOT_AVAILABLE;
+
+ *outCompileResult = static_cast<slang::ICompileResult*>(artifact);
+ (*outCompileResult)->addRef();
+ return SLANG_OK;
+}
+
+/// Visitor used by `ComponentType::enumerateModules`
+struct EnumerateModulesVisitor : ComponentTypeVisitor
+{
+ EnumerateModulesVisitor(ComponentType::EnumerateModulesCallback callback, void* userData)
+ : m_callback(callback), m_userData(userData)
+ {
+ }
+
+ ComponentType::EnumerateModulesCallback m_callback;
+ void* m_userData;
+
+ void visitEntryPoint(EntryPoint*, EntryPoint::EntryPointSpecializationInfo*) SLANG_OVERRIDE {}
+
+ void visitRenamedEntryPoint(
+ RenamedEntryPointComponentType* entryPoint,
+ EntryPoint::EntryPointSpecializationInfo* specializationInfo) SLANG_OVERRIDE
+ {
+ entryPoint->getBase()->acceptVisitor(this, specializationInfo);
+ }
+
+ void visitModule(Module* module, Module::ModuleSpecializationInfo*) SLANG_OVERRIDE
+ {
+ m_callback(module, m_userData);
+ }
+
+ void visitComposite(
+ CompositeComponentType* composite,
+ CompositeComponentType::CompositeSpecializationInfo* specializationInfo) SLANG_OVERRIDE
+ {
+ visitChildren(composite, specializationInfo);
+ }
+
+ void visitSpecialized(SpecializedComponentType* specialized) SLANG_OVERRIDE
+ {
+ visitChildren(specialized);
+ }
+
+ void visitTypeConformance(TypeConformance* conformance) SLANG_OVERRIDE
+ {
+ SLANG_UNUSED(conformance);
+ }
+};
+
+
+void ComponentType::enumerateModules(EnumerateModulesCallback callback, void* userData)
+{
+ EnumerateModulesVisitor visitor(callback, userData);
+ acceptVisitor(&visitor, nullptr);
+}
+
+/// Visitor used by `ComponentType::enumerateIRModules`
+struct EnumerateIRModulesVisitor : ComponentTypeVisitor
+{
+ EnumerateIRModulesVisitor(ComponentType::EnumerateIRModulesCallback callback, void* userData)
+ : m_callback(callback), m_userData(userData)
+ {
+ }
+
+ ComponentType::EnumerateIRModulesCallback m_callback;
+ void* m_userData;
+
+ void visitEntryPoint(EntryPoint*, EntryPoint::EntryPointSpecializationInfo*) SLANG_OVERRIDE {}
+
+ void visitRenamedEntryPoint(
+ RenamedEntryPointComponentType* entryPoint,
+ EntryPoint::EntryPointSpecializationInfo* specializationInfo) SLANG_OVERRIDE
+ {
+ entryPoint->getBase()->acceptVisitor(this, specializationInfo);
+ }
+
+ void visitModule(Module* module, Module::ModuleSpecializationInfo*) SLANG_OVERRIDE
+ {
+ m_callback(module->getIRModule(), m_userData);
+ }
+
+ void visitComposite(
+ CompositeComponentType* composite,
+ CompositeComponentType::CompositeSpecializationInfo* specializationInfo) SLANG_OVERRIDE
+ {
+ visitChildren(composite, specializationInfo);
+ }
+
+ void visitSpecialized(SpecializedComponentType* specialized) SLANG_OVERRIDE
+ {
+ visitChildren(specialized);
+
+ m_callback(specialized->getIRModule(), m_userData);
+ }
+
+ void visitTypeConformance(TypeConformance* conformance) SLANG_OVERRIDE
+ {
+ m_callback(conformance->getIRModule(), m_userData);
+ }
+};
+
+void ComponentType::enumerateIRModules(EnumerateIRModulesCallback callback, void* userData)
+{
+ EnumerateIRModulesVisitor visitor(callback, userData);
+ acceptVisitor(&visitor, nullptr);
+}
+
+IArtifact* ComponentType::getTargetArtifact(Int targetIndex, slang::IBlob** outDiagnostics)
+{
+ auto linkage = getLinkage();
+ if (targetIndex < 0 || targetIndex >= linkage->targets.getCount())
+ return nullptr;
+ ComPtr<IArtifact> artifact;
+ if (m_targetArtifacts.tryGetValue(targetIndex, artifact))
+ {
+ return artifact.get();
+ }
+
+ // If the user hasn't specified any entry points, then we should
+ // discover all entrypoints that are defined in linked modules, and
+ // include all of them in the compile.
+ //
+ if (getEntryPointCount() == 0)
+ {
+ List<Module*> modules;
+ this->enumerateModules([&](Module* module) { modules.add(module); });
+ List<RefPtr<ComponentType>> components;
+ components.add(this);
+ bool entryPointsDiscovered = false;
+ for (auto module : modules)
+ {
+ for (auto entryPoint : module->getEntryPoints())
+ {
+ components.add(entryPoint);
+ entryPointsDiscovered = true;
+ }
+ }
+
+ // If any entry points were discovered, then we should emit the program with entrypoints
+ // linked.
+ if (entryPointsDiscovered)
+ {
+ RefPtr<CompositeComponentType> composite =
+ new CompositeComponentType(linkage, components);
+ ComPtr<IComponentType> linkedComponentType;
+ SLANG_RETURN_NULL_ON_FAIL(
+ composite->link(linkedComponentType.writeRef(), outDiagnostics));
+ auto targetArtifact = static_cast<ComponentType*>(linkedComponentType.get())
+ ->getTargetArtifact(targetIndex, outDiagnostics);
+ if (targetArtifact)
+ {
+ m_targetArtifacts[targetIndex] = targetArtifact;
+ }
+ return targetArtifact;
+ }
+ }
+
+ auto target = linkage->targets[targetIndex];
+ auto targetProgram = getTargetProgram(target);
+
+ DiagnosticSink sink(linkage->getSourceManager(), Lexer::sourceLocationLexer);
+ applySettingsToDiagnosticSink(&sink, &sink, linkage->m_optionSet);
+ applySettingsToDiagnosticSink(&sink, &sink, m_optionSet);
+
+ IArtifact* targetArtifact = targetProgram->getOrCreateWholeProgramResult(&sink);
+ sink.getBlobIfNeeded(outDiagnostics);
+ m_targetArtifacts[targetIndex] = ComPtr<IArtifact>(targetArtifact);
+ return targetArtifact;
+}
+
+SLANG_NO_THROW SlangResult SLANG_MCALL
+ComponentType::getTargetCode(Int targetIndex, slang::IBlob** outCode, slang::IBlob** outDiagnostics)
+{
+ IArtifact* artifact = getTargetArtifact(targetIndex, outDiagnostics);
+
+ if (artifact == nullptr)
+ return SLANG_FAIL;
+
+ return artifact->loadBlob(ArtifactKeep::Yes, outCode);
+}
+
+SLANG_NO_THROW SlangResult SLANG_MCALL ComponentType::getTargetMetadata(
+ Int targetIndex,
+ slang::IMetadata** outMetadata,
+ slang::IBlob** outDiagnostics)
+{
+ IArtifact* artifact = getTargetArtifact(targetIndex, outDiagnostics);
+
+ if (artifact == nullptr)
+ return SLANG_FAIL;
+
+ auto metadata = findAssociatedRepresentation<IArtifactPostEmitMetadata>(artifact);
+ if (!metadata)
+ return SLANG_E_NOT_AVAILABLE;
+ *outMetadata = static_cast<slang::IMetadata*>(metadata);
+ (*outMetadata)->addRef();
+ return SLANG_OK;
+}
+
+Type* ComponentType::getTypeFromString(String const& typeStr, DiagnosticSink* sink)
+{
+ // If we've looked up this type name before,
+ // then we can re-use it.
+ //
+ Type* type = nullptr;
+ if (m_types.tryGetValue(typeStr, type))
+ return type;
+
+
+ // TODO(JS): For now just used the linkages ASTBuilder to keep on scope
+ //
+ // The parseTermString uses the linkage ASTBuilder for it's parsing.
+ //
+ // It might be possible to just create a temporary ASTBuilder - the worry though is
+ // that the parsing sets a member variable in AST node to one of these scopes, and then
+ // it become a dangling pointer. So for now we go with the linkages.
+ auto astBuilder = getLinkage()->getASTBuilder();
+
+ // Otherwise, we need to start looking in
+ // the modules that were directly or
+ // indirectly referenced.
+ //
+ Scope* scope = _getOrCreateScopeForLegacyLookup(astBuilder);
+
+ auto linkage = getLinkage();
+
+ SLANG_AST_BUILDER_RAII(linkage->getASTBuilder());
+
+ Expr* typeExpr = linkage->parseTermString(typeStr, scope);
+ SharedSemanticsContext sharedSemanticsContext(linkage, nullptr, sink);
+ SemanticsVisitor visitor(&sharedSemanticsContext);
+ type = visitor.TranslateTypeNode(typeExpr);
+ auto typeOut = visitor.tryCoerceToProperType(TypeExp(type));
+ if (typeOut.type)
+ type = typeOut.type;
+
+ if (type)
+ {
+ m_types[typeStr] = type;
+ }
+ return type;
+}
+
+Expr* ComponentType::findDeclFromString(String const& name, DiagnosticSink* sink)
+{
+ // If we've looked up this type name before,
+ // then we can re-use it.
+ //
+ Expr* result = nullptr;
+ if (m_decls.tryGetValue(name, result))
+ return result;
+
+
+ // TODO(JS): For now just used the linkages ASTBuilder to keep on scope
+ //
+ // The parseTermString uses the linkage ASTBuilder for it's parsing.
+ //
+ // It might be possible to just create a temporary ASTBuilder - the worry though is
+ // that the parsing sets a member variable in AST node to one of these scopes, and then
+ // it become a dangling pointer. So for now we go with the linkages.
+ auto astBuilder = getLinkage()->getASTBuilder();
+
+ // Otherwise, we need to start looking in
+ // the modules that were directly or
+ // indirectly referenced.
+ //
+ Scope* scope = _getOrCreateScopeForLegacyLookup(astBuilder);
+
+ auto linkage = getLinkage();
+
+ SLANG_AST_BUILDER_RAII(linkage->getASTBuilder());
+
+ Expr* expr = linkage->parseTermString(name, scope);
+
+ SemanticsContext context(linkage->getSemanticsForReflection());
+ context = context.allowStaticReferenceToNonStaticMember().withSink(sink);
+
+ SemanticsVisitor visitor(context);
+
+ auto checkedExpr = visitor.CheckTerm(expr);
+
+ if (as<DeclRefExpr>(checkedExpr) || as<OverloadedExpr>(checkedExpr))
+ {
+ result = checkedExpr;
+ }
+
+ m_decls[name] = result;
+ return result;
+}
+
+static bool _isSimpleName(String const& name)
+{
+ for (char c : name)
+ {
+ if (!CharUtil::isAlphaOrDigit(c) && c != '_' && c != '$')
+ return false;
+ }
+ return true;
+}
+
+Expr* ComponentType::findDeclFromStringInType(
+ Type* type,
+ String const& name,
+ LookupMask mask,
+ DiagnosticSink* sink)
+{
+ // Only look up in the type if it is a DeclRefType
+ if (!as<DeclRefType>(type))
+ return nullptr;
+
+ // TODO(JS): For now just used the linkages ASTBuilder to keep on scope
+ //
+ // The parseTermString uses the linkage ASTBuilder for it's parsing.
+ //
+ // It might be possible to just create a temporary ASTBuilder - the worry though is
+ // that the parsing sets a member variable in AST node to one of these scopes, and then
+ // it become a dangling pointer. So for now we go with the linkages.
+ auto astBuilder = getLinkage()->getASTBuilder();
+
+ // Otherwise, we need to start looking in
+ // the modules that were directly or
+ // indirectly referenced.
+ //
+ Scope* scope = _getOrCreateScopeForLegacyLookup(astBuilder);
+
+ auto linkage = getLinkage();
+
+ SLANG_AST_BUILDER_RAII(linkage->getASTBuilder());
+
+ Expr* expr = nullptr;
+
+ if (_isSimpleName(name))
+ {
+ auto varExpr = astBuilder->create<VarExpr>();
+ varExpr->scope = scope;
+ varExpr->name = getLinkage()->getNamePool()->getName(name);
+ expr = varExpr;
+ }
+ else
+ {
+ expr = linkage->parseTermString(name, scope);
+ }
+ SemanticsContext context(linkage->getSemanticsForReflection());
+ context = context.allowStaticReferenceToNonStaticMember().withSink(sink);
+
+ SemanticsVisitor visitor(context);
+
+ GenericAppExpr* genericOuterExpr = nullptr;
+ if (as<GenericAppExpr>(expr))
+ {
+ // Unwrap the generic application, and re-wrap it around the static-member expr
+ genericOuterExpr = as<GenericAppExpr>(expr);
+ expr = genericOuterExpr->functionExpr;
+ }
+
+ if (!as<VarExpr>(expr))
+ return nullptr;
+
+ auto rs = astBuilder->create<StaticMemberExpr>();
+ auto typeExpr = astBuilder->create<SharedTypeExpr>();
+ auto typetype = astBuilder->getOrCreate<TypeType>(type);
+ typeExpr->type = typetype;
+ rs->baseExpression = typeExpr;
+ rs->name = as<VarExpr>(expr)->name;
+
+ expr = rs;
+
+ // If we have a generic-app expression, re-wrap the static-member expr
+ if (genericOuterExpr)
+ {
+ genericOuterExpr->functionExpr = expr;
+ expr = genericOuterExpr;
+ }
+
+ auto checkedTerm = visitor.CheckTerm(expr);
+
+ // Check if checkedTerm is overloaded functions and avoid resolving if so
+ // to preserve all function overloads with different signatures
+ Expr* resolvedTerm = checkedTerm;
+ if (auto overloadedExpr = as<OverloadedExpr>(checkedTerm))
+ {
+ // Check if all candidates are function references
+ bool allAreFunctions = true;
+ for (auto item : overloadedExpr->lookupResult2.items)
+ {
+ if (!as<FunctionDeclBase>(item.declRef.getDecl()))
+ {
+ allAreFunctions = false;
+ break;
+ }
+ }
+
+ // If not all are functions, resolve the overload as usual
+ if (!allAreFunctions)
+ {
+ resolvedTerm = visitor.maybeResolveOverloadedExpr(checkedTerm, mask, sink);
+ }
+ }
+ else
+ {
+ // Not overloaded, resolve as usual
+ resolvedTerm = visitor.maybeResolveOverloadedExpr(checkedTerm, mask, sink);
+ }
+
+ if (auto overloadedExpr = as<OverloadedExpr>(resolvedTerm))
+ {
+ return overloadedExpr;
+ }
+ if (auto declRefExpr = as<DeclRefExpr>(resolvedTerm))
+ {
+ return declRefExpr;
+ }
+
+ return nullptr;
+}
+
+bool ComponentType::isSubType(Type* subType, Type* superType)
+{
+ SemanticsContext context(getLinkage()->getSemanticsForReflection());
+ SemanticsVisitor visitor(context);
+
+ return (visitor.isSubtype(subType, superType, IsSubTypeOptions::None) != nullptr);
+}
+
+static void collectExportedConstantInContainer(
+ Dictionary<String, IntVal*>& dict,
+ ASTBuilder* builder,
+ ContainerDecl* containerDecl)
+{
+ for (auto varMember : containerDecl->getDirectMemberDeclsOfType<VarDeclBase>())
+ {
+ if (!varMember->val)
+ continue;
+ bool isExported = false;
+ bool isConst = false;
+ bool isExtern = false;
+ for (auto modifier : varMember->modifiers)
+ {
+ if (as<HLSLExportModifier>(modifier))
+ isExported = true;
+ if (as<ExternAttribute>(modifier) || as<ExternModifier>(modifier))
+ {
+ isExtern = true;
+ isExported = true;
+ }
+ if (as<ConstModifier>(modifier))
+ isConst = true;
+ }
+ if (isExported && isConst)
+ {
+ auto mangledName = getMangledName(builder, varMember);
+ if (isExtern && dict.containsKey(mangledName))
+ continue;
+ dict[mangledName] = varMember->val;
+ }
+ }
+
+ for (auto member : containerDecl->getDirectMemberDecls())
+ {
+ if (as<NamespaceDecl>(member) || as<FileDecl>(member))
+ {
+ collectExportedConstantInContainer(dict, builder, (ContainerDecl*)member);
+ }
+ }
+}
+
+Dictionary<String, IntVal*>& ComponentType::getMangledNameToIntValMap()
+{
+ if (m_mapMangledNameToIntVal)
+ {
+ return *m_mapMangledNameToIntVal;
+ }
+ m_mapMangledNameToIntVal = std::make_unique<Dictionary<String, IntVal*>>();
+ auto astBuilder = getLinkage()->getASTBuilder();
+ SLANG_AST_BUILDER_RAII(astBuilder);
+ Scope* scope = _getOrCreateScopeForLegacyLookup(astBuilder);
+ for (; scope; scope = scope->nextSibling)
+ {
+ if (scope->containerDecl)
+ collectExportedConstantInContainer(
+ *m_mapMangledNameToIntVal,
+ astBuilder,
+ scope->containerDecl);
+ }
+ return *m_mapMangledNameToIntVal;
+}
+
+ConstantIntVal* ComponentType::tryFoldIntVal(IntVal* intVal)
+{
+ auto astBuilder = getLinkage()->getASTBuilder();
+ SLANG_AST_BUILDER_RAII(astBuilder);
+ return as<ConstantIntVal>(intVal->linkTimeResolve(getMangledNameToIntValMap()));
+}
+
+TargetProgram* ComponentType::getTargetProgram(TargetRequest* target)
+{
+ RefPtr<TargetProgram> targetProgram;
+ if (!m_targetPrograms.tryGetValue(target, targetProgram))
+ {
+ targetProgram = new TargetProgram(this, target);
+ m_targetPrograms[target] = targetProgram;
+ }
+ return targetProgram;
+}
+
+} // namespace Slang
diff --git a/source/slang/slang-linkable.h b/source/slang/slang-linkable.h
new file mode 100644
index 000000000..e900fd275
--- /dev/null
+++ b/source/slang/slang-linkable.h
@@ -0,0 +1,430 @@
+// slang-linkable.h
+#pragma once
+
+//
+// This file defines the `ComponentType` class, which
+// provides the root of the hierarchy for classes
+// that represent units of linkable code.
+//
+// The most obvious case of linkable code is a single
+// `Module` produced by invoking the Slang front-end
+// on source code (or by loading a previously compiled
+// `.slang-module` file).
+//
+
+#include "../compiler-core/slang-artifact.h"
+#include "slang-ast-base.h"
+#include "slang-compiler-fwd.h"
+#include "slang-compiler-options.h"
+
+#include <slang-com-helper.h>
+#include <slang.h>
+
+namespace Slang
+{
+class Linkage;
+
+class EntryPoint;
+
+class ComponentType;
+class ComponentTypeVisitor;
+
+/// Information collected about global or entry-point shader parameters
+struct ShaderParamInfo
+{
+ DeclRef<VarDeclBase> paramDeclRef;
+ Int firstSpecializationParamIndex = 0;
+ Int specializationParamCount = 0;
+};
+
+/// Tracks an ordered list of modules that something depends on.
+/// TODO: Shader caching currently relies on this being in well defined order.
+struct ModuleDependencyList
+{
+public:
+ /// Get the list of modules that are depended on.
+ List<Module*> const& getModuleList() { return m_moduleList; }
+
+ /// Add a module and everything it depends on to the list.
+ void addDependency(Module* module);
+
+ /// Add a module to the list, but not the modules it depends on.
+ void addLeafDependency(Module* module);
+
+private:
+ void _addDependency(Module* module);
+
+ List<Module*> m_moduleList;
+ HashSet<Module*> m_moduleSet;
+};
+
+/// Tracks an unordered list of source files that something depends on
+/// TODO: Shader caching currently relies on this being in well defined order.
+struct FileDependencyList
+{
+public:
+ /// Get the list of files that are depended on.
+ List<SourceFile*> const& getFileList() { return m_fileList; }
+
+ /// Add a file to the list, if it is not already present
+ void addDependency(SourceFile* sourceFile);
+
+ /// Add all of the paths that `module` depends on to the list
+ void addDependency(Module* module);
+
+ void clear()
+ {
+ m_fileList.clear();
+ m_fileSet.clear();
+ }
+
+private:
+ // TODO: We are using a `HashSet` here to deduplicate
+ // the paths so that we don't return the same path
+ // multiple times from `getFilePathList`, but because
+ // order isn't important, we could potentially do better
+ // in terms of memory (at some cost in performance) by
+ // just sorting the `m_fileList` every once in
+ // a while and then deduplicating.
+
+ List<SourceFile*> m_fileList;
+ HashSet<SourceFile*> m_fileSet;
+};
+
+/// Base class for "component types" that represent the pieces a final
+/// shader program gets linked together from.
+///
+class ComponentType : public RefObject,
+ public slang::IComponentType,
+ public slang::IComponentType2,
+ public slang::IModulePrecompileService_Experimental
+{
+public:
+ //
+ // ISlangUnknown interface
+ //
+
+ SLANG_REF_OBJECT_IUNKNOWN_ALL;
+ ISlangUnknown* getInterface(Guid const& guid);
+
+ //
+ // slang::IComponentType interface
+ //
+
+ SLANG_NO_THROW slang::ISession* SLANG_MCALL getSession() SLANG_OVERRIDE;
+ SLANG_NO_THROW slang::ProgramLayout* SLANG_MCALL
+ getLayout(SlangInt targetIndex, slang::IBlob** outDiagnostics) SLANG_OVERRIDE;
+ SLANG_NO_THROW SlangResult SLANG_MCALL getEntryPointCode(
+ SlangInt entryPointIndex,
+ SlangInt targetIndex,
+ slang::IBlob** outCode,
+ slang::IBlob** outDiagnostics) SLANG_OVERRIDE;
+
+ IArtifact* getTargetArtifact(SlangInt targetIndex, slang::IBlob** outDiagnostics);
+
+ SLANG_NO_THROW SlangResult SLANG_MCALL getTargetCode(
+ SlangInt targetIndex,
+ slang::IBlob** outCode,
+ slang::IBlob** outDiagnostics = nullptr) SLANG_OVERRIDE;
+ SLANG_NO_THROW SlangResult SLANG_MCALL getEntryPointMetadata(
+ SlangInt entryPointIndex,
+ SlangInt targetIndex,
+ slang::IMetadata** outMetadata,
+ slang::IBlob** outDiagnostics) SLANG_OVERRIDE;
+ SLANG_NO_THROW SlangResult SLANG_MCALL getTargetMetadata(
+ SlangInt targetIndex,
+ slang::IMetadata** outMetadata,
+ slang::IBlob** outDiagnostics = nullptr) SLANG_OVERRIDE;
+
+ SLANG_NO_THROW SlangResult SLANG_MCALL getResultAsFileSystem(
+ SlangInt entryPointIndex,
+ SlangInt targetIndex,
+ ISlangMutableFileSystem** outFileSystem) SLANG_OVERRIDE;
+
+ SLANG_NO_THROW SlangResult SLANG_MCALL specialize(
+ slang::SpecializationArg const* specializationArgs,
+ SlangInt specializationArgCount,
+ slang::IComponentType** outSpecializedComponentType,
+ ISlangBlob** outDiagnostics) SLANG_OVERRIDE;
+ SLANG_NO_THROW SlangResult SLANG_MCALL
+ renameEntryPoint(const char* newName, slang::IComponentType** outEntryPoint) SLANG_OVERRIDE;
+ SLANG_NO_THROW SlangResult SLANG_MCALL link(
+ slang::IComponentType** outLinkedComponentType,
+ ISlangBlob** outDiagnostics) SLANG_OVERRIDE;
+ SLANG_NO_THROW SlangResult SLANG_MCALL getEntryPointHostCallable(
+ int entryPointIndex,
+ int targetIndex,
+ ISlangSharedLibrary** outSharedLibrary,
+ slang::IBlob** outDiagnostics) SLANG_OVERRIDE;
+
+ /// ComponentType is the only class inheriting from IComponentType that provides a
+ /// meaningful implementation for this function. All others should forward these and
+ /// implement `buildHash`.
+ SLANG_NO_THROW void SLANG_MCALL getEntryPointHash(
+ SlangInt entryPointIndex,
+ SlangInt targetIndex,
+ slang::IBlob** outHash) SLANG_OVERRIDE;
+
+ SLANG_NO_THROW SlangResult SLANG_MCALL linkWithOptions(
+ slang::IComponentType** outLinkedComponentType,
+ uint32_t count,
+ slang::CompilerOptionEntry* entries,
+ ISlangBlob** outDiagnostics) override;
+
+ //
+ // slang::IComponentType2 interface
+ //
+
+ SLANG_NO_THROW SlangResult SLANG_MCALL getEntryPointCompileResult(
+ SlangInt entryPointIndex,
+ SlangInt targetIndex,
+ slang::ICompileResult** outCompileResult,
+ slang::IBlob** outDiagnostics) SLANG_OVERRIDE;
+ SLANG_NO_THROW SlangResult SLANG_MCALL getTargetCompileResult(
+ SlangInt targetIndex,
+ slang::ICompileResult** outCompileResult,
+ slang::IBlob** outDiagnostics = nullptr) SLANG_OVERRIDE;
+
+ //
+ // slang::IModulePrecompileService interface
+ //
+ SLANG_NO_THROW SlangResult SLANG_MCALL
+ precompileForTarget(SlangCompileTarget target, slang::IBlob** outDiagnostics) SLANG_OVERRIDE;
+
+ SLANG_NO_THROW SlangResult SLANG_MCALL getPrecompiledTargetCode(
+ SlangCompileTarget target,
+ slang::IBlob** outCode,
+ slang::IBlob** outDiagnostics = nullptr) SLANG_OVERRIDE;
+
+ SLANG_NO_THROW SlangInt SLANG_MCALL getModuleDependencyCount() SLANG_OVERRIDE;
+
+ SLANG_NO_THROW SlangResult SLANG_MCALL getModuleDependency(
+ SlangInt dependencyIndex,
+ slang::IModule** outModule,
+ slang::IBlob** outDiagnostics = nullptr) SLANG_OVERRIDE;
+
+ CompilerOptionSet& getOptionSet() { return m_optionSet; }
+
+ /// Get the linkage (aka "session" in the public API) for this component type.
+ Linkage* getLinkage() { return m_linkage; }
+
+ /// Get the target-specific version of this program for the given `target`.
+ ///
+ /// The `target` must be a target on the `Linkage` that was used to create this program.
+ TargetProgram* getTargetProgram(TargetRequest* target);
+
+ /// Update the hash builder with the dependencies for this component type.
+ virtual void buildHash(DigestBuilder<SHA1>& builder) = 0;
+
+ /// Get the number of entry points linked into this component type.
+ virtual Index getEntryPointCount() = 0;
+
+ /// Get one of the entry points linked into this component type.
+ virtual RefPtr<EntryPoint> getEntryPoint(Index index) = 0;
+
+ /// Get the mangled name of one of the entry points linked into this component type.
+ virtual String getEntryPointMangledName(Index index) = 0;
+
+ /// Get the name override of one of the entry points linked into this component type.
+ virtual String getEntryPointNameOverride(Index index) = 0;
+
+ /// Get the number of global shader parameters linked into this component type.
+ virtual Index getShaderParamCount() = 0;
+
+ /// Get one of the global shader parametesr linked into this component type.
+ virtual ShaderParamInfo getShaderParam(Index index) = 0;
+
+ /// Get the specialization parameter at `index`.
+ virtual SpecializationParam const& getSpecializationParam(Index index) = 0;
+
+ /// Get the number of "requirements" that this component type has.
+ ///
+ /// A requirement represents another component type that this component
+ /// needs in order to function correctly. For example, the dependency
+ /// of one module on another module that it `import`s is represented
+ /// as a requirement, as is the dependency of an entry point on the
+ /// module that defines it.
+ ///
+ virtual Index getRequirementCount() = 0;
+
+ /// Get the requirement at `index`.
+ virtual RefPtr<ComponentType> getRequirement(Index index) = 0;
+
+ /// Parse a type from a string, in the context of this component type.
+ ///
+ /// Any names in the string will be resolved using the modules
+ /// referenced by the program.
+ ///
+ /// On an error, returns null and reports diagnostic messages
+ /// to the provided `sink`.
+ ///
+ /// TODO: This function shouldn't be on the base class, since
+ /// it only really makes sense on `Module`.
+ ///
+ Type* getTypeFromString(String const& typeStr, DiagnosticSink* sink);
+
+ Expr* findDeclFromString(String const& name, DiagnosticSink* sink);
+
+ Expr* findDeclFromStringInType(
+ Type* type,
+ String const& name,
+ LookupMask mask,
+ DiagnosticSink* sink);
+
+ bool isSubType(Type* subType, Type* superType);
+
+ Dictionary<String, IntVal*>& getMangledNameToIntValMap();
+ ConstantIntVal* tryFoldIntVal(IntVal* intVal);
+
+ /// Get a list of modules that this component type depends on.
+ ///
+ virtual List<Module*> const& getModuleDependencies() = 0;
+
+ /// Get the full list of source files this component type depends on.
+ ///
+ virtual List<SourceFile*> const& getFileDependencies() = 0;
+
+ /// Callback for use with `enumerateIRModules`
+ typedef void (*EnumerateIRModulesCallback)(IRModule* irModule, void* userData);
+
+ /// Invoke `callback` on all the IR modules that are (transitively) linked into this component
+ /// type.
+ void enumerateIRModules(EnumerateIRModulesCallback callback, void* userData);
+
+ /// Invoke `callback` on all the IR modules that are (transitively) linked into this component
+ /// type.
+ template<typename F>
+ void enumerateIRModules(F const& callback)
+ {
+ struct Helper
+ {
+ static void helper(IRModule* irModule, void* userData) { (*(F*)userData)(irModule); }
+ };
+ enumerateIRModules(&Helper::helper, (void*)&callback);
+ }
+
+ /// Callback for use with `enumerateModules`
+ typedef void (*EnumerateModulesCallback)(Module* module, void* userData);
+
+ /// Invoke `callback` on all the modules that are (transitively) linked into this component
+ /// type.
+ void enumerateModules(EnumerateModulesCallback callback, void* userData);
+
+ /// Invoke `callback` on all the modules that are (transitively) linked into this component
+ /// type.
+ template<typename F>
+ void enumerateModules(F const& callback)
+ {
+ struct Helper
+ {
+ static void helper(Module* module, void* userData) { (*(F*)userData)(module); }
+ };
+ enumerateModules(&Helper::helper, (void*)&callback);
+ }
+
+ /// Side-band information generated when specializing this component type.
+ ///
+ /// Difference subclasses of `ComponentType` are expected to create their
+ /// own subclass of `SpecializationInfo` as the output of `_validateSpecializationArgs`.
+ /// Later, whenever we want to use a specialized component type we will
+ /// also have the `SpecializationInfo` available and will expect it to
+ /// have the correct (subclass-specific) type.
+ ///
+ class SpecializationInfo : public RefObject
+ {
+ };
+
+ /// Validate the given specialization `args` and compute any side-band specialization info.
+ ///
+ /// Any errors will be reported to `sink`, which can thus be used to test
+ /// if the operation was successful.
+ ///
+ /// A null return value is allowed, since not all subclasses require
+ /// custom side-band specialization information.
+ ///
+ /// This function is an implementation detail of `specialize()`.
+ ///
+ virtual RefPtr<SpecializationInfo> _validateSpecializationArgsImpl(
+ SpecializationArg const* args,
+ Index argCount,
+ DiagnosticSink* sink) = 0;
+
+ /// Validate the given specialization `args` and compute any side-band specialization info.
+ ///
+ /// Any errors will be reported to `sink`, which can thus be used to test
+ /// if the operation was successful.
+ ///
+ /// A null return value is allowed, since not all subclasses require
+ /// custom side-band specialization information.
+ ///
+ /// This function is an implementation detail of `specialize()`.
+ ///
+ RefPtr<SpecializationInfo> _validateSpecializationArgs(
+ SpecializationArg const* args,
+ Index argCount,
+ DiagnosticSink* sink)
+ {
+ if (argCount == 0)
+ return nullptr;
+ return _validateSpecializationArgsImpl(args, argCount, sink);
+ }
+
+ /// Specialize this component type given `specializationArgs`
+ ///
+ /// Any diagnostics will be reported to `sink`, which can be used
+ /// to determine if the operation was successful. It is allowed
+ /// for this operation to have a non-null return even when an
+ /// error is ecnountered.
+ ///
+ RefPtr<ComponentType> specialize(
+ SpecializationArg const* specializationArgs,
+ SlangInt specializationArgCount,
+ DiagnosticSink* sink);
+
+ /// Invoke `visitor` on this component type, using the appropriate dynamic type.
+ ///
+ /// This function implements the "visitor pattern" for `ComponentType`.
+ ///
+ /// If the `specializationInfo` argument is non-null, it must be specialization
+ /// information generated for this specific component type by `_validateSpecializationArgs`.
+ /// In that case, appropriately-typed specialization information will be passed
+ /// when invoking the `visitor`.
+ ///
+ virtual void acceptVisitor(
+ ComponentTypeVisitor* visitor,
+ SpecializationInfo* specializationInfo) = 0;
+
+ /// Create a scope suitable for looking up names or parsing specialization arguments.
+ ///
+ /// This facility is only needed to support legacy APIs for string-based lookup
+ /// and parsing via Slang reflection, and is not recommended for future APIs to use.
+ ///
+ Scope* _getOrCreateScopeForLegacyLookup(ASTBuilder* astBuilder);
+
+protected:
+ ComponentType(Linkage* linkage);
+
+protected:
+ Linkage* m_linkage;
+
+ CompilerOptionSet m_optionSet;
+
+ // Cache of target-specific programs for each target.
+ Dictionary<TargetRequest*, RefPtr<TargetProgram>> m_targetPrograms;
+
+ // Any types looked up dynamically using `getTypeFromString`
+ //
+ // TODO: Remove this. Type lookup should only be supported on `Module`s.
+ //
+ Dictionary<String, Type*> m_types;
+
+ // Any decls looked up dynamically using `findDeclFromString`.
+ Dictionary<String, Expr*> m_decls;
+
+ Scope* m_lookupScope = nullptr;
+ std::unique_ptr<Dictionary<String, IntVal*>> m_mapMangledNameToIntVal;
+
+ Dictionary<Int, ComPtr<IArtifact>> m_targetArtifacts;
+};
+
+} // namespace Slang
diff --git a/source/slang/slang-module.cpp b/source/slang/slang-module.cpp
new file mode 100644
index 000000000..06967de7d
--- /dev/null
+++ b/source/slang/slang-module.cpp
@@ -0,0 +1,420 @@
+// slang-module.cpp
+#include "slang-module.h"
+
+#include "slang-check-impl.h"
+#include "slang-compiler.h"
+#include "slang-mangle.h"
+#include "slang-serialize-container.h"
+
+namespace Slang
+{
+
+//
+// Module
+//
+
+Module::Module(Linkage* linkage, ASTBuilder* astBuilder)
+ : ComponentType(linkage), m_mangledExportPool(StringSlicePool::Style::Empty)
+{
+ if (astBuilder)
+ {
+ m_astBuilder = astBuilder;
+ }
+ else
+ {
+ m_astBuilder = linkage->getASTBuilder();
+ }
+ getOptionSet() = linkage->m_optionSet;
+ addModuleDependency(this);
+}
+
+ISlangUnknown* Module::getInterface(const Guid& guid)
+{
+ if (guid == IModule::getTypeGuid())
+ return asExternal(this);
+ if (guid == IModulePrecompileService_Experimental::getTypeGuid())
+ return static_cast<slang::IModulePrecompileService_Experimental*>(this);
+ return Super::getInterface(guid);
+}
+
+void Module::buildHash(DigestBuilder<SHA1>& builder)
+{
+ builder.append(computeDigest());
+}
+
+slang::DeclReflection* Module::getModuleReflection()
+{
+ return (slang::DeclReflection*)m_moduleDecl;
+}
+
+SHA1::Digest Module::computeDigest()
+{
+ if (m_digest == SHA1::Digest())
+ {
+ DigestBuilder<SHA1> digestBuilder;
+ auto version = String(getBuildTagString());
+ digestBuilder.append(version);
+ getOptionSet().buildHash(digestBuilder);
+
+ auto fileDependencies = getFileDependencies();
+
+ for (auto file : fileDependencies)
+ {
+ digestBuilder.append(file->getDigest());
+ }
+ m_digest = digestBuilder.finalize();
+ }
+ return m_digest;
+}
+
+void Module::addModuleDependency(Module* module)
+{
+ m_moduleDependencyList.addDependency(module);
+ m_fileDependencyList.addDependency(module);
+}
+
+void Module::addFileDependency(SourceFile* sourceFile)
+{
+ m_fileDependencyList.addDependency(sourceFile);
+}
+
+void Module::setModuleDecl(ModuleDecl* moduleDecl)
+{
+ m_moduleDecl = moduleDecl;
+ moduleDecl->module = this;
+}
+
+void Module::setName(String name)
+{
+ m_name = getLinkage()->getNamePool()->getName(name);
+}
+
+
+RefPtr<EntryPoint> Module::findEntryPointByName(UnownedStringSlice const& name)
+{
+ for (auto entryPoint : m_entryPoints)
+ {
+ if (entryPoint->getName()->text.getUnownedSlice() == name)
+ return entryPoint;
+ }
+
+ return nullptr;
+}
+
+RefPtr<EntryPoint> Module::findAndCheckEntryPoint(
+ UnownedStringSlice const& name,
+ SlangStage stage,
+ ISlangBlob** outDiagnostics)
+{
+ // If there is already an entrypoint marked with the [shader] attribute,
+ // we should just return that.
+ //
+ if (auto existingEntryPoint = findEntryPointByName(name))
+ return existingEntryPoint;
+
+ SLANG_AST_BUILDER_RAII(m_astBuilder);
+
+ // If the function hasn't been marked as [shader], then it won't be discovered
+ // by findEntryPointByName. We need to route this to the `findAndValidateEntryPoint`
+ // function. To do that we need to setup a FrontEndCompileRequest and a
+ // FrontEndEntryPointRequest.
+ //
+ DiagnosticSink sink(getLinkage()->getSourceManager(), DiagnosticSink::SourceLocationLexer());
+ FrontEndCompileRequest frontEndRequest(getLinkage(), StdWriters::getSingleton(), &sink);
+ RefPtr<TranslationUnitRequest> tuRequest = new TranslationUnitRequest(&frontEndRequest);
+ tuRequest->module = this;
+ tuRequest->moduleName = m_name;
+ frontEndRequest.translationUnits.add(tuRequest);
+ FrontEndEntryPointRequest entryPointRequest(
+ &frontEndRequest,
+ 0,
+ getLinkage()->getNamePool()->getName(name),
+ Profile((Stage)stage));
+ auto result = findAndValidateEntryPoint(&entryPointRequest);
+ if (outDiagnostics)
+ {
+ sink.getBlobIfNeeded(outDiagnostics);
+ }
+ return result;
+}
+
+void Module::_addEntryPoint(EntryPoint* entryPoint)
+{
+ m_entryPoints.add(entryPoint);
+}
+
+static bool _canExportDeclSymbol(ASTNodeType type)
+{
+ switch (type)
+ {
+ case ASTNodeType::EmptyDecl:
+ {
+ return false;
+ }
+ default:
+ break;
+ }
+
+ return true;
+}
+
+static bool _canRecurseExportSymbol(Decl* decl)
+{
+ if (as<FunctionDeclBase>(decl) || as<ScopeDecl>(decl))
+ {
+ return false;
+ }
+ return true;
+}
+
+void Module::_processFindDeclsExportSymbolsRec(Decl* decl)
+{
+ if (_canExportDeclSymbol(decl->astNodeType))
+ {
+ // It's a reference to a declaration in another module, so first get the symbol name.
+ String mangledName = getMangledName(getCurrentASTBuilder(), decl);
+
+ Index index = Index(m_mangledExportPool.add(mangledName));
+
+ // TODO(JS): It appears that more than one entity might have the same mangled name.
+ // So for now we ignore and just take the first one.
+ if (index == m_mangledExportSymbols.getCount())
+ {
+ m_mangledExportSymbols.add(decl);
+ }
+ }
+
+ if (!_canRecurseExportSymbol(decl))
+ {
+ // We don't need to recurse any further into this
+ return;
+ }
+
+ // If it's a container process it's children
+ if (auto containerDecl = as<ContainerDecl>(decl))
+ {
+ for (auto child : containerDecl->getDirectMemberDecls())
+ {
+ _processFindDeclsExportSymbolsRec(child);
+ }
+ }
+
+ // GenericDecl is also a container, so do subsequent test
+ if (auto genericDecl = as<GenericDecl>(decl))
+ {
+ _processFindDeclsExportSymbolsRec(genericDecl->inner);
+ }
+}
+
+Decl* Module::findExportedDeclByMangledName(const UnownedStringSlice& mangledName)
+{
+ // If this module is a serialized module that is being
+ // deserialized on-demand, then we want to use the
+ // mangled name mapping that was baked into the serialized
+ // data, rather than attempt to enumerate all of the declarations
+ // in the module (as would be done if we proceeded to call
+ // `ensureExportLookupAcceleratorBuilt()`).
+ //
+ if (this->m_moduleDecl->isUsingOnDemandDeserializationForExports())
+ {
+ return m_moduleDecl->_findSerializedDeclByMangledExportName(mangledName);
+ }
+
+ ensureExportLookupAcceleratorBuilt();
+
+ const Index index = m_mangledExportPool.findIndex(mangledName);
+ return (index >= 0) ? m_mangledExportSymbols[index] : nullptr;
+}
+
+void Module::ensureExportLookupAcceleratorBuilt()
+{
+ // Will be non zero if has been previously attempted
+ if (m_mangledExportSymbols.getCount() == 0)
+ {
+ // Build up the exported mangled name list
+ _processFindDeclsExportSymbolsRec(getModuleDecl());
+
+ // If nothing found, mark that we have tried looking by making
+ // m_mangledExportSymbols.getCount() != 0
+ if (m_mangledExportSymbols.getCount() == 0)
+ {
+ m_mangledExportSymbols.add(nullptr);
+ }
+ }
+}
+
+Count Module::getExportedDeclCount()
+{
+ ensureExportLookupAcceleratorBuilt();
+
+ return m_mangledExportPool.getSlicesCount();
+}
+
+Decl* Module::getExportedDecl(Index index)
+{
+ ensureExportLookupAcceleratorBuilt();
+ return m_mangledExportSymbols[index];
+}
+
+UnownedStringSlice Module::getExportedDeclMangledName(Index index)
+{
+ ensureExportLookupAcceleratorBuilt();
+ return m_mangledExportPool.getSlices()[index];
+}
+
+SLANG_NO_THROW SlangResult SLANG_MCALL Module::serialize(ISlangBlob** outSerializedBlob)
+{
+ SerialContainerUtil::WriteOptions writeOptions;
+ OwnedMemoryStream memoryStream(FileAccess::Write);
+ SLANG_RETURN_ON_FAIL(SerialContainerUtil::write(this, writeOptions, &memoryStream));
+ *outSerializedBlob = RawBlob::create(
+ memoryStream.getContents().getBuffer(),
+ (size_t)memoryStream.getContents().getCount())
+ .detach();
+ return SLANG_OK;
+}
+
+SLANG_NO_THROW SlangResult SLANG_MCALL Module::writeToFile(char const* fileName)
+{
+ SerialContainerUtil::WriteOptions writeOptions;
+ FileStream fileStream;
+ SLANG_RETURN_ON_FAIL(fileStream.init(fileName, FileMode::Create));
+ return SerialContainerUtil::write(this, writeOptions, &fileStream);
+}
+
+SLANG_NO_THROW const char* SLANG_MCALL Module::getName()
+{
+ if (m_name)
+ return m_name->text.getBuffer();
+ return nullptr;
+}
+
+SLANG_NO_THROW const char* SLANG_MCALL Module::getFilePath()
+{
+ if (m_pathInfo.hasFoundPath())
+ return m_pathInfo.foundPath.getBuffer();
+ return nullptr;
+}
+
+SLANG_NO_THROW const char* SLANG_MCALL Module::getUniqueIdentity()
+{
+ if (m_pathInfo.hasUniqueIdentity())
+ return m_pathInfo.getMostUniqueIdentity().getBuffer();
+ return nullptr;
+}
+
+SLANG_NO_THROW SlangInt32 SLANG_MCALL Module::getDependencyFileCount()
+{
+ return (SlangInt32)getFileDependencies().getCount();
+}
+
+SLANG_NO_THROW char const* SLANG_MCALL Module::getDependencyFilePath(SlangInt32 index)
+{
+ SourceFile* sourceFile = getFileDependencies()[index];
+ return sourceFile->getPathInfo().hasFoundPath()
+ ? sourceFile->getPathInfo().getMostUniqueIdentity().getBuffer()
+ : nullptr;
+}
+
+void Module::_discoverEntryPoints(DiagnosticSink* sink, const List<RefPtr<TargetRequest>>& targets)
+{
+ if (m_entryPoints.getCount() > 0)
+ return;
+ _discoverEntryPointsImpl(m_moduleDecl, sink, targets);
+}
+void Module::_discoverEntryPointsImpl(
+ ContainerDecl* containerDecl,
+ DiagnosticSink* sink,
+ const List<RefPtr<TargetRequest>>& targets)
+{
+ for (auto globalDecl : containerDecl->getDirectMemberDecls())
+ {
+ auto maybeFuncDecl = globalDecl;
+ if (auto genericDecl = as<GenericDecl>(maybeFuncDecl))
+ {
+ maybeFuncDecl = genericDecl->inner;
+ }
+
+ if (as<NamespaceDeclBase>(globalDecl) || as<FileDecl>(globalDecl) ||
+ as<StructDecl>(globalDecl))
+ {
+ _discoverEntryPointsImpl(as<ContainerDecl>(globalDecl), sink, targets);
+ continue;
+ }
+
+ auto funcDecl = as<FuncDecl>(maybeFuncDecl);
+ if (!funcDecl)
+ continue;
+
+ Profile profile;
+ bool resolvedStageOfProfileWithEntryPoint = resolveStageOfProfileWithEntryPoint(
+ profile,
+ getLinkage()->m_optionSet,
+ targets,
+ funcDecl,
+ sink);
+ if (!resolvedStageOfProfileWithEntryPoint)
+ {
+ // If there isn't a [shader] attribute, look for a [numthreads] attribute
+ // since that implicitly means a compute shader. We'll not do this when compiling for
+ // CUDA/Torch since [numthreads] attributes are utilized differently for those targets.
+ //
+
+ bool allTargetsCUDARelated = true;
+ for (auto target : targets)
+ {
+ if (!isCUDATarget(target) &&
+ target->getTarget() != CodeGenTarget::PyTorchCppBinding)
+ {
+ allTargetsCUDARelated = false;
+ break;
+ }
+ }
+
+ if (allTargetsCUDARelated && targets.getCount() > 0)
+ continue;
+
+ bool canDetermineStage = false;
+ for (auto modifier : funcDecl->modifiers)
+ {
+ if (as<NumThreadsAttribute>(modifier))
+ {
+ if (funcDecl->findModifier<OutputTopologyAttribute>())
+ profile.setStage(Stage::Mesh);
+ else
+ profile.setStage(Stage::Compute);
+ canDetermineStage = true;
+ break;
+ }
+ else if (as<PatchConstantFuncAttribute>(modifier))
+ {
+ profile.setStage(Stage::Hull);
+ canDetermineStage = true;
+ break;
+ }
+ }
+ if (!canDetermineStage)
+ continue;
+ }
+
+ RefPtr<EntryPoint> entryPoint =
+ EntryPoint::create(getLinkage(), makeDeclRef(funcDecl), profile);
+
+ validateEntryPoint(entryPoint, sink);
+
+ // Note: in the case that the user didn't explicitly
+ // specify entry points and we are instead compiling
+ // a shader "library," then we do not want to automatically
+ // combine the entry points into groups in the generated
+ // `Program`, since that would be slightly too magical.
+ //
+ // Instead, each entry point will end up in a singleton
+ // group, so that its entry-point parameters lay out
+ // independent of the others.
+ //
+ _addEntryPoint(entryPoint);
+ }
+}
+
+
+} // namespace Slang
diff --git a/source/slang/slang-module.h b/source/slang/slang-module.h
new file mode 100644
index 000000000..7a71f242d
--- /dev/null
+++ b/source/slang/slang-module.h
@@ -0,0 +1,525 @@
+// slang-module.h
+#pragma once
+
+//
+// This file provides the `Module` class, which is
+// central to many parts of the Slang compiler codebase.
+//
+
+#include "../core/slang-string-util.h"
+#include "slang-ast-builder.h"
+#include "slang-entry-point.h"
+#include "slang-linkable.h"
+
+namespace Slang
+{
+
+/// A module of code that has been compiled through the front-end
+///
+/// A module comprises all the code from one translation unit (which
+/// may span multiple Slang source files), and provides access
+/// to both the AST and IR representations of that code.
+///
+/// This class serves multiple important roles in the Slang compiler:
+///
+/// * this class implements the `slang::IModule` interface from
+/// the public Slang API.
+///
+/// * this class is the primary output of front-end compilation,
+/// and its data is what gets stored/loaded using the `.slang-module`
+/// file format.
+///
+/// * The checked AST in a `Module` provides all of the information
+/// that the front-end uses when checking code that `import`s
+/// that module (e.g., the names and signatures of functions defined
+/// in the module).
+///
+/// * The checked AST is also used to service queries through the
+/// Slang reflection API.
+///
+/// * the `Module` class is a subclass of `ComponentType` and thus
+/// is a unit of linkable code. One or more modules (and other
+/// linkable objects) can be combined to form a linked program.
+///
+/// * The Slang IR in a `Module` provides all of the information that
+/// the back-end uses when generating code for a program/binary
+/// that links this module (or any of its entry points).
+///
+class Module : public ComponentType, public slang::IModule
+{
+ typedef ComponentType Super;
+
+public:
+ SLANG_REF_OBJECT_IUNKNOWN_ALL
+
+ ISlangUnknown* getInterface(const Guid& guid);
+
+
+ // Forward `IComponentType` methods
+
+ SLANG_NO_THROW slang::ISession* SLANG_MCALL getSession() SLANG_OVERRIDE
+ {
+ return Super::getSession();
+ }
+
+ SLANG_NO_THROW slang::ProgramLayout* SLANG_MCALL
+ getLayout(SlangInt targetIndex, slang::IBlob** outDiagnostics) SLANG_OVERRIDE
+ {
+ return Super::getLayout(targetIndex, outDiagnostics);
+ }
+
+ SLANG_NO_THROW SlangResult SLANG_MCALL getEntryPointCode(
+ SlangInt entryPointIndex,
+ SlangInt targetIndex,
+ slang::IBlob** outCode,
+ slang::IBlob** outDiagnostics) SLANG_OVERRIDE
+ {
+ return Super::getEntryPointCode(entryPointIndex, targetIndex, outCode, outDiagnostics);
+ }
+
+ SLANG_NO_THROW SlangResult SLANG_MCALL getTargetCode(
+ SlangInt targetIndex,
+ slang::IBlob** outCode,
+ slang::IBlob** outDiagnostics) SLANG_OVERRIDE
+ {
+ return Super::getTargetCode(targetIndex, outCode, outDiagnostics);
+ }
+
+ SLANG_NO_THROW SlangResult SLANG_MCALL getResultAsFileSystem(
+ SlangInt entryPointIndex,
+ SlangInt targetIndex,
+ ISlangMutableFileSystem** outFileSystem) SLANG_OVERRIDE
+ {
+ return Super::getResultAsFileSystem(entryPointIndex, targetIndex, outFileSystem);
+ }
+
+ SLANG_NO_THROW SlangResult SLANG_MCALL specialize(
+ slang::SpecializationArg const* specializationArgs,
+ SlangInt specializationArgCount,
+ slang::IComponentType** outSpecializedComponentType,
+ ISlangBlob** outDiagnostics) SLANG_OVERRIDE
+ {
+ return Super::specialize(
+ specializationArgs,
+ specializationArgCount,
+ outSpecializedComponentType,
+ outDiagnostics);
+ }
+
+ SLANG_NO_THROW SlangResult SLANG_MCALL
+ renameEntryPoint(const char* newName, slang::IComponentType** outEntryPoint) SLANG_OVERRIDE
+ {
+ return Super::renameEntryPoint(newName, outEntryPoint);
+ }
+
+ SLANG_NO_THROW SlangResult SLANG_MCALL
+ link(slang::IComponentType** outLinkedComponentType, ISlangBlob** outDiagnostics) SLANG_OVERRIDE
+ {
+ return Super::link(outLinkedComponentType, outDiagnostics);
+ }
+
+ SLANG_NO_THROW SlangResult SLANG_MCALL getEntryPointHostCallable(
+ int entryPointIndex,
+ int targetIndex,
+ ISlangSharedLibrary** outSharedLibrary,
+ slang::IBlob** outDiagnostics) SLANG_OVERRIDE
+ {
+ return Super::getEntryPointHostCallable(
+ entryPointIndex,
+ targetIndex,
+ outSharedLibrary,
+ outDiagnostics);
+ }
+
+ SLANG_NO_THROW SlangResult SLANG_MCALL
+ findEntryPointByName(char const* name, slang::IEntryPoint** outEntryPoint) SLANG_OVERRIDE
+ {
+ if (outEntryPoint == nullptr)
+ {
+ return SLANG_E_INVALID_ARG;
+ }
+ SLANG_AST_BUILDER_RAII(m_astBuilder);
+ ComPtr<slang::IEntryPoint> entryPoint(findEntryPointByName(UnownedStringSlice(name)));
+ if ((!entryPoint))
+ return SLANG_FAIL;
+
+ *outEntryPoint = entryPoint.detach();
+ return SLANG_OK;
+ }
+
+ virtual SLANG_NO_THROW SlangResult SLANG_MCALL findAndCheckEntryPoint(
+ char const* name,
+ SlangStage stage,
+ slang::IEntryPoint** outEntryPoint,
+ ISlangBlob** outDiagnostics) override
+ {
+ if (outEntryPoint == nullptr)
+ {
+ return SLANG_E_INVALID_ARG;
+ }
+ ComPtr<slang::IEntryPoint> entryPoint(
+ findAndCheckEntryPoint(UnownedStringSlice(name), stage, outDiagnostics));
+ if ((!entryPoint))
+ return SLANG_FAIL;
+
+ *outEntryPoint = entryPoint.detach();
+ return SLANG_OK;
+ }
+
+ virtual SLANG_NO_THROW SlangInt32 SLANG_MCALL getDefinedEntryPointCount() override
+ {
+ return (SlangInt32)m_entryPoints.getCount();
+ }
+
+ virtual SLANG_NO_THROW SlangResult SLANG_MCALL
+ getDefinedEntryPoint(SlangInt32 index, slang::IEntryPoint** outEntryPoint) override
+ {
+ if (index < 0 || index >= m_entryPoints.getCount())
+ return SLANG_E_INVALID_ARG;
+
+ if (outEntryPoint == nullptr)
+ {
+ return SLANG_E_INVALID_ARG;
+ }
+
+ ComPtr<slang::IEntryPoint> entryPoint(m_entryPoints[index].Ptr());
+ *outEntryPoint = entryPoint.detach();
+ return SLANG_OK;
+ }
+
+ virtual SLANG_NO_THROW SlangResult SLANG_MCALL linkWithOptions(
+ slang::IComponentType** outLinkedComponentType,
+ uint32_t count,
+ slang::CompilerOptionEntry* entries,
+ ISlangBlob** outDiagnostics) override
+ {
+ return Super::linkWithOptions(outLinkedComponentType, count, entries, outDiagnostics);
+ }
+ //
+
+ SLANG_NO_THROW void SLANG_MCALL getEntryPointHash(
+ SlangInt entryPointIndex,
+ SlangInt targetIndex,
+ slang::IBlob** outHash) SLANG_OVERRIDE
+ {
+ return Super::getEntryPointHash(entryPointIndex, targetIndex, outHash);
+ }
+
+ SLANG_NO_THROW SlangResult SLANG_MCALL getEntryPointMetadata(
+ SlangInt entryPointIndex,
+ SlangInt targetIndex,
+ slang::IMetadata** outMetadata,
+ slang::IBlob** outDiagnostics) SLANG_OVERRIDE
+ {
+ return Super::getEntryPointMetadata(
+ entryPointIndex,
+ targetIndex,
+ outMetadata,
+ outDiagnostics);
+ }
+
+ SLANG_NO_THROW SlangResult SLANG_MCALL getTargetMetadata(
+ SlangInt targetIndex,
+ slang::IMetadata** outMetadata,
+ slang::IBlob** outDiagnostics) SLANG_OVERRIDE
+ {
+ return Super::getTargetMetadata(targetIndex, outMetadata, outDiagnostics);
+ }
+
+ SLANG_NO_THROW SlangResult SLANG_MCALL getEntryPointCompileResult(
+ SlangInt entryPointIndex,
+ SlangInt targetIndex,
+ slang::ICompileResult** outCompileResult,
+ slang::IBlob** outDiagnostics) SLANG_OVERRIDE
+ {
+ return Super::getEntryPointCompileResult(
+ entryPointIndex,
+ targetIndex,
+ outCompileResult,
+ outDiagnostics);
+ }
+
+ SLANG_NO_THROW SlangResult SLANG_MCALL getTargetCompileResult(
+ SlangInt targetIndex,
+ slang::ICompileResult** outCompileResult,
+ slang::IBlob** outDiagnostics) SLANG_OVERRIDE
+ {
+ return Super::getTargetCompileResult(targetIndex, outCompileResult, outDiagnostics);
+ }
+
+ /// Get a serialized representation of the checked module.
+ virtual SLANG_NO_THROW SlangResult SLANG_MCALL
+ serialize(ISlangBlob** outSerializedBlob) override;
+
+ /// Write the serialized representation of this module to a file.
+ virtual SLANG_NO_THROW SlangResult SLANG_MCALL writeToFile(char const* fileName) override;
+
+ /// Get the name of the module.
+ virtual SLANG_NO_THROW const char* SLANG_MCALL getName() override;
+
+ /// Get the path of the module.
+ virtual SLANG_NO_THROW const char* SLANG_MCALL getFilePath() override;
+
+ /// Get the unique identity of the module.
+ virtual SLANG_NO_THROW const char* SLANG_MCALL getUniqueIdentity() override;
+
+ /// Get the number of dependency files that this module depends on.
+ /// This includes both the explicit source files, as well as any
+ /// additional files that were transitively referenced (e.g., via
+ /// a `#include` directive).
+ virtual SLANG_NO_THROW SlangInt32 SLANG_MCALL getDependencyFileCount() override;
+
+ /// Get the path to a file this module depends on.
+ virtual SLANG_NO_THROW char const* SLANG_MCALL getDependencyFilePath(SlangInt32 index) override;
+
+
+ // IModulePrecompileService_Experimental
+ /// Precompile TU to target language
+ virtual SLANG_NO_THROW SlangResult SLANG_MCALL
+ precompileForTarget(SlangCompileTarget target, slang::IBlob** outDiagnostics) override;
+
+ virtual SLANG_NO_THROW SlangResult SLANG_MCALL getPrecompiledTargetCode(
+ SlangCompileTarget target,
+ slang::IBlob** outCode,
+ slang::IBlob** outDiagnostics = nullptr) override;
+
+ virtual SLANG_NO_THROW SlangInt SLANG_MCALL getModuleDependencyCount() SLANG_OVERRIDE;
+
+ virtual SLANG_NO_THROW SlangResult SLANG_MCALL getModuleDependency(
+ SlangInt dependencyIndex,
+ slang::IModule** outModule,
+ slang::IBlob** outDiagnostics = nullptr) SLANG_OVERRIDE;
+
+ virtual void buildHash(DigestBuilder<SHA1>& builder) SLANG_OVERRIDE;
+
+ virtual SLANG_NO_THROW slang::DeclReflection* SLANG_MCALL getModuleReflection() SLANG_OVERRIDE;
+
+ void setDigest(SHA1::Digest const& digest) { m_digest = digest; }
+ SHA1::Digest computeDigest();
+
+ /// Create a module (initially empty).
+ Module(Linkage* linkage, ASTBuilder* astBuilder = nullptr);
+
+ /// Get the AST for the module (if it has been parsed)
+ ModuleDecl* getModuleDecl() { return m_moduleDecl; }
+
+ /// The the IR for the module (if it has been generated)
+ IRModule* getIRModule() { return m_irModule; }
+
+ /// Get the list of other modules this module depends on
+ List<Module*> const& getModuleDependencyList()
+ {
+ return m_moduleDependencyList.getModuleList();
+ }
+
+ /// Get the list of files this module depends on
+ List<SourceFile*> const& getFileDependencyList() { return m_fileDependencyList.getFileList(); }
+
+ /// Register a module that this module depends on
+ void addModuleDependency(Module* module);
+
+ /// Register a source file that this module depends on
+ void addFileDependency(SourceFile* sourceFile);
+
+ void clearFileDependency() { m_fileDependencyList.clear(); }
+ /// Set the AST for this module.
+ ///
+ /// This should only be called once, during creation of the module.
+ ///
+ void setModuleDecl(ModuleDecl* moduleDecl); // { m_moduleDecl = moduleDecl; }
+
+ void setName(String name);
+ void setName(Name* name) { m_name = name; }
+ Name* getNameObj() { return m_name; }
+
+ void setPathInfo(PathInfo pathInfo) { m_pathInfo = pathInfo; }
+
+ /// Set the IR for this module.
+ ///
+ /// This should only be called once, during creation of the module.
+ ///
+ void setIRModule(IRModule* irModule) { m_irModule = irModule; }
+
+ Index getEntryPointCount() SLANG_OVERRIDE { return 0; }
+ RefPtr<EntryPoint> getEntryPoint(Index index) SLANG_OVERRIDE
+ {
+ SLANG_UNUSED(index);
+ return nullptr;
+ }
+ String getEntryPointMangledName(Index index) SLANG_OVERRIDE
+ {
+ SLANG_UNUSED(index);
+ return String();
+ }
+ String getEntryPointNameOverride(Index index) SLANG_OVERRIDE
+ {
+ SLANG_UNUSED(index);
+ return String();
+ }
+
+ Index getShaderParamCount() SLANG_OVERRIDE { return m_shaderParams.getCount(); }
+ ShaderParamInfo getShaderParam(Index index) SLANG_OVERRIDE { return m_shaderParams[index]; }
+
+ SLANG_NO_THROW Index SLANG_MCALL getSpecializationParamCount() SLANG_OVERRIDE
+ {
+ return m_specializationParams.getCount();
+ }
+ SpecializationParam const& getSpecializationParam(Index index) SLANG_OVERRIDE
+ {
+ return m_specializationParams[index];
+ }
+
+ Index getRequirementCount() SLANG_OVERRIDE;
+ RefPtr<ComponentType> getRequirement(Index index) SLANG_OVERRIDE;
+
+ List<Module*> const& getModuleDependencies() SLANG_OVERRIDE
+ {
+ return m_moduleDependencyList.getModuleList();
+ }
+ List<SourceFile*> const& getFileDependencies() SLANG_OVERRIDE
+ {
+ return m_fileDependencyList.getFileList();
+ }
+
+ /// Given a mangled name finds the exported NodeBase associated with this module.
+ /// If not found returns nullptr.
+ Decl* findExportedDeclByMangledName(const UnownedStringSlice& mangledName);
+
+ /// Ensure that the any accelerator(s) used for `findExportedDeclByMangledName`
+ /// have already been built.
+ ///
+ void ensureExportLookupAcceleratorBuilt();
+
+ Count getExportedDeclCount();
+ Decl* getExportedDecl(Index index);
+ UnownedStringSlice getExportedDeclMangledName(Index index);
+
+ /// Get the ASTBuilder
+ ASTBuilder* getASTBuilder() { return m_astBuilder; }
+
+ /// Collect information on the shader parameters of the module.
+ ///
+ /// This method should only be called once, after the core
+ /// structured of the module (its AST and IR) have been created,
+ /// and before any of the `ComponentType` APIs are used.
+ ///
+ /// TODO: We might eventually consider a non-stateful approach
+ /// to constructing a `Module`.
+ ///
+ void _collectShaderParams();
+
+ void _discoverEntryPoints(DiagnosticSink* sink, const List<RefPtr<TargetRequest>>& targets);
+ void _discoverEntryPointsImpl(
+ ContainerDecl* containerDecl,
+ DiagnosticSink* sink,
+ const List<RefPtr<TargetRequest>>& targets);
+
+
+ class ModuleSpecializationInfo : public SpecializationInfo
+ {
+ public:
+ struct GenericArgInfo
+ {
+ Decl* paramDecl = nullptr;
+ Val* argVal = nullptr;
+ };
+
+ List<GenericArgInfo> genericArgs;
+ List<ExpandedSpecializationArg> existentialArgs;
+ };
+
+ RefPtr<EntryPoint> findEntryPointByName(UnownedStringSlice const& name);
+ RefPtr<EntryPoint> findAndCheckEntryPoint(
+ UnownedStringSlice const& name,
+ SlangStage stage,
+ ISlangBlob** outDiagnostics);
+
+ List<RefPtr<EntryPoint>>& getEntryPoints() { return m_entryPoints; }
+ void _addEntryPoint(EntryPoint* entryPoint);
+ void _processFindDeclsExportSymbolsRec(Decl* decl);
+
+ // Gets the files that has been included into the module.
+ Dictionary<SourceFile*, FileDecl*>& getIncludedSourceFileMap()
+ {
+ return m_mapSourceFileToFileDecl;
+ }
+
+protected:
+ void acceptVisitor(ComponentTypeVisitor* visitor, SpecializationInfo* specializationInfo)
+ SLANG_OVERRIDE;
+
+ RefPtr<SpecializationInfo> _validateSpecializationArgsImpl(
+ SpecializationArg const* args,
+ Index argCount,
+ DiagnosticSink* sink) SLANG_OVERRIDE;
+
+private:
+ Name* m_name = nullptr;
+ PathInfo m_pathInfo;
+
+ // The AST for the module
+ ModuleDecl* m_moduleDecl = nullptr;
+
+ // The IR for the module
+ RefPtr<IRModule> m_irModule = nullptr;
+
+ List<ShaderParamInfo> m_shaderParams;
+ SpecializationParams m_specializationParams;
+
+ List<Module*> m_requirements;
+
+ // A digest that uniquely identifies the contents of the module.
+ SHA1::Digest m_digest;
+
+ // List of modules this module depends on
+ ModuleDependencyList m_moduleDependencyList;
+
+ // List of source files this module depends on
+ FileDependencyList m_fileDependencyList;
+
+ // Entry points that were defined in this module
+ //
+ // Note: the entry point defined in the module are *not*
+ // part of the memory image/layout of the module when
+ // it is considered as an IComponentType. This can be
+ // a bit confusing, but if all the entry points in the
+ // module were automatically linked into the component
+ // type, we'd need a way to access just the global
+ // scope of the module without the entry points, in
+ // case we wanted to link a single entry point against
+ // the global scope. The `Module` type provides exactly
+ // that "module without its entry points" unit of
+ // granularity for linking.
+ //
+ // This list only exists for lookup purposes, so that
+ // the user can find an existing entry-point function
+ // that was defined as part of the module.
+ //
+ List<RefPtr<EntryPoint>> m_entryPoints;
+
+ // The builder that owns all of the AST nodes from parsing the source of
+ // this module.
+ RefPtr<ASTBuilder> m_astBuilder;
+
+ // Holds map of exported mangled names to symbols. m_mangledExportPool maps names to indices,
+ // and m_mangledExportSymbols holds the NodeBase* values for each index.
+ StringSlicePool m_mangledExportPool;
+ List<Decl*> m_mangledExportSymbols;
+
+ // Source files that have been pulled into the module with `__include`.
+ Dictionary<SourceFile*, FileDecl*> m_mapSourceFileToFileDecl;
+
+public:
+ SLANG_NO_THROW SlangResult SLANG_MCALL disassemble(slang::IBlob** outDisassembledBlob) override
+ {
+ if (!outDisassembledBlob)
+ return SLANG_E_INVALID_ARG;
+ String disassembly;
+ this->getIRModule()->getModuleInst()->dump(disassembly);
+ auto blob = StringUtil::createStringBlob(disassembly);
+ *outDisassembledBlob = blob.detach();
+ return SLANG_OK;
+ }
+};
+
+} // namespace Slang
diff --git a/source/slang/slang-pass-through.cpp b/source/slang/slang-pass-through.cpp
new file mode 100644
index 000000000..268cbc98f
--- /dev/null
+++ b/source/slang/slang-pass-through.cpp
@@ -0,0 +1,275 @@
+// slang-pass-through.cpp
+#include "slang-pass-through.h"
+
+#include "../core/slang-type-text-util.h"
+#include "compiler-core/slang-slice-allocator.h"
+#include "slang-compiler.h"
+
+namespace Slang
+{
+
+void printDiagnosticArg(StringBuilder& sb, PassThroughMode val)
+{
+ sb << TypeTextUtil::getPassThroughName(SlangPassThrough(val));
+}
+
+SourceLanguage getDefaultSourceLanguageForDownstreamCompiler(PassThroughMode compiler)
+{
+ switch (compiler)
+ {
+ case PassThroughMode::None:
+ {
+ return SourceLanguage::Unknown;
+ }
+ case PassThroughMode::Fxc:
+ case PassThroughMode::Dxc:
+ {
+ return SourceLanguage::HLSL;
+ }
+ case PassThroughMode::Glslang:
+ {
+ return SourceLanguage::GLSL;
+ }
+ case PassThroughMode::LLVM:
+ case PassThroughMode::Clang:
+ case PassThroughMode::VisualStudio:
+ case PassThroughMode::Gcc:
+ case PassThroughMode::GenericCCpp:
+ {
+ // These could ingest C, but we only have this function to work out a
+ // 'default' language to ingest.
+ return SourceLanguage::CPP;
+ }
+ case PassThroughMode::NVRTC:
+ {
+ return SourceLanguage::CUDA;
+ }
+ case PassThroughMode::Tint:
+ {
+ return SourceLanguage::WGSL;
+ }
+ case PassThroughMode::SpirvDis:
+ {
+ return SourceLanguage::SPIRV;
+ }
+ case PassThroughMode::MetalC:
+ {
+ return SourceLanguage::Metal;
+ }
+ default:
+ break;
+ }
+ SLANG_ASSERT(!"Unknown compiler");
+ return SourceLanguage::Unknown;
+}
+
+PassThroughMode getDownstreamCompilerRequiredForTarget(CodeGenTarget target)
+{
+ switch (target)
+ {
+ // Don't *require* a downstream compiler for source output
+ case CodeGenTarget::GLSL:
+ case CodeGenTarget::HLSL:
+ case CodeGenTarget::CUDASource:
+ case CodeGenTarget::CPPSource:
+ case CodeGenTarget::HostCPPSource:
+ case CodeGenTarget::PyTorchCppBinding:
+ case CodeGenTarget::CSource:
+ case CodeGenTarget::Metal:
+ case CodeGenTarget::WGSL:
+ {
+ return PassThroughMode::None;
+ }
+ case CodeGenTarget::None:
+ {
+ return PassThroughMode::None;
+ }
+ case CodeGenTarget::WGSLSPIRVAssembly:
+ case CodeGenTarget::SPIRVAssembly:
+ case CodeGenTarget::SPIRV:
+ {
+ return PassThroughMode::SpirvDis;
+ }
+ case CodeGenTarget::DXBytecode:
+ case CodeGenTarget::DXBytecodeAssembly:
+ {
+ return PassThroughMode::Fxc;
+ }
+ case CodeGenTarget::DXIL:
+ case CodeGenTarget::DXILAssembly:
+ {
+ return PassThroughMode::Dxc;
+ }
+ case CodeGenTarget::MetalLib:
+ case CodeGenTarget::MetalLibAssembly:
+ {
+ return PassThroughMode::MetalC;
+ }
+ case CodeGenTarget::ShaderHostCallable:
+ case CodeGenTarget::ShaderSharedLibrary:
+ case CodeGenTarget::HostExecutable:
+ case CodeGenTarget::HostHostCallable:
+ case CodeGenTarget::HostSharedLibrary:
+ {
+ // We need some C/C++ compiler
+ return PassThroughMode::GenericCCpp;
+ }
+ case CodeGenTarget::PTX:
+ {
+ return PassThroughMode::NVRTC;
+ }
+ case CodeGenTarget::WGSLSPIRV:
+ {
+ return PassThroughMode::Tint;
+ }
+ default:
+ break;
+ }
+
+ SLANG_ASSERT(!"Unhandled target");
+ return PassThroughMode::None;
+}
+
+
+void reportExternalCompileError(
+ const char* compilerName,
+ Severity severity,
+ SlangResult res,
+ const UnownedStringSlice& diagnostic,
+ DiagnosticSink* sink)
+{
+ StringBuilder builder;
+ if (compilerName)
+ {
+ builder << compilerName << ": ";
+ }
+
+ if (SLANG_FAILED(res) && res != SLANG_FAIL)
+ {
+ {
+ char tmp[17];
+ sprintf_s(tmp, SLANG_COUNT_OF(tmp), "0x%08x", uint32_t(res));
+ builder << "Result(" << tmp << ") ";
+ }
+
+ PlatformUtil::appendResult(res, builder);
+ }
+
+ if (diagnostic.getLength() > 0)
+ {
+ builder.append(diagnostic);
+ if (!diagnostic.endsWith("\n"))
+ {
+ builder.append("\n");
+ }
+ }
+
+ sink->diagnoseRaw(severity, builder.getUnownedSlice());
+}
+
+void reportExternalCompileError(
+ const char* compilerName,
+ SlangResult res,
+ const UnownedStringSlice& diagnostic,
+ DiagnosticSink* sink)
+{
+ // TODO(tfoley): need a better policy for how we translate diagnostics
+ // back into the Slang world (although we should always try to generate
+ // HLSL that doesn't produce any diagnostics...)
+ reportExternalCompileError(
+ compilerName,
+ SLANG_FAILED(res) ? Severity::Error : Severity::Warning,
+ res,
+ diagnostic,
+ sink);
+}
+
+static Severity _getDiagnosticSeverity(ArtifactDiagnostic::Severity severity)
+{
+ switch (severity)
+ {
+ case ArtifactDiagnostic::Severity::Warning:
+ return Severity::Warning;
+ case ArtifactDiagnostic::Severity::Info:
+ return Severity::Note;
+ default:
+ return Severity::Error;
+ }
+}
+
+SlangResult passthroughDownstreamDiagnostics(
+ DiagnosticSink* sink,
+ IDownstreamCompiler* compiler,
+ IArtifact* artifact)
+{
+ auto diagnostics = findAssociatedRepresentation<IArtifactDiagnostics>(artifact);
+
+ if (!diagnostics)
+ return SLANG_OK;
+
+ if (diagnostics->getCount())
+ {
+ StringBuilder compilerText;
+ DownstreamCompilerUtil::appendAsText(compiler->getDesc(), compilerText);
+
+ StringBuilder builder;
+
+ auto const diagnosticCount = diagnostics->getCount();
+ for (Index i = 0; i < diagnosticCount; ++i)
+ {
+ const auto& diagnostic = *diagnostics->getAt(i);
+
+ builder.clear();
+
+ const Severity severity = _getDiagnosticSeverity(diagnostic.severity);
+
+ if (diagnostic.filePath.count == 0 && diagnostic.location.line == 0 &&
+ severity == Severity::Note)
+ {
+ // If theres no filePath line number and it's info, output severity and text alone
+ builder << getSeverityName(severity) << " : ";
+ }
+ else
+ {
+ if (diagnostic.filePath.count)
+ {
+ builder << asStringSlice(diagnostic.filePath);
+ }
+
+ if (diagnostic.location.line)
+ {
+ builder << "(" << diagnostic.location.line << ")";
+ }
+
+ builder << ": ";
+
+ if (diagnostic.stage == ArtifactDiagnostic::Stage::Link)
+ {
+ builder << "link ";
+ }
+
+ builder << getSeverityName(severity);
+ builder << " " << asStringSlice(diagnostic.code) << ": ";
+ }
+
+ builder << asStringSlice(diagnostic.text);
+ reportExternalCompileError(
+ compilerText.getBuffer(),
+ severity,
+ SLANG_OK,
+ builder.getUnownedSlice(),
+ sink);
+ }
+ }
+
+ // If any errors are emitted, then we are done
+ if (diagnostics->hasOfAtLeastSeverity(ArtifactDiagnostic::Severity::Error))
+ {
+ return SLANG_FAIL;
+ }
+
+ return SLANG_OK;
+}
+
+
+} // namespace Slang
diff --git a/source/slang/slang-pass-through.h b/source/slang/slang-pass-through.h
new file mode 100644
index 000000000..8cb350572
--- /dev/null
+++ b/source/slang/slang-pass-through.h
@@ -0,0 +1,91 @@
+// slang-pass-through.h
+#pragma once
+
+//
+// This file gathers together declarations for utility code
+// related to the concept of "pass-through" compilation.
+//
+// Note that in the Slang codebase there is an unfortunate
+// conflation of terminology (and a resulting conflation
+// in a lot of the implementation logic) between the cases
+// of true *pass-through* compilation and the logic related
+// to *downstream* compilers:
+//
+// * While the Slang compiler is architected to support direct
+// generation of binary output code (e.g., there is support
+// for emitting SPIR-V directly from Slang IR), many targets
+// are supported by first generating intermediate source code
+// from Slang IR and then invoking a *downstream* compiler on
+// that intermediate source code to produce output binaries.
+// This is an important kind of compilation flow that Slang
+// was designed to support.
+//
+// * In contrast, true *pass-through* compilation is a legacy
+// feature of `slangc` that exists almost entirely to support
+// some of the earliest test cases that were written for the
+// compiler. In true pass-through mode, `slangc` skips invoking
+// large parts of the Slang compiler (the entire front-end,
+// along with all of the back-end up to the point that intermediate
+// source code would be generated) and then uses the *input*
+// source as if it was the *intermediate* code for the "last mile"
+// of code generation (invoking a downstream compiler). This
+// feature can and *should* be deprecate and removed, because
+// the complexity it creates for the rest of the compiler is
+// no longer worth it.
+//
+// This file may contain a mix of declarations used for one or
+// both of the above purposes, just because the terminology used
+// in the codebase isn't always precise or clear. Over time the
+// declarations here can and should be more clearly partitioned
+// so that we can distinguish the essential (downstream compilation)
+// parts, and the parts that should eventually get removed (true
+// pass-through compilation).
+//
+
+#include "../core/slang-string.h"
+#include "slang-target.h"
+
+namespace Slang
+{
+
+enum class PassThroughMode : SlangPassThroughIntegral
+{
+ None = SLANG_PASS_THROUGH_NONE, ///< don't pass through: use Slang compiler
+ Fxc = SLANG_PASS_THROUGH_FXC, ///< pass through HLSL to `D3DCompile` API
+ Dxc = SLANG_PASS_THROUGH_DXC, ///< pass through HLSL to `IDxcCompiler` API
+ Glslang = SLANG_PASS_THROUGH_GLSLANG, ///< pass through GLSL to `glslang` library
+ SpirvDis = SLANG_PASS_THROUGH_SPIRV_DIS, ///< pass through spirv-dis
+ Clang = SLANG_PASS_THROUGH_CLANG, ///< Pass through clang compiler
+ VisualStudio = SLANG_PASS_THROUGH_VISUAL_STUDIO, ///< Visual studio compiler
+ Gcc = SLANG_PASS_THROUGH_GCC, ///< Gcc compiler
+ GenericCCpp = SLANG_PASS_THROUGH_GENERIC_C_CPP, ///< Generic C/C++ compiler
+ NVRTC = SLANG_PASS_THROUGH_NVRTC, ///< NVRTC CUDA compiler
+ LLVM = SLANG_PASS_THROUGH_LLVM, ///< LLVM 'compiler'
+ SpirvOpt = SLANG_PASS_THROUGH_SPIRV_OPT, ///< pass thorugh spirv to spirv-opt
+ MetalC = SLANG_PASS_THROUGH_METAL,
+ Tint = SLANG_PASS_THROUGH_TINT, ///< pass through spirv to Tint API
+ SpirvLink = SLANG_PASS_THROUGH_SPIRV_LINK, ///< pass through spirv to spirv-link
+ CountOf = SLANG_PASS_THROUGH_COUNT_OF,
+};
+void printDiagnosticArg(StringBuilder& sb, PassThroughMode val);
+
+/// Given a target returns the required downstream compiler
+PassThroughMode getDownstreamCompilerRequiredForTarget(CodeGenTarget target);
+
+/// Given a target returns a downstream compiler the prelude should be taken from.
+SourceLanguage getDefaultSourceLanguageForDownstreamCompiler(PassThroughMode compiler);
+
+/* Report an error appearing from external compiler to the diagnostic sink error to the diagnostic
+sink.
+@param compilerName The name of the compiler the error came for (or nullptr if not known)
+@param res Result associated with the error. The error code will be reported. (Can take HRESULT -
+and will expand to string if known)
+@param diagnostic The diagnostic string associated with the compile failure
+@param sink The diagnostic sink to report to */
+void reportExternalCompileError(
+ const char* compilerName,
+ SlangResult res,
+ const UnownedStringSlice& diagnostic,
+ DiagnosticSink* sink);
+
+} // namespace Slang
diff --git a/source/slang/slang-profile.cpp b/source/slang/slang-profile.cpp
index 7e12b4f4e..f7f6b7bdd 100644
--- a/source/slang/slang-profile.cpp
+++ b/source/slang/slang-profile.cpp
@@ -4,6 +4,190 @@
namespace Slang
{
+Profile Profile::lookUp(UnownedStringSlice const& name)
+{
+#define PROFILE(TAG, NAME, STAGE, VERSION) \
+ if (name == UnownedTerminatedStringSlice(#NAME)) \
+ return Profile::TAG;
+#define PROFILE_ALIAS(TAG, DEF, NAME) \
+ if (name == UnownedTerminatedStringSlice(#NAME)) \
+ return Profile::TAG;
+#include "slang-profile-defs.h"
+
+ return Profile::Unknown;
+}
+
+Profile Profile::lookUp(char const* name)
+{
+ return lookUp(UnownedTerminatedStringSlice(name));
+}
+
+CapabilitySet Profile::getCapabilityName()
+{
+ List<CapabilityName> result;
+ switch (getVersion())
+ {
+#define PROFILE_VERSION(TAG, NAME) \
+ case ProfileVersion::TAG: \
+ result.add(CapabilityName::TAG); \
+ break;
+#include "slang-profile-defs.h"
+ default:
+ break;
+ }
+ switch (getStage())
+ {
+#define PROFILE_STAGE(TAG, NAME, VAL) \
+ case Stage::TAG: \
+ result.add(CapabilityName::NAME); \
+ break;
+#include "slang-profile-defs.h"
+ default:
+ break;
+ }
+
+ CapabilitySet resultSet = CapabilitySet(result);
+ for (auto i : this->additionalCapabilities)
+ resultSet.join(i);
+ return resultSet;
+}
+
+char const* Profile::getName()
+{
+ switch (raw)
+ {
+ default:
+ return "unknown";
+
+#define PROFILE(TAG, NAME, STAGE, VERSION) \
+ case Profile::TAG: \
+ return #NAME;
+#define PROFILE_ALIAS(TAG, DEF, NAME) /* empty */
+#include "slang-profile-defs.h"
+ }
+}
+
+static const StageInfo kStages[] = {
+#define PROFILE_STAGE(ID, NAME, ENUM) {#NAME, Stage::ID},
+
+#define PROFILE_STAGE_ALIAS(ID, NAME, VAL) {#NAME, Stage::ID},
+
+#include "slang-profile-defs.h"
+};
+
+ConstArrayView<StageInfo> getStageInfos()
+{
+ return makeConstArrayView(kStages);
+}
+
+Stage findStageByName(String const& name)
+{
+ for (auto entry : kStages)
+ {
+ if (name == entry.name)
+ {
+ return entry.stage;
+ }
+ }
+
+ return Stage::Unknown;
+}
+
+UnownedStringSlice getStageText(Stage stage)
+{
+ for (auto entry : kStages)
+ {
+ if (stage == entry.stage)
+ {
+ return UnownedStringSlice(entry.name);
+ }
+ }
+ return UnownedStringSlice();
+}
+
+Stage getStageFromAtom(CapabilityAtom atom)
+{
+ switch (atom)
+ {
+ case CapabilityAtom::vertex:
+ return Stage::Vertex;
+ case CapabilityAtom::hull:
+ return Stage::Hull;
+ case CapabilityAtom::domain:
+ return Stage::Domain;
+ case CapabilityAtom::geometry:
+ return Stage::Geometry;
+ case CapabilityAtom::fragment:
+ return Stage::Fragment;
+ case CapabilityAtom::compute:
+ return Stage::Compute;
+ case CapabilityAtom::_mesh:
+ return Stage::Mesh;
+ case CapabilityAtom::_amplification:
+ return Stage::Amplification;
+ case CapabilityAtom::_anyhit:
+ return Stage::AnyHit;
+ case CapabilityAtom::_closesthit:
+ return Stage::ClosestHit;
+ case CapabilityAtom::_intersection:
+ return Stage::Intersection;
+ case CapabilityAtom::_raygen:
+ return Stage::RayGeneration;
+ case CapabilityAtom::_miss:
+ return Stage::Miss;
+ case CapabilityAtom::_callable:
+ return Stage::Callable;
+ case CapabilityAtom::dispatch:
+ return Stage::Dispatch;
+ default:
+ SLANG_UNEXPECTED("unknown stage atom");
+ UNREACHABLE_RETURN(Stage::Unknown);
+ }
+}
+
+CapabilityAtom getAtomFromStage(Stage stage)
+{
+ // Convert Slang::Stage to CapabilityAtom.
+ // Note that capabilities do not share the same values as Slang::Stage
+ // and must be explicitly converted.
+ switch (stage)
+ {
+ case Stage::Compute:
+ return CapabilityAtom::compute;
+ case Stage::Vertex:
+ return CapabilityAtom::vertex;
+ case Stage::Fragment:
+ return CapabilityAtom::fragment;
+ case Stage::Geometry:
+ return CapabilityAtom::geometry;
+ case Stage::Hull:
+ return CapabilityAtom::hull;
+ case Stage::Domain:
+ return CapabilityAtom::domain;
+ case Stage::Mesh:
+ return CapabilityAtom::_mesh;
+ case Stage::Amplification:
+ return CapabilityAtom::_amplification;
+ case Stage::RayGeneration:
+ return CapabilityAtom::_raygen;
+ case Stage::AnyHit:
+ return CapabilityAtom::_anyhit;
+ case Stage::ClosestHit:
+ return CapabilityAtom::_closesthit;
+ case Stage::Miss:
+ return CapabilityAtom::_miss;
+ case Stage::Intersection:
+ return CapabilityAtom::_intersection;
+ case Stage::Callable:
+ return CapabilityAtom::_callable;
+ case Stage::Dispatch:
+ return CapabilityAtom::dispatch;
+ default:
+ SLANG_UNEXPECTED("unknown stage");
+ UNREACHABLE_RETURN(CapabilityAtom::Invalid);
+ }
+}
+
ProfileFamily getProfileFamily(ProfileVersion version)
{
switch (version)
@@ -59,5 +243,93 @@ void printDiagnosticArg(StringBuilder& sb, ProfileVersion val)
sb << Profile(val).getName();
}
+String getHLSLProfileName(Profile profile)
+{
+ switch (profile.getFamily())
+ {
+ case ProfileFamily::DX:
+ // Profile version is a DX one, so stick with it.
+ break;
+
+ default:
+ // Profile is a non-DX profile family, so we need to try
+ // to clobber it with something to get a default.
+ //
+ // TODO: This is a huge hack...
+ profile.setVersion(ProfileVersion::DX_5_1);
+ break;
+ }
+
+ char const* stagePrefix = nullptr;
+ switch (profile.getStage())
+ {
+ // Note: All of the raytracing-related stages require
+ // compiling for a `lib_*` profile, even when only a
+ // single entry point is present.
+ //
+ // We also go ahead and use this target in any case
+ // where we don't know the actual stage to compiel for,
+ // as a fallback option.
+ //
+ // TODO: We also want to use this option when compiling
+ // multiple entry points to a DXIL library.
+ //
+ default:
+ stagePrefix = "lib";
+ break;
+
+ // The traditional rasterization pipeline and compute
+ // shaders all have custom profile names that identify
+ // both the stage and shader model, which need to be
+ // used when compiling a single entry point.
+ //
+#define CASE(NAME, PREFIX) \
+ case Stage::NAME: \
+ stagePrefix = #PREFIX; \
+ break
+ CASE(Vertex, vs);
+ CASE(Hull, hs);
+ CASE(Domain, ds);
+ CASE(Geometry, gs);
+ CASE(Fragment, ps);
+ CASE(Compute, cs);
+ CASE(Amplification, as);
+ CASE(Mesh, ms);
+#undef CASE
+ }
+
+ char const* versionSuffix = nullptr;
+ switch (profile.getVersion())
+ {
+#define CASE(TAG, SUFFIX) \
+ case ProfileVersion::TAG: \
+ versionSuffix = #SUFFIX; \
+ break
+ CASE(DX_4_0, _4_0);
+ CASE(DX_4_1, _4_1);
+ CASE(DX_5_0, _5_0);
+ CASE(DX_5_1, _5_1);
+ CASE(DX_6_0, _6_0);
+ CASE(DX_6_1, _6_1);
+ CASE(DX_6_2, _6_2);
+ CASE(DX_6_3, _6_3);
+ CASE(DX_6_4, _6_4);
+ CASE(DX_6_5, _6_5);
+ CASE(DX_6_6, _6_6);
+ CASE(DX_6_7, _6_7);
+ CASE(DX_6_8, _6_8);
+ CASE(DX_6_9, _6_9);
+#undef CASE
+
+ default:
+ return "unknown";
+ }
+
+ String result;
+ result.append(stagePrefix);
+ result.append(versionSuffix);
+ return result;
+}
+
} // namespace Slang
diff --git a/source/slang/slang-profile.h b/source/slang/slang-profile.h
index ca7b8b2ae..96c968aa8 100644
--- a/source/slang/slang-profile.h
+++ b/source/slang/slang-profile.h
@@ -130,6 +130,8 @@ UnownedStringSlice getStageText(Stage stage);
Stage getStageFromAtom(CapabilityAtom atom);
CapabilityAtom getAtomFromStage(Stage stage);
+String getHLSLProfileName(Profile profile);
+
} // namespace Slang
#endif
diff --git a/source/slang/slang-serialize-ast.cpp b/source/slang/slang-serialize-ast.cpp
index 325aa3244..261436e38 100644
--- a/source/slang/slang-serialize-ast.cpp
+++ b/source/slang/slang-serialize-ast.cpp
@@ -1234,7 +1234,6 @@ void serialize(ASTSerializer const& serializer, ValNodeOperand& value)
//
#if 0 // FIDDLE TEMPLATE:
%for _,T in ipairs(astStructTypes) do
-% TRACE(T)
/// Fossilized representation of a value of type `$T`
struct Fossilized_$T
% if T.directSuperClass then
diff --git a/source/slang/slang-session.cpp b/source/slang/slang-session.cpp
new file mode 100644
index 000000000..da39949da
--- /dev/null
+++ b/source/slang/slang-session.cpp
@@ -0,0 +1,2068 @@
+// slang-session.cpp
+#include "slang-session.h"
+
+#include "compiler-core/slang-artifact-util.h"
+#include "slang-check-impl.h"
+#include "slang-compiler.h"
+#include "slang-lower-to-ir.h"
+#include "slang-mangle.h"
+#include "slang-options.h"
+#include "slang-parser.h"
+#include "slang-preprocessor.h"
+#include "slang-serialize-ast.h"
+#include "slang-serialize-container.h"
+#include "slang-serialize-ir.h"
+
+namespace Slang
+{
+
+Linkage::Linkage(Session* session, ASTBuilder* astBuilder, Linkage* builtinLinkage)
+ : m_session(session)
+ , m_retainedSession(session)
+ , m_sourceManager(&m_defaultSourceManager)
+ , m_astBuilder(astBuilder)
+ , m_cmdLineContext(new CommandLineContext())
+ , m_stringSlicePool(StringSlicePool::Style::Default)
+{
+ namePool = session->getNamePool();
+
+ m_defaultSourceManager.initialize(session->getBuiltinSourceManager(), nullptr);
+
+ setFileSystem(nullptr);
+
+ // Copy of the built in linkages modules
+ if (builtinLinkage)
+ {
+ for (const auto& nameToMod : builtinLinkage->mapNameToLoadedModules)
+ mapNameToLoadedModules.add(nameToMod);
+ }
+
+ m_semanticsForReflection = new SharedSemanticsContext(this, nullptr, nullptr);
+}
+
+SharedSemanticsContext* Linkage::getSemanticsForReflection()
+{
+ return m_semanticsForReflection.get();
+}
+
+ISlangUnknown* Linkage::getInterface(const Guid& guid)
+{
+ if (guid == ISlangUnknown::getTypeGuid() || guid == ISession::getTypeGuid())
+ return asExternal(this);
+
+ return nullptr;
+}
+
+Linkage::~Linkage()
+{
+ // Upstream type checking cache.
+ if (m_typeCheckingCache)
+ {
+ auto globalSession = getSessionImpl();
+ std::lock_guard<std::mutex> lock(globalSession->m_typeCheckingCacheMutex);
+ if (!globalSession->m_typeCheckingCache ||
+ globalSession->getTypeCheckingCache()->resolvedOperatorOverloadCache.getCount() <
+ getTypeCheckingCache()->resolvedOperatorOverloadCache.getCount())
+ {
+ globalSession->m_typeCheckingCache = m_typeCheckingCache;
+ getTypeCheckingCache()->version++;
+ }
+ destroyTypeCheckingCache();
+ }
+}
+
+SearchDirectoryList& Linkage::getSearchDirectories()
+{
+ auto list = m_optionSet.getArray(CompilerOptionName::Include);
+ if (list.getCount() != searchDirectoryCache.searchDirectories.getCount())
+ {
+ searchDirectoryCache.searchDirectories.clear();
+ for (auto dir : list)
+ searchDirectoryCache.searchDirectories.add(SearchDirectory(dir.stringValue));
+ }
+ return searchDirectoryCache;
+}
+
+TypeCheckingCache* Linkage::getTypeCheckingCache()
+{
+ if (!m_typeCheckingCache)
+ {
+ m_typeCheckingCache = new TypeCheckingCache();
+ }
+ return static_cast<TypeCheckingCache*>(m_typeCheckingCache.get());
+}
+
+void Linkage::destroyTypeCheckingCache()
+{
+ m_typeCheckingCache = nullptr;
+}
+
+SLANG_NO_THROW slang::IGlobalSession* SLANG_MCALL Linkage::getGlobalSession()
+{
+ return asExternal(getSessionImpl());
+}
+
+void Linkage::addTarget(slang::TargetDesc const& desc)
+{
+ SLANG_AST_BUILDER_RAII(getASTBuilder());
+
+ auto targetIndex = addTarget(CodeGenTarget(desc.format));
+ auto target = targets[targetIndex];
+
+ auto& optionSet = target->getOptionSet();
+ optionSet.inheritFrom(m_optionSet);
+
+ optionSet.set(CompilerOptionName::FloatingPointMode, FloatingPointMode(desc.floatingPointMode));
+ optionSet.addTargetFlags(desc.flags);
+ optionSet.setProfile(Profile(desc.profile));
+ optionSet.set(CompilerOptionName::LineDirectiveMode, LineDirectiveMode(desc.lineDirectiveMode));
+ optionSet.set(CompilerOptionName::GLSLForceScalarLayout, desc.forceGLSLScalarBufferLayout);
+
+ CompilerOptionSet targetOptions;
+ targetOptions.load(desc.compilerOptionEntryCount, desc.compilerOptionEntries);
+ optionSet.overrideWith(targetOptions);
+}
+
+SLANG_NO_THROW slang::IModule* SLANG_MCALL
+Linkage::loadModule(const char* moduleName, slang::IBlob** outDiagnostics)
+{
+ SLANG_AST_BUILDER_RAII(getASTBuilder());
+
+ DiagnosticSink sink(getSourceManager(), Lexer::sourceLocationLexer);
+ applySettingsToDiagnosticSink(&sink, &sink, m_optionSet);
+
+ if (isInLanguageServer())
+ {
+ sink.setFlags(DiagnosticSink::Flag::HumaneLoc | DiagnosticSink::Flag::LanguageServer);
+ }
+
+ try
+ {
+ auto name = getNamePool()->getName(moduleName);
+
+ auto module = findOrImportModule(name, SourceLoc(), &sink);
+ sink.getBlobIfNeeded(outDiagnostics);
+
+ return asExternal(module);
+ }
+ catch (const AbortCompilationException& e)
+ {
+ outputExceptionDiagnostic(e, sink, outDiagnostics);
+ return nullptr;
+ }
+ catch (const Exception& e)
+ {
+ outputExceptionDiagnostic(e, sink, outDiagnostics);
+ return nullptr;
+ }
+ catch (...)
+ {
+ outputExceptionDiagnostic(sink, outDiagnostics);
+ return nullptr;
+ }
+}
+
+slang::IModule* Linkage::loadModuleFromBlob(
+ const char* moduleName,
+ const char* path,
+ slang::IBlob* source,
+ ModuleBlobType blobType,
+ slang::IBlob** outDiagnostics)
+{
+ SLANG_AST_BUILDER_RAII(getASTBuilder());
+
+ DiagnosticSink sink(getSourceManager(), Lexer::sourceLocationLexer);
+ applySettingsToDiagnosticSink(&sink, &sink, m_optionSet);
+
+ if (isInLanguageServer())
+ {
+ sink.setFlags(DiagnosticSink::Flag::HumaneLoc | DiagnosticSink::Flag::LanguageServer);
+ }
+
+
+ try
+ {
+ auto getDigestStr = [](auto x)
+ {
+ DigestBuilder<SHA1> digestBuilder;
+ digestBuilder.append(x);
+ return digestBuilder.finalize().toString();
+ };
+
+ String moduleNameStr = moduleName;
+ if (!moduleName)
+ moduleNameStr = getDigestStr(source);
+
+ auto name = getNamePool()->getName(moduleNameStr);
+ RefPtr<LoadedModule> loadedModule;
+ if (mapNameToLoadedModules.tryGetValue(name, loadedModule))
+ {
+ return loadedModule;
+ }
+ String pathStr = path;
+ if (pathStr.getLength() == 0)
+ {
+ // If path is empty, use a digest from source as path.
+ pathStr = getDigestStr(source);
+ }
+ auto pathInfo = PathInfo::makeFromString(pathStr);
+ if (File::exists(pathStr))
+ {
+ String cannonicalPath;
+ if (SLANG_SUCCEEDED(Path::getCanonical(pathStr, cannonicalPath)))
+ {
+ pathInfo = PathInfo::makeNormal(pathStr, cannonicalPath);
+ }
+ }
+ RefPtr<Module> module =
+ loadModuleImpl(name, pathInfo, source, SourceLoc(), &sink, nullptr, blobType);
+ sink.getBlobIfNeeded(outDiagnostics);
+ return asExternal(module.get());
+ }
+ catch (const AbortCompilationException& e)
+ {
+ outputExceptionDiagnostic(e, sink, outDiagnostics);
+ return nullptr;
+ }
+ catch (const Exception& e)
+ {
+ outputExceptionDiagnostic(e, sink, outDiagnostics);
+ return nullptr;
+ }
+ catch (...)
+ {
+ outputExceptionDiagnostic(sink, outDiagnostics);
+ return nullptr;
+ }
+}
+
+SLANG_NO_THROW slang::IModule* SLANG_MCALL Linkage::loadModuleFromSource(
+ const char* moduleName,
+ const char* path,
+ slang::IBlob* source,
+ slang::IBlob** outDiagnostics)
+{
+ return loadModuleFromBlob(moduleName, path, source, ModuleBlobType::Source, outDiagnostics);
+}
+
+SLANG_NO_THROW slang::IModule* SLANG_MCALL Linkage::loadModuleFromSourceString(
+ const char* moduleName,
+ const char* path,
+ const char* source,
+ slang::IBlob** outDiagnostics)
+{
+ auto sourceBlob = StringBlob::create(UnownedStringSlice(source));
+ return loadModuleFromSource(moduleName, path, sourceBlob.get(), outDiagnostics);
+}
+
+SLANG_NO_THROW slang::IModule* SLANG_MCALL Linkage::loadModuleFromIRBlob(
+ const char* moduleName,
+ const char* path,
+ slang::IBlob* source,
+ slang::IBlob** outDiagnostics)
+{
+ return loadModuleFromBlob(moduleName, path, source, ModuleBlobType::IR, outDiagnostics);
+}
+
+SLANG_NO_THROW SlangResult SLANG_MCALL Linkage::loadModuleInfoFromIRBlob(
+ slang::IBlob* source,
+ SlangInt& outModuleVersion,
+ const char*& outModuleCompilerVersion,
+ const char*& outModuleName)
+{
+ // We start by reading the content of the file as
+ // an in-memory RIFF container.
+ //
+ auto rootChunk = RIFF::RootChunk::getFromBlob(source);
+ if (!rootChunk)
+ {
+ return SLANG_FAIL;
+ }
+
+ auto moduleChunk = ModuleChunk::find(rootChunk);
+ if (!moduleChunk)
+ {
+ return SLANG_FAIL;
+ }
+
+ auto irChunk = moduleChunk->findIR();
+ if (!irChunk)
+ {
+ return SLANG_FAIL;
+ }
+
+ RefPtr<IRModule> irModule;
+ String compilerVersion;
+ UInt version;
+ String name;
+ SLANG_RETURN_ON_FAIL(readSerializedModuleInfo(irChunk, compilerVersion, version, name));
+ const auto compilerVersionSlice = m_stringSlicePool.addAndGetSlice(compilerVersion);
+ const auto nameSlice = m_stringSlicePool.addAndGetSlice(name);
+ outModuleCompilerVersion = compilerVersionSlice.begin();
+ outModuleName = nameSlice.begin();
+ outModuleVersion = SlangInt(version);
+
+ return SLANG_OK;
+}
+
+SLANG_NO_THROW SlangResult SLANG_MCALL Linkage::createCompositeComponentType(
+ slang::IComponentType* const* componentTypes,
+ SlangInt componentTypeCount,
+ slang::IComponentType** outCompositeComponentType,
+ ISlangBlob** outDiagnostics)
+{
+ if (outCompositeComponentType == nullptr)
+ return SLANG_E_INVALID_ARG;
+
+ SLANG_AST_BUILDER_RAII(getASTBuilder());
+
+ // Attempting to create a "composite" of just one component type should
+ // just return the component type itself, to avoid redundant work.
+ //
+ if (componentTypeCount == 1)
+ {
+ auto componentType = componentTypes[0];
+ componentType->addRef();
+ *outCompositeComponentType = componentType;
+ return SLANG_OK;
+ }
+
+ DiagnosticSink sink(getSourceManager(), Lexer::sourceLocationLexer);
+ applySettingsToDiagnosticSink(&sink, &sink, m_optionSet);
+
+ List<RefPtr<ComponentType>> childComponents;
+ for (Int cc = 0; cc < componentTypeCount; ++cc)
+ {
+ childComponents.add(asInternal(componentTypes[cc]));
+ }
+
+ RefPtr<ComponentType> composite = CompositeComponentType::create(this, childComponents);
+
+ sink.getBlobIfNeeded(outDiagnostics);
+
+ *outCompositeComponentType = asExternal(composite.detach());
+ return SLANG_OK;
+}
+
+SLANG_NO_THROW slang::TypeReflection* SLANG_MCALL Linkage::specializeType(
+ slang::TypeReflection* inUnspecializedType,
+ slang::SpecializationArg const* specializationArgs,
+ SlangInt specializationArgCount,
+ ISlangBlob** outDiagnostics)
+{
+ SLANG_AST_BUILDER_RAII(getASTBuilder());
+
+ auto unspecializedType = asInternal(inUnspecializedType);
+
+ List<Type*> typeArgs;
+
+ for (Int ii = 0; ii < specializationArgCount; ++ii)
+ {
+ auto& arg = specializationArgs[ii];
+ if (arg.kind != slang::SpecializationArg::Kind::Type)
+ return nullptr;
+
+ typeArgs.add(asInternal(arg.type));
+ }
+
+ DiagnosticSink sink(getSourceManager(), Lexer::sourceLocationLexer);
+ auto specializedType =
+ specializeType(unspecializedType, typeArgs.getCount(), typeArgs.getBuffer(), &sink);
+ sink.getBlobIfNeeded(outDiagnostics);
+
+ return asExternal(specializedType);
+}
+
+DeclRef<GenericDecl> getGenericParentDeclRef(
+ ASTBuilder* astBuilder,
+ SemanticsVisitor* visitor,
+ DeclRef<Decl> declRef)
+{
+ // Create substituted parent decl ref.
+ auto decl = declRef.getDecl();
+
+ while (decl && !as<GenericDecl>(decl))
+ {
+ decl = decl->parentDecl;
+ }
+
+ if (!decl)
+ {
+ // No generic parent
+ return DeclRef<GenericDecl>();
+ }
+
+ auto genericDecl = as<GenericDecl>(decl);
+ auto genericDeclRef =
+ createDefaultSubstitutionsIfNeeded(astBuilder, visitor, DeclRef(genericDecl))
+ .as<GenericDecl>();
+ return substituteDeclRef(SubstitutionSet(declRef), astBuilder, genericDeclRef)
+ .as<GenericDecl>();
+}
+
+bool Linkage::isSpecialized(DeclRef<Decl> declRef)
+{
+ // For now, we only support two 'states': fully applied or not at all.
+ // If we add support for partial specialization, we will need to update this logic.
+ //
+ // If it's not specialized, then declRef will be the one with default substitutions.
+ //
+ SemanticsVisitor visitor(getSemanticsForReflection());
+
+ auto decl = declRef.getDecl();
+ while (decl && !as<GenericDecl>(decl))
+ {
+ decl = decl->parentDecl;
+ }
+
+ if (!decl)
+ return true; // no generics => always specialized
+
+ auto defaultArgs = getDefaultSubstitutionArgs(getASTBuilder(), &visitor, as<GenericDecl>(decl));
+ auto currentArgs =
+ SubstitutionSet(declRef).findGenericAppDeclRef(as<GenericDecl>(decl))->getArgs();
+
+ if (defaultArgs.getCount() != currentArgs.getCount()) // should really never happen.
+ return true;
+
+ for (Index i = 0; i < defaultArgs.getCount(); ++i)
+ {
+ if (defaultArgs[i] != currentArgs[i])
+ return true;
+ }
+
+ return false;
+}
+
+bool isFuncGeneric(DeclRef<Decl> declRef)
+{
+ if (auto funcDecl = as<FuncDecl>(declRef.getDecl()))
+ {
+ if (funcDecl->parentDecl && as<GenericDecl>(funcDecl->parentDecl))
+ {
+ return true;
+ }
+ }
+
+ return false;
+}
+
+DeclRef<Decl> Linkage::specializeWithArgTypes(
+ Expr* funcExpr,
+ List<Type*> argTypes,
+ DiagnosticSink* sink)
+{
+ SemanticsVisitor visitor(getSemanticsForReflection());
+ SemanticsVisitor::ExprLocalScope scope;
+ visitor = visitor.withSink(sink).withExprLocalScope(&scope);
+
+ SLANG_AST_BUILDER_RAII(getASTBuilder());
+
+ if (auto declRefFuncExpr = as<DeclRefExpr>(funcExpr))
+ {
+ if (isFuncGeneric(declRefFuncExpr->declRef) && !isSpecialized(declRefFuncExpr->declRef))
+ {
+ if (auto genericDeclRef = getGenericParentDeclRef(
+ getCurrentASTBuilder(),
+ &visitor,
+ declRefFuncExpr->declRef))
+ {
+ auto genericDeclRefExpr = getCurrentASTBuilder()->create<DeclRefExpr>();
+ genericDeclRefExpr->declRef = genericDeclRef;
+ funcExpr = genericDeclRefExpr;
+ }
+ }
+ }
+
+ List<Expr*> argExprs;
+ for (SlangInt aa = 0; aa < argTypes.getCount(); ++aa)
+ {
+ auto argType = argTypes[aa];
+
+ // Create an 'empty' expr with the given type. Ideally, the expression itself should not
+ // matter only its checked type.
+ //
+ auto argExpr = getCurrentASTBuilder()->create<VarExpr>();
+ argExpr->type = argType;
+ argExpr->type.isLeftValue = true;
+ argExprs.add(argExpr);
+ }
+
+ // Construct invoke expr.
+ auto invokeExpr = getCurrentASTBuilder()->create<InvokeExpr>();
+ invokeExpr->functionExpr = funcExpr;
+ invokeExpr->arguments = argExprs;
+
+ auto checkedInvokeExpr = visitor.CheckInvokeExprWithCheckedOperands(invokeExpr);
+
+ return as<DeclRefExpr>(as<InvokeExpr>(checkedInvokeExpr)->functionExpr)->declRef;
+}
+
+
+DeclRef<Decl> Linkage::specializeGeneric(
+ DeclRef<Decl> declRef,
+ List<Expr*> argExprs,
+ DiagnosticSink* sink)
+{
+ SLANG_AST_BUILDER_RAII(getASTBuilder());
+ SLANG_ASSERT(declRef);
+
+ SemanticsVisitor visitor(getSemanticsForReflection());
+ visitor = visitor.withSink(sink);
+
+ auto genericDeclRef = getGenericParentDeclRef(getASTBuilder(), &visitor, declRef);
+
+ DeclRefExpr* declRefExpr = getASTBuilder()->create<DeclRefExpr>();
+ declRefExpr->declRef = genericDeclRef;
+
+ GenericAppExpr* genericAppExpr = getASTBuilder()->create<GenericAppExpr>();
+ genericAppExpr->functionExpr = declRefExpr;
+ genericAppExpr->arguments = argExprs;
+
+ auto specializedDeclRef =
+ as<DeclRefExpr>(visitor.checkGenericAppWithCheckedArgs(genericAppExpr))->declRef;
+
+ return specializedDeclRef;
+}
+
+SLANG_NO_THROW slang::TypeLayoutReflection* SLANG_MCALL Linkage::getTypeLayout(
+ slang::TypeReflection* inType,
+ SlangInt targetIndex,
+ slang::LayoutRules rules,
+ ISlangBlob** outDiagnostics)
+{
+ SLANG_AST_BUILDER_RAII(getASTBuilder());
+
+ auto type = asInternal(inType);
+
+ if (targetIndex < 0 || targetIndex >= targets.getCount())
+ return nullptr;
+
+ auto target = targets[targetIndex];
+
+ // TODO: We need a way to pass through the layout rules
+ // that the user requested (e.g., constant buffers vs.
+ // structured buffer rules). Right now the API only
+ // exposes a single case, so this isn't a big deal.
+ //
+ SLANG_UNUSED(rules);
+
+ auto typeLayout = target->getTypeLayout(type, rules);
+
+ // TODO: We currently don't have a path for capturing
+ // errors that occur during layout (e.g., types that
+ // are invalid because of target-specific layout constraints).
+ //
+ SLANG_UNUSED(outDiagnostics);
+
+ return asExternal(typeLayout);
+}
+
+SLANG_NO_THROW slang::TypeReflection* SLANG_MCALL Linkage::getContainerType(
+ slang::TypeReflection* inType,
+ slang::ContainerType containerType,
+ ISlangBlob** outDiagnostics)
+{
+ SLANG_AST_BUILDER_RAII(getASTBuilder());
+
+ auto type = asInternal(inType);
+
+ Type* containerTypeReflection = nullptr;
+ ContainerTypeKey key = {inType, containerType};
+ if (!m_containerTypes.tryGetValue(key, containerTypeReflection))
+ {
+ switch (containerType)
+ {
+ case slang::ContainerType::ConstantBuffer:
+ {
+ SemanticsVisitor visitor(getSemanticsForReflection());
+ auto layoutType = getASTBuilder()->getDefaultLayoutType();
+ Type* cbType = visitor.getConstantBufferType(type, layoutType);
+ containerTypeReflection = cbType;
+ }
+ break;
+ case slang::ContainerType::ParameterBlock:
+ {
+ ParameterBlockType* pbType = getASTBuilder()->getParameterBlockType(type);
+ containerTypeReflection = pbType;
+ }
+ break;
+ case slang::ContainerType::StructuredBuffer:
+ {
+ HLSLStructuredBufferType* sbType = getASTBuilder()->getStructuredBufferType(type);
+ containerTypeReflection = sbType;
+ }
+ break;
+ case slang::ContainerType::UnsizedArray:
+ {
+ ArrayExpressionType* arrType = getASTBuilder()->getArrayType(type, nullptr);
+ containerTypeReflection = arrType;
+ }
+ break;
+ default:
+ containerTypeReflection = type;
+ break;
+ }
+
+ m_containerTypes.add(key, containerTypeReflection);
+ }
+
+ SLANG_UNUSED(outDiagnostics);
+
+ return asExternal(containerTypeReflection);
+}
+
+SLANG_NO_THROW slang::TypeReflection* SLANG_MCALL Linkage::getDynamicType()
+{
+ SLANG_AST_BUILDER_RAII(getASTBuilder());
+
+ return asExternal(getASTBuilder()->getSharedASTBuilder()->getDynamicType());
+}
+
+SLANG_NO_THROW SlangResult SLANG_MCALL
+Linkage::getTypeRTTIMangledName(slang::TypeReflection* type, ISlangBlob** outNameBlob)
+{
+ SLANG_AST_BUILDER_RAII(getASTBuilder());
+
+ auto internalType = asInternal(type);
+ if (auto declRefType = as<DeclRefType>(internalType))
+ {
+ auto name = getMangledName(m_astBuilder, declRefType->getDeclRef());
+ Slang::ComPtr<ISlangBlob> blob = Slang::StringUtil::createStringBlob(name);
+ *outNameBlob = blob.detach();
+ return SLANG_OK;
+ }
+ return SLANG_FAIL;
+}
+
+SLANG_NO_THROW SlangResult SLANG_MCALL Linkage::getTypeConformanceWitnessMangledName(
+ slang::TypeReflection* type,
+ slang::TypeReflection* interfaceType,
+ ISlangBlob** outNameBlob)
+{
+ SLANG_AST_BUILDER_RAII(getASTBuilder());
+
+ auto subType = asInternal(type);
+ auto supType = asInternal(interfaceType);
+ auto name = getMangledNameForConformanceWitness(m_astBuilder, subType, supType);
+ Slang::ComPtr<ISlangBlob> blob = Slang::StringUtil::createStringBlob(name);
+ *outNameBlob = blob.detach();
+ return SLANG_OK;
+}
+
+SLANG_NO_THROW SlangResult SLANG_MCALL Linkage::getTypeConformanceWitnessSequentialID(
+ slang::TypeReflection* type,
+ slang::TypeReflection* interfaceType,
+ uint32_t* outId)
+{
+ SLANG_AST_BUILDER_RAII(getASTBuilder());
+
+ auto subType = asInternal(type);
+ auto supType = asInternal(interfaceType);
+
+ if (!subType || !supType)
+ return SLANG_FAIL;
+
+ auto name = getMangledNameForConformanceWitness(m_astBuilder, subType, supType);
+ auto interfaceName = getMangledTypeName(m_astBuilder, supType);
+ uint32_t resultIndex = 0;
+ if (mapMangledNameToRTTIObjectIndex.tryGetValue(name, resultIndex))
+ {
+ if (outId)
+ *outId = resultIndex;
+ return SLANG_OK;
+ }
+ auto idAllocator = mapInterfaceMangledNameToSequentialIDCounters.tryGetValue(interfaceName);
+ if (!idAllocator)
+ {
+ mapInterfaceMangledNameToSequentialIDCounters[interfaceName] = 0;
+ idAllocator = mapInterfaceMangledNameToSequentialIDCounters.tryGetValue(interfaceName);
+ }
+ resultIndex = (*idAllocator);
+ ++(*idAllocator);
+ mapMangledNameToRTTIObjectIndex[name] = resultIndex;
+ if (outId)
+ *outId = resultIndex;
+ return SLANG_OK;
+}
+
+SLANG_NO_THROW SlangResult SLANG_MCALL Linkage::getDynamicObjectRTTIBytes(
+ slang::TypeReflection* type,
+ slang::TypeReflection* interfaceType,
+ uint32_t* outBuffer,
+ uint32_t bufferSize)
+{
+ // Slang RTTI header format:
+ // byte 0-7: pointer to RTTI struct describing the type. (not used for now, set to 1 for valid
+ // types, and 0 to represent null).
+ // byte 8-11: 32-bit sequential ID of the type conformance witness.
+ // byte 12-15: unused.
+
+ if (bufferSize < 16)
+ return SLANG_E_BUFFER_TOO_SMALL;
+
+ SLANG_AST_BUILDER_RAII(getASTBuilder());
+
+ SLANG_RETURN_ON_FAIL(getTypeConformanceWitnessSequentialID(type, interfaceType, outBuffer + 2));
+
+ // Make the RTTI part non zero.
+ outBuffer[0] = 1;
+
+ return SLANG_OK;
+}
+
+SLANG_NO_THROW SlangResult SLANG_MCALL Linkage::createTypeConformanceComponentType(
+ slang::TypeReflection* type,
+ slang::TypeReflection* interfaceType,
+ slang::ITypeConformance** outConformanceComponentType,
+ SlangInt conformanceIdOverride,
+ ISlangBlob** outDiagnostics)
+{
+ if (outConformanceComponentType == nullptr)
+ return SLANG_E_INVALID_ARG;
+
+ SLANG_AST_BUILDER_RAII(getASTBuilder());
+
+ RefPtr<TypeConformance> result;
+ DiagnosticSink sink;
+ applySettingsToDiagnosticSink(&sink, &sink, m_optionSet);
+
+ try
+ {
+ SemanticsVisitor visitor(getSemanticsForReflection());
+ visitor = visitor.withSink(&sink);
+
+ auto witness = visitor.isSubtype(
+ (Slang::Type*)type,
+ (Slang::Type*)interfaceType,
+ IsSubTypeOptions::None);
+ if (auto subtypeWitness = as<SubtypeWitness>(witness))
+ {
+ result = new TypeConformance(this, subtypeWitness, conformanceIdOverride, &sink);
+ }
+ }
+ catch (...)
+ {
+ }
+ sink.getBlobIfNeeded(outDiagnostics);
+ bool success = (result != nullptr);
+ *outConformanceComponentType = result.detach();
+ return success ? SLANG_OK : SLANG_FAIL;
+}
+
+SLANG_NO_THROW SlangResult SLANG_MCALL
+Linkage::createCompileRequest(SlangCompileRequest** outCompileRequest)
+{
+ auto compileRequest = new EndToEndCompileRequest(this);
+ compileRequest->addRef();
+ *outCompileRequest = asExternal(compileRequest);
+ return SLANG_OK;
+}
+
+SLANG_NO_THROW SlangInt SLANG_MCALL Linkage::getLoadedModuleCount()
+{
+ return loadedModulesList.getCount();
+}
+
+SLANG_NO_THROW slang::IModule* SLANG_MCALL Linkage::getLoadedModule(SlangInt index)
+{
+ if (index >= 0 && index < loadedModulesList.getCount())
+ return loadedModulesList[index].get();
+ return nullptr;
+}
+
+void Linkage::buildHash(DigestBuilder<SHA1>& builder, SlangInt targetIndex)
+{
+ // Add the Slang compiler version to the hash
+ auto version = String(getBuildTagString());
+ builder.append(version);
+
+ // Add compiler options, including search path, preprocessor includes, etc.
+ m_optionSet.buildHash(builder);
+
+ auto addTargetDigest = [&](TargetRequest* targetReq)
+ {
+ targetReq->getOptionSet().buildHash(builder);
+
+ const PassThroughMode passThroughMode =
+ getDownstreamCompilerRequiredForTarget(targetReq->getTarget());
+ const SourceLanguage sourceLanguage =
+ getDefaultSourceLanguageForDownstreamCompiler(passThroughMode);
+
+ // Add prelude for the given downstream compiler.
+ ComPtr<ISlangBlob> prelude;
+ getGlobalSession()->getLanguagePrelude(
+ (SlangSourceLanguage)sourceLanguage,
+ prelude.writeRef());
+ if (prelude)
+ {
+ builder.append(prelude);
+ }
+
+ // TODO: Downstream compilers (specifically dxc) can currently #include additional
+ // dependencies. This is currently the case for NVAPI headers included in the prelude. These
+ // dependencies are currently not picked up by the shader cache which is a significant
+ // issue. This can only be fixed by running the preprocessor in the slang compiler so dxc
+ // (or any other downstream compiler for that matter) isn't resolving any includes
+ // implicitly.
+
+ // Add the downstream compiler version (if it exists) to the hash
+ auto downstreamCompiler =
+ getSessionImpl()->getOrLoadDownstreamCompiler(passThroughMode, nullptr);
+ if (downstreamCompiler)
+ {
+ ComPtr<ISlangBlob> versionString;
+ if (SLANG_SUCCEEDED(downstreamCompiler->getVersionString(versionString.writeRef())))
+ {
+ builder.append(versionString);
+ }
+ }
+ };
+
+ // Add the target specified by targetIndex
+ if (targetIndex == -1)
+ {
+ // -1 means all targets.
+ for (auto targetReq : targets)
+ {
+ addTargetDigest(targetReq);
+ }
+ }
+ else
+ {
+ auto targetReq = targets[targetIndex];
+ addTargetDigest(targetReq);
+ }
+}
+
+SlangResult Linkage::addSearchPath(char const* path)
+{
+ m_optionSet.add(CompilerOptionName::Include, String(path));
+ return SLANG_OK;
+}
+
+SlangResult Linkage::addPreprocessorDefine(char const* name, char const* value)
+{
+ CompilerOptionValue val;
+ val.kind = CompilerOptionValueKind::String;
+ val.stringValue = name;
+ val.stringValue2 = value;
+ m_optionSet.add(CompilerOptionName::MacroDefine, val);
+ return SLANG_OK;
+}
+
+SlangResult Linkage::setMatrixLayoutMode(SlangMatrixLayoutMode mode)
+{
+ m_optionSet.setMatrixLayoutMode((MatrixLayoutMode)mode);
+ return SLANG_OK;
+}
+
+SlangResult Linkage::loadFile(String const& path, PathInfo& outPathInfo, ISlangBlob** outBlob)
+{
+ outPathInfo.type = PathInfo::Type::Unknown;
+
+ SLANG_RETURN_ON_FAIL(m_fileSystemExt->loadFile(path.getBuffer(), outBlob));
+
+ ComPtr<ISlangBlob> uniqueIdentity;
+ // Get the unique identity
+ if (SLANG_FAILED(
+ m_fileSystemExt->getFileUniqueIdentity(path.getBuffer(), uniqueIdentity.writeRef())))
+ {
+ // We didn't get a unique identity, so go with just a found path
+ outPathInfo.type = PathInfo::Type::FoundPath;
+ outPathInfo.foundPath = path;
+ }
+ else
+ {
+ outPathInfo = PathInfo::makeNormal(path, StringUtil::getString(uniqueIdentity));
+ }
+ return SLANG_OK;
+}
+
+Expr* Linkage::parseTermString(String typeStr, Scope* scope)
+{
+ // Create a SourceManager on the stack, so any allocations for 'SourceFile'/'SourceView' etc
+ // will be cleaned up
+ SourceManager localSourceManager;
+ localSourceManager.initialize(getSourceManager(), nullptr);
+
+ Slang::SourceFile* srcFile =
+ localSourceManager.createSourceFileWithString(PathInfo::makeTypeParse(), typeStr);
+
+ // We'll use a temporary diagnostic sink
+ DiagnosticSink sink(&localSourceManager, nullptr);
+
+ // RAII type to make make sure current SourceManager is restored after parse.
+ // Use RAII - to make sure everything is reset even if an exception is thrown.
+ struct ScopeReplaceSourceManager
+ {
+ ScopeReplaceSourceManager(Linkage* linkage, SourceManager* replaceManager)
+ : m_linkage(linkage), m_originalSourceManager(linkage->getSourceManager())
+ {
+ linkage->setSourceManager(replaceManager);
+ }
+
+ ~ScopeReplaceSourceManager() { m_linkage->setSourceManager(m_originalSourceManager); }
+
+ private:
+ Linkage* m_linkage;
+ SourceManager* m_originalSourceManager;
+ };
+
+ // We need to temporarily replace the SourceManager for this CompileRequest
+ ScopeReplaceSourceManager scopeReplaceSourceManager(this, &localSourceManager);
+
+ SourceLanguage sourceLanguage = SourceLanguage::Slang;
+ SlangLanguageVersion languageVersion = m_optionSet.getLanguageVersion();
+
+ auto tokens = preprocessSource(
+ srcFile,
+ &sink,
+ nullptr,
+ Dictionary<String, String>(),
+ this,
+ sourceLanguage,
+ languageVersion);
+
+ if (sourceLanguage == SourceLanguage::Unknown)
+ sourceLanguage = SourceLanguage::Slang;
+
+ return parseTermFromSourceFile(
+ getASTBuilder(),
+ tokens,
+ &sink,
+ scope,
+ getNamePool(),
+ sourceLanguage);
+}
+
+UInt Linkage::addTarget(CodeGenTarget target)
+{
+ RefPtr<TargetRequest> targetReq = new TargetRequest(this, target);
+
+ Index result = targets.getCount();
+ targets.add(targetReq);
+ return UInt(result);
+}
+
+void Linkage::loadParsedModule(
+ RefPtr<FrontEndCompileRequest> compileRequest,
+ RefPtr<TranslationUnitRequest> translationUnit,
+ Name* name,
+ const PathInfo& pathInfo)
+{
+ // Note: we add the loaded module to our name->module listing
+ // before doing semantic checking, so that if it tries to
+ // recursively `import` itself, we can detect it.
+ //
+ RefPtr<Module> loadedModule = translationUnit->getModule();
+
+ // Get a path
+ String mostUniqueIdentity = pathInfo.getMostUniqueIdentity();
+ SLANG_ASSERT(mostUniqueIdentity.getLength() > 0);
+
+ mapPathToLoadedModule.add(mostUniqueIdentity, loadedModule);
+ mapNameToLoadedModules.add(name, loadedModule);
+
+ auto sink = translationUnit->compileRequest->getSink();
+
+ int errorCountBefore = sink->getErrorCount();
+ int errorCountAfter;
+ try
+ {
+ compileRequest->checkAllTranslationUnits();
+ }
+ catch (...)
+ {
+ mapPathToLoadedModule.remove(mostUniqueIdentity);
+ mapNameToLoadedModules.remove(name);
+ throw;
+ }
+ errorCountAfter = sink->getErrorCount();
+ if (isInLanguageServer())
+ {
+ // Don't generate IR as language server.
+ // This means that we currently cannot report errors that are detected during IR passes.
+ // Ideally we want to run those passes, but that is too risky for what it is worth right
+ // now.
+ }
+ else
+ {
+ if (errorCountAfter != errorCountBefore)
+ {
+ // There must have been an error in the loaded module.
+ // Remove from maps if there were errors during semantic checking
+ mapPathToLoadedModule.remove(mostUniqueIdentity);
+ mapNameToLoadedModules.remove(name);
+ }
+ else
+ {
+ // If we didn't run into any errors, then try to generate
+ // IR code for the imported module.
+ if (errorCountAfter == 0)
+ {
+ loadedModule->setIRModule(
+ generateIRForTranslationUnit(getASTBuilder(), translationUnit));
+ }
+ }
+ }
+ loadedModulesList.add(loadedModule);
+}
+
+RefPtr<Module> Linkage::findOrLoadSerializedModuleForModuleLibrary(
+ ISlangBlob* blobHoldingSerializedData,
+ ModuleChunk const* moduleChunk,
+ RIFF::ListChunk const* libraryChunk,
+ DiagnosticSink* sink)
+{
+ RefPtr<Module> resultModule;
+
+ // We will attempt things in a few different steps, trying to
+ // decode as little of the serialized module as necessary at
+ // each step, so that we don't waste time on the heavyweight
+ // stuff when we didn't need to.
+ //
+ // The first step is to simply decode the module name, and
+ // see if we have a already loaded a matching module.
+
+ auto moduleName = getNamePool()->getName(moduleChunk->getName());
+ if (mapNameToLoadedModules.tryGetValue(moduleName, resultModule))
+ return resultModule;
+
+ // It is possible that the module has been loaded, but somehow
+ // under a different name, so next we decode the list of file
+ // paths that the module depends on, and then rely on the assumption
+ // that the first of those paths represents the file for the module
+ // itself to detect if we've already loaded a module from that
+ // path.
+ //
+ // Note: While this is a distasteful assumption to make, it is
+ // one that gets made in several parts of the compiler codebase
+ // already. It isn't something that can be fixed in just one
+ // place at this point.
+
+ auto fileDependenciesList = moduleChunk->getFileDependencies();
+ auto firstFileDependencyChunk = fileDependenciesList.getFirst();
+ if (!firstFileDependencyChunk)
+ return nullptr;
+
+ auto modulePathInfo = PathInfo::makePath(firstFileDependencyChunk->getValue());
+ if (mapPathToLoadedModule.tryGetValue(modulePathInfo.getMostUniqueIdentity(), resultModule))
+ return resultModule;
+
+ // If we failed to find a previously-loaded module, then we
+ // will go ahead and load the module from the serialized form.
+ //
+ PathInfo filePathInfo;
+ return loadSerializedModule(
+ moduleName,
+ modulePathInfo,
+ blobHoldingSerializedData,
+ moduleChunk,
+ libraryChunk,
+ SourceLoc(),
+ sink);
+}
+
+RefPtr<Module> Linkage::loadSerializedModule(
+ Name* moduleName,
+ const PathInfo& moduleFilePathInfo,
+ ISlangBlob* blobHoldingSerializedData,
+ ModuleChunk const* moduleChunk,
+ RIFF::ListChunk const* containerChunk,
+ SourceLoc const& requestingLoc,
+ DiagnosticSink* sink)
+{
+ auto astBuilder = getASTBuilder();
+ SLANG_AST_BUILDER_RAII(astBuilder);
+
+ auto module = RefPtr(new Module(this, astBuilder));
+ module->setName(moduleName);
+
+ // Just as if we were processing an `import` declaration in
+ // source code, we will track the fact that this serialized
+ // modlue is (effectively) being imported, so that we can
+ // diagnose anything troublesome, like an attempt at a
+ // recursive import.
+ //
+ ModuleBeingImportedRAII moduleBeingImported(this, module, moduleName, requestingLoc);
+
+ // We will register the module in our data structures to
+ // track loaded modules, and then remove it in the case
+ // where there is some kind of failure.
+ //
+ String mostUniqueIdentity = moduleFilePathInfo.getMostUniqueIdentity();
+ SLANG_ASSERT(mostUniqueIdentity.getLength() > 0);
+
+ mapPathToLoadedModule.add(mostUniqueIdentity, module);
+ mapNameToLoadedModules.add(moduleName, module);
+ try
+ {
+ if (SLANG_FAILED(loadSerializedModuleContents(
+ module,
+ moduleFilePathInfo,
+ blobHoldingSerializedData,
+ moduleChunk,
+ containerChunk,
+ sink)))
+ {
+ mapPathToLoadedModule.remove(mostUniqueIdentity);
+ mapNameToLoadedModules.remove(moduleName);
+ return nullptr;
+ }
+
+ loadedModulesList.add(module);
+ return module;
+ }
+ catch (...)
+ {
+ mapPathToLoadedModule.remove(mostUniqueIdentity);
+ mapNameToLoadedModules.remove(moduleName);
+ throw;
+ }
+}
+
+RefPtr<Module> Linkage::loadBinaryModuleImpl(
+ Name* moduleName,
+ const PathInfo& moduleFilePathInfo,
+ ISlangBlob* moduleFileContents,
+ SourceLoc const& requestingLoc,
+ DiagnosticSink* sink)
+{
+ auto astBuilder = getASTBuilder();
+ SLANG_AST_BUILDER_RAII(astBuilder);
+
+ // We start by reading the content of the file as
+ // an in-memory RIFF container.
+ //
+ auto rootChunk = RIFF::RootChunk::getFromBlob(moduleFileContents);
+ if (!rootChunk)
+ {
+ return nullptr;
+ }
+
+ auto moduleChunk = ModuleChunk::find(rootChunk);
+ if (!moduleChunk)
+ {
+ return nullptr;
+ }
+
+ // Next, we attempt to check if the binary module is up to
+ // date with the compilation options in use as well as
+ // the contents of all the files its compilation depended
+ // on (as determined by its hash).
+ //
+ String mostUniqueIdentity = moduleFilePathInfo.getMostUniqueIdentity();
+ SLANG_ASSERT(mostUniqueIdentity.getLength() > 0);
+ if (m_optionSet.getBoolOption(CompilerOptionName::UseUpToDateBinaryModule))
+ {
+ if (!isBinaryModuleUpToDate(moduleFilePathInfo.foundPath, moduleChunk))
+ {
+ return nullptr;
+ }
+ }
+
+ // If everything seems reasonable, then we will go ahead and load
+ // the module more completely from that serialized representation.
+ //
+ RefPtr<Module> module = loadSerializedModule(
+ moduleName,
+ moduleFilePathInfo,
+ moduleFileContents,
+ moduleChunk,
+ rootChunk,
+ requestingLoc,
+ sink);
+
+ return module;
+}
+
+void Linkage::_diagnoseErrorInImportedModule(DiagnosticSink* sink)
+{
+ for (auto info = m_modulesBeingImported; info; info = info->next)
+ {
+ sink->diagnose(info->importLoc, Diagnostics::errorInImportedModule, info->name);
+ }
+ if (!isInLanguageServer())
+ {
+ sink->diagnose(SourceLoc(), Diagnostics::complationCeased);
+ }
+}
+
+RefPtr<Module> Linkage::loadModuleImpl(
+ Name* moduleName,
+ const PathInfo& modulePathInfo,
+ ISlangBlob* moduleBlob,
+ SourceLoc const& requestingLoc,
+ DiagnosticSink* sink,
+ const LoadedModuleDictionary* additionalLoadedModules,
+ ModuleBlobType blobType)
+{
+ switch (blobType)
+ {
+ case ModuleBlobType::IR:
+ return loadBinaryModuleImpl(moduleName, modulePathInfo, moduleBlob, requestingLoc, sink);
+
+ case ModuleBlobType::Source:
+ return loadSourceModuleImpl(
+ moduleName,
+ modulePathInfo,
+ moduleBlob,
+ requestingLoc,
+ sink,
+ additionalLoadedModules);
+
+ default:
+ SLANG_UNEXPECTED("unknown module blob type");
+ UNREACHABLE_RETURN(nullptr);
+ }
+}
+
+RefPtr<Module> Linkage::loadSourceModuleImpl(
+ Name* name,
+ const PathInfo& filePathInfo,
+ ISlangBlob* sourceBlob,
+ SourceLoc const& srcLoc,
+ DiagnosticSink* sink,
+ const LoadedModuleDictionary* additionalLoadedModules)
+{
+ RefPtr<FrontEndCompileRequest> frontEndReq = new FrontEndCompileRequest(this, nullptr, sink);
+
+ frontEndReq->additionalLoadedModules = additionalLoadedModules;
+
+ RefPtr<TranslationUnitRequest> translationUnit = new TranslationUnitRequest(frontEndReq);
+ translationUnit->compileRequest = frontEndReq;
+ translationUnit->setModuleName(name);
+ Stage impliedStage;
+ translationUnit->sourceLanguage = SourceLanguage::Slang;
+
+ // If we are loading from a file with apparaent glsl extension,
+ // set the source language to GLSL to enable GLSL compatibility mode.
+ if ((SourceLanguage)findSourceLanguageFromPath(filePathInfo.getName(), impliedStage) ==
+ SourceLanguage::GLSL)
+ {
+ translationUnit->sourceLanguage = SourceLanguage::GLSL;
+ }
+
+ frontEndReq->addTranslationUnit(translationUnit);
+
+ auto module = translationUnit->getModule();
+
+ ModuleBeingImportedRAII moduleBeingImported(this, module, name, srcLoc);
+
+ // Create an artifact for the source
+ auto sourceArtifact = ArtifactUtil::createArtifact(
+ ArtifactDesc::make(ArtifactKind::Source, ArtifactPayload::Slang, ArtifactStyle::Unknown));
+
+ if (sourceBlob)
+ {
+ // If the user has already provided a source blob, use that.
+ sourceArtifact->addRepresentation(
+ new SourceBlobWithPathInfoArtifactRepresentation(filePathInfo, sourceBlob));
+ }
+ else if (
+ filePathInfo.type == PathInfo::Type::Normal ||
+ filePathInfo.type == PathInfo::Type::FoundPath)
+ {
+ // Create with the 'friendly' name
+ // We create that it was loaded from the file system
+ sourceArtifact->addRepresentation(new ExtFileArtifactRepresentation(
+ filePathInfo.foundPath.getUnownedSlice(),
+ getFileSystemExt()));
+ }
+ else
+ {
+ return nullptr;
+ }
+
+ translationUnit->addSourceArtifact(sourceArtifact);
+
+ if (SLANG_FAILED(translationUnit->requireSourceFiles()))
+ {
+ // Some problem accessing source files
+ return nullptr;
+ }
+ int errorCountBefore = sink->getErrorCount();
+ frontEndReq->parseTranslationUnit(translationUnit);
+ int errorCountAfter = sink->getErrorCount();
+
+ if (errorCountAfter != errorCountBefore && !isInLanguageServer())
+ {
+ _diagnoseErrorInImportedModule(sink);
+ // Something went wrong during the parsing, so we should bail out.
+ return nullptr;
+ }
+
+ try
+ {
+ loadParsedModule(frontEndReq, translationUnit, name, filePathInfo);
+ }
+ catch (const Slang::AbortCompilationException&)
+ {
+ // Something is fatally wrong, we should return nullptr.
+ module = nullptr;
+ }
+ errorCountAfter = sink->getErrorCount();
+
+ if (errorCountAfter != errorCountBefore && !isInLanguageServer())
+ {
+ // If something is fatally wrong, we want to report
+ // the diagnostic even if we are in language server
+ // and processing a different module.
+ _diagnoseErrorInImportedModule(sink);
+ // Something went wrong during the parsing, so we should bail out.
+ return nullptr;
+ }
+
+ if (!module)
+ return nullptr;
+
+ module->setPathInfo(filePathInfo);
+ return module;
+}
+
+bool Linkage::isBeingImported(Module* module)
+{
+ for (auto ii = m_modulesBeingImported; ii; ii = ii->next)
+ {
+ if (module == ii->module)
+ return true;
+ }
+ return false;
+}
+
+// Derive a file name for the module, by taking the given
+// identifier, replacing all occurrences of `_` with `-`,
+// and then appending `.slang`.
+//
+// For example, `foo_bar` becomes `foo-bar.slang`.
+String getFileNameFromModuleName(Name* name, bool translateUnderScore)
+{
+ String fileName;
+ if (!getText(name).getUnownedSlice().endsWithCaseInsensitive(".slang"))
+ {
+ StringBuilder sb;
+ for (auto c : getText(name))
+ {
+ if (translateUnderScore && c == '_')
+ c = '-';
+
+ sb.append(c);
+ }
+ sb.append(".slang");
+ fileName = sb.produceString();
+ }
+ else
+ {
+ fileName = getText(name);
+ }
+ return fileName;
+}
+
+RefPtr<Module> Linkage::findOrImportModule(
+ Name* moduleName,
+ SourceLoc const& requestingLoc,
+ DiagnosticSink* sink,
+ const LoadedModuleDictionary* loadedModules)
+{
+ // Have we already loaded a module matching this name?
+ //
+ RefPtr<LoadedModule> previouslyLoadedModule;
+ if (mapNameToLoadedModules.tryGetValue(moduleName, previouslyLoadedModule))
+ {
+ // If the map shows a null module having been loaded,
+ // then that means there was a prior load attempt,
+ // but it failed, so we won't bother trying again.
+ //
+ if (!previouslyLoadedModule)
+ return nullptr;
+
+ // If state shows us that the module is already being
+ // imported deeper on the call stack, then we've
+ // hit a recursive case, and that is an error.
+ //
+ if (isBeingImported(previouslyLoadedModule))
+ {
+ // We seem to be in the middle of loading this module
+ sink->diagnose(requestingLoc, Diagnostics::recursiveModuleImport, moduleName);
+ return nullptr;
+ }
+
+ return previouslyLoadedModule;
+ }
+
+ // If the user is providing an additional list of loaded modules, we find
+ // if the module being imported is in that list. This allows a translation
+ // unit to use previously checked translation units in the same
+ // FrontEndCompileRequest.
+ {
+ Module* previouslyLoadedLocalModule = nullptr;
+ if (loadedModules && loadedModules->tryGetValue(moduleName, previouslyLoadedLocalModule))
+ {
+ return previouslyLoadedLocalModule;
+ }
+ }
+
+ // If the name being requested matches the name of a built-in module,
+ // then we will special-case the process by loading that builtin
+ // module directly.
+ //
+ // TODO: right now this logic is only considering the built-in `glsl`
+ // module, but it should probably be generalized so that we can more
+ // easily support having multiple built-in modules rather than just
+ // putting everything into `core`.
+ //
+ if (moduleName == getSessionImpl()->glslModuleName)
+ {
+ // This is a builtin glsl module, just load it from embedded definition.
+ auto glslModule = getSessionImpl()->getBuiltinModule(slang::BuiltinModuleName::GLSL);
+ if (!glslModule)
+ {
+ // Note: the way this logic is currently written, if the built-in
+ // `glsl` module fails to load, then we will *not* fall back to
+ // searching for a user-defined module in a file like `glsl.slang`.
+ //
+ // It is unclear if this should be the default behavior or not.
+ // Should built-in modules be prioritized over user modules?
+ // Should built-in modules shadow user modules, even when the
+ // built-in module fails to load, for some reason?
+ //
+ sink->diagnose(requestingLoc, Diagnostics::glslModuleNotAvailable, moduleName);
+ }
+ return glslModule;
+ }
+
+ // We are going to use a loop to search for a suitable file to
+ // load the module from, to account for a few key choices:
+ //
+ // * We can both load modules from a source `.slang` file,
+ // or from a binary `.slang-module` file.
+ //
+ // * For a variety of reasons, the `import` logic has historically
+ // translated underscores in a module name into dashes (so that
+ // `import my_module` will look for `my-module.slang`), and we
+ // try to support both that convention as well as a convention
+ // that preserves underscores.
+ //
+ // To try to keep this logic as orthogonal as possible, we first
+ // construct lists of the options we want to iterate over, and
+ // then do the actual loop later.
+
+ ShortList<ModuleBlobType, 2> typesToTry;
+ if (isInLanguageServer())
+ {
+ // When in language server, we always prefer to use source module if it is available.
+ typesToTry.add(ModuleBlobType::Source);
+ typesToTry.add(ModuleBlobType::IR);
+ }
+ else
+ {
+ // Look for a precompiled module first, if not exist, load from source.
+ typesToTry.add(ModuleBlobType::IR);
+ typesToTry.add(ModuleBlobType::Source);
+ }
+
+ // We will always search for a file name that directly matches the
+ // module name as written first, and then search for one with
+ // underscores replaced by dashes. The latter is the original
+ // behavior that `import` provided, but it seems safest to prefer
+ // the exact name spelled in the user's code when there might
+ // actually be ambiguity.
+ //
+ auto defaultSourceFileName = getFileNameFromModuleName(moduleName, false);
+ auto alternativeSourceFileName = getFileNameFromModuleName(moduleName, true);
+ String sourceFileNamesToTry[] = {defaultSourceFileName, alternativeSourceFileName};
+
+ // We are going to look for the candidate file using the same
+ // logic that would be used for a preprocessor `#include`,
+ // so we set up the necessary state.
+ //
+ IncludeSystem includeSystem(&getSearchDirectories(), getFileSystemExt(), getSourceManager());
+
+ // Just like with a `#include`, the search will take into
+ // account the path to the file where the request to import
+ // this module came from (e.g. the source file with the
+ // `import` declaration), if such a path is available.
+ //
+ PathInfo requestingPathInfo =
+ getSourceManager()->getPathInfo(requestingLoc, SourceLocType::Actual);
+
+ for (auto type : typesToTry)
+ {
+ for (auto sourceFileName : sourceFileNamesToTry)
+ {
+ // The `sourceFileName` will have the `.slang` extension,
+ // so if we are looking for a binary module, we need
+ // to change the extension we will look for.
+ //
+ String fileName;
+ switch (type)
+ {
+ case ModuleBlobType::Source:
+ fileName = sourceFileName;
+ break;
+
+ case ModuleBlobType::IR:
+ fileName = Path::replaceExt(sourceFileName, "slang-module");
+ break;
+ }
+
+ // We now search for a file matching the desired name,
+ // using the same logic as for a `#include`.
+ //
+ // TODO: We might want to consider how to handle the case
+ // of an `import` with a relative path a little specially,
+ // since it could in theory be possible for two `.slang`
+ // files with the same base name to exist in different
+ // directories in a project, and we'd want file-relative
+ // `import`s to work for each, without having either one
+ // be able to "claim" the bare identifier of the base
+ // name for itself.
+ //
+ PathInfo filePathInfo;
+ if (SLANG_FAILED(
+ includeSystem.findFile(fileName, requestingPathInfo.foundPath, filePathInfo)))
+ {
+ // If we failed to find the file at this step, we
+ // will continue the search for our other options.
+ //
+ continue;
+ }
+
+ // We will *again* search for a previously loaded module.
+ //
+ // It is possible that the same file will have been loaded
+ // as a module under two different module names. The easiest
+ // way for this to happen is if there are `import` declarations
+ // using both the underscore and dash conventions (e.g., both
+ // `import "my-module.slang"` and `import my_module`).
+ //
+ // This case may also arise if one file `import`s a module using
+ // just an identifier for its name, but another `import`s it
+ // using a path (e.g., `import "subdir/file.slang"`).
+ //
+ // No matter how the situation arises, we only want to have one
+ // copy of the "same" module loaded at a given time, so we
+ // will re-use the existing module if we find one here.
+ //
+ if (mapPathToLoadedModule.tryGetValue(
+ filePathInfo.getMostUniqueIdentity(),
+ previouslyLoadedModule))
+ {
+ // TODO: If we find a previously-loaded module at this step,
+ // then we should probably register that module under the
+ // given `moduleName` in the map of loaded modules, so
+ // that subsequent `import`s using the same form will find it.
+ //
+ return previouslyLoadedModule;
+ }
+
+ // Now we try to load the content of the file.
+ //
+ // If for some reason we could find a file at the
+ // given path, but for some reason couldn't *open*
+ // and *read* it, then we continue the search
+ // using whatever other candidate file names are left.
+ //
+ ComPtr<ISlangBlob> fileContents;
+ if (SLANG_FAILED(includeSystem.loadFile(filePathInfo, fileContents)))
+ {
+ continue;
+ }
+
+ // If we found a real file and were able to load its contents,
+ // then we'll go ahead and try to load a module from it,
+ // whether by compiling it or decoding the binary.
+ //
+ auto module = loadModuleImpl(
+ moduleName,
+ filePathInfo,
+ fileContents,
+ requestingLoc,
+ sink,
+ loadedModules,
+ type);
+
+ // If the attempt to load the module from the given path
+ // was successful, we go ahead and use it, without trying
+ // out any other options.
+ //
+ if (module)
+ return module;
+ }
+ }
+
+ // If we tried out all of our candidate file names
+ // and failed with each of them, then we diagnose
+ // an error based on the original *source* file
+ // name.
+ //
+ // TODO: this should really be an error message
+ // that clearly states something like "no file
+ // suitable for module `whatever` was found
+ // and loaded.
+ //
+ // Ideally that error message would include whatever
+ // of the candidate file names from the loop above
+ // got furthest along in the process (or just a
+ // list of the file names that were tried, if
+ // nothing was even found via the include system).
+ //
+ sink->diagnose(requestingLoc, Diagnostics::cannotOpenFile, defaultSourceFileName);
+
+ // If the attempt to import the module failed, then
+ // we will stick a null pointer into the map of loaded
+ // modules, so that subsequent attempts to load a module
+ // with this name will return null without having to
+ // go through all the above steps yet again.
+ //
+ mapNameToLoadedModules[moduleName] = nullptr;
+ return nullptr;
+}
+
+SourceFile* Linkage::loadSourceFile(String pathFrom, String path)
+{
+ IncludeSystem includeSystem(&getSearchDirectories(), getFileSystemExt(), getSourceManager());
+ ComPtr<slang::IBlob> blob;
+ PathInfo pathInfo;
+ SLANG_RETURN_NULL_ON_FAIL(includeSystem.findFile(path, pathFrom, pathInfo));
+ SourceFile* sourceFile = nullptr;
+ SLANG_RETURN_NULL_ON_FAIL(includeSystem.loadFile(pathInfo, blob, sourceFile));
+ return sourceFile;
+}
+
+// Check if a serialized module is up-to-date with current compiler options and source files.
+bool Linkage::isBinaryModuleUpToDate(String fromPath, RIFF::ListChunk const* baseChunk)
+{
+ auto moduleChunk = ModuleChunk::find(baseChunk);
+ if (!moduleChunk)
+ return false;
+
+ SHA1::Digest existingDigest = moduleChunk->getDigest();
+
+ DigestBuilder<SHA1> digestBuilder;
+ auto version = String(getBuildTagString());
+ digestBuilder.append(version);
+ m_optionSet.buildHash(digestBuilder);
+
+ // Find the canonical path of the directory containing the module source file.
+ String moduleSrcPath = "";
+
+ auto dependencyChunks = moduleChunk->getFileDependencies();
+ if (auto firstDependencyChunk = dependencyChunks.getFirst())
+ {
+ moduleSrcPath = firstDependencyChunk->getValue();
+
+ IncludeSystem includeSystem(
+ &getSearchDirectories(),
+ getFileSystemExt(),
+ getSourceManager());
+ PathInfo modulePathInfo;
+ if (SLANG_SUCCEEDED(includeSystem.findFile(moduleSrcPath, fromPath, modulePathInfo)))
+ {
+ moduleSrcPath = modulePathInfo.foundPath;
+ Path::getCanonical(moduleSrcPath, moduleSrcPath);
+ }
+ }
+
+ for (auto dependencyChunk : dependencyChunks)
+ {
+ auto file = dependencyChunk->getValue();
+ auto sourceFile = loadSourceFile(fromPath, file);
+ if (!sourceFile)
+ {
+ // If we cannot find the source file from `fromPath`,
+ // try again from the module's source file path.
+ if (dependencyChunks.getFirst())
+ sourceFile = loadSourceFile(moduleSrcPath, file);
+ }
+ if (!sourceFile)
+ return false;
+ digestBuilder.append(sourceFile->getDigest());
+ }
+ return digestBuilder.finalize() == existingDigest;
+}
+
+SLANG_NO_THROW bool SLANG_MCALL
+Linkage::isBinaryModuleUpToDate(const char* modulePath, slang::IBlob* binaryModuleBlob)
+{
+ auto rootChunk = RIFF::RootChunk::getFromBlob(binaryModuleBlob);
+ if (!rootChunk)
+ return false;
+ return isBinaryModuleUpToDate(modulePath, rootChunk);
+}
+
+SourceFile* Linkage::findFile(Name* name, SourceLoc loc, IncludeSystem& outIncludeSystem)
+{
+ auto impl = [&](bool translateUnderScore) -> SourceFile*
+ {
+ auto fileName = getFileNameFromModuleName(name, translateUnderScore);
+
+ // Next, try to find the file of the given name,
+ // using our ordinary include-handling logic.
+
+ auto& searchDirs = getSearchDirectories();
+ outIncludeSystem = IncludeSystem(&searchDirs, getFileSystemExt(), getSourceManager());
+
+ // Get the original path info
+ PathInfo pathIncludedFromInfo = getSourceManager()->getPathInfo(loc, SourceLocType::Actual);
+ PathInfo filePathInfo;
+
+ ComPtr<ISlangBlob> fileContents;
+
+ // We have to load via the found path - as that is how file was originally loaded
+ if (SLANG_FAILED(
+ outIncludeSystem.findFile(fileName, pathIncludedFromInfo.foundPath, filePathInfo)))
+ {
+ return nullptr;
+ }
+ // Otherwise, try to load it.
+ SourceFile* sourceFile;
+ if (SLANG_FAILED(outIncludeSystem.loadFile(filePathInfo, fileContents, sourceFile)))
+ {
+ return nullptr;
+ }
+ return sourceFile;
+ };
+ if (auto rs = impl(false))
+ return rs;
+ return impl(true);
+}
+
+Linkage::IncludeResult Linkage::findAndIncludeFile(
+ Module* module,
+ TranslationUnitRequest* translationUnit,
+ Name* name,
+ SourceLoc const& loc,
+ DiagnosticSink* sink)
+{
+ IncludeResult result;
+ result.fileDecl = nullptr;
+ result.isNew = false;
+
+ IncludeSystem includeSystem;
+ auto sourceFile = findFile(name, loc, includeSystem);
+ if (!sourceFile)
+ {
+ sink->diagnose(loc, Diagnostics::cannotOpenFile, getText(name));
+ return result;
+ }
+
+ // If the file has already been included, don't need to do anything further.
+ if (auto existingFileDecl = module->getIncludedSourceFileMap().tryGetValue(sourceFile))
+ {
+ result.fileDecl = *existingFileDecl;
+ result.isNew = false;
+ return result;
+ }
+
+ if (isInLanguageServer())
+ {
+ // HACK: When in language server mode, we will always load the currently opend file as a
+ // fresh module even if some previously opened file already references the current file via
+ // `import` or `include`. see comments in `WorkspaceVersion::getOrLoadModule()` for the
+ // reason behind this. An undesired outcome of this decision is that we could endup
+ // including the currently opened file itself via chain of `__include`s because the
+ // currently opened file will not have a true unique file system identity that allows it to
+ // be deduplicated correct. Therefore we insert a hack logic here to detect re-inclusion by
+ // just the file path. We can clean up this hack by making the language server truly support
+ // incremental checking so we can reuse the previously loaded module instead of needing to
+ // always start with a fresh copy.
+ //
+ for (auto file : translationUnit->getSourceFiles())
+ {
+ if (file->getPathInfo().hasFoundPath() &&
+ Path::equals(file->getPathInfo().foundPath, sourceFile->getPathInfo().foundPath))
+ return result;
+ }
+ }
+
+ module->addFileDependency(sourceFile);
+
+ // Create a transparent FileDecl to hold all children from the included file.
+ auto fileDecl = module->getASTBuilder()->create<FileDecl>();
+ fileDecl->nameAndLoc.name = name;
+ fileDecl->parentDecl = module->getModuleDecl();
+ module->getIncludedSourceFileMap().add(sourceFile, fileDecl);
+
+ FrontEndPreprocessorHandler preprocessorHandler(
+ module,
+ module->getASTBuilder(),
+ sink,
+ translationUnit);
+ auto combinedPreprocessorDefinitions = translationUnit->getCombinedPreprocessorDefinitions();
+ SourceLanguage sourceLanguage = translationUnit->sourceLanguage;
+ SlangLanguageVersion slangLanguageVersion = module->getModuleDecl()->languageVersion;
+ auto tokens = preprocessSource(
+ sourceFile,
+ sink,
+ &includeSystem,
+ combinedPreprocessorDefinitions,
+ this,
+ sourceLanguage,
+ slangLanguageVersion,
+ &preprocessorHandler);
+
+ if (sourceLanguage == SourceLanguage::Unknown)
+ sourceLanguage = translationUnit->sourceLanguage;
+
+ if (slangLanguageVersion != module->getModuleDecl()->languageVersion)
+ {
+ sink->diagnose(
+ tokens.begin()->getLoc(),
+ Diagnostics::languageVersionDiffersFromIncludingModule);
+ }
+
+ auto outerScope = module->getModuleDecl()->ownedScope;
+ parseSourceFile(
+ module->getASTBuilder(),
+ translationUnit,
+ sourceLanguage,
+ tokens,
+ sink,
+ outerScope,
+ fileDecl);
+
+ module->getModuleDecl()->addMember(fileDecl);
+
+ result.fileDecl = fileDecl;
+ result.isNew = true;
+ return result;
+}
+
+void Linkage::setFileSystem(ISlangFileSystem* inFileSystem)
+{
+ // Set the fileSystem
+ m_fileSystem = inFileSystem;
+
+ // Release what's there
+ m_fileSystemExt.setNull();
+
+ // If nullptr passed in set up default
+ if (inFileSystem == nullptr)
+ {
+ m_fileSystemExt = new Slang::CacheFileSystem(Slang::OSFileSystem::getExtSingleton());
+ }
+ else
+ {
+ if (auto cacheFileSystem = as<CacheFileSystem>(inFileSystem))
+ {
+ m_fileSystemExt = cacheFileSystem;
+ }
+ else
+ {
+ if (m_requireCacheFileSystem)
+ {
+ m_fileSystemExt = new Slang::CacheFileSystem(inFileSystem);
+ }
+ else
+ {
+ // See if we have the full ISlangFileSystemExt interface, if we do just use it
+ inFileSystem->queryInterface(SLANG_IID_PPV_ARGS(m_fileSystemExt.writeRef()));
+
+ // If not wrap with CacheFileSystem that emulates ISlangFileSystemExt from the
+ // ISlangFileSystem interface
+ if (!m_fileSystemExt)
+ {
+ // Construct a wrapper to emulate the extended interface behavior
+ m_fileSystemExt = new Slang::CacheFileSystem(m_fileSystem);
+ }
+ }
+ }
+ }
+
+ // If requires a cache file system, check that it does have one
+ SLANG_ASSERT(m_requireCacheFileSystem == false || as<CacheFileSystem>(m_fileSystemExt));
+
+ // Set the file system used on the source manager
+ getSourceManager()->setFileSystemExt(m_fileSystemExt);
+}
+
+SlangResult Linkage::loadSerializedModuleContents(
+ Module* module,
+ const PathInfo& moduleFilePathInfo,
+ ISlangBlob* blobHoldingSerializedData,
+ ModuleChunk const* moduleChunk,
+ RIFF::ListChunk const* containerChunk,
+ DiagnosticSink* sink)
+{
+ // At this point we've dealt with basically all of
+ // the formalities, and we just need to get down
+ // to the real work of decoding the information
+ // in the `moduleChunk`.
+
+ //
+ // TODO(tfoley): The fact that a separate `containerChunk` is getting
+ // passed in here is entirely byproduct of the support for "module libraries"
+ // that can (in principle) contain multiple serialized modules. When
+ // things are serialized in the "container" representation used for
+ // a module library, there is a single `DebugChunk` as a child of
+ // the container, with all of the `ModuleChunk`s sharing that debug info.
+ //
+ // In contrast, the more typical kind of serialized module that the compiler
+ // produces serializes a single `ModuleChunk`, and the `DebugChunk` is
+ // one of its direct children. Thus there are currently two different
+ // locations where debug information might be found.
+ //
+ // Prior to the change where we navigate the serialized RIFF hierarchy
+ // in memory without copying it, this issue was addressed by having
+ // the subroutine that looked for a `DebugChunk` start at the `ModuleChunk`
+ // and work its way up through the hierarchy using parent pointers that
+ // were created as part of RIFF loading. When navigating the RIFF in-place
+ // we don't have such parent pointers.
+ //
+ // As a short-term solution, we should deprecate and remove the support
+ // for "module libraries" so that the code doesn't have to handle two
+ // different layouts.
+ //
+ // In the longer term, we should be making some conscious design decisions
+ // around how we want to organize the top-level structure of our serialized
+ // intermediate/output formats, since there's quite a mix of different
+ // approaches currently in use.
+ //
+
+ auto sourceManager = getSourceManager();
+ RefPtr<SerialSourceLocReader> sourceLocReader;
+ if (auto debugChunk = DebugChunk::find(moduleChunk, containerChunk))
+ {
+ SLANG_RETURN_ON_FAIL(
+ readSourceLocationsFromDebugChunk(debugChunk, sourceManager, sourceLocReader));
+ }
+
+ auto astChunk = moduleChunk->findAST();
+ if (!astChunk)
+ return SLANG_FAIL;
+
+ auto irChunk = moduleChunk->findIR();
+ if (!irChunk)
+ return SLANG_FAIL;
+
+ auto astBuilder = getASTBuilder();
+ auto session = getSessionImpl();
+
+ // For the purposes of any modules referenced
+ // by the module we're about to decode, we will
+ // construct a source location that represents
+ // the module itself (if possible).
+ //
+ // TODO(tfoley): This logic seems like overkill, given
+ // that many (most? all?) control-flow paths that can
+ // reach this routine will have already found a `SourceFile`
+ // to represent the module, as part of even getting the
+ // `moduleFilePathInfo` to pass in
+ //
+ // The approach here is more or less exactly copied
+ // from what the old `SerialContainerUtil::read` function
+ // used to do, with the hopes that it will as many tests
+ // passing as possible.
+ //
+ // Down the line somebody should scrutinize all of this
+ // kind of logic in the compiler codebase, because there
+ // is something that feels unclean about how paths are being handled.
+ //
+ SourceLoc serializedModuleLoc;
+ {
+ auto sourceFile =
+ sourceManager->findSourceFileByPathRecursively(moduleFilePathInfo.foundPath);
+ if (!sourceFile)
+ {
+ sourceFile = sourceManager->createSourceFileWithString(moduleFilePathInfo, String());
+ sourceManager->addSourceFile(moduleFilePathInfo.getMostUniqueIdentity(), sourceFile);
+ }
+ auto sourceView =
+ sourceManager->createSourceView(sourceFile, &moduleFilePathInfo, SourceLoc());
+ serializedModuleLoc = sourceView->getRange().begin;
+ }
+
+ auto moduleDecl = readSerializedModuleAST(
+ this,
+ astBuilder,
+ sink,
+ blobHoldingSerializedData,
+ astChunk,
+ sourceLocReader,
+ serializedModuleLoc);
+ if (!moduleDecl)
+ return SLANG_FAIL;
+ module->setModuleDecl(moduleDecl);
+
+ RefPtr<IRModule> irModule;
+ SLANG_RETURN_ON_FAIL(readSerializedModuleIR(irChunk, session, sourceLocReader, irModule));
+ module->setIRModule(irModule);
+
+ // The handling of file dependencies is complicated, because of
+ // the way that the encoding logic tried to make all of the
+ // paths be relative to the primary source file for the module.
+ //
+ // We end up needing to undo some amount of that work here.
+ //
+
+ module->clearFileDependency();
+ String moduleSourcePath = moduleFilePathInfo.foundPath;
+ bool isFirst = true;
+ for (auto depenencyFileChunk : moduleChunk->getFileDependencies())
+ {
+ auto encodedDependencyFilePath = depenencyFileChunk->getValue();
+
+ auto sourceFile = loadSourceFile(moduleFilePathInfo.foundPath, encodedDependencyFilePath);
+ if (isFirst)
+ {
+ // The first file is the source for the main module file.
+ // We store the module path as the basis for finding the remaining
+ // dependent files.
+ if (sourceFile)
+ moduleSourcePath = sourceFile->getPathInfo().foundPath;
+ isFirst = false;
+ }
+ // If we cannot find the dependent file directly, try to find
+ // it relative to the module source path.
+ if (!sourceFile)
+ {
+ sourceFile = loadSourceFile(moduleSourcePath, encodedDependencyFilePath);
+ }
+ if (sourceFile)
+ {
+ module->addFileDependency(sourceFile);
+ }
+ }
+ module->setPathInfo(moduleFilePathInfo);
+ module->setDigest(moduleChunk->getDigest());
+ module->_collectShaderParams();
+ module->_discoverEntryPoints(sink, targets);
+
+ // Hook up fileDecl's scope to module's scope.
+ for (auto fileDecl : moduleDecl->getDirectMemberDeclsOfType<FileDecl>())
+ {
+ addSiblingScopeForContainerDecl(m_astBuilder, moduleDecl->ownedScope, fileDecl);
+ }
+
+ return SLANG_OK;
+}
+
+void Linkage::setRequireCacheFileSystem(bool requireCacheFileSystem)
+{
+ if (requireCacheFileSystem == m_requireCacheFileSystem)
+ {
+ return;
+ }
+
+ ComPtr<ISlangFileSystem> scopeFileSystem(m_fileSystem);
+ m_requireCacheFileSystem = requireCacheFileSystem;
+
+ setFileSystem(scopeFileSystem);
+}
+
+RefPtr<Module> findOrImportModule(
+ Linkage* linkage,
+ Name* name,
+ SourceLoc const& loc,
+ DiagnosticSink* sink,
+ const LoadedModuleDictionary* loadedModules)
+{
+ return linkage->findOrImportModule(name, loc, sink, loadedModules);
+}
+
+Type* checkProperType(Linkage* linkage, TypeExp typeExp, DiagnosticSink* sink);
+
+
+} // namespace Slang
diff --git a/source/slang/slang-session.h b/source/slang/slang-session.h
new file mode 100644
index 000000000..5b3405d60
--- /dev/null
+++ b/source/slang/slang-session.h
@@ -0,0 +1,476 @@
+// slang-session.h
+#pragma once
+
+//
+// This file declares the `Linkage` class, which implements
+// the `slang::ISession` interface from the public API.
+//
+// TODO: there is an unfortunate and confusing situation
+// where the public Slang API `ISession` type is implemented
+// by the internal `Linkage` class, while the internal
+// `Session` class implements the `IGlobalSession` interface
+// from the public API.
+//
+
+#include "../compiler-core/slang-artifact.h"
+#include "../compiler-core/slang-command-line-args.h"
+#include "../compiler-core/slang-include-system.h"
+#include "../compiler-core/slang-name.h"
+#include "../core/slang-riff.h"
+#include "../core/slang-smart-pointer.h"
+#include "slang-ast-base.h"
+#include "slang-compiler-fwd.h"
+#include "slang-compiler-options.h"
+#include "slang-content-assist-info.h"
+#include "slang-global-session.h"
+
+#include <slang.h>
+
+namespace Slang
+{
+
+/// A dictionary of modules to be considered when resolving `import`s,
+/// beyond those that would normally be found through a `Linkage`.
+///
+/// Checking of an `import` declaration will bottleneck through
+/// `Linkage::findOrImportModule`, which would usually just check for
+/// any module that had been previously loaded into the same `Linkage`
+/// (e.g., by a call to `Linkage::loadModule()`).
+///
+/// In the case where compilation is being done through an
+/// explicit `FrontEndCompileRequest` or `EndToEndCompileRequest`,
+/// the modules being compiled by that request do not get added to
+/// the surrounding `Linkage`.
+///
+/// There is a corner case when an explicit compile request has
+/// multiple `TranslationUnitRequest`s, because the user (reasonably)
+/// expects that if they compile `A.slang` and `B.slang` as two
+/// distinct translation units in the same compile request, then
+/// an `import B` inside of `A.slang` should resolve to reference
+/// the code of `B.slang`. But because neither `A` nor `B` gets
+/// added to the `Linkage`, and the `Linkage` is what usually
+/// determines what is or isn't loaded, that intuition will
+/// be wrong, without a bit of help.
+///
+/// The `LoadedModuleDictionary` is thus filled in by a
+/// `FrontEndCompileRequest` to collect the modules it is compiling,
+/// so that they can cross-reference one another (albeit with
+/// a current implementation restriction that modules in the
+/// request can only `import` those earlier in the request...).
+///
+/// The dictionary then gets passed around between nearly all of
+/// the operations that deal with loading modules, to make sure
+/// that they can detect a previously loaded module.
+///
+typedef Dictionary<Name*, Module*> LoadedModuleDictionary;
+
+enum class ModuleBlobType
+{
+ Source,
+ IR
+};
+
+struct ContainerTypeKey
+{
+ slang::TypeReflection* elementType;
+ slang::ContainerType containerType;
+ bool operator==(ContainerTypeKey other) const
+ {
+ return elementType == other.elementType && containerType == other.containerType;
+ }
+ Slang::HashCode getHashCode() const
+ {
+ return Slang::combineHash(
+ Slang::getHashCode(elementType),
+ Slang::getHashCode(containerType));
+ }
+};
+
+/// A context for loading and re-using code modules.
+class Linkage : public RefObject, public slang::ISession
+{
+public:
+ SLANG_REF_OBJECT_IUNKNOWN_ALL
+
+ CompilerOptionSet m_optionSet;
+
+ ISlangUnknown* getInterface(const Guid& guid);
+
+ SLANG_NO_THROW slang::IGlobalSession* SLANG_MCALL getGlobalSession() override;
+ SLANG_NO_THROW slang::IModule* SLANG_MCALL
+ loadModule(const char* moduleName, slang::IBlob** outDiagnostics = nullptr) override;
+ slang::IModule* loadModuleFromBlob(
+ const char* moduleName,
+ const char* path,
+ slang::IBlob* source,
+ ModuleBlobType blobType,
+ slang::IBlob** outDiagnostics = nullptr);
+ SLANG_NO_THROW slang::IModule* SLANG_MCALL loadModuleFromIRBlob(
+ const char* moduleName,
+ const char* path,
+ slang::IBlob* source,
+ slang::IBlob** outDiagnostics = nullptr) override;
+ SLANG_NO_THROW SlangResult SLANG_MCALL loadModuleInfoFromIRBlob(
+ slang::IBlob* source,
+ SlangInt& outModuleVersion,
+ const char*& outModuleCompilerVersion,
+ const char*& outModuleName) override;
+ SLANG_NO_THROW slang::IModule* SLANG_MCALL loadModuleFromSource(
+ const char* moduleName,
+ const char* path,
+ slang::IBlob* source,
+ slang::IBlob** outDiagnostics = nullptr) override;
+ SLANG_NO_THROW slang::IModule* SLANG_MCALL loadModuleFromSourceString(
+ const char* moduleName,
+ const char* path,
+ const char* string,
+ slang::IBlob** outDiagnostics = nullptr) override;
+ SLANG_NO_THROW SlangResult SLANG_MCALL createCompositeComponentType(
+ slang::IComponentType* const* componentTypes,
+ SlangInt componentTypeCount,
+ slang::IComponentType** outCompositeComponentType,
+ ISlangBlob** outDiagnostics = nullptr) override;
+ SLANG_NO_THROW slang::TypeReflection* SLANG_MCALL specializeType(
+ slang::TypeReflection* type,
+ slang::SpecializationArg const* specializationArgs,
+ SlangInt specializationArgCount,
+ ISlangBlob** outDiagnostics = nullptr) override;
+ SLANG_NO_THROW slang::TypeLayoutReflection* SLANG_MCALL getTypeLayout(
+ slang::TypeReflection* type,
+ SlangInt targetIndex = 0,
+ slang::LayoutRules rules = slang::LayoutRules::Default,
+ ISlangBlob** outDiagnostics = nullptr) override;
+ SLANG_NO_THROW slang::TypeReflection* SLANG_MCALL getContainerType(
+ slang::TypeReflection* elementType,
+ slang::ContainerType containerType,
+ ISlangBlob** outDiagnostics = nullptr) override;
+ SLANG_NO_THROW slang::TypeReflection* SLANG_MCALL getDynamicType() override;
+ SLANG_NO_THROW SlangResult SLANG_MCALL
+ getTypeRTTIMangledName(slang::TypeReflection* type, ISlangBlob** outNameBlob) override;
+ SLANG_NO_THROW SlangResult SLANG_MCALL getTypeConformanceWitnessMangledName(
+ slang::TypeReflection* type,
+ slang::TypeReflection* interfaceType,
+ ISlangBlob** outNameBlob) override;
+ SLANG_NO_THROW SlangResult SLANG_MCALL getTypeConformanceWitnessSequentialID(
+ slang::TypeReflection* type,
+ slang::TypeReflection* interfaceType,
+ uint32_t* outId) override;
+ SLANG_NO_THROW SlangResult SLANG_MCALL getDynamicObjectRTTIBytes(
+ slang::TypeReflection* type,
+ slang::TypeReflection* interfaceType,
+ uint32_t* outBytes,
+ uint32_t bufferSize) override;
+ SLANG_NO_THROW SlangResult SLANG_MCALL createTypeConformanceComponentType(
+ slang::TypeReflection* type,
+ slang::TypeReflection* interfaceType,
+ slang::ITypeConformance** outConformance,
+ SlangInt conformanceIdOverride,
+ ISlangBlob** outDiagnostics) override;
+ SLANG_NO_THROW SlangResult SLANG_MCALL
+ createCompileRequest(SlangCompileRequest** outCompileRequest) override;
+ virtual SLANG_NO_THROW SlangInt SLANG_MCALL getLoadedModuleCount() override;
+ virtual SLANG_NO_THROW slang::IModule* SLANG_MCALL getLoadedModule(SlangInt index) override;
+ virtual SLANG_NO_THROW bool SLANG_MCALL
+ isBinaryModuleUpToDate(const char* modulePath, slang::IBlob* binaryModuleBlob) override;
+
+ // Updates the supplied builder with linkage-related information, which includes preprocessor
+ // defines, the compiler version, and other compiler options. This is then merged with the hash
+ // produced for the program to produce a key that can be used with the shader cache.
+ void buildHash(DigestBuilder<SHA1>& builder, SlangInt targetIndex = -1);
+
+ void addTarget(slang::TargetDesc const& desc);
+ SlangResult addSearchPath(char const* path);
+ SlangResult addPreprocessorDefine(char const* name, char const* value);
+ SlangResult setMatrixLayoutMode(SlangMatrixLayoutMode mode);
+ /// Create an initially-empty linkage
+ Linkage(Session* session, ASTBuilder* astBuilder, Linkage* builtinLinkage);
+
+ /// Dtor
+ ~Linkage();
+
+ bool isInLanguageServer()
+ {
+ return contentAssistInfo.checkingMode != ContentAssistCheckingMode::None;
+ }
+
+ /// Get the parent session for this linkage
+ Session* getSessionImpl() { return m_session; }
+
+ // Information on the targets we are being asked to
+ // generate code for.
+ List<RefPtr<TargetRequest>> targets;
+
+ // Directories to search for `#include` files or `import`ed modules
+ SearchDirectoryList& getSearchDirectories();
+
+ // Source manager to help track files loaded
+ SourceManager m_defaultSourceManager;
+ SourceManager* m_sourceManager = nullptr;
+ RefPtr<CommandLineContext> m_cmdLineContext;
+
+ // Used to store strings returned by the api as const char*
+ StringSlicePool m_stringSlicePool;
+
+ // Name pool for looking up names
+ NamePool* namePool = nullptr;
+
+ NamePool* getNamePool() { return namePool; }
+
+ ASTBuilder* getASTBuilder() { return m_astBuilder; }
+
+ RefPtr<ASTBuilder> m_astBuilder;
+
+ // Cache for container types.
+ Dictionary<ContainerTypeKey, Type*> m_containerTypes;
+
+ // cache used by type checking, implemented in check.cpp
+ TypeCheckingCache* getTypeCheckingCache();
+ void destroyTypeCheckingCache();
+
+ RefPtr<RefObject> m_typeCheckingCache = nullptr;
+
+ // Modules that have been dynamically loaded via `import`
+ //
+ // This is a list of unique modules loaded, in the order they were encountered.
+ List<RefPtr<LoadedModule>> loadedModulesList;
+
+ // Map from the path (or uniqueIdentity if available) of a module file to its definition
+ Dictionary<String, RefPtr<LoadedModule>> mapPathToLoadedModule;
+
+ // Map from the logical name of a module to its definition
+ Dictionary<Name*, RefPtr<LoadedModule>> mapNameToLoadedModules;
+
+ // Map from the mangled name of RTTI objects to sequential IDs
+ // used by `switch`-based dynamic dispatch.
+ Dictionary<String, uint32_t> mapMangledNameToRTTIObjectIndex;
+
+ // Counters for allocating sequential IDs to witness tables conforming to each interface type.
+ Dictionary<String, uint32_t> mapInterfaceMangledNameToSequentialIDCounters;
+
+ SearchDirectoryList searchDirectoryCache;
+
+ // The resulting specialized IR module for each entry point request
+ List<RefPtr<IRModule>> compiledModules;
+
+ ContentAssistInfo contentAssistInfo;
+
+ /// File system implementation to use when loading files from disk.
+ ///
+ /// If this member is `null`, a default implementation that tries
+ /// to use the native OS filesystem will be used instead.
+ ///
+ ComPtr<ISlangFileSystem> m_fileSystem;
+
+ /// The extended file system implementation. Will be set to a default implementation
+ /// if fileSystem is nullptr. Otherwise it will either be fileSystem's interface,
+ /// or a wrapped impl that makes fileSystem operate as fileSystemExt
+ ComPtr<ISlangFileSystemExt> m_fileSystemExt;
+
+ /// Get the currenly set file system
+ ISlangFileSystemExt* getFileSystemExt() { return m_fileSystemExt; }
+
+ /// Load a file into memory using the configured file system.
+ ///
+ /// @param path The path to attempt to load from
+ /// @param outBlob A destination pointer to receive the loaded blob
+ /// @returns A `SlangResult` to indicate success or failure.
+ ///
+ SlangResult loadFile(String const& path, PathInfo& outPathInfo, ISlangBlob** outBlob);
+
+ Expr* parseTermString(String str, Scope* scope);
+
+ Type* specializeType(
+ Type* unspecializedType,
+ Int argCount,
+ Type* const* args,
+ DiagnosticSink* sink);
+
+ /// Add a new target and return its index.
+ UInt addTarget(CodeGenTarget target);
+
+ /// "Bottleneck" routine for loading a module.
+ ///
+ /// All attempts to load a module, whether through
+ /// Slang API calls, `import` operations, or other
+ /// means, should bottleneck through `loadModuleImpl`,
+ /// or one of the specialized cases `loadSourceModuleImpl`
+ /// and `loadBinaryModuleImpl`.
+ ///
+ RefPtr<Module> loadModuleImpl(
+ Name* name,
+ const PathInfo& filePathInfo,
+ ISlangBlob* fileContentsBlob,
+ SourceLoc const& loc,
+ DiagnosticSink* sink,
+ const LoadedModuleDictionary* additionalLoadedModules,
+ ModuleBlobType blobType);
+
+ RefPtr<Module> loadSourceModuleImpl(
+ Name* name,
+ const PathInfo& filePathInfo,
+ ISlangBlob* fileContentsBlob,
+ SourceLoc const& loc,
+ DiagnosticSink* sink,
+ const LoadedModuleDictionary* additionalLoadedModules);
+
+ RefPtr<Module> loadBinaryModuleImpl(
+ Name* name,
+ const PathInfo& filePathInfo,
+ ISlangBlob* fileContentsBlob,
+ SourceLoc const& loc,
+ DiagnosticSink* sink);
+
+ /// Either finds a previously-loaded module matching what
+ /// was serialized into `moduleChunk`, or else attempts
+ /// to load the serialized module.
+ ///
+ /// If a previously-loaded module is found that matches the
+ /// name or path information in `moduleChunk`, then that
+ /// previously-loaded module is returned.
+ ///
+ /// Othwerise, attempts to load a module from `moduleChunk`
+ /// and, if successful, returns the freshly loaded module.
+ ///
+ /// Otherwise, return null.
+ ///
+ RefPtr<Module> findOrLoadSerializedModuleForModuleLibrary(
+ ISlangBlob* blobHoldingSerializedData,
+ ModuleChunk const* moduleChunk,
+ RIFF::ListChunk const* libraryChunk,
+ DiagnosticSink* sink);
+
+ RefPtr<Module> loadSerializedModule(
+ Name* moduleName,
+ const PathInfo& moduleFilePathInfo,
+ ISlangBlob* blobHoldingSerializedData,
+ ModuleChunk const* moduleChunk,
+ RIFF::ListChunk const* containerChunk, //< The outer container, if there is one.
+ SourceLoc const& requestingLoc,
+ DiagnosticSink* sink);
+
+ SlangResult loadSerializedModuleContents(
+ Module* module,
+ const PathInfo& moduleFilePathInfo,
+ ISlangBlob* blobHoldingSerializedData,
+ ModuleChunk const* moduleChunk,
+ RIFF::ListChunk const* containerChunk, //< The outer container, if there is one.
+ DiagnosticSink* sink);
+
+ SourceFile* loadSourceFile(String pathFrom, String path);
+
+ void loadParsedModule(
+ RefPtr<FrontEndCompileRequest> compileRequest,
+ RefPtr<TranslationUnitRequest> translationUnit,
+ Name* name,
+ PathInfo const& pathInfo);
+
+ bool isBinaryModuleUpToDate(String fromPath, RIFF::ListChunk const* baseChunk);
+
+ RefPtr<Module> findOrImportModule(
+ Name* name,
+ SourceLoc const& loc,
+ DiagnosticSink* sink,
+ const LoadedModuleDictionary* loadedModules = nullptr);
+
+ SourceFile* findFile(Name* name, SourceLoc loc, IncludeSystem& outIncludeSystem);
+ struct IncludeResult
+ {
+ FileDecl* fileDecl;
+ bool isNew;
+ };
+ IncludeResult findAndIncludeFile(
+ Module* module,
+ TranslationUnitRequest* translationUnit,
+ Name* name,
+ SourceLoc const& loc,
+ DiagnosticSink* sink);
+
+ SourceManager* getSourceManager() { return m_sourceManager; }
+
+ /// Override the source manager for the linkage.
+ ///
+ /// This is only used to install a temporary override when
+ /// parsing stuff from strings (where we don't want to retain
+ /// full source files for the parsed result).
+ ///
+ /// TODO: We should remove the need for this hack.
+ ///
+ void setSourceManager(SourceManager* sourceManager) { m_sourceManager = sourceManager; }
+
+ void setRequireCacheFileSystem(bool requireCacheFileSystem);
+
+ void setFileSystem(ISlangFileSystem* fileSystem);
+
+ DeclRef<Decl> specializeGeneric(
+ DeclRef<Decl> declRef,
+ List<Expr*> argExprs,
+ DiagnosticSink* sink);
+
+ DeclRef<Decl> specializeWithArgTypes(
+ Expr* funcExpr,
+ List<Type*> argTypes,
+ DiagnosticSink* sink);
+
+ bool isSpecialized(DeclRef<Decl> declRef);
+
+ DiagnosticSink::Flags diagnosticSinkFlags = 0;
+
+ bool m_requireCacheFileSystem = false;
+
+ // Modules that have been read in with the -r option
+ List<ComPtr<IArtifact>> m_libModules;
+
+ void _stopRetainingParentSession() { m_retainedSession = nullptr; }
+
+ // Get shared semantics information for reflection purposes.
+ SharedSemanticsContext* getSemanticsForReflection();
+
+private:
+ /// The global Slang library session that this linkage is a child of
+ Session* m_session = nullptr;
+
+ RefPtr<Session> m_retainedSession;
+
+ /// Tracks state of modules currently being loaded.
+ ///
+ /// This information is used to diagnose cases where
+ /// a user tries to recursively import the same module
+ /// (possibly along a transitive chain of `import`s).
+ ///
+ struct ModuleBeingImportedRAII
+ {
+ public:
+ ModuleBeingImportedRAII(
+ Linkage* linkage,
+ Module* module,
+ Name* name,
+ SourceLoc const& importLoc)
+ : linkage(linkage), module(module), name(name), importLoc(importLoc)
+ {
+ next = linkage->m_modulesBeingImported;
+ linkage->m_modulesBeingImported = this;
+ }
+
+ ~ModuleBeingImportedRAII() { linkage->m_modulesBeingImported = next; }
+
+ Linkage* linkage;
+ Module* module;
+ Name* name;
+ SourceLoc importLoc;
+ ModuleBeingImportedRAII* next;
+ };
+
+ // Any modules currently being imported will be listed here
+ ModuleBeingImportedRAII* m_modulesBeingImported = nullptr;
+
+ /// Is the given module in the middle of being imported?
+ bool isBeingImported(Module* module);
+
+ /// Diagnose that an error occured in the process of importing a module
+ void _diagnoseErrorInImportedModule(DiagnosticSink* sink);
+
+ List<Type*> m_specializedTypes;
+
+ RefPtr<SharedSemanticsContext> m_semanticsForReflection;
+};
+} // namespace Slang
diff --git a/source/slang/slang-target-program.cpp b/source/slang/slang-target-program.cpp
new file mode 100644
index 000000000..ffb859b55
--- /dev/null
+++ b/source/slang/slang-target-program.cpp
@@ -0,0 +1,113 @@
+// slang-target-program.cpp
+#include "slang-target-program.h"
+
+#include "slang-compiler.h"
+#include "slang-type-layout.h"
+
+namespace Slang
+{
+
+//
+// TargetProgram
+//
+
+TargetProgram::TargetProgram(ComponentType* componentType, TargetRequest* targetReq)
+ : m_program(componentType), m_targetReq(targetReq)
+{
+ m_entryPointResults.setCount(componentType->getEntryPointCount());
+ m_optionSet.overrideWith(m_program->getOptionSet());
+ m_optionSet.inheritFrom(targetReq->getOptionSet());
+}
+
+IArtifact* TargetProgram::_createWholeProgramResult(
+ DiagnosticSink* sink,
+ EndToEndCompileRequest* endToEndReq)
+{
+ // We want to call `emitEntryPoints` function to generate code that contains
+ // all the entrypoints defined in `m_program`.
+ // The current logic of `emitEntryPoints` takes a list of entry-point indices to
+ // emit code for, so we construct such a list first.
+ List<Int> entryPointIndices;
+
+ m_entryPointResults.setCount(m_program->getEntryPointCount());
+ entryPointIndices.setCount(m_program->getEntryPointCount());
+ for (Index i = 0; i < entryPointIndices.getCount(); i++)
+ entryPointIndices[i] = i;
+
+ CodeGenContext::Shared sharedCodeGenContext(this, entryPointIndices, sink, endToEndReq);
+ CodeGenContext codeGenContext(&sharedCodeGenContext);
+
+ if (SLANG_FAILED(codeGenContext.emitEntryPoints(m_wholeProgramResult)))
+ {
+ return nullptr;
+ }
+
+ return m_wholeProgramResult;
+}
+
+IArtifact* TargetProgram::_createEntryPointResult(
+ Int entryPointIndex,
+ DiagnosticSink* sink,
+ EndToEndCompileRequest* endToEndReq)
+{
+ // It is possible that entry points got added to the `Program`
+ // *after* we created this `TargetProgram`, so there might be
+ // a request for an entry point that we didn't allocate space for.
+ //
+ // TODO: Change the construction logic so that a `Program` is
+ // constructed all at once rather than incrementally, to avoid
+ // this problem.
+ //
+ if (entryPointIndex >= m_entryPointResults.getCount())
+ m_entryPointResults.setCount(entryPointIndex + 1);
+
+
+ CodeGenContext::EntryPointIndices entryPointIndices;
+ entryPointIndices.add(entryPointIndex);
+
+ CodeGenContext::Shared sharedCodeGenContext(this, entryPointIndices, sink, endToEndReq);
+ CodeGenContext codeGenContext(&sharedCodeGenContext);
+
+ codeGenContext.emitEntryPoints(m_entryPointResults[entryPointIndex]);
+
+ return m_entryPointResults[entryPointIndex];
+}
+
+IArtifact* TargetProgram::getOrCreateWholeProgramResult(DiagnosticSink* sink)
+{
+ if (m_wholeProgramResult)
+ return m_wholeProgramResult;
+
+ // If we haven't yet computed a layout for this target
+ // program, we need to make sure that is done before
+ // code generation.
+ //
+ if (!getOrCreateIRModuleForLayout(sink))
+ {
+ return nullptr;
+ }
+
+ return _createWholeProgramResult(sink);
+}
+
+IArtifact* TargetProgram::getOrCreateEntryPointResult(Int entryPointIndex, DiagnosticSink* sink)
+{
+ if (entryPointIndex >= m_entryPointResults.getCount())
+ m_entryPointResults.setCount(entryPointIndex + 1);
+
+ if (IArtifact* artifact = m_entryPointResults[entryPointIndex])
+ return artifact;
+
+ // If we haven't yet computed a layout for this target
+ // program, we need to make sure that is done before
+ // code generation.
+ //
+ if (!getOrCreateIRModuleForLayout(sink))
+ {
+ return nullptr;
+ }
+
+ return _createEntryPointResult(entryPointIndex, sink);
+}
+
+} // namespace Slang
diff --git a/source/slang/slang-target-program.h b/source/slang/slang-target-program.h
new file mode 100644
index 000000000..57a9c46fb
--- /dev/null
+++ b/source/slang/slang-target-program.h
@@ -0,0 +1,143 @@
+// slang-target-program.h
+#pragma once
+
+//
+// This file declares the `TargetProgram` class, which is
+// primarily used to cache generated target code for a
+// linked program/binary and/or its entry points.
+//
+
+#include "../core/slang-smart-pointer.h"
+#include "slang-hlsl-to-vulkan-layout-options.h"
+#include "slang-ir.h"
+#include "slang-linkable.h"
+#include "slang-target.h"
+
+namespace Slang
+{
+
+/// A `TargetProgram` represents a `ComponentType` specialized for a particular `TargetRequest`
+///
+/// TODO: This should probably be renamed to `TargetComponentType`.
+///
+/// By binding a component type to a specific target, a `TargetProgram` allows
+/// for things like layout to be computed, that fundamentally depend on
+/// the choice of target.
+///
+/// A `TargetProgram` handles request for compiled kernel code for
+/// entry point functions. In practice, kernel code can only be
+/// correctly generated when the underlying `ComponentType` is "fully linked"
+/// (has no remaining unsatisfied requirements).
+///
+class TargetProgram : public RefObject
+{
+public:
+ TargetProgram(ComponentType* componentType, TargetRequest* targetReq);
+
+ /// Get the underlying program
+ ComponentType* getProgram() { return m_program; }
+
+ /// Get the underlying target
+ TargetRequest* getTargetReq() { return m_targetReq; }
+
+ /// Get the layout for the program on the target.
+ ///
+ /// If this is the first time the layout has been
+ /// requested, report any errors that arise during
+ /// layout to the given `sink`.
+ ///
+ ProgramLayout* getOrCreateLayout(DiagnosticSink* sink);
+
+ /// Get the layout for the program on the target.
+ ///
+ /// This routine assumes that `getOrCreateLayout`
+ /// has already been called previously.
+ ///
+ ProgramLayout* getExistingLayout()
+ {
+ SLANG_ASSERT(m_layout);
+ return m_layout;
+ }
+
+ /// Get the compiled code for an entry point on the target.
+ ///
+ /// If this is the first time that code generation has
+ /// been requested, report any errors that arise during
+ /// code generation to the given `sink`.
+ ///
+ IArtifact* getOrCreateEntryPointResult(Int entryPointIndex, DiagnosticSink* sink);
+ IArtifact* getOrCreateWholeProgramResult(DiagnosticSink* sink);
+
+ IArtifact* getExistingWholeProgramResult() { return m_wholeProgramResult; }
+ /// Get the compiled code for an entry point on the target.
+ ///
+ /// This routine assumes that `getOrCreateEntryPointResult`
+ /// has already been called previously.
+ ///
+ IArtifact* getExistingEntryPointResult(Int entryPointIndex)
+ {
+ return m_entryPointResults[entryPointIndex];
+ }
+
+ IArtifact* _createWholeProgramResult(
+ DiagnosticSink* sink,
+ EndToEndCompileRequest* endToEndReq = nullptr);
+
+ /// Internal helper for `getOrCreateEntryPointResult`.
+ ///
+ /// This is used so that command-line and API-based
+ /// requests for code can bottleneck through the same place.
+ ///
+ /// Shouldn't be called directly by most code.
+ ///
+ IArtifact* _createEntryPointResult(
+ Int entryPointIndex,
+ DiagnosticSink* sink,
+ EndToEndCompileRequest* endToEndReq = nullptr);
+
+ RefPtr<IRModule> getOrCreateIRModuleForLayout(DiagnosticSink* sink);
+
+ RefPtr<IRModule> getExistingIRModuleForLayout() { return m_irModuleForLayout; }
+
+ CompilerOptionSet& getOptionSet() { return m_optionSet; }
+
+ HLSLToVulkanLayoutOptions* getHLSLToVulkanLayoutOptions()
+ {
+ return m_targetReq->getHLSLToVulkanLayoutOptions();
+ }
+
+ bool shouldEmitSPIRVDirectly()
+ {
+ return isKhronosTarget(m_targetReq) && getOptionSet().shouldEmitSPIRVDirectly();
+ }
+
+private:
+ RefPtr<IRModule> createIRModuleForLayout(DiagnosticSink* sink);
+
+ // The program being compiled or laid out
+ ComponentType* m_program;
+
+ // The target that code/layout will be generated for
+ TargetRequest* m_targetReq;
+
+ // The computed layout, if it has been generated yet
+ RefPtr<ProgramLayout> m_layout;
+
+ CompilerOptionSet m_optionSet;
+
+ // Generated compile results for each entry point
+ // in the parent `Program` (indexing matches
+ // the order they are given in the `Program`)
+ ComPtr<IArtifact> m_wholeProgramResult;
+ List<ComPtr<IArtifact>> m_entryPointResults;
+
+ RefPtr<IRModule> m_irModuleForLayout;
+};
+
+/// Given a target request returns which (if any) intermediate source language is required
+/// to produce it.
+///
+/// If no intermediate source language is required, will return SourceLanguage::Unknown
+SourceLanguage getIntermediateSourceLanguageForTarget(TargetProgram* req);
+
+} // namespace Slang
diff --git a/source/slang/slang-target.cpp b/source/slang/slang-target.cpp
new file mode 100644
index 000000000..29430abde
--- /dev/null
+++ b/source/slang/slang-target.cpp
@@ -0,0 +1,248 @@
+// slang-target.cpp
+#include "slang-target.h"
+
+#include "../core/slang-type-text-util.h"
+#include "compiler-core/slang-artifact-desc-util.h"
+#include "slang-compiler.h"
+#include "slang-type-layout.h"
+
+namespace Slang
+{
+
+bool isHeterogeneousTarget(CodeGenTarget target)
+{
+ return ArtifactDescUtil::makeDescForCompileTarget(asExternal(target)).style ==
+ ArtifactStyle::Host;
+}
+
+void printDiagnosticArg(StringBuilder& sb, CodeGenTarget val)
+{
+ UnownedStringSlice name = TypeTextUtil::getCompileTargetName(asExternal(val));
+ name = name.getLength() ? name : toSlice("<unknown>");
+ sb << name;
+}
+
+//
+// TargetRequest
+//
+
+TargetRequest::TargetRequest(Linkage* linkage, CodeGenTarget format)
+ : linkage(linkage)
+{
+ optionSet = linkage->m_optionSet;
+ optionSet.add(CompilerOptionName::Target, format);
+}
+
+TargetRequest::TargetRequest(const TargetRequest& other)
+ : RefObject(), linkage(other.linkage), optionSet(other.optionSet)
+{
+}
+
+
+Session* TargetRequest::getSession()
+{
+ return linkage->getSessionImpl();
+}
+
+HLSLToVulkanLayoutOptions* TargetRequest::getHLSLToVulkanLayoutOptions()
+{
+ if (!hlslToVulkanOptions)
+ {
+ hlslToVulkanOptions = new HLSLToVulkanLayoutOptions();
+ hlslToVulkanOptions->loadFromOptionSet(optionSet);
+ }
+ return hlslToVulkanOptions.get();
+}
+
+void TargetRequest::setTargetCaps(CapabilitySet capSet)
+{
+ cookedCapabilities = capSet;
+}
+
+CapabilitySet TargetRequest::getTargetCaps()
+{
+ if (!cookedCapabilities.isEmpty())
+ return cookedCapabilities;
+
+ // The full `CapabilitySet` for the target will be computed
+ // from the combination of the code generation format, and
+ // the profile.
+ //
+ // Note: the preofile might have been set in a way that is
+ // inconsistent with the output code format of SPIR-V, but
+ // a profile of Direct3D Shader Model 5.1. In those cases,
+ // the format should always override the implications in
+ // the profile.
+ //
+ // TODO: This logic isn't currently taking int account
+ // the information in the profile, because the current
+ // `CapabilityAtom`s that we support don't include any
+ // of the details there (e.g., the shader model versions).
+ //
+ // Eventually, we'd want to have a rich set of capability
+ // atoms, so that most of the information about what operations
+ // are available where can be directly encoded on the declarations.
+
+ List<CapabilityName> atoms;
+
+ // If the user specified a explicit profile, we should pull
+ // a corresponding atom representing the target version from the profile.
+ CapabilitySet profileCaps = optionSet.getProfile().getCapabilityName();
+
+ bool isGLSLTarget = false;
+ switch (getTarget())
+ {
+ case CodeGenTarget::GLSL:
+ isGLSLTarget = true;
+ atoms.add(CapabilityName::glsl);
+ break;
+ case CodeGenTarget::SPIRV:
+ case CodeGenTarget::SPIRVAssembly:
+ if (getOptionSet().shouldEmitSPIRVDirectly())
+ {
+ // Default to SPIRV 1.5 if the user has not specified a target version.
+ bool hasTargetVersionAtom = false;
+ if (!profileCaps.isEmpty())
+ {
+ profileCaps.join(CapabilitySet(CapabilityName::spirv_1_0));
+ for (auto profileCapAtomSet : profileCaps.getAtomSets())
+ {
+ for (auto atom : profileCapAtomSet)
+ {
+ if (isTargetVersionAtom(asAtom(atom)))
+ {
+ atoms.add((CapabilityName)atom);
+ hasTargetVersionAtom = true;
+ }
+ }
+ }
+ }
+ if (!hasTargetVersionAtom)
+ {
+ atoms.add(CapabilityName::spirv_1_5);
+ }
+ // If the user specified any SPIR-V extensions in the profile,
+ // pull them in.
+ for (auto profileCapAtomSet : profileCaps.getAtomSets())
+ {
+ for (auto atom : profileCapAtomSet)
+ {
+ if (isSpirvExtensionAtom(asAtom(atom)))
+ {
+ atoms.add((CapabilityName)atom);
+ hasTargetVersionAtom = true;
+ }
+ }
+ }
+ }
+ else
+ {
+ isGLSLTarget = true;
+ atoms.add(CapabilityName::glsl);
+ profileCaps.addSpirvVersionFromOtherAsGlslSpirvVersion(profileCaps);
+ }
+ break;
+
+ case CodeGenTarget::HLSL:
+ case CodeGenTarget::DXBytecode:
+ case CodeGenTarget::DXBytecodeAssembly:
+ case CodeGenTarget::DXIL:
+ case CodeGenTarget::DXILAssembly:
+ atoms.add(CapabilityName::hlsl);
+ break;
+
+ case CodeGenTarget::CSource:
+ atoms.add(CapabilityName::c);
+ break;
+
+ case CodeGenTarget::CPPSource:
+ case CodeGenTarget::PyTorchCppBinding:
+ case CodeGenTarget::HostExecutable:
+ case CodeGenTarget::ShaderSharedLibrary:
+ case CodeGenTarget::HostSharedLibrary:
+ case CodeGenTarget::HostHostCallable:
+ case CodeGenTarget::ShaderHostCallable:
+ atoms.add(CapabilityName::cpp);
+ break;
+
+ case CodeGenTarget::CUDASource:
+ case CodeGenTarget::PTX:
+ atoms.add(CapabilityName::cuda);
+ break;
+
+ case CodeGenTarget::Metal:
+ case CodeGenTarget::MetalLib:
+ case CodeGenTarget::MetalLibAssembly:
+ atoms.add(CapabilityName::metal);
+ break;
+
+ case CodeGenTarget::WGSLSPIRV:
+ case CodeGenTarget::WGSLSPIRVAssembly:
+ case CodeGenTarget::WGSL:
+ atoms.add(CapabilityName::wgsl);
+ break;
+
+ default:
+ break;
+ }
+
+ CapabilitySet targetCap = CapabilitySet(atoms);
+
+ if (profileCaps.atLeastOneSetImpliedInOther(targetCap) ==
+ CapabilitySet::ImpliesReturnFlags::Implied)
+ targetCap.join(profileCaps);
+
+ for (auto atomVal : optionSet.getArray(CompilerOptionName::Capability))
+ {
+ CapabilitySet toAdd;
+ switch (atomVal.kind)
+ {
+ case CompilerOptionValueKind::Int:
+ toAdd = CapabilitySet(CapabilityName(atomVal.intValue));
+ break;
+ case CompilerOptionValueKind::String:
+ toAdd = CapabilitySet(findCapabilityName(atomVal.stringValue.getUnownedSlice()));
+ break;
+ }
+
+ if (isGLSLTarget)
+ targetCap.addSpirvVersionFromOtherAsGlslSpirvVersion(toAdd);
+
+ if (!targetCap.isIncompatibleWith(toAdd))
+ targetCap.join(toAdd);
+ }
+
+ cookedCapabilities = targetCap;
+
+ SLANG_ASSERT(!cookedCapabilities.isInvalid());
+
+ return cookedCapabilities;
+}
+
+
+TypeLayout* TargetRequest::getTypeLayout(Type* type, slang::LayoutRules rules)
+{
+ SLANG_AST_BUILDER_RAII(getLinkage()->getASTBuilder());
+
+ // TODO: We are not passing in a `ProgramLayout` here, although one
+ // is nominally required to establish the global ordering of
+ // generic type parameters, which might be referenced from field types.
+ //
+ // The solution here is to make sure that the reflection data for
+ // uses of global generic/existential types does *not* include any
+ // kind of index in that global ordering, and just refers to the
+ // parameter instead (leaving the user to figure out how that
+ // maps to the ordering via some API on the program layout).
+ //
+ auto layoutContext = getInitialLayoutContextForTarget(this, nullptr, rules);
+
+ RefPtr<TypeLayout> result;
+ auto key = TypeLayoutKey{type, rules};
+ if (getTypeLayouts().tryGetValue(key, result))
+ return result.Ptr();
+ result = createTypeLayout(layoutContext, type);
+ getTypeLayouts()[key] = result;
+ return result.Ptr();
+}
+
+} // namespace Slang
diff --git a/source/slang/slang-target.h b/source/slang/slang-target.h
new file mode 100644
index 000000000..6ac52657f
--- /dev/null
+++ b/source/slang/slang-target.h
@@ -0,0 +1,141 @@
+// slang-target.h
+#pragma once
+
+//
+// This file declares the `TargetRequest` class, which is
+// the primary way that the Slang compiler groups together
+// a compilation target and options that affect output
+// code generation and/or layout for that target.
+//
+
+#include "../core/slang-string.h"
+#include "slang-ast-base.h"
+#include "slang-compiler-fwd.h"
+#include "slang-compiler-options.h"
+#include "slang-hlsl-to-vulkan-layout-options.h"
+
+#include <slang.h>
+
+namespace Slang
+{
+
+enum class CodeGenTarget : SlangCompileTargetIntegral
+{
+ Unknown = SLANG_TARGET_UNKNOWN,
+ None = SLANG_TARGET_NONE,
+ GLSL = SLANG_GLSL,
+ HLSL = SLANG_HLSL,
+ SPIRV = SLANG_SPIRV,
+ SPIRVAssembly = SLANG_SPIRV_ASM,
+ DXBytecode = SLANG_DXBC,
+ DXBytecodeAssembly = SLANG_DXBC_ASM,
+ DXIL = SLANG_DXIL,
+ DXILAssembly = SLANG_DXIL_ASM,
+ CSource = SLANG_C_SOURCE,
+ CPPSource = SLANG_CPP_SOURCE,
+ PyTorchCppBinding = SLANG_CPP_PYTORCH_BINDING,
+ HostCPPSource = SLANG_HOST_CPP_SOURCE,
+ HostExecutable = SLANG_HOST_EXECUTABLE,
+ HostSharedLibrary = SLANG_HOST_SHARED_LIBRARY,
+ ShaderSharedLibrary = SLANG_SHADER_SHARED_LIBRARY,
+ ShaderHostCallable = SLANG_SHADER_HOST_CALLABLE,
+ CUDASource = SLANG_CUDA_SOURCE,
+ PTX = SLANG_PTX,
+ CUDAObjectCode = SLANG_CUDA_OBJECT_CODE,
+ ObjectCode = SLANG_OBJECT_CODE,
+ HostHostCallable = SLANG_HOST_HOST_CALLABLE,
+ Metal = SLANG_METAL,
+ MetalLib = SLANG_METAL_LIB,
+ MetalLibAssembly = SLANG_METAL_LIB_ASM,
+ WGSL = SLANG_WGSL,
+ WGSLSPIRVAssembly = SLANG_WGSL_SPIRV_ASM,
+ WGSLSPIRV = SLANG_WGSL_SPIRV,
+ HostVM = SLANG_HOST_VM,
+ CountOf = SLANG_TARGET_COUNT_OF,
+};
+
+bool isHeterogeneousTarget(CodeGenTarget target);
+
+void printDiagnosticArg(StringBuilder& sb, CodeGenTarget val);
+
+class TargetRequest;
+
+/// Are we generating code for a D3D API?
+bool isD3DTarget(TargetRequest* targetReq);
+
+// Are we generating code for Metal?
+bool isMetalTarget(TargetRequest* targetReq);
+
+/// Are we generating code for a Khronos API (OpenGL or Vulkan)?
+bool isKhronosTarget(TargetRequest* targetReq);
+bool isKhronosTarget(CodeGenTarget target);
+
+/// Are we generating code for a CUDA API (CUDA / OptiX)?
+bool isCUDATarget(TargetRequest* targetReq);
+
+// Are we generating code for a CPU target
+bool isCPUTarget(TargetRequest* targetReq);
+
+/// Are we generating code for the WebGPU API?
+bool isWGPUTarget(TargetRequest* targetReq);
+bool isWGPUTarget(CodeGenTarget target);
+
+/// A request to generate output in some target format.
+class TargetRequest : public RefObject
+{
+public:
+ TargetRequest(Linkage* linkage, CodeGenTarget format);
+
+ TargetRequest(const TargetRequest& other);
+
+ Linkage* getLinkage() { return linkage; }
+
+ Session* getSession();
+
+ CodeGenTarget getTarget()
+ {
+ return optionSet.getEnumOption<CodeGenTarget>(CompilerOptionName::Target);
+ }
+
+ // TypeLayouts created on the fly by reflection API
+ struct TypeLayoutKey
+ {
+ Type* type;
+ slang::LayoutRules rules;
+ HashCode getHashCode() const
+ {
+ Hasher hasher;
+ hasher.hashValue(type);
+ hasher.hashValue(rules);
+ return hasher.getResult();
+ }
+ bool operator==(TypeLayoutKey other) const
+ {
+ return type == other.type && rules == other.rules;
+ }
+ };
+ Dictionary<TypeLayoutKey, RefPtr<TypeLayout>> typeLayouts;
+
+ Dictionary<TypeLayoutKey, RefPtr<TypeLayout>>& getTypeLayouts() { return typeLayouts; }
+
+ TypeLayout* getTypeLayout(Type* type, slang::LayoutRules rules);
+
+ CompilerOptionSet& getOptionSet() { return optionSet; }
+
+ CapabilitySet getTargetCaps();
+
+ void setTargetCaps(CapabilitySet capSet);
+
+ HLSLToVulkanLayoutOptions* getHLSLToVulkanLayoutOptions();
+
+private:
+ Linkage* linkage = nullptr;
+ CompilerOptionSet optionSet;
+ CapabilitySet cookedCapabilities;
+ RefPtr<HLSLToVulkanLayoutOptions> hlslToVulkanOptions;
+};
+
+/// Are resource types "bindless" (implemented as ordinary data) on the given `target`?
+bool areResourceTypesBindlessOnTarget(TargetRequest* target);
+
+} // namespace Slang
diff --git a/source/slang/slang-translation-unit.cpp b/source/slang/slang-translation-unit.cpp
new file mode 100644
index 000000000..049de01eb
--- /dev/null
+++ b/source/slang/slang-translation-unit.cpp
@@ -0,0 +1,251 @@
+// slang-translation-unit.cpp
+#include "slang-translation-unit.h"
+
+#include "slang-compiler.h"
+
+namespace Slang
+{
+
+//
+// TranslationUnitRequest
+//
+
+TranslationUnitRequest::TranslationUnitRequest(FrontEndCompileRequest* compileRequest)
+ : compileRequest(compileRequest)
+{
+ module = new Module(compileRequest->getLinkage());
+}
+
+TranslationUnitRequest::TranslationUnitRequest(FrontEndCompileRequest* compileRequest, Module* m)
+ : compileRequest(compileRequest), module(m), isChecked(true)
+{
+ moduleName = getNamePool()->getName(m->getName());
+}
+
+Session* TranslationUnitRequest::getSession()
+{
+ return compileRequest->getSession();
+}
+
+NamePool* TranslationUnitRequest::getNamePool()
+{
+ return compileRequest->getNamePool();
+}
+
+SourceManager* TranslationUnitRequest::getSourceManager()
+{
+ return compileRequest->getSourceManager();
+}
+
+Scope* TranslationUnitRequest::getLanguageScope()
+{
+ Scope* languageScope = nullptr;
+ switch (sourceLanguage)
+ {
+ case SourceLanguage::HLSL:
+ languageScope = getSession()->hlslLanguageScope;
+ break;
+ case SourceLanguage::GLSL:
+ languageScope = getSession()->glslLanguageScope;
+ break;
+ case SourceLanguage::Slang:
+ default:
+ languageScope = getSession()->slangLanguageScope;
+ break;
+ }
+ return languageScope;
+}
+
+Dictionary<String, String> TranslationUnitRequest::getCombinedPreprocessorDefinitions()
+{
+ Dictionary<String, String> combinedPreprocessorDefinitions;
+ for (const auto& def : preprocessorDefinitions)
+ combinedPreprocessorDefinitions.addIfNotExists(def);
+ for (const auto& def : compileRequest->optionSet.getArray(CompilerOptionName::MacroDefine))
+ combinedPreprocessorDefinitions.addIfNotExists(def.stringValue, def.stringValue2);
+
+ // Define standard macros, if not already defined. This style assumes using `#if __SOME_VAR`
+ // style, as in
+ //
+ // ```
+ // #if __SLANG_COMPILER__
+ // ```
+ //
+ // This choice is made because slang outputs a warning on using a variable in an #if if not
+ // defined
+ //
+ // Of course this means using #ifndef/#ifdef/defined() is probably not appropraite with thes
+ // variables.
+ {
+ // Used to identify level of HLSL language compatibility
+ combinedPreprocessorDefinitions.addIfNotExists("__HLSL_VERSION", "2018");
+
+ // Indicates this is being compiled by the slang *compiler*
+ combinedPreprocessorDefinitions.addIfNotExists("__SLANG_COMPILER__", "1");
+
+ // Set macro depending on source type
+ switch (sourceLanguage)
+ {
+ case SourceLanguage::HLSL:
+ // Used to indicate compiled as HLSL language
+ combinedPreprocessorDefinitions.addIfNotExists("__HLSL__", "1");
+ break;
+ case SourceLanguage::Slang:
+ // Used to indicate compiled as Slang language
+ combinedPreprocessorDefinitions.addIfNotExists("__SLANG__", "1");
+ break;
+ default:
+ break;
+ }
+
+ // If not set, define as 0.
+ combinedPreprocessorDefinitions.addIfNotExists("__HLSL__", "0");
+ combinedPreprocessorDefinitions.addIfNotExists("__SLANG__", "0");
+ }
+
+ return combinedPreprocessorDefinitions;
+}
+
+void TranslationUnitRequest::addSourceArtifact(IArtifact* sourceArtifact)
+{
+ SLANG_ASSERT(sourceArtifact);
+ m_sourceArtifacts.add(ComPtr<IArtifact>(sourceArtifact));
+}
+
+
+void TranslationUnitRequest::addSource(IArtifact* sourceArtifact, SourceFile* sourceFile)
+{
+ SLANG_ASSERT(sourceArtifact && sourceFile);
+ // Must be in sync!
+ SLANG_ASSERT(m_sourceFiles.getCount() == m_sourceArtifacts.getCount());
+
+ addSourceArtifact(sourceArtifact);
+ _addSourceFile(sourceFile);
+}
+
+void TranslationUnitRequest::addIncludedSourceFileIfNotExist(SourceFile* sourceFile)
+{
+ if (m_includedFileSet.contains(sourceFile))
+ return;
+
+ sourceFile->setIncludedFile();
+ m_sourceFiles.add(sourceFile);
+ m_includedFileSet.add(sourceFile);
+}
+
+PathInfo TranslationUnitRequest::_findSourcePathInfo(IArtifact* artifact)
+{
+ auto pathRep = findRepresentation<IPathArtifactRepresentation>(artifact);
+
+ if (pathRep && pathRep->getPathType() == SLANG_PATH_TYPE_FILE)
+ {
+ // See if we have a unique identity set with the path
+ if (const auto uniqueIdentity = pathRep->getUniqueIdentity())
+ {
+ return PathInfo::makeNormal(pathRep->getPath(), uniqueIdentity);
+ }
+
+ // If we couldn't get a unique identity, just use the path
+ return PathInfo::makePath(pathRep->getPath());
+ }
+
+ // If there isn't a path, we can try with the name
+ const char* name = artifact->getName();
+ if (name && name[0] != 0)
+ {
+ return PathInfo::makeFromString(name);
+ }
+
+ return PathInfo::makeUnknown();
+}
+
+SlangResult TranslationUnitRequest::requireSourceFiles()
+{
+ SLANG_ASSERT(m_sourceFiles.getCount() <= m_sourceArtifacts.getCount());
+
+ if (m_sourceFiles.getCount() == m_sourceArtifacts.getCount())
+ {
+ return SLANG_OK;
+ }
+
+ auto sink = compileRequest->getSink();
+ SourceManager* sourceManager = compileRequest->getSourceManager();
+
+ for (Index i = m_sourceFiles.getCount(); i < m_sourceArtifacts.getCount(); ++i)
+ {
+ IArtifact* artifact = m_sourceArtifacts[i];
+
+ const PathInfo pathInfo = _findSourcePathInfo(artifact);
+
+ SourceFile* sourceFile = nullptr;
+ ComPtr<ISlangBlob> blob;
+
+ // If we have a unique identity see if we have it already
+ if (pathInfo.hasUniqueIdentity())
+ {
+ // See if this an already loaded source file
+ sourceFile = sourceManager->findSourceFileRecursively(pathInfo.uniqueIdentity);
+ // If we have a sourceFile see if it has a blob
+ if (sourceFile)
+ {
+ blob = sourceFile->getContentBlob();
+ }
+ }
+
+ // If we *don't* have a blob try and get a blob from the artifact
+ if (!blob)
+ {
+ const SlangResult res = artifact->loadBlob(ArtifactKeep::Yes, blob.writeRef());
+ if (SLANG_FAILED(res))
+ {
+ // Report couldn't load
+ sink->diagnose(SourceLoc(), Diagnostics::cannotOpenFile, pathInfo.getName());
+ return res;
+ }
+ }
+
+ // If we don't have a blob on the artifact we can now add the one we have
+ if (!findRepresentation<ISlangBlob>(artifact))
+ {
+ artifact->addRepresentationUnknown(blob);
+ }
+
+ // If we have a sourceFile check if it has contents, and set the blob if doesn't
+ if (sourceFile)
+ {
+ if (!sourceFile->getContentBlob())
+ {
+ sourceFile->setContents(blob);
+ }
+ }
+ else
+ {
+ // Create a new source file, using the pathInfo and blob
+ sourceFile = sourceManager->createSourceFileWithBlob(pathInfo, blob);
+ }
+
+ auto uniqueIdentity = pathInfo.getMostUniqueIdentity();
+ if (uniqueIdentity.getLength())
+ sourceManager->addSourceFileIfNotExist(uniqueIdentity, sourceFile);
+
+ // Finally add the source file
+ _addSourceFile(sourceFile);
+ }
+
+ return SLANG_OK;
+}
+
+void TranslationUnitRequest::_addSourceFile(SourceFile* sourceFile)
+{
+ m_sourceFiles.add(sourceFile);
+
+ getModule()->addFileDependency(sourceFile);
+ getModule()->getIncludedSourceFileMap().add(sourceFile, nullptr);
+}
+
+List<SourceFile*> const& TranslationUnitRequest::getSourceFiles()
+{
+ return m_sourceFiles;
+}
+
+} // namespace Slang
diff --git a/source/slang/slang-translation-unit.h b/source/slang/slang-translation-unit.h
new file mode 100644
index 000000000..ad19cb9ac
--- /dev/null
+++ b/source/slang/slang-translation-unit.h
@@ -0,0 +1,117 @@
+// slang-translation-unit.h
+#pragma once
+
+//
+// This file provides the `TranslationUnitRequest` class,
+// which is used to represent the inputs to front-end compilation
+// that will yield a single `Module`.
+//
+
+#include "../compiler-core/slang-artifact.h"
+#include "../compiler-core/slang-source-loc.h"
+#include "../core/slang-smart-pointer.h"
+#include "slang-compiler-fwd.h"
+#include "slang-entry-point.h"
+#include "slang-module.h"
+#include "slang-profile.h"
+
+namespace Slang
+{
+
+/// A request for the front-end to compile a translation unit.
+class TranslationUnitRequest : public RefObject
+{
+public:
+ TranslationUnitRequest(FrontEndCompileRequest* compileRequest);
+ TranslationUnitRequest(FrontEndCompileRequest* compileRequest, Module* m);
+
+ // The parent compile request
+ FrontEndCompileRequest* compileRequest = nullptr;
+
+ // The language in which the source file(s)
+ // are assumed to be written
+ SourceLanguage sourceLanguage = SourceLanguage::Unknown;
+
+ /// Makes any source artifact available as a SourceFile.
+ /// If successful any of the source artifacts will be represented by the same index
+ /// of sourceArtifacts
+ SlangResult requireSourceFiles();
+
+ /// Get the source files.
+ /// Since lazily evaluated requires calling requireSourceFiles to know it's in sync
+ /// with sourceArtifacts.
+ List<SourceFile*> const& getSourceFiles();
+
+ /// Get the source artifacts associated
+ const List<ComPtr<IArtifact>>& getSourceArtifacts() const { return m_sourceArtifacts; }
+
+ /// Clear all of the source
+ void clearSource()
+ {
+ m_sourceArtifacts.clear();
+ m_sourceFiles.clear();
+ m_includedFileSet.clear();
+ }
+
+ /// Add a source artifact
+ void addSourceArtifact(IArtifact* sourceArtifact);
+
+ /// Add both the artifact and the sourceFile.
+ void addSource(IArtifact* sourceArtifact, SourceFile* sourceFile);
+
+ void addIncludedSourceFileIfNotExist(SourceFile* sourceFile);
+
+ // The entry points associated with this translation unit
+ List<RefPtr<EntryPoint>> const& getEntryPoints() { return module->getEntryPoints(); }
+
+ void _addEntryPoint(EntryPoint* entryPoint) { module->_addEntryPoint(entryPoint); }
+
+ // Preprocessor definitions to use for this translation unit only
+ // (whereas the ones on `compileRequest` will be shared)
+ Dictionary<String, String> preprocessorDefinitions;
+
+ /// The name that will be used for the module this translation unit produces.
+ Name* moduleName = nullptr;
+
+ /// Result of compiling this translation unit (a module)
+ RefPtr<Module> module;
+
+ bool isChecked = false;
+
+ Module* getModule() { return module; }
+ ModuleDecl* getModuleDecl() { return module->getModuleDecl(); }
+
+ Session* getSession();
+ NamePool* getNamePool();
+ SourceManager* getSourceManager();
+
+ Scope* getLanguageScope();
+
+ Dictionary<String, String> getCombinedPreprocessorDefinitions();
+
+ void setModuleName(Name* name)
+ {
+ moduleName = name;
+ if (module)
+ module->setName(name);
+ }
+
+protected:
+ void _addSourceFile(SourceFile* sourceFile);
+ /* Given an artifact, find a PathInfo.
+ If no PathInfo can be found will return an unknown PathInfo */
+ PathInfo _findSourcePathInfo(IArtifact* artifact);
+
+ List<ComPtr<IArtifact>> m_sourceArtifacts;
+ // The source file(s) that will be compiled to form this translation unit
+ //
+ // Usually, for HLSL or GLSL there will be only one file.
+ // NOTE! This member is generated lazily from m_sourceArtifacts
+ // it is *necessary* to call requireSourceFiles to ensure it's in sync.
+ List<SourceFile*> m_sourceFiles;
+
+ // Track all the included source files added in m_sourceFiles
+ HashSet<SourceFile*> m_includedFileSet;
+};
+
+} // namespace Slang
diff --git a/source/slang/slang.cpp b/source/slang/slang.cpp
index 839ba7938..a428e7928 100644
--- a/source/slang/slang.cpp
+++ b/source/slang/slang.cpp
@@ -48,102 +48,9 @@
// Used to print exception type names in internal-compiler-error messages
#include <typeinfo>
-extern Slang::String get_slang_cuda_prelude();
-extern Slang::String get_slang_cpp_prelude();
-extern Slang::String get_slang_hlsl_prelude();
-
namespace Slang
{
-
-/* static */ const BaseTypeInfo BaseTypeInfo::s_info[Index(BaseType::CountOf)] = {
- {0, 0, uint8_t(BaseType::Void)},
- {uint8_t(sizeof(bool)), 0, uint8_t(BaseType::Bool)},
- {uint8_t(sizeof(int8_t)),
- BaseTypeInfo::Flag::Signed | BaseTypeInfo::Flag::Integer,
- uint8_t(BaseType::Int8)},
- {uint8_t(sizeof(int16_t)),
- BaseTypeInfo::Flag::Signed | BaseTypeInfo::Flag::Integer,
- uint8_t(BaseType::Int16)},
- {uint8_t(sizeof(int32_t)),
- BaseTypeInfo::Flag::Signed | BaseTypeInfo::Flag::Integer,
- uint8_t(BaseType::Int)},
- {uint8_t(sizeof(int64_t)),
- BaseTypeInfo::Flag::Signed | BaseTypeInfo::Flag::Integer,
- uint8_t(BaseType::Int64)},
- {uint8_t(sizeof(uint8_t)), BaseTypeInfo::Flag::Integer, uint8_t(BaseType::UInt8)},
- {uint8_t(sizeof(uint16_t)), BaseTypeInfo::Flag::Integer, uint8_t(BaseType::UInt16)},
- {uint8_t(sizeof(uint32_t)), BaseTypeInfo::Flag::Integer, uint8_t(BaseType::UInt)},
- {uint8_t(sizeof(uint64_t)), BaseTypeInfo::Flag::Integer, uint8_t(BaseType::UInt64)},
- {uint8_t(sizeof(uint16_t)), BaseTypeInfo::Flag::FloatingPoint, uint8_t(BaseType::Half)},
- {uint8_t(sizeof(float)), BaseTypeInfo::Flag::FloatingPoint, uint8_t(BaseType::Float)},
- {uint8_t(sizeof(double)), BaseTypeInfo::Flag::FloatingPoint, uint8_t(BaseType::Double)},
- {uint8_t(sizeof(char)),
- BaseTypeInfo::Flag::Signed | BaseTypeInfo::Flag::Integer,
- uint8_t(BaseType::Char)},
- {uint8_t(sizeof(intptr_t)),
- BaseTypeInfo::Flag::Signed | BaseTypeInfo::Flag::Integer,
- uint8_t(BaseType::IntPtr)},
- {uint8_t(sizeof(uintptr_t)), BaseTypeInfo::Flag::Integer, uint8_t(BaseType::UIntPtr)},
-};
-
-/* static */ bool BaseTypeInfo::check()
-{
- for (Index i = 0; i < SLANG_COUNT_OF(s_info); ++i)
- {
- if (s_info[i].baseType != i)
- {
- SLANG_ASSERT(!"Inconsistency between the s_info table and BaseInfo");
- return false;
- }
- }
- return true;
-}
-
-/* static */ UnownedStringSlice BaseTypeInfo::asText(BaseType baseType)
-{
- switch (baseType)
- {
- case BaseType::Void:
- return UnownedStringSlice::fromLiteral("void");
- case BaseType::Bool:
- return UnownedStringSlice::fromLiteral("bool");
- case BaseType::Int8:
- return UnownedStringSlice::fromLiteral("int8_t");
- case BaseType::Int16:
- return UnownedStringSlice::fromLiteral("int16_t");
- case BaseType::Int:
- return UnownedStringSlice::fromLiteral("int");
- case BaseType::Int64:
- return UnownedStringSlice::fromLiteral("int64_t");
- case BaseType::UInt8:
- return UnownedStringSlice::fromLiteral("uint8_t");
- case BaseType::UInt16:
- return UnownedStringSlice::fromLiteral("uint16_t");
- case BaseType::UInt:
- return UnownedStringSlice::fromLiteral("uint");
- case BaseType::UInt64:
- return UnownedStringSlice::fromLiteral("uint64_t");
- case BaseType::Half:
- return UnownedStringSlice::fromLiteral("half");
- case BaseType::Float:
- return UnownedStringSlice::fromLiteral("float");
- case BaseType::Double:
- return UnownedStringSlice::fromLiteral("double");
- case BaseType::Char:
- return UnownedStringSlice::fromLiteral("char");
- case BaseType::IntPtr:
- return UnownedStringSlice::fromLiteral("intptr_t");
- case BaseType::UIntPtr:
- return UnownedStringSlice::fromLiteral("uintptr_t");
- default:
- {
- SLANG_ASSERT(!"Unknown basic type");
- return UnownedStringSlice();
- }
- }
-}
-
const char* getBuildTagString()
{
if (UnownedStringSlice(SLANG_TAG_VERSION) == "0.0.0-unknown")
@@ -158,1102 +65,6 @@ const char* getBuildTagString()
return SLANG_TAG_VERSION;
}
-
-void Session::init()
-{
- SLANG_ASSERT(BaseTypeInfo::check());
-
-#if SLANG_ENABLE_IR_BREAK_ALLOC
- // Read environment variable for IR debugging
- StringBuilder irBreakEnv;
- if (SLANG_SUCCEEDED(PlatformUtil::getEnvironmentVariable(
- UnownedStringSlice("SLANG_DEBUG_IR_BREAK"),
- irBreakEnv)))
- {
- String envValue = irBreakEnv.produceString();
- if (envValue.getLength())
- {
- _slangIRAllocBreak = stringToInt(envValue);
- _slangIRPrintStackAtBreak = true;
- }
- }
-#endif
-
- _initCodeGenTransitionMap();
-
- ::memset(m_downstreamCompilerLocators, 0, sizeof(m_downstreamCompilerLocators));
- DownstreamCompilerUtil::setDefaultLocators(m_downstreamCompilerLocators);
- m_downstreamCompilerSet = new DownstreamCompilerSet;
-
- m_completionTokenName = getNamePool()->getName("#?");
-
- m_sharedLibraryLoader = DefaultSharedLibraryLoader::getSingleton();
-
- // Set up the command line options
- initCommandOptions(m_commandOptions);
-
- // Set up shared AST builder
- m_sharedASTBuilder = new SharedASTBuilder;
- m_sharedASTBuilder->init(this);
-
- // And the global ASTBuilder
- auto builtinAstBuilder = m_sharedASTBuilder->getInnerASTBuilder();
- globalAstBuilder = builtinAstBuilder;
-
- // Make sure our source manager is initialized
- builtinSourceManager.initialize(nullptr, nullptr);
-
- // Built in linkage uses the built in builder
- m_builtinLinkage = new Linkage(this, builtinAstBuilder, nullptr);
- m_builtinLinkage->m_optionSet.set(CompilerOptionName::DebugInformation, DebugInfoLevel::None);
-
- // Because the `Session` retains the builtin `Linkage`,
- // we need to make sure that the parent pointer inside
- // `Linkage` doesn't create a retain cycle.
- //
- // This operation ensures that the parent pointer will
- // just be a raw pointer, so that the builtin linkage
- // doesn't keep the parent session alive.
- //
- m_builtinLinkage->_stopRetainingParentSession();
-
- // Create scopes for various language builtins.
- //
- // TODO: load these on-demand to avoid parsing
- // the core module code for languages the user won't use.
-
- baseLanguageScope = builtinAstBuilder->create<Scope>();
-
- // Will stay in scope as long as ASTBuilder
- baseModuleDecl =
- populateBaseLanguageModule(m_builtinLinkage->getASTBuilder(), baseLanguageScope);
-
- coreLanguageScope = builtinAstBuilder->create<Scope>();
- coreLanguageScope->nextSibling = baseLanguageScope;
-
- hlslLanguageScope = builtinAstBuilder->create<Scope>();
- hlslLanguageScope->nextSibling = coreLanguageScope;
-
- slangLanguageScope = builtinAstBuilder->create<Scope>();
- slangLanguageScope->nextSibling = hlslLanguageScope;
-
- glslLanguageScope = builtinAstBuilder->create<Scope>();
- glslLanguageScope->nextSibling = slangLanguageScope;
-
- glslModuleName = getNameObj("glsl");
-
- {
- for (Index i = 0; i < Index(SourceLanguage::CountOf); ++i)
- {
- m_defaultDownstreamCompilers[i] = PassThroughMode::None;
- }
- m_defaultDownstreamCompilers[Index(SourceLanguage::C)] = PassThroughMode::GenericCCpp;
- m_defaultDownstreamCompilers[Index(SourceLanguage::CPP)] = PassThroughMode::GenericCCpp;
- m_defaultDownstreamCompilers[Index(SourceLanguage::CUDA)] = PassThroughMode::NVRTC;
- }
-
- // Set up default prelude code for target languages that need a prelude
- m_languagePreludes[Index(SourceLanguage::CUDA)] = get_slang_cuda_prelude();
- m_languagePreludes[Index(SourceLanguage::CPP)] = get_slang_cpp_prelude();
- m_languagePreludes[Index(SourceLanguage::HLSL)] = get_slang_hlsl_prelude();
-
- if (!spirvCoreGrammarInfo)
- spirvCoreGrammarInfo = SPIRVCoreGrammarInfo::getEmbeddedVersion();
-}
-
-Module* Session::getBuiltinModule(slang::BuiltinModuleName name)
-{
- auto info = getBuiltinModuleInfo(name);
- auto builtinLinkage = getBuiltinLinkage();
- auto moduleNameObj = builtinLinkage->getNamePool()->getName(info.name);
- RefPtr<Module> module;
- if (builtinLinkage->mapNameToLoadedModules.tryGetValue(moduleNameObj, module))
- return module.get();
- return nullptr;
-}
-
-void Session::_initCodeGenTransitionMap()
-{
- // TODO(JS): Might want to do something about these in the future...
-
- // PassThroughMode getDownstreamCompilerRequiredForTarget(CodeGenTarget target);
- // SourceLanguage getDefaultSourceLanguageForDownstreamCompiler(PassThroughMode compiler);
-
- // Set up the default ways to do compilations between code gen targets
- auto& map = m_codeGenTransitionMap;
-
- // TODO(JS): There currently isn't a 'downstream compiler' for direct spirv output. If we did
- // it would presumably a transition from SlangIR to SPIRV.
-
- // For C and C++ we default to use the 'genericCCpp' compiler
- {
- const CodeGenTarget sources[] = {CodeGenTarget::CSource, CodeGenTarget::CPPSource};
- for (auto source : sources)
- {
- // We *don't* add a default for host callable, as we will determine what is suitable
- // depending on what is available. We prefer LLVM if that's available. If it's not we
- // can use generic C/C++ compiler
-
- map.addTransition(
- source,
- CodeGenTarget::ShaderSharedLibrary,
- PassThroughMode::GenericCCpp);
- map.addTransition(
- source,
- CodeGenTarget::HostSharedLibrary,
- PassThroughMode::GenericCCpp);
- map.addTransition(source, CodeGenTarget::HostExecutable, PassThroughMode::GenericCCpp);
- map.addTransition(source, CodeGenTarget::ObjectCode, PassThroughMode::GenericCCpp);
- }
- }
-
-
- // Add all the straightforward transitions
- map.addTransition(CodeGenTarget::CUDASource, CodeGenTarget::PTX, PassThroughMode::NVRTC);
- map.addTransition(CodeGenTarget::HLSL, CodeGenTarget::DXBytecode, PassThroughMode::Fxc);
- map.addTransition(CodeGenTarget::HLSL, CodeGenTarget::DXIL, PassThroughMode::Dxc);
- map.addTransition(CodeGenTarget::GLSL, CodeGenTarget::SPIRV, PassThroughMode::Glslang);
- map.addTransition(CodeGenTarget::Metal, CodeGenTarget::MetalLib, PassThroughMode::MetalC);
- map.addTransition(CodeGenTarget::WGSL, CodeGenTarget::WGSLSPIRV, PassThroughMode::Tint);
- // To assembly
- map.addTransition(CodeGenTarget::SPIRV, CodeGenTarget::SPIRVAssembly, PassThroughMode::Glslang);
- // We use glslang to turn SPIR-V into SPIR-V assembly.
- map.addTransition(
- CodeGenTarget::WGSLSPIRV,
- CodeGenTarget::WGSLSPIRVAssembly,
- PassThroughMode::Glslang);
- map.addTransition(CodeGenTarget::DXIL, CodeGenTarget::DXILAssembly, PassThroughMode::Dxc);
- map.addTransition(
- CodeGenTarget::DXBytecode,
- CodeGenTarget::DXBytecodeAssembly,
- PassThroughMode::Fxc);
- map.addTransition(
- CodeGenTarget::MetalLib,
- CodeGenTarget::MetalLibAssembly,
- PassThroughMode::MetalC);
-}
-
-void Session::addBuiltins(char const* sourcePath, char const* source)
-{
- auto sourceBlob = StringBlob::moveCreate(String(source));
-
- // TODO(tfoley): Add ability to directly new builtins to the appropriate scope
- Module* module = nullptr;
- addBuiltinSource(coreLanguageScope, sourcePath, sourceBlob, module);
- if (module)
- coreModules.add(module);
-}
-
-void Session::setSharedLibraryLoader(ISlangSharedLibraryLoader* loader)
-{
- // External API allows passing of nullptr to reset the loader
- loader = loader ? loader : DefaultSharedLibraryLoader::getSingleton();
-
- _setSharedLibraryLoader(loader);
-}
-
-ISlangSharedLibraryLoader* Session::getSharedLibraryLoader()
-{
- return (m_sharedLibraryLoader == DefaultSharedLibraryLoader::getSingleton())
- ? nullptr
- : m_sharedLibraryLoader.get();
-}
-
-SlangResult Session::checkCompileTargetSupport(SlangCompileTarget inTarget)
-{
- auto target = CodeGenTarget(inTarget);
-
- const PassThroughMode mode = getDownstreamCompilerRequiredForTarget(target);
- return (mode != PassThroughMode::None) ? checkPassThroughSupport(SlangPassThrough(mode))
- : SLANG_OK;
-}
-
-SlangResult Session::checkPassThroughSupport(SlangPassThrough inPassThrough)
-{
- return checkExternalCompilerSupport(this, PassThroughMode(inPassThrough));
-}
-
-void Session::writeCoreModuleDoc(String config)
-{
- ASTBuilder* astBuilder = getBuiltinLinkage()->getASTBuilder();
- SourceManager* sourceManager = getBuiltinSourceManager();
-
- DiagnosticSink sink(sourceManager, Lexer::sourceLocationLexer);
-
- List<String> docStrings;
-
- // For all the modules add their doc output to docStrings
- for (Module* m : coreModules)
- {
- RefPtr<ASTMarkup> markup(new ASTMarkup);
- ASTMarkupUtil::extract(m->getModuleDecl(), sourceManager, &sink, markup);
-
- DocMarkdownWriter writer(markup, astBuilder, &sink);
- auto rootPage = writer.writeAll(config.getUnownedSlice());
- File::writeAllText("toc.html", writer.writeTOC());
- rootPage->writeToDisk();
- rootPage->writeSummary(toSlice("summary.txt"));
- }
- ComPtr<ISlangBlob> diagnosticBlob;
- sink.getBlobIfNeeded(diagnosticBlob.writeRef());
- if (diagnosticBlob && diagnosticBlob->getBufferSize() != 0)
- {
- // Write the diagnostic blob to stdout.
- fprintf(stderr, "%s", (const char*)diagnosticBlob->getBufferPointer());
- }
-}
-
-const char* getBuiltinModuleNameStr(slang::BuiltinModuleName name)
-{
- const char* result = nullptr;
- switch (name)
- {
- case slang::BuiltinModuleName::Core:
- result = "core";
- break;
- case slang::BuiltinModuleName::GLSL:
- result = "glsl";
- break;
- default:
- SLANG_UNEXPECTED("Unknown builtin module");
- }
- return result;
-}
-
-TypeCheckingCache* Session::getTypeCheckingCache()
-{
- return static_cast<TypeCheckingCache*>(m_typeCheckingCache.get());
-}
-
-Session::BuiltinModuleInfo Session::getBuiltinModuleInfo(slang::BuiltinModuleName name)
-{
- Session::BuiltinModuleInfo result;
-
- result.name = getBuiltinModuleNameStr(name);
-
- switch (name)
- {
- case slang::BuiltinModuleName::Core:
- result.languageScope = coreLanguageScope;
- break;
- case slang::BuiltinModuleName::GLSL:
- result.name = "glsl";
- result.languageScope = glslLanguageScope;
- break;
- default:
- SLANG_UNEXPECTED("Unknown builtin module");
- }
- return result;
-}
-
-SlangResult Session::compileCoreModule(slang::CompileCoreModuleFlags compileFlags)
-{
- return compileBuiltinModule(slang::BuiltinModuleName::Core, compileFlags);
-}
-
-void Session::getBuiltinModuleSource(StringBuilder& sb, slang::BuiltinModuleName moduleName)
-{
- switch (moduleName)
- {
- case slang::BuiltinModuleName::Core:
- sb << (const char*)getCoreLibraryCode()->getBufferPointer()
- << (const char*)getHLSLLibraryCode()->getBufferPointer()
- << (const char*)getAutodiffLibraryCode()->getBufferPointer();
- break;
- case slang::BuiltinModuleName::GLSL:
- sb << (const char*)getGLSLLibraryCode()->getBufferPointer();
- break;
- }
-}
-
-SlangResult Session::compileBuiltinModule(
- slang::BuiltinModuleName moduleName,
- slang::CompileCoreModuleFlags compileFlags)
-{
- SLANG_AST_BUILDER_RAII(m_builtinLinkage->getASTBuilder());
-
-#ifdef _DEBUG
- time_t beginTime = 0;
- if (moduleName == slang::BuiltinModuleName::Core)
- {
- // Print a message in debug builds to notice the user that compiling the core module
- // can take a while.
- time(&beginTime);
- fprintf(stderr, "Compiling core module on debug build, this can take a while.\n");
- }
-#endif
- BuiltinModuleInfo builtinModuleInfo = getBuiltinModuleInfo(moduleName);
- auto moduleNameObj = m_builtinLinkage->getNamePool()->getName(builtinModuleInfo.name);
- if (m_builtinLinkage->mapNameToLoadedModules.tryGetValue(moduleNameObj))
- {
- // Already have the builtin module loaded
- return SLANG_FAIL;
- }
-
- StringBuilder moduleSrcBuilder;
- getBuiltinModuleSource(moduleSrcBuilder, moduleName);
-
- // TODO(JS): Could make this return a SlangResult as opposed to exception
- auto moduleSrcBlob = StringBlob::moveCreate(moduleSrcBuilder.produceString());
- Module* compiledModule = nullptr;
- addBuiltinSource(
- builtinModuleInfo.languageScope,
- builtinModuleInfo.name,
- moduleSrcBlob,
- compiledModule);
-
- if (moduleName == slang::BuiltinModuleName::Core)
- {
- // We need to retain this AST so that we can use it in other code
- // (Note that the `Scope` type does not retain the AST it points to)
- coreModules.add(compiledModule);
- }
-
- if (compileFlags & slang::CompileCoreModuleFlag::WriteDocumentation)
- {
- // Load config file first.
- String configText;
- if (SLANG_FAILED(File::readAllText("config.txt", configText)))
- {
- fprintf(
- stderr,
- "Error writing documentation: config file not found on current working "
- "directory.\n");
- }
- else
- {
- writeCoreModuleDoc(configText);
- }
- }
-
- finalizeSharedASTBuilder();
-
-#ifdef _DEBUG
- if (moduleName == slang::BuiltinModuleName::Core)
- {
- time_t endTime;
- time(&endTime);
- fprintf(stderr, "Compiling core module took %.2f seconds.\n", difftime(endTime, beginTime));
- }
-#endif
- return SLANG_OK;
-}
-
-SlangResult Session::loadCoreModule(const void* coreModule, size_t coreModuleSizeInBytes)
-{
- return loadBuiltinModule(slang::BuiltinModuleName::Core, coreModule, coreModuleSizeInBytes);
-}
-
-SlangResult Session::loadBuiltinModule(
- slang::BuiltinModuleName moduleName,
- const void* moduleData,
- size_t sizeInBytes)
-{
- SLANG_PROFILE;
-
-
- SLANG_AST_BUILDER_RAII(m_builtinLinkage->getASTBuilder());
-
- BuiltinModuleInfo builtinModuleInfo = getBuiltinModuleInfo(moduleName);
- auto nameObj = m_builtinLinkage->getNamePool()->getName(builtinModuleInfo.name);
- if (m_builtinLinkage->mapNameToLoadedModules.containsKey(nameObj))
- {
- // Already have a core module loaded
- return SLANG_FAIL;
- }
-
- // Make a file system to read it from
- ComPtr<ISlangFileSystemExt> fileSystem;
- SLANG_RETURN_ON_FAIL(loadArchiveFileSystem(moduleData, sizeInBytes, fileSystem));
-
- // Let's try loading serialized modules and adding them
- Module* module = nullptr;
- SLANG_RETURN_ON_FAIL(_readBuiltinModule(
- fileSystem,
- builtinModuleInfo.languageScope,
- builtinModuleInfo.name,
- module));
-
- if (moduleName == slang::BuiltinModuleName::Core)
- {
- // We need to retain this AST so that we can use it in other code
- // (Note that the `Scope` type does not retain the AST it points to)
- coreModules.add(module);
- }
-
- finalizeSharedASTBuilder();
- return SLANG_OK;
-}
-
-SlangResult Session::saveCoreModule(SlangArchiveType archiveType, ISlangBlob** outBlob)
-{
- return saveBuiltinModule(slang::BuiltinModuleName::Core, archiveType, outBlob);
-}
-
-SlangResult Session::saveBuiltinModule(
- slang::BuiltinModuleName moduleTag,
- SlangArchiveType archiveType,
- ISlangBlob** outBlob)
-{
- // If no builtin modules have been loaded, then there is
- // nothing to save, and we fail immediately.
- //
- if (m_builtinLinkage->mapNameToLoadedModules.getCount() == 0)
- {
- return SLANG_FAIL;
- }
-
- // The module will need to be looked up by its name, and
- // will also be serialized out to a path with a matching name.
- //
- BuiltinModuleInfo moduleInfo = getBuiltinModuleInfo(moduleTag);
- const char* moduleName = moduleInfo.name;
-
- // If we cannot find a loaded module in the linkage with
- // the appropriate name, then for some reason it hasn't
- // been loaded, and we fail.
- //
- RefPtr<Module> module;
- m_builtinLinkage->mapNameToLoadedModules.tryGetValue(
- getNameObj(UnownedStringSlice(moduleName)),
- module);
- if (!module)
- {
- return SLANG_FAIL;
- }
-
- // AST serialization needs access to an AST builder, so
- // we establish a current builder for the duration of
- // the serialization process.
- //
- SLANG_AST_BUILDER_RAII(m_builtinLinkage->getASTBuilder());
-
- // The serialized module will be represented as a logical
- // file in an archive, so we create a logical file system
- // to represent that archive.
- //
- ComPtr<ISlangMutableFileSystem> fileSystem;
- SLANG_RETURN_ON_FAIL(createArchiveFileSystem(archiveType, fileSystem));
- //
- // The created file system must support the `IArchiveFileSystem`
- // interface (since we created it with `createArchiveFileSystem`).
- //
- auto archiveFileSystem = as<IArchiveFileSystem>(fileSystem);
- if (!archiveFileSystem)
- {
- return SLANG_FAIL;
- }
-
- // The output file name that we'll write to in that file system
- // is just the builtin module name with a `.slang-module` suffix.
- //
- StringBuilder moduleFileName;
- moduleFileName << moduleName << ".slang-module";
-
- // The module serialization step has some options that we need
- // to configure appropriately.
- //
- SerialContainerUtil::WriteOptions options;
- //
- // We want builtin modules to be saved with their source location
- // information.
- //
- // And in order to work with source locations, the serialization
- // process will also need access to the source manager that
- // can translate locations into their humane format.
- //
- options.sourceManagerToUseWhenSerializingSourceLocs = m_builtinLinkage->getSourceManager();
-
- // At this point we can finally delegate down to the next level,
- // which handles the serialization of a Slang module into a
- // byte stream.
- //
- OwnedMemoryStream stream(FileAccess::Write);
- SLANG_RETURN_ON_FAIL(SerialContainerUtil::write(module, options, &stream));
- auto contents = stream.getContents();
-
- // Once the stream that represents the module has been written, we can
- // write it to a file in the logical file system.
- //
- // TODO(tfoley): why can't the file system let us open the file for output?
- //
- SLANG_RETURN_ON_FAIL(fileSystem->saveFile(
- moduleFileName.getBuffer(),
- contents.getBuffer(),
- contents.getCount()));
-
- // And finally, we can ask the archive file system to serialize itself
- // out as a blob of bytes, which yields the final serialized representation
- // of the module.
- //
- SLANG_RETURN_ON_FAIL(archiveFileSystem->storeArchive(
- // The `true` here indicates that the blob that gets created should own
- // its content, independent from the file system object itself; otherwise
- // the file system might return a blob that shares storage with itself.
- true,
- outBlob));
-
- return SLANG_OK;
-}
-
-SlangResult Session::_readBuiltinModule(
- ISlangFileSystem* fileSystem,
- Scope* scope,
- String moduleName,
- Module*& outModule)
-{
- // Get the name of the module
- StringBuilder moduleFilename;
- moduleFilename << moduleName << ".slang-module";
-
- // Load it
- ComPtr<ISlangBlob> fileContents;
- SLANG_RETURN_ON_FAIL(fileSystem->loadFile(moduleFilename.getBuffer(), fileContents.writeRef()));
-
- RIFF::RootChunk const* rootChunk = RIFF::RootChunk::getFromBlob(fileContents);
- if (!rootChunk)
- {
- return SLANG_FAIL;
- }
-
- Linkage* linkage = getBuiltinLinkage();
- SourceManager* sourceManager = getBuiltinSourceManager();
- NamePool* sessionNamePool = &namePool;
-
- auto moduleChunk = ModuleChunk::find(rootChunk);
- if (!moduleChunk)
- return SLANG_FAIL;
-
- SHA1::Digest moduleDigest = moduleChunk->getDigest();
-
- auto irChunk = moduleChunk->findIR();
- if (!irChunk)
- return SLANG_FAIL;
-
- auto astChunk = moduleChunk->findAST();
- if (!astChunk)
- return SLANG_FAIL;
-
- // Source location information is stored as a distinct
- // chunk from the IR and AST, so we need to search for
- // that chunk and then set up the information for use
- // in the IR and AST deserialization (if we find anything).
- //
- RefPtr<SerialSourceLocReader> sourceLocReader;
- if (auto debugChunk = DebugChunk::find(moduleChunk))
- {
- SLANG_RETURN_ON_FAIL(
- readSourceLocationsFromDebugChunk(debugChunk, sourceManager, sourceLocReader));
- }
-
- // At this point we create the `Module` object that will
- // represent the builtin module we are reading, although
- // it is still possible that deserialization will fail
- // at one of the following steps.
- //
- auto astBuilder = linkage->getASTBuilder();
- RefPtr<Module> module(new Module(linkage, astBuilder));
- module->setName(moduleName);
- module->setDigest(moduleDigest);
-
-
- // Next, we set about deserializing the AST representation
- // of the module.
- //
- auto moduleDecl = readSerializedModuleAST(
- linkage,
- astBuilder,
- nullptr, // no sink
- fileContents,
- astChunk,
- sourceLocReader,
- SourceLoc());
- if (!moduleDecl)
- {
- return SLANG_FAIL;
- }
- moduleDecl->module = module;
- module->setModuleDecl(moduleDecl);
-
- // After the AST module has been read in, we next look
- // to deserialize the IR module.
- //
- RefPtr<IRModule> irModule;
- SLANG_RETURN_ON_FAIL(readSerializedModuleIR(irChunk, this, sourceLocReader, irModule));
-
- irModule->setName(module->getNameObj());
- module->setIRModule(irModule);
-
- // Put in the loaded module map
- linkage->mapNameToLoadedModules.add(sessionNamePool->getName(moduleName), module);
-
-
- // Add the resulting code to the appropriate scope
- if (!scope->containerDecl)
- {
- // We are the first chunk of code to be loaded for this scope
- scope->containerDecl = moduleDecl;
- }
- else
- {
- // We need to create a new scope to link into the whole thing
- auto subScope = linkage->getASTBuilder()->create<Scope>();
- subScope->containerDecl = moduleDecl;
- subScope->nextSibling = scope->nextSibling;
- scope->nextSibling = subScope;
- }
-
- outModule = module.get();
-
- return SLANG_OK;
-}
-
-SLANG_NO_THROW SlangResult SLANG_MCALL
-Session::queryInterface(SlangUUID const& uuid, void** outObject)
-{
- if (uuid == Session::getTypeGuid())
- {
- addReference();
- *outObject = static_cast<Session*>(this);
- return SLANG_OK;
- }
-
- if (uuid == ISlangUnknown::getTypeGuid() || uuid == IGlobalSession::getTypeGuid())
- {
- addReference();
- *outObject = static_cast<slang::IGlobalSession*>(this);
- return SLANG_OK;
- }
-
- return SLANG_E_NO_INTERFACE;
-}
-
-static size_t _getStructureSize(const uint8_t* src)
-{
- size_t size = 0;
- ::memcpy(&size, src, sizeof(size_t));
- return size;
-}
-
-template<typename T>
-static T makeFromSizeVersioned(const uint8_t* src)
-{
- // The structure size must be size_t
- SLANG_COMPILE_TIME_ASSERT(sizeof(((T*)src)->structureSize) == sizeof(size_t));
-
- // The structureSize field *must* be the first element of T
- // Ideally would use SLANG_COMPILE_TIME_ASSERT, but that doesn't work on gcc.
- // Can't just assert, because determined to be a constant expression
- {
- auto offset = SLANG_OFFSET_OF(T, structureSize);
- SLANG_ASSERT(offset == 0);
- // Needed because offset is only 'used' by an assert
- SLANG_UNUSED(offset);
- }
-
- // The source size is held in the first element of T, and will be in the first bytes of src.
- const size_t srcSize = _getStructureSize(src);
- const size_t dstSize = sizeof(T);
-
- // If they are the same size, and appropriate alignment we can just cast and return
- if (srcSize == dstSize && (size_t(src) & (alignof(T) - 1)) == 0)
- {
- return *(const T*)src;
- }
-
- // Assumes T can default constructed sensibly
- T dst;
-
- // It's structure size should be setup and should be dstSize
- SLANG_ASSERT(dst.structureSize == dstSize);
-
- // The size to copy is the minimum on the two sizes
- const auto copySize = std::min(srcSize, dstSize);
- ::memcpy(&dst, src, copySize);
-
- // The final struct size is the destination size
- dst.structureSize = dstSize;
-
- return dst;
-}
-
-SLANG_NO_THROW SlangResult SLANG_MCALL
-Session::createSession(slang::SessionDesc const& inDesc, slang::ISession** outSession)
-{
- RefPtr<ASTBuilder> astBuilder(new ASTBuilder(m_sharedASTBuilder, "Session::astBuilder"));
- slang::SessionDesc desc = makeFromSizeVersioned<slang::SessionDesc>((uint8_t*)&inDesc);
-
- RefPtr<Linkage> linkage = new Linkage(this, astBuilder, getBuiltinLinkage());
-
- if (desc.skipSPIRVValidation)
- {
- linkage->m_optionSet.set(CompilerOptionName::SkipSPIRVValidation, true);
- }
-
- {
- std::lock_guard<std::mutex> lock(m_typeCheckingCacheMutex);
- if (m_typeCheckingCache)
- linkage->m_typeCheckingCache =
- new TypeCheckingCache(*static_cast<TypeCheckingCache*>(m_typeCheckingCache.get()));
- }
-
- Int searchPathCount = desc.searchPathCount;
- for (Int ii = 0; ii < searchPathCount; ++ii)
- {
- linkage->addSearchPath(desc.searchPaths[ii]);
- }
-
- Int macroCount = desc.preprocessorMacroCount;
- for (Int ii = 0; ii < macroCount; ++ii)
- {
- auto& macro = desc.preprocessorMacros[ii];
- linkage->addPreprocessorDefine(macro.name, macro.value);
- }
-
- if (desc.fileSystem)
- {
- linkage->setFileSystem(desc.fileSystem);
- }
-
- if (desc.structureSize >= offsetof(slang::SessionDesc, enableEffectAnnotations))
- {
- linkage->m_optionSet.set(
- CompilerOptionName::EnableEffectAnnotations,
- desc.enableEffectAnnotations);
- }
-
- linkage->m_optionSet.load(desc.compilerOptionEntryCount, desc.compilerOptionEntries);
-
- if (!linkage->m_optionSet.hasOption(CompilerOptionName::MatrixLayoutColumn) &&
- !linkage->m_optionSet.hasOption(CompilerOptionName::MatrixLayoutRow))
- linkage->setMatrixLayoutMode(desc.defaultMatrixLayoutMode);
-
- {
- const Int targetCount = desc.targetCount;
- const uint8_t* targetDescPtr = reinterpret_cast<const uint8_t*>(desc.targets);
- for (Int ii = 0; ii < targetCount; ++ii, targetDescPtr += _getStructureSize(targetDescPtr))
- {
- const auto targetDesc = makeFromSizeVersioned<slang::TargetDesc>(targetDescPtr);
- linkage->addTarget(targetDesc);
- }
- }
-
- // If any target requires debug info, then we will need to enable debug info when lowering to
- // target-agnostic IR. The target-agnostic IR will only include debug info if the linkage IR
- // options specify that it should, so make sure the linkage debug info level is greater than or
- // equal to that of any target.
- DebugInfoLevel linkageDebugInfoLevel = linkage->m_optionSet.getDebugInfoLevel();
- for (auto target : linkage->targets)
- linkageDebugInfoLevel =
- Math::Max(linkageDebugInfoLevel, target->getOptionSet().getDebugInfoLevel());
- linkage->m_optionSet.set(CompilerOptionName::DebugInformation, linkageDebugInfoLevel);
-
- // Add any referenced modules to the linkage
- for (auto& option : linkage->m_optionSet.options)
- {
- if (option.key != CompilerOptionName::ReferenceModule)
- continue;
- for (auto& path : option.value)
- {
- DiagnosticSink sink;
- ComPtr<IArtifact> artifact;
- SlangResult result = createArtifactFromReferencedModule(
- path.stringValue,
- SourceLoc{},
- &sink,
- artifact.writeRef());
- if (SLANG_FAILED(result))
- {
- sink.diagnose(SourceLoc{}, Diagnostics::unableToReadFile, path.stringValue);
- return result;
- }
- linkage->m_libModules.add(artifact);
- }
- }
-
- *outSession = asExternal(linkage.detach());
- return SLANG_OK;
-}
-
-SLANG_NO_THROW SlangResult SLANG_MCALL
-Session::createCompileRequest(slang::ICompileRequest** outCompileRequest)
-{
- auto req = new EndToEndCompileRequest(this);
-
- // Give it a ref (for output)
- req->addRef();
- // Check it is what we think it should be
- SLANG_ASSERT(req->debugGetReferenceCount() == 1);
-
- *outCompileRequest = req;
- return SLANG_OK;
-}
-
-SLANG_NO_THROW SlangProfileID SLANG_MCALL Session::findProfile(char const* name)
-{
- return SlangProfileID(Slang::Profile::lookUp(name).raw);
-}
-
-SLANG_NO_THROW SlangCapabilityID SLANG_MCALL Session::findCapability(char const* name)
-{
- return SlangCapabilityID(Slang::findCapabilityName(UnownedTerminatedStringSlice(name)));
-}
-
-SLANG_NO_THROW void SLANG_MCALL
-Session::setDownstreamCompilerPath(SlangPassThrough inPassThrough, char const* path)
-{
- PassThroughMode passThrough = PassThroughMode(inPassThrough);
- SLANG_ASSERT(
- int(passThrough) > int(PassThroughMode::None) &&
- int(passThrough) < int(PassThroughMode::CountOf));
-
- if (m_downstreamCompilerPaths[int(passThrough)] != path)
- {
- // Make access redetermine compiler
- resetDownstreamCompiler(passThrough);
- // Set the path
- m_downstreamCompilerPaths[int(passThrough)] = path;
- }
-}
-
-SLANG_NO_THROW void SLANG_MCALL
-Session::setDownstreamCompilerPrelude(SlangPassThrough inPassThrough, char const* prelude)
-{
- PassThroughMode downstreamCompiler = PassThroughMode(inPassThrough);
- SLANG_ASSERT(
- int(downstreamCompiler) > int(PassThroughMode::None) &&
- int(downstreamCompiler) < int(PassThroughMode::CountOf));
- const SourceLanguage sourceLanguage =
- getDefaultSourceLanguageForDownstreamCompiler(downstreamCompiler);
- setLanguagePrelude(SlangSourceLanguage(sourceLanguage), prelude);
-}
-
-SLANG_NO_THROW void SLANG_MCALL
-Session::getDownstreamCompilerPrelude(SlangPassThrough inPassThrough, ISlangBlob** outPrelude)
-{
- PassThroughMode downstreamCompiler = PassThroughMode(inPassThrough);
- SLANG_ASSERT(
- int(downstreamCompiler) > int(PassThroughMode::None) &&
- int(downstreamCompiler) < int(PassThroughMode::CountOf));
- const SourceLanguage sourceLanguage =
- getDefaultSourceLanguageForDownstreamCompiler(downstreamCompiler);
- getLanguagePrelude(SlangSourceLanguage(sourceLanguage), outPrelude);
-}
-
-SLANG_NO_THROW void SLANG_MCALL
-Session::setLanguagePrelude(SlangSourceLanguage inSourceLanguage, char const* prelude)
-{
- SourceLanguage sourceLanguage = SourceLanguage(inSourceLanguage);
- SLANG_ASSERT(
- int(sourceLanguage) > int(SourceLanguage::Unknown) &&
- int(sourceLanguage) < int(SourceLanguage::CountOf));
-
- SLANG_ASSERT(sourceLanguage != SourceLanguage::Unknown);
-
- if (sourceLanguage != SourceLanguage::Unknown)
- {
- m_languagePreludes[int(sourceLanguage)] = prelude;
- }
-}
-
-SLANG_NO_THROW void SLANG_MCALL
-Session::getLanguagePrelude(SlangSourceLanguage inSourceLanguage, ISlangBlob** outPrelude)
-{
- SourceLanguage sourceLanguage = SourceLanguage(inSourceLanguage);
-
- *outPrelude = nullptr;
- if (sourceLanguage != SourceLanguage::Unknown)
- {
- SLANG_ASSERT(
- int(sourceLanguage) > int(SourceLanguage::Unknown) &&
- int(sourceLanguage) < int(SourceLanguage::CountOf));
- *outPrelude =
- Slang::StringUtil::createStringBlob(m_languagePreludes[int(sourceLanguage)]).detach();
- }
-}
-
-SLANG_NO_THROW const char* SLANG_MCALL Session::getBuildTagString()
-{
- return ::Slang::getBuildTagString();
-}
-
-SLANG_NO_THROW SlangResult SLANG_MCALL Session::setDefaultDownstreamCompiler(
- SlangSourceLanguage sourceLanguage,
- SlangPassThrough defaultCompiler)
-{
- if (DownstreamCompilerInfo::canCompile(defaultCompiler, sourceLanguage))
- {
- m_defaultDownstreamCompilers[int(sourceLanguage)] = PassThroughMode(defaultCompiler);
- return SLANG_OK;
- }
- return SLANG_FAIL;
-}
-
-SlangPassThrough SLANG_MCALL
-Session::getDefaultDownstreamCompiler(SlangSourceLanguage inSourceLanguage)
-{
- SLANG_ASSERT(inSourceLanguage >= 0 && inSourceLanguage < SLANG_SOURCE_LANGUAGE_COUNT_OF);
- auto sourceLanguage = SourceLanguage(inSourceLanguage);
- return SlangPassThrough(m_defaultDownstreamCompilers[int(sourceLanguage)]);
-}
-
-void Session::setDownstreamCompilerForTransition(
- SlangCompileTarget source,
- SlangCompileTarget target,
- SlangPassThrough compiler)
-{
- if (compiler == SLANG_PASS_THROUGH_NONE)
- {
- // Removing the transition means a default can be used
- m_codeGenTransitionMap.removeTransition(CodeGenTarget(source), CodeGenTarget(target));
- }
- else
- {
- m_codeGenTransitionMap.addTransition(
- CodeGenTarget(source),
- CodeGenTarget(target),
- PassThroughMode(compiler));
- }
-}
-
-SlangPassThrough Session::getDownstreamCompilerForTransition(
- SlangCompileTarget inSource,
- SlangCompileTarget inTarget)
-{
- const CodeGenTarget source = CodeGenTarget(inSource);
- const CodeGenTarget target = CodeGenTarget(inTarget);
-
- if (m_codeGenTransitionMap.hasTransition(source, target))
- {
- return (SlangPassThrough)m_codeGenTransitionMap.getTransition(source, target);
- }
-
- const auto desc = ArtifactDescUtil::makeDescForCompileTarget(inTarget);
-
- // Special case host-callable
- if ((desc.kind == ArtifactKind::HostCallable) &&
- (source == CodeGenTarget::CSource || source == CodeGenTarget::CPPSource))
- {
- // We prefer LLVM if it's available
- if (const auto llvm = getOrLoadDownstreamCompiler(PassThroughMode::LLVM, nullptr))
- {
- return SLANG_PASS_THROUGH_LLVM;
- }
- }
-
- // Use the legacy 'sourceLanguage' default mechanism.
- // This says nothing about the target type, so it is *assumed* the target type is possible
- // If not it will fail when trying to compile to an unknown target
- const SourceLanguage sourceLanguage =
- (SourceLanguage)TypeConvertUtil::getSourceLanguageFromTarget(inSource);
- if (sourceLanguage != SourceLanguage::Unknown)
- {
- return getDefaultDownstreamCompiler(SlangSourceLanguage(sourceLanguage));
- }
-
- // Unknwon
- return SLANG_PASS_THROUGH_NONE;
-}
-
-IDownstreamCompiler* Session::getDownstreamCompiler(CodeGenTarget source, CodeGenTarget target)
-{
- PassThroughMode compilerType = (PassThroughMode)getDownstreamCompilerForTransition(
- SlangCompileTarget(source),
- SlangCompileTarget(target));
- return getOrLoadDownstreamCompiler(compilerType, nullptr);
-}
-
-SLANG_NO_THROW SlangResult SLANG_MCALL Session::setSPIRVCoreGrammar(char const* jsonPath)
-{
- if (!jsonPath)
- {
- spirvCoreGrammarInfo = SPIRVCoreGrammarInfo::getEmbeddedVersion();
- SLANG_ASSERT(spirvCoreGrammarInfo);
- }
- else
- {
- SourceManager* sourceManager = getBuiltinSourceManager();
- SLANG_ASSERT(sourceManager);
- DiagnosticSink sink(sourceManager, Lexer::sourceLocationLexer);
-
- String contents;
- const auto readRes = File::readAllText(jsonPath, contents);
- if (SLANG_FAILED(readRes))
- {
- sink.diagnose(SourceLoc{}, Diagnostics::unableToReadFile, jsonPath);
- return readRes;
- }
- const auto pathInfo = PathInfo::makeFromString(jsonPath);
- const auto sourceFile = sourceManager->createSourceFileWithString(pathInfo, contents);
- const auto sourceView = sourceManager->createSourceView(sourceFile, nullptr, SourceLoc());
- spirvCoreGrammarInfo = SPIRVCoreGrammarInfo::loadFromJSON(*sourceView, sink);
- }
- return spirvCoreGrammarInfo ? SLANG_OK : SLANG_FAIL;
-}
-
-struct ParsedCommandLineData : public ISlangUnknown, public ComObject
-{
- SLANG_COM_OBJECT_IUNKNOWN_ALL
-
- ISlangUnknown* getInterface(const Slang::Guid& guid)
- {
- if (guid == ISlangUnknown::getTypeGuid())
- return this;
- return nullptr;
- }
- List<SerializedOptionsData> options;
- List<slang::TargetDesc> targets;
-};
-
-SLANG_NO_THROW SlangResult SLANG_MCALL Session::parseCommandLineArguments(
- int argc,
- const char* const* argv,
- slang::SessionDesc* outDesc,
- ISlangUnknown** outAllocation)
-{
- if (outDesc->structureSize < sizeof(slang::SessionDesc))
- return SLANG_E_BUFFER_TOO_SMALL;
- RefPtr<ParsedCommandLineData> outData = new ParsedCommandLineData();
- RefPtr<EndToEndCompileRequest> tempReq = new EndToEndCompileRequest(this);
- tempReq->processCommandLineArguments(argv, argc);
- outData->options.setCount(1 + tempReq->getLinkage()->targets.getCount());
- int optionDataIndex = 0;
- SerializedOptionsData& optionData = outData->options[optionDataIndex];
- optionDataIndex++;
- tempReq->getOptionSet().serialize(&optionData);
- tempReq->m_optionSetForDefaultTarget.serialize(&optionData);
- for (auto target : tempReq->getLinkage()->targets)
- {
- slang::TargetDesc tdesc;
- SerializedOptionsData& targetOptionData = outData->options[optionDataIndex];
- optionDataIndex++;
- tempReq->getTargetOptionSet(target).serialize(&targetOptionData);
- tdesc.compilerOptionEntryCount = (uint32_t)targetOptionData.entries.getCount();
- tdesc.compilerOptionEntries = targetOptionData.entries.getBuffer();
- outData->targets.add(tdesc);
- }
- outDesc->compilerOptionEntryCount = (uint32_t)optionData.entries.getCount();
- outDesc->compilerOptionEntries = optionData.entries.getBuffer();
- outDesc->targetCount = outData->targets.getCount();
- outDesc->targets = outData->targets.getBuffer();
- *outAllocation = outData.get();
- outData->addRef();
- return SLANG_OK;
-}
-
-SLANG_NO_THROW SlangResult SLANG_MCALL
-Session::getSessionDescDigest(slang::SessionDesc* sessionDesc, ISlangBlob** outBlob)
-{
- ComPtr<slang::ISession> tempSession;
- createSession(*sessionDesc, tempSession.writeRef());
- auto linkage = static_cast<Linkage*>(tempSession.get());
- DigestBuilder<SHA1> digestBuilder;
- linkage->buildHash(digestBuilder, -1);
- auto blob = digestBuilder.finalize().toBlob();
- *outBlob = blob.detach();
- return SLANG_OK;
-}
-
Profile getEffectiveProfile(EntryPoint* entryPoint, TargetRequest* target)
{
auto entryPointProfile = entryPoint->getProfile();
@@ -1375,6885 +186,4 @@ Profile getEffectiveProfile(EntryPoint* entryPoint, TargetRequest* target)
return effectiveProfile;
}
-
-//
-
-Linkage::Linkage(Session* session, ASTBuilder* astBuilder, Linkage* builtinLinkage)
- : m_session(session)
- , m_retainedSession(session)
- , m_sourceManager(&m_defaultSourceManager)
- , m_astBuilder(astBuilder)
- , m_cmdLineContext(new CommandLineContext())
- , m_stringSlicePool(StringSlicePool::Style::Default)
-{
- namePool = session->getNamePool();
-
- m_defaultSourceManager.initialize(session->getBuiltinSourceManager(), nullptr);
-
- setFileSystem(nullptr);
-
- // Copy of the built in linkages modules
- if (builtinLinkage)
- {
- for (const auto& nameToMod : builtinLinkage->mapNameToLoadedModules)
- mapNameToLoadedModules.add(nameToMod);
- }
-
- m_semanticsForReflection = new SharedSemanticsContext(this, nullptr, nullptr);
-}
-
-SharedSemanticsContext* Linkage::getSemanticsForReflection()
-{
- return m_semanticsForReflection.get();
-}
-
-ISlangUnknown* Linkage::getInterface(const Guid& guid)
-{
- if (guid == ISlangUnknown::getTypeGuid() || guid == ISession::getTypeGuid())
- return asExternal(this);
-
- return nullptr;
-}
-
-Linkage::~Linkage()
-{
- // Upstream type checking cache.
- if (m_typeCheckingCache)
- {
- auto globalSession = getSessionImpl();
- std::lock_guard<std::mutex> lock(globalSession->m_typeCheckingCacheMutex);
- if (!globalSession->m_typeCheckingCache ||
- globalSession->getTypeCheckingCache()->resolvedOperatorOverloadCache.getCount() <
- getTypeCheckingCache()->resolvedOperatorOverloadCache.getCount())
- {
- globalSession->m_typeCheckingCache = m_typeCheckingCache;
- getTypeCheckingCache()->version++;
- }
- destroyTypeCheckingCache();
- }
-}
-
-SearchDirectoryList& Linkage::getSearchDirectories()
-{
- auto list = m_optionSet.getArray(CompilerOptionName::Include);
- if (list.getCount() != searchDirectoryCache.searchDirectories.getCount())
- {
- searchDirectoryCache.searchDirectories.clear();
- for (auto dir : list)
- searchDirectoryCache.searchDirectories.add(SearchDirectory(dir.stringValue));
- }
- return searchDirectoryCache;
-}
-
-TypeCheckingCache* Linkage::getTypeCheckingCache()
-{
- if (!m_typeCheckingCache)
- {
- m_typeCheckingCache = new TypeCheckingCache();
- }
- return static_cast<TypeCheckingCache*>(m_typeCheckingCache.get());
-}
-
-void Linkage::destroyTypeCheckingCache()
-{
- m_typeCheckingCache = nullptr;
-}
-
-SLANG_NO_THROW slang::IGlobalSession* SLANG_MCALL Linkage::getGlobalSession()
-{
- return asExternal(getSessionImpl());
-}
-
-void Linkage::addTarget(slang::TargetDesc const& desc)
-{
- SLANG_AST_BUILDER_RAII(getASTBuilder());
-
- auto targetIndex = addTarget(CodeGenTarget(desc.format));
- auto target = targets[targetIndex];
-
- auto& optionSet = target->getOptionSet();
- optionSet.inheritFrom(m_optionSet);
-
- optionSet.set(CompilerOptionName::FloatingPointMode, FloatingPointMode(desc.floatingPointMode));
- optionSet.addTargetFlags(desc.flags);
- optionSet.setProfile(Profile(desc.profile));
- optionSet.set(CompilerOptionName::LineDirectiveMode, LineDirectiveMode(desc.lineDirectiveMode));
- optionSet.set(CompilerOptionName::GLSLForceScalarLayout, desc.forceGLSLScalarBufferLayout);
-
- CompilerOptionSet targetOptions;
- targetOptions.load(desc.compilerOptionEntryCount, desc.compilerOptionEntries);
- optionSet.overrideWith(targetOptions);
-}
-
-#if 0
- SLANG_NO_THROW SlangInt SLANG_MCALL Linkage::getTargetCount()
- {
- return targets.getCount();
- }
-
- SLANG_NO_THROW slang::ITarget* SLANG_MCALL Linkage::getTargetByIndex(SlangInt index)
- {
- if (index < 0) return nullptr;
- if (index >= targets.getCount()) return nullptr;
- return asExternal(targets[index]);
- }
-#endif
-
-static void outputExceptionDiagnostic(
- const AbortCompilationException& exception,
- DiagnosticSink& sink,
- slang::IBlob** outDiagnostics)
-{
- sink.diagnoseRaw(Severity::Error, exception.Message.getUnownedSlice());
- sink.getBlobIfNeeded(outDiagnostics);
-}
-
-static void outputExceptionDiagnostic(
- const Exception& exception,
- DiagnosticSink& sink,
- slang::IBlob** outDiagnostics)
-{
- try
- {
- sink.diagnoseRaw(Severity::Internal, exception.Message.getUnownedSlice());
- }
- catch (const AbortCompilationException&)
- {
- // Catch and ignore the AbortCompilationException that diagnoseRaw throws
- // for Internal severity to prevent exception leak from loadModule
- }
- sink.getBlobIfNeeded(outDiagnostics);
-}
-
-static void outputExceptionDiagnostic(DiagnosticSink& sink, slang::IBlob** outDiagnostics)
-{
- try
- {
- sink.diagnoseRaw(Severity::Fatal, "An unknown exception occurred");
- }
- catch (const AbortCompilationException&)
- {
- // Catch and ignore the AbortCompilationException that diagnoseRaw throws
- // for Fatal severity to prevent exception leak from loadModule
- }
- sink.getBlobIfNeeded(outDiagnostics);
-}
-
-SLANG_NO_THROW slang::IModule* SLANG_MCALL
-Linkage::loadModule(const char* moduleName, slang::IBlob** outDiagnostics)
-{
- SLANG_AST_BUILDER_RAII(getASTBuilder());
-
- DiagnosticSink sink(getSourceManager(), Lexer::sourceLocationLexer);
- applySettingsToDiagnosticSink(&sink, &sink, m_optionSet);
-
- if (isInLanguageServer())
- {
- sink.setFlags(DiagnosticSink::Flag::HumaneLoc | DiagnosticSink::Flag::LanguageServer);
- }
-
- try
- {
- auto name = getNamePool()->getName(moduleName);
-
- auto module = findOrImportModule(name, SourceLoc(), &sink);
- sink.getBlobIfNeeded(outDiagnostics);
-
- return asExternal(module);
- }
- catch (const AbortCompilationException& e)
- {
- outputExceptionDiagnostic(e, sink, outDiagnostics);
- return nullptr;
- }
- catch (const Exception& e)
- {
- outputExceptionDiagnostic(e, sink, outDiagnostics);
- return nullptr;
- }
- catch (...)
- {
- outputExceptionDiagnostic(sink, outDiagnostics);
- return nullptr;
- }
-}
-
-slang::IModule* Linkage::loadModuleFromBlob(
- const char* moduleName,
- const char* path,
- slang::IBlob* source,
- ModuleBlobType blobType,
- slang::IBlob** outDiagnostics)
-{
- SLANG_AST_BUILDER_RAII(getASTBuilder());
-
- DiagnosticSink sink(getSourceManager(), Lexer::sourceLocationLexer);
- applySettingsToDiagnosticSink(&sink, &sink, m_optionSet);
-
- if (isInLanguageServer())
- {
- sink.setFlags(DiagnosticSink::Flag::HumaneLoc | DiagnosticSink::Flag::LanguageServer);
- }
-
-
- try
- {
- auto getDigestStr = [](auto x)
- {
- DigestBuilder<SHA1> digestBuilder;
- digestBuilder.append(x);
- return digestBuilder.finalize().toString();
- };
-
- String moduleNameStr = moduleName;
- if (!moduleName)
- moduleNameStr = getDigestStr(source);
-
- auto name = getNamePool()->getName(moduleNameStr);
- RefPtr<LoadedModule> loadedModule;
- if (mapNameToLoadedModules.tryGetValue(name, loadedModule))
- {
- return loadedModule;
- }
- String pathStr = path;
- if (pathStr.getLength() == 0)
- {
- // If path is empty, use a digest from source as path.
- pathStr = getDigestStr(source);
- }
- auto pathInfo = PathInfo::makeFromString(pathStr);
- if (File::exists(pathStr))
- {
- String cannonicalPath;
- if (SLANG_SUCCEEDED(Path::getCanonical(pathStr, cannonicalPath)))
- {
- pathInfo = PathInfo::makeNormal(pathStr, cannonicalPath);
- }
- }
- RefPtr<Module> module =
- loadModuleImpl(name, pathInfo, source, SourceLoc(), &sink, nullptr, blobType);
- sink.getBlobIfNeeded(outDiagnostics);
- return asExternal(module.get());
- }
- catch (const AbortCompilationException& e)
- {
- outputExceptionDiagnostic(e, sink, outDiagnostics);
- return nullptr;
- }
- catch (const Exception& e)
- {
- outputExceptionDiagnostic(e, sink, outDiagnostics);
- return nullptr;
- }
- catch (...)
- {
- outputExceptionDiagnostic(sink, outDiagnostics);
- return nullptr;
- }
-}
-
-SLANG_NO_THROW slang::IModule* SLANG_MCALL Linkage::loadModuleFromSource(
- const char* moduleName,
- const char* path,
- slang::IBlob* source,
- slang::IBlob** outDiagnostics)
-{
- return loadModuleFromBlob(moduleName, path, source, ModuleBlobType::Source, outDiagnostics);
-}
-
-SLANG_NO_THROW slang::IModule* SLANG_MCALL Linkage::loadModuleFromSourceString(
- const char* moduleName,
- const char* path,
- const char* source,
- slang::IBlob** outDiagnostics)
-{
- auto sourceBlob = StringBlob::create(UnownedStringSlice(source));
- return loadModuleFromSource(moduleName, path, sourceBlob.get(), outDiagnostics);
-}
-
-SLANG_NO_THROW slang::IModule* SLANG_MCALL Linkage::loadModuleFromIRBlob(
- const char* moduleName,
- const char* path,
- slang::IBlob* source,
- slang::IBlob** outDiagnostics)
-{
- return loadModuleFromBlob(moduleName, path, source, ModuleBlobType::IR, outDiagnostics);
-}
-
-SLANG_NO_THROW SlangResult SLANG_MCALL Linkage::loadModuleInfoFromIRBlob(
- slang::IBlob* source,
- SlangInt& outModuleVersion,
- const char*& outModuleCompilerVersion,
- const char*& outModuleName)
-{
- // We start by reading the content of the file as
- // an in-memory RIFF container.
- //
- auto rootChunk = RIFF::RootChunk::getFromBlob(source);
- if (!rootChunk)
- {
- return SLANG_FAIL;
- }
-
- auto moduleChunk = ModuleChunk::find(rootChunk);
- if (!moduleChunk)
- {
- return SLANG_FAIL;
- }
-
- auto irChunk = moduleChunk->findIR();
- if (!irChunk)
- {
- return SLANG_FAIL;
- }
-
- RefPtr<IRModule> irModule;
- String compilerVersion;
- UInt version;
- String name;
- SLANG_RETURN_ON_FAIL(readSerializedModuleInfo(irChunk, compilerVersion, version, name));
- const auto compilerVersionSlice = m_stringSlicePool.addAndGetSlice(compilerVersion);
- const auto nameSlice = m_stringSlicePool.addAndGetSlice(name);
- outModuleCompilerVersion = compilerVersionSlice.begin();
- outModuleName = nameSlice.begin();
- outModuleVersion = SlangInt(version);
-
- return SLANG_OK;
-}
-
-SLANG_NO_THROW SlangResult SLANG_MCALL Linkage::createCompositeComponentType(
- slang::IComponentType* const* componentTypes,
- SlangInt componentTypeCount,
- slang::IComponentType** outCompositeComponentType,
- ISlangBlob** outDiagnostics)
-{
- if (outCompositeComponentType == nullptr)
- return SLANG_E_INVALID_ARG;
-
- SLANG_AST_BUILDER_RAII(getASTBuilder());
-
- // Attempting to create a "composite" of just one component type should
- // just return the component type itself, to avoid redundant work.
- //
- if (componentTypeCount == 1)
- {
- auto componentType = componentTypes[0];
- componentType->addRef();
- *outCompositeComponentType = componentType;
- return SLANG_OK;
- }
-
- DiagnosticSink sink(getSourceManager(), Lexer::sourceLocationLexer);
- applySettingsToDiagnosticSink(&sink, &sink, m_optionSet);
-
- List<RefPtr<ComponentType>> childComponents;
- for (Int cc = 0; cc < componentTypeCount; ++cc)
- {
- childComponents.add(asInternal(componentTypes[cc]));
- }
-
- RefPtr<ComponentType> composite = CompositeComponentType::create(this, childComponents);
-
- sink.getBlobIfNeeded(outDiagnostics);
-
- *outCompositeComponentType = asExternal(composite.detach());
- return SLANG_OK;
-}
-
-SLANG_NO_THROW slang::TypeReflection* SLANG_MCALL Linkage::specializeType(
- slang::TypeReflection* inUnspecializedType,
- slang::SpecializationArg const* specializationArgs,
- SlangInt specializationArgCount,
- ISlangBlob** outDiagnostics)
-{
- SLANG_AST_BUILDER_RAII(getASTBuilder());
-
- auto unspecializedType = asInternal(inUnspecializedType);
-
- List<Type*> typeArgs;
-
- for (Int ii = 0; ii < specializationArgCount; ++ii)
- {
- auto& arg = specializationArgs[ii];
- if (arg.kind != slang::SpecializationArg::Kind::Type)
- return nullptr;
-
- typeArgs.add(asInternal(arg.type));
- }
-
- DiagnosticSink sink(getSourceManager(), Lexer::sourceLocationLexer);
- auto specializedType =
- specializeType(unspecializedType, typeArgs.getCount(), typeArgs.getBuffer(), &sink);
- sink.getBlobIfNeeded(outDiagnostics);
-
- return asExternal(specializedType);
-}
-
-DeclRef<GenericDecl> getGenericParentDeclRef(
- ASTBuilder* astBuilder,
- SemanticsVisitor* visitor,
- DeclRef<Decl> declRef)
-{
- // Create substituted parent decl ref.
- auto decl = declRef.getDecl();
-
- while (decl && !as<GenericDecl>(decl))
- {
- decl = decl->parentDecl;
- }
-
- if (!decl)
- {
- // No generic parent
- return DeclRef<GenericDecl>();
- }
-
- auto genericDecl = as<GenericDecl>(decl);
- auto genericDeclRef =
- createDefaultSubstitutionsIfNeeded(astBuilder, visitor, DeclRef(genericDecl))
- .as<GenericDecl>();
- return substituteDeclRef(SubstitutionSet(declRef), astBuilder, genericDeclRef)
- .as<GenericDecl>();
-}
-
-bool Linkage::isSpecialized(DeclRef<Decl> declRef)
-{
- // For now, we only support two 'states': fully applied or not at all.
- // If we add support for partial specialization, we will need to update this logic.
- //
- // If it's not specialized, then declRef will be the one with default substitutions.
- //
- SemanticsVisitor visitor(getSemanticsForReflection());
-
- auto decl = declRef.getDecl();
- while (decl && !as<GenericDecl>(decl))
- {
- decl = decl->parentDecl;
- }
-
- if (!decl)
- return true; // no generics => always specialized
-
- auto defaultArgs = getDefaultSubstitutionArgs(getASTBuilder(), &visitor, as<GenericDecl>(decl));
- auto currentArgs =
- SubstitutionSet(declRef).findGenericAppDeclRef(as<GenericDecl>(decl))->getArgs();
-
- if (defaultArgs.getCount() != currentArgs.getCount()) // should really never happen.
- return true;
-
- for (Index i = 0; i < defaultArgs.getCount(); ++i)
- {
- if (defaultArgs[i] != currentArgs[i])
- return true;
- }
-
- return false;
-}
-
-bool isFuncGeneric(DeclRef<Decl> declRef)
-{
- if (auto funcDecl = as<FuncDecl>(declRef.getDecl()))
- {
- if (funcDecl->parentDecl && as<GenericDecl>(funcDecl->parentDecl))
- {
- return true;
- }
- }
-
- return false;
-}
-
-DeclRef<Decl> Linkage::specializeWithArgTypes(
- Expr* funcExpr,
- List<Type*> argTypes,
- DiagnosticSink* sink)
-{
- SemanticsVisitor visitor(getSemanticsForReflection());
- SemanticsVisitor::ExprLocalScope scope;
- visitor = visitor.withSink(sink).withExprLocalScope(&scope);
-
- SLANG_AST_BUILDER_RAII(getASTBuilder());
-
- if (auto declRefFuncExpr = as<DeclRefExpr>(funcExpr))
- {
- if (isFuncGeneric(declRefFuncExpr->declRef) && !isSpecialized(declRefFuncExpr->declRef))
- {
- if (auto genericDeclRef = getGenericParentDeclRef(
- getCurrentASTBuilder(),
- &visitor,
- declRefFuncExpr->declRef))
- {
- auto genericDeclRefExpr = getCurrentASTBuilder()->create<DeclRefExpr>();
- genericDeclRefExpr->declRef = genericDeclRef;
- funcExpr = genericDeclRefExpr;
- }
- }
- }
-
- List<Expr*> argExprs;
- for (SlangInt aa = 0; aa < argTypes.getCount(); ++aa)
- {
- auto argType = argTypes[aa];
-
- // Create an 'empty' expr with the given type. Ideally, the expression itself should not
- // matter only its checked type.
- //
- auto argExpr = getCurrentASTBuilder()->create<VarExpr>();
- argExpr->type = argType;
- argExpr->type.isLeftValue = true;
- argExprs.add(argExpr);
- }
-
- // Construct invoke expr.
- auto invokeExpr = getCurrentASTBuilder()->create<InvokeExpr>();
- invokeExpr->functionExpr = funcExpr;
- invokeExpr->arguments = argExprs;
-
- auto checkedInvokeExpr = visitor.CheckInvokeExprWithCheckedOperands(invokeExpr);
-
- return as<DeclRefExpr>(as<InvokeExpr>(checkedInvokeExpr)->functionExpr)->declRef;
-}
-
-
-DeclRef<Decl> Linkage::specializeGeneric(
- DeclRef<Decl> declRef,
- List<Expr*> argExprs,
- DiagnosticSink* sink)
-{
- SLANG_AST_BUILDER_RAII(getASTBuilder());
- SLANG_ASSERT(declRef);
-
- SemanticsVisitor visitor(getSemanticsForReflection());
- visitor = visitor.withSink(sink);
-
- auto genericDeclRef = getGenericParentDeclRef(getASTBuilder(), &visitor, declRef);
-
- DeclRefExpr* declRefExpr = getASTBuilder()->create<DeclRefExpr>();
- declRefExpr->declRef = genericDeclRef;
-
- GenericAppExpr* genericAppExpr = getASTBuilder()->create<GenericAppExpr>();
- genericAppExpr->functionExpr = declRefExpr;
- genericAppExpr->arguments = argExprs;
-
- auto specializedDeclRef =
- as<DeclRefExpr>(visitor.checkGenericAppWithCheckedArgs(genericAppExpr))->declRef;
-
- return specializedDeclRef;
-}
-
-SLANG_NO_THROW slang::TypeLayoutReflection* SLANG_MCALL Linkage::getTypeLayout(
- slang::TypeReflection* inType,
- SlangInt targetIndex,
- slang::LayoutRules rules,
- ISlangBlob** outDiagnostics)
-{
- SLANG_AST_BUILDER_RAII(getASTBuilder());
-
- auto type = asInternal(inType);
-
- if (targetIndex < 0 || targetIndex >= targets.getCount())
- return nullptr;
-
- auto target = targets[targetIndex];
-
- // TODO: We need a way to pass through the layout rules
- // that the user requested (e.g., constant buffers vs.
- // structured buffer rules). Right now the API only
- // exposes a single case, so this isn't a big deal.
- //
- SLANG_UNUSED(rules);
-
- auto typeLayout = target->getTypeLayout(type, rules);
-
- // TODO: We currently don't have a path for capturing
- // errors that occur during layout (e.g., types that
- // are invalid because of target-specific layout constraints).
- //
- SLANG_UNUSED(outDiagnostics);
-
- return asExternal(typeLayout);
-}
-
-SLANG_NO_THROW slang::TypeReflection* SLANG_MCALL Linkage::getContainerType(
- slang::TypeReflection* inType,
- slang::ContainerType containerType,
- ISlangBlob** outDiagnostics)
-{
- SLANG_AST_BUILDER_RAII(getASTBuilder());
-
- auto type = asInternal(inType);
-
- Type* containerTypeReflection = nullptr;
- ContainerTypeKey key = {inType, containerType};
- if (!m_containerTypes.tryGetValue(key, containerTypeReflection))
- {
- switch (containerType)
- {
- case slang::ContainerType::ConstantBuffer:
- {
- SemanticsVisitor visitor(getSemanticsForReflection());
- auto layoutType = getASTBuilder()->getDefaultLayoutType();
- Type* cbType = visitor.getConstantBufferType(type, layoutType);
- containerTypeReflection = cbType;
- }
- break;
- case slang::ContainerType::ParameterBlock:
- {
- ParameterBlockType* pbType = getASTBuilder()->getParameterBlockType(type);
- containerTypeReflection = pbType;
- }
- break;
- case slang::ContainerType::StructuredBuffer:
- {
- HLSLStructuredBufferType* sbType = getASTBuilder()->getStructuredBufferType(type);
- containerTypeReflection = sbType;
- }
- break;
- case slang::ContainerType::UnsizedArray:
- {
- ArrayExpressionType* arrType = getASTBuilder()->getArrayType(type, nullptr);
- containerTypeReflection = arrType;
- }
- break;
- default:
- containerTypeReflection = type;
- break;
- }
-
- m_containerTypes.add(key, containerTypeReflection);
- }
-
- SLANG_UNUSED(outDiagnostics);
-
- return asExternal(containerTypeReflection);
-}
-
-SLANG_NO_THROW slang::TypeReflection* SLANG_MCALL Linkage::getDynamicType()
-{
- SLANG_AST_BUILDER_RAII(getASTBuilder());
-
- return asExternal(getASTBuilder()->getSharedASTBuilder()->getDynamicType());
-}
-
-SLANG_NO_THROW SlangResult SLANG_MCALL
-Linkage::getTypeRTTIMangledName(slang::TypeReflection* type, ISlangBlob** outNameBlob)
-{
- SLANG_AST_BUILDER_RAII(getASTBuilder());
-
- auto internalType = asInternal(type);
- if (auto declRefType = as<DeclRefType>(internalType))
- {
- auto name = getMangledName(m_astBuilder, declRefType->getDeclRef());
- Slang::ComPtr<ISlangBlob> blob = Slang::StringUtil::createStringBlob(name);
- *outNameBlob = blob.detach();
- return SLANG_OK;
- }
- return SLANG_FAIL;
-}
-
-SLANG_NO_THROW SlangResult SLANG_MCALL Linkage::getTypeConformanceWitnessMangledName(
- slang::TypeReflection* type,
- slang::TypeReflection* interfaceType,
- ISlangBlob** outNameBlob)
-{
- SLANG_AST_BUILDER_RAII(getASTBuilder());
-
- auto subType = asInternal(type);
- auto supType = asInternal(interfaceType);
- auto name = getMangledNameForConformanceWitness(m_astBuilder, subType, supType);
- Slang::ComPtr<ISlangBlob> blob = Slang::StringUtil::createStringBlob(name);
- *outNameBlob = blob.detach();
- return SLANG_OK;
-}
-
-SLANG_NO_THROW SlangResult SLANG_MCALL Linkage::getTypeConformanceWitnessSequentialID(
- slang::TypeReflection* type,
- slang::TypeReflection* interfaceType,
- uint32_t* outId)
-{
- SLANG_AST_BUILDER_RAII(getASTBuilder());
-
- auto subType = asInternal(type);
- auto supType = asInternal(interfaceType);
-
- if (!subType || !supType)
- return SLANG_FAIL;
-
- auto name = getMangledNameForConformanceWitness(m_astBuilder, subType, supType);
- auto interfaceName = getMangledTypeName(m_astBuilder, supType);
- uint32_t resultIndex = 0;
- if (mapMangledNameToRTTIObjectIndex.tryGetValue(name, resultIndex))
- {
- if (outId)
- *outId = resultIndex;
- return SLANG_OK;
- }
- auto idAllocator = mapInterfaceMangledNameToSequentialIDCounters.tryGetValue(interfaceName);
- if (!idAllocator)
- {
- mapInterfaceMangledNameToSequentialIDCounters[interfaceName] = 0;
- idAllocator = mapInterfaceMangledNameToSequentialIDCounters.tryGetValue(interfaceName);
- }
- resultIndex = (*idAllocator);
- ++(*idAllocator);
- mapMangledNameToRTTIObjectIndex[name] = resultIndex;
- if (outId)
- *outId = resultIndex;
- return SLANG_OK;
-}
-
-SLANG_NO_THROW SlangResult SLANG_MCALL Linkage::getDynamicObjectRTTIBytes(
- slang::TypeReflection* type,
- slang::TypeReflection* interfaceType,
- uint32_t* outBuffer,
- uint32_t bufferSize)
-{
- // Slang RTTI header format:
- // byte 0-7: pointer to RTTI struct describing the type. (not used for now, set to 1 for valid
- // types, and 0 to represent null).
- // byte 8-11: 32-bit sequential ID of the type conformance witness.
- // byte 12-15: unused.
-
- if (bufferSize < 16)
- return SLANG_E_BUFFER_TOO_SMALL;
-
- SLANG_AST_BUILDER_RAII(getASTBuilder());
-
- SLANG_RETURN_ON_FAIL(getTypeConformanceWitnessSequentialID(type, interfaceType, outBuffer + 2));
-
- // Make the RTTI part non zero.
- outBuffer[0] = 1;
-
- return SLANG_OK;
-}
-
-SLANG_NO_THROW SlangResult SLANG_MCALL Linkage::createTypeConformanceComponentType(
- slang::TypeReflection* type,
- slang::TypeReflection* interfaceType,
- slang::ITypeConformance** outConformanceComponentType,
- SlangInt conformanceIdOverride,
- ISlangBlob** outDiagnostics)
-{
- if (outConformanceComponentType == nullptr)
- return SLANG_E_INVALID_ARG;
-
- SLANG_AST_BUILDER_RAII(getASTBuilder());
-
- RefPtr<TypeConformance> result;
- DiagnosticSink sink;
- applySettingsToDiagnosticSink(&sink, &sink, m_optionSet);
-
- try
- {
- SemanticsVisitor visitor(getSemanticsForReflection());
- visitor = visitor.withSink(&sink);
-
- auto witness = visitor.isSubtype(
- (Slang::Type*)type,
- (Slang::Type*)interfaceType,
- IsSubTypeOptions::None);
- if (auto subtypeWitness = as<SubtypeWitness>(witness))
- {
- result = new TypeConformance(this, subtypeWitness, conformanceIdOverride, &sink);
- }
- }
- catch (...)
- {
- }
- sink.getBlobIfNeeded(outDiagnostics);
- bool success = (result != nullptr);
- *outConformanceComponentType = result.detach();
- return success ? SLANG_OK : SLANG_FAIL;
-}
-
-SLANG_NO_THROW SlangResult SLANG_MCALL
-Linkage::createCompileRequest(SlangCompileRequest** outCompileRequest)
-{
- auto compileRequest = new EndToEndCompileRequest(this);
- compileRequest->addRef();
- *outCompileRequest = asExternal(compileRequest);
- return SLANG_OK;
-}
-
-SLANG_NO_THROW SlangInt SLANG_MCALL Linkage::getLoadedModuleCount()
-{
- return loadedModulesList.getCount();
-}
-
-SLANG_NO_THROW slang::IModule* SLANG_MCALL Linkage::getLoadedModule(SlangInt index)
-{
- if (index >= 0 && index < loadedModulesList.getCount())
- return loadedModulesList[index].get();
- return nullptr;
-}
-
-void Linkage::buildHash(DigestBuilder<SHA1>& builder, SlangInt targetIndex)
-{
- // Add the Slang compiler version to the hash
- auto version = String(getBuildTagString());
- builder.append(version);
-
- // Add compiler options, including search path, preprocessor includes, etc.
- m_optionSet.buildHash(builder);
-
- auto addTargetDigest = [&](TargetRequest* targetReq)
- {
- targetReq->getOptionSet().buildHash(builder);
-
- const PassThroughMode passThroughMode =
- getDownstreamCompilerRequiredForTarget(targetReq->getTarget());
- const SourceLanguage sourceLanguage =
- getDefaultSourceLanguageForDownstreamCompiler(passThroughMode);
-
- // Add prelude for the given downstream compiler.
- ComPtr<ISlangBlob> prelude;
- getGlobalSession()->getLanguagePrelude(
- (SlangSourceLanguage)sourceLanguage,
- prelude.writeRef());
- if (prelude)
- {
- builder.append(prelude);
- }
-
- // TODO: Downstream compilers (specifically dxc) can currently #include additional
- // dependencies. This is currently the case for NVAPI headers included in the prelude. These
- // dependencies are currently not picked up by the shader cache which is a significant
- // issue. This can only be fixed by running the preprocessor in the slang compiler so dxc
- // (or any other downstream compiler for that matter) isn't resolving any includes
- // implicitly.
-
- // Add the downstream compiler version (if it exists) to the hash
- auto downstreamCompiler =
- getSessionImpl()->getOrLoadDownstreamCompiler(passThroughMode, nullptr);
- if (downstreamCompiler)
- {
- ComPtr<ISlangBlob> versionString;
- if (SLANG_SUCCEEDED(downstreamCompiler->getVersionString(versionString.writeRef())))
- {
- builder.append(versionString);
- }
- }
- };
-
- // Add the target specified by targetIndex
- if (targetIndex == -1)
- {
- // -1 means all targets.
- for (auto targetReq : targets)
- {
- addTargetDigest(targetReq);
- }
- }
- else
- {
- auto targetReq = targets[targetIndex];
- addTargetDigest(targetReq);
- }
-}
-
-SlangResult Linkage::addSearchPath(char const* path)
-{
- m_optionSet.add(CompilerOptionName::Include, String(path));
- return SLANG_OK;
-}
-
-SlangResult Linkage::addPreprocessorDefine(char const* name, char const* value)
-{
- CompilerOptionValue val;
- val.kind = CompilerOptionValueKind::String;
- val.stringValue = name;
- val.stringValue2 = value;
- m_optionSet.add(CompilerOptionName::MacroDefine, val);
- return SLANG_OK;
-}
-
-SlangResult Linkage::setMatrixLayoutMode(SlangMatrixLayoutMode mode)
-{
- m_optionSet.setMatrixLayoutMode((MatrixLayoutMode)mode);
- return SLANG_OK;
-}
-
-//
-// TargetRequest
-//
-
-TargetRequest::TargetRequest(Linkage* linkage, CodeGenTarget format)
- : linkage(linkage)
-{
- optionSet = linkage->m_optionSet;
- optionSet.add(CompilerOptionName::Target, format);
-}
-
-TargetRequest::TargetRequest(const TargetRequest& other)
- : RefObject(), linkage(other.linkage), optionSet(other.optionSet)
-{
-}
-
-
-Session* TargetRequest::getSession()
-{
- return linkage->getSessionImpl();
-}
-
-HLSLToVulkanLayoutOptions* TargetRequest::getHLSLToVulkanLayoutOptions()
-{
- if (!hlslToVulkanOptions)
- {
- hlslToVulkanOptions = new HLSLToVulkanLayoutOptions();
- hlslToVulkanOptions->loadFromOptionSet(optionSet);
- }
- return hlslToVulkanOptions.get();
-}
-
-void TargetRequest::setTargetCaps(CapabilitySet capSet)
-{
- cookedCapabilities = capSet;
-}
-
-CapabilitySet TargetRequest::getTargetCaps()
-{
- if (!cookedCapabilities.isEmpty())
- return cookedCapabilities;
-
- // The full `CapabilitySet` for the target will be computed
- // from the combination of the code generation format, and
- // the profile.
- //
- // Note: the preofile might have been set in a way that is
- // inconsistent with the output code format of SPIR-V, but
- // a profile of Direct3D Shader Model 5.1. In those cases,
- // the format should always override the implications in
- // the profile.
- //
- // TODO: This logic isn't currently taking int account
- // the information in the profile, because the current
- // `CapabilityAtom`s that we support don't include any
- // of the details there (e.g., the shader model versions).
- //
- // Eventually, we'd want to have a rich set of capability
- // atoms, so that most of the information about what operations
- // are available where can be directly encoded on the declarations.
-
- List<CapabilityName> atoms;
-
- // If the user specified a explicit profile, we should pull
- // a corresponding atom representing the target version from the profile.
- CapabilitySet profileCaps = optionSet.getProfile().getCapabilityName();
-
- bool isGLSLTarget = false;
- switch (getTarget())
- {
- case CodeGenTarget::GLSL:
- isGLSLTarget = true;
- atoms.add(CapabilityName::glsl);
- break;
- case CodeGenTarget::SPIRV:
- case CodeGenTarget::SPIRVAssembly:
- if (getOptionSet().shouldEmitSPIRVDirectly())
- {
- // Default to SPIRV 1.5 if the user has not specified a target version.
- bool hasTargetVersionAtom = false;
- if (!profileCaps.isEmpty())
- {
- profileCaps.join(CapabilitySet(CapabilityName::spirv_1_0));
- for (auto profileCapAtomSet : profileCaps.getAtomSets())
- {
- for (auto atom : profileCapAtomSet)
- {
- if (isTargetVersionAtom(asAtom(atom)))
- {
- atoms.add((CapabilityName)atom);
- hasTargetVersionAtom = true;
- }
- }
- }
- }
- if (!hasTargetVersionAtom)
- {
- atoms.add(CapabilityName::spirv_1_5);
- }
- // If the user specified any SPIR-V extensions in the profile,
- // pull them in.
- for (auto profileCapAtomSet : profileCaps.getAtomSets())
- {
- for (auto atom : profileCapAtomSet)
- {
- if (isSpirvExtensionAtom(asAtom(atom)))
- {
- atoms.add((CapabilityName)atom);
- hasTargetVersionAtom = true;
- }
- }
- }
- }
- else
- {
- isGLSLTarget = true;
- atoms.add(CapabilityName::glsl);
- profileCaps.addSpirvVersionFromOtherAsGlslSpirvVersion(profileCaps);
- }
- break;
-
- case CodeGenTarget::HLSL:
- case CodeGenTarget::DXBytecode:
- case CodeGenTarget::DXBytecodeAssembly:
- case CodeGenTarget::DXIL:
- case CodeGenTarget::DXILAssembly:
- atoms.add(CapabilityName::hlsl);
- break;
-
- case CodeGenTarget::CSource:
- atoms.add(CapabilityName::c);
- break;
-
- case CodeGenTarget::CPPSource:
- case CodeGenTarget::PyTorchCppBinding:
- case CodeGenTarget::HostExecutable:
- case CodeGenTarget::ShaderSharedLibrary:
- case CodeGenTarget::HostSharedLibrary:
- case CodeGenTarget::HostHostCallable:
- case CodeGenTarget::ShaderHostCallable:
- atoms.add(CapabilityName::cpp);
- break;
-
- case CodeGenTarget::CUDASource:
- case CodeGenTarget::PTX:
- atoms.add(CapabilityName::cuda);
- break;
-
- case CodeGenTarget::Metal:
- case CodeGenTarget::MetalLib:
- case CodeGenTarget::MetalLibAssembly:
- atoms.add(CapabilityName::metal);
- break;
-
- case CodeGenTarget::WGSLSPIRV:
- case CodeGenTarget::WGSLSPIRVAssembly:
- case CodeGenTarget::WGSL:
- atoms.add(CapabilityName::wgsl);
- break;
-
- default:
- break;
- }
-
- CapabilitySet targetCap = CapabilitySet(atoms);
-
- if (profileCaps.atLeastOneSetImpliedInOther(targetCap) ==
- CapabilitySet::ImpliesReturnFlags::Implied)
- targetCap.join(profileCaps);
-
- for (auto atomVal : optionSet.getArray(CompilerOptionName::Capability))
- {
- CapabilitySet toAdd;
- switch (atomVal.kind)
- {
- case CompilerOptionValueKind::Int:
- toAdd = CapabilitySet(CapabilityName(atomVal.intValue));
- break;
- case CompilerOptionValueKind::String:
- toAdd = CapabilitySet(findCapabilityName(atomVal.stringValue.getUnownedSlice()));
- break;
- }
-
- if (isGLSLTarget)
- targetCap.addSpirvVersionFromOtherAsGlslSpirvVersion(toAdd);
-
- if (!targetCap.isIncompatibleWith(toAdd))
- targetCap.join(toAdd);
- }
-
- cookedCapabilities = targetCap;
-
- SLANG_ASSERT(!cookedCapabilities.isInvalid());
-
- return cookedCapabilities;
-}
-
-
-TypeLayout* TargetRequest::getTypeLayout(Type* type, slang::LayoutRules rules)
-{
- SLANG_AST_BUILDER_RAII(getLinkage()->getASTBuilder());
-
- // TODO: We are not passing in a `ProgramLayout` here, although one
- // is nominally required to establish the global ordering of
- // generic type parameters, which might be referenced from field types.
- //
- // The solution here is to make sure that the reflection data for
- // uses of global generic/existential types does *not* include any
- // kind of index in that global ordering, and just refers to the
- // parameter instead (leaving the user to figure out how that
- // maps to the ordering via some API on the program layout).
- //
- auto layoutContext = getInitialLayoutContextForTarget(this, nullptr, rules);
-
- RefPtr<TypeLayout> result;
- auto key = TypeLayoutKey{type, rules};
- if (getTypeLayouts().tryGetValue(key, result))
- return result.Ptr();
- result = createTypeLayout(layoutContext, type);
- getTypeLayouts()[key] = result;
- return result.Ptr();
-}
-
-//
-// TranslationUnitRequest
-//
-
-TranslationUnitRequest::TranslationUnitRequest(FrontEndCompileRequest* compileRequest)
- : compileRequest(compileRequest)
-{
- module = new Module(compileRequest->getLinkage());
-}
-
-TranslationUnitRequest::TranslationUnitRequest(FrontEndCompileRequest* compileRequest, Module* m)
- : compileRequest(compileRequest), module(m), isChecked(true)
-{
- moduleName = getNamePool()->getName(m->getName());
-}
-
-Session* TranslationUnitRequest::getSession()
-{
- return compileRequest->getSession();
-}
-
-NamePool* TranslationUnitRequest::getNamePool()
-{
- return compileRequest->getNamePool();
-}
-
-SourceManager* TranslationUnitRequest::getSourceManager()
-{
- return compileRequest->getSourceManager();
-}
-
-Scope* TranslationUnitRequest::getLanguageScope()
-{
- Scope* languageScope = nullptr;
- switch (sourceLanguage)
- {
- case SourceLanguage::HLSL:
- languageScope = getSession()->hlslLanguageScope;
- break;
- case SourceLanguage::GLSL:
- languageScope = getSession()->glslLanguageScope;
- break;
- case SourceLanguage::Slang:
- default:
- languageScope = getSession()->slangLanguageScope;
- break;
- }
- return languageScope;
-}
-
-Dictionary<String, String> TranslationUnitRequest::getCombinedPreprocessorDefinitions()
-{
- Dictionary<String, String> combinedPreprocessorDefinitions;
- for (const auto& def : preprocessorDefinitions)
- combinedPreprocessorDefinitions.addIfNotExists(def);
- for (const auto& def : compileRequest->optionSet.getArray(CompilerOptionName::MacroDefine))
- combinedPreprocessorDefinitions.addIfNotExists(def.stringValue, def.stringValue2);
-
- // Define standard macros, if not already defined. This style assumes using `#if __SOME_VAR`
- // style, as in
- //
- // ```
- // #if __SLANG_COMPILER__
- // ```
- //
- // This choice is made because slang outputs a warning on using a variable in an #if if not
- // defined
- //
- // Of course this means using #ifndef/#ifdef/defined() is probably not appropraite with thes
- // variables.
- {
- // Used to identify level of HLSL language compatibility
- combinedPreprocessorDefinitions.addIfNotExists("__HLSL_VERSION", "2018");
-
- // Indicates this is being compiled by the slang *compiler*
- combinedPreprocessorDefinitions.addIfNotExists("__SLANG_COMPILER__", "1");
-
- // Set macro depending on source type
- switch (sourceLanguage)
- {
- case SourceLanguage::HLSL:
- // Used to indicate compiled as HLSL language
- combinedPreprocessorDefinitions.addIfNotExists("__HLSL__", "1");
- break;
- case SourceLanguage::Slang:
- // Used to indicate compiled as Slang language
- combinedPreprocessorDefinitions.addIfNotExists("__SLANG__", "1");
- break;
- default:
- break;
- }
-
- // If not set, define as 0.
- combinedPreprocessorDefinitions.addIfNotExists("__HLSL__", "0");
- combinedPreprocessorDefinitions.addIfNotExists("__SLANG__", "0");
- }
-
- return combinedPreprocessorDefinitions;
-}
-
-void TranslationUnitRequest::addSourceArtifact(IArtifact* sourceArtifact)
-{
- SLANG_ASSERT(sourceArtifact);
- m_sourceArtifacts.add(ComPtr<IArtifact>(sourceArtifact));
-}
-
-
-void TranslationUnitRequest::addSource(IArtifact* sourceArtifact, SourceFile* sourceFile)
-{
- SLANG_ASSERT(sourceArtifact && sourceFile);
- // Must be in sync!
- SLANG_ASSERT(m_sourceFiles.getCount() == m_sourceArtifacts.getCount());
-
- addSourceArtifact(sourceArtifact);
- _addSourceFile(sourceFile);
-}
-
-void TranslationUnitRequest::addIncludedSourceFileIfNotExist(SourceFile* sourceFile)
-{
- if (m_includedFileSet.contains(sourceFile))
- return;
-
- sourceFile->setIncludedFile();
- m_sourceFiles.add(sourceFile);
- m_includedFileSet.add(sourceFile);
-}
-
-PathInfo TranslationUnitRequest::_findSourcePathInfo(IArtifact* artifact)
-{
- auto pathRep = findRepresentation<IPathArtifactRepresentation>(artifact);
-
- if (pathRep && pathRep->getPathType() == SLANG_PATH_TYPE_FILE)
- {
- // See if we have a unique identity set with the path
- if (const auto uniqueIdentity = pathRep->getUniqueIdentity())
- {
- return PathInfo::makeNormal(pathRep->getPath(), uniqueIdentity);
- }
-
- // If we couldn't get a unique identity, just use the path
- return PathInfo::makePath(pathRep->getPath());
- }
-
- // If there isn't a path, we can try with the name
- const char* name = artifact->getName();
- if (name && name[0] != 0)
- {
- return PathInfo::makeFromString(name);
- }
-
- return PathInfo::makeUnknown();
-}
-
-SlangResult TranslationUnitRequest::requireSourceFiles()
-{
- SLANG_ASSERT(m_sourceFiles.getCount() <= m_sourceArtifacts.getCount());
-
- if (m_sourceFiles.getCount() == m_sourceArtifacts.getCount())
- {
- return SLANG_OK;
- }
-
- auto sink = compileRequest->getSink();
- SourceManager* sourceManager = compileRequest->getSourceManager();
-
- for (Index i = m_sourceFiles.getCount(); i < m_sourceArtifacts.getCount(); ++i)
- {
- IArtifact* artifact = m_sourceArtifacts[i];
-
- const PathInfo pathInfo = _findSourcePathInfo(artifact);
-
- SourceFile* sourceFile = nullptr;
- ComPtr<ISlangBlob> blob;
-
- // If we have a unique identity see if we have it already
- if (pathInfo.hasUniqueIdentity())
- {
- // See if this an already loaded source file
- sourceFile = sourceManager->findSourceFileRecursively(pathInfo.uniqueIdentity);
- // If we have a sourceFile see if it has a blob
- if (sourceFile)
- {
- blob = sourceFile->getContentBlob();
- }
- }
-
- // If we *don't* have a blob try and get a blob from the artifact
- if (!blob)
- {
- const SlangResult res = artifact->loadBlob(ArtifactKeep::Yes, blob.writeRef());
- if (SLANG_FAILED(res))
- {
- // Report couldn't load
- sink->diagnose(SourceLoc(), Diagnostics::cannotOpenFile, pathInfo.getName());
- return res;
- }
- }
-
- // If we don't have a blob on the artifact we can now add the one we have
- if (!findRepresentation<ISlangBlob>(artifact))
- {
- artifact->addRepresentationUnknown(blob);
- }
-
- // If we have a sourceFile check if it has contents, and set the blob if doesn't
- if (sourceFile)
- {
- if (!sourceFile->getContentBlob())
- {
- sourceFile->setContents(blob);
- }
- }
- else
- {
- // Create a new source file, using the pathInfo and blob
- sourceFile = sourceManager->createSourceFileWithBlob(pathInfo, blob);
- }
-
- auto uniqueIdentity = pathInfo.getMostUniqueIdentity();
- if (uniqueIdentity.getLength())
- sourceManager->addSourceFileIfNotExist(uniqueIdentity, sourceFile);
-
- // Finally add the source file
- _addSourceFile(sourceFile);
- }
-
- return SLANG_OK;
-}
-
-void TranslationUnitRequest::_addSourceFile(SourceFile* sourceFile)
-{
- m_sourceFiles.add(sourceFile);
-
- getModule()->addFileDependency(sourceFile);
- getModule()->getIncludedSourceFileMap().add(sourceFile, nullptr);
-}
-
-List<SourceFile*> const& TranslationUnitRequest::getSourceFiles()
-{
- return m_sourceFiles;
-}
-
-EndToEndCompileRequest::~EndToEndCompileRequest()
-{
- // Flush any writers associated with the request
- m_writers->flushWriters();
-
- m_linkage.setNull();
- m_frontEndReq.setNull();
-}
-
-static ISlangWriter* _getDefaultWriter(WriterChannel chan)
-{
- static FileWriter stdOut(stdout, WriterFlag::IsStatic | WriterFlag::IsUnowned);
- static FileWriter stdError(stderr, WriterFlag::IsStatic | WriterFlag::IsUnowned);
- static NullWriter nullWriter(WriterFlag::IsStatic | WriterFlag::IsConsole);
-
- switch (chan)
- {
- case WriterChannel::StdError:
- return &stdError;
- case WriterChannel::StdOutput:
- return &stdOut;
- case WriterChannel::Diagnostic:
- return &nullWriter;
- default:
- {
- SLANG_ASSERT(!"Unknown type");
- return &stdError;
- }
- }
-}
-
-void EndToEndCompileRequest::setWriter(WriterChannel chan, ISlangWriter* writer)
-{
- // If the user passed in null, we will use the default writer on that channel
- m_writers->setWriter(SlangWriterChannel(chan), writer ? writer : _getDefaultWriter(chan));
-
- // For diagnostic output, if the user passes in nullptr, we set on m_sink.writer as that enables
- // buffering on DiagnosticSink
- if (chan == WriterChannel::Diagnostic)
- {
- m_sink.writer = writer;
- }
-}
-
-SlangResult Linkage::loadFile(String const& path, PathInfo& outPathInfo, ISlangBlob** outBlob)
-{
- outPathInfo.type = PathInfo::Type::Unknown;
-
- SLANG_RETURN_ON_FAIL(m_fileSystemExt->loadFile(path.getBuffer(), outBlob));
-
- ComPtr<ISlangBlob> uniqueIdentity;
- // Get the unique identity
- if (SLANG_FAILED(
- m_fileSystemExt->getFileUniqueIdentity(path.getBuffer(), uniqueIdentity.writeRef())))
- {
- // We didn't get a unique identity, so go with just a found path
- outPathInfo.type = PathInfo::Type::FoundPath;
- outPathInfo.foundPath = path;
- }
- else
- {
- outPathInfo = PathInfo::makeNormal(path, StringUtil::getString(uniqueIdentity));
- }
- return SLANG_OK;
-}
-
-Expr* Linkage::parseTermString(String typeStr, Scope* scope)
-{
- // Create a SourceManager on the stack, so any allocations for 'SourceFile'/'SourceView' etc
- // will be cleaned up
- SourceManager localSourceManager;
- localSourceManager.initialize(getSourceManager(), nullptr);
-
- Slang::SourceFile* srcFile =
- localSourceManager.createSourceFileWithString(PathInfo::makeTypeParse(), typeStr);
-
- // We'll use a temporary diagnostic sink
- DiagnosticSink sink(&localSourceManager, nullptr);
-
- // RAII type to make make sure current SourceManager is restored after parse.
- // Use RAII - to make sure everything is reset even if an exception is thrown.
- struct ScopeReplaceSourceManager
- {
- ScopeReplaceSourceManager(Linkage* linkage, SourceManager* replaceManager)
- : m_linkage(linkage), m_originalSourceManager(linkage->getSourceManager())
- {
- linkage->setSourceManager(replaceManager);
- }
-
- ~ScopeReplaceSourceManager() { m_linkage->setSourceManager(m_originalSourceManager); }
-
- private:
- Linkage* m_linkage;
- SourceManager* m_originalSourceManager;
- };
-
- // We need to temporarily replace the SourceManager for this CompileRequest
- ScopeReplaceSourceManager scopeReplaceSourceManager(this, &localSourceManager);
-
- SourceLanguage sourceLanguage = SourceLanguage::Slang;
- SlangLanguageVersion languageVersion = m_optionSet.getLanguageVersion();
-
- auto tokens = preprocessSource(
- srcFile,
- &sink,
- nullptr,
- Dictionary<String, String>(),
- this,
- sourceLanguage,
- languageVersion);
-
- if (sourceLanguage == SourceLanguage::Unknown)
- sourceLanguage = SourceLanguage::Slang;
-
- return parseTermFromSourceFile(
- getASTBuilder(),
- tokens,
- &sink,
- scope,
- getNamePool(),
- sourceLanguage);
-}
-
-Type* checkProperType(Linkage* linkage, TypeExp typeExp, DiagnosticSink* sink);
-
-Type* ComponentType::getTypeFromString(String const& typeStr, DiagnosticSink* sink)
-{
- // If we've looked up this type name before,
- // then we can re-use it.
- //
- Type* type = nullptr;
- if (m_types.tryGetValue(typeStr, type))
- return type;
-
-
- // TODO(JS): For now just used the linkages ASTBuilder to keep on scope
- //
- // The parseTermString uses the linkage ASTBuilder for it's parsing.
- //
- // It might be possible to just create a temporary ASTBuilder - the worry though is
- // that the parsing sets a member variable in AST node to one of these scopes, and then
- // it become a dangling pointer. So for now we go with the linkages.
- auto astBuilder = getLinkage()->getASTBuilder();
-
- // Otherwise, we need to start looking in
- // the modules that were directly or
- // indirectly referenced.
- //
- Scope* scope = _getOrCreateScopeForLegacyLookup(astBuilder);
-
- auto linkage = getLinkage();
-
- SLANG_AST_BUILDER_RAII(linkage->getASTBuilder());
-
- Expr* typeExpr = linkage->parseTermString(typeStr, scope);
- SharedSemanticsContext sharedSemanticsContext(linkage, nullptr, sink);
- SemanticsVisitor visitor(&sharedSemanticsContext);
- type = visitor.TranslateTypeNode(typeExpr);
- auto typeOut = visitor.tryCoerceToProperType(TypeExp(type));
- if (typeOut.type)
- type = typeOut.type;
-
- if (type)
- {
- m_types[typeStr] = type;
- }
- return type;
-}
-
-Expr* ComponentType::findDeclFromString(String const& name, DiagnosticSink* sink)
-{
- // If we've looked up this type name before,
- // then we can re-use it.
- //
- Expr* result = nullptr;
- if (m_decls.tryGetValue(name, result))
- return result;
-
-
- // TODO(JS): For now just used the linkages ASTBuilder to keep on scope
- //
- // The parseTermString uses the linkage ASTBuilder for it's parsing.
- //
- // It might be possible to just create a temporary ASTBuilder - the worry though is
- // that the parsing sets a member variable in AST node to one of these scopes, and then
- // it become a dangling pointer. So for now we go with the linkages.
- auto astBuilder = getLinkage()->getASTBuilder();
-
- // Otherwise, we need to start looking in
- // the modules that were directly or
- // indirectly referenced.
- //
- Scope* scope = _getOrCreateScopeForLegacyLookup(astBuilder);
-
- auto linkage = getLinkage();
-
- SLANG_AST_BUILDER_RAII(linkage->getASTBuilder());
-
- Expr* expr = linkage->parseTermString(name, scope);
-
- SemanticsContext context(linkage->getSemanticsForReflection());
- context = context.allowStaticReferenceToNonStaticMember().withSink(sink);
-
- SemanticsVisitor visitor(context);
-
- auto checkedExpr = visitor.CheckTerm(expr);
-
- if (as<DeclRefExpr>(checkedExpr) || as<OverloadedExpr>(checkedExpr))
- {
- result = checkedExpr;
- }
-
- m_decls[name] = result;
- return result;
-}
-
-bool isSimpleName(String const& name)
-{
- for (char c : name)
- {
- if (!CharUtil::isAlphaOrDigit(c) && c != '_' && c != '$')
- return false;
- }
- return true;
-}
-
-Expr* ComponentType::findDeclFromStringInType(
- Type* type,
- String const& name,
- LookupMask mask,
- DiagnosticSink* sink)
-{
- // Only look up in the type if it is a DeclRefType
- if (!as<DeclRefType>(type))
- return nullptr;
-
- // TODO(JS): For now just used the linkages ASTBuilder to keep on scope
- //
- // The parseTermString uses the linkage ASTBuilder for it's parsing.
- //
- // It might be possible to just create a temporary ASTBuilder - the worry though is
- // that the parsing sets a member variable in AST node to one of these scopes, and then
- // it become a dangling pointer. So for now we go with the linkages.
- auto astBuilder = getLinkage()->getASTBuilder();
-
- // Otherwise, we need to start looking in
- // the modules that were directly or
- // indirectly referenced.
- //
- Scope* scope = _getOrCreateScopeForLegacyLookup(astBuilder);
-
- auto linkage = getLinkage();
-
- SLANG_AST_BUILDER_RAII(linkage->getASTBuilder());
-
- Expr* expr = nullptr;
-
- if (isSimpleName(name))
- {
- auto varExpr = astBuilder->create<VarExpr>();
- varExpr->scope = scope;
- varExpr->name = getLinkage()->getNamePool()->getName(name);
- expr = varExpr;
- }
- else
- {
- expr = linkage->parseTermString(name, scope);
- }
- SemanticsContext context(linkage->getSemanticsForReflection());
- context = context.allowStaticReferenceToNonStaticMember().withSink(sink);
-
- SemanticsVisitor visitor(context);
-
- GenericAppExpr* genericOuterExpr = nullptr;
- if (as<GenericAppExpr>(expr))
- {
- // Unwrap the generic application, and re-wrap it around the static-member expr
- genericOuterExpr = as<GenericAppExpr>(expr);
- expr = genericOuterExpr->functionExpr;
- }
-
- if (!as<VarExpr>(expr))
- return nullptr;
-
- auto rs = astBuilder->create<StaticMemberExpr>();
- auto typeExpr = astBuilder->create<SharedTypeExpr>();
- auto typetype = astBuilder->getOrCreate<TypeType>(type);
- typeExpr->type = typetype;
- rs->baseExpression = typeExpr;
- rs->name = as<VarExpr>(expr)->name;
-
- expr = rs;
-
- // If we have a generic-app expression, re-wrap the static-member expr
- if (genericOuterExpr)
- {
- genericOuterExpr->functionExpr = expr;
- expr = genericOuterExpr;
- }
-
- auto checkedTerm = visitor.CheckTerm(expr);
-
- // Check if checkedTerm is overloaded functions and avoid resolving if so
- // to preserve all function overloads with different signatures
- Expr* resolvedTerm = checkedTerm;
- if (auto overloadedExpr = as<OverloadedExpr>(checkedTerm))
- {
- // Check if all candidates are function references
- bool allAreFunctions = true;
- for (auto item : overloadedExpr->lookupResult2.items)
- {
- if (!as<FunctionDeclBase>(item.declRef.getDecl()))
- {
- allAreFunctions = false;
- break;
- }
- }
-
- // If not all are functions, resolve the overload as usual
- if (!allAreFunctions)
- {
- resolvedTerm = visitor.maybeResolveOverloadedExpr(checkedTerm, mask, sink);
- }
- }
- else
- {
- // Not overloaded, resolve as usual
- resolvedTerm = visitor.maybeResolveOverloadedExpr(checkedTerm, mask, sink);
- }
-
-
- if (auto overloadedExpr = as<OverloadedExpr>(resolvedTerm))
- {
- return overloadedExpr;
- }
- if (auto declRefExpr = as<DeclRefExpr>(resolvedTerm))
- {
- return declRefExpr;
- }
-
- return nullptr;
-}
-
-bool ComponentType::isSubType(Type* subType, Type* superType)
-{
- SemanticsContext context(getLinkage()->getSemanticsForReflection());
- SemanticsVisitor visitor(context);
-
- return (visitor.isSubtype(subType, superType, IsSubTypeOptions::None) != nullptr);
-}
-
-static void collectExportedConstantInContainer(
- Dictionary<String, IntVal*>& dict,
- ASTBuilder* builder,
- ContainerDecl* containerDecl)
-{
- for (auto varMember : containerDecl->getDirectMemberDeclsOfType<VarDeclBase>())
- {
- if (!varMember->val)
- continue;
- bool isExported = false;
- bool isConst = false;
- bool isExtern = false;
- for (auto modifier : varMember->modifiers)
- {
- if (as<HLSLExportModifier>(modifier))
- isExported = true;
- if (as<ExternAttribute>(modifier) || as<ExternModifier>(modifier))
- {
- isExtern = true;
- isExported = true;
- }
- if (as<ConstModifier>(modifier))
- isConst = true;
- }
- if (isExported && isConst)
- {
- auto mangledName = getMangledName(builder, varMember);
- if (isExtern && dict.containsKey(mangledName))
- continue;
- dict[mangledName] = varMember->val;
- }
- }
-
- for (auto member : containerDecl->getDirectMemberDecls())
- {
- if (as<NamespaceDecl>(member) || as<FileDecl>(member))
- {
- collectExportedConstantInContainer(dict, builder, (ContainerDecl*)member);
- }
- }
-}
-
-Dictionary<String, IntVal*>& ComponentType::getMangledNameToIntValMap()
-{
- if (m_mapMangledNameToIntVal)
- {
- return *m_mapMangledNameToIntVal;
- }
- m_mapMangledNameToIntVal = std::make_unique<Dictionary<String, IntVal*>>();
- auto astBuilder = getLinkage()->getASTBuilder();
- SLANG_AST_BUILDER_RAII(astBuilder);
- Scope* scope = _getOrCreateScopeForLegacyLookup(astBuilder);
- for (; scope; scope = scope->nextSibling)
- {
- if (scope->containerDecl)
- collectExportedConstantInContainer(
- *m_mapMangledNameToIntVal,
- astBuilder,
- scope->containerDecl);
- }
- return *m_mapMangledNameToIntVal;
-}
-
-ConstantIntVal* ComponentType::tryFoldIntVal(IntVal* intVal)
-{
- auto astBuilder = getLinkage()->getASTBuilder();
- SLANG_AST_BUILDER_RAII(astBuilder);
- return as<ConstantIntVal>(intVal->linkTimeResolve(getMangledNameToIntValMap()));
-}
-
-CompileRequestBase::CompileRequestBase(Linkage* linkage, DiagnosticSink* sink)
- : m_linkage(linkage), m_sink(sink)
-{
-}
-
-
-FrontEndCompileRequest::FrontEndCompileRequest(
- Linkage* linkage,
- StdWriters* writers,
- DiagnosticSink* sink)
- : CompileRequestBase(linkage, sink), m_writers(writers)
-{
- optionSet.inheritFrom(linkage->m_optionSet);
-}
-
-/// Handlers for preprocessor callbacks to use when doing ordinary front-end compilation
-struct FrontEndPreprocessorHandler : PreprocessorHandler
-{
-public:
- FrontEndPreprocessorHandler(
- Module* module,
- ASTBuilder* astBuilder,
- DiagnosticSink* sink,
- TranslationUnitRequest* translationUnit)
- : m_module(module)
- , m_astBuilder(astBuilder)
- , m_sink(sink)
- , m_translationUnit(translationUnit)
- {
- }
-
-protected:
- Module* m_module;
- ASTBuilder* m_astBuilder;
- DiagnosticSink* m_sink;
- TranslationUnitRequest* m_translationUnit = nullptr;
-
- // The first task that this handler tries to deal with is
- // capturing all the files on which a module is dependent.
- //
- // That information is exposed through public APIs and used
- // by applications to decide when they need to "hot reload"
- // their shader code.
- //
- void handleFileDependency(SourceFile* sourceFile) SLANG_OVERRIDE
- {
- m_module->addFileDependency(sourceFile);
- m_translationUnit->addIncludedSourceFileIfNotExist(sourceFile);
- }
-
- // The second task that this handler deals with is detecting
- // whether any macro values were set in a given source file
- // that are semantically relevant to other stages of compilation.
- //
- void handleEndOfTranslationUnit(Preprocessor* preprocessor) SLANG_OVERRIDE
- {
- // We look at the preprocessor state after reading the entire
- // source file/string, in order to see if any macros have been
- // set that should be considered semantically relevant for
- // later stages of compilation.
- //
- // Note: Checking the macro environment *after* preprocessing is complete
- // means that we can treat macros introduced via `-D` options or the API
- // equivalently to macros introduced via `#define`s in user code.
- //
- // For now, the only case of semantically-relevant macros we need to worrry
- // about are the NVAPI macros used to establish the register/space to use.
- //
- static const char* kNVAPIRegisterMacroName = "NV_SHADER_EXTN_SLOT";
- static const char* kNVAPISpaceMacroName = "NV_SHADER_EXTN_REGISTER_SPACE";
-
- // For NVAPI use, the `NV_SHADER_EXTN_SLOT` macro is required to be defined.
- //
- String nvapiRegister;
- SourceLoc nvapiRegisterLoc;
- if (!SLANG_FAILED(findMacroValue(
- preprocessor,
- kNVAPIRegisterMacroName,
- nvapiRegister,
- nvapiRegisterLoc)))
- {
- // In contrast, NVAPI can be used without defining `NV_SHADER_EXTN_REGISTER_SPACE`,
- // which effectively defaults to `space0`.
- //
- String nvapiSpace = "space0";
- SourceLoc nvapiSpaceLoc;
- findMacroValue(preprocessor, kNVAPISpaceMacroName, nvapiSpace, nvapiSpaceLoc);
-
- // We are going to store the values of these macros on the AST-level `ModuleDecl`
- // so that they will be available to later processing stages.
- //
- auto moduleDecl = m_module->getModuleDecl();
-
- if (auto existingModifier = moduleDecl->findModifier<NVAPISlotModifier>())
- {
- // If there is already a modifier attached to the module (perhaps
- // because of preprocessing a different source file, or because
- // of settings established via command-line options), then we
- // need to validate that the values being set in this file
- // match those already set (or else there is likely to be
- // some kind of error in the user's code).
- //
- _validateNVAPIMacroMatch(
- kNVAPIRegisterMacroName,
- existingModifier->registerName,
- nvapiRegister,
- nvapiRegisterLoc);
- _validateNVAPIMacroMatch(
- kNVAPISpaceMacroName,
- existingModifier->spaceName,
- nvapiSpace,
- nvapiSpaceLoc);
- }
- else
- {
- // If there is no existing modifier on the module, then we
- // take responsibility for adding one, based on the macro
- // values we saw.
- //
- auto modifier = m_astBuilder->create<NVAPISlotModifier>();
- modifier->loc = nvapiRegisterLoc;
- modifier->registerName = nvapiRegister;
- modifier->spaceName = nvapiSpace;
-
- addModifier(moduleDecl, modifier);
- }
- }
- }
-
- /// Validate that a re-defintion of an NVAPI-related macro matches any previous definition
- void _validateNVAPIMacroMatch(
- char const* macroName,
- String const& existingValue,
- String const& newValue,
- SourceLoc loc)
- {
- if (existingValue != newValue)
- {
- m_sink->diagnose(
- loc,
- Diagnostics::nvapiMacroMismatch,
- macroName,
- existingValue,
- newValue);
- }
- }
-};
-
-
-// Holds the hierarchy of views, the children being views that were 'initiated' (have an initiating
-// SourceLoc) in the parent.
-typedef Dictionary<SourceView*, List<SourceView*>> ViewInitiatingHierarchy;
-
-// Calculate the hierarchy from the sourceManager
-static void _calcViewInitiatingHierarchy(
- SourceManager* sourceManager,
- ViewInitiatingHierarchy& outHierarchy)
-{
- const List<SourceView*> emptyList;
- outHierarchy.clear();
-
- // Iterate over all managers
- for (SourceManager* curManager = sourceManager; curManager;
- curManager = curManager->getParent())
- {
- // Iterate over all views
- for (SourceView* view : curManager->getSourceViews())
- {
- if (view->getInitiatingSourceLoc().isValid())
- {
- // Look up the view it came from
- SourceView* parentView =
- sourceManager->findSourceViewRecursively(view->getInitiatingSourceLoc());
- if (parentView)
- {
- List<SourceView*>& children = outHierarchy.getOrAddValue(parentView, emptyList);
- // It shouldn't have already been added
- SLANG_ASSERT(children.indexOf(view) < 0);
- children.add(view);
- }
- }
- }
- }
-
- // Order all the children, by their raw SourceLocs. This is desirable, so that a trivial
- // traversal will traverse children in the order they are initiated in the parent source. This
- // assumes they increase in SourceLoc implies an later within a source file - this is true
- // currently.
- for (auto& [_, value] : outHierarchy)
- {
- value.sort(
- [](SourceView* a, SourceView* b) -> bool {
- return a->getInitiatingSourceLoc().getRaw() < b->getInitiatingSourceLoc().getRaw();
- });
- }
-}
-
-// Given a source file, find the view that is the initial SourceView use of the source. It must have
-// an initiating SourceLoc that is not valid.
-static SourceView* _findInitialSourceView(SourceFile* sourceFile)
-{
- // TODO(JS):
- // This might be overkill - presumably the SourceView would belong to the same manager as it's
- // SourceFile? That is not enforced by the SourceManager in any way though so we just search all
- // managers, and all views.
- for (SourceManager* sourceManager = sourceFile->getSourceManager(); sourceManager;
- sourceManager = sourceManager->getParent())
- {
- for (SourceView* view : sourceManager->getSourceViews())
- {
- if (view->getSourceFile() == sourceFile && !view->getInitiatingSourceLoc().isValid())
- {
- return view;
- }
- }
- }
-
- return nullptr;
-}
-
-static void _outputInclude(SourceFile* sourceFile, Index depth, DiagnosticSink* sink)
-{
- StringBuilder buf;
-
- for (Index i = 0; i < depth; ++i)
- {
- buf << " ";
- }
-
- // Output the found path for now
- // TODO(JS). We could use the verbose paths flag to control what path is output -> as it may be
- // useful to output the full path for example
-
- const PathInfo& pathInfo = sourceFile->getPathInfo();
- buf << "'" << pathInfo.foundPath << "'";
-
- // TODO(JS)?
- // You might want to know where this include was from.
- // If I output this though there will be a problem... as the indenting won't be clearly shown.
- // Perhaps I output in two sections, one the hierarchy and the other the locations of the
- // includes?
-
- sink->diagnose(SourceLoc(), Diagnostics::includeOutput, buf);
-}
-
-static void _outputIncludesRec(
- SourceView* sourceView,
- Index depth,
- ViewInitiatingHierarchy& hierarchy,
- DiagnosticSink* sink)
-{
- SourceFile* sourceFile = sourceView->getSourceFile();
- const PathInfo& pathInfo = sourceFile->getPathInfo();
-
- switch (pathInfo.type)
- {
- case PathInfo::Type::TokenPaste:
- case PathInfo::Type::CommandLine:
- case PathInfo::Type::TypeParse:
- {
- // If any of these types we don't output
- return;
- }
- default:
- break;
- }
-
- // Okay output this file at the current depth
- _outputInclude(sourceFile, depth, sink);
-
- // Now recurse to all of the children at the next depth
- List<SourceView*>* children = hierarchy.tryGetValue(sourceView);
- if (children)
- {
- for (SourceView* child : *children)
- {
- _outputIncludesRec(child, depth + 1, hierarchy, sink);
- }
- }
-}
-
-static void _outputPreprocessorTokens(const TokenList& toks, ISlangWriter* writer)
-{
- if (writer == nullptr)
- {
- return;
- }
-
- StringBuilder buf;
- for (const auto& tok : toks)
- {
- buf << tok.getContent();
- // We'll separate tokens with space for now
- buf.appendChar(' ');
- }
-
- buf.appendChar('\n');
-
- writer->write(buf.getBuffer(), buf.getLength());
-}
-
-static void _outputIncludes(
- const List<SourceFile*>& sourceFiles,
- SourceManager* sourceManager,
- DiagnosticSink* sink)
-{
- // Set up the hierarchy to know how all the source views relate. This could be argued as
- // overkill, but makes recursive output pretty simple
- ViewInitiatingHierarchy hierarchy;
- _calcViewInitiatingHierarchy(sourceManager, hierarchy);
-
- // For all the source files
- for (SourceFile* sourceFile : sourceFiles)
- {
- if (sourceFile->isIncludedFile())
- continue;
-
- // Find an initial view (this is the view of this file, that doesn't have an initiating loc)
- SourceView* sourceView = _findInitialSourceView(sourceFile);
- if (!sourceView)
- {
- // Okay, didn't find one, so just output the file
- _outputInclude(sourceFile, 0, sink);
- }
- else
- {
- // Output from this view recursively
- _outputIncludesRec(sourceView, 0, hierarchy, sink);
- }
- }
-}
-
-void FrontEndCompileRequest::parseTranslationUnit(TranslationUnitRequest* translationUnit)
-{
- SLANG_PROFILE;
- if (translationUnit->isChecked)
- return;
-
- auto linkage = getLinkage();
-
- SLANG_AST_BUILDER_RAII(linkage->getASTBuilder());
-
- // TODO(JS): NOTE! Here we are using the searchDirectories on the linkage. This is because
- // currently the API only allows the setting search paths on linkage.
- //
- // Here we should probably be using the searchDirectories on the FrontEndCompileRequest.
- // If searchDirectories.parent pointed to the one in the Linkage would mean linkage paths
- // would be checked too (after those on the FrontEndCompileRequest).
- IncludeSystem includeSystem(
- &linkage->getSearchDirectories(),
- linkage->getFileSystemExt(),
- linkage->getSourceManager());
-
- auto combinedPreprocessorDefinitions = translationUnit->getCombinedPreprocessorDefinitions();
-
- auto module = translationUnit->getModule();
-
- ASTBuilder* astBuilder = module->getASTBuilder();
-
- ModuleDecl* translationUnitSyntax = astBuilder->create<ModuleDecl>();
-
- translationUnitSyntax->nameAndLoc.name = translationUnit->moduleName;
- translationUnitSyntax->module = module;
- module->setModuleDecl(translationUnitSyntax);
-
- // When compiling a module of code that belongs to the Slang
- // core module, we add a modifier to the module to act
- // as a marker, so that downstream code can detect declarations
- // that came from the core module (by walking up their
- // chain of ancestors and looking for the marker), and treat
- // them differently from user declarations.
- //
- // We are adding the marker here, before we even parse the
- // code in the module, in case the subsequent steps would
- // like to treat the core module differently. Alternatively
- // we could pass down the `m_isStandardLibraryCode` flag to
- // these passes.
- //
- if (m_isCoreModuleCode)
- {
- translationUnitSyntax->modifiers.first = astBuilder->create<FromCoreModuleModifier>();
- }
-
- // We use a custom handler for preprocessor callbacks, to
- // ensure that relevant state that is only visible during
- // preprocessoing can be communicated to later phases of
- // compilation.
- //
- FrontEndPreprocessorHandler preprocessorHandler(module, astBuilder, getSink(), translationUnit);
-
- for (auto sourceFile : translationUnit->getSourceFiles())
- {
- module->getIncludedSourceFileMap().addIfNotExists(sourceFile, nullptr);
- }
-
- for (auto sourceFile : translationUnit->getSourceFiles())
- {
- SourceLanguage sourceLanguage = translationUnit->sourceLanguage;
- SlangLanguageVersion languageVersion =
- translationUnit->compileRequest->optionSet.getLanguageVersion();
- auto tokens = preprocessSource(
- sourceFile,
- getSink(),
- &includeSystem,
- combinedPreprocessorDefinitions,
- getLinkage(),
- sourceLanguage,
- languageVersion,
- &preprocessorHandler);
-
- translationUnitSyntax->languageVersion = languageVersion;
-
- if (sourceLanguage == SourceLanguage::Unknown)
- sourceLanguage = translationUnit->sourceLanguage;
-
- Scope* languageScope = nullptr;
- switch (sourceLanguage)
- {
- case SourceLanguage::HLSL:
- languageScope = getSession()->hlslLanguageScope;
- break;
- case SourceLanguage::GLSL:
- languageScope = getSession()->glslLanguageScope;
- break;
- case SourceLanguage::Slang:
- default:
- languageScope = getSession()->slangLanguageScope;
- break;
- }
-
- if (optionSet.getBoolOption(CompilerOptionName::OutputIncludes))
- {
- _outputIncludes(
- translationUnit->getSourceFiles(),
- getSink()->getSourceManager(),
- getSink());
- }
-
- if (optionSet.getBoolOption(CompilerOptionName::PreprocessorOutput))
- {
- if (m_writers)
- {
- _outputPreprocessorTokens(
- tokens,
- m_writers->getWriter(SLANG_WRITER_CHANNEL_STD_OUTPUT));
- }
- // If we output the preprocessor output then we are done doing anything else
- return;
- }
-
- parseSourceFile(
- astBuilder,
- translationUnit,
- sourceLanguage,
- tokens,
- getSink(),
- languageScope,
- translationUnitSyntax);
-
- // Let's try dumping
-
- if (optionSet.getBoolOption(CompilerOptionName::DumpAst))
- {
- StringBuilder buf;
- SourceWriter writer(linkage->getSourceManager(), LineDirectiveMode::None, nullptr);
-
- ASTDumpUtil::dump(
- translationUnit->getModuleDecl(),
- ASTDumpUtil::Style::Flat,
- 0,
- &writer);
-
- const String& path = sourceFile->getPathInfo().foundPath;
- if (path.getLength())
- {
- String fileName = Path::getFileNameWithoutExt(path);
- fileName.append(".slang-ast");
-
- File::writeAllText(fileName, writer.getContent());
- }
- }
-
-#if 0
- // Test serialization
- {
- ASTSerialTestUtil::testSerialize(translationUnit->getModuleDecl(), getSession()->getNamePool(), getLinkage()->getASTBuilder()->getSharedASTBuilder(), getSourceManager());
- }
-#endif
- }
-}
-
-RefPtr<ComponentType> createUnspecializedGlobalComponentType(
- FrontEndCompileRequest* compileRequest);
-
-RefPtr<ComponentType> createUnspecializedGlobalAndEntryPointsComponentType(
- FrontEndCompileRequest* compileRequest,
- List<RefPtr<ComponentType>>& outUnspecializedEntryPoints);
-
-RefPtr<ComponentType> createSpecializedGlobalComponentType(EndToEndCompileRequest* endToEndReq);
-
-RefPtr<ComponentType> createSpecializedGlobalAndEntryPointsComponentType(
- EndToEndCompileRequest* endToEndReq,
- List<RefPtr<ComponentType>>& outSpecializedEntryPoints);
-
-void FrontEndCompileRequest::checkAllTranslationUnits()
-{
- SLANG_PROFILE;
-
- LoadedModuleDictionary loadedModules;
- if (additionalLoadedModules)
- loadedModules = *additionalLoadedModules;
-
- // Iterate over all translation units and
- // apply the semantic checking logic.
- for (auto& translationUnit : translationUnits)
- {
- if (translationUnit->isChecked)
- continue;
-
- checkTranslationUnit(translationUnit.Ptr(), loadedModules);
-
- // Add the checked module to list of loadedModules so that they can be
- // discovered by `findOrImportModule` when processing future `import` decls.
- // TODO: this does not handle the case where a translation unit to discover
- // another translation unit added later to the compilation request.
- // We should output an error message when we detect such a case, or support
- // this scenario with a recursive style checking.
- loadedModules.add(translationUnit->moduleName, translationUnit->getModule());
- }
- checkEntryPoints();
-}
-
-void FrontEndCompileRequest::generateIR()
-{
- SLANG_PROFILE;
- SLANG_AST_BUILDER_RAII(getLinkage()->getASTBuilder());
-
- // Our task in this function is to generate IR code
- // for all of the declarations in the translation
- // units that were loaded.
-
- // Each translation unit is its own little world
- // for code generation (we are not trying to
- // replicate the GLSL linkage model), and so
- // we will generate IR for each (if needed)
- // in isolation.
- for (auto& translationUnit : translationUnits)
- {
- // Skip if the module is precompiled.
- if (translationUnit->getModule()->getIRModule())
- continue;
-
- // We want to only run generateIRForTranslationUnit once here. This is for two side effects:
- // * it can dump ir
- // * it can generate diagnostics
-
- /// Generate IR for translation unit.
- RefPtr<IRModule> irModule(
- generateIRForTranslationUnit(getLinkage()->getASTBuilder(), translationUnit));
-
- if (verifyDebugSerialization)
- {
- SerialContainerUtil::WriteOptions options;
-
- options.sourceManagerToUseWhenSerializingSourceLocs = getSourceManager();
-
- // Verify debug information
- if (SLANG_FAILED(
- SerialContainerUtil::verifyIRSerialize(irModule, getSession(), options)))
- {
- getSink()->diagnose(
- irModule->getModuleInst()->sourceLoc,
- Diagnostics::serialDebugVerificationFailed);
- }
- }
-
- // Set the module on the translation unit
- translationUnit->getModule()->setIRModule(irModule);
- }
-}
-
-// Try to infer a single common source language for a request
-static SourceLanguage inferSourceLanguage(FrontEndCompileRequest* request)
-{
- SourceLanguage language = SourceLanguage::Unknown;
- for (auto& translationUnit : request->translationUnits)
- {
- // Allow any other language to overide Slang as a choice
- if (language == SourceLanguage::Unknown || language == SourceLanguage::Slang)
- {
- language = translationUnit->sourceLanguage;
- }
- else if (language == translationUnit->sourceLanguage)
- {
- // same language as we currently have, so keep going
- }
- else
- {
- // we found a mismatch, so inference fails
- return SourceLanguage::Unknown;
- }
- }
- return language;
-}
-
-SlangResult FrontEndCompileRequest::executeActionsInner()
-{
- SLANG_PROFILE_SECTION(frontEndExecute);
- SLANG_AST_BUILDER_RAII(getLinkage()->getASTBuilder());
-
- for (TranslationUnitRequest* translationUnit : translationUnits)
- {
- // Make sure SourceFile representation is available for all translationUnits
- SLANG_RETURN_ON_FAIL(translationUnit->requireSourceFiles());
- }
-
-
- // Parse everything from the input files requested
- for (TranslationUnitRequest* translationUnit : translationUnits)
- {
- parseTranslationUnit(translationUnit);
- }
-
- if (optionSet.getBoolOption(CompilerOptionName::PreprocessorOutput))
- {
- // If doing pre-processor output, then we are done
- return SLANG_OK;
- }
-
- if (getSink()->getErrorCount() != 0)
- return SLANG_FAIL;
-
- // Perform semantic checking on the whole collection
- {
- SLANG_PROFILE_SECTION(SemanticChecking);
- checkAllTranslationUnits();
- }
-
- if (getSink()->getErrorCount() != 0)
- return SLANG_FAIL;
-
- // After semantic checking is performed we can try and output doc information for this
- if (optionSet.getBoolOption(CompilerOptionName::Doc))
- {
- // TODO: implement the logic to output generated documents to target directory/zip file.
- }
-
- // Look up all the entry points that are expected,
- // and use them to populate the `program` member.
- //
- m_globalComponentType = createUnspecializedGlobalComponentType(this);
- if (getSink()->getErrorCount() != 0)
- return SLANG_FAIL;
-
- m_globalAndEntryPointsComponentType =
- createUnspecializedGlobalAndEntryPointsComponentType(this, m_unspecializedEntryPoints);
- if (getSink()->getErrorCount() != 0)
- return SLANG_FAIL;
-
- // We always generate IR for all the translation units.
- //
- // TODO: We may eventually have a mode where we skip
- // IR codegen and only produce an AST (e.g., for use when
- // debugging problems in the parser or semantic checking),
- // but for now there are no cases where not having IR
- // makes sense.
- //
- generateIR();
- if (getSink()->getErrorCount() != 0)
- return SLANG_FAIL;
-
- // Do parameter binding generation, for each compilation target.
- //
- for (auto targetReq : getLinkage()->targets)
- {
- auto targetProgram = m_globalAndEntryPointsComponentType->getTargetProgram(targetReq);
- targetProgram->getOrCreateLayout(getSink());
- targetProgram->getOrCreateIRModuleForLayout(getSink());
- }
- if (getSink()->getErrorCount() != 0)
- return SLANG_FAIL;
-
- return SLANG_OK;
-}
-
-EndToEndCompileRequest::EndToEndCompileRequest(Session* session)
- : m_session(session), m_sink(nullptr, Lexer::sourceLocationLexer)
-{
- RefPtr<ASTBuilder> astBuilder(
- new ASTBuilder(session->m_sharedASTBuilder, "EndToEnd::Linkage::astBuilder"));
- m_linkage = new Linkage(session, astBuilder, session->getBuiltinLinkage());
- init();
-}
-
-EndToEndCompileRequest::EndToEndCompileRequest(Linkage* linkage)
- : m_session(linkage->getSessionImpl())
- , m_linkage(linkage)
- , m_sink(nullptr, Lexer::sourceLocationLexer)
-{
- init();
-}
-
-SLANG_NO_THROW SlangResult SLANG_MCALL
-EndToEndCompileRequest::queryInterface(SlangUUID const& uuid, void** outObject)
-{
- if (uuid == EndToEndCompileRequest::getTypeGuid())
- {
- // Special case to cast directly into internal type
- // NOTE! No addref(!)
- *outObject = this;
- return SLANG_OK;
- }
-
- if (uuid == ISlangUnknown::getTypeGuid() && uuid == ICompileRequest::getTypeGuid())
- {
- addReference();
- *outObject = static_cast<slang::ICompileRequest*>(this);
- return SLANG_OK;
- }
-
- return SLANG_E_NO_INTERFACE;
-}
-
-void EndToEndCompileRequest::init()
-{
- m_sink.setSourceManager(m_linkage->getSourceManager());
-
- m_writers = new StdWriters;
-
- // Set all the default writers
- for (int i = 0; i < int(WriterChannel::CountOf); ++i)
- {
- setWriter(WriterChannel(i), nullptr);
- }
-
- m_frontEndReq = new FrontEndCompileRequest(getLinkage(), m_writers, getSink());
-}
-
-SlangResult EndToEndCompileRequest::executeActionsInner()
-{
- SLANG_PROFILE_SECTION(endToEndActions);
- // If no code-generation target was specified, then try to infer one from the source language,
- // just to make sure we can do something reasonable when invoked from the command line.
- //
- // TODO: This logic should be moved into `options.cpp` or somewhere else
- // specific to the command-line tool.
- //
- if (getLinkage()->targets.getCount() == 0)
- {
- auto language = inferSourceLanguage(getFrontEndReq());
- switch (language)
- {
- case SourceLanguage::HLSL:
- getLinkage()->addTarget(CodeGenTarget::DXBytecode);
- break;
-
- case SourceLanguage::GLSL:
- getLinkage()->addTarget(CodeGenTarget::SPIRV);
- break;
-
- default:
- break;
- }
- }
-
- // Update compiler settings in target requests.
- for (auto target : getLinkage()->targets)
- target->getOptionSet().inheritFrom(getOptionSet());
- m_frontEndReq->optionSet = getOptionSet();
-
- // We only do parsing and semantic checking if we *aren't* doing
- // a pass-through compilation.
- //
- if (m_passThrough == PassThroughMode::None)
- {
- SLANG_RETURN_ON_FAIL(getFrontEndReq()->executeActionsInner());
- }
-
- if (getOptionSet().getBoolOption(CompilerOptionName::PreprocessorOutput))
- {
- return SLANG_OK;
- }
-
- // If command line specifies to skip codegen, we exit here.
- // Note: this is a debugging option.
- //
- if (getOptionSet().getBoolOption(CompilerOptionName::SkipCodeGen))
- {
- // We will use the program (and matching layout information)
- // that was computed in the front-end for all subsequent
- // reflection queries, etc.
- //
- m_specializedGlobalComponentType = getUnspecializedGlobalComponentType();
- m_specializedGlobalAndEntryPointsComponentType =
- getUnspecializedGlobalAndEntryPointsComponentType();
- m_specializedEntryPoints = getFrontEndReq()->getUnspecializedEntryPoints();
-
- SLANG_RETURN_ON_FAIL(maybeCreateContainer());
-
- SLANG_RETURN_ON_FAIL(maybeWriteContainer(m_containerOutputPath));
-
- return SLANG_OK;
- }
-
- // If requested, attempt to compile the translation unit all the way down to the target
- // language(s) and stash the result blobs in IR.
- for (auto target : getLinkage()->targets)
- {
- SlangCompileTarget targetEnum = SlangCompileTarget(target->getTarget());
- if (target->getOptionSet().getBoolOption(CompilerOptionName::EmbedDownstreamIR))
- {
- auto frontEndReq = getFrontEndReq();
-
- for (auto translationUnit : frontEndReq->translationUnits)
- {
- SLANG_RETURN_ON_FAIL(
- translationUnit->getModule()->precompileForTarget(targetEnum, nullptr));
-
- if (frontEndReq->optionSet.shouldDumpIR())
- {
- DiagnosticSinkWriter writer(frontEndReq->getSink());
-
- dumpIR(
- translationUnit->getModule()->getIRModule(),
- frontEndReq->m_irDumpOptions,
- "PRECOMPILE_FOR_TARGET_COMPLETE_ALL",
- frontEndReq->getSourceManager(),
- &writer);
-
- dumpIR(
- translationUnit->getModule()->getIRModule()->getModuleInst(),
- frontEndReq->m_irDumpOptions,
- frontEndReq->getSourceManager(),
- &writer);
- }
- }
- }
- }
-
- // If codegen is enabled, we need to move along to
- // apply any generic specialization that the user asked for.
- //
- if (m_passThrough == PassThroughMode::None)
- {
- m_specializedGlobalComponentType = createSpecializedGlobalComponentType(this);
- if (getSink()->getErrorCount() != 0)
- return SLANG_FAIL;
-
- m_specializedGlobalAndEntryPointsComponentType =
- createSpecializedGlobalAndEntryPointsComponentType(this, m_specializedEntryPoints);
- if (getSink()->getErrorCount() != 0)
- return SLANG_FAIL;
-
- // For each code generation target, we will generate specialized
- // parameter binding information (taking global generic
- // arguments into account at this time).
- //
- for (auto targetReq : getLinkage()->targets)
- {
- auto targetProgram =
- m_specializedGlobalAndEntryPointsComponentType->getTargetProgram(targetReq);
- targetProgram->getOrCreateLayout(getSink());
- }
- if (getSink()->getErrorCount() != 0)
- return SLANG_FAIL;
- }
- else
- {
- // We need to create dummy `EntryPoint` objects
- // to make sure that the logic in `generateOutput`
- // sees something worth processing.
- //
- List<RefPtr<ComponentType>> dummyEntryPoints;
- for (auto entryPointReq : getFrontEndReq()->getEntryPointReqs())
- {
- RefPtr<EntryPoint> dummyEntryPoint = EntryPoint::createDummyForPassThrough(
- getLinkage(),
- entryPointReq->getName(),
- entryPointReq->getProfile());
-
- dummyEntryPoints.add(dummyEntryPoint);
- }
-
- RefPtr<ComponentType> composedProgram =
- CompositeComponentType::create(getLinkage(), dummyEntryPoints);
-
- m_specializedGlobalComponentType = getUnspecializedGlobalComponentType();
- m_specializedGlobalAndEntryPointsComponentType = composedProgram;
- m_specializedEntryPoints = getFrontEndReq()->getUnspecializedEntryPoints();
- }
-
- // Generate output code, in whatever format was requested
- generateOutput();
- if (getSink()->getErrorCount() != 0)
- return SLANG_FAIL;
-
- return SLANG_OK;
-}
-
-// Act as expected of the API-based compiler
-SlangResult EndToEndCompileRequest::executeActions()
-{
- SlangResult res = executeActionsInner();
-
- m_diagnosticOutput = getSink()->outputBuffer.produceString();
- return res;
-}
-
-int FrontEndCompileRequest::addTranslationUnit(SourceLanguage language, Name* moduleName)
-{
- RefPtr<TranslationUnitRequest> translationUnit = new TranslationUnitRequest(this);
- translationUnit->compileRequest = this;
- translationUnit->sourceLanguage = SourceLanguage(language);
-
- translationUnit->setModuleName(moduleName);
- return addTranslationUnit(translationUnit);
-}
-
-int FrontEndCompileRequest::addTranslationUnit(TranslationUnitRequest* translationUnit)
-{
- Index result = translationUnits.getCount();
- translationUnits.add(translationUnit);
- return (int)result;
-}
-
-void FrontEndCompileRequest::addTranslationUnitSourceArtifact(
- int translationUnitIndex,
- IArtifact* sourceArtifact)
-{
- auto translationUnit = translationUnits[translationUnitIndex];
-
- // Add the source file
- translationUnit->addSourceArtifact(sourceArtifact);
-
- if (!translationUnit->moduleName)
- {
- translationUnit->setModuleName(
- getNamePool()->getName(Path::getFileNameWithoutExt(sourceArtifact->getName())));
- }
- if (translationUnit->module->getFilePath() == nullptr)
- translationUnit->module->setPathInfo(PathInfo::makePath(sourceArtifact->getName()));
-}
-
-void FrontEndCompileRequest::addTranslationUnitSourceBlob(
- int translationUnitIndex,
- String const& path,
- ISlangBlob* sourceBlob)
-{
- auto translationUnit = translationUnits[translationUnitIndex];
- auto sourceDesc =
- ArtifactDescUtil::makeDescForSourceLanguage(asExternal(translationUnit->sourceLanguage));
-
- auto artifact = ArtifactUtil::createArtifact(sourceDesc, path.getBuffer());
- artifact->addRepresentationUnknown(sourceBlob);
-
- addTranslationUnitSourceArtifact(translationUnitIndex, artifact);
-}
-
-void FrontEndCompileRequest::addTranslationUnitSourceFile(
- int translationUnitIndex,
- String const& path)
-{
- // TODO: We need to consider whether a relative `path` should cause
- // us to look things up using the registered search paths.
- //
- // This behavior wouldn't make sense for command-line invocations
- // of `slangc`, but at least one API user wondered by the search
- // paths were not taken into account by this function.
- //
-
- auto fileSystemExt = getLinkage()->getFileSystemExt();
- auto translationUnit = getTranslationUnit(translationUnitIndex);
-
- auto sourceDesc =
- ArtifactDescUtil::makeDescForSourceLanguage(asExternal(translationUnit->sourceLanguage));
-
- auto sourceArtifact = ArtifactUtil::createArtifact(sourceDesc, path.getBuffer());
-
- auto extRep = new ExtFileArtifactRepresentation(path.getUnownedSlice(), fileSystemExt);
- sourceArtifact->addRepresentation(extRep);
-
- SlangResult existsRes = SLANG_OK;
-
- // If we require caching, we demand it's loaded here.
- //
- // In practice this probably means repro capture is enabled. So we want to
- // load the blob such that it's in the cache, even if it doesn't actually
- // have to be loaded for the compilation.
- if (getLinkage()->m_requireCacheFileSystem)
- {
- ComPtr<ISlangBlob> blob;
- // If we can load the blob, then it exists
- existsRes = sourceArtifact->loadBlob(ArtifactKeep::Yes, blob.writeRef());
- }
- else
- {
- existsRes = sourceArtifact->exists() ? SLANG_OK : SLANG_E_NOT_FOUND;
- }
-
- if (SLANG_FAILED(existsRes))
- {
- // Emit a diagnostic!
- getSink()->diagnose(SourceLoc(), Diagnostics::cannotOpenFile, path);
- return;
- }
-
- addTranslationUnitSourceArtifact(translationUnitIndex, sourceArtifact);
-}
-
-int FrontEndCompileRequest::addEntryPoint(
- int translationUnitIndex,
- String const& name,
- Profile entryPointProfile)
-{
- auto translationUnitReq = translationUnits[translationUnitIndex];
-
- Index result = m_entryPointReqs.getCount();
-
- RefPtr<FrontEndEntryPointRequest> entryPointReq = new FrontEndEntryPointRequest(
- this,
- translationUnitIndex,
- getNamePool()->getName(name),
- entryPointProfile);
-
- m_entryPointReqs.add(entryPointReq);
- // translationUnitReq->entryPoints.add(entryPointReq);
-
- return int(result);
-}
-
-int EndToEndCompileRequest::addEntryPoint(
- int translationUnitIndex,
- String const& name,
- Profile entryPointProfile,
- List<String> const& genericTypeNames)
-{
- getFrontEndReq()->addEntryPoint(translationUnitIndex, name, entryPointProfile);
-
- EntryPointInfo entryPointInfo;
- for (auto typeName : genericTypeNames)
- entryPointInfo.specializationArgStrings.add(typeName);
-
- Index result = m_entryPoints.getCount();
- m_entryPoints.add(_Move(entryPointInfo));
- return (int)result;
-}
-
-UInt Linkage::addTarget(CodeGenTarget target)
-{
- RefPtr<TargetRequest> targetReq = new TargetRequest(this, target);
-
- Index result = targets.getCount();
- targets.add(targetReq);
- return UInt(result);
-}
-
-void Linkage::loadParsedModule(
- RefPtr<FrontEndCompileRequest> compileRequest,
- RefPtr<TranslationUnitRequest> translationUnit,
- Name* name,
- const PathInfo& pathInfo)
-{
- // Note: we add the loaded module to our name->module listing
- // before doing semantic checking, so that if it tries to
- // recursively `import` itself, we can detect it.
- //
- RefPtr<Module> loadedModule = translationUnit->getModule();
-
- // Get a path
- String mostUniqueIdentity = pathInfo.getMostUniqueIdentity();
- SLANG_ASSERT(mostUniqueIdentity.getLength() > 0);
-
- mapPathToLoadedModule.add(mostUniqueIdentity, loadedModule);
- mapNameToLoadedModules.add(name, loadedModule);
-
- auto sink = translationUnit->compileRequest->getSink();
-
- int errorCountBefore = sink->getErrorCount();
- int errorCountAfter;
- try
- {
- compileRequest->checkAllTranslationUnits();
- }
- catch (...)
- {
- mapPathToLoadedModule.remove(mostUniqueIdentity);
- mapNameToLoadedModules.remove(name);
- throw;
- }
- errorCountAfter = sink->getErrorCount();
- if (isInLanguageServer())
- {
- // Don't generate IR as language server.
- // This means that we currently cannot report errors that are detected during IR passes.
- // Ideally we want to run those passes, but that is too risky for what it is worth right
- // now.
- }
- else
- {
- if (errorCountAfter != errorCountBefore)
- {
- // There must have been an error in the loaded module.
- // Remove from maps if there were errors during semantic checking
- mapPathToLoadedModule.remove(mostUniqueIdentity);
- mapNameToLoadedModules.remove(name);
- }
- else
- {
- // If we didn't run into any errors, then try to generate
- // IR code for the imported module.
- if (errorCountAfter == 0)
- {
- loadedModule->setIRModule(
- generateIRForTranslationUnit(getASTBuilder(), translationUnit));
- }
- }
- }
- loadedModulesList.add(loadedModule);
-}
-
-RefPtr<Module> Linkage::findOrLoadSerializedModuleForModuleLibrary(
- ISlangBlob* blobHoldingSerializedData,
- ModuleChunk const* moduleChunk,
- RIFF::ListChunk const* libraryChunk,
- DiagnosticSink* sink)
-{
- RefPtr<Module> resultModule;
-
- // We will attempt things in a few different steps, trying to
- // decode as little of the serialized module as necessary at
- // each step, so that we don't waste time on the heavyweight
- // stuff when we didn't need to.
- //
- // The first step is to simply decode the module name, and
- // see if we have a already loaded a matching module.
-
- auto moduleName = getNamePool()->getName(moduleChunk->getName());
- if (mapNameToLoadedModules.tryGetValue(moduleName, resultModule))
- return resultModule;
-
- // It is possible that the module has been loaded, but somehow
- // under a different name, so next we decode the list of file
- // paths that the module depends on, and then rely on the assumption
- // that the first of those paths represents the file for the module
- // itself to detect if we've already loaded a module from that
- // path.
- //
- // Note: While this is a distasteful assumption to make, it is
- // one that gets made in several parts of the compiler codebase
- // already. It isn't something that can be fixed in just one
- // place at this point.
-
- auto fileDependenciesList = moduleChunk->getFileDependencies();
- auto firstFileDependencyChunk = fileDependenciesList.getFirst();
- if (!firstFileDependencyChunk)
- return nullptr;
-
- auto modulePathInfo = PathInfo::makePath(firstFileDependencyChunk->getValue());
- if (mapPathToLoadedModule.tryGetValue(modulePathInfo.getMostUniqueIdentity(), resultModule))
- return resultModule;
-
- // If we failed to find a previously-loaded module, then we
- // will go ahead and load the module from the serialized form.
- //
- PathInfo filePathInfo;
- return loadSerializedModule(
- moduleName,
- modulePathInfo,
- blobHoldingSerializedData,
- moduleChunk,
- libraryChunk,
- SourceLoc(),
- sink);
-}
-
-RefPtr<Module> Linkage::loadSerializedModule(
- Name* moduleName,
- const PathInfo& moduleFilePathInfo,
- ISlangBlob* blobHoldingSerializedData,
- ModuleChunk const* moduleChunk,
- RIFF::ListChunk const* containerChunk,
- SourceLoc const& requestingLoc,
- DiagnosticSink* sink)
-{
- auto astBuilder = getASTBuilder();
- SLANG_AST_BUILDER_RAII(astBuilder);
-
- auto module = RefPtr(new Module(this, astBuilder));
- module->setName(moduleName);
-
- // Just as if we were processing an `import` declaration in
- // source code, we will track the fact that this serialized
- // modlue is (effectively) being imported, so that we can
- // diagnose anything troublesome, like an attempt at a
- // recursive import.
- //
- ModuleBeingImportedRAII moduleBeingImported(this, module, moduleName, requestingLoc);
-
- // We will register the module in our data structures to
- // track loaded modules, and then remove it in the case
- // where there is some kind of failure.
- //
- String mostUniqueIdentity = moduleFilePathInfo.getMostUniqueIdentity();
- SLANG_ASSERT(mostUniqueIdentity.getLength() > 0);
-
- mapPathToLoadedModule.add(mostUniqueIdentity, module);
- mapNameToLoadedModules.add(moduleName, module);
- try
- {
- if (SLANG_FAILED(loadSerializedModuleContents(
- module,
- moduleFilePathInfo,
- blobHoldingSerializedData,
- moduleChunk,
- containerChunk,
- sink)))
- {
- mapPathToLoadedModule.remove(mostUniqueIdentity);
- mapNameToLoadedModules.remove(moduleName);
- return nullptr;
- }
-
- loadedModulesList.add(module);
- return module;
- }
- catch (...)
- {
- mapPathToLoadedModule.remove(mostUniqueIdentity);
- mapNameToLoadedModules.remove(moduleName);
- throw;
- }
-}
-
-RefPtr<Module> Linkage::loadBinaryModuleImpl(
- Name* moduleName,
- const PathInfo& moduleFilePathInfo,
- ISlangBlob* moduleFileContents,
- SourceLoc const& requestingLoc,
- DiagnosticSink* sink)
-{
- auto astBuilder = getASTBuilder();
- SLANG_AST_BUILDER_RAII(astBuilder);
-
- // We start by reading the content of the file as
- // an in-memory RIFF container.
- //
- auto rootChunk = RIFF::RootChunk::getFromBlob(moduleFileContents);
- if (!rootChunk)
- {
- return nullptr;
- }
-
- auto moduleChunk = ModuleChunk::find(rootChunk);
- if (!moduleChunk)
- {
- return nullptr;
- }
-
- // Next, we attempt to check if the binary module is up to
- // date with the compilation options in use as well as
- // the contents of all the files its compilation depended
- // on (as determined by its hash).
- //
- String mostUniqueIdentity = moduleFilePathInfo.getMostUniqueIdentity();
- SLANG_ASSERT(mostUniqueIdentity.getLength() > 0);
- if (m_optionSet.getBoolOption(CompilerOptionName::UseUpToDateBinaryModule))
- {
- if (!isBinaryModuleUpToDate(moduleFilePathInfo.foundPath, moduleChunk))
- {
- return nullptr;
- }
- }
-
- // If everything seems reasonable, then we will go ahead and load
- // the module more completely from that serialized representation.
- //
- RefPtr<Module> module = loadSerializedModule(
- moduleName,
- moduleFilePathInfo,
- moduleFileContents,
- moduleChunk,
- rootChunk,
- requestingLoc,
- sink);
-
- return module;
-}
-
-void Linkage::_diagnoseErrorInImportedModule(DiagnosticSink* sink)
-{
- for (auto info = m_modulesBeingImported; info; info = info->next)
- {
- sink->diagnose(info->importLoc, Diagnostics::errorInImportedModule, info->name);
- }
- if (!isInLanguageServer())
- {
- sink->diagnose(SourceLoc(), Diagnostics::complationCeased);
- }
-}
-
-RefPtr<Module> Linkage::loadModuleImpl(
- Name* moduleName,
- const PathInfo& modulePathInfo,
- ISlangBlob* moduleBlob,
- SourceLoc const& requestingLoc,
- DiagnosticSink* sink,
- const LoadedModuleDictionary* additionalLoadedModules,
- ModuleBlobType blobType)
-{
- switch (blobType)
- {
- case ModuleBlobType::IR:
- return loadBinaryModuleImpl(moduleName, modulePathInfo, moduleBlob, requestingLoc, sink);
-
- case ModuleBlobType::Source:
- return loadSourceModuleImpl(
- moduleName,
- modulePathInfo,
- moduleBlob,
- requestingLoc,
- sink,
- additionalLoadedModules);
-
- default:
- SLANG_UNEXPECTED("unknown module blob type");
- UNREACHABLE_RETURN(nullptr);
- }
-}
-
-RefPtr<Module> Linkage::loadSourceModuleImpl(
- Name* name,
- const PathInfo& filePathInfo,
- ISlangBlob* sourceBlob,
- SourceLoc const& srcLoc,
- DiagnosticSink* sink,
- const LoadedModuleDictionary* additionalLoadedModules)
-{
- RefPtr<FrontEndCompileRequest> frontEndReq = new FrontEndCompileRequest(this, nullptr, sink);
-
- frontEndReq->additionalLoadedModules = additionalLoadedModules;
-
- RefPtr<TranslationUnitRequest> translationUnit = new TranslationUnitRequest(frontEndReq);
- translationUnit->compileRequest = frontEndReq;
- translationUnit->setModuleName(name);
- Stage impliedStage;
- translationUnit->sourceLanguage = SourceLanguage::Slang;
-
- // If we are loading from a file with apparaent glsl extension,
- // set the source language to GLSL to enable GLSL compatibility mode.
- if ((SourceLanguage)findSourceLanguageFromPath(filePathInfo.getName(), impliedStage) ==
- SourceLanguage::GLSL)
- {
- translationUnit->sourceLanguage = SourceLanguage::GLSL;
- }
-
- frontEndReq->addTranslationUnit(translationUnit);
-
- auto module = translationUnit->getModule();
-
- ModuleBeingImportedRAII moduleBeingImported(this, module, name, srcLoc);
-
- // Create an artifact for the source
- auto sourceArtifact = ArtifactUtil::createArtifact(
- ArtifactDesc::make(ArtifactKind::Source, ArtifactPayload::Slang, ArtifactStyle::Unknown));
-
- if (sourceBlob)
- {
- // If the user has already provided a source blob, use that.
- sourceArtifact->addRepresentation(
- new SourceBlobWithPathInfoArtifactRepresentation(filePathInfo, sourceBlob));
- }
- else if (
- filePathInfo.type == PathInfo::Type::Normal ||
- filePathInfo.type == PathInfo::Type::FoundPath)
- {
- // Create with the 'friendly' name
- // We create that it was loaded from the file system
- sourceArtifact->addRepresentation(new ExtFileArtifactRepresentation(
- filePathInfo.foundPath.getUnownedSlice(),
- getFileSystemExt()));
- }
- else
- {
- return nullptr;
- }
-
- translationUnit->addSourceArtifact(sourceArtifact);
-
- if (SLANG_FAILED(translationUnit->requireSourceFiles()))
- {
- // Some problem accessing source files
- return nullptr;
- }
- int errorCountBefore = sink->getErrorCount();
- frontEndReq->parseTranslationUnit(translationUnit);
- int errorCountAfter = sink->getErrorCount();
-
- if (errorCountAfter != errorCountBefore && !isInLanguageServer())
- {
- _diagnoseErrorInImportedModule(sink);
- // Something went wrong during the parsing, so we should bail out.
- return nullptr;
- }
-
- try
- {
- loadParsedModule(frontEndReq, translationUnit, name, filePathInfo);
- }
- catch (const Slang::AbortCompilationException&)
- {
- // Something is fatally wrong, we should return nullptr.
- module = nullptr;
- }
- errorCountAfter = sink->getErrorCount();
-
- if (errorCountAfter != errorCountBefore && !isInLanguageServer())
- {
- // If something is fatally wrong, we want to report
- // the diagnostic even if we are in language server
- // and processing a different module.
- _diagnoseErrorInImportedModule(sink);
- // Something went wrong during the parsing, so we should bail out.
- return nullptr;
- }
-
- if (!module)
- return nullptr;
-
- module->setPathInfo(filePathInfo);
- return module;
-}
-
-bool Linkage::isBeingImported(Module* module)
-{
- for (auto ii = m_modulesBeingImported; ii; ii = ii->next)
- {
- if (module == ii->module)
- return true;
- }
- return false;
-}
-
-// Derive a file name for the module, by taking the given
-// identifier, replacing all occurrences of `_` with `-`,
-// and then appending `.slang`.
-//
-// For example, `foo_bar` becomes `foo-bar.slang`.
-String getFileNameFromModuleName(Name* name, bool translateUnderScore)
-{
- String fileName;
- if (!getText(name).getUnownedSlice().endsWithCaseInsensitive(".slang"))
- {
- StringBuilder sb;
- for (auto c : getText(name))
- {
- if (translateUnderScore && c == '_')
- c = '-';
-
- sb.append(c);
- }
- sb.append(".slang");
- fileName = sb.produceString();
- }
- else
- {
- fileName = getText(name);
- }
- return fileName;
-}
-
-RefPtr<Module> Linkage::findOrImportModule(
- Name* moduleName,
- SourceLoc const& requestingLoc,
- DiagnosticSink* sink,
- const LoadedModuleDictionary* loadedModules)
-{
- // Have we already loaded a module matching this name?
- //
- RefPtr<LoadedModule> previouslyLoadedModule;
- if (mapNameToLoadedModules.tryGetValue(moduleName, previouslyLoadedModule))
- {
- // If the map shows a null module having been loaded,
- // then that means there was a prior load attempt,
- // but it failed, so we won't bother trying again.
- //
- if (!previouslyLoadedModule)
- return nullptr;
-
- // If state shows us that the module is already being
- // imported deeper on the call stack, then we've
- // hit a recursive case, and that is an error.
- //
- if (isBeingImported(previouslyLoadedModule))
- {
- // We seem to be in the middle of loading this module
- sink->diagnose(requestingLoc, Diagnostics::recursiveModuleImport, moduleName);
- return nullptr;
- }
-
- return previouslyLoadedModule;
- }
-
- // If the user is providing an additional list of loaded modules, we find
- // if the module being imported is in that list. This allows a translation
- // unit to use previously checked translation units in the same
- // FrontEndCompileRequest.
- {
- Module* previouslyLoadedLocalModule = nullptr;
- if (loadedModules && loadedModules->tryGetValue(moduleName, previouslyLoadedLocalModule))
- {
- return previouslyLoadedLocalModule;
- }
- }
-
- // If the name being requested matches the name of a built-in module,
- // then we will special-case the process by loading that builtin
- // module directly.
- //
- // TODO: right now this logic is only considering the built-in `glsl`
- // module, but it should probably be generalized so that we can more
- // easily support having multiple built-in modules rather than just
- // putting everything into `core`.
- //
- if (moduleName == getSessionImpl()->glslModuleName)
- {
- // This is a builtin glsl module, just load it from embedded definition.
- auto glslModule = getSessionImpl()->getBuiltinModule(slang::BuiltinModuleName::GLSL);
- if (!glslModule)
- {
- // Note: the way this logic is currently written, if the built-in
- // `glsl` module fails to load, then we will *not* fall back to
- // searching for a user-defined module in a file like `glsl.slang`.
- //
- // It is unclear if this should be the default behavior or not.
- // Should built-in modules be prioritized over user modules?
- // Should built-in modules shadow user modules, even when the
- // built-in module fails to load, for some reason?
- //
- sink->diagnose(requestingLoc, Diagnostics::glslModuleNotAvailable, moduleName);
- }
- return glslModule;
- }
-
- // We are going to use a loop to search for a suitable file to
- // load the module from, to account for a few key choices:
- //
- // * We can both load modules from a source `.slang` file,
- // or from a binary `.slang-module` file.
- //
- // * For a variety of reasons, the `import` logic has historically
- // translated underscores in a module name into dashes (so that
- // `import my_module` will look for `my-module.slang`), and we
- // try to support both that convention as well as a convention
- // that preserves underscores.
- //
- // To try to keep this logic as orthogonal as possible, we first
- // construct lists of the options we want to iterate over, and
- // then do the actual loop later.
-
- ShortList<ModuleBlobType, 2> typesToTry;
- if (isInLanguageServer())
- {
- // When in language server, we always prefer to use source module if it is available.
- typesToTry.add(ModuleBlobType::Source);
- typesToTry.add(ModuleBlobType::IR);
- }
- else
- {
- // Look for a precompiled module first, if not exist, load from source.
- typesToTry.add(ModuleBlobType::IR);
- typesToTry.add(ModuleBlobType::Source);
- }
-
- // We will always search for a file name that directly matches the
- // module name as written first, and then search for one with
- // underscores replaced by dashes. The latter is the original
- // behavior that `import` provided, but it seems safest to prefer
- // the exact name spelled in the user's code when there might
- // actually be ambiguity.
- //
- auto defaultSourceFileName = getFileNameFromModuleName(moduleName, false);
- auto alternativeSourceFileName = getFileNameFromModuleName(moduleName, true);
- String sourceFileNamesToTry[] = {defaultSourceFileName, alternativeSourceFileName};
-
- // We are going to look for the candidate file using the same
- // logic that would be used for a preprocessor `#include`,
- // so we set up the necessary state.
- //
- IncludeSystem includeSystem(&getSearchDirectories(), getFileSystemExt(), getSourceManager());
-
- // Just like with a `#include`, the search will take into
- // account the path to the file where the request to import
- // this module came from (e.g. the source file with the
- // `import` declaration), if such a path is available.
- //
- PathInfo requestingPathInfo =
- getSourceManager()->getPathInfo(requestingLoc, SourceLocType::Actual);
-
- for (auto type : typesToTry)
- {
- for (auto sourceFileName : sourceFileNamesToTry)
- {
- // The `sourceFileName` will have the `.slang` extension,
- // so if we are looking for a binary module, we need
- // to change the extension we will look for.
- //
- String fileName;
- switch (type)
- {
- case ModuleBlobType::Source:
- fileName = sourceFileName;
- break;
-
- case ModuleBlobType::IR:
- fileName = Path::replaceExt(sourceFileName, "slang-module");
- break;
- }
-
- // We now search for a file matching the desired name,
- // using the same logic as for a `#include`.
- //
- // TODO: We might want to consider how to handle the case
- // of an `import` with a relative path a little specially,
- // since it could in theory be possible for two `.slang`
- // files with the same base name to exist in different
- // directories in a project, and we'd want file-relative
- // `import`s to work for each, without having either one
- // be able to "claim" the bare identifier of the base
- // name for itself.
- //
- PathInfo filePathInfo;
- if (SLANG_FAILED(
- includeSystem.findFile(fileName, requestingPathInfo.foundPath, filePathInfo)))
- {
- // If we failed to find the file at this step, we
- // will continue the search for our other options.
- //
- continue;
- }
-
- // We will *again* search for a previously loaded module.
- //
- // It is possible that the same file will have been loaded
- // as a module under two different module names. The easiest
- // way for this to happen is if there are `import` declarations
- // using both the underscore and dash conventions (e.g., both
- // `import "my-module.slang"` and `import my_module`).
- //
- // This case may also arise if one file `import`s a module using
- // just an identifier for its name, but another `import`s it
- // using a path (e.g., `import "subdir/file.slang"`).
- //
- // No matter how the situation arises, we only want to have one
- // copy of the "same" module loaded at a given time, so we
- // will re-use the existing module if we find one here.
- //
- if (mapPathToLoadedModule.tryGetValue(
- filePathInfo.getMostUniqueIdentity(),
- previouslyLoadedModule))
- {
- // TODO: If we find a previously-loaded module at this step,
- // then we should probably register that module under the
- // given `moduleName` in the map of loaded modules, so
- // that subsequent `import`s using the same form will find it.
- //
- return previouslyLoadedModule;
- }
-
- // Now we try to load the content of the file.
- //
- // If for some reason we could find a file at the
- // given path, but for some reason couldn't *open*
- // and *read* it, then we continue the search
- // using whatever other candidate file names are left.
- //
- ComPtr<ISlangBlob> fileContents;
- if (SLANG_FAILED(includeSystem.loadFile(filePathInfo, fileContents)))
- {
- continue;
- }
-
- // If we found a real file and were able to load its contents,
- // then we'll go ahead and try to load a module from it,
- // whether by compiling it or decoding the binary.
- //
- auto module = loadModuleImpl(
- moduleName,
- filePathInfo,
- fileContents,
- requestingLoc,
- sink,
- loadedModules,
- type);
-
- // If the attempt to load the module from the given path
- // was successful, we go ahead and use it, without trying
- // out any other options.
- //
- if (module)
- return module;
- }
- }
-
- // If we tried out all of our candidate file names
- // and failed with each of them, then we diagnose
- // an error based on the original *source* file
- // name.
- //
- // TODO: this should really be an error message
- // that clearly states something like "no file
- // suitable for module `whatever` was found
- // and loaded.
- //
- // Ideally that error message would include whatever
- // of the candidate file names from the loop above
- // got furthest along in the process (or just a
- // list of the file names that were tried, if
- // nothing was even found via the include system).
- //
- sink->diagnose(requestingLoc, Diagnostics::cannotOpenFile, defaultSourceFileName);
-
- // If the attempt to import the module failed, then
- // we will stick a null pointer into the map of loaded
- // modules, so that subsequent attempts to load a module
- // with this name will return null without having to
- // go through all the above steps yet again.
- //
- mapNameToLoadedModules[moduleName] = nullptr;
- return nullptr;
-}
-
-SourceFile* Linkage::loadSourceFile(String pathFrom, String path)
-{
- IncludeSystem includeSystem(&getSearchDirectories(), getFileSystemExt(), getSourceManager());
- ComPtr<slang::IBlob> blob;
- PathInfo pathInfo;
- SLANG_RETURN_NULL_ON_FAIL(includeSystem.findFile(path, pathFrom, pathInfo));
- SourceFile* sourceFile = nullptr;
- SLANG_RETURN_NULL_ON_FAIL(includeSystem.loadFile(pathInfo, blob, sourceFile));
- return sourceFile;
-}
-
-// Check if a serialized module is up-to-date with current compiler options and source files.
-bool Linkage::isBinaryModuleUpToDate(String fromPath, RIFF::ListChunk const* baseChunk)
-{
- auto moduleChunk = ModuleChunk::find(baseChunk);
- if (!moduleChunk)
- return false;
-
- SHA1::Digest existingDigest = moduleChunk->getDigest();
-
- DigestBuilder<SHA1> digestBuilder;
- auto version = String(getBuildTagString());
- digestBuilder.append(version);
- m_optionSet.buildHash(digestBuilder);
-
- // Find the canonical path of the directory containing the module source file.
- String moduleSrcPath = "";
-
- auto dependencyChunks = moduleChunk->getFileDependencies();
- if (auto firstDependencyChunk = dependencyChunks.getFirst())
- {
- moduleSrcPath = firstDependencyChunk->getValue();
-
- IncludeSystem includeSystem(
- &getSearchDirectories(),
- getFileSystemExt(),
- getSourceManager());
- PathInfo modulePathInfo;
- if (SLANG_SUCCEEDED(includeSystem.findFile(moduleSrcPath, fromPath, modulePathInfo)))
- {
- moduleSrcPath = modulePathInfo.foundPath;
- Path::getCanonical(moduleSrcPath, moduleSrcPath);
- }
- }
-
- for (auto dependencyChunk : dependencyChunks)
- {
- auto file = dependencyChunk->getValue();
- auto sourceFile = loadSourceFile(fromPath, file);
- if (!sourceFile)
- {
- // If we cannot find the source file from `fromPath`,
- // try again from the module's source file path.
- if (dependencyChunks.getFirst())
- sourceFile = loadSourceFile(moduleSrcPath, file);
- }
- if (!sourceFile)
- return false;
- digestBuilder.append(sourceFile->getDigest());
- }
- return digestBuilder.finalize() == existingDigest;
-}
-
-SLANG_NO_THROW bool SLANG_MCALL
-Linkage::isBinaryModuleUpToDate(const char* modulePath, slang::IBlob* binaryModuleBlob)
-{
- auto rootChunk = RIFF::RootChunk::getFromBlob(binaryModuleBlob);
- if (!rootChunk)
- return false;
- return isBinaryModuleUpToDate(modulePath, rootChunk);
-}
-
-SourceFile* Linkage::findFile(Name* name, SourceLoc loc, IncludeSystem& outIncludeSystem)
-{
- auto impl = [&](bool translateUnderScore) -> SourceFile*
- {
- auto fileName = getFileNameFromModuleName(name, translateUnderScore);
-
- // Next, try to find the file of the given name,
- // using our ordinary include-handling logic.
-
- auto& searchDirs = getSearchDirectories();
- outIncludeSystem = IncludeSystem(&searchDirs, getFileSystemExt(), getSourceManager());
-
- // Get the original path info
- PathInfo pathIncludedFromInfo = getSourceManager()->getPathInfo(loc, SourceLocType::Actual);
- PathInfo filePathInfo;
-
- ComPtr<ISlangBlob> fileContents;
-
- // We have to load via the found path - as that is how file was originally loaded
- if (SLANG_FAILED(
- outIncludeSystem.findFile(fileName, pathIncludedFromInfo.foundPath, filePathInfo)))
- {
- return nullptr;
- }
- // Otherwise, try to load it.
- SourceFile* sourceFile;
- if (SLANG_FAILED(outIncludeSystem.loadFile(filePathInfo, fileContents, sourceFile)))
- {
- return nullptr;
- }
- return sourceFile;
- };
- if (auto rs = impl(false))
- return rs;
- return impl(true);
-}
-
-Linkage::IncludeResult Linkage::findAndIncludeFile(
- Module* module,
- TranslationUnitRequest* translationUnit,
- Name* name,
- SourceLoc const& loc,
- DiagnosticSink* sink)
-{
- IncludeResult result;
- result.fileDecl = nullptr;
- result.isNew = false;
-
- IncludeSystem includeSystem;
- auto sourceFile = findFile(name, loc, includeSystem);
- if (!sourceFile)
- {
- sink->diagnose(loc, Diagnostics::cannotOpenFile, getText(name));
- return result;
- }
-
- // If the file has already been included, don't need to do anything further.
- if (auto existingFileDecl = module->getIncludedSourceFileMap().tryGetValue(sourceFile))
- {
- result.fileDecl = *existingFileDecl;
- result.isNew = false;
- return result;
- }
-
- if (isInLanguageServer())
- {
- // HACK: When in language server mode, we will always load the currently opend file as a
- // fresh module even if some previously opened file already references the current file via
- // `import` or `include`. see comments in `WorkspaceVersion::getOrLoadModule()` for the
- // reason behind this. An undesired outcome of this decision is that we could endup
- // including the currently opened file itself via chain of `__include`s because the
- // currently opened file will not have a true unique file system identity that allows it to
- // be deduplicated correct. Therefore we insert a hack logic here to detect re-inclusion by
- // just the file path. We can clean up this hack by making the language server truly support
- // incremental checking so we can reuse the previously loaded module instead of needing to
- // always start with a fresh copy.
- //
- for (auto file : translationUnit->getSourceFiles())
- {
- if (file->getPathInfo().hasFoundPath() &&
- Path::equals(file->getPathInfo().foundPath, sourceFile->getPathInfo().foundPath))
- return result;
- }
- }
-
- module->addFileDependency(sourceFile);
-
- // Create a transparent FileDecl to hold all children from the included file.
- auto fileDecl = module->getASTBuilder()->create<FileDecl>();
- fileDecl->nameAndLoc.name = name;
- fileDecl->parentDecl = module->getModuleDecl();
- module->getIncludedSourceFileMap().add(sourceFile, fileDecl);
-
- FrontEndPreprocessorHandler preprocessorHandler(
- module,
- module->getASTBuilder(),
- sink,
- translationUnit);
- auto combinedPreprocessorDefinitions = translationUnit->getCombinedPreprocessorDefinitions();
- SourceLanguage sourceLanguage = translationUnit->sourceLanguage;
- SlangLanguageVersion slangLanguageVersion = module->getModuleDecl()->languageVersion;
- auto tokens = preprocessSource(
- sourceFile,
- sink,
- &includeSystem,
- combinedPreprocessorDefinitions,
- this,
- sourceLanguage,
- slangLanguageVersion,
- &preprocessorHandler);
-
- if (sourceLanguage == SourceLanguage::Unknown)
- sourceLanguage = translationUnit->sourceLanguage;
-
- if (slangLanguageVersion != module->getModuleDecl()->languageVersion)
- {
- sink->diagnose(
- tokens.begin()->getLoc(),
- Diagnostics::languageVersionDiffersFromIncludingModule);
- }
-
- auto outerScope = module->getModuleDecl()->ownedScope;
- parseSourceFile(
- module->getASTBuilder(),
- translationUnit,
- sourceLanguage,
- tokens,
- sink,
- outerScope,
- fileDecl);
-
- module->getModuleDecl()->addMember(fileDecl);
-
- result.fileDecl = fileDecl;
- result.isNew = true;
- return result;
-}
-
-//
-// ModuleDependencyList
-//
-
-void ModuleDependencyList::addDependency(Module* module)
-{
- // If we depend on a module, then we depend on everything it depends on.
- //
- // Note: We are processing these sub-depenencies before adding the
- // `module` itself, so that in the common case a module will always
- // appear *after* everything it depends on.
- //
- // However, this rule is being violated in the compiler right now because
- // the modules for hte top-level translation units of a compile request
- // will be added to the list first (using `addLeafDependency`) to
- // maintain compatibility with old behavior. This may be fixed later.
- //
- for (auto subDependency : module->getModuleDependencyList())
- {
- _addDependency(subDependency);
- }
- _addDependency(module);
-}
-
-void ModuleDependencyList::addLeafDependency(Module* module)
-{
- _addDependency(module);
-}
-
-void ModuleDependencyList::_addDependency(Module* module)
-{
- if (m_moduleSet.contains(module))
- return;
-
- m_moduleList.add(module);
- m_moduleSet.add(module);
-}
-
-//
-// FileDependencyList
-//
-
-void FileDependencyList::addDependency(SourceFile* sourceFile)
-{
- if (m_fileSet.contains(sourceFile))
- return;
-
- m_fileList.add(sourceFile);
- m_fileSet.add(sourceFile);
-}
-
-void FileDependencyList::addDependency(Module* module)
-{
- for (SourceFile* sourceFile : module->getFileDependencyList())
- {
- addDependency(sourceFile);
- }
-}
-
-//
-// Module
-//
-
-Module::Module(Linkage* linkage, ASTBuilder* astBuilder)
- : ComponentType(linkage), m_mangledExportPool(StringSlicePool::Style::Empty)
-{
- if (astBuilder)
- {
- m_astBuilder = astBuilder;
- }
- else
- {
- m_astBuilder = linkage->getASTBuilder();
- }
- getOptionSet() = linkage->m_optionSet;
- addModuleDependency(this);
-}
-
-ISlangUnknown* Module::getInterface(const Guid& guid)
-{
- if (guid == IModule::getTypeGuid())
- return asExternal(this);
- if (guid == IModulePrecompileService_Experimental::getTypeGuid())
- return static_cast<slang::IModulePrecompileService_Experimental*>(this);
- return Super::getInterface(guid);
-}
-
-void Module::buildHash(DigestBuilder<SHA1>& builder)
-{
- builder.append(computeDigest());
-}
-
-slang::DeclReflection* Module::getModuleReflection()
-{
- return (slang::DeclReflection*)m_moduleDecl;
-}
-
-SHA1::Digest Module::computeDigest()
-{
- if (m_digest == SHA1::Digest())
- {
- DigestBuilder<SHA1> digestBuilder;
- auto version = String(getBuildTagString());
- digestBuilder.append(version);
- getOptionSet().buildHash(digestBuilder);
-
- auto fileDependencies = getFileDependencies();
-
- for (auto file : fileDependencies)
- {
- digestBuilder.append(file->getDigest());
- }
- m_digest = digestBuilder.finalize();
- }
- return m_digest;
-}
-
-void Module::addModuleDependency(Module* module)
-{
- m_moduleDependencyList.addDependency(module);
- m_fileDependencyList.addDependency(module);
-}
-
-void Module::addFileDependency(SourceFile* sourceFile)
-{
- m_fileDependencyList.addDependency(sourceFile);
-}
-
-void Module::setModuleDecl(ModuleDecl* moduleDecl)
-{
- m_moduleDecl = moduleDecl;
- moduleDecl->module = this;
-}
-
-void Module::setName(String name)
-{
- m_name = getLinkage()->getNamePool()->getName(name);
-}
-
-
-RefPtr<EntryPoint> Module::findEntryPointByName(UnownedStringSlice const& name)
-{
- for (auto entryPoint : m_entryPoints)
- {
- if (entryPoint->getName()->text.getUnownedSlice() == name)
- return entryPoint;
- }
-
- return nullptr;
-}
-
-RefPtr<EntryPoint> Module::findAndCheckEntryPoint(
- UnownedStringSlice const& name,
- SlangStage stage,
- ISlangBlob** outDiagnostics)
-{
- // If there is already an entrypoint marked with the [shader] attribute,
- // we should just return that.
- //
- if (auto existingEntryPoint = findEntryPointByName(name))
- return existingEntryPoint;
-
- SLANG_AST_BUILDER_RAII(m_astBuilder);
-
- // If the function hasn't been marked as [shader], then it won't be discovered
- // by findEntryPointByName. We need to route this to the `findAndValidateEntryPoint`
- // function. To do that we need to setup a FrontEndCompileRequest and a
- // FrontEndEntryPointRequest.
- //
- DiagnosticSink sink(getLinkage()->getSourceManager(), DiagnosticSink::SourceLocationLexer());
- FrontEndCompileRequest frontEndRequest(getLinkage(), StdWriters::getSingleton(), &sink);
- RefPtr<TranslationUnitRequest> tuRequest = new TranslationUnitRequest(&frontEndRequest);
- tuRequest->module = this;
- tuRequest->moduleName = m_name;
- frontEndRequest.translationUnits.add(tuRequest);
- FrontEndEntryPointRequest entryPointRequest(
- &frontEndRequest,
- 0,
- getLinkage()->getNamePool()->getName(name),
- Profile((Stage)stage));
- auto result = findAndValidateEntryPoint(&entryPointRequest);
- if (outDiagnostics)
- {
- sink.getBlobIfNeeded(outDiagnostics);
- }
- return result;
-}
-
-void Module::_addEntryPoint(EntryPoint* entryPoint)
-{
- m_entryPoints.add(entryPoint);
-}
-
-static bool _canExportDeclSymbol(ASTNodeType type)
-{
- switch (type)
- {
- case ASTNodeType::EmptyDecl:
- {
- return false;
- }
- default:
- break;
- }
-
- return true;
-}
-
-static bool _canRecurseExportSymbol(Decl* decl)
-{
- if (as<FunctionDeclBase>(decl) || as<ScopeDecl>(decl))
- {
- return false;
- }
- return true;
-}
-
-void Module::_processFindDeclsExportSymbolsRec(Decl* decl)
-{
- if (_canExportDeclSymbol(decl->astNodeType))
- {
- // It's a reference to a declaration in another module, so first get the symbol name.
- String mangledName = getMangledName(getCurrentASTBuilder(), decl);
-
- Index index = Index(m_mangledExportPool.add(mangledName));
-
- // TODO(JS): It appears that more than one entity might have the same mangled name.
- // So for now we ignore and just take the first one.
- if (index == m_mangledExportSymbols.getCount())
- {
- m_mangledExportSymbols.add(decl);
- }
- }
-
- if (!_canRecurseExportSymbol(decl))
- {
- // We don't need to recurse any further into this
- return;
- }
-
- // If it's a container process it's children
- if (auto containerDecl = as<ContainerDecl>(decl))
- {
- for (auto child : containerDecl->getDirectMemberDecls())
- {
- _processFindDeclsExportSymbolsRec(child);
- }
- }
-
- // GenericDecl is also a container, so do subsequent test
- if (auto genericDecl = as<GenericDecl>(decl))
- {
- _processFindDeclsExportSymbolsRec(genericDecl->inner);
- }
-}
-
-Decl* Module::findExportedDeclByMangledName(const UnownedStringSlice& mangledName)
-{
- // If this module is a serialized module that is being
- // deserialized on-demand, then we want to use the
- // mangled name mapping that was baked into the serialized
- // data, rather than attempt to enumerate all of the declarations
- // in the module (as would be done if we proceeded to call
- // `ensureExportLookupAcceleratorBuilt()`).
- //
- if (this->m_moduleDecl->isUsingOnDemandDeserializationForExports())
- {
- return m_moduleDecl->_findSerializedDeclByMangledExportName(mangledName);
- }
-
- ensureExportLookupAcceleratorBuilt();
-
- const Index index = m_mangledExportPool.findIndex(mangledName);
- return (index >= 0) ? m_mangledExportSymbols[index] : nullptr;
-}
-
-void Module::ensureExportLookupAcceleratorBuilt()
-{
- // Will be non zero if has been previously attempted
- if (m_mangledExportSymbols.getCount() == 0)
- {
- // Build up the exported mangled name list
- _processFindDeclsExportSymbolsRec(getModuleDecl());
-
- // If nothing found, mark that we have tried looking by making
- // m_mangledExportSymbols.getCount() != 0
- if (m_mangledExportSymbols.getCount() == 0)
- {
- m_mangledExportSymbols.add(nullptr);
- }
- }
-}
-
-Count Module::getExportedDeclCount()
-{
- ensureExportLookupAcceleratorBuilt();
-
- return m_mangledExportPool.getSlicesCount();
-}
-
-Decl* Module::getExportedDecl(Index index)
-{
- ensureExportLookupAcceleratorBuilt();
- return m_mangledExportSymbols[index];
-}
-
-UnownedStringSlice Module::getExportedDeclMangledName(Index index)
-{
- ensureExportLookupAcceleratorBuilt();
- return m_mangledExportPool.getSlices()[index];
-}
-
-// ComponentType
-
-ComponentType::ComponentType(Linkage* linkage)
- : m_linkage(linkage)
-{
-}
-
-ComponentType* asInternal(slang::IComponentType* inComponentType)
-{
- // Note: we use a `queryInterface` here instead of just a `static_cast`
- // to ensure that the `IComponentType` we get is the preferred/canonical
- // one, which shares its address with the `ComponentType`.
- //
- // TODO: An alternative choice here would be to have a "magic" IID that
- // we pass into `queryInterface` that returns the `ComponentType` directly
- // (without even `addRef`-ing it).
- //
- ComPtr<slang::IComponentType> componentType;
- inComponentType->queryInterface(SLANG_IID_PPV_ARGS(componentType.writeRef()));
- return static_cast<ComponentType*>(componentType.get());
-}
-
-ISlangUnknown* ComponentType::getInterface(Guid const& guid)
-{
- if (guid == ISlangUnknown::getTypeGuid() || guid == slang::IComponentType::getTypeGuid())
- {
- return static_cast<slang::IComponentType*>(this);
- }
- if (guid == IModulePrecompileService_Experimental::getTypeGuid())
- return static_cast<slang::IModulePrecompileService_Experimental*>(this);
- if (guid == IComponentType2::getTypeGuid())
- return static_cast<slang::IComponentType2*>(this);
- return nullptr;
-}
-
-SLANG_NO_THROW slang::ISession* SLANG_MCALL ComponentType::getSession()
-{
- return m_linkage;
-}
-
-SLANG_NO_THROW slang::ProgramLayout* SLANG_MCALL
-ComponentType::getLayout(Int targetIndex, slang::IBlob** outDiagnostics)
-{
- auto linkage = getLinkage();
- if (targetIndex < 0 || targetIndex >= linkage->targets.getCount())
- return nullptr;
- auto target = linkage->targets[targetIndex];
-
- DiagnosticSink sink(linkage->getSourceManager(), Lexer::sourceLocationLexer);
- auto programLayout = getTargetProgram(target)->getOrCreateLayout(&sink);
- sink.getBlobIfNeeded(outDiagnostics);
-
- return asExternal(programLayout);
-}
-
-static ICastable* _findDiagnosticRepresentation(IArtifact* artifact)
-{
- if (auto rep = findAssociatedRepresentation<IArtifactDiagnostics>(artifact))
- {
- return rep;
- }
-
- for (auto associated : artifact->getAssociated())
- {
- if (isDerivedFrom(associated->getDesc().payload, ArtifactPayload::Diagnostics))
- {
- return associated;
- }
- }
- return nullptr;
-}
-
-static IArtifact* _findObfuscatedSourceMap(IArtifact* artifact)
-{
- // If we find any obfuscated source maps, we are done
- for (auto associated : artifact->getAssociated())
- {
- const auto desc = associated->getDesc();
-
- if (isDerivedFrom(desc.payload, ArtifactPayload::SourceMap) &&
- isDerivedFrom(desc.style, ArtifactStyle::Obfuscated))
- {
- return associated;
- }
- }
- return nullptr;
-}
-
-SLANG_NO_THROW SlangResult SLANG_MCALL ComponentType::getResultAsFileSystem(
- SlangInt entryPointIndex,
- Int targetIndex,
- ISlangMutableFileSystem** outFileSystem)
-{
- ComPtr<ISlangBlob> diagnostics;
- ComPtr<ISlangBlob> code;
-
- SLANG_RETURN_ON_FAIL(
- getEntryPointCode(entryPointIndex, targetIndex, diagnostics.writeRef(), code.writeRef()));
-
- auto linkage = getLinkage();
-
- auto target = linkage->targets[targetIndex];
-
- auto targetProgram = getTargetProgram(target);
-
- IArtifact* artifact = targetProgram->getExistingEntryPointResult(entryPointIndex);
-
- // Add diagnostics id needs be...
- if (diagnostics && !_findDiagnosticRepresentation(artifact))
- {
- // Add as an associated
-
- auto diagnosticsArtifact = Artifact::create(
- ArtifactDesc::make(Artifact::Kind::HumanText, ArtifactPayload::Diagnostics));
- diagnosticsArtifact->addRepresentationUnknown(diagnostics);
-
- artifact->addAssociated(diagnosticsArtifact);
-
- SLANG_ASSERT(diagnosticsArtifact == _findDiagnosticRepresentation(artifact));
- }
-
- // Add obfuscated source maps
- if (!_findObfuscatedSourceMap(artifact))
- {
- List<IRModule*> irModules;
- enumerateIRModules([&](IRModule* irModule) -> void { irModules.add(irModule); });
-
- for (auto irModule : irModules)
- {
- if (auto obfuscatedSourceMap = irModule->getObfuscatedSourceMap())
- {
- auto artifactDesc = ArtifactDesc::make(
- ArtifactKind::Json,
- ArtifactPayload::SourceMap,
- ArtifactStyle::Obfuscated);
-
- // Create the source map artifact
- auto sourceMapArtifact = Artifact::create(
- artifactDesc,
- obfuscatedSourceMap->get().m_file.getUnownedSlice());
-
- sourceMapArtifact->addRepresentation(obfuscatedSourceMap);
-
- // associate with the artifact
- artifact->addAssociated(sourceMapArtifact);
- }
- }
- }
-
- // Turn into a file system and return
- ComPtr<ISlangMutableFileSystem> fileSystem(new MemoryFileSystem);
-
- // Filter the containerArtifact into things that can be written
- ComPtr<IArtifact> writeArtifact;
- SLANG_RETURN_ON_FAIL(ArtifactContainerUtil::filter(artifact, writeArtifact));
- SLANG_RETURN_ON_FAIL(ArtifactContainerUtil::writeContainer(writeArtifact, "", fileSystem));
-
- *outFileSystem = fileSystem.detach();
-
- return SLANG_OK;
-}
-
-SLANG_NO_THROW SlangResult SLANG_MCALL ComponentType::getEntryPointCode(
- SlangInt entryPointIndex,
- Int targetIndex,
- slang::IBlob** outCode,
- slang::IBlob** outDiagnostics)
-{
- auto linkage = getLinkage();
- if (targetIndex < 0 || targetIndex >= linkage->targets.getCount())
- return SLANG_E_INVALID_ARG;
- auto target = linkage->targets[targetIndex];
-
- auto targetProgram = getTargetProgram(target);
-
- DiagnosticSink sink(linkage->getSourceManager(), Lexer::sourceLocationLexer);
- applySettingsToDiagnosticSink(&sink, &sink, linkage->m_optionSet);
- applySettingsToDiagnosticSink(&sink, &sink, m_optionSet);
-
- IArtifact* artifact = targetProgram->getOrCreateEntryPointResult(entryPointIndex, &sink);
- sink.getBlobIfNeeded(outDiagnostics);
-
- if (artifact == nullptr)
- return SLANG_FAIL;
-
- return artifact->loadBlob(ArtifactKeep::Yes, outCode);
-}
-
-SLANG_NO_THROW void SLANG_MCALL ComponentType::getEntryPointHash(
- SlangInt entryPointIndex,
- SlangInt targetIndex,
- slang::IBlob** outHash)
-{
- DigestBuilder<SHA1> builder;
-
- // A note on enums that may be hashed in as part of the following two function calls:
- //
- // While enums are not guaranteed to be encoded the same way across all versions of
- // the compiler, part of hashing the linkage is hashing in the compiler version.
- // Consequently, any encoding differences as a result of different compiler versions
- // will already be reflected in the resulting hash.
- getLinkage()->buildHash(builder, targetIndex);
-
- buildHash(builder);
-
- // Add the name and name override for the specified entry point to the hash.
- auto entryPoint = getEntryPoint(entryPointIndex);
- if (entryPoint)
- {
- auto entryPointName = entryPoint->getName()->text;
- builder.append(entryPointName);
- auto entryPointMangledName = getEntryPointMangledName(entryPointIndex);
- builder.append(entryPointMangledName);
- auto entryPointNameOverride = getEntryPointNameOverride(entryPointIndex);
- builder.append(entryPointNameOverride);
- }
-
- auto hash = builder.finalize().toBlob();
- *outHash = hash.detach();
-}
-
-SLANG_NO_THROW SlangResult SLANG_MCALL ComponentType::getEntryPointHostCallable(
- int entryPointIndex,
- int targetIndex,
- ISlangSharedLibrary** outSharedLibrary,
- slang::IBlob** outDiagnostics)
-{
- auto linkage = getLinkage();
- if (targetIndex < 0 || targetIndex >= linkage->targets.getCount())
- return SLANG_E_INVALID_ARG;
- auto target = linkage->targets[targetIndex];
-
- auto targetProgram = getTargetProgram(target);
-
- DiagnosticSink sink(linkage->getSourceManager(), Lexer::sourceLocationLexer);
- applySettingsToDiagnosticSink(&sink, &sink, m_optionSet);
-
- IArtifact* artifact = targetProgram->getOrCreateEntryPointResult(entryPointIndex, &sink);
- sink.getBlobIfNeeded(outDiagnostics);
-
- if (artifact == nullptr)
- return SLANG_FAIL;
-
- return artifact->loadSharedLibrary(ArtifactKeep::Yes, outSharedLibrary);
-}
-
-SLANG_NO_THROW SlangResult SLANG_MCALL ComponentType::getEntryPointMetadata(
- SlangInt entryPointIndex,
- Int targetIndex,
- slang::IMetadata** outMetadata,
- slang::IBlob** outDiagnostics)
-{
- auto linkage = getLinkage();
- if (targetIndex < 0 || targetIndex >= linkage->targets.getCount())
- return SLANG_E_INVALID_ARG;
- auto target = linkage->targets[targetIndex];
-
- auto targetProgram = getTargetProgram(target);
-
- DiagnosticSink sink(linkage->getSourceManager(), Lexer::sourceLocationLexer);
- applySettingsToDiagnosticSink(&sink, &sink, linkage->m_optionSet);
- applySettingsToDiagnosticSink(&sink, &sink, m_optionSet);
-
- IArtifact* artifact = targetProgram->getOrCreateEntryPointResult(entryPointIndex, &sink);
- sink.getBlobIfNeeded(outDiagnostics);
-
- if (artifact == nullptr)
- return SLANG_E_NOT_AVAILABLE;
-
- auto metadata = findAssociatedRepresentation<IArtifactPostEmitMetadata>(artifact);
- if (!metadata)
- return SLANG_E_NOT_AVAILABLE;
-
- *outMetadata = static_cast<slang::IMetadata*>(metadata);
- (*outMetadata)->addRef();
- return SLANG_OK;
-}
-
-RefPtr<ComponentType> ComponentType::specialize(
- SpecializationArg const* inSpecializationArgs,
- SlangInt specializationArgCount,
- DiagnosticSink* sink)
-{
- if (specializationArgCount == 0)
- {
- return this;
- }
-
- List<SpecializationArg> specializationArgs;
- specializationArgs.addRange(inSpecializationArgs, specializationArgCount);
-
- // We next need to validate that the specialization arguments
- // make sense, and also expand them to include any derived data
- // (e.g., interface conformance witnesses) that doesn't get
- // passed explicitly through the API interface.
- //
- RefPtr<SpecializationInfo> specializationInfo =
- _validateSpecializationArgs(specializationArgs.getBuffer(), specializationArgCount, sink);
-
- return new SpecializedComponentType(this, specializationInfo, specializationArgs, sink);
-}
-
-SLANG_NO_THROW SlangResult SLANG_MCALL ComponentType::specialize(
- slang::SpecializationArg const* specializationArgs,
- SlangInt specializationArgCount,
- slang::IComponentType** outSpecializedComponentType,
- ISlangBlob** outDiagnostics)
-{
- DiagnosticSink sink(getLinkage()->getSourceManager(), Lexer::sourceLocationLexer);
-
- // First let's check if the number of arguments given matches
- // the number of parameters that are present on this component type.
- //
- auto specializationParamCount = getSpecializationParamCount();
- if (specializationArgCount != specializationParamCount)
- {
- sink.diagnose(
- SourceLoc(),
- Diagnostics::mismatchSpecializationArguments,
- specializationParamCount,
- specializationArgCount);
- sink.getBlobIfNeeded(outDiagnostics);
- return SLANG_FAIL;
- }
-
- List<SpecializationArg> expandedArgs;
- for (Int aa = 0; aa < specializationArgCount; ++aa)
- {
- auto apiArg = specializationArgs[aa];
-
- SpecializationArg expandedArg;
- switch (apiArg.kind)
- {
- case slang::SpecializationArg::Kind::Type:
- expandedArg.val = asInternal(apiArg.type);
- break;
-
- default:
- sink.getBlobIfNeeded(outDiagnostics);
- return SLANG_FAIL;
- }
- expandedArgs.add(expandedArg);
- }
-
- auto specializedComponentType =
- specialize(expandedArgs.getBuffer(), expandedArgs.getCount(), &sink);
-
- sink.getBlobIfNeeded(outDiagnostics);
-
- *outSpecializedComponentType = specializedComponentType.detach();
-
- return SLANG_OK;
-}
-
-SLANG_NO_THROW SlangResult SLANG_MCALL
-ComponentType::renameEntryPoint(const char* newName, IComponentType** outEntryPoint)
-{
- RefPtr<RenamedEntryPointComponentType> result =
- new RenamedEntryPointComponentType(this, newName);
- *outEntryPoint = result.detach();
- return SLANG_OK;
-}
-
-RefPtr<ComponentType> fillRequirements(ComponentType* inComponentType);
-
-SLANG_NO_THROW SlangResult SLANG_MCALL
-ComponentType::link(slang::IComponentType** outLinkedComponentType, ISlangBlob** outDiagnostics)
-{
- // TODO: It should be possible for `fillRequirements` to fail,
- // in cases where we have a dependency that can't be automatically
- // resolved.
- //
- SLANG_UNUSED(outDiagnostics);
-
- DiagnosticSink sink(getLinkage()->getSourceManager(), Lexer::sourceLocationLexer);
-
- try
- {
- auto linked = fillRequirements(this);
- if (!linked)
- return SLANG_FAIL;
-
- *outLinkedComponentType = ComPtr<slang::IComponentType>(linked).detach();
- return SLANG_OK;
- }
- catch (const AbortCompilationException& e)
- {
- outputExceptionDiagnostic(e, sink, outDiagnostics);
- return SLANG_FAIL;
- }
- catch (const Exception& e)
- {
- outputExceptionDiagnostic(e, sink, outDiagnostics);
- return SLANG_FAIL;
- }
- catch (...)
- {
- outputExceptionDiagnostic(sink, outDiagnostics);
- return SLANG_FAIL;
- }
-}
-
-SLANG_NO_THROW SlangResult SLANG_MCALL ComponentType::linkWithOptions(
- slang::IComponentType** outLinkedComponentType,
- uint32_t count,
- slang::CompilerOptionEntry* entries,
- ISlangBlob** outDiagnostics)
-{
- SLANG_RETURN_ON_FAIL(link(outLinkedComponentType, outDiagnostics));
-
- auto linked = *outLinkedComponentType;
-
- if (linked)
- {
- static_cast<ComponentType*>(linked)->getOptionSet().load(count, entries);
- }
-
- return SLANG_OK;
-}
-
-SLANG_NO_THROW SlangResult SLANG_MCALL ComponentType::getEntryPointCompileResult(
- SlangInt entryPointIndex,
- Int targetIndex,
- slang::ICompileResult** outCompileResult,
- slang::IBlob** outDiagnostics)
-{
- auto linkage = getLinkage();
- if (targetIndex < 0 || targetIndex >= linkage->targets.getCount())
- return SLANG_E_INVALID_ARG;
- auto target = linkage->targets[targetIndex];
-
- auto targetProgram = getTargetProgram(target);
-
- DiagnosticSink sink(linkage->getSourceManager(), Lexer::sourceLocationLexer);
- applySettingsToDiagnosticSink(&sink, &sink, linkage->m_optionSet);
- applySettingsToDiagnosticSink(&sink, &sink, m_optionSet);
-
- IArtifact* artifact = targetProgram->getOrCreateEntryPointResult(entryPointIndex, &sink);
- sink.getBlobIfNeeded(outDiagnostics);
- if (artifact == nullptr)
- return SLANG_E_NOT_AVAILABLE;
-
- *outCompileResult = static_cast<slang::ICompileResult*>(artifact);
- (*outCompileResult)->addRef();
- return SLANG_OK;
-}
-
-SLANG_NO_THROW SlangResult SLANG_MCALL ComponentType::getTargetCompileResult(
- Int targetIndex,
- slang::ICompileResult** outCompileResult,
- slang::IBlob** outDiagnostics)
-{
- IArtifact* artifact = getTargetArtifact(targetIndex, outDiagnostics);
- if (artifact == nullptr)
- return SLANG_E_NOT_AVAILABLE;
-
- *outCompileResult = static_cast<slang::ICompileResult*>(artifact);
- (*outCompileResult)->addRef();
- return SLANG_OK;
-}
-
-/// Visitor used by `ComponentType::enumerateModules`
-struct EnumerateModulesVisitor : ComponentTypeVisitor
-{
- EnumerateModulesVisitor(ComponentType::EnumerateModulesCallback callback, void* userData)
- : m_callback(callback), m_userData(userData)
- {
- }
-
- ComponentType::EnumerateModulesCallback m_callback;
- void* m_userData;
-
- void visitEntryPoint(EntryPoint*, EntryPoint::EntryPointSpecializationInfo*) SLANG_OVERRIDE {}
-
- void visitRenamedEntryPoint(
- RenamedEntryPointComponentType* entryPoint,
- EntryPoint::EntryPointSpecializationInfo* specializationInfo) SLANG_OVERRIDE
- {
- entryPoint->getBase()->acceptVisitor(this, specializationInfo);
- }
-
- void visitModule(Module* module, Module::ModuleSpecializationInfo*) SLANG_OVERRIDE
- {
- m_callback(module, m_userData);
- }
-
- void visitComposite(
- CompositeComponentType* composite,
- CompositeComponentType::CompositeSpecializationInfo* specializationInfo) SLANG_OVERRIDE
- {
- visitChildren(composite, specializationInfo);
- }
-
- void visitSpecialized(SpecializedComponentType* specialized) SLANG_OVERRIDE
- {
- visitChildren(specialized);
- }
-
- void visitTypeConformance(TypeConformance* conformance) SLANG_OVERRIDE
- {
- SLANG_UNUSED(conformance);
- }
-};
-
-
-void ComponentType::enumerateModules(EnumerateModulesCallback callback, void* userData)
-{
- EnumerateModulesVisitor visitor(callback, userData);
- acceptVisitor(&visitor, nullptr);
-}
-
-/// Visitor used by `ComponentType::enumerateIRModules`
-struct EnumerateIRModulesVisitor : ComponentTypeVisitor
-{
- EnumerateIRModulesVisitor(ComponentType::EnumerateIRModulesCallback callback, void* userData)
- : m_callback(callback), m_userData(userData)
- {
- }
-
- ComponentType::EnumerateIRModulesCallback m_callback;
- void* m_userData;
-
- void visitEntryPoint(EntryPoint*, EntryPoint::EntryPointSpecializationInfo*) SLANG_OVERRIDE {}
-
- void visitRenamedEntryPoint(
- RenamedEntryPointComponentType* entryPoint,
- EntryPoint::EntryPointSpecializationInfo* specializationInfo) SLANG_OVERRIDE
- {
- entryPoint->getBase()->acceptVisitor(this, specializationInfo);
- }
-
- void visitModule(Module* module, Module::ModuleSpecializationInfo*) SLANG_OVERRIDE
- {
- m_callback(module->getIRModule(), m_userData);
- }
-
- void visitComposite(
- CompositeComponentType* composite,
- CompositeComponentType::CompositeSpecializationInfo* specializationInfo) SLANG_OVERRIDE
- {
- visitChildren(composite, specializationInfo);
- }
-
- void visitSpecialized(SpecializedComponentType* specialized) SLANG_OVERRIDE
- {
- visitChildren(specialized);
-
- m_callback(specialized->getIRModule(), m_userData);
- }
-
- void visitTypeConformance(TypeConformance* conformance) SLANG_OVERRIDE
- {
- m_callback(conformance->getIRModule(), m_userData);
- }
-};
-
-void ComponentType::enumerateIRModules(EnumerateIRModulesCallback callback, void* userData)
-{
- EnumerateIRModulesVisitor visitor(callback, userData);
- acceptVisitor(&visitor, nullptr);
-}
-
-IArtifact* ComponentType::getTargetArtifact(Int targetIndex, slang::IBlob** outDiagnostics)
-{
- auto linkage = getLinkage();
- if (targetIndex < 0 || targetIndex >= linkage->targets.getCount())
- return nullptr;
- ComPtr<IArtifact> artifact;
- if (m_targetArtifacts.tryGetValue(targetIndex, artifact))
- {
- return artifact.get();
- }
-
- // If the user hasn't specified any entry points, then we should
- // discover all entrypoints that are defined in linked modules, and
- // include all of them in the compile.
- //
- if (getEntryPointCount() == 0)
- {
- List<Module*> modules;
- this->enumerateModules([&](Module* module) { modules.add(module); });
- List<RefPtr<ComponentType>> components;
- components.add(this);
- bool entryPointsDiscovered = false;
- for (auto module : modules)
- {
- for (auto entryPoint : module->getEntryPoints())
- {
- components.add(entryPoint);
- entryPointsDiscovered = true;
- }
- }
-
- // If any entry points were discovered, then we should emit the program with entrypoints
- // linked.
- if (entryPointsDiscovered)
- {
- RefPtr<CompositeComponentType> composite =
- new CompositeComponentType(linkage, components);
- ComPtr<IComponentType> linkedComponentType;
- SLANG_RETURN_NULL_ON_FAIL(
- composite->link(linkedComponentType.writeRef(), outDiagnostics));
- auto targetArtifact = static_cast<ComponentType*>(linkedComponentType.get())
- ->getTargetArtifact(targetIndex, outDiagnostics);
- if (targetArtifact)
- {
- m_targetArtifacts[targetIndex] = targetArtifact;
- }
- return targetArtifact;
- }
- }
-
- auto target = linkage->targets[targetIndex];
- auto targetProgram = getTargetProgram(target);
-
- DiagnosticSink sink(linkage->getSourceManager(), Lexer::sourceLocationLexer);
- applySettingsToDiagnosticSink(&sink, &sink, linkage->m_optionSet);
- applySettingsToDiagnosticSink(&sink, &sink, m_optionSet);
-
- IArtifact* targetArtifact = targetProgram->getOrCreateWholeProgramResult(&sink);
- sink.getBlobIfNeeded(outDiagnostics);
- m_targetArtifacts[targetIndex] = ComPtr<IArtifact>(targetArtifact);
- return targetArtifact;
-}
-
-SLANG_NO_THROW SlangResult SLANG_MCALL
-ComponentType::getTargetCode(Int targetIndex, slang::IBlob** outCode, slang::IBlob** outDiagnostics)
-{
- IArtifact* artifact = getTargetArtifact(targetIndex, outDiagnostics);
-
- if (artifact == nullptr)
- return SLANG_FAIL;
-
- return artifact->loadBlob(ArtifactKeep::Yes, outCode);
-}
-
-SLANG_NO_THROW SlangResult SLANG_MCALL ComponentType::getTargetMetadata(
- Int targetIndex,
- slang::IMetadata** outMetadata,
- slang::IBlob** outDiagnostics)
-{
- IArtifact* artifact = getTargetArtifact(targetIndex, outDiagnostics);
-
- if (artifact == nullptr)
- return SLANG_FAIL;
-
- auto metadata = findAssociatedRepresentation<IArtifactPostEmitMetadata>(artifact);
- if (!metadata)
- return SLANG_E_NOT_AVAILABLE;
- *outMetadata = static_cast<slang::IMetadata*>(metadata);
- (*outMetadata)->addRef();
- return SLANG_OK;
-}
-
-//
-// CompositeComponentType
-//
-
-RefPtr<ComponentType> CompositeComponentType::create(
- Linkage* linkage,
- List<RefPtr<ComponentType>> const& childComponents)
-{
- // TODO: We should ideally be caching the results of
- // composition on the `linkage`, so that if we get
- // asked for the same composite again later we re-use
- // it rather than re-create it.
- //
- // Similarly, we might want to do some amount of
- // work to "canonicalize" the input for composition.
- // E.g., if the user does:
- //
- // X = compose(A,B);
- // Y = compose(C,D);
- // Z = compose(X,Y);
- //
- // W = compose(A, B, C, D);
- //
- // Then there is no observable difference between
- // Z and W, so we might prefer to have them be identical.
-
- // If there is only a single child, then we should
- // just return that child rather than create a dummy composite.
- //
- if (childComponents.getCount() == 1)
- {
- return childComponents[0];
- }
-
- return new CompositeComponentType(linkage, childComponents);
-}
-
-
-CompositeComponentType::CompositeComponentType(
- Linkage* linkage,
- List<RefPtr<ComponentType>> const& childComponents)
- : ComponentType(linkage), m_childComponents(childComponents)
-{
- HashSet<ComponentType*> requirementsSet;
- for (auto child : childComponents)
- {
- child->enumerateModules([&](Module* module) { requirementsSet.add(module); });
- }
-
- for (auto child : childComponents)
- {
- auto childEntryPointCount = child->getEntryPointCount();
- for (Index cc = 0; cc < childEntryPointCount; ++cc)
- {
- m_entryPoints.add(child->getEntryPoint(cc));
- m_entryPointMangledNames.add(child->getEntryPointMangledName(cc));
- m_entryPointNameOverrides.add(child->getEntryPointNameOverride(cc));
- }
-
- auto childShaderParamCount = child->getShaderParamCount();
- for (Index pp = 0; pp < childShaderParamCount; ++pp)
- {
- m_shaderParams.add(child->getShaderParam(pp));
- }
-
- auto childSpecializationParamCount = child->getSpecializationParamCount();
- for (Index pp = 0; pp < childSpecializationParamCount; ++pp)
- {
- m_specializationParams.add(child->getSpecializationParam(pp));
- }
-
- for (auto module : child->getModuleDependencies())
- {
- m_moduleDependencyList.addDependency(module);
- }
- for (auto sourceFile : child->getFileDependencies())
- {
- m_fileDependencyList.addDependency(sourceFile);
- }
-
- auto childRequirementCount = child->getRequirementCount();
- for (Index rr = 0; rr < childRequirementCount; ++rr)
- {
- auto childRequirement = child->getRequirement(rr);
- if (!requirementsSet.contains(childRequirement))
- {
- requirementsSet.add(childRequirement);
- m_requirements.add(childRequirement);
- }
- }
- }
-}
-
-void CompositeComponentType::buildHash(DigestBuilder<SHA1>& builder)
-{
- auto componentCount = getChildComponentCount();
-
- for (Index i = 0; i < componentCount; ++i)
- {
- getChildComponent(i)->buildHash(builder);
- }
-}
-
-Index CompositeComponentType::getEntryPointCount()
-{
- return m_entryPoints.getCount();
-}
-
-RefPtr<EntryPoint> CompositeComponentType::getEntryPoint(Index index)
-{
- return m_entryPoints[index];
-}
-
-String CompositeComponentType::getEntryPointMangledName(Index index)
-{
- return m_entryPointMangledNames[index];
-}
-
-String CompositeComponentType::getEntryPointNameOverride(Index index)
-{
- return m_entryPointNameOverrides[index];
-}
-
-Index CompositeComponentType::getShaderParamCount()
-{
- return m_shaderParams.getCount();
-}
-
-ShaderParamInfo CompositeComponentType::getShaderParam(Index index)
-{
- return m_shaderParams[index];
-}
-
-Index CompositeComponentType::getSpecializationParamCount()
-{
- return m_specializationParams.getCount();
-}
-
-SpecializationParam const& CompositeComponentType::getSpecializationParam(Index index)
-{
- return m_specializationParams[index];
-}
-
-Index CompositeComponentType::getRequirementCount()
-{
- return m_requirements.getCount();
-}
-
-RefPtr<ComponentType> CompositeComponentType::getRequirement(Index index)
-{
- return m_requirements[index];
-}
-
-List<Module*> const& CompositeComponentType::getModuleDependencies()
-{
- return m_moduleDependencyList.getModuleList();
-}
-
-List<SourceFile*> const& CompositeComponentType::getFileDependencies()
-{
- return m_fileDependencyList.getFileList();
-}
-
-void CompositeComponentType::acceptVisitor(
- ComponentTypeVisitor* visitor,
- SpecializationInfo* specializationInfo)
-{
- visitor->visitComposite(this, as<CompositeSpecializationInfo>(specializationInfo));
-}
-
-RefPtr<ComponentType::SpecializationInfo> CompositeComponentType::_validateSpecializationArgsImpl(
- SpecializationArg const* args,
- Index argCount,
- DiagnosticSink* sink)
-{
- SLANG_UNUSED(argCount);
-
- RefPtr<CompositeSpecializationInfo> specializationInfo = new CompositeSpecializationInfo();
-
- Index offset = 0;
- for (auto child : m_childComponents)
- {
- auto childParamCount = child->getSpecializationParamCount();
- SLANG_ASSERT(offset + childParamCount <= argCount);
-
- auto childInfo = child->_validateSpecializationArgs(args + offset, childParamCount, sink);
-
- specializationInfo->childInfos.add(childInfo);
-
- offset += childParamCount;
- }
- return specializationInfo;
-}
-
-//
-// SpecializedComponentType
-//
-
-/// Utility type for collecting modules references by types/declarations
-struct SpecializationArgModuleCollector : ComponentTypeVisitor
-{
- HashSet<Module*> m_modulesSet;
- List<Module*> m_modulesList;
-
- void addModule(Module* module)
- {
- m_modulesList.add(module);
- m_modulesSet.add(module);
- }
-
- void maybeAddModule(Module* module)
- {
- if (!module)
- return;
- if (m_modulesSet.contains(module))
- return;
-
- addModule(module);
- }
-
- void collectReferencedModules(Decl* decl)
- {
- auto module = getModule(decl);
- maybeAddModule(module);
- }
-
- void collectReferencedModules(SubstitutionSet substitutions)
- {
- substitutions.forEachGenericSubstitution(
- [this](GenericDecl*, Val::OperandView<Val> args)
- {
- for (auto arg : args)
- {
- collectReferencedModules(arg);
- }
- });
- }
-
- void collectReferencedModules(DeclRefBase* declRef)
- {
- collectReferencedModules(declRef->getDecl());
- collectReferencedModules(SubstitutionSet(declRef));
- }
-
- void collectReferencedModules(Type* type)
- {
- if (auto declRefType = as<DeclRefType>(type))
- {
- collectReferencedModules(declRefType->getDeclRef());
- }
-
- // TODO: Handle non-decl-ref composite type cases
- // (e.g., function types).
- }
-
- void collectReferencedModules(Val* val)
- {
- if (auto type = as<Type>(val))
- {
- collectReferencedModules(type);
- }
- else if (auto declRefVal = as<DeclRefIntVal>(val))
- {
- collectReferencedModules(declRefVal->getDeclRef());
- }
-
- // TODO: other cases of values that could reference
- // a declaration.
- }
-
- void collectReferencedModules(List<ExpandedSpecializationArg> const& args)
- {
- for (auto arg : args)
- {
- collectReferencedModules(arg.val);
- collectReferencedModules(arg.witness);
- }
- }
-
- //
- // ComponentTypeVisitor methods
- //
-
- void visitEntryPoint(
- EntryPoint* entryPoint,
- EntryPoint::EntryPointSpecializationInfo* specializationInfo) SLANG_OVERRIDE
- {
- SLANG_UNUSED(entryPoint);
-
- if (!specializationInfo)
- return;
-
- collectReferencedModules(specializationInfo->specializedFuncDeclRef);
- collectReferencedModules(specializationInfo->existentialSpecializationArgs);
- }
-
- void visitRenamedEntryPoint(
- RenamedEntryPointComponentType* entryPoint,
- EntryPoint::EntryPointSpecializationInfo* specializationInfo) SLANG_OVERRIDE
- {
- entryPoint->getBase()->acceptVisitor(this, specializationInfo);
- }
-
- void visitModule(Module* module, Module::ModuleSpecializationInfo* specializationInfo)
- SLANG_OVERRIDE
- {
- SLANG_UNUSED(module);
-
- if (!specializationInfo)
- return;
-
- for (auto arg : specializationInfo->genericArgs)
- {
- collectReferencedModules(arg.argVal);
- }
- collectReferencedModules(specializationInfo->existentialArgs);
- }
-
- void visitComposite(
- CompositeComponentType* composite,
- CompositeComponentType::CompositeSpecializationInfo* specializationInfo) SLANG_OVERRIDE
- {
- visitChildren(composite, specializationInfo);
- }
-
- void visitSpecialized(SpecializedComponentType* specialized) SLANG_OVERRIDE
- {
- visitChildren(specialized);
- }
-
- void visitTypeConformance(TypeConformance* conformance) SLANG_OVERRIDE
- {
- SLANG_UNUSED(conformance);
- }
-};
-
-SpecializedComponentType::SpecializedComponentType(
- ComponentType* base,
- ComponentType::SpecializationInfo* specializationInfo,
- List<SpecializationArg> const& specializationArgs,
- DiagnosticSink* sink)
- : ComponentType(base->getLinkage())
- , m_base(base)
- , m_specializationInfo(specializationInfo)
- , m_specializationArgs(specializationArgs)
-{
- m_optionSet.overrideWith(base->getOptionSet());
-
- m_irModule = generateIRForSpecializedComponentType(this, sink);
-
- // We need to account for the fact that a specialized
- // entity like `myShader<SomeType>` needs to not only
- // depend on the module(s) that `myShader` depends on,
- // but also on any modules that `SomeType` depends on.
- //
- // We will set up a "collector" type that will be
- // used to build a list of these additional modules.
- //
- SpecializationArgModuleCollector moduleCollector;
-
- // We don't want to go adding additional requirements for
- // modules that the base component type already includes,
- // so we will add those to the set of modules in
- // the collector before we starting trying to add others.
- //
- base->enumerateModules([&](Module* module) { moduleCollector.m_modulesSet.add(module); });
-
- // In order to collect the additional modules, we need
- // to inspect the specialization arguments and see what
- // they depend on.
- //
- // Naively, it seems like we'd just want to iterate
- // over `specializationArgs`, which gives the specialization
- // arguments as the user supplied them. However, such
- // an approach would have a subtle problem.
- //
- // If we have a generic entry point like:
- //
- // // In module A
- // myShader<T : IThing>
- //
- //
- // And the type `SomeType` that is being used as an argument doesn't
- // directly conform to `IThing`:
- //
- // // In module B
- // struct SomeType { ... }
- //
- // and the conformance of `SomeType` to `IThing` is
- // coming from yet another module:
- //
- // // In module C
- // import B;
- // extension SomeType : IThing { ... }
- //
- // In this case, the specialized component for `myShader<SomeType>`
- // needs to depend on all of:
- //
- // * Module A, because it defines `myShader`
- // * Module B, because it defines `SomeType`
- // * Module C, because it defines the conformance `SomeType : IThing`
- //
- // We thus need to iterate over a form of the specialization
- // arguments that includes the "expanded" arguments like
- // interface conformance witnesses that got added during
- // semantic checking.
- //
- // The expanded arguments are being stored in the `specializationInfo`
- // today (for use by downstream code generation), and the easiest
- // way to walk that information and get to the leaf nodes where
- // the expanded arguments are stored is to apply a visitor to
- // the specialized component type we are in the middle of constructing.
- //
- moduleCollector.visitSpecialized(this);
-
- // Now that we've collected our additional information, we can
- // start to build up the final lists for the specialized component type.
- //
- // The starting point for our lists comes from the base component type.
- //
- m_moduleDependencies = base->getModuleDependencies();
- m_fileDependencies = base->getFileDependencies();
-
- Index baseRequirementCount = base->getRequirementCount();
- for (Index r = 0; r < baseRequirementCount; r++)
- {
- m_requirements.add(base->getRequirement(r));
- }
-
- // The specialized component type will need to have additional
- // dependencies and requirements based on the modules that
- // were collected when looking at the specialization arguments.
-
- // We want to avoid adding the same file dependency more than once.
- //
- HashSet<SourceFile*> fileDependencySet;
- for (SourceFile* sourceFile : m_fileDependencies)
- fileDependencySet.add(sourceFile);
-
- for (auto module : moduleCollector.m_modulesList)
- {
- // The specialized component type will have an open (unsatisfied)
- // requirement for each of the modules that its specialization
- // arguments need.
- //
- // Note: what this means in practice is that the component type
- // records that the given module(s) will need to be linked in
- // before final code can be generated, but it importantly
- // does not dictate the final placement of the parameters from
- // those modules in the layout.
- //
- m_requirements.add(module);
-
- // The speciialized component type will also have a dependency
- // on all the files that any of the modules involved in
- // it depend on (including those that are required but not
- // yet linked in).
- //
- // The file path information is what a client would need to
- // use to decide if kernel code is out of date compared to
- // source files, so we want to include anything that could
- // affect the validity of generated code.
- //
- for (SourceFile* sourceFile : module->getFileDependencies())
- {
- if (fileDependencySet.contains(sourceFile))
- continue;
- fileDependencySet.add(sourceFile);
- m_fileDependencies.add(sourceFile);
- }
-
- // Finalyl we also add the module for the specialization arguments
- // to the list of modules that would be used for legacy lookup
- // operations where we need an implicit/default scope to use
- // and want it to be expansive.
- //
- // TODO: This stuff really isn't worth keeping around long
- // term, and we should ditch the entire "legacy lookup" idea.
- //
- m_moduleDependencies.add(module);
- }
-
- // Because we are specializing shader code, the mangled entry
- // point names for this component type may be different than
- // for the base component type (e.g., the mangled name for `f<int>`
- // is different than that that of the generic `f` function
- // itself).
- //
- // We will compute the mangled names of all the entry points and
- // store them here, so that we don't have to do it on the fly.
- // Because the `ComponentType` structure is hierarchical, we
- // need to use a recursive visitor to compute the names,
- // and we will define that visitor locally:
- //
- struct EntryPointMangledNameCollector : ComponentTypeVisitor
- {
- List<String>* mangledEntryPointNames;
- List<String>* entryPointNameOverrides;
-
- void visitEntryPoint(
- EntryPoint* entryPoint,
- EntryPoint::EntryPointSpecializationInfo* specializationInfo) SLANG_OVERRIDE
- {
- auto funcDeclRef = entryPoint->getFuncDeclRef();
- if (specializationInfo)
- funcDeclRef = specializationInfo->specializedFuncDeclRef;
-
- (*mangledEntryPointNames).add(getMangledName(m_astBuilder, funcDeclRef));
- (*entryPointNameOverrides).add(entryPoint->getEntryPointNameOverride(0));
- }
-
- void visitRenamedEntryPoint(
- RenamedEntryPointComponentType* entryPoint,
- EntryPoint::EntryPointSpecializationInfo* specializationInfo) SLANG_OVERRIDE
- {
- entryPoint->getBase()->acceptVisitor(this, specializationInfo);
- (*entryPointNameOverrides).getLast() = entryPoint->getEntryPointNameOverride(0);
- }
-
- void visitModule(Module*, Module::ModuleSpecializationInfo*) SLANG_OVERRIDE {}
- void visitComposite(
- CompositeComponentType* composite,
- CompositeComponentType::CompositeSpecializationInfo* specializationInfo) SLANG_OVERRIDE
- {
- visitChildren(composite, specializationInfo);
- }
- void visitSpecialized(SpecializedComponentType* specialized) SLANG_OVERRIDE
- {
- visitChildren(specialized);
- }
- void visitTypeConformance(TypeConformance* conformance) SLANG_OVERRIDE
- {
- SLANG_UNUSED(conformance);
- }
- EntryPointMangledNameCollector(ASTBuilder* astBuilder)
- : m_astBuilder(astBuilder)
- {
- }
- ASTBuilder* m_astBuilder;
- };
-
- // With the visitor defined, we apply it to ourself to compute
- // and collect the mangled entry point names.
- //
- EntryPointMangledNameCollector collector(getLinkage()->getASTBuilder());
- collector.mangledEntryPointNames = &m_entryPointMangledNames;
- collector.entryPointNameOverrides = &m_entryPointNameOverrides;
- collector.visitSpecialized(this);
-}
-
-void SpecializedComponentType::buildHash(DigestBuilder<SHA1>& builder)
-{
- auto specializationArgCount = getSpecializationArgCount();
- for (Index i = 0; i < specializationArgCount; ++i)
- {
- auto specializationArg = getSpecializationArg(i);
- auto argString = specializationArg.val->toString();
- builder.append(argString);
- }
-
- getBaseComponentType()->buildHash(builder);
-}
-
-void SpecializedComponentType::acceptVisitor(
- ComponentTypeVisitor* visitor,
- SpecializationInfo* specializationInfo)
-{
- SLANG_ASSERT(specializationInfo == nullptr);
- SLANG_UNUSED(specializationInfo);
- visitor->visitSpecialized(this);
-}
-
-Index SpecializedComponentType::getRequirementCount()
-{
- return m_requirements.getCount();
-}
-
-RefPtr<ComponentType> SpecializedComponentType::getRequirement(Index index)
-{
- return m_requirements[index];
-}
-
-String SpecializedComponentType::getEntryPointMangledName(Index index)
-{
- return m_entryPointMangledNames[index];
-}
-
-String SpecializedComponentType::getEntryPointNameOverride(Index index)
-{
- return m_entryPointNameOverrides[index];
-}
-
-// RenamedEntryPointComponentType
-
-RenamedEntryPointComponentType::RenamedEntryPointComponentType(ComponentType* base, String newName)
- : ComponentType(base->getLinkage()), m_base(base), m_entryPointNameOverride(newName)
-{
-}
-
-void RenamedEntryPointComponentType::acceptVisitor(
- ComponentTypeVisitor* visitor,
- SpecializationInfo* specializationInfo)
-{
- visitor->visitRenamedEntryPoint(
- this,
- as<EntryPoint::EntryPointSpecializationInfo>(specializationInfo));
-}
-
-void RenamedEntryPointComponentType::buildHash(DigestBuilder<SHA1>& builder)
-{
- SLANG_UNUSED(builder);
-}
-
-void ComponentTypeVisitor::visitChildren(
- CompositeComponentType* composite,
- CompositeComponentType::CompositeSpecializationInfo* specializationInfo)
-{
- auto childCount = composite->getChildComponentCount();
- for (Index ii = 0; ii < childCount; ++ii)
- {
- auto child = composite->getChildComponent(ii);
- auto childSpecializationInfo =
- specializationInfo ? specializationInfo->childInfos[ii] : nullptr;
-
- child->acceptVisitor(this, childSpecializationInfo);
- }
-}
-
-void ComponentTypeVisitor::visitChildren(SpecializedComponentType* specialized)
-{
- specialized->getBaseComponentType()->acceptVisitor(this, specialized->getSpecializationInfo());
-}
-
-TargetProgram* ComponentType::getTargetProgram(TargetRequest* target)
-{
- RefPtr<TargetProgram> targetProgram;
- if (!m_targetPrograms.tryGetValue(target, targetProgram))
- {
- targetProgram = new TargetProgram(this, target);
- m_targetPrograms[target] = targetProgram;
- }
- return targetProgram;
-}
-
-//
-// TargetProgram
-//
-
-TargetProgram::TargetProgram(ComponentType* componentType, TargetRequest* targetReq)
- : m_program(componentType), m_targetReq(targetReq)
-{
- m_entryPointResults.setCount(componentType->getEntryPointCount());
- m_optionSet.overrideWith(m_program->getOptionSet());
- m_optionSet.inheritFrom(targetReq->getOptionSet());
-}
-
-//
-
-Session* CompileRequestBase::getSession()
-{
- return getLinkage()->getSessionImpl();
-}
-
-void Linkage::setFileSystem(ISlangFileSystem* inFileSystem)
-{
- // Set the fileSystem
- m_fileSystem = inFileSystem;
-
- // Release what's there
- m_fileSystemExt.setNull();
-
- // If nullptr passed in set up default
- if (inFileSystem == nullptr)
- {
- m_fileSystemExt = new Slang::CacheFileSystem(Slang::OSFileSystem::getExtSingleton());
- }
- else
- {
- if (auto cacheFileSystem = as<CacheFileSystem>(inFileSystem))
- {
- m_fileSystemExt = cacheFileSystem;
- }
- else
- {
- if (m_requireCacheFileSystem)
- {
- m_fileSystemExt = new Slang::CacheFileSystem(inFileSystem);
- }
- else
- {
- // See if we have the full ISlangFileSystemExt interface, if we do just use it
- inFileSystem->queryInterface(SLANG_IID_PPV_ARGS(m_fileSystemExt.writeRef()));
-
- // If not wrap with CacheFileSystem that emulates ISlangFileSystemExt from the
- // ISlangFileSystem interface
- if (!m_fileSystemExt)
- {
- // Construct a wrapper to emulate the extended interface behavior
- m_fileSystemExt = new Slang::CacheFileSystem(m_fileSystem);
- }
- }
- }
- }
-
- // If requires a cache file system, check that it does have one
- SLANG_ASSERT(m_requireCacheFileSystem == false || as<CacheFileSystem>(m_fileSystemExt));
-
- // Set the file system used on the source manager
- getSourceManager()->setFileSystemExt(m_fileSystemExt);
-}
-
-SlangResult Linkage::loadSerializedModuleContents(
- Module* module,
- const PathInfo& moduleFilePathInfo,
- ISlangBlob* blobHoldingSerializedData,
- ModuleChunk const* moduleChunk,
- RIFF::ListChunk const* containerChunk,
- DiagnosticSink* sink)
-{
- // At this point we've dealt with basically all of
- // the formalities, and we just need to get down
- // to the real work of decoding the information
- // in the `moduleChunk`.
-
- //
- // TODO(tfoley): The fact that a separate `containerChunk` is getting
- // passed in here is entirely byproduct of the support for "module libraries"
- // that can (in principle) contain multiple serialized modules. When
- // things are serialized in the "container" representation used for
- // a module library, there is a single `DebugChunk` as a child of
- // the container, with all of the `ModuleChunk`s sharing that debug info.
- //
- // In contrast, the more typical kind of serialized module that the compiler
- // produces serializes a single `ModuleChunk`, and the `DebugChunk` is
- // one of its direct children. Thus there are currently two different
- // locations where debug information might be found.
- //
- // Prior to the change where we navigate the serialized RIFF hierarchy
- // in memory without copying it, this issue was addressed by having
- // the subroutine that looked for a `DebugChunk` start at the `ModuleChunk`
- // and work its way up through the hierarchy using parent pointers that
- // were created as part of RIFF loading. When navigating the RIFF in-place
- // we don't have such parent pointers.
- //
- // As a short-term solution, we should deprecate and remove the support
- // for "module libraries" so that the code doesn't have to handle two
- // different layouts.
- //
- // In the longer term, we should be making some conscious design decisions
- // around how we want to organize the top-level structure of our serialized
- // intermediate/output formats, since there's quite a mix of different
- // approaches currently in use.
- //
-
- auto sourceManager = getSourceManager();
- RefPtr<SerialSourceLocReader> sourceLocReader;
- if (auto debugChunk = DebugChunk::find(moduleChunk, containerChunk))
- {
- SLANG_RETURN_ON_FAIL(
- readSourceLocationsFromDebugChunk(debugChunk, sourceManager, sourceLocReader));
- }
-
- auto astChunk = moduleChunk->findAST();
- if (!astChunk)
- return SLANG_FAIL;
-
- auto irChunk = moduleChunk->findIR();
- if (!irChunk)
- return SLANG_FAIL;
-
- auto astBuilder = getASTBuilder();
- auto session = getSessionImpl();
-
- // For the purposes of any modules referenced
- // by the module we're about to decode, we will
- // construct a source location that represents
- // the module itself (if possible).
- //
- // TODO(tfoley): This logic seems like overkill, given
- // that many (most? all?) control-flow paths that can
- // reach this routine will have already found a `SourceFile`
- // to represent the module, as part of even getting the
- // `moduleFilePathInfo` to pass in
- //
- // The approach here is more or less exactly copied
- // from what the old `SerialContainerUtil::read` function
- // used to do, with the hopes that it will as many tests
- // passing as possible.
- //
- // Down the line somebody should scrutinize all of this
- // kind of logic in the compiler codebase, because there
- // is something that feels unclean about how paths are being handled.
- //
- SourceLoc serializedModuleLoc;
- {
- auto sourceFile =
- sourceManager->findSourceFileByPathRecursively(moduleFilePathInfo.foundPath);
- if (!sourceFile)
- {
- sourceFile = sourceManager->createSourceFileWithString(moduleFilePathInfo, String());
- sourceManager->addSourceFile(moduleFilePathInfo.getMostUniqueIdentity(), sourceFile);
- }
- auto sourceView =
- sourceManager->createSourceView(sourceFile, &moduleFilePathInfo, SourceLoc());
- serializedModuleLoc = sourceView->getRange().begin;
- }
-
- auto moduleDecl = readSerializedModuleAST(
- this,
- astBuilder,
- sink,
- blobHoldingSerializedData,
- astChunk,
- sourceLocReader,
- serializedModuleLoc);
- if (!moduleDecl)
- return SLANG_FAIL;
- module->setModuleDecl(moduleDecl);
-
- RefPtr<IRModule> irModule;
- SLANG_RETURN_ON_FAIL(readSerializedModuleIR(irChunk, session, sourceLocReader, irModule));
- module->setIRModule(irModule);
-
- // The handling of file dependencies is complicated, because of
- // the way that the encoding logic tried to make all of the
- // paths be relative to the primary source file for the module.
- //
- // We end up needing to undo some amount of that work here.
- //
-
- module->clearFileDependency();
- String moduleSourcePath = moduleFilePathInfo.foundPath;
- bool isFirst = true;
- for (auto depenencyFileChunk : moduleChunk->getFileDependencies())
- {
- auto encodedDependencyFilePath = depenencyFileChunk->getValue();
-
- auto sourceFile = loadSourceFile(moduleFilePathInfo.foundPath, encodedDependencyFilePath);
- if (isFirst)
- {
- // The first file is the source for the main module file.
- // We store the module path as the basis for finding the remaining
- // dependent files.
- if (sourceFile)
- moduleSourcePath = sourceFile->getPathInfo().foundPath;
- isFirst = false;
- }
- // If we cannot find the dependent file directly, try to find
- // it relative to the module source path.
- if (!sourceFile)
- {
- sourceFile = loadSourceFile(moduleSourcePath, encodedDependencyFilePath);
- }
- if (sourceFile)
- {
- module->addFileDependency(sourceFile);
- }
- }
- module->setPathInfo(moduleFilePathInfo);
- module->setDigest(moduleChunk->getDigest());
- module->_collectShaderParams();
- module->_discoverEntryPoints(sink, targets);
-
- // Hook up fileDecl's scope to module's scope.
- for (auto fileDecl : moduleDecl->getDirectMemberDeclsOfType<FileDecl>())
- {
- addSiblingScopeForContainerDecl(m_astBuilder, moduleDecl->ownedScope, fileDecl);
- }
-
- return SLANG_OK;
-}
-
-void Linkage::setRequireCacheFileSystem(bool requireCacheFileSystem)
-{
- if (requireCacheFileSystem == m_requireCacheFileSystem)
- {
- return;
- }
-
- ComPtr<ISlangFileSystem> scopeFileSystem(m_fileSystem);
- m_requireCacheFileSystem = requireCacheFileSystem;
-
- setFileSystem(scopeFileSystem);
-}
-
-RefPtr<Module> findOrImportModule(
- Linkage* linkage,
- Name* name,
- SourceLoc const& loc,
- DiagnosticSink* sink,
- const LoadedModuleDictionary* loadedModules)
-{
- return linkage->findOrImportModule(name, loc, sink, loadedModules);
-}
-
-void Session::addBuiltinSource(
- Scope* scope,
- String const& path,
- ISlangBlob* sourceBlob,
- Module*& outModule)
-{
- SourceManager* sourceManager = getBuiltinSourceManager();
-
- DiagnosticSink sink(sourceManager, Lexer::sourceLocationLexer);
-
- RefPtr<FrontEndCompileRequest> compileRequest =
- new FrontEndCompileRequest(m_builtinLinkage, nullptr, &sink);
- compileRequest->m_isCoreModuleCode = true;
-
- // Set the source manager on the sink
- sink.setSourceManager(sourceManager);
- // Make the linkage use the builtin source manager
- Linkage* linkage = compileRequest->getLinkage();
- linkage->setSourceManager(sourceManager);
-
- Name* moduleName = getNamePool()->getName(path);
- auto translationUnitIndex =
- compileRequest->addTranslationUnit(SourceLanguage::Slang, moduleName);
-
- compileRequest->addTranslationUnitSourceBlob(translationUnitIndex, path, sourceBlob);
-
- SlangResult res = compileRequest->executeActionsInner();
- if (SLANG_FAILED(res))
- {
- char const* diagnostics = sink.outputBuffer.getBuffer();
- fprintf(stderr, "%s", diagnostics);
-
- PlatformUtil::outputDebugMessage(diagnostics);
-
- SLANG_UNEXPECTED("error in Slang core module");
- }
-
- // Compiling the core module should not yield any warnings.
- SLANG_ASSERT(sink.outputBuffer.getLength() == 0);
-
- // Extract the AST for the code we just parsed
- auto module = compileRequest->translationUnits[translationUnitIndex]->getModule();
- auto moduleDecl = module->getModuleDecl();
-
- // Extact documentation markup.
- ASTMarkup markup;
- ASTMarkupUtil::extract(moduleDecl, sourceManager, &sink, &markup);
- markup.attachToAST();
-
- // Put in the loaded module map
- linkage->mapNameToLoadedModules.add(moduleName, module);
-
- // Add the resulting code to the appropriate scope
- if (!scope->containerDecl)
- {
- // We are the first chunk of code to be loaded for this scope
- scope->containerDecl = moduleDecl;
- }
- else
- {
- // We need to create a new scope to link into the whole thing
- auto subScope = module->getASTBuilder()->create<Scope>();
- subScope->containerDecl = moduleDecl;
- subScope->nextSibling = scope->nextSibling;
- scope->nextSibling = subScope;
- }
-
- outModule = module;
-}
-
-Session::~Session()
-{
- // This is necessary because this ASTBuilder uses the SharedASTBuilder also owned by the
- // session. If the SharedASTBuilder gets dtored before the globalASTBuilder it has a dangling
- // pointer, which is referenced in the ASTBuilder dtor (likely) causing a crash.
- //
- // By destroying first we know it is destroyed, before the SharedASTBuilder.
- globalAstBuilder.setNull();
-
- // destroy modules next
- coreModules = decltype(coreModules)();
-}
-
-} // namespace Slang
-
-
-/* !!!!!!!!!!!!!!!!!! EndToEndCompileRequestImpl !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! */
-
-namespace Slang
-{
-
-void EndToEndCompileRequest::setFileSystem(ISlangFileSystem* fileSystem)
-{
- getLinkage()->setFileSystem(fileSystem);
-}
-
-void EndToEndCompileRequest::setCompileFlags(SlangCompileFlags flags)
-{
- if (flags & SLANG_COMPILE_FLAG_NO_MANGLING)
- getOptionSet().set(CompilerOptionName::NoMangle, true);
- if (flags & SLANG_COMPILE_FLAG_NO_CODEGEN)
- getOptionSet().set(CompilerOptionName::SkipCodeGen, true);
- if (flags & SLANG_COMPILE_FLAG_OBFUSCATE)
- getOptionSet().set(CompilerOptionName::Obfuscate, true);
-}
-
-SlangCompileFlags EndToEndCompileRequest::getCompileFlags()
-{
- SlangCompileFlags result = 0;
- if (getOptionSet().getBoolOption(CompilerOptionName::NoMangle))
- result |= SLANG_COMPILE_FLAG_NO_MANGLING;
- if (getOptionSet().getBoolOption(CompilerOptionName::SkipCodeGen))
- result |= SLANG_COMPILE_FLAG_NO_CODEGEN;
- if (getOptionSet().getBoolOption(CompilerOptionName::Obfuscate))
- result |= SLANG_COMPILE_FLAG_OBFUSCATE;
- return result;
-}
-
-void EndToEndCompileRequest::setDumpIntermediates(int enable)
-{
- getOptionSet().set(CompilerOptionName::DumpIntermediates, enable);
-}
-
-void EndToEndCompileRequest::setTrackLiveness(bool v)
-{
- getOptionSet().set(CompilerOptionName::TrackLiveness, v);
-}
-
-void EndToEndCompileRequest::setDumpIntermediatePrefix(const char* prefix)
-{
- getOptionSet().set(CompilerOptionName::DumpIntermediatePrefix, String(prefix));
-}
-
-void EndToEndCompileRequest::setLineDirectiveMode(SlangLineDirectiveMode mode)
-{
- getOptionSet().set(CompilerOptionName::LineDirectiveMode, mode);
-}
-
-void EndToEndCompileRequest::setCommandLineCompilerMode()
-{
- m_isCommandLineCompile = true;
-
- // legacy slangc tool defaults to column major layout.
- if (!getOptionSet().hasOption(CompilerOptionName::MatrixLayoutRow))
- getOptionSet().setMatrixLayoutMode(kMatrixLayoutMode_ColumnMajor);
-}
-
-void EndToEndCompileRequest::_completeTargetRequest(UInt targetIndex)
-{
- auto linkage = getLinkage();
-
- TargetRequest* targetRequest = linkage->targets[Index(targetIndex)];
-
- targetRequest->getOptionSet().inheritFrom(getLinkage()->m_optionSet);
- targetRequest->getOptionSet().inheritFrom(m_optionSetForDefaultTarget);
-}
-
-void EndToEndCompileRequest::setCodeGenTarget(SlangCompileTarget target)
-{
- auto linkage = getLinkage();
- linkage->targets.clear();
- const auto targetIndex = linkage->addTarget(CodeGenTarget(target));
- SLANG_ASSERT(targetIndex == 0);
- _completeTargetRequest(0);
-}
-
-int EndToEndCompileRequest::addCodeGenTarget(SlangCompileTarget target)
-{
- const auto targetIndex = getLinkage()->addTarget(CodeGenTarget(target));
- _completeTargetRequest(targetIndex);
- return int(targetIndex);
-}
-
-void EndToEndCompileRequest::setTargetProfile(int targetIndex, SlangProfileID profile)
-{
- getTargetOptionSet(targetIndex).setProfile(Profile(profile));
-}
-
-void EndToEndCompileRequest::setTargetFlags(int targetIndex, SlangTargetFlags flags)
-{
- getTargetOptionSet(targetIndex).setTargetFlags(flags);
-}
-
-void EndToEndCompileRequest::setTargetForceGLSLScalarBufferLayout(int targetIndex, bool value)
-{
- getTargetOptionSet(targetIndex).set(CompilerOptionName::GLSLForceScalarLayout, value);
-}
-
-void EndToEndCompileRequest::setTargetForceDXLayout(int targetIndex, bool value)
-{
- getTargetOptionSet(targetIndex).set(CompilerOptionName::ForceDXLayout, value);
-}
-
-void EndToEndCompileRequest::setTargetFloatingPointMode(
- int targetIndex,
- SlangFloatingPointMode mode)
-{
- getTargetOptionSet(targetIndex)
- .set(CompilerOptionName::FloatingPointMode, FloatingPointMode(mode));
-}
-
-void EndToEndCompileRequest::setMatrixLayoutMode(SlangMatrixLayoutMode mode)
-{
- getOptionSet().setMatrixLayoutMode((MatrixLayoutMode)mode);
-}
-
-void EndToEndCompileRequest::setTargetMatrixLayoutMode(int targetIndex, SlangMatrixLayoutMode mode)
-{
- getTargetOptionSet(targetIndex).setMatrixLayoutMode(MatrixLayoutMode(mode));
-}
-
-void EndToEndCompileRequest::setTargetGenerateWholeProgram(int targetIndex, bool value)
-{
- getTargetOptionSet(targetIndex).set(CompilerOptionName::GenerateWholeProgram, value);
-}
-
-void EndToEndCompileRequest::setTargetEmbedDownstreamIR(int targetIndex, bool value)
-{
- getTargetOptionSet(targetIndex).set(CompilerOptionName::EmbedDownstreamIR, value);
-}
-
-void EndToEndCompileRequest::setTargetLineDirectiveMode(
- SlangInt targetIndex,
- SlangLineDirectiveMode mode)
-{
- getTargetOptionSet(targetIndex)
- .set(CompilerOptionName::LineDirectiveMode, LineDirectiveMode(mode));
-}
-
-void EndToEndCompileRequest::overrideDiagnosticSeverity(
- SlangInt messageID,
- SlangSeverity overrideSeverity)
-{
- getSink()->overrideDiagnosticSeverity(int(messageID), Severity(overrideSeverity));
-}
-
-SlangDiagnosticFlags EndToEndCompileRequest::getDiagnosticFlags()
-{
- DiagnosticSink::Flags sinkFlags = getSink()->getFlags();
-
- SlangDiagnosticFlags flags = 0;
-
- if (sinkFlags & DiagnosticSink::Flag::VerbosePath)
- flags |= SLANG_DIAGNOSTIC_FLAG_VERBOSE_PATHS;
-
- if (sinkFlags & DiagnosticSink::Flag::TreatWarningsAsErrors)
- flags |= SLANG_DIAGNOSTIC_FLAG_TREAT_WARNINGS_AS_ERRORS;
-
- return flags;
-}
-
-void EndToEndCompileRequest::setDiagnosticFlags(SlangDiagnosticFlags flags)
-{
- DiagnosticSink::Flags sinkFlags = getSink()->getFlags();
-
- if (flags & SLANG_DIAGNOSTIC_FLAG_VERBOSE_PATHS)
- sinkFlags |= DiagnosticSink::Flag::VerbosePath;
- else
- sinkFlags &= ~DiagnosticSink::Flag::VerbosePath;
-
- if (flags & SLANG_DIAGNOSTIC_FLAG_TREAT_WARNINGS_AS_ERRORS)
- sinkFlags |= DiagnosticSink::Flag::TreatWarningsAsErrors;
- else
- sinkFlags &= ~DiagnosticSink::Flag::TreatWarningsAsErrors;
-
- getSink()->setFlags(sinkFlags);
-}
-
-SlangResult EndToEndCompileRequest::addTargetCapability(
- SlangInt targetIndex,
- SlangCapabilityID capability)
-{
- auto& targets = getLinkage()->targets;
- if (targetIndex < 0 || targetIndex >= targets.getCount())
- return SLANG_E_INVALID_ARG;
- getTargetOptionSet(targetIndex).addCapabilityAtom(CapabilityName(capability));
- return SLANG_OK;
-}
-
-void EndToEndCompileRequest::setDebugInfoLevel(SlangDebugInfoLevel level)
-{
- getOptionSet().set(CompilerOptionName::DebugInformation, DebugInfoLevel(level));
-}
-
-void EndToEndCompileRequest::setDebugInfoFormat(SlangDebugInfoFormat format)
-{
- getOptionSet().set(CompilerOptionName::DebugInformationFormat, DebugInfoFormat(format));
-}
-
-void EndToEndCompileRequest::setOptimizationLevel(SlangOptimizationLevel level)
-{
- getOptionSet().set(CompilerOptionName::Optimization, OptimizationLevel(level));
-}
-
-void EndToEndCompileRequest::setOutputContainerFormat(SlangContainerFormat format)
-{
- m_containerFormat = ContainerFormat(format);
-}
-
-void EndToEndCompileRequest::setPassThrough(SlangPassThrough inPassThrough)
-{
- m_passThrough = PassThroughMode(inPassThrough);
-}
-
-void EndToEndCompileRequest::setReportDownstreamTime(bool value)
-{
- getOptionSet().set(CompilerOptionName::ReportDownstreamTime, value);
-}
-
-void EndToEndCompileRequest::setReportPerfBenchmark(bool value)
-{
- getOptionSet().set(CompilerOptionName::ReportPerfBenchmark, value);
-}
-
-void EndToEndCompileRequest::setSkipSPIRVValidation(bool value)
-{
- getOptionSet().set(CompilerOptionName::SkipSPIRVValidation, value);
-}
-
-void EndToEndCompileRequest::setTargetUseMinimumSlangOptimization(int targetIndex, bool value)
-{
- getTargetOptionSet(targetIndex).set(CompilerOptionName::MinimumSlangOptimization, value);
-}
-
-void EndToEndCompileRequest::setIgnoreCapabilityCheck(bool value)
-{
- getOptionSet().set(CompilerOptionName::IgnoreCapabilities, value);
-}
-
-void EndToEndCompileRequest::setDiagnosticCallback(
- SlangDiagnosticCallback callback,
- void const* userData)
-{
- ComPtr<ISlangWriter> writer(new CallbackWriter(callback, userData, WriterFlag::IsConsole));
- setWriter(WriterChannel::Diagnostic, writer);
-}
-
-void EndToEndCompileRequest::setWriter(SlangWriterChannel chan, ISlangWriter* writer)
-{
- setWriter(WriterChannel(chan), writer);
-}
-
-ISlangWriter* EndToEndCompileRequest::getWriter(SlangWriterChannel chan)
-{
- return getWriter(WriterChannel(chan));
-}
-
-void EndToEndCompileRequest::addSearchPath(const char* path)
-{
- getOptionSet().addSearchPath(path);
-}
-
-void EndToEndCompileRequest::addPreprocessorDefine(const char* key, const char* value)
-{
- getOptionSet().addPreprocessorDefine(key, value);
-}
-
-void EndToEndCompileRequest::setEnableEffectAnnotations(bool value)
-{
- getOptionSet().set(CompilerOptionName::EnableEffectAnnotations, value);
-}
-
-char const* EndToEndCompileRequest::getDiagnosticOutput()
-{
- return m_diagnosticOutput.begin();
-}
-
-SlangResult EndToEndCompileRequest::getDiagnosticOutputBlob(ISlangBlob** outBlob)
-{
- if (!outBlob)
- return SLANG_E_INVALID_ARG;
-
- if (!m_diagnosticOutputBlob)
- {
- m_diagnosticOutputBlob = StringUtil::createStringBlob(m_diagnosticOutput);
- }
-
- ComPtr<ISlangBlob> resultBlob = m_diagnosticOutputBlob;
- *outBlob = resultBlob.detach();
- return SLANG_OK;
-}
-
-int EndToEndCompileRequest::addTranslationUnit(SlangSourceLanguage language, char const* inName)
-{
- auto frontEndReq = getFrontEndReq();
- NamePool* namePool = frontEndReq->getNamePool();
-
- // Work out a module name. Can be nullptr if so will generate a name
- Name* moduleName = inName ? namePool->getName(inName) : frontEndReq->m_defaultModuleName;
-
- // If moduleName is nullptr a name will be generated
- return frontEndReq->addTranslationUnit(Slang::SourceLanguage(language), moduleName);
-}
-
-void EndToEndCompileRequest::setDefaultModuleName(const char* defaultModuleName)
-{
- auto frontEndReq = getFrontEndReq();
- NamePool* namePool = frontEndReq->getNamePool();
- frontEndReq->m_defaultModuleName = namePool->getName(defaultModuleName);
-}
-
-SlangResult _addLibraryReference(
- EndToEndCompileRequest* req,
- ModuleLibrary* moduleLibrary,
- bool includeEntryPoint)
-{
- FrontEndCompileRequest* frontEndRequest = req->getFrontEndReq();
-
- if (includeEntryPoint)
- {
- frontEndRequest->m_extraEntryPoints.addRange(
- moduleLibrary->m_entryPoints.getBuffer(),
- moduleLibrary->m_entryPoints.getCount());
- }
-
- for (auto m : moduleLibrary->m_modules)
- {
- RefPtr<TranslationUnitRequest> tu = new TranslationUnitRequest(frontEndRequest, m);
- frontEndRequest->translationUnits.add(tu);
- // For modules loaded for EndToEndCompileRequest,
- // we don't need the automatically discovered entrypoints.
- if (!includeEntryPoint)
- m->getEntryPoints().clear();
- }
- return SLANG_OK;
-}
-
-SlangResult _addLibraryReference(
- EndToEndCompileRequest* req,
- String path,
- IArtifact* artifact,
- bool includeEntryPoint)
-{
- auto desc = artifact->getDesc();
-
- // TODO(JS):
- // This isn't perhaps the best way to handle this scenario, as IArtifact can
- // support lazy evaluation, with suitable hander.
- // For now we just read in and strip out the bits we want.
- if (isDerivedFrom(desc.kind, ArtifactKind::Container) &&
- isDerivedFrom(desc.payload, ArtifactPayload::CompileResults))
- {
- // We want to read as a file system
- ComPtr<IArtifact> container;
-
- SLANG_RETURN_ON_FAIL(ArtifactContainerUtil::readContainer(artifact, container));
-
- // Find the payload... It should be linkable
- if (!ArtifactDescUtil::isLinkable(container->getDesc()))
- {
- return SLANG_FAIL;
- }
-
- ComPtr<IModuleLibrary> libraryIntf;
- SLANG_RETURN_ON_FAIL(
- loadModuleLibrary(ArtifactKeep::Yes, container, path, req, libraryIntf));
-
- auto library = as<ModuleLibrary>(libraryIntf);
-
- // Look for source maps
- for (auto associated : container->getAssociated())
- {
- auto assocDesc = associated->getDesc();
-
- // If we find an obfuscated source map load it and associate
- if (isDerivedFrom(assocDesc.kind, ArtifactKind::Json) &&
- isDerivedFrom(assocDesc.payload, ArtifactPayload::SourceMap) &&
- isDerivedFrom(assocDesc.style, ArtifactStyle::Obfuscated))
- {
- ComPtr<ICastable> castable;
- SLANG_RETURN_ON_FAIL(associated->getOrCreateRepresentation(
- SourceMap::getTypeGuid(),
- ArtifactKeep::Yes,
- castable.writeRef()));
- auto sourceMapBox = asBoxValue<SourceMap>(castable);
- SLANG_ASSERT(sourceMapBox);
-
- // TODO(JS):
- // There is perhaps (?) a risk here that we might copy the obfuscated map
- // into some output container. Currently that only happens for source maps
- // that are from translation units.
- //
- // On the other hand using "import" is a way that such source maps *would* be
- // copied into the output, and that is something that could be a vector
- // for leaking.
- //
- // That isn't a risk from -r though because, it doesn't create a translation
- // unit(s).
- for (auto module : library->m_modules)
- {
- module->getIRModule()->setObfuscatedSourceMap(sourceMapBox);
- }
-
- // Look up the source file
- auto sourceManager = req->getSink()->getSourceManager();
-
- auto name = Path::getFileNameWithoutExt(associated->getName());
-
- if (name.getLength())
- {
- // Note(tfoley): There is a subtle requirement here, that any
- // source file `name` that might be searched for here *must*
- // have been added to the `sourceManager` already, as a
- // byproduct of debug source location information getting
- // deserialized as part of the call to `loadModuleLibrary()` above.
- //
- // The implicit dependency is frustrating, and could potentially
- // break if somehow the debug info chunk was stripped from a binary,
- // while the source map was left in (which should be valid, even if
- // it is unlikely to be what a user wants).
- //
- // Ideally the source map would either be made an integral part of
- // the debug source location chunk, so they are loaded together,
- // or the `SourceManager` would be adapted so that it can store
- // registered source maps independent of whether or not the
- // corresponding source file(s) have been loaded.
-
- auto sourceFile = sourceManager->findSourceFileByPathRecursively(name);
- SLANG_ASSERT(sourceFile);
- sourceFile->setSourceMap(sourceMapBox, SourceMapKind::Obfuscated);
- }
- }
- }
-
- SLANG_RETURN_ON_FAIL(_addLibraryReference(req, library, includeEntryPoint));
- return SLANG_OK;
- }
-
- if (desc.kind == ArtifactKind::Library && desc.payload == ArtifactPayload::SlangIR)
- {
- ComPtr<IModuleLibrary> libraryIntf;
-
- SLANG_RETURN_ON_FAIL(
- loadModuleLibrary(ArtifactKeep::Yes, artifact, path, req, libraryIntf));
-
- auto library = as<ModuleLibrary>(libraryIntf);
- if (!library)
- {
- return SLANG_FAIL;
- }
-
- SLANG_RETURN_ON_FAIL(_addLibraryReference(req, library, includeEntryPoint));
- return SLANG_OK;
- }
-
- // TODO(JS):
- // Do we want to check the path exists?
-
- // Add to the m_libModules
- auto linkage = req->getLinkage();
- linkage->m_libModules.add(ComPtr<IArtifact>(artifact));
-
- return SLANG_OK;
-}
-
-SlangResult EndToEndCompileRequest::addLibraryReference(
- const char* basePath,
- const void* libData,
- size_t libDataSize)
-{
- // We need to deserialize and add the modules
- ComPtr<IModuleLibrary> library;
-
- auto libBlob = RawBlob::create((const Byte*)libData, libDataSize);
-
- SLANG_RETURN_ON_FAIL(
- loadModuleLibrary(libBlob, (const Byte*)libData, libDataSize, basePath, this, library));
-
- // Create an artifact without any name (as one is not provided)
- auto artifact =
- Artifact::create(ArtifactDesc::make(ArtifactKind::Library, ArtifactPayload::SlangIR));
- artifact->addRepresentation(library);
-
- return _addLibraryReference(this, basePath, artifact, true);
-}
-
-void EndToEndCompileRequest::addTranslationUnitPreprocessorDefine(
- int translationUnitIndex,
- const char* key,
- const char* value)
-{
- getFrontEndReq()->translationUnits[translationUnitIndex]->preprocessorDefinitions[key] = value;
-}
-
-void EndToEndCompileRequest::addTranslationUnitSourceFile(
- int translationUnitIndex,
- char const* path)
-{
- auto frontEndReq = getFrontEndReq();
- if (!path)
- return;
- if (translationUnitIndex < 0)
- return;
- if (Index(translationUnitIndex) >= frontEndReq->translationUnits.getCount())
- return;
-
- frontEndReq->addTranslationUnitSourceFile(translationUnitIndex, path);
-}
-
-void EndToEndCompileRequest::addTranslationUnitSourceString(
- int translationUnitIndex,
- char const* path,
- char const* source)
-{
- if (!source)
- return;
- addTranslationUnitSourceStringSpan(translationUnitIndex, path, source, source + strlen(source));
-}
-
-void EndToEndCompileRequest::addTranslationUnitSourceStringSpan(
- int translationUnitIndex,
- char const* path,
- char const* sourceBegin,
- char const* sourceEnd)
-{
- auto frontEndReq = getFrontEndReq();
- if (!sourceBegin)
- return;
- if (translationUnitIndex < 0)
- return;
- if (Index(translationUnitIndex) >= frontEndReq->translationUnits.getCount())
- return;
-
- if (!path)
- path = "";
-
- const auto slice = UnownedStringSlice(sourceBegin, sourceEnd);
-
- auto blob = RawBlob::create(slice.begin(), slice.getLength());
-
- frontEndReq->addTranslationUnitSourceBlob(translationUnitIndex, path, blob);
-}
-
-void EndToEndCompileRequest::addTranslationUnitSourceBlob(
- int translationUnitIndex,
- char const* path,
- ISlangBlob* sourceBlob)
-{
- auto frontEndReq = getFrontEndReq();
- if (!sourceBlob)
- return;
- if (translationUnitIndex < 0)
- return;
- if (Slang::Index(translationUnitIndex) >= frontEndReq->translationUnits.getCount())
- return;
-
- if (!path)
- path = "";
-
- frontEndReq->addTranslationUnitSourceBlob(translationUnitIndex, path, sourceBlob);
-}
-
-
-int EndToEndCompileRequest::addEntryPoint(
- int translationUnitIndex,
- char const* name,
- SlangStage stage)
-{
- return addEntryPointEx(translationUnitIndex, name, stage, 0, nullptr);
-}
-
-int EndToEndCompileRequest::addEntryPointEx(
- int translationUnitIndex,
- char const* name,
- SlangStage stage,
- int genericParamTypeNameCount,
- char const** genericParamTypeNames)
-{
- auto frontEndReq = getFrontEndReq();
- if (!name)
- return -1;
- if (translationUnitIndex < 0)
- return -1;
- if (Index(translationUnitIndex) >= frontEndReq->translationUnits.getCount())
- return -1;
-
- List<String> typeNames;
- for (int i = 0; i < genericParamTypeNameCount; i++)
- typeNames.add(genericParamTypeNames[i]);
-
- return addEntryPoint(translationUnitIndex, name, Profile(Stage(stage)), typeNames);
-}
-
-SlangResult EndToEndCompileRequest::setGlobalGenericArgs(
- int genericArgCount,
- char const** genericArgs)
-{
- auto& argStrings = m_globalSpecializationArgStrings;
- argStrings.clear();
- for (int i = 0; i < genericArgCount; i++)
- argStrings.add(genericArgs[i]);
-
- return SLANG_OK;
-}
-
-SlangResult EndToEndCompileRequest::setTypeNameForGlobalExistentialTypeParam(
- int slotIndex,
- char const* typeName)
-{
- if (slotIndex < 0)
- return SLANG_FAIL;
- if (!typeName)
- return SLANG_FAIL;
-
- auto& typeArgStrings = m_globalSpecializationArgStrings;
- if (Index(slotIndex) >= typeArgStrings.getCount())
- typeArgStrings.setCount(slotIndex + 1);
- typeArgStrings[slotIndex] = String(typeName);
- return SLANG_OK;
-}
-
-SlangResult EndToEndCompileRequest::setTypeNameForEntryPointExistentialTypeParam(
- int entryPointIndex,
- int slotIndex,
- char const* typeName)
-{
- if (entryPointIndex < 0)
- return SLANG_FAIL;
- if (slotIndex < 0)
- return SLANG_FAIL;
- if (!typeName)
- return SLANG_FAIL;
-
- if (Index(entryPointIndex) >= m_entryPoints.getCount())
- return SLANG_FAIL;
-
- auto& entryPointInfo = m_entryPoints[entryPointIndex];
- auto& typeArgStrings = entryPointInfo.specializationArgStrings;
- if (Index(slotIndex) >= typeArgStrings.getCount())
- typeArgStrings.setCount(slotIndex + 1);
- typeArgStrings[slotIndex] = String(typeName);
- return SLANG_OK;
-}
-
-void EndToEndCompileRequest::setAllowGLSLInput(bool value)
-{
- getOptionSet().set(CompilerOptionName::AllowGLSL, value);
-}
-
-SlangResult EndToEndCompileRequest::compile()
-{
- SlangResult res = SLANG_FAIL;
- double downstreamStartTime = 0.0;
- double totalStartTime = 0.0;
-
- if (getOptionSet().getBoolOption(CompilerOptionName::ReportDownstreamTime))
- {
- getSession()->getCompilerElapsedTime(&totalStartTime, &downstreamStartTime);
- PerformanceProfiler::getProfiler()->clear();
- }
-#if !defined(SLANG_DEBUG_INTERNAL_ERROR)
- // By default we'd like to catch as many internal errors as possible,
- // and report them to the user nicely (rather than just crash their
- // application). Internally Slang currently uses exceptions for this.
- //
- // TODO: Consider using `setjmp()`-style escape so that we can work
- // with applications that disable exceptions.
- //
- // TODO: Consider supporting Windows "Structured Exception Handling"
- // so that we can also recover from a wider class of crashes.
-
- try
- {
- SLANG_PROFILE_SECTION(compileInner);
- res = executeActions();
- }
- catch (const AbortCompilationException& e)
- {
- // This situation indicates a fatal (but not necessarily internal) error
- // that forced compilation to terminate. There should already have been
- // a diagnostic produced, so we don't need to add one here.
- if (getSink()->getErrorCount() == 0)
- {
- // If for some reason we didn't output any diagnostic, something is
- // going wrong, but we want to make sure we at least output something.
- getSink()->diagnose(
- SourceLoc(),
- Diagnostics::compilationAbortedDueToException,
- typeid(e).name(),
- e.Message);
- }
- }
- catch (const Exception& e)
- {
- // The compiler failed due to an internal error that was detected.
- // We will print out information on the exception to help out the user
- // in either filing a bug, or locating what in their code created
- // a problem.
- getSink()->diagnose(
- SourceLoc(),
- Diagnostics::compilationAbortedDueToException,
- typeid(e).name(),
- e.Message);
- }
- catch (...)
- {
- // The compiler failed due to some exception that wasn't a sublass of
- // `Exception`, so something really fishy is going on. We want to
- // let the user know that we messed up, so they know to blame Slang
- // and not some other component in their system.
- getSink()->diagnose(SourceLoc(), Diagnostics::compilationAborted);
- }
- m_diagnosticOutput = getSink()->outputBuffer.produceString();
-
-#else
- // When debugging, we probably don't want to filter out any errors, since
- // we are probably trying to root-cause and *fix* those errors.
- {
- res = req->executeActions();
- }
-#endif
-
- if (getOptionSet().getBoolOption(CompilerOptionName::ReportDownstreamTime))
- {
- double downstreamEndTime = 0;
- double totalEndTime = 0;
- getSession()->getCompilerElapsedTime(&totalEndTime, &downstreamEndTime);
- double downstreamTime = downstreamEndTime - downstreamStartTime;
- String downstreamTimeStr = String(downstreamTime, "%.2f");
- getSink()->diagnose(SourceLoc(), Diagnostics::downstreamCompileTime, downstreamTimeStr);
- }
- if (getOptionSet().getBoolOption(CompilerOptionName::ReportPerfBenchmark))
- {
- StringBuilder perfResult;
- PerformanceProfiler::getProfiler()->getResult(perfResult);
- perfResult << "\nType Dictionary Size: " << getSession()->m_typeDictionarySize << "\n";
- getSink()->diagnose(
- SourceLoc(),
- Diagnostics::performanceBenchmarkResult,
- perfResult.produceString());
- }
-
- // Repro dump handling
- {
- auto dumpRepro = getOptionSet().getStringOption(CompilerOptionName::DumpRepro);
- auto dumpReproOnError = getOptionSet().getBoolOption(CompilerOptionName::DumpReproOnError);
-
- if (dumpRepro.getLength())
- {
- SlangResult saveRes = ReproUtil::saveState(this, dumpRepro);
- if (SLANG_FAILED(saveRes))
- {
- getSink()->diagnose(SourceLoc(), Diagnostics::unableToWriteReproFile, dumpRepro);
- return saveRes;
- }
- }
- else if (dumpReproOnError && SLANG_FAILED(res))
- {
- String reproFileName;
- SlangResult saveRes = SLANG_FAIL;
-
- RefPtr<Stream> stream;
- if (SLANG_SUCCEEDED(ReproUtil::findUniqueReproDumpStream(this, reproFileName, stream)))
- {
- saveRes = ReproUtil::saveState(this, stream);
- }
-
- if (SLANG_FAILED(saveRes))
- {
- getSink()->diagnose(
- SourceLoc(),
- Diagnostics::unableToWriteReproFile,
- reproFileName);
- }
- }
- }
-
- auto reflectionPath = getOptionSet().getStringOption(CompilerOptionName::EmitReflectionJSON);
- if (reflectionPath.getLength() != 0)
- {
- auto bufferWriter = PrettyWriter();
- emitReflectionJSON(this, this->getReflection(), bufferWriter);
- if (reflectionPath == "-")
- {
- auto builder = bufferWriter.getBuilder();
- StdWriters::getOut().write(builder.getBuffer(), builder.getLength());
- }
- else if (SLANG_FAILED(File::writeAllText(reflectionPath, bufferWriter.getBuilder())))
- {
- getSink()->diagnose(SourceLoc(), Diagnostics::unableToWriteFile, reflectionPath);
- }
- }
-
- return res;
-}
-
-int EndToEndCompileRequest::getDependencyFileCount()
-{
- auto frontEndReq = getFrontEndReq();
- auto program = frontEndReq->getGlobalAndEntryPointsComponentType();
- return (int)program->getFileDependencies().getCount();
-}
-
-char const* EndToEndCompileRequest::getDependencyFilePath(int index)
-{
- auto frontEndReq = getFrontEndReq();
- auto program = frontEndReq->getGlobalAndEntryPointsComponentType();
- SourceFile* sourceFile = program->getFileDependencies()[index];
- return sourceFile->getPathInfo().hasFoundPath()
- ? sourceFile->getPathInfo().getMostUniqueIdentity().getBuffer()
- : "unknown";
-}
-
-int EndToEndCompileRequest::getTranslationUnitCount()
-{
- return (int)getFrontEndReq()->translationUnits.getCount();
-}
-
-void const* EndToEndCompileRequest::getEntryPointCode(int entryPointIndex, size_t* outSize)
-{
- // Zero the size initially, in case need to return nullptr for error.
- if (outSize)
- {
- *outSize = 0;
- }
-
- auto linkage = getLinkage();
- auto program = getSpecializedGlobalAndEntryPointsComponentType();
-
- // TODO: We should really accept a target index in this API
- Index targetIndex = 0;
- auto targetCount = linkage->targets.getCount();
- if (targetIndex >= targetCount)
- return nullptr;
- auto targetReq = linkage->targets[targetIndex];
-
-
- if (entryPointIndex < 0)
- return nullptr;
- if (Index(entryPointIndex) >= program->getEntryPointCount())
- return nullptr;
- auto entryPoint = program->getEntryPoint(entryPointIndex);
-
- auto targetProgram = program->getTargetProgram(targetReq);
- if (!targetProgram)
- return nullptr;
- IArtifact* artifact = targetProgram->getExistingEntryPointResult(entryPointIndex);
- if (!artifact)
- {
- return nullptr;
- }
-
- ComPtr<ISlangBlob> blob;
- SLANG_RETURN_NULL_ON_FAIL(artifact->loadBlob(ArtifactKeep::Yes, blob.writeRef()));
-
- if (outSize)
- {
- *outSize = blob->getBufferSize();
- }
-
- return (void*)blob->getBufferPointer();
-}
-
-SlangResult EndToEndCompileRequest::getCompileTimeProfile(
- ISlangProfiler** compileTimeProfile,
- bool shouldClear)
-{
- if (compileTimeProfile == nullptr)
- {
- return SLANG_E_INVALID_ARG;
- }
-
- SlangProfiler* profiler = new SlangProfiler(PerformanceProfiler::getProfiler());
-
- if (shouldClear)
- {
- PerformanceProfiler::getProfiler()->clear();
- }
-
- ComPtr<ISlangProfiler> result(profiler);
- *compileTimeProfile = result.detach();
- return SLANG_OK;
-}
-
-static SlangResult _getEntryPointResult(
- EndToEndCompileRequest* req,
- int entryPointIndex,
- int targetIndex,
- ComPtr<IArtifact>& outArtifact)
-{
- auto linkage = req->getLinkage();
- auto program = req->getSpecializedGlobalAndEntryPointsComponentType();
-
- Index targetCount = linkage->targets.getCount();
- if ((targetIndex < 0) || (targetIndex >= targetCount))
- {
- return SLANG_E_INVALID_ARG;
- }
- auto targetReq = linkage->targets[targetIndex];
-
- // Get the entry point count on the program, rather than (say) req->m_entryPoints.getCount()
- // because
- // 1) The entry point is fetched from the program anyway so must be consistent
- // 2) The req may not have all entry points (for example when an entry point is in a module)
- const Index entryPointCount = program->getEntryPointCount();
-
- if ((entryPointIndex < 0) || (entryPointIndex >= entryPointCount))
- {
- return SLANG_E_INVALID_ARG;
- }
- auto entryPointReq = program->getEntryPoint(entryPointIndex);
-
- auto targetProgram = program->getTargetProgram(targetReq);
- if (!targetProgram)
- return SLANG_FAIL;
-
- outArtifact = targetProgram->getExistingEntryPointResult(entryPointIndex);
- return SLANG_OK;
-}
-
-static SlangResult _getWholeProgramResult(
- EndToEndCompileRequest* req,
- int targetIndex,
- ComPtr<IArtifact>& outArtifact)
-{
- auto linkage = req->getLinkage();
- auto program = req->getSpecializedGlobalAndEntryPointsComponentType();
-
- if (!program)
- {
- return SLANG_FAIL;
- }
-
- Index targetCount = linkage->targets.getCount();
- if ((targetIndex < 0) || (targetIndex >= targetCount))
- {
- return SLANG_E_INVALID_ARG;
- }
- auto targetReq = linkage->targets[targetIndex];
-
- auto targetProgram = program->getTargetProgram(targetReq);
- if (!targetProgram)
- return SLANG_FAIL;
- outArtifact = targetProgram->getExistingWholeProgramResult();
- return SLANG_OK;
-}
-
-SlangResult EndToEndCompileRequest::getEntryPointCodeBlob(
- int entryPointIndex,
- int targetIndex,
- ISlangBlob** outBlob)
-{
- if (!outBlob)
- return SLANG_E_INVALID_ARG;
- ComPtr<IArtifact> artifact;
- SLANG_RETURN_ON_FAIL(_getEntryPointResult(this, entryPointIndex, targetIndex, artifact));
- SLANG_RETURN_ON_FAIL(artifact->loadBlob(ArtifactKeep::Yes, outBlob));
-
- return SLANG_OK;
-}
-
-SlangResult EndToEndCompileRequest::getEntryPointHostCallable(
- int entryPointIndex,
- int targetIndex,
- ISlangSharedLibrary** outSharedLibrary)
-{
- if (!outSharedLibrary)
- return SLANG_E_INVALID_ARG;
- ComPtr<IArtifact> artifact;
- SLANG_RETURN_ON_FAIL(_getEntryPointResult(this, entryPointIndex, targetIndex, artifact));
- SLANG_RETURN_ON_FAIL(artifact->loadSharedLibrary(ArtifactKeep::Yes, outSharedLibrary));
- return SLANG_OK;
-}
-
-SlangResult EndToEndCompileRequest::getTargetCodeBlob(int targetIndex, ISlangBlob** outBlob)
-{
- if (!outBlob)
- return SLANG_E_INVALID_ARG;
-
- ComPtr<IArtifact> artifact;
- SLANG_RETURN_ON_FAIL(_getWholeProgramResult(this, targetIndex, artifact));
- SLANG_RETURN_ON_FAIL(artifact->loadBlob(ArtifactKeep::Yes, outBlob));
- return SLANG_OK;
-}
-
-SlangResult EndToEndCompileRequest::getTargetHostCallable(
- int targetIndex,
- ISlangSharedLibrary** outSharedLibrary)
-{
- if (!outSharedLibrary)
- return SLANG_E_INVALID_ARG;
-
- ComPtr<IArtifact> artifact;
- SLANG_RETURN_ON_FAIL(_getWholeProgramResult(this, targetIndex, artifact));
- SLANG_RETURN_ON_FAIL(artifact->loadSharedLibrary(ArtifactKeep::Yes, outSharedLibrary));
- return SLANG_OK;
-}
-
-char const* EndToEndCompileRequest::getEntryPointSource(int entryPointIndex)
-{
- return (char const*)getEntryPointCode(entryPointIndex, nullptr);
-}
-
-ISlangMutableFileSystem* EndToEndCompileRequest::getCompileRequestResultAsFileSystem()
-{
- if (!m_containerFileSystem)
- {
- if (m_containerArtifact)
- {
- ComPtr<ISlangMutableFileSystem> fileSystem(new MemoryFileSystem);
-
- // Filter the containerArtifact into things that can be written
- ComPtr<IArtifact> writeArtifact;
- if (SLANG_SUCCEEDED(
- ArtifactContainerUtil::filter(m_containerArtifact, writeArtifact)) &&
- writeArtifact)
- {
- if (SLANG_SUCCEEDED(
- ArtifactContainerUtil::writeContainer(writeArtifact, "", fileSystem)))
- {
- m_containerFileSystem.swap(fileSystem);
- }
- }
- }
- }
-
- return m_containerFileSystem;
-}
-
-void const* EndToEndCompileRequest::getCompileRequestCode(size_t* outSize)
-{
- if (m_containerArtifact)
- {
- ComPtr<ISlangBlob> containerBlob;
- if (SLANG_SUCCEEDED(
- m_containerArtifact->loadBlob(ArtifactKeep::Yes, containerBlob.writeRef())))
- {
- *outSize = containerBlob->getBufferSize();
- return containerBlob->getBufferPointer();
- }
- }
-
- // Container blob does not have any contents
- *outSize = 0;
- return nullptr;
-}
-
-SlangResult EndToEndCompileRequest::getContainerCode(ISlangBlob** outBlob)
-{
- if (m_containerArtifact)
- {
- ComPtr<ISlangBlob> containerBlob;
- if (SLANG_SUCCEEDED(
- m_containerArtifact->loadBlob(ArtifactKeep::Yes, containerBlob.writeRef())))
- {
- *outBlob = containerBlob.detach();
- return SLANG_OK;
- }
- }
- return SLANG_FAIL;
-}
-
-SlangResult EndToEndCompileRequest::loadRepro(
- ISlangFileSystem* fileSystem,
- const void* data,
- size_t size)
-{
- List<uint8_t> buffer;
- SLANG_RETURN_ON_FAIL(ReproUtil::loadState((const uint8_t*)data, size, getSink(), buffer));
-
- MemoryOffsetBase base;
- base.set(buffer.getBuffer(), buffer.getCount());
-
- ReproUtil::RequestState* requestState = ReproUtil::getRequest(buffer);
-
- SLANG_RETURN_ON_FAIL(ReproUtil::load(base, requestState, fileSystem, this));
- return SLANG_OK;
-}
-
-SlangResult EndToEndCompileRequest::saveRepro(ISlangBlob** outBlob)
-{
- OwnedMemoryStream stream(FileAccess::Write);
-
- SLANG_RETURN_ON_FAIL(ReproUtil::saveState(this, &stream));
-
- // Put the content of the stream in the blob
-
- List<uint8_t> data;
- stream.swapContents(data);
-
- *outBlob = ListBlob::moveCreate(data).detach();
- return SLANG_OK;
-}
-
-SlangResult EndToEndCompileRequest::enableReproCapture()
-{
- getLinkage()->setRequireCacheFileSystem(true);
- return SLANG_OK;
-}
-
-SlangResult EndToEndCompileRequest::processCommandLineArguments(
- char const* const* args,
- int argCount)
-{
- return parseOptions(this, argCount, args);
-}
-
-SlangReflection* EndToEndCompileRequest::getReflection()
-{
- auto linkage = getLinkage();
- auto program = getSpecializedGlobalAndEntryPointsComponentType();
-
- // Note(tfoley): The API signature doesn't let the client
- // specify which target they want to access reflection
- // information for, so for now we default to the first one.
- //
- // TODO: Add a new `spGetReflectionForTarget(req, targetIndex)`
- // so that we can do this better, and make it clear that
- // `spGetReflection()` is shorthand for `targetIndex == 0`.
- //
- Slang::Index targetIndex = 0;
- auto targetCount = linkage->targets.getCount();
- if (targetIndex >= targetCount)
- return nullptr;
-
- auto targetReq = linkage->targets[targetIndex];
- auto targetProgram = program->getTargetProgram(targetReq);
-
-
- DiagnosticSink sink(linkage->getSourceManager(), Lexer::sourceLocationLexer);
- auto programLayout = targetProgram->getOrCreateLayout(&sink);
-
- return (SlangReflection*)programLayout;
-}
-
-SlangResult EndToEndCompileRequest::getProgram(slang::IComponentType** outProgram)
-{
- auto program = getSpecializedGlobalComponentType();
- *outProgram = Slang::ComPtr<slang::IComponentType>(program).detach();
- return SLANG_OK;
-}
-
-SlangResult EndToEndCompileRequest::getProgramWithEntryPoints(slang::IComponentType** outProgram)
-{
- auto program = getSpecializedGlobalAndEntryPointsComponentType();
- *outProgram = Slang::ComPtr<slang::IComponentType>(program).detach();
- return SLANG_OK;
-}
-
-SlangResult EndToEndCompileRequest::getModule(
- SlangInt translationUnitIndex,
- slang::IModule** outModule)
-{
- auto module = getFrontEndReq()->getTranslationUnit(translationUnitIndex)->getModule();
-
- *outModule = Slang::ComPtr<slang::IModule>(module).detach();
- return SLANG_OK;
-}
-
-SlangResult EndToEndCompileRequest::getSession(slang::ISession** outSession)
-{
- auto session = getLinkage();
- *outSession = Slang::ComPtr<slang::ISession>(session).detach();
- return SLANG_OK;
-}
-
-SlangResult EndToEndCompileRequest::getEntryPoint(
- SlangInt entryPointIndex,
- slang::IComponentType** outEntryPoint)
-{
- auto entryPoint = getSpecializedEntryPointComponentType(entryPointIndex);
- *outEntryPoint = Slang::ComPtr<slang::IComponentType>(entryPoint).detach();
- return SLANG_OK;
-}
-
-SlangResult EndToEndCompileRequest::isParameterLocationUsed(
- Int entryPointIndex,
- Int targetIndex,
- SlangParameterCategory category,
- UInt spaceIndex,
- UInt registerIndex,
- bool& outUsed)
-{
- if (!ShaderBindingRange::isUsageTracked((slang::ParameterCategory)category))
- return SLANG_E_NOT_AVAILABLE;
-
- ComPtr<IArtifact> artifact;
- if (SLANG_FAILED(_getEntryPointResult(
- this,
- static_cast<int>(entryPointIndex),
- static_cast<int>(targetIndex),
- artifact)))
- return SLANG_E_INVALID_ARG;
-
- if (!artifact)
- return SLANG_E_NOT_AVAILABLE;
-
- // Find a rep
- auto metadata = findAssociatedRepresentation<IArtifactPostEmitMetadata>(artifact);
- if (!metadata)
- return SLANG_E_NOT_AVAILABLE;
-
- return metadata->isParameterLocationUsed(category, spaceIndex, registerIndex, outUsed);
-}
-
} // namespace Slang