summaryrefslogtreecommitdiff
path: root/source/slang/slang-global-session.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'source/slang/slang-global-session.cpp')
-rw-r--r--source/slang/slang-global-session.cpp1217
1 files changed, 1217 insertions, 0 deletions
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