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.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.cpp')
| -rw-r--r-- | source/slang/slang.cpp | 8070 |
1 files changed, 0 insertions, 8070 deletions
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 |
