summaryrefslogtreecommitdiff
path: root/source/slang/slang-serialize-container.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'source/slang/slang-serialize-container.cpp')
-rw-r--r--source/slang/slang-serialize-container.cpp1135
1 files changed, 436 insertions, 699 deletions
diff --git a/source/slang/slang-serialize-container.cpp b/source/slang/slang-serialize-container.cpp
index f82357459..c2253ed45 100644
--- a/source/slang/slang-serialize-container.cpp
+++ b/source/slang/slang-serialize-container.cpp
@@ -10,89 +10,239 @@
#include "slang-mangled-lexer.h"
#include "slang-parser.h"
#include "slang-serialize-ast.h"
-#include "slang-serialize-factory.h"
#include "slang-serialize-ir.h"
#include "slang-serialize-source-loc.h"
namespace Slang
{
-
-/* static */ SlangResult SerialContainerUtil::write(
- Module* module,
- const WriteOptions& options,
- Stream* stream)
+struct ModuleEncodingContext
{
- RiffContainer container;
+public:
+ ModuleEncodingContext(SerialContainerUtil::WriteOptions const& options, Stream* stream)
+ : options(options), encoder(stream), containerStringPool(StringSlicePool::Style::Default)
{
- SerialContainerData data;
- SLANG_RETURN_ON_FAIL(SerialContainerUtil::addModuleToData(module, options, data));
- SLANG_RETURN_ON_FAIL(SerialContainerUtil::write(data, options, &container));
+ if (options.optionFlags & SerialOptionFlag::SourceLocation)
+ {
+ sourceLocWriter = new SerialSourceLocWriter(options.sourceManager);
+ }
}
- // We now write the RiffContainer to the stream
- SLANG_RETURN_ON_FAIL(RiffUtil::write(container.getRoot(), true, stream));
- return SLANG_OK;
-}
-/* static */ SlangResult SerialContainerUtil::write(
- FrontEndCompileRequest* frontEndReq,
- const WriteOptions& options,
- Stream* stream)
-{
- RiffContainer container;
+ ~ModuleEncodingContext()
+ {
+ encoder.setRIFFChunk(encoder.getRIFF()->getRoot());
+ encodeFinalPieces();
+ }
+
+ SlangResult encodeModuleList(FrontEndCompileRequest* frontEndReq)
+ {
+ // Encoding a front-end compile request into a RIFF
+ // is simply a matter of encoding the module for each
+ // of the translation units that got compiled.
+ //
+ Encoder::WithKeyValuePair withArray(&encoder, SerialBinary::kModuleListFourCc);
+ for (TranslationUnitRequest* translationUnit : frontEndReq->translationUnits)
+ {
+ SLANG_RETURN_ON_FAIL(encode(translationUnit->module));
+ }
+ return SLANG_OK;
+ }
+
+ SlangResult encode(FrontEndCompileRequest* frontEndReq)
+ {
+ Encoder::WithObject withObject(&encoder, SerialBinary::kContainerFourCc);
+ SLANG_RETURN_ON_FAIL(encodeModuleList(frontEndReq));
+ return SLANG_OK;
+ }
+
+ SlangResult encode(EndToEndCompileRequest* request)
+ {
+ Encoder::WithObject withObject(&encoder, SerialBinary::kContainerFourCc);
+
+ // Encoding an end-to-end compile request starts with the same
+ // work as for a front-end request: we encode each of
+ // the modules for the translation units.
+ //
+ SLANG_RETURN_ON_FAIL(encodeModuleList(request->getFrontEndReq()));
+ //
+ // If code generation is disabled, then we can skip all further
+ // steps, and the encoding process is no different
+ // than for a front-end request.
+ //
+ if (request->getOptionSet().getBoolOption(CompilerOptionName::SkipCodeGen))
+ {
+ return SLANG_OK;
+ }
+
+ // If code generation is enabled, then we need to encode
+ // information on each of the code generation targets, as well
+ // as the entry points.
+ //
+ // We start with the targets, each of which will have a Slang IR
+ // representation of the layout information for the program
+ // on that target.
+ //
+ auto linkage = request->getLinkage();
+ auto sink = request->getSink();
+ auto program = request->getSpecializedGlobalAndEntryPointsComponentType();
+ {
+ Encoder::WithArray withArray(&encoder); // kContainerFourCc
+
+ for (auto target : linkage->targets)
+ {
+ auto targetProgram = program->getTargetProgram(target);
+ encode(targetProgram, sink);
+ }
+ }
+
+ // The compiled `program` may also have zero or more entry points,
+ // and we need to encode information about each of them.
+ //
+ {
+ Encoder::WithArray withArray(&encoder, SerialBinary::kEntryPointListFourCc);
+
+ auto entryPointCount = program->getEntryPointCount();
+ for (Index ii = 0; ii < entryPointCount; ++ii)
+ {
+ auto entryPoint = program->getEntryPoint(ii);
+ auto entryPointMangledName = program->getEntryPointMangledName(ii);
+ encode(entryPoint, entryPointMangledName);
+ }
+ }
+
+ return SLANG_OK;
+ }
+
+ SlangResult encode(TargetProgram* targetProgram, DiagnosticSink* sink)
{
- SerialContainerData data;
+ // TODO:
+ // Serialization of target component IR is causing the embedded precompiled binary
+ // feature to fail. The resulting data modules contain both TU IR and TC IR, with only
+ // one module header. Yong suggested to ignore the TC IR for now, though also that
+ // OV was using the feature, so disabling this might cause problems.
+
+ IRModule* irModule = targetProgram->getOrCreateIRModuleForLayout(sink);
+
+ // Okay, we need to serialize this target program and its IR too...
+ IRSerialData serialData;
+ IRSerialWriter writer;
+
SLANG_RETURN_ON_FAIL(
- SerialContainerUtil::addFrontEndRequestToData(frontEndReq, options, data));
- SLANG_RETURN_ON_FAIL(SerialContainerUtil::write(data, options, &container));
+ writer.write(irModule, sourceLocWriter, options.optionFlags, &serialData));
+ SLANG_RETURN_ON_FAIL(IRSerialWriter::writeContainer(serialData, encoder.getRIFF()));
+
+ return SLANG_OK;
}
- // We now write the RiffContainer to the stream
- SLANG_RETURN_ON_FAIL(RiffUtil::write(container.getRoot(), true, stream));
- return SLANG_OK;
-}
-/* static */ SlangResult SerialContainerUtil::write(
- EndToEndCompileRequest* request,
- const WriteOptions& options,
- Stream* stream)
-{
- RiffContainer container;
+ void encode(Name* name) { encoder.encode(name->text); }
+
+ void encode(String const& value) { encoder.encode(value); }
+
+ void encode(uint32_t value) { encoder.encode(UInt(value)); }
+
+ void encodeData(void const* data, size_t size) { encoder.encodeData(data, size); }
+
+ SlangResult encode(EntryPoint* entryPoint, String const& entryPointMangledName)
{
- SerialContainerData data;
- SLANG_RETURN_ON_FAIL(SerialContainerUtil::addEndToEndRequestToData(request, options, data));
- SLANG_RETURN_ON_FAIL(SerialContainerUtil::write(data, options, &container));
+ Encoder::WithObject withObject(&encoder, SerialBinary::kEntryPointFourCc);
+
+ {
+ Encoder::WithObject withProperty(&encoder, SerialBinary::kNameFourCC);
+ encode(entryPoint->getName());
+ }
+ {
+ Encoder::WithObject withProperty(&encoder, SerialBinary::kProfileFourCC);
+ encode(entryPoint->getProfile().raw);
+ }
+ {
+ Encoder::WithObject withProperty(&encoder, SerialBinary::kMangledNameFourCC);
+ encode(entryPointMangledName);
+ }
+
+ return SLANG_OK;
}
- // We now write the RiffContainer to the stream
- SLANG_RETURN_ON_FAIL(RiffUtil::write(container.getRoot(), true, stream));
- return SLANG_OK;
-}
-/* static */ SlangResult SerialContainerUtil::addModuleToData(
- Module* module,
- const WriteOptions& options,
- SerialContainerData& outData)
-{
- if (options.optionFlags & (SerialOptionFlag::ASTModule | SerialOptionFlag::IRModule))
+
+ SlangResult encode(Module* module)
{
- SerialContainerData::Module dstModule;
+ if (!(options.optionFlags & (SerialOptionFlag::IRModule | SerialOptionFlag::ASTModule)))
+ return SLANG_OK;
- // NOTE: The astBuilder is not set here, as not needed to be scoped for serialization (it is
- // assumed the TranslationUnitRequest stays in scope)
+ Encoder::WithObject withModule(&encoder, SerialBinary::kModuleFourCC);
- if (options.optionFlags & SerialOptionFlag::ASTModule)
+ // The first piece that we write for a module is its header.
+ // The header is intended to provide information that can be
+ // used to determine if a precompiled module is up-to-date.
+ //
+ // Update(tfoley): Okay, let's skip the whole header idea and just
+ // serialize these things as properties of the module itself...
{
- // Root AST node
- auto moduleDecl = module->getModuleDecl();
- SLANG_ASSERT(moduleDecl);
+ // So many things need the module name, that it makes
+ // sense to serialize it separately from all the rest.
+ //
+ {
+ Encoder::WithObject withProperty(&encoder, SerialBinary::kNameFourCC);
+ encoder.encodeString(module->getNameObj()->text);
+ }
+
+ // The header includes a digest of all the compile options and
+ // the files that the compiled result depended on.
+ //
+ auto digest = module->computeDigest();
+ encoder.encodeData(PropertyKeys<Module>::Digest, digest.data, sizeof(digest.data));
- dstModule.astRootNode = moduleDecl;
+ // The header includes an array of the paths of all of the
+ // files that the compiled result depended on.
+ //
+ encodeModuleDependencyPaths(module);
}
- if (options.optionFlags & SerialOptionFlag::IRModule)
+
+ // If serialization of Slang IR modules is enabled, and there
+ // is IR available for this module, then we we encode it.
+ //
+ if ((options.optionFlags & SerialOptionFlag::IRModule))
{
- // IR module
- dstModule.irModule = module->getIRModule();
- SLANG_ASSERT(dstModule.irModule);
+ if (auto irModule = module->getIRModule())
+ {
+ Encoder::WithKeyValuePair withKey(&encoder, PropertyKeys<Module>::IRModule);
+
+ IRSerialData serialData;
+ IRSerialWriter writer;
+ SLANG_RETURN_ON_FAIL(
+ writer.write(irModule, sourceLocWriter, options.optionFlags, &serialData));
+ SLANG_RETURN_ON_FAIL(IRSerialWriter::writeContainer(serialData, encoder.getRIFF()));
+ }
}
+ // If serialization of AST information is enabled, and we have AST
+ // information available, then we serialize it here.
+ //
+ if (options.optionFlags & SerialOptionFlag::ASTModule)
+ {
+ if (auto moduleDecl = module->getModuleDecl())
+ {
+ Encoder::WithKeyValuePair withKey(&encoder, PropertyKeys<Module>::ASTModule);
+
+ writeSerializedModuleAST(&encoder, moduleDecl, sourceLocWriter);
+ }
+ }
+
+ return SLANG_OK;
+ }
+
+ SlangResult encodeModuleDependencyPaths(Module* module)
+ {
+ Encoder::WithObject withProperty(&encoder, PropertyKeys<Module>::FileDependencies);
+
+ // TODO(tfoley): This is some of the most complicated logic
+ // in the encoding system, because it tries to translate
+ // the file dependency paths into something that isn't
+ // specific to the machine on which a module was built.
+ //
+ // The comments that follow are from the original implementation
+ // of this logic, because I cannot state with confidence
+ // that I know what's happening in all of this.
+
+
// Here we assume that the first file in the file dependencies is the module's file path.
// We store the module's file path as a relative path with respect to the first search
// directory that contains the module, and store the paths of dependent files as relative
@@ -155,6 +305,7 @@ namespace Slang
}
Path::getCanonical(linkageRoot, linkageRoot);
+ Encoder::WithArray withArray(&encoder);
for (auto file : fileDependencies)
{
if (file->getPathInfo().hasFoundPath())
@@ -170,728 +321,314 @@ namespace Slang
{
auto relativeModulePath =
Path::getRelativePath(linkageRoot, canonicalModulePath);
- dstModule.dependentFiles.add(relativeModulePath);
+
+ encoder.encodeString(relativeModulePath);
}
else
{
// For all other dependnet files, store them as relative paths with respect
// to the module's path.
canonicalFilePath = Path::getRelativePath(moduleDir, canonicalFilePath);
- dstModule.dependentFiles.add(canonicalFilePath);
+ encoder.encodeString(canonicalFilePath);
}
}
else
{
// If the module is coming from string instead of an actual file, store it as
// is.
- dstModule.dependentFiles.add(canonicalModulePath);
+ encoder.encodeString(canonicalModulePath);
}
}
else
{
- dstModule.dependentFiles.add(file->getPathInfo().getMostUniqueIdentity());
+ encoder.encodeString(file->getPathInfo().getMostUniqueIdentity());
}
}
- dstModule.digest = module->computeDigest();
- outData.modules.add(dstModule);
- }
- return SLANG_OK;
-}
-
-/* static */ SlangResult SerialContainerUtil::addFrontEndRequestToData(
- FrontEndCompileRequest* frontEndReq,
- const WriteOptions& options,
- SerialContainerData& outData)
-{
- // Go through translation units, adding modules
- for (TranslationUnitRequest* translationUnit : frontEndReq->translationUnits)
- {
- SLANG_RETURN_ON_FAIL(addModuleToData(translationUnit->module, options, outData));
- }
-
- return SLANG_OK;
-}
-
-/* static */ SlangResult SerialContainerUtil::addEndToEndRequestToData(
- EndToEndCompileRequest* request,
- const WriteOptions& options,
- SerialContainerData& out)
-{
- auto linkage = request->getLinkage();
- auto sink = request->getSink();
-
- // Output the parsed modules.
- addFrontEndRequestToData(request->getFrontEndReq(), options, out);
-
- // If we are skipping code generation, then we are done.
- if (request->getOptionSet().getBoolOption(CompilerOptionName::SkipCodeGen))
- {
return SLANG_OK;
}
- //
- auto program = request->getSpecializedGlobalAndEntryPointsComponentType();
- // Add all the target modules
+ SlangResult encodeFinalPieces()
{
- for (auto target : linkage->targets)
+ // We can now output the debug information. This is for all IR and AST
+ if (sourceLocWriter)
{
- auto targetProgram = program->getTargetProgram(target);
- auto irModule = targetProgram->getOrCreateIRModuleForLayout(sink);
-
- SerialContainerData::TargetComponent targetComponent;
+ // Write out the debug info
+ SerialSourceLocData debugData;
+ sourceLocWriter->write(&debugData);
- targetComponent.irModule = irModule;
-
- auto& dstTarget = targetComponent.target;
-
- dstTarget.floatingPointMode = target->getOptionSet().getFloatingPointMode();
- dstTarget.profile = target->getOptionSet().getProfile();
- dstTarget.flags = target->getOptionSet().getTargetFlags();
- dstTarget.codeGenTarget = target->getTarget();
-
- out.targetComponents.add(targetComponent);
+ debugData.writeContainer(encoder.getRIFF());
}
- }
- // Entry points
- {
- auto entryPointCount = program->getEntryPointCount();
- for (Index ii = 0; ii < entryPointCount; ++ii)
+ // Write the container string table
+ if (containerStringPool.getAdded().getCount() > 0)
{
- auto entryPoint = program->getEntryPoint(ii);
- auto entryPointMangledName = program->getEntryPointMangledName(ii);
-
- SerialContainerData::EntryPoint dstEntryPoint;
+ Encoder::WithKeyValuePair withKey(&encoder, SerialBinary::kStringTableFourCc);
- dstEntryPoint.name = entryPoint->getName();
- dstEntryPoint.mangledName = entryPointMangledName;
- dstEntryPoint.profile = entryPoint->getProfile();
+ List<char> encodedTable;
+ SerialStringTableUtil::encodeStringTable(containerStringPool, encodedTable);
- out.entryPoints.add(dstEntryPoint);
+ encoder.encodeData(encodedTable.getBuffer(), encodedTable.getCount());
}
+
+ return SLANG_OK;
}
- return SLANG_OK;
-}
-/* static */ SlangResult SerialContainerUtil::write(
- const SerialContainerData& data,
- const WriteOptions& options,
- RiffContainer* container)
-{
+private:
+ SerialContainerUtil::WriteOptions const& options;
RefPtr<SerialSourceLocWriter> sourceLocWriter;
// The string pool used across the whole of the container
- StringSlicePool containerStringPool(StringSlicePool::Style::Default);
+ StringSlicePool containerStringPool;
- RiffContainer::ScopeChunk scopeModule(
- container,
- RiffContainer::Chunk::Kind::List,
- SerialBinary::kContainerFourCc);
+ Encoder encoder;
+};
- if (data.modules.getCount() &&
- (options.optionFlags & (SerialOptionFlag::IRModule | SerialOptionFlag::ASTModule)))
- {
- // Module list
- RiffContainer::ScopeChunk moduleListScope(
- container,
- RiffContainer::Chunk::Kind::List,
- SerialBinary::kModuleListFourCc);
-
- if (options.optionFlags & SerialOptionFlag::SourceLocation)
- {
- sourceLocWriter = new SerialSourceLocWriter(options.sourceManager);
- }
-
- RefPtr<SerialClasses> serialClasses;
-
- for (const auto& module : data.modules)
- {
- // 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.
+//
+// To serialize a module (or compile request) to a stream, we first
+// construct a RIFF container from it, and then serialize that
+// container out to a byte stream.
+//
- // 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)
- {
- IRSerialData serialData;
- IRSerialWriter writer;
- SLANG_RETURN_ON_FAIL(writer.write(
- module.irModule,
- sourceLocWriter,
- options.optionFlags,
- &serialData));
- SLANG_RETURN_ON_FAIL(IRSerialWriter::writeContainer(serialData, container));
- }
-
- // Write the AST information
-
- if (options.optionFlags & SerialOptionFlag::ASTModule)
- {
- if (ModuleDecl* moduleDecl = as<ModuleDecl>(module.astRootNode))
- {
- // Put in AST module
- RiffContainer::ScopeChunk scopeASTModule(
- container,
- RiffContainer::Chunk::Kind::List,
- ASTSerialBinary::kSlangASTModuleFourCC);
-
- if (!serialClasses)
- {
- SLANG_RETURN_ON_FAIL(SerialClassesUtil::create(serialClasses));
- }
-
- ModuleSerialFilter filter(moduleDecl);
- auto astWriterFlag = SerialWriter::Flag::ZeroInitialize;
- if ((options.optionFlags & SerialOptionFlag::ASTFunctionBody) == 0)
- astWriterFlag = (SerialWriter::Flag::Enum)(
- astWriterFlag | SerialWriter::Flag::SkipFunctionBody);
-
- SerialWriter writer(serialClasses, &filter, astWriterFlag);
-
- writer.getExtraObjects().set(sourceLocWriter);
-
- // Add the module and everything that isn't filtered out in the filter.
- writer.addPointer(moduleDecl);
+/* static */ SlangResult SerialContainerUtil::write(
+ Module* module,
+ const WriteOptions& options,
+ Stream* stream)
+{
+ ModuleEncodingContext context(options, stream);
+ SLANG_RETURN_ON_FAIL(context.encode(module));
+ return SLANG_OK;
+}
+/* static */ SlangResult SerialContainerUtil::write(
+ FrontEndCompileRequest* request,
+ const WriteOptions& options,
+ Stream* stream)
+{
+ ModuleEncodingContext context(options, stream);
+ SLANG_RETURN_ON_FAIL(context.encode(request));
+ return SLANG_OK;
+}
- // We can now serialize it into the riff container.
- SLANG_RETURN_ON_FAIL(writer.writeIntoContainer(
- ASTSerialBinary::kSlangASTModuleDataFourCC,
- container));
- }
- }
- }
+/* static */ SlangResult SerialContainerUtil::write(
+ EndToEndCompileRequest* request,
+ const WriteOptions& options,
+ Stream* stream)
+{
+ ModuleEncodingContext context(options, stream);
+ SLANG_RETURN_ON_FAIL(context.encode(request));
+ return SLANG_OK;
+}
- // TODO:
- // Serialization of target component IR is causing the embedded precompiled binary
- // feature to fail. The resulting data modules contain both TU IR and TC IR, with only
- // one module header. Yong suggested to ignore the TC IR for now, though also that
- // OV was using the feature, so disabling this might cause problems.
-#if 0
- if (data.targetComponents.getCount() && (options.optionFlags & SerialOptionFlag::IRModule))
- {
- // TODO: in the case where we have specialization, we might need
- // to serialize IR related to `program`...
+String StringChunkRef::getValue()
+{
+ return Decoder(ptr()).decodeString();
+}
- for (const auto& targetComponent : data.targetComponents)
- {
- IRModule* irModule = targetComponent.irModule;
+ChunkRefList<StringChunkRef> ModuleChunkRef::getFileDependencies()
+{
+ Decoder decoder(ptr());
+ Decoder::WithProperty withProperty(decoder, PropertyKeys<Module>::FileDependencies);
+ return ChunkRefList<StringChunkRef>(as<RiffContainer::ListChunk>(decoder.getCursor()));
+}
- // Okay, we need to serialize this target program and its IR too...
- IRSerialData serialData;
- IRSerialWriter writer;
+ModuleChunkRef ModuleChunkRef::find(RiffContainer* container)
+{
+ auto found = container->getRoot()->findListRec(SerialBinary::kModuleFourCC);
+ return ModuleChunkRef(found);
+}
- SLANG_RETURN_ON_FAIL(writer.write(irModule, sourceLocWriter, options.optionFlags, &serialData));
- SLANG_RETURN_ON_FAIL(IRSerialWriter::writeContainer(serialData, options.compressionType, container));
- }
- }
-#endif
+SHA1::Digest ModuleChunkRef::getDigest()
+{
+ auto foundChunk =
+ static_cast<RiffContainer::DataChunk*>(ptr()->findContained(PropertyKeys<Module>::Digest));
+ if (!foundChunk)
+ {
+ SLANG_UNEXPECTED("module chunk had no digest");
}
-
- if (data.entryPoints.getCount())
+ if (foundChunk->calcPayloadSize() != sizeof(SHA1::Digest))
{
- for (const auto& entryPoint : data.entryPoints)
- {
- RiffContainer::ScopeChunk entryPointScope(
- container,
- RiffContainer::Chunk::Kind::Data,
- SerialBinary::kEntryPointFourCc);
-
- SerialContainerBinary::EntryPoint dst;
-
- dst.name = uint32_t(containerStringPool.add(entryPoint.name->text));
- dst.profile = entryPoint.profile.raw;
- dst.mangledName = uint32_t(containerStringPool.add(entryPoint.mangledName));
-
- container->write(&dst, sizeof(dst));
- }
+ SLANG_UNEXPECTED("module digest chunk had wrong size");
}
- // We can now output the debug information. This is for all IR and AST
- if (sourceLocWriter)
- {
- // Write out the debug info
- SerialSourceLocData debugData;
- sourceLocWriter->write(&debugData);
+ SHA1::Digest digest;
+ foundChunk->getPayload(&digest);
+ return digest;
+}
- debugData.writeContainer(container);
- }
+String ModuleChunkRef::getName()
+{
+ // TODO(tfoley): This kind of logic needs a way
+ // to be greatly simplified, so that we don't
+ // have to express such complicated logic for
+ // simply extracting a single string property...
+ //
+ Decoder decoder(ptr());
+ Decoder::WithProperty withProperty(decoder, SerialBinary::kNameFourCC);
+ return decoder.decodeString();
+}
- // Write the container string table
- if (containerStringPool.getAdded().getCount() > 0)
- {
- RiffContainer::ScopeChunk stringTableScope(
- container,
- RiffContainer::Chunk::Kind::Data,
- SerialBinary::kStringTableFourCc);
- List<char> encodedTable;
- SerialStringTableUtil::encodeStringTable(containerStringPool, encodedTable);
+IRModuleChunkRef ModuleChunkRef::findIR()
+{
+ auto foundProperty = ptr()->findContainedList(PropertyKeys<Module>::IRModule);
+ if (!foundProperty)
+ return IRModuleChunkRef(nullptr);
+ return IRModuleChunkRef(
+ static_cast<RiffContainer::ListChunk*>(foundProperty->getFirstContainedChunk()));
+}
- container->write(encodedTable.getBuffer(), encodedTable.getCount());
- }
+ASTModuleChunkRef ModuleChunkRef::findAST()
+{
+ auto foundProperty = ptr()->findContainedList(PropertyKeys<Module>::ASTModule);
+ if (!foundProperty)
+ return ASTModuleChunkRef(nullptr);
+ return ASTModuleChunkRef(
+ static_cast<RiffContainer::ListChunk*>(foundProperty->getFirstContainedChunk()));
+}
- return SLANG_OK;
+ContainerChunkRef ContainerChunkRef::find(RiffContainer* container)
+{
+ auto found = container->getRoot()->findListRec(SerialBinary::kContainerFourCc);
+ return ContainerChunkRef(found);
}
+ChunkRefList<ModuleChunkRef> ContainerChunkRef::getModules()
+{
+ auto found = ptr()->findContainedList(SerialBinary::kModuleListFourCc);
+ return ChunkRefList<ModuleChunkRef>(found);
+}
-static List<ExtensionDecl*>& _getCandidateExtensionList(
- AggTypeDecl* typeDecl,
- Dictionary<AggTypeDecl*, RefPtr<CandidateExtensionList>>& mapTypeToCandidateExtensions)
+ChunkRefList<EntryPointChunkRef> ContainerChunkRef::getEntryPoints()
{
- RefPtr<CandidateExtensionList> entry;
- if (!mapTypeToCandidateExtensions.tryGetValue(typeDecl, entry))
- {
- entry = new CandidateExtensionList();
- mapTypeToCandidateExtensions.add(typeDecl, entry);
- }
- return entry->candidateExtensions;
+ auto found = ptr()->findContainedList(SerialBinary::kEntryPointListFourCc);
+ return ChunkRefList<EntryPointChunkRef>(found);
}
-/* static */ Result SerialContainerUtil::read(
- RiffContainer* container,
- const ReadOptions& options,
- const LoadedModuleDictionary* additionalLoadedModules,
- SerialContainerData& out)
+String EntryPointChunkRef::getMangledName() const
{
- out.clear();
+ // TODO(tfoley): This kind of logic needs a way
+ // to be greatly simplified, so that we don't
+ // have to express such complicated logic for
+ // simply extracting a single string property...
+ //
+ Decoder decoder(ptr());
+ Decoder::WithProperty withProperty(decoder, SerialBinary::kMangledNameFourCC);
+ return decoder.decodeString();
+}
- RiffContainer::ListChunk* containerChunk =
- container->getRoot()->findListRec(SerialBinary::kContainerFourCc);
- if (!containerChunk)
- {
- // Must be a container
- return SLANG_FAIL;
- }
+String EntryPointChunkRef::getName() const
+{
+ // TODO(tfoley): This kind of logic needs a way
+ // to be greatly simplified, so that we don't
+ // have to express such complicated logic for
+ // simply extracting a single string property...
+ //
+ Decoder decoder(ptr());
+ Decoder::WithProperty withProperty(decoder, SerialBinary::kNameFourCC);
+ return decoder.decodeString();
+}
- StringSlicePool containerStringPool(StringSlicePool::Style::Default);
+Profile EntryPointChunkRef::getProfile() const
+{
+ // TODO(tfoley): This kind of logic needs a way
+ // to be greatly simplified, so that we don't
+ // have to express such complicated logic for
+ // simply extracting a single string property...
+ //
+ Decoder decoder(ptr());
+ Decoder::WithProperty withProperty(decoder, SerialBinary::kProfileFourCC);
- if (RiffContainer::Data* stringTableData =
- containerChunk->findContainedData(SerialBinary::kStringTableFourCc))
- {
- SerialStringTableUtil::decodeStringTable(
- (const char*)stringTableData->getPayload(),
- stringTableData->getSize(),
- containerStringPool);
- }
+ Profile::RawVal rawVal;
+ decoder.decode(rawVal);
- RefPtr<SerialSourceLocReader> sourceLocReader;
- RefPtr<SerialClasses> serialClasses;
+ return Profile(rawVal);
+}
- // Debug information
- if (auto debugChunk = containerChunk->findContainedList(SerialSourceLocData::kDebugFourCc))
- {
- // Read into data
- SerialSourceLocData sourceLocData;
- SLANG_RETURN_ON_FAIL(sourceLocData.readContainer(debugChunk));
- // Turn into DebugReader
- sourceLocReader = new SerialSourceLocReader;
- SLANG_RETURN_ON_FAIL(sourceLocReader->read(&sourceLocData, options.sourceManager));
- }
+RiffContainer::ListChunk* findDebugChunk(RiffContainer::Chunk* startingChunk)
+{
+ if (!startingChunk)
+ return nullptr;
- // Create a source loc representing the binary module.
- SourceLoc binaryModuleLoc = SourceLoc();
+ RiffContainer::ListChunk* container = as<RiffContainer::ListChunk>(startingChunk);
+ if (!container)
+ container = startingChunk->m_parent;
- if (options.modulePath.getLength())
+ for (; container; container = container->m_parent)
{
- auto srcManager = options.linkage->getSourceManager();
- auto modulePathInfo = PathInfo::makePath(options.modulePath);
- auto srcFile = srcManager->findSourceFileByPathRecursively(modulePathInfo.foundPath);
- if (!srcFile)
+ if (auto debugChunk = container->findContainedList(SerialSourceLocData::kDebugFourCc))
{
- srcFile = srcManager->createSourceFileWithString(modulePathInfo, String());
- srcManager->addSourceFile(options.modulePath, srcFile);
+ return debugChunk;
}
- auto srcView = srcManager->createSourceView(srcFile, &modulePathInfo, SourceLoc());
- binaryModuleLoc = srcView->getRange().begin;
}
- // Add modules
- if (RiffContainer::ListChunk* moduleList =
- containerChunk->findContainedList(SerialBinary::kModuleListFourCc))
- {
- RiffContainer::Chunk* chunk = moduleList->getFirstContainedChunk();
- while (chunk)
- {
- auto startChunk = chunk;
-
- RefPtr<ASTBuilder> astBuilder = options.astBuilder;
- NodeBase* astRootNode = nullptr;
- RefPtr<IRModule> irModule;
- SerialContainerData::Module module;
- if (auto headerChunk =
- as<RiffContainer::DataChunk>(chunk, SerialBinary::kModuleHeaderFourCc))
- {
- 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))
- {
- if (!options.readHeaderOnly)
- {
- IRSerialData serialData;
- SLANG_RETURN_ON_FAIL(IRSerialReader::readContainer(irChunk, &serialData));
-
- // 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;
- }
-
- if (auto astChunk =
- as<RiffContainer::ListChunk>(chunk, ASTSerialBinary::kSlangASTModuleFourCC))
- {
- if (!options.readHeaderOnly)
- {
- RiffContainer::Data* astData =
- astChunk->findContainedData(ASTSerialBinary::kSlangASTModuleDataFourCC);
-
- if (astData)
- {
- 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.
-
- StringBuilder buf;
- buf << "tu" << out.modules.getCount();
- if (!astBuilder)
- {
- astBuilder =
- new ASTBuilder(options.sharedASTBuilder, buf.produceString());
- }
-
- /// We need to make the current ASTBuilder available for access via
- /// thread_local global.
- SetASTBuilderContextRAII astBuilderRAII(astBuilder);
-
- DefaultSerialObjectFactory objectFactory(astBuilder);
-
- SerialReader reader(serialClasses, &objectFactory);
-
- // 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()));
-
- // 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));
-
- // 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();
-
- String currentModuleName;
- Module* currentModule = nullptr;
-
- // Index from 1 (0 is null)
- for (Index i = 1; i < entriesCount; ++i)
- {
- const SerialInfo::Entry* entry = entries[i];
- if (entry->typeKind == SerialTypeKind::ImportSymbol)
- {
- // Import symbols are always serialized with a mangled name in
- // the form of <module_name>!<symbol_mangled_name>. As
- // symbol_mangled_name may not contain the name of its parent
- // module in the case of an `extern` or `export` symbol.
- //
- UnownedStringSlice mangledName =
- reader.getStringSlice(SerialIndex(i));
- List<UnownedStringSlice> slicesOut;
- StringUtil::split(mangledName, '!', slicesOut);
- if (slicesOut.getCount() != 2)
- return SLANG_FAIL;
- auto moduleName = slicesOut[0];
- mangledName = slicesOut[1];
-
- // 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
- {
- // The modules are loaded on the linkage.
- Linkage* linkage = options.linkage;
-
- NamePool* namePool = linkage->getNamePool();
- Name* moduleNameName = namePool->getName(moduleName);
- readModule = linkage->findOrImportModule(
- moduleNameName,
- binaryModuleLoc,
- options.sink,
- additionalLoadedModules);
- if (!readModule)
- {
- return SLANG_FAIL;
- }
-
- // Set the current module and name
- currentModule = readModule;
- currentModuleName = moduleName;
- }
-
- // Look up the symbol
- NodeBase* nodeBase =
- readModule->findExportFromMangledName(mangledName);
-
- if (!nodeBase)
- {
- 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;
- }
-
- // 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.
-
- {
- 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;
-
- // Get the parse infos
- const auto syntaxParseInfos = getSyntaxParseInfos();
- SLANG_ASSERT(syntaxParseInfos.getCount());
-
- for (auto& obj : reader.getObjects())
- {
-
- if (obj.m_kind == SerialTypeKind::NodeBase)
- {
- NodeBase* nodeBase = (NodeBase*)obj.m_ptr;
- SLANG_ASSERT(nodeBase);
-
- if (ExtensionDecl* extensionDecl =
- dynamicCast<ExtensionDecl>(nodeBase))
- {
- if (auto targetDeclRefType =
- as<DeclRefType>(extensionDecl->targetType))
- {
- ShortList<AggTypeDecl*> baseDecls;
- getExtensionTargetDeclList(
- astBuilder,
- targetDeclRefType,
- extensionDecl,
- baseDecls);
- for (auto baseDecl : baseDecls)
- {
- _getCandidateExtensionList(
- baseDecl,
- moduleDecl->mapTypeToCandidateExtensions)
- .add(extensionDecl);
- }
- }
- }
- else if (
- SyntaxDecl* syntaxDecl = dynamicCast<SyntaxDecl>(nodeBase))
- {
- // Set up the dictionary lazily
- if (syntaxKeywordDict.getCount() == 0)
- {
- 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());
- }
-
- // 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 if (Val* val = dynamicCast<Val>(nodeBase))
- {
- val->_setUnique();
- }
- }
- }
- }
- }
- }
-
- // Onto next chunk
- chunk = chunk->m_next;
- }
-
- if (astBuilder || irModule)
- {
- module.astBuilder = astBuilder;
- module.astRootNode = astRootNode;
- module.irModule = irModule;
-
- out.modules.add(module);
- }
-
- // If no progress, step to next chunk
- chunk = (chunk == startChunk) ? chunk->m_next : chunk;
- }
- }
+ return nullptr;
+}
- // Add all the entry points
- {
- List<RiffContainer::DataChunk*> entryPointChunks;
- containerChunk->findContained(SerialBinary::kEntryPointFourCc, entryPointChunks);
+SlangResult readSourceLocationsFromDebugChunk(
+ RiffContainer::ListChunk* debugChunk,
+ SourceManager* sourceManager,
+ RefPtr<SerialSourceLocReader>& outReader)
+{
+ if (!debugChunk)
+ return SLANG_FAIL;
- for (auto entryPointChunk : entryPointChunks)
- {
- auto reader = entryPointChunk->asReadHelper();
+ // Source location serialization uses the old approach where
+ // there is an intermediate in-memory data structure that the
+ // raw data from the RIFF gets deserialized into, before that
+ // intermediate representation gets transformed into something
+ // more directly usable.
+ //
+ // Thus we start with a first step where we simply read the data
+ // from the RIFF into the intermediate structure.
+ //
+ SerialSourceLocData intermediateData;
+ SLANG_RETURN_ON_FAIL(intermediateData.readContainer(debugChunk));
- SerialContainerBinary::EntryPoint srcEntryPoint;
- SLANG_RETURN_ON_FAIL(reader.read(srcEntryPoint));
+ // After reading the data into the intermediate representation,
+ // we turn it into a `SerialSourceLocReader`, which vends source
+ // location information to other deserialization tasks (both IR
+ // and AST deserialization).
+ //
+ auto reader = RefPtr(new SerialSourceLocReader());
+ SLANG_RETURN_ON_FAIL(reader->read(&intermediateData, sourceManager));
- SerialContainerData::EntryPoint dstEntryPoint;
+ outReader = reader;
+ return SLANG_OK;
+}
- dstEntryPoint.name = options.namePool->getName(
- containerStringPool.getSlice(StringSlicePool::Handle(srcEntryPoint.name)));
- dstEntryPoint.profile.raw = srcEntryPoint.profile;
- dstEntryPoint.mangledName =
- containerStringPool.getSlice(StringSlicePool::Handle(srcEntryPoint.mangledName));
+SlangResult decodeModuleIR(
+ RefPtr<IRModule>& outIRModule,
+ RiffContainer::Chunk* chunk,
+ Session* session,
+ SerialSourceLocReader* sourceLocReader)
+{
+ // IR serialization still uses the older approach, where
+ // data gets deserialized from the RIFF into an intermediate
+ // data structure (`IRSerialData`), and then the actual
+ // in-memory structures are created based on the intermediate.
+ //
+ // Thus we start by running the `IRSerialReader::readContainer`
+ // logic to get the `IRSerialData` representation.
+ //
+ // TODO(tfoley): This should all get streamlined so that we
+ // are deserializing IR nodes directly from the format written
+ // into the RIFF.
+ //
+ auto listChunk = as<RiffContainer::ListChunk>(chunk);
+ if (!listChunk)
+ return SLANG_FAIL;
+ IRSerialData serialData;
+ SLANG_RETURN_ON_FAIL(IRSerialReader::readContainer(listChunk, &serialData));
- out.entryPoints.add(dstEntryPoint);
- }
- }
+ // Next we read the actual IR representation out from the
+ // `serialData`. This is the step that may pull source-location
+ // information from the provided `sourceLocReader`.
+ //
+ IRSerialReader reader;
+ SLANG_RETURN_ON_FAIL(reader.read(serialData, session, sourceLocReader, outIRModule));
return SLANG_OK;
}