diff options
| author | Theresa Foley <10618364+tangent-vector@users.noreply.github.com> | 2025-07-24 12:59:58 -0700 |
|---|---|---|
| committer | GitHub <noreply@github.com> | 2025-07-24 19:59:58 +0000 |
| commit | 8ccd495d5eaa82cb831378c28dd190e657b6c999 (patch) | |
| tree | c39dc364d95f78984fd4ba2776d259ff944d2596 /source/slang/slang-global-session.cpp | |
| parent | 2d23a962766a97cbb11bcee5483a66aec923da49 (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>
Diffstat (limited to 'source/slang/slang-global-session.cpp')
| -rw-r--r-- | source/slang/slang-global-session.cpp | 1217 |
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 |
