summaryrefslogtreecommitdiff
path: root/source/slang/slang.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'source/slang/slang.cpp')
-rw-r--r--source/slang/slang.cpp870
1 files changed, 616 insertions, 254 deletions
diff --git a/source/slang/slang.cpp b/source/slang/slang.cpp
index 351ab6f06..99457647d 100644
--- a/source/slang/slang.cpp
+++ b/source/slang/slang.cpp
@@ -570,64 +570,110 @@ SlangResult Session::saveCoreModule(SlangArchiveType archiveType, ISlangBlob** o
}
SlangResult Session::saveBuiltinModule(
- slang::BuiltinModuleName builtinModuleName,
+ 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)
{
- // There is no standard lib loaded
return SLANG_FAIL;
}
- BuiltinModuleInfo builtinModuleInfo = getBuiltinModuleInfo(builtinModuleName);
-
- // Make a file system to read it from
- ComPtr<ISlangMutableFileSystem> fileSystem;
- SLANG_RETURN_ON_FAIL(createArchiveFileSystem(archiveType, fileSystem));
-
- // Must have archiveFileSystem interface
- auto archiveFileSystem = as<IArchiveFileSystem>(fileSystem);
- if (!archiveFileSystem)
- {
- 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(builtinModuleInfo.name)),
+ 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());
- // Set up options
- SerialContainerUtil::WriteOptions options;
+ // 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;
+ }
- // Save with SourceLocation information
- options.optionFlags |= SerialOptionFlag::SourceLocation;
+ // 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";
- // TODO(JS): Should this be the Session::getBuiltinSourceManager()?
+ // 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.
+ //
+ options.optionFlags |= SerialOptionFlag::SourceLocation;
+ //
+ // 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.sourceManager = m_builtinLinkage->getSourceManager();
- StringBuilder builder;
- builder << builtinModuleInfo.name << ".slang-module";
-
+ // 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();
- // Write into the file system
- SLANG_RETURN_ON_FAIL(
- fileSystem->saveFile(builder.getBuffer(), contents.getBuffer(), contents.getCount()));
+ // 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));
- // Now need to convert into a blob
- SLANG_RETURN_ON_FAIL(archiveFileSystem->storeArchive(true, outBlob));
return SLANG_OK;
}
@@ -654,74 +700,98 @@ SlangResult Session::_readBuiltinModule(
SLANG_RETURN_ON_FAIL(RiffUtil::read(&stream, riffContainer));
}
- // Load up the module
+ Linkage* linkage = getBuiltinLinkage();
+ SourceManager* sourceManager = getBuiltinSourceManager();
+ NamePool* sessionNamePool = &namePool;
- SerialContainerData containerData;
+ auto moduleChunk = ModuleChunkRef::find(&riffContainer);
+ if (!moduleChunk)
+ return SLANG_FAIL;
- Linkage* linkage = getBuiltinLinkage();
+ SHA1::Digest moduleDigest = moduleChunk.getDigest();
- SourceManager* sourceManger = getBuiltinSourceManager();
+ auto irChunk = moduleChunk.findIR();
+ if (!irChunk)
+ return SLANG_FAIL;
- NamePool* sessionNamePool = &namePool;
- NamePool* linkageNamePool = linkage->getNamePool();
+ auto astChunk = moduleChunk.findAST();
+ if (!astChunk)
+ return SLANG_FAIL;
- SerialContainerUtil::ReadOptions options;
- options.namePool = linkageNamePool;
- options.session = this;
- options.sharedASTBuilder = linkage->getASTBuilder()->getSharedASTBuilder();
- options.astBuilder = linkage->getASTBuilder();
- options.sourceManager = sourceManger;
- options.linkage = linkage;
+ // 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 = findDebugChunk(moduleChunk.ptr()))
+ {
+ SLANG_RETURN_ON_FAIL(
+ readSourceLocationsFromDebugChunk(debugChunk, sourceManager, sourceLocReader));
+ }
- // Hmm - don't have a suitable sink yet, so attempt to just not have one
- options.sink = nullptr;
+ // 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);
- SLANG_RETURN_ON_FAIL(
- SerialContainerUtil::read(&riffContainer, options, nullptr, containerData));
- for (auto& srcModule : containerData.modules)
+ // Next, we set about deserializing the AST representation
+ // of the module.
+ //
+ auto moduleDecl = readSerializedModuleAST(
+ linkage,
+ astBuilder,
+ nullptr, // no sink
+ astChunk,
+ sourceLocReader,
+ SourceLoc());
+ if (!moduleDecl)
{
- RefPtr<Module> module(new Module(linkage, srcModule.astBuilder));
- module->setName(moduleName);
- module->setDigest(srcModule.digest);
-
- ModuleDecl* moduleDecl = as<ModuleDecl>(srcModule.astRootNode);
- // Set the module back reference on the decl
- moduleDecl->module = module;
+ return SLANG_FAIL;
+ }
+ moduleDecl->module = module;
+ module->setModuleDecl(moduleDecl);
- if (moduleDecl)
- {
- if (isFromCoreModule(moduleDecl))
- {
- registerBuiltinDecls(this, moduleDecl);
- }
+ if (isFromCoreModule(moduleDecl))
+ {
+ registerBuiltinDecls(this, moduleDecl);
+ }
- 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(decodeModuleIR(irModule, irChunk, this, sourceLocReader));
- srcModule.irModule->setName(module->getNameObj());
- module->setIRModule(srcModule.irModule);
+ irModule->setName(module->getNameObj());
+ module->setIRModule(irModule);
- // Put in the loaded module map
- linkage->mapNameToLoadedModules.add(sessionNamePool->getName(moduleName), module);
+ // 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();
+ // 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;
}
@@ -1526,9 +1596,10 @@ slang::IModule* Linkage::loadModuleFromBlob(
pathInfo = PathInfo::makeNormal(pathStr, cannonicalPath);
}
}
- auto module = loadModule(name, pathInfo, source, SourceLoc(), &sink, nullptr, blobType);
+ RefPtr<Module> module =
+ loadModuleImpl(name, pathInfo, source, SourceLoc(), &sink, nullptr, blobType);
sink.getBlobIfNeeded(outDiagnostics);
- return asExternal(module);
+ return asExternal(module.detach());
}
catch (const AbortCompilationException& e)
{
@@ -4057,101 +4128,157 @@ void Linkage::loadParsedModule(
loadedModulesList.add(loadedModule);
}
-RefPtr<Module> Linkage::loadDeserializedModule(
- Name* name,
- const PathInfo& filePathInfo,
- SerialContainerData::Module& moduleEntry,
+RefPtr<Module> Linkage::findOrLoadSerializedModuleForModuleLibrary(
+ ModuleChunkRef moduleChunk,
DiagnosticSink* sink)
{
- SLANG_AST_BUILDER_RAII(m_astBuilder);
RefPtr<Module> resultModule;
- if (mapNameToLoadedModules.tryGetValue(name, resultModule))
- return resultModule;
- if (mapPathToLoadedModule.tryGetValue(filePathInfo.getMostUniqueIdentity(), 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;
- resultModule = new Module(this, m_astBuilder);
- prepareDeserializedModule(moduleEntry, filePathInfo, resultModule, sink);
+ // 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 fileDependenciesChunk = moduleChunk.getFileDependencies();
+ auto firstFileDependencyChunk = fileDependenciesChunk.getFirst();
+ if (!firstFileDependencyChunk)
+ return nullptr;
+
+ auto modulePathInfo = PathInfo::makePath(firstFileDependencyChunk.getValue());
+ if (mapPathToLoadedModule.tryGetValue(modulePathInfo.getMostUniqueIdentity(), resultModule))
+ return resultModule;
- loadedModulesList.add(resultModule);
- mapPathToLoadedModule.add(filePathInfo.getMostUniqueIdentity(), resultModule);
- mapNameToLoadedModules.add(name, 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, moduleChunk, SourceLoc(), sink);
}
-RefPtr<Module> Linkage::loadModuleFromIRBlobImpl(
- Name* name,
- const PathInfo& filePathInfo,
- ISlangBlob* fileContentsBlob,
- SourceLoc const& loc,
- DiagnosticSink* sink,
- const LoadedModuleDictionary* additionalLoadedModules)
+RefPtr<Module> Linkage::loadSerializedModule(
+ Name* moduleName,
+ const PathInfo& moduleFilePathInfo,
+ ModuleChunkRef moduleChunk,
+ SourceLoc const& requestingLoc,
+ DiagnosticSink* sink)
{
- SLANG_AST_BUILDER_RAII(m_astBuilder);
+ auto astBuilder = getASTBuilder();
+ SLANG_AST_BUILDER_RAII(astBuilder);
- RefPtr<Module> resultModule = new Module(this, getASTBuilder());
- resultModule->setName(name);
- ModuleBeingImportedRAII moduleBeingImported(this, resultModule, name, loc);
+ auto module = RefPtr(new Module(this, astBuilder));
+ module->setName(moduleName);
- String mostUniqueIdentity = filePathInfo.getMostUniqueIdentity();
- SLANG_ASSERT(mostUniqueIdentity.getLength() > 0);
+ // 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);
- RiffContainer container;
- MemoryStreamBase readStream(
- FileAccess::Read,
- fileContentsBlob->getBufferPointer(),
- fileContentsBlob->getBufferSize());
- SLANG_RETURN_NULL_ON_FAIL(RiffUtil::read(&readStream, container));
+ // 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);
- if (m_optionSet.getBoolOption(CompilerOptionName::UseUpToDateBinaryModule))
+ mapPathToLoadedModule.add(mostUniqueIdentity, module);
+ mapNameToLoadedModules.add(moduleName, module);
+ try
{
- if (!isBinaryModuleUpToDate(filePathInfo.foundPath, &container))
+ if (SLANG_FAILED(
+ loadSerializedModuleContents(module, moduleFilePathInfo, moduleChunk, sink)))
+ {
+ mapPathToLoadedModule.remove(mostUniqueIdentity);
+ mapNameToLoadedModules.remove(moduleName);
return nullptr;
- }
+ }
- mapPathToLoadedModule.add(mostUniqueIdentity, resultModule);
- mapNameToLoadedModules.add(name, resultModule);
-
- 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.modulePath = filePathInfo.foundPath;
- SerialContainerData containerData;
- if (SLANG_FAILED(SerialContainerUtil::read(
- &container,
- readOptions,
- additionalLoadedModules,
- containerData)) ||
- containerData.modules.getCount() != 1)
+ loadedModulesList.add(module);
+ return module;
+ }
+ catch (...)
{
mapPathToLoadedModule.remove(mostUniqueIdentity);
- mapNameToLoadedModules.remove(name);
- return nullptr;
+ mapNameToLoadedModules.remove(moduleName);
+ throw;
}
- auto moduleEntry = containerData.modules.getFirst();
+}
- prepareDeserializedModule(moduleEntry, filePathInfo, resultModule, sink);
+RefPtr<Module> Linkage::loadBinaryModuleImpl(
+ Name* moduleName,
+ const PathInfo& moduleFilePathInfo,
+ ISlangBlob* moduleFileContents,
+ SourceLoc const& requestingLoc,
+ DiagnosticSink* sink)
+{
+ auto astBuilder = getASTBuilder();
+ SLANG_AST_BUILDER_RAII(astBuilder);
- loadedModulesList.add(resultModule);
- resultModule->setPathInfo(filePathInfo);
- resultModule->getIRModule()->setName(resultModule->getNameObj());
+ // We start by reading the content of the file into
+ // an in-memory RIFF container.
+ //
+ // TODO(tfoley): this is an unnecessary copy step, since
+ // we can simply use the contents of the blob directly
+ // and navigate it in-memory.
+ //
+ RiffContainer riffContainer;
+ {
+ MemoryStreamBase readStream(
+ FileAccess::Read,
+ moduleFileContents->getBufferPointer(),
+ moduleFileContents->getBufferSize());
+ SLANG_RETURN_NULL_ON_FAIL(RiffUtil::read(&readStream, riffContainer));
+ }
- return resultModule;
-}
+ auto moduleChunkRef = ModuleChunkRef::find(&riffContainer);
+ if (!moduleChunkRef)
+ {
+ return nullptr;
+ }
-Module* Linkage::loadModule(String const& name)
-{
- // TODO: We either need to have a diagnostics sink
- // get passed into this operation, or associate
- // one with the linkage.
+ // 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).
//
- DiagnosticSink* sink = nullptr;
- return findOrImportModule(getNamePool()->getName(name), SourceLoc(), sink);
+ String mostUniqueIdentity = moduleFilePathInfo.getMostUniqueIdentity();
+ SLANG_ASSERT(mostUniqueIdentity.getLength() > 0);
+ if (m_optionSet.getBoolOption(CompilerOptionName::UseUpToDateBinaryModule))
+ {
+ if (!isBinaryModuleUpToDate(moduleFilePathInfo.foundPath, moduleChunkRef))
+ {
+ 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, moduleChunkRef, requestingLoc, sink);
+
+ return module;
}
void Linkage::_diagnoseErrorInImportedModule(DiagnosticSink* sink)
@@ -4166,24 +4293,43 @@ void Linkage::_diagnoseErrorInImportedModule(DiagnosticSink* sink)
}
}
-RefPtr<Module> Linkage::loadModule(
- Name* name,
- const PathInfo& filePathInfo,
- ISlangBlob* sourceBlob,
- SourceLoc const& srcLoc,
+RefPtr<Module> Linkage::loadModuleImpl(
+ Name* moduleName,
+ const PathInfo& modulePathInfo,
+ ISlangBlob* moduleBlob,
+ SourceLoc const& requestingLoc,
DiagnosticSink* sink,
const LoadedModuleDictionary* additionalLoadedModules,
ModuleBlobType blobType)
{
- if (blobType == ModuleBlobType::IR)
- return loadModuleFromIRBlobImpl(
- name,
- filePathInfo,
- sourceBlob,
- srcLoc,
+ 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;
@@ -4275,8 +4421,10 @@ RefPtr<Module> Linkage::loadModule(
return nullptr;
}
- if (module)
- module->setPathInfo(filePathInfo);
+ if (!module)
+ return nullptr;
+
+ module->setPathInfo(filePathInfo);
return module;
}
@@ -4319,126 +4467,263 @@ String getFileNameFromModuleName(Name* name, bool translateUnderScore)
}
RefPtr<Module> Linkage::findOrImportModule(
- Name* name,
- SourceLoc const& loc,
+ Name* moduleName,
+ SourceLoc const& requestingLoc,
DiagnosticSink* sink,
const LoadedModuleDictionary* loadedModules)
{
// Have we already loaded a module matching this name?
//
- RefPtr<LoadedModule> loadedModule;
- if (mapNameToLoadedModules.tryGetValue(name, loadedModule))
+ 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 (!loadedModule)
+ 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(loadedModule))
+ if (isBeingImported(previouslyLoadedModule))
{
// We seem to be in the middle of loading this module
- sink->diagnose(loc, Diagnostics::recursiveModuleImport, name);
+ sink->diagnose(requestingLoc, Diagnostics::recursiveModuleImport, moduleName);
return nullptr;
}
- return loadedModule;
+ 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* previouslyLoadedModule = nullptr;
- if (loadedModules && loadedModules->tryGetValue(name, previouslyLoadedModule))
{
- return previouslyLoadedModule;
+ Module* previouslyLoadedLocalModule = nullptr;
+ if (loadedModules && loadedModules->tryGetValue(moduleName, previouslyLoadedLocalModule))
+ {
+ return previouslyLoadedLocalModule;
+ }
}
- if (name == getSessionImpl()->glslModuleName)
+ // 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)
{
- sink->diagnose(loc, Diagnostics::glslModuleNotAvailable, name);
+ // 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;
}
- // Next, try to find the file of the given name,
- // using our ordinary include-handling logic.
+ // 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.
- IncludeSystem includeSystem(&getSearchDirectories(), getFileSystemExt(), getSourceManager());
+ 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);
+ }
- // Get the original path info
- PathInfo pathIncludedFromInfo = getSourceManager()->getPathInfo(loc, SourceLocType::Actual);
- PathInfo filePathInfo;
+ // 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());
- // Look for a precompiled module first, if not exist, load from source.
- bool shouldCheckBinaryModuleSettings[2] = {true, false};
+ // 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 checkBinaryModule : shouldCheckBinaryModuleSettings)
+ for (auto type : typesToTry)
{
- // When in language server, we always prefer to use source module if it is available.
- if (isInLanguageServer())
- checkBinaryModule = !checkBinaryModule;
-
- // Try without translating `_` to `-` first, if that fails, try translating.
- for (int translateUnderScore = 0; translateUnderScore <= 1; translateUnderScore++)
+ for (auto sourceFileName : sourceFileNamesToTry)
{
- auto moduleSourceFileName = getFileNameFromModuleName(name, translateUnderScore == 1);
+ // 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;
- if (checkBinaryModule == 1)
- fileName = Path::replaceExt(moduleSourceFileName, "slang-module");
- else
- fileName = moduleSourceFileName;
+ switch (type)
+ {
+ case ModuleBlobType::Source:
+ fileName = sourceFileName;
+ break;
- ComPtr<ISlangBlob> fileContents;
+ case ModuleBlobType::IR:
+ fileName = Path::replaceExt(sourceFileName, "slang-module");
+ break;
+ }
- // We have to load via the found path - as that is how file was originally loaded
+ // 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, pathIncludedFromInfo.foundPath, filePathInfo)))
+ 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;
}
- // Maybe this was loaded previously at a different relative name?
+ // 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(),
- loadedModule))
- return loadedModule;
+ 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;
+ }
- // Try to load it
- if (!fileContents && SLANG_FAILED(includeSystem.loadFile(filePathInfo, fileContents)))
+ // 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;
}
- // We've found a file that we can load for the given module, so
- // go ahead and perform the module-load action
- auto resultModule = loadModule(
- name,
+ // 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,
- loc,
+ requestingLoc,
sink,
loadedModules,
- (checkBinaryModule == 1 ? ModuleBlobType::IR : ModuleBlobType::Source));
- if (resultModule)
- return resultModule;
+ 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;
}
}
- // Error: we cannot find the file.
- sink->diagnose(loc, Diagnostics::cannotOpenFile, getFileNameFromModuleName(name, false));
- mapNameToLoadedModules[name] = nullptr;
+ // 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;
}
@@ -4454,27 +4739,19 @@ SourceFile* Linkage::loadSourceFile(String pathFrom, String path)
}
// Check if a serialized module is up-to-date with current compiler options and source files.
-bool Linkage::isBinaryModuleUpToDate(String fromPath, RiffContainer* container)
+bool Linkage::isBinaryModuleUpToDate(String fromPath, RiffContainer* riffContainer)
{
- 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)))
+ auto moduleChunk = ModuleChunkRef::find(riffContainer);
+ if (!moduleChunk)
return false;
- if (containerData.modules.getCount() != 1)
- return false;
+ return isBinaryModuleUpToDate(fromPath, moduleChunk);
+}
+
+bool Linkage::isBinaryModuleUpToDate(String fromPath, ModuleChunkRef moduleChunk)
+{
+ SHA1::Digest existingDigest = moduleChunk.getDigest();
- auto& moduleHeader = containerData.modules[0];
DigestBuilder<SHA1> digestBuilder;
auto version = String(getBuildTagString());
digestBuilder.append(version);
@@ -4482,9 +4759,12 @@ bool Linkage::isBinaryModuleUpToDate(String fromPath, RiffContainer* container)
// Find the canonical path of the directory containing the module source file.
String moduleSrcPath = "";
- if (moduleHeader.dependentFiles.getCount())
+
+ auto dependencyChunks = moduleChunk.getFileDependencies();
+ if (auto firstDependencyChunk = dependencyChunks.getFirst())
{
- moduleSrcPath = moduleHeader.dependentFiles.getFirst();
+ moduleSrcPath = firstDependencyChunk.getValue();
+
IncludeSystem includeSystem(
&getSearchDirectories(),
getFileSystemExt(),
@@ -4497,21 +4777,22 @@ bool Linkage::isBinaryModuleUpToDate(String fromPath, RiffContainer* container)
}
}
- for (auto file : moduleHeader.dependentFiles)
+ 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 (moduleHeader.dependentFiles.getCount() != 0)
+ if (dependencyChunks.getFirst())
sourceFile = loadSourceFile(moduleSrcPath, file);
}
if (!sourceFile)
return false;
digestBuilder.append(sourceFile->getDigest());
}
- return digestBuilder.finalize() == moduleHeader.digest;
+ return digestBuilder.finalize() == existingDigest;
}
SLANG_NO_THROW bool SLANG_MCALL
@@ -6243,20 +6524,100 @@ void Linkage::setFileSystem(ISlangFileSystem* inFileSystem)
getSourceManager()->setFileSystemExt(m_fileSystemExt);
}
-void Linkage::prepareDeserializedModule(
- SerialContainerData::Module& moduleEntry,
- const PathInfo& filePathInfo,
+SlangResult Linkage::loadSerializedModuleContents(
Module* module,
+ const PathInfo& moduleFilePathInfo,
+ ModuleChunkRef moduleChunk,
DiagnosticSink* sink)
{
- module->setIRModule(moduleEntry.irModule);
- module->setModuleDecl(as<ModuleDecl>(moduleEntry.astRootNode));
+ // 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`.
+
+ auto sourceManager = getSourceManager();
+ RefPtr<SerialSourceLocReader> sourceLocReader;
+ if (auto debugChunk = findDebugChunk(moduleChunk.ptr()))
+ {
+ 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,
+ astChunk,
+ sourceLocReader,
+ serializedModuleLoc);
+ if (!moduleDecl)
+ return SLANG_FAIL;
+ module->setModuleDecl(moduleDecl);
+
+ RefPtr<IRModule> irModule;
+ SLANG_RETURN_ON_FAIL(decodeModuleIR(irModule, irChunk, session, sourceLocReader));
+ 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 = filePathInfo.foundPath;
+ String moduleSourcePath = moduleFilePathInfo.foundPath;
bool isFirst = true;
- for (auto file : moduleEntry.dependentFiles)
+ for (auto depenencyFileChunk : moduleChunk.getFileDependencies())
{
- auto sourceFile = loadSourceFile(filePathInfo.foundPath, file);
+ auto encodedDependencyFilePath = depenencyFileChunk.getValue();
+
+ auto sourceFile = loadSourceFile(moduleFilePathInfo.foundPath, encodedDependencyFilePath);
if (isFirst)
{
// The first file is the source for the main module file.
@@ -6270,20 +6631,19 @@ void Linkage::prepareDeserializedModule(
// it relative to the module source path.
if (!sourceFile)
{
- sourceFile = loadSourceFile(moduleSourcePath, file);
+ sourceFile = loadSourceFile(moduleSourcePath, encodedDependencyFilePath);
}
if (sourceFile)
{
module->addFileDependency(sourceFile);
}
}
- module->setPathInfo(filePathInfo);
- module->setDigest(moduleEntry.digest);
+ module->setPathInfo(moduleFilePathInfo);
+ module->setDigest(moduleChunk.getDigest());
module->_collectShaderParams();
module->_discoverEntryPoints(sink, targets);
// Hook up fileDecl's scope to module's scope.
- auto moduleDecl = module->getModuleDecl();
for (auto globalDecl : moduleDecl->members)
{
if (auto fileDecl = as<FileDecl>(globalDecl))
@@ -6291,6 +6651,8 @@ void Linkage::prepareDeserializedModule(
addSiblingScopeForContainerDecl(m_astBuilder, moduleDecl->ownedScope, fileDecl);
}
}
+
+ return SLANG_OK;
}
void Linkage::setRequireCacheFileSystem(bool requireCacheFileSystem)