summaryrefslogtreecommitdiffstats
path: root/source/slang
diff options
context:
space:
mode:
Diffstat (limited to 'source/slang')
-rwxr-xr-xsource/slang/slang-compiler.h12
-rw-r--r--source/slang/slang-serialize-container.cpp402
-rw-r--r--source/slang/slang-serialize-container.h3
-rw-r--r--source/slang/slang-serialize-types.h3
-rw-r--r--source/slang/slang.cpp101
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);