diff options
Diffstat (limited to 'source/slang')
| -rwxr-xr-x | source/slang/slang-compiler.h | 12 | ||||
| -rw-r--r-- | source/slang/slang-serialize-container.cpp | 402 | ||||
| -rw-r--r-- | source/slang/slang-serialize-container.h | 3 | ||||
| -rw-r--r-- | source/slang/slang-serialize-types.h | 3 | ||||
| -rw-r--r-- | source/slang/slang.cpp | 101 |
5 files changed, 350 insertions, 171 deletions
diff --git a/source/slang/slang-compiler.h b/source/slang/slang-compiler.h index 7da003b8a..6c55a7807 100755 --- a/source/slang/slang-compiler.h +++ b/source/slang/slang-compiler.h @@ -256,6 +256,12 @@ namespace Slang /// Add all of the paths that `module` depends on to the list void addDependency(Module* module); + void clear() + { + m_fileList.clear(); + m_fileSet.clear(); + } + private: // TODO: We are using a `HashSet` here to deduplicate @@ -1400,6 +1406,7 @@ namespace Slang /// Register a source file that this module depends on void addFileDependency(SourceFile* sourceFile); + void clearFileDependency() { m_fileDependencyList.clear(); } /// Set the AST for this module. /// /// This should only be called once, during creation of the module. @@ -1819,6 +1826,7 @@ namespace Slang SlangCompileRequest** outCompileRequest) override; virtual SLANG_NO_THROW SlangInt SLANG_MCALL getLoadedModuleCount() override; virtual SLANG_NO_THROW slang::IModule* SLANG_MCALL getLoadedModule(SlangInt index) override; + virtual SLANG_NO_THROW bool SLANG_MCALL isBinaryModuleUpToDate(const char* modulePath, slang::IBlob* binaryModuleBlob) override; // Updates the supplied builder with linkage-related information, which includes preprocessor // defines, the compiler version, and other compiler options. This is then merged with the hash @@ -1952,6 +1960,8 @@ namespace Slang DiagnosticSink* sink, const LoadedModuleDictionary* additionalLoadedModules); + SourceFile* loadSourceFile(String pathFrom, String path); + void loadParsedModule( RefPtr<FrontEndCompileRequest> compileRequest, RefPtr<TranslationUnitRequest> translationUnit, @@ -1961,6 +1971,8 @@ namespace Slang /// Load a module of the given name. Module* loadModule(String const& name); + bool isBinaryModuleUpToDate(String fromPath, RiffContainer* container); + RefPtr<Module> findOrImportModule( Name* name, SourceLoc const& loc, diff --git a/source/slang/slang-serialize-container.cpp b/source/slang/slang-serialize-container.cpp index 39b67614d..c9db7ff5e 100644 --- a/source/slang/slang-serialize-container.cpp +++ b/source/slang/slang-serialize-container.cpp @@ -11,7 +11,7 @@ #include "slang-serialize-ir.h" #include "slang-serialize-source-loc.h" #include "slang-serialize-factory.h" - +#include "../core/slang-stream.h" #include "slang-parser.h" #include "slang-mangled-lexer.h" @@ -80,7 +80,31 @@ namespace Slang { dstModule.irModule = module->getIRModule(); SLANG_ASSERT(dstModule.irModule); } - + DigestBuilder<SHA1> digestBuilder; + module->getOptionSet().buildHash(digestBuilder); + auto fileDependencies = module->getFileDependencies(); + String canonicalModulePath; + if (auto modulePath = module->getFilePath()) + { + canonicalModulePath = Path::getParentDirectory(modulePath); + Path::getCanonical(canonicalModulePath, canonicalModulePath); + } + for (auto file : fileDependencies) + { + digestBuilder.append(file->getDigest()); + if (file->getPathInfo().hasFoundPath()) + { + String canonicalFilePath = file->getPathInfo().foundPath; + Path::getCanonical(file->getPathInfo().foundPath, canonicalFilePath); + canonicalFilePath = Path::getRelativePath(canonicalModulePath, canonicalFilePath); + dstModule.dependentFiles.add(canonicalFilePath); + } + else + { + dstModule.dependentFiles.add(file->getPathInfo().getMostUniqueIdentity()); + } + } + dstModule.digest = digestBuilder.finalize(); outData.modules.add(dstModule); } @@ -189,6 +213,24 @@ namespace Slang { // Okay, we need to serialize this module to our container file. // We currently don't serialize it's name..., but support for that could be added. + // First, we write a header that can be used to verify if the precompiled module is up-to-date. + // The header has: + // 1) a digest of all compile options and dependent source files. + // 2) a list of source file paths. + // + { + RiffContainer::ScopeChunk scopeHeader(container, RiffContainer::Chunk::Kind::Data, SerialBinary::kModuleHeaderFourCc); + OwnedMemoryStream headerMemStream(FileAccess::Write); + StringBuilder filePathsSB; + for (auto fileDependency : module.dependentFiles) + filePathsSB << fileDependency << "\n"; + headerMemStream.write(module.digest.data, sizeof(module.digest.data)); + uint32_t fileListLength = (uint32_t)filePathsSB.getLength(); + headerMemStream.write(&fileListLength, sizeof(uint32_t)); + headerMemStream.write(filePathsSB.getBuffer(), fileListLength); + container->write(headerMemStream.getContents().getBuffer(), headerMemStream.getContents().getCount()); + } + // Write the IR information if ((options.optionFlags & SerialOptionFlag::IRModule) && module.irModule) { @@ -357,16 +399,53 @@ static List<ExtensionDecl*>& _getCandidateExtensionList( RefPtr<ASTBuilder> astBuilder = options.astBuilder; NodeBase* astRootNode = nullptr; RefPtr<IRModule> irModule; + SerialContainerData::Module module; + bool hasHeader = false; + if (auto headerChunk = as<RiffContainer::DataChunk>(chunk, SerialBinary::kModuleHeaderFourCc)) + { + hasHeader = true; + MemoryStreamBase memStream( + FileAccess::Read, + headerChunk->getSingleData()->getPayload(), + headerChunk->getSingleData()->getSize()); + size_t readSize = 0; + memStream.read(module.digest.data, sizeof(SHA1::Digest), readSize); + if (readSize != sizeof(SHA1::Digest)) + return SLANG_FAIL; + uint32_t fileListLength = 0; + memStream.read(&fileListLength, sizeof(uint32_t), readSize); + if (readSize != sizeof(uint32_t)) + return SLANG_FAIL; + List<uint8_t> fileListContent; + fileListContent.setCount(fileListLength); + memStream.read(fileListContent.getBuffer(), fileListContent.getCount(), readSize); + if (readSize != (size_t)fileListContent.getCount()) + return SLANG_FAIL; + UnownedStringSlice fileListString((const char*)fileListContent.getBuffer(), fileListContent.getCount()); + List<UnownedStringSlice> fileList; + StringUtil::split(fileListString, '\n', fileList); + for (auto file : fileList) + { + if (file.getLength()) + { + module.dependentFiles.add(file); + } + } + // Onto next chunk + chunk = chunk->m_next; + } if (auto irChunk = as<RiffContainer::ListChunk>(chunk, IRSerialBinary::kIRModuleFourCc)) { - IRSerialData serialData; - - SLANG_RETURN_ON_FAIL(IRSerialReader::readContainer(irChunk, containerCompressionType, &serialData)); + if (!options.readHeaderOnly) + { + IRSerialData serialData; + SLANG_RETURN_ON_FAIL(IRSerialReader::readContainer(irChunk, containerCompressionType, &serialData)); - // Read IR back from serialData - IRSerialReader reader; - SLANG_RETURN_ON_FAIL(reader.read(serialData, options.session, sourceLocReader, irModule)); + // Read IR back from serialData + IRSerialReader reader; + SLANG_RETURN_ON_FAIL(reader.read(serialData, options.session, sourceLocReader, irModule)); + } // Onto next chunk chunk = chunk->m_next; @@ -374,207 +453,210 @@ static List<ExtensionDecl*>& _getCandidateExtensionList( if (auto astChunk = as<RiffContainer::ListChunk>(chunk, ASTSerialBinary::kSlangASTModuleFourCC)) { - RiffContainer::Data* astData = astChunk->findContainedData(ASTSerialBinary::kSlangASTModuleDataFourCC); - - if (astData) + if (!options.readHeaderOnly) { - if (!serialClasses) - { - SLANG_RETURN_ON_FAIL(SerialClassesUtil::create(serialClasses)); - } - - // TODO(JS): We probably want to store off better information about each of the translation unit - // including some kind of 'name'. - // For now we just generate a name. + RiffContainer::Data* astData = astChunk->findContainedData(ASTSerialBinary::kSlangASTModuleDataFourCC); - StringBuilder buf; - buf << "tu" << out.modules.getCount(); - if (!astBuilder) + if (astData) { - astBuilder = new ASTBuilder(options.sharedASTBuilder, buf.produceString()); - } + if (!serialClasses) + { + SLANG_RETURN_ON_FAIL(SerialClassesUtil::create(serialClasses)); + } - /// We need to make the current ASTBuilder available for access via thread_local global. - SetASTBuilderContextRAII astBuilderRAII(astBuilder); + // TODO(JS): We probably want to store off better information about each of the translation unit + // including some kind of 'name'. + // For now we just generate a name. - DefaultSerialObjectFactory objectFactory(astBuilder); + StringBuilder buf; + buf << "tu" << out.modules.getCount(); + if (!astBuilder) + { + astBuilder = new ASTBuilder(options.sharedASTBuilder, buf.produceString()); + } - SerialReader reader(serialClasses, &objectFactory); + /// We need to make the current ASTBuilder available for access via thread_local global. + SetASTBuilderContextRAII astBuilderRAII(astBuilder); - // Sets up the entry table - one entry for each 'object'. - // No native objects are constructed. No objects are deserialized. - SLANG_RETURN_ON_FAIL(reader.loadEntries((const uint8_t*)astData->getPayload(), astData->getSize())); + DefaultSerialObjectFactory objectFactory(astBuilder); - // Construct a native object for each table entry (where appropriate). - // Note that this *doesn't* set all object pointers - some are special cased and created on demand (strings) - // and imported symbols will have their object pointers unset (they are resolved in next step) - SLANG_RETURN_ON_FAIL(reader.constructObjects(options.namePool)); + SerialReader reader(serialClasses, &objectFactory); - // Resolve external references if the linkage is specified - if (options.linkage) - { - const auto& entries = reader.getEntries(); - auto& objects = reader.getObjects(); - const Index entriesCount = entries.getCount(); + // Sets up the entry table - one entry for each 'object'. + // No native objects are constructed. No objects are deserialized. + SLANG_RETURN_ON_FAIL(reader.loadEntries((const uint8_t*)astData->getPayload(), astData->getSize())); - String currentModuleName; - Module* currentModule = nullptr; + // Construct a native object for each table entry (where appropriate). + // Note that this *doesn't* set all object pointers - some are special cased and created on demand (strings) + // and imported symbols will have their object pointers unset (they are resolved in next step) + SLANG_RETURN_ON_FAIL(reader.constructObjects(options.namePool)); - // Index from 1 (0 is null) - for (Index i = 1; i < entriesCount; ++i) + // Resolve external references if the linkage is specified + if (options.linkage) { - const SerialInfo::Entry* entry = entries[i]; - if (entry->typeKind == SerialTypeKind::ImportSymbol) - { - UnownedStringSlice mangledName = reader.getStringSlice(SerialIndex(i)); + const auto& entries = reader.getEntries(); + auto& objects = reader.getObjects(); + const Index entriesCount = entries.getCount(); - String moduleName; - SLANG_RETURN_ON_FAIL(MangledNameParser::parseModuleName(mangledName, moduleName)); + String currentModuleName; + Module* currentModule = nullptr; - // If we already have looked up this module and it has the same name just use what we have - Module* readModule = nullptr; - if (currentModule && moduleName == currentModuleName.getUnownedSlice()) - { - readModule = currentModule; - } - else + // Index from 1 (0 is null) + for (Index i = 1; i < entriesCount; ++i) + { + const SerialInfo::Entry* entry = entries[i]; + if (entry->typeKind == SerialTypeKind::ImportSymbol) { - // The modules are loaded on the linkage. - Linkage* linkage = options.linkage; + UnownedStringSlice mangledName = reader.getStringSlice(SerialIndex(i)); - NamePool* namePool = linkage->getNamePool(); - Name* moduleNameName = namePool->getName(moduleName); + String moduleName; + SLANG_RETURN_ON_FAIL(MangledNameParser::parseModuleName(mangledName, moduleName)); - readModule = linkage->findOrImportModule(moduleNameName, SourceLoc::fromRaw(0), options.sink, additionalLoadedModules); - if (!readModule) + // If we already have looked up this module and it has the same name just use what we have + Module* readModule = nullptr; + if (currentModule && moduleName == currentModuleName.getUnownedSlice()) { - return SLANG_FAIL; + readModule = currentModule; } + else + { + // The modules are loaded on the linkage. + Linkage* linkage = options.linkage; - // Set the current module and name - currentModule = readModule; - currentModuleName = moduleName; - } + NamePool* namePool = linkage->getNamePool(); + Name* moduleNameName = namePool->getName(moduleName); - // Look up the symbol - NodeBase* nodeBase = readModule->findExportFromMangledName(mangledName); + readModule = linkage->findOrImportModule(moduleNameName, SourceLoc::fromRaw(0), options.sink, additionalLoadedModules); + if (!readModule) + { + return SLANG_FAIL; + } - if (!nodeBase) - { - if (options.sink) + // Set the current module and name + currentModule = readModule; + currentModuleName = moduleName; + } + + // Look up the symbol + NodeBase* nodeBase = readModule->findExportFromMangledName(mangledName); + + if (!nodeBase) { - options.sink->diagnose(SourceLoc::fromRaw(0), Diagnostics::unableToFindSymbolInModule, mangledName, moduleName); + if (options.sink) + { + options.sink->diagnose(SourceLoc::fromRaw(0), Diagnostics::unableToFindSymbolInModule, mangledName, moduleName); + } + + // If didn't find the export then we create an UnresolvedDecl node to represent the error. + auto unresolved = astBuilder->create<UnresolvedDecl>(); + unresolved->nameAndLoc.name = + options.linkage->getNamePool()->getName(mangledName); + nodeBase = unresolved; } - // If didn't find the export then we create an UnresolvedDecl node to represent the error. - auto unresolved = astBuilder->create<UnresolvedDecl>(); - unresolved->nameAndLoc.name = - options.linkage->getNamePool()->getName(mangledName); - nodeBase = unresolved; + // set the result + objects[i] = nodeBase; } - - // set the result - objects[i] = nodeBase; } } - } - - // Set the sourceLocReader before doing de-serialize, such can lookup the remapped sourceLocs - reader.getExtraObjects().set(sourceLocReader); - - // TODO(JS): - // If modules can have more complicated relationships (like a two modules can refer to symbols - // from each other), then we can make this work by - // 1) deserialize *without* the external symbols being set up - // 2) calculate the symbols - // 3) deserialize the other module (in the same way) - // 4) run deserializeObjects *again* on each module - // This is less efficient than it might be (because deserialize phase is done twice) so if this is necessary - // may want a mechanism that *just* does reference lookups. - // - // For now if we assume a module can only access symbols from another module, and not the reverse. - // So we just need to deserialize and we are done - SLANG_RETURN_ON_FAIL(reader.deserializeObjects()); - - // Get the root node. It's at index 1 (0 is the null value). - astRootNode = reader.getPointer(SerialIndex(1)).dynamicCast<NodeBase>(); - // Go through all AST nodes: - // 1) Add the extensions to the module mapTypeToCandidateExtensions cache - // 2) We need to fix the callback pointers for parsing - // 3) Register all `Val`s to the ASTBuilder's deduplication map. + // Set the sourceLocReader before doing de-serialize, such can lookup the remapped sourceLocs + reader.getExtraObjects().set(sourceLocReader); + + // TODO(JS): + // If modules can have more complicated relationships (like a two modules can refer to symbols + // from each other), then we can make this work by + // 1) deserialize *without* the external symbols being set up + // 2) calculate the symbols + // 3) deserialize the other module (in the same way) + // 4) run deserializeObjects *again* on each module + // This is less efficient than it might be (because deserialize phase is done twice) so if this is necessary + // may want a mechanism that *just* does reference lookups. + // + // For now if we assume a module can only access symbols from another module, and not the reverse. + // So we just need to deserialize and we are done + SLANG_RETURN_ON_FAIL(reader.deserializeObjects()); + + // Get the root node. It's at index 1 (0 is the null value). + astRootNode = reader.getPointer(SerialIndex(1)).dynamicCast<NodeBase>(); + + // Go through all AST nodes: + // 1) Add the extensions to the module mapTypeToCandidateExtensions cache + // 2) We need to fix the callback pointers for parsing + // 3) Register all `Val`s to the ASTBuilder's deduplication map. - { - ModuleDecl* moduleDecl = as<ModuleDecl>(astRootNode); + { + ModuleDecl* moduleDecl = as<ModuleDecl>(astRootNode); - // Maps from keyword name name to index in (syntaxParseInfos) - // Will be filled in lazily if needed (for SyntaxDecl setup) - Dictionary<Name*, Index> syntaxKeywordDict; - - OrderedDictionary<Val*, List<Val**>> valUses; + // Maps from keyword name name to index in (syntaxParseInfos) + // Will be filled in lazily if needed (for SyntaxDecl setup) + Dictionary<Name*, Index> syntaxKeywordDict; - // Get the parse infos - const auto syntaxParseInfos = getSyntaxParseInfos(); - SLANG_ASSERT(syntaxParseInfos.getCount()); + OrderedDictionary<Val*, List<Val**>> valUses; - for (auto& obj : reader.getObjects()) - { + // Get the parse infos + const auto syntaxParseInfos = getSyntaxParseInfos(); + SLANG_ASSERT(syntaxParseInfos.getCount()); - if (obj.m_kind == SerialTypeKind::NodeBase) + for (auto& obj : reader.getObjects()) { - NodeBase* nodeBase = (NodeBase*)obj.m_ptr; - SLANG_ASSERT(nodeBase); - if (ExtensionDecl* extensionDecl = dynamicCast<ExtensionDecl>(nodeBase)) + if (obj.m_kind == SerialTypeKind::NodeBase) { - if (auto targetDeclRefType = as<DeclRefType>(extensionDecl->targetType)) + NodeBase* nodeBase = (NodeBase*)obj.m_ptr; + SLANG_ASSERT(nodeBase); + + if (ExtensionDecl* extensionDecl = dynamicCast<ExtensionDecl>(nodeBase)) { - // Attach our extension to that type as a candidate... - if (auto aggTypeDeclRef = targetDeclRefType->getDeclRef().as<AggTypeDecl>()) + if (auto targetDeclRefType = as<DeclRefType>(extensionDecl->targetType)) { - auto aggTypeDecl = aggTypeDeclRef.getDecl(); + // Attach our extension to that type as a candidate... + if (auto aggTypeDeclRef = targetDeclRefType->getDeclRef().as<AggTypeDecl>()) + { + auto aggTypeDecl = aggTypeDeclRef.getDecl(); - _getCandidateExtensionList(aggTypeDecl, moduleDecl->mapTypeToCandidateExtensions).add(extensionDecl); + _getCandidateExtensionList(aggTypeDecl, moduleDecl->mapTypeToCandidateExtensions).add(extensionDecl); + } } } - } - else if (SyntaxDecl* syntaxDecl = dynamicCast<SyntaxDecl>(nodeBase)) - { - // Set up the dictionary lazily - if (syntaxKeywordDict.getCount() == 0) + else if (SyntaxDecl* syntaxDecl = dynamicCast<SyntaxDecl>(nodeBase)) { - NamePool* namePool = options.session->getNamePool(); - for (Index i = 0; i < syntaxParseInfos.getCount(); ++i) + // Set up the dictionary lazily + if (syntaxKeywordDict.getCount() == 0) { - const auto& entry = syntaxParseInfos[i]; - syntaxKeywordDict.add(namePool->getName(entry.keywordName), i); + NamePool* namePool = options.session->getNamePool(); + for (Index i = 0; i < syntaxParseInfos.getCount(); ++i) + { + const auto& entry = syntaxParseInfos[i]; + syntaxKeywordDict.add(namePool->getName(entry.keywordName), i); + } + // Must have something in it at this point + SLANG_ASSERT(syntaxKeywordDict.getCount()); } - // Must have something in it at this point - SLANG_ASSERT(syntaxKeywordDict.getCount()); - } - // Look up the index - Index* entryIndexPtr = syntaxKeywordDict.tryGetValue(syntaxDecl->getName()); - if (entryIndexPtr) - { - // Set up SyntaxDecl based on the ParseSyntaxIndo - auto& info = syntaxParseInfos[*entryIndexPtr]; - syntaxDecl->parseCallback = *info.callback; - syntaxDecl->parseUserData = const_cast<ReflectClassInfo*>(info.classInfo); + // Look up the index + Index* entryIndexPtr = syntaxKeywordDict.tryGetValue(syntaxDecl->getName()); + if (entryIndexPtr) + { + // Set up SyntaxDecl based on the ParseSyntaxIndo + auto& info = syntaxParseInfos[*entryIndexPtr]; + syntaxDecl->parseCallback = *info.callback; + syntaxDecl->parseUserData = const_cast<ReflectClassInfo*>(info.classInfo); + } + else + { + // If we don't find a setup entry, we use `parseSimpleSyntax`, and set + // the parseUserData to the ReflectClassInfo (as parseSimpleSyntax needs this) + syntaxDecl->parseCallback = &parseSimpleSyntax; + SLANG_ASSERT(syntaxDecl->syntaxClass.classInfo); + syntaxDecl->parseUserData = const_cast<ReflectClassInfo*>(syntaxDecl->syntaxClass.classInfo); + } } - else + else if (Val* val = dynamicCast<Val>(nodeBase)) { - // If we don't find a setup entry, we use `parseSimpleSyntax`, and set - // the parseUserData to the ReflectClassInfo (as parseSimpleSyntax needs this) - syntaxDecl->parseCallback = &parseSimpleSyntax; - SLANG_ASSERT(syntaxDecl->syntaxClass.classInfo); - syntaxDecl->parseUserData = const_cast<ReflectClassInfo*>(syntaxDecl->syntaxClass.classInfo); + val->_setUnique(); } } - else if (Val* val = dynamicCast<Val>(nodeBase)) - { - val->_setUnique(); - } } } } @@ -586,8 +668,6 @@ static List<ExtensionDecl*>& _getCandidateExtensionList( if (astBuilder || irModule) { - SerialContainerData::Module module; - module.astBuilder = astBuilder; module.astRootNode = astRootNode; module.irModule = irModule; diff --git a/source/slang/slang-serialize-container.h b/source/slang/slang-serialize-container.h index a2b596a24..9b9fbe6a1 100644 --- a/source/slang/slang-serialize-container.h +++ b/source/slang/slang-serialize-container.h @@ -53,6 +53,8 @@ struct SerialContainerData RefPtr<IRModule> irModule; ///< The IR for the module RefPtr<ASTBuilder> astBuilder; ///< The astBuilder that owns the astRootNode NodeBase* astRootNode = nullptr; ///< The module decl + List<String> dependentFiles; + SHA1::Digest digest; }; struct EntryPoint @@ -92,6 +94,7 @@ struct SerialContainerUtil ASTBuilder* astBuilder = nullptr; // Optional. If not provided will create one in SerialContainerData. Linkage* linkage = nullptr; DiagnosticSink* sink = nullptr; + bool readHeaderOnly = false; }; /// Add module to outData diff --git a/source/slang/slang-serialize-types.h b/source/slang/slang-serialize-types.h index 7623eb4cf..6be35ba09 100644 --- a/source/slang/slang-serialize-types.h +++ b/source/slang/slang-serialize-types.h @@ -144,6 +144,9 @@ struct SerialBinary /// Container static const FourCC kContainerHeaderFourCc = SLANG_FOUR_CC('S', 'c', 'h', 'd'); + // Module header + static const FourCC kModuleHeaderFourCc = SLANG_FOUR_CC('S', 'm', 'h', 'd'); + struct ContainerHeader { uint32_t compressionType; ///< Holds the compression type used (if used at all) diff --git a/source/slang/slang.cpp b/source/slang/slang.cpp index 62fb3cbdb..c0f89b0dc 100644 --- a/source/slang/slang.cpp +++ b/source/slang/slang.cpp @@ -3165,12 +3165,19 @@ RefPtr<Module> Linkage::loadModuleFromIRBlobImpl( String mostUniqueIdentity = filePathInfo.getMostUniqueIdentity(); SLANG_ASSERT(mostUniqueIdentity.getLength() > 0); - mapPathToLoadedModule.add(mostUniqueIdentity, resultModule); - mapNameToLoadedModules.add(name, resultModule); - RiffContainer container; MemoryStreamBase readStream(FileAccess::Read, fileContentsBlob->getBufferPointer(), fileContentsBlob->getBufferSize()); SLANG_RETURN_NULL_ON_FAIL(RiffUtil::read(&readStream, container)); + + if (m_optionSet.getBoolOption(CompilerOptionName::UseUpToDateBinaryModule)) + { + if (!isBinaryModuleUpToDate(filePathInfo.foundPath, &container)) + return nullptr; + } + + mapPathToLoadedModule.add(mostUniqueIdentity, resultModule); + mapNameToLoadedModules.add(name, resultModule); + SerialContainerUtil::ReadOptions readOptions; readOptions.linkage = this; readOptions.astBuilder = getASTBuilder(); @@ -3180,12 +3187,25 @@ RefPtr<Module> Linkage::loadModuleFromIRBlobImpl( readOptions.sourceManager = getSourceManager(); readOptions.namePool = getNamePool(); SerialContainerData containerData; - SLANG_RETURN_NULL_ON_FAIL(SerialContainerUtil::read(&container, readOptions, additionalLoadedModules, containerData)); - if (containerData.modules.getCount() != 1) + if (SLANG_FAILED(SerialContainerUtil::read(&container, readOptions, additionalLoadedModules, containerData)) || + containerData.modules.getCount() != 1) + { + mapPathToLoadedModule.remove(mostUniqueIdentity); + mapNameToLoadedModules.remove(name); return nullptr; + } auto moduleEntry = containerData.modules.getFirst(); resultModule->setIRModule(moduleEntry.irModule); resultModule->setModuleDecl(as<ModuleDecl>(moduleEntry.astRootNode)); + resultModule->clearFileDependency(); + for (auto file : moduleEntry.dependentFiles) + { + auto sourceFile = loadSourceFile(filePathInfo.foundPath, file); + if (sourceFile) + { + resultModule->addFileDependency(sourceFile); + } + } prepareDeserializedModule(resultModule, sink); @@ -3463,7 +3483,7 @@ RefPtr<Module> Linkage::findOrImportModule( // We've found a file that we can load for the given module, so // go ahead and perform the module-load action - return loadModule( + auto resultModule = loadModule( name, filePathInfo, fileContents, @@ -3471,6 +3491,8 @@ RefPtr<Module> Linkage::findOrImportModule( sink, loadedModules, (checkBinaryModule == 1 ? ModuleBlobType::IR : ModuleBlobType::Source)); + if (resultModule) + return resultModule; } // Error: we cannot find the file. @@ -3479,6 +3501,67 @@ RefPtr<Module> Linkage::findOrImportModule( 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, RiffContainer* container) +{ + DiagnosticSink sink; + SerialContainerUtil::ReadOptions readOptions; + readOptions.linkage = this; + readOptions.astBuilder = getASTBuilder(); + readOptions.session = getSessionImpl(); + readOptions.sharedASTBuilder = getASTBuilder()->getSharedASTBuilder(); + readOptions.sink = &sink; + readOptions.sourceManager = getSourceManager(); + readOptions.namePool = getNamePool(); + readOptions.readHeaderOnly = true; + + SerialContainerData containerData; + if (SLANG_FAILED(SerialContainerUtil::read(container, readOptions, nullptr, containerData))) + return false; + + if (containerData.modules.getCount() != 1) + return false; + + auto& moduleHeader = containerData.modules[0]; + DigestBuilder<SHA1> digestBuilder; + m_optionSet.buildHash(digestBuilder); + for (auto file : moduleHeader.dependentFiles) + { + 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 (moduleHeader.dependentFiles.getCount() != 0) + sourceFile = loadSourceFile(moduleHeader.dependentFiles.getFirst(), file); + } + if (!sourceFile) + return false; + digestBuilder.append(sourceFile->getDigest()); + } + return digestBuilder.finalize() == moduleHeader.digest; +} + +SLANG_NO_THROW bool SLANG_MCALL Linkage::isBinaryModuleUpToDate(const char* modulePath, slang::IBlob* binaryModuleBlob) +{ + RiffContainer container; + MemoryStreamBase readStream(FileAccess::Read, binaryModuleBlob->getBufferPointer(), binaryModuleBlob->getBufferSize()); + if (SLANG_FAILED(RiffUtil::read(&readStream, container))) + return false; + return isBinaryModuleUpToDate(modulePath, &container); +} + SourceFile* Linkage::findFile(Name* name, SourceLoc loc, IncludeSystem& outIncludeSystem) { auto fileName = getFileNameFromModuleName(name); @@ -3665,7 +3748,7 @@ Module::Module(Linkage* linkage, ASTBuilder* astBuilder) { m_astBuilder = linkage->getASTBuilder(); } - + getOptionSet() = linkage->m_optionSet; addModuleDependency(this); } @@ -4004,9 +4087,7 @@ SLANG_NO_THROW void SLANG_MCALL ComponentType::getEntryPointHash( // Enumerate all file dependencies and add them to the hash. for (SourceFile* sourceFile : getFileDependencies()) { - // TODO: We want to lazily evaluate & cache the source file digest - SHA1::Digest digest = SHA1::compute(sourceFile->getContent().begin(), sourceFile->getContent().getLength()); - builder.append(digest); + builder.append(sourceFile->getDigest()); } buildHash(builder); |
