diff options
Diffstat (limited to 'source/slang')
| -rw-r--r-- | source/slang/slang-ir.h | 3 | ||||
| -rw-r--r-- | source/slang/slang-serialize-ast.cpp | 91 | ||||
| -rw-r--r-- | source/slang/slang-serialize-container.cpp | 103 | ||||
| -rw-r--r-- | source/slang/slang-serialize-container.h | 10 | ||||
| -rw-r--r-- | source/slang/slang-serialize-ir-types.h | 3 | ||||
| -rw-r--r-- | source/slang/slang-serialize-ir.cpp | 983 | ||||
| -rw-r--r-- | source/slang/slang-serialize-ir.h | 127 | ||||
| -rw-r--r-- | source/slang/slang-serialize-source-loc.h | 76 | ||||
| -rw-r--r-- | source/slang/slang-serialize-types.h | 7 | ||||
| -rw-r--r-- | source/slang/slang.cpp | 4 |
10 files changed, 483 insertions, 924 deletions
diff --git a/source/slang/slang-ir.h b/source/slang/slang-ir.h index bd16eb1b9..c29a52ff7 100644 --- a/source/slang/slang-ir.h +++ b/source/slang/slang-ir.h @@ -2442,6 +2442,9 @@ public: ContainerPool& getContainerPool() { return m_containerPool; } private: + friend struct IRSerialReadContext; + friend struct IRSerialWriteContext; + IRModule() = delete; /// Ctor diff --git a/source/slang/slang-serialize-ast.cpp b/source/slang/slang-serialize-ast.cpp index 1c05e3ce2..325aa3244 100644 --- a/source/slang/slang-serialize-ast.cpp +++ b/source/slang/slang-serialize-ast.cpp @@ -7,10 +7,12 @@ #include "slang-diagnostics.h" #include "slang-mangle.h" #include "slang-parser.h" -#include "slang-serialize-ast.cpp.fiddle" #include "slang-serialize-fossil.h" #include "slang-serialize-riff.h" +// +#include "slang-serialize-ast.cpp.fiddle" + #define SLANG_ENABLE_AST_DESERIALIZATION_STATS 0 #define SLANG_DISABLE_ON_DEMAND_AST_DESERIALIZATION 1 @@ -345,13 +347,12 @@ struct ASTSerialContext; using ASTSerializer = Serializer_<ISerializerImpl, ASTSerialContext>; /// Context interface for AST serialization -struct ASTSerialContext +struct ASTSerialContext : SourceLocSerialContext { public: virtual void handleASTNode(ASTSerializer const& serializer, NodeBase*& value) = 0; virtual void handleASTNodeContents(ASTSerializer const& serializer, NodeBase* value) = 0; virtual void handleName(ASTSerializer const& serializer, Name*& value) = 0; - virtual void handleSourceLoc(ASTSerializer const& serializer, SourceLoc& value) = 0; virtual void handleToken(ASTSerializer const& serializer, Token& value) = 0; virtual void handleContainerDeclDirectMemberDecls( ASTSerializer const& serializer, @@ -552,13 +553,13 @@ private: // virtual void handleName(ASTSerializer const& serializer, Name*& value) override; - virtual void handleSourceLoc(ASTSerializer const& serializer, SourceLoc& value) override; virtual void handleToken(ASTSerializer const& serializer, Token& value) override; virtual void handleASTNode(ASTSerializer const& serializer, NodeBase*& node) override; virtual void handleASTNodeContents(ASTSerializer const& serializer, NodeBase* node) override; virtual void handleContainerDeclDirectMemberDecls( ASTSerializer const& serializer, ContainerDeclDirectMemberDecls& value) override; + virtual SerialSourceLocWriter* getSourceLocWriter() override { return _sourceLocWriter; } void _writeImportedModule(ASTSerializer const& serializer, ModuleDecl* moduleDecl); void _writeImportedDecl( @@ -695,13 +696,13 @@ private: // virtual void handleName(ASTSerializer const& serializer, Name*& value) override; - virtual void handleSourceLoc(ASTSerializer const& serializer, SourceLoc& value) override; virtual void handleToken(ASTSerializer const& serializer, Token& value) override; virtual void handleASTNode(ASTSerializer const& serializer, NodeBase*& outNode) override; virtual void handleASTNodeContents(ASTSerializer const& serializer, NodeBase* node) override; virtual void handleContainerDeclDirectMemberDecls( ASTSerializer const& serializer, ContainerDeclDirectMemberDecls& value) override; + virtual SerialSourceLocReader* getSourceLocReader() override { return _sourceLocReader; } ModuleDecl* _readImportedModule(ASTSerializer const& serializer); NodeBase* _readImportedDecl(ASTSerializer const& serializer); @@ -711,86 +712,6 @@ private: }; // -// Let's look at a concrete example of how the `ASTSerialReadContext` -// and `ASTSerialWriteContext` get applied to handle one of the types -// that needs them for additional context. -// -// The `serialize()` function for `SourceLoc` is declared to take -// an `ASTSerializer` argument instead of a simple `Serializer`: -// -void serialize(ASTSerializer const& serializer, SourceLoc& value) -{ - // Its body is trivial, because the actual handling of `SourceLoc` - // serialization is delegated to the `ASTSerialWriteContext` and - // `ASTSerialReadContext`. - // - serializer.getContext()->handleSourceLoc(serializer, value); -} - -void ASTSerialWriteContext::handleSourceLoc(ASTSerializer const& serializer, SourceLoc& value) -{ - // Writing of source location information can be disabled by - // compiler options, and in that case the `_sourceLocWriter` - // may be null. - // - // In order to handle that possibility, we serialize a `SourceLoc` - // as an optional value, dependent on whether we have a - // `_sourceLocWriter` that can be used. - // - SLANG_SCOPED_SERIALIZER_OPTIONAL(serializer); - if (_sourceLocWriter != nullptr) - { - // The `SourceLoc` type is implemented under the hood as an - // integer offset that can only be decoded using the specific - // `SourceManager` that created it. - // - // The source location writer handles the task of translating - // the under-the-hood representation to a single integer value - // (represented as `SerialSourceLocData::SourceLoc`) that can - // be decoded on the other side using other data that the - // source location writer will write out as part of its own - // representation (all of which goes into the dedicated debug - // data chunk, distinct from the AST). - // - SerialSourceLocData::SourceLoc rawValue = _sourceLocWriter->addSourceLoc(value); - serialize(serializer, rawValue); - } -} - -void ASTSerialReadContext::handleSourceLoc(ASTSerializer const& serializer, SourceLoc& value) -{ - // Because the source location was *written* as an optional, - // we clearly need to *read* it as one. - // - SLANG_SCOPED_SERIALIZER_OPTIONAL(serializer); - if (hasElements(serializer)) - { - SerialSourceLocData::SourceLoc rawValue; - serialize(serializer, rawValue); - - // Even if the serialized optional had a value, it is - // possible that the debug-data chunk got stripped from - // the compiled module file, in which case we wouldn't - // have access to the data needed to decode it. - // - // In that case, the `_sourceLocReader` member would be - // null, so we handle that possibility here. - // - if (auto sourceLocReader = _sourceLocReader) - { - value = sourceLocReader->getSourceLoc(rawValue); - } - } -} - -// Now that we've seen the relevant serialization logic, it is clear that -// a `SourceLoc` gets fossilized the same way that an optional wrapping -// an integer (of type `SerialSourceLocData::SourceLoc`) would. -// -SLANG_DECLARE_FOSSILIZED_AS(SourceLoc, std::optional<SerialSourceLocData::SourceLoc>); - - -// // Earlier we generated forward declarations for all of the types // that we'll be able to handle with fiddle, but there are still a // large number of types that we currently have to hand-write the diff --git a/source/slang/slang-serialize-container.cpp b/source/slang/slang-serialize-container.cpp index 1d7f6d438..d4d1fc3ad 100644 --- a/source/slang/slang-serialize-container.cpp +++ b/source/slang/slang-serialize-container.cpp @@ -12,6 +12,7 @@ #include "slang-serialize-ast.h" #include "slang-serialize-ir.h" #include "slang-serialize-source-loc.h" +#include "slang-serialize-types.h" namespace Slang { @@ -137,11 +138,10 @@ public: 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(writer.write(irModule, _sourceLocWriter, &serialData)); - SLANG_RETURN_ON_FAIL(IRSerialWriter::writeTo(serialData, _cursor)); + { + SLANG_SCOPED_RIFF_BUILDER_LIST_CHUNK(_cursor, PropertyKeys<IRModule>::IRModule); + writeSerializedModuleIR(_cursor, irModule, _sourceLocWriter); + } return SLANG_OK; } @@ -216,10 +216,8 @@ public: // if (auto irModule = module->getIRModule()) { - IRSerialData serialData; - IRSerialWriter writer; - SLANG_RETURN_ON_FAIL(writer.write(irModule, _sourceLocWriter, &serialData)); - SLANG_RETURN_ON_FAIL(IRSerialWriter::writeTo(serialData, _cursor)); + SLANG_SCOPED_RIFF_BUILDER_LIST_CHUNK(_cursor, PropertyKeys<IRModule>::IRModule); + writeSerializedModuleIR(_cursor, irModule, _sourceLocWriter); } // If we have AST information available, then we serialize it here. @@ -457,11 +455,11 @@ String ModuleChunk::getName() const IRModuleChunk const* ModuleChunk::findIR() const { - auto foundChunk = findListChunk(IRSerialBinary::kIRModuleFourCc); - if (!foundChunk) + auto foundProperty = findListChunk(PropertyKeys<IRModule>::IRModule); + if (!foundProperty) return nullptr; - return static_cast<IRModuleChunk const*>(foundChunk); + return static_cast<IRModuleChunk const*>(foundProperty->getFirstChild().get()); } ASTModuleChunk const* ModuleChunk::findAST() const @@ -576,37 +574,38 @@ SlangResult readSourceLocationsFromDebugChunk( return SLANG_OK; } -SlangResult decodeModuleIR( - RefPtr<IRModule>& outIRModule, - IRModuleChunk const* chunk, - Session* session, - SerialSourceLocReader* sourceLocReader) +static void calcModuleInstructionList(IRModule* module, List<IRInst*>& instsOut) { - // 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. - // - IRSerialData serialData; - SLANG_RETURN_ON_FAIL(IRSerialReader::readFrom(chunk, &serialData)); + // We reserve 0 for null + instsOut.setCount(1); + instsOut[0] = nullptr; - // 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)); + // Stack for parentInst + List<IRInst*> parentInstStack; - return SLANG_OK; + IRModuleInst* moduleInst = module->getModuleInst(); + parentInstStack.add(moduleInst); + + // Add to list + instsOut.add(moduleInst); + + // Traverse all of the instructions + while (parentInstStack.getCount()) + { + // If it's in the stack it is assumed it is already in the inst map + IRInst* parentInst = parentInstStack.getLast(); + parentInstStack.removeLast(); + + IRInstListBase childrenList = parentInst->getDecorationsAndChildren(); + for (IRInst* child : childrenList) + { + instsOut.add(child); + parentInstStack.add(child); + } + } } + /* static */ SlangResult SerialContainerUtil::verifyIRSerialize( IRModule* module, Session* session, @@ -615,9 +614,7 @@ SlangResult decodeModuleIR( // Verify if we can stream out with raw source locs List<IRInst*> originalInsts; - IRSerialWriter::calcInstructionList(module, originalInsts); - - IRSerialData irData; + calcModuleInstructionList(module, originalInsts); OwnedMemoryStream memoryStream(FileAccess::ReadWrite); @@ -627,7 +624,6 @@ SlangResult decodeModuleIR( // Need to put all of this in a module chunk SLANG_SCOPED_RIFF_BUILDER_LIST_CHUNK(cursor, SerialBinary::kModuleFourCC); - RefPtr<SerialSourceLocWriter> sourceLocWriter; if (options.sourceManagerToUseWhenSerializingSourceLocs) @@ -637,11 +633,9 @@ SlangResult decodeModuleIR( } { - // Write IR out to `irData` - IRSerialWriter writer; - SLANG_RETURN_ON_FAIL(writer.write(module, sourceLocWriter, &irData)); + SLANG_SCOPED_RIFF_BUILDER_LIST_CHUNK(cursor, PropertyKeys<IRModule>::IRModule); + writeSerializedModuleIR(cursor, module, sourceLocWriter); } - SLANG_RETURN_ON_FAIL(IRSerialWriter::writeTo(irData, cursor)); // Write the debug info Riff container if (sourceLocWriter) @@ -703,25 +697,12 @@ SlangResult decodeModuleIR( return SLANG_FAIL; } - { - IRSerialData irReadData; - IRSerialReader reader; - SLANG_RETURN_ON_FAIL(reader.readFrom(irChunk, &irReadData)); - - // Check the stream read data is the same - if (irData != irReadData) - { - SLANG_ASSERT(!"Streamed in data doesn't match"); - return SLANG_FAIL; - } - - SLANG_RETURN_ON_FAIL(reader.read(irData, session, sourceLocReader, irReadModule)); - } + readSerializedModuleIR(irChunk, session, sourceLocReader, irReadModule); } } List<IRInst*> readInsts; - IRSerialWriter::calcInstructionList(irReadModule, readInsts); + calcModuleInstructionList(irReadModule, readInsts); if (readInsts.getCount() != originalInsts.getCount()) { diff --git a/source/slang/slang-serialize-container.h b/source/slang/slang-serialize-container.h index 1608b085c..2a3ecef75 100644 --- a/source/slang/slang-serialize-container.h +++ b/source/slang/slang-serialize-container.h @@ -68,7 +68,9 @@ public: String getValue() const; }; -struct IRModuleChunk; +struct IRModuleChunk : RIFF::ListChunk +{ +}; struct ASTModuleChunk : RIFF::ListChunk { @@ -129,12 +131,6 @@ SlangResult readSourceLocationsFromDebugChunk( SourceManager* sourceManager, RefPtr<SerialSourceLocReader>& outReader); -SlangResult decodeModuleIR( - RefPtr<IRModule>& outIRModule, - IRModuleChunk const* chunk, - Session* session, - SerialSourceLocReader* sourceLocReader); - } // namespace Slang #endif diff --git a/source/slang/slang-serialize-ir-types.h b/source/slang/slang-serialize-ir-types.h index d4c9dd9d7..dea45d72f 100644 --- a/source/slang/slang-serialize-ir-types.h +++ b/source/slang/slang-serialize-ir-types.h @@ -19,9 +19,6 @@ class Name; struct IRSerialBinary { - /// IR module list - static const FourCC::RawValue kIRModuleFourCc = SLANG_FOUR_CC('S', 'i', 'm', 'd'); - /* NOTE! All FourCC that can be compressed must start with capital 'S', because compressed version is the same FourCC with the 'S' replaced with 's' */ diff --git a/source/slang/slang-serialize-ir.cpp b/source/slang/slang-serialize-ir.cpp index 093643d09..52a0a6537 100644 --- a/source/slang/slang-serialize-ir.cpp +++ b/source/slang/slang-serialize-ir.cpp @@ -1,761 +1,436 @@ // slang-serialize-ir.cpp #include "slang-serialize-ir.h" -#include "../core/slang-byte-encode-util.h" -#include "../core/slang-math.h" -#include "../core/slang-text-io.h" +#include "core/slang-blob-builder.h" #include "slang-ir-insts.h" +#include "slang-ir-validate.h" +#include "slang-serialize-fossil.h" +#include "slang-serialize-source-loc.h" +#include "slang-serialize.h" namespace Slang { -static bool _isConstant(IROp opIn) +// +// We wrap everything up in an IRModuleInfo, to prepare for the case in which +// we want to serialize some sidecar information to help with on-demand loading +// or backwards compat +// +struct IRModuleInfo { - const int op = (kIROpMask_OpMask & opIn); - return op >= kIROp_FirstConstant && op <= kIROp_LastConstant; -} + RefPtr<IRModule> module; +}; -// !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! IRSerialWriter !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! +// +// We need some small amount of additional context to serialize IR Modules, keep track of that here +// +struct IRSerialContext; +using IRSerializer = Serializer_<ISerializerImpl, IRSerialContext>; -void IRSerialWriter::_addInstruction(IRInst* inst) +struct IRSerialContext : SourceLocSerialContext { - // It cannot already be in the map - SLANG_ASSERT(!m_instMap.containsKey(inst)); - - // Add to the map - m_instMap.add(inst, Ser::InstIndex(m_insts.getCount())); - m_insts.add(inst); -} +public: + virtual void handleIRModule(IRSerializer const& serializer, IRModule*& value) = 0; + virtual void handleName(IRSerializer const& serializer, Name*& value) = 0; +}; -Result IRSerialWriter::_calcDebugInfo(SerialSourceLocWriter* sourceLocWriter) +struct IRSerialWriteContext : IRSerialContext { - // We need to find the unique source Locs - // We are not going to store SourceLocs directly, because there may be multiple views mapping - // down to the same underlying source file - - // First find all the unique locs - struct InstLoc + IRSerialWriteContext(SerialSourceLocWriter* sourceLocWriter) + : _sourceLocWriter(sourceLocWriter) { - typedef InstLoc ThisType; - - SLANG_FORCE_INLINE bool operator<(const ThisType& rhs) const - { - return sourceLoc < rhs.sourceLoc || - (sourceLoc == rhs.sourceLoc && instIndex < rhs.instIndex); - } - - uint32_t instIndex; - uint32_t sourceLoc; - }; - - // Find all of the source locations and their associated instructions - List<InstLoc> instLocs; - const Index numInsts = m_insts.getCount(); - for (Index i = 1; i < numInsts; i++) - { - IRInst* srcInst = m_insts[i]; - if (!srcInst->sourceLoc.isValid()) - { - continue; - } - InstLoc instLoc; - instLoc.instIndex = uint32_t(i); - instLoc.sourceLoc = uint32_t(srcInst->sourceLoc.getRaw()); - instLocs.add(instLoc); } - // Sort them - instLocs.sort(); + virtual void handleIRModule(IRSerializer const& serializer, IRModule*& value) override; + virtual void handleName(IRSerializer const& serializer, Name*& value) override; + virtual SerialSourceLocWriter* getSourceLocWriter() override { return _sourceLocWriter; } - // Look for runs - const InstLoc* startInstLoc = instLocs.begin(); - const InstLoc* endInstLoc = instLocs.end(); + SerialSourceLocWriter* _sourceLocWriter; +}; - while (startInstLoc < endInstLoc) +struct IRSerialReadContext : IRSerialContext, RefObject +{ + IRSerialReadContext(Session* session, SerialSourceLocReader* sourceLocReader) + : _session(session), _sourceLocReader(sourceLocReader) { - const uint32_t startSourceLoc = startInstLoc->sourceLoc; - - // Find the run with the same source loc - - const InstLoc* curInstLoc = startInstLoc + 1; - uint32_t curInstIndex = startInstLoc->instIndex + 1; - - // Find the run size with same source loc and run of instruction indices - for (; curInstLoc < endInstLoc && curInstLoc->sourceLoc == startSourceLoc && - curInstLoc->instIndex == curInstIndex; - ++curInstLoc, ++curInstIndex) - { - } - - // Add the run + } + virtual void handleIRModule(IRSerializer const& serializer, IRModule*& value) override; + virtual void handleName(IRSerializer const& serializer, Name*& value) override; + virtual SerialSourceLocReader* getSourceLocReader() override { return _sourceLocReader; } - IRSerialData::SourceLocRun sourceLocRun; - sourceLocRun.m_numInst = curInstIndex - startInstLoc->instIndex; - ; - sourceLocRun.m_startInstIndex = IRSerialData::InstIndex(startInstLoc->instIndex); - sourceLocRun.m_sourceLoc = - sourceLocWriter->addSourceLoc(SourceLoc::fromRaw(startSourceLoc)); + // Used to allocate an IRModule + Session* _session; - m_serialData->m_debugSourceLocRuns.add(sourceLocRun); + // + SerialSourceLocReader* _sourceLocReader; - // Next - startInstLoc = curInstLoc; - } + // The module in which we will allocate our instructions + RefPtr<IRModule> _module; +}; - return SLANG_OK; +// IROps are serialized as integers +SLANG_DECLARE_FOSSILIZED_AS(IROp, FossilUInt); +void serialize(Serializer const& serializer, IROp& value) +{ + serializeEnum(serializer, value); } -Result IRSerialWriter::write( - IRModule* module, - SerialSourceLocWriter* sourceLocWriter, - IRSerialData* serialData) +/// Serialize a `value` of type `IRModuleInfo`, currently no extra information +/// besides the IRModule +SLANG_DECLARE_FOSSILIZED_AS_MEMBER(IRModuleInfo, module); +void serialize(IRSerializer const& serializer, IRModuleInfo& value) { - typedef Ser::Inst::PayloadType PayloadType; - - m_serialData = serialData; - - serialData->clear(); - - // We reserve 0 for null - m_insts.clear(); - m_insts.add(nullptr); - - // Reset - m_instMap.clear(); - m_decorations.clear(); - - // Stack for parentInst - List<IRInst*> parentInstStack; - - IRModuleInst* moduleInst = module->getModuleInst(); - parentInstStack.add(moduleInst); - - // Add to the map - _addInstruction(moduleInst); - - // Traverse all of the instructions - while (parentInstStack.getCount()) - { - // If it's in the stack it is assumed it is already in the inst map - IRInst* parentInst = parentInstStack.getLast(); - parentInstStack.removeLast(); - SLANG_ASSERT(m_instMap.containsKey(parentInst)); - - // Okay we go through each of the children in order. If they are IRInstParent derived, we - // add to stack to process later cos we want breadth first so the order of children is the - // same as their index order, meaning we don't need to store explicit indices - const Ser::InstIndex startChildInstIndex = Ser::InstIndex(m_insts.getCount()); - - IRInstListBase childrenList = parentInst->getDecorationsAndChildren(); - for (IRInst* child : childrenList) - { - // This instruction can't be in the map... - SLANG_ASSERT(!m_instMap.containsKey(child)); - - _addInstruction(child); - - parentInstStack.add(child); - } + serialize(serializer, value.module); +} - // If it had any children, then store the information about it - if (Ser::InstIndex(m_insts.getCount()) != startChildInstIndex) - { - Ser::InstRun run; - run.m_parentIndex = m_instMap[parentInst]; - run.m_startInstIndex = startChildInstIndex; - run.m_numChildren = Ser::SizeType(m_insts.getCount() - int(startChildInstIndex)); +// +// Serialized linked list of child instructions as regular lists, we can fix up +// the pointers on deserialization +// +SLANG_DECLARE_FOSSILIZED_AS(IRInstListBase, List<IRInst*>); - m_serialData->m_childRuns.add(run); - } - } +void serialize(IRSerializer const& serializer, IRInstListBase& value) +{ + SLANG_SCOPED_SERIALIZER_ARRAY(serializer); -#if 0 + if (isWriting(serializer)) { - List<IRInst*> workInsts; - calcInstructionList(module, workInsts); - SLANG_ASSERT(workInsts.getCount() == m_insts.getCount()); - for (UInt i = 0; i < workInsts.getCount(); ++i) + for (auto inst : value) { - SLANG_ASSERT(workInsts[i] == m_insts[i]); + serialize(serializer, inst); } } -#endif - - // Set to the right size - m_serialData->m_insts.setCount(m_insts.getCount()); - // Clear all instructions - memset(m_serialData->m_insts.begin(), 0, sizeof(Ser::Inst) * m_serialData->m_insts.getCount()); - - // Need to set up the actual instructions + else { - const Index numInsts = m_insts.getCount(); + IRInst* first = nullptr; + IRInst* prev = nullptr; - for (Index i = 1; i < numInsts; ++i) + while (hasElements(serializer)) { - IRInst* srcInst = m_insts[i]; - Ser::Inst& dstInst = m_serialData->m_insts[i]; - - dstInst.m_op = uint16_t(srcInst->getOp() & kIROpMask_OpMask); - dstInst.m_payloadType = PayloadType::Empty; - - dstInst.m_resultTypeIndex = getInstIndex(srcInst->getFullType()); + IRInst* inst = nullptr; + serialize(serializer, inst); + first = first ? first : inst; - IRConstant* irConst = as<IRConstant>(srcInst); - if (irConst) - { - switch (srcInst->getOp()) - { - // Special handling for the ir const derived types - case kIROp_BlobLit: - { - // Blobs are serialized into string table like strings - auto stringLit = static_cast<IRBlobLit*>(srcInst); - dstInst.m_payloadType = PayloadType::String_1; - dstInst.m_payload.m_stringIndices[0] = - getStringIndex(stringLit->getStringSlice()); - break; - } - case kIROp_StringLit: - { - auto stringLit = static_cast<IRStringLit*>(srcInst); - dstInst.m_payloadType = PayloadType::String_1; - dstInst.m_payload.m_stringIndices[0] = - getStringIndex(stringLit->getStringSlice()); - break; - } - case kIROp_IntLit: - { - dstInst.m_payloadType = PayloadType::Int64; - dstInst.m_payload.m_int64 = irConst->value.intVal; - break; - } - case kIROp_PtrLit: - { - dstInst.m_payloadType = PayloadType::Int64; - dstInst.m_payload.m_int64 = (intptr_t)irConst->value.ptrVal; - break; - } - case kIROp_FloatLit: - { - dstInst.m_payloadType = PayloadType::Float64; - dstInst.m_payload.m_float64 = irConst->value.floatVal; - break; - } - case kIROp_BoolLit: - { - dstInst.m_payloadType = PayloadType::UInt32; - dstInst.m_payload.m_uint32 = irConst->value.intVal ? 1 : 0; - break; - } - case kIROp_VoidLit: - { - dstInst.m_payloadType = PayloadType::Empty; - break; - } - default: - { - SLANG_RELEASE_ASSERT(!"Unhandled constant type"); - return SLANG_FAIL; - } - } - continue; - } - - // ModuleInst is different, in so far as it holds a pointer to IRModule, but we don't - // need to save that off in a special way, so can just use regular path - - const int numOperands = int(srcInst->operandCount); - Ser::InstIndex* dstOperands = nullptr; - - if (numOperands <= Ser::Inst::kMaxOperands) - { - // Checks the compile below is valid - SLANG_COMPILE_TIME_ASSERT( - PayloadType(0) == PayloadType::Empty && - PayloadType(1) == PayloadType::Operand_1 && - PayloadType(2) == PayloadType::Operand_2); - - dstInst.m_payloadType = PayloadType(numOperands); - dstOperands = dstInst.m_payload.m_operands; - } - else + if (prev) { - dstInst.m_payloadType = PayloadType::OperandExternal; - - int operandArrayBaseIndex = int(m_serialData->m_externalOperands.getCount()); - m_serialData->m_externalOperands.setCount(operandArrayBaseIndex + numOperands); - - dstOperands = m_serialData->m_externalOperands.begin() + operandArrayBaseIndex; - - auto& externalOperands = dstInst.m_payload.m_externalOperand; - externalOperands.m_arrayIndex = Ser::ArrayIndex(operandArrayBaseIndex); - externalOperands.m_size = Ser::SizeType(numOperands); + prev->next = inst; } - for (int j = 0; j < numOperands; ++j) - { - const Ser::InstIndex dstInstIndex = getInstIndex(srcInst->getOperand(j)); - dstOperands[j] = dstInstIndex; - } + inst->prev = prev; + prev = inst; } + if (prev) + { + prev->next = nullptr; + } + value = IRInstListBase(first, prev); } - - // Convert strings into a string table - { - SerialStringTableUtil::encodeStringTable(m_stringSlicePool, serialData->m_stringTable); - } - - if (sourceLocWriter) - { - _calcDebugInfo(sourceLocWriter); - } - - m_serialData = nullptr; - return SLANG_OK; -} - -Result _writeInstArrayChunk( - FourCC chunkId, - const List<IRSerialData::Inst>& array, - RIFF::BuildCursor& cursor) -{ - if (array.getCount() == 0) - { - return SLANG_OK; - } - - return SerialRiffUtil::writeArrayChunk(chunkId, array, cursor); } -/* static */ Result IRSerialWriter::writeTo(const IRSerialData& data, RIFF::BuildCursor& cursor) +// +// Initializing an IRUse requires a small bit of special setup, handle that +// here +// +void serializeUse(IRSerializer const& serializer, IRInst* user, IRUse& use) { - SLANG_SCOPED_RIFF_BUILDER_LIST_CHUNK(cursor, Bin::kIRModuleFourCc); - - SLANG_RETURN_ON_FAIL(_writeInstArrayChunk(Bin::kInstFourCc, data.m_insts, cursor)); - SLANG_RETURN_ON_FAIL( - SerialRiffUtil::writeArrayChunk(Bin::kChildRunFourCc, data.m_childRuns, cursor)); - SLANG_RETURN_ON_FAIL(SerialRiffUtil::writeArrayChunk( - Bin::kExternalOperandsFourCc, - data.m_externalOperands, - cursor)); - SLANG_RETURN_ON_FAIL(SerialRiffUtil::writeArrayChunk( - SerialBinary::kStringTableFourCc, - data.m_stringTable, - cursor)); - - SLANG_RETURN_ON_FAIL(SerialRiffUtil::writeArrayChunk( - Bin::kUInt32RawSourceLocFourCc, - data.m_rawSourceLocs, - cursor)); - - if (data.m_debugSourceLocRuns.getCount()) + SLANG_ASSERT(user); + IRInst* used = isWriting(serializer) ? use.get() : nullptr; + serialize(serializer, used); + if (isReading(serializer)) { - SerialRiffUtil::writeArrayChunk( - Bin::kDebugSourceLocRunFourCc, - data.m_debugSourceLocRuns, - cursor); + use.init(user, used); } - - return SLANG_OK; } -/* static */ void IRSerialWriter::calcInstructionList(IRModule* module, List<IRInst*>& instsOut) +template<typename T> +void serializeObject(IRSerializer const& serializer, T*& inst, IRInst*) { - // We reserve 0 for null - instsOut.setCount(1); - instsOut[0] = nullptr; + // Each IR instruction has: + // + // * An opcode + // * Zero or more operands + // * Zero or more children + // + // Most instructions are entirely defined by those properties. + // + // The instructions that represent simple constants (integers, strings, etc.) are + // unique in that they have "payload" data that holds their value, instead of having + // any operands. + // + // Note that as a result of the serialization strategy used by fossil, it + // is not possible for the deserialization logic to interact with any + // systems for deduplication or simplification of instructions. - // Stack for parentInst - List<IRInst*> parentInstStack; + SLANG_SCOPED_SERIALIZER_VARIANT(serializer); - IRModuleInst* moduleInst = module->getModuleInst(); - parentInstStack.add(moduleInst); + // + // Since we're calling deferSerializeObjectContents at the end of this + // function we need only serialize/deserialize enough to allocate the + // instruction itself, + // + // For most instructions this is simply the operand count, however for a + // couple of exceptions (IRModuleInst and anything under IRConstant) we + // may need to allocate more space, so first find out what sort of + // instruction it is. + // + IROp op = isWriting(serializer) ? inst->m_op : kIROp_Invalid; + uint32_t operandCount = isWriting(serializer) ? inst->operandCount : ~0; + serialize(serializer, op); + serialize(serializer, operandCount); - // Add to list - instsOut.add(moduleInst); + // + // If it's a string literal, the data is stored inline, so we need to know + // the length of the string in order to allocate, handle that here, and we + // may as well just read the whole string for convenience. + // + String stringLitString; + if (op == kIROp_StringLit || op == kIROp_BlobLit) + { + if (isWriting(serializer)) + { + stringLitString = cast<IRConstant>(inst)->getStringSlice(); + } + serialize(serializer, stringLitString); + } - // Traverse all of the instructions - while (parentInstStack.getCount()) + // + // Now we have read/written everything we need in order to allocate the inst, do so + // This will involve calculating the allocation size for constants also + // + if (isReading(serializer)) { - // If it's in the stack it is assumed it is already in the inst map - IRInst* parentInst = parentInstStack.getLast(); - parentInstStack.removeLast(); + const auto readContext = static_cast<IRSerialReadContext*>(serializer.getContext()); - IRInstListBase childrenList = parentInst->getDecorationsAndChildren(); - for (IRInst* child : childrenList) + // We need to handle the special case instructions which aren't just defined by operands and + // children, IRModuleInst and IRConstants + size_t minSizeInBytes = 0; + switch (op) { - instsOut.add(child); - parentInstStack.add(child); + case kIROp_ModuleInst: + minSizeInBytes = offsetof(IRModuleInst, module) + + sizeof(IRModuleInst::module); // NOLINT(bugprone-sizeof-expression) + break; + case kIROp_BoolLit: + case kIROp_IntLit: + case kIROp_FloatLit: + case kIROp_PtrLit: + case kIROp_VoidLit: + minSizeInBytes = offsetof(IRConstant, value) + sizeof(IRConstant::value); + break; + case kIROp_StringLit: + case kIROp_BlobLit: + minSizeInBytes = offsetof(IRConstant, value) + + offsetof(IRConstant::StringValue, chars) + stringLitString.getLength(); + break; + } + inst = cast<T>(readContext->_module->_allocateInst(op, operandCount, minSizeInBytes)); + if (op == kIROp_StringLit || op == kIROp_BlobLit) + { + const auto c = cast<IRConstant>(inst); + char* dstChars = c->value.stringVal.chars; + c->value.stringVal.numChars = uint32_t(stringLitString.getLength()); + memcpy(dstChars, stringLitString.getBuffer(), stringLitString.getLength()); } } -} - -// !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! IRSerialReader !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! -static Result _readInstArrayChunk(RIFF::DataChunk const* chunk, List<IRSerialData::Inst>& arrayOut) -{ - SerialRiffUtil::ListResizerForType<IRSerialData::Inst> resizer(arrayOut); - return SerialRiffUtil::readArrayChunk(chunk, resizer); + // We've allocated the object, we can leave the rest for later + deferSerializeObjectContents(serializer, inst); } -/* static */ Result IRSerialReader::readFrom( - IRModuleChunk const* irModuleChunk, - IRSerialData* outData) +template<typename T> +void serializeObjectContents(IRSerializer const& serializer, T*& value, IRInst*) { - typedef IRSerialBinary Bin; - - outData->clear(); - - for (auto chunk : irModuleChunk->getChildren()) + // + // This is all that's necessary for normal instructions + // We serialize the source location, type, operands and children + // + serialize(serializer, value->sourceLoc); + serializeUse(serializer, value, value->typeUse); + for (Index i = 0; i < value->operandCount; ++i) { - auto dataChunk = as<RIFF::DataChunk>(chunk); - if (!dataChunk) - { - continue; - } + serializeUse(serializer, value, value->getOperands()[i]); + } + // There's an overload for this call further up in this file + serialize(serializer, value->m_decorationsAndChildren); - switch (dataChunk->getType()) + // + // IRConstants require a little special handling + // IRModuleInst also has some extra information, but it's just a pointer to + // the IRModule value, and this is handled at the top level + // + if (const auto constant = as<IRConstant>(value)) + { + switch (value->m_op) { - case Bin::kInstFourCc: - { - SLANG_RETURN_ON_FAIL(_readInstArrayChunk(dataChunk, outData->m_insts)); - break; - } - case Bin::kChildRunFourCc: - { - SLANG_RETURN_ON_FAIL( - SerialRiffUtil::readArrayChunk(dataChunk, outData->m_childRuns)); - break; - } - case Bin::kExternalOperandsFourCc: - { - SLANG_RETURN_ON_FAIL( - SerialRiffUtil::readArrayChunk(dataChunk, outData->m_externalOperands)); - break; - } - case SerialBinary::kStringTableFourCc: + case kIROp_BoolLit: + case kIROp_IntLit: { - SLANG_RETURN_ON_FAIL( - SerialRiffUtil::readArrayChunk(dataChunk, outData->m_stringTable)); - break; + serialize(serializer, constant->value.intVal); } - case Bin::kUInt32RawSourceLocFourCc: + break; + case kIROp_FloatLit: { - SLANG_RETURN_ON_FAIL( - SerialRiffUtil::readArrayChunk(dataChunk, outData->m_rawSourceLocs)); - break; + serialize(serializer, constant->value.intVal); } - case Bin::kDebugSourceLocRunFourCc: + break; + case kIROp_PtrLit: { - SLANG_RETURN_ON_FAIL( - SerialRiffUtil::readArrayChunk(dataChunk, outData->m_debugSourceLocRuns)); - break; + // Clang gets upset using intptr_t here, due to long and long + // long being distinct types + auto i = reinterpret_cast<UInt64>(constant->value.ptrVal); + serialize(serializer, i); + constant->value.ptrVal = reinterpret_cast<void*>(i); } + break; + case kIROp_StringLit: + case kIROp_BlobLit: + // Since we had to read the string anyway to get the length in + // serializeObject for this instruction, the string contents + // have already been filled in, nothing more to do here. + break; + case kIROp_VoidLit: + break; default: - { - break; - } + SLANG_UNREACHABLE("unhandled constant"); } } - - return SLANG_OK; } -Result IRSerialReader::read( - const IRSerialData& data, - Session* session, - SerialSourceLocReader* sourceLocReader, - RefPtr<IRModule>& outModule) +// +// Handlers for IRModule, there is a little extra setup to do once top level +// entries are deserialized to set up m_mapMangledNameToGlobalInst, this is +// done at the end of readSerializedModuleIR +// +void serializeObject(IRSerializer const& serializer, IRModule*& value, IRModule*) { - // Only used in debug builds - [[maybe_unused]] typedef Ser::Inst::PayloadType PayloadType; + serializer.getContext()->handleIRModule(serializer, value); +} - m_serialData = &data; +void IRSerialWriteContext::handleIRModule(IRSerializer const& serializer, IRModule*& value) +{ + SLANG_SCOPED_SERIALIZER_STRUCT(serializer); + serialize(serializer, value->m_name); + serialize(serializer, value->m_moduleInst); +} - auto module = IRModule::create(session); - outModule = module; - m_module = module; +void IRSerialReadContext::handleIRModule(IRSerializer const& serializer, IRModule*& value) +{ + SLANG_SCOPED_SERIALIZER_STRUCT(serializer); + value = new IRModule{_session}; + SLANG_ASSERT(!_module); + _module = value; + serialize(serializer, value->m_name); + serialize(serializer, value->m_moduleInst); + value->m_moduleInst->module = value; +} - // Convert m_stringTable into StringSlicePool. - SerialStringTableUtil::decodeStringTable( - data.m_stringTable.getBuffer(), - data.m_stringTable.getCount(), - m_stringTable); +// +// Serialize Names via the name pool on the session, this is used just for the +// IRModule name member. +// +void serializeObject(IRSerializer const& serializer, Name*& value, Name*) +{ + serializer.getContext()->handleName(serializer, value); +} - // Each IR instruction has: - // - // * An opcode - // * Zero or more operands - // * Zero or more children - // - // Most instructions are entirely defined by those properties. - // - // The instructions that represent simple constants (integers, strings, etc.) are - // unique in that they have "payload" data that holds their value, instead of having - // any operands. - // - // The deserialization logic here is set up to handle an arbitrary configuration - // of IR instructions, which means it can handle cases where: - // - // * An instruction earlier in the serialized stream might refer to an instruction - // later in the stream, as one of its operands or (transitive) children. - // - // * An instruction in the stream transitively depends on itself via operand - // and/or child relationships. - // - // In order to handle these cases, deserialization proceeds in multiple passes. - // In the first pass, `IRInst`s are allocated for each instruction in the stream, - // based on their memory requirements (number of operands in the ordinary case - // and payload size in the case of simple constants). Subsequent passes then - // fill in the operands and/or children. - // - // Note that as a result of the strategy used here, it is not possible for the - // deserialization logic to interact with any systems for deduplication or - // simplification of instructions. An alternative version of the deserializer that - // uses the `IRBuilder` interface instead might be possible, but would need a - // plan for how to handle forward and/or circular references in the IR module. +void IRSerialWriteContext::handleName(IRSerializer const& serializer, Name*& value) +{ + serialize(serializer, value->text); +} - // Add all the instructions - List<IRInst*> insts; +void IRSerialReadContext::handleName(IRSerializer const& serializer, Name*& value) +{ + String text; + serialize(serializer, text); + value = _session->getNamePool()->getName(text); +} - const Index numInsts = data.m_insts.getCount(); +// +// {write,read}SerializedModuleIR() +// - SLANG_ASSERT(numInsts > 0); +void writeSerializedModuleIR( + RIFF::BuildCursor& cursor, + IRModule* irModule, + SerialSourceLocWriter* sourceLocWriter) +{ + // The flow here is very similar to writeSerializedModuleAST which is very + // well documented. - insts.setCount(numInsts); - insts[0] = nullptr; + IRModuleInfo moduleInfo{.module = irModule}; - // 0 holds null - // 1 holds the IRModuleInst + BlobBuilder blobBuilder; { - // Check that insts[1] is the module inst - const Ser::Inst& srcInst = data.m_insts[1]; - SLANG_RELEASE_ASSERT(srcInst.m_op == kIROp_ModuleInst); - SLANG_ASSERT(srcInst.m_payloadType == PayloadType::Empty); - - // The root IR instruction for the module will already have - // been created as part of creating `module` above. - // - auto moduleInst = module->getModuleInst(); - - // Set the IRModuleInst - insts[1] = moduleInst; + Fossil::SerialWriter writer(blobBuilder); + IRSerialWriteContext context{sourceLocWriter}; + IRSerializer serializer(&writer, &context); + serialize(serializer, moduleInfo); } - for (Index i = 2; i < numInsts; ++i) - { - const Ser::Inst& srcInst = data.m_insts[i]; - - const IROp op((IROp)srcInst.m_op); - - if (_isConstant(op)) - { - // Handling of constants - - // Calculate the minimum object size (ie not including the payload of value) - const size_t prefixSize = SLANG_OFFSET_OF(IRConstant, value); + ComPtr<ISlangBlob> blob; + blobBuilder.writeToBlob(blob.writeRef()); - // All IR constants have zero operands. - Int operandCount = 0; - - IRConstant* irConst = nullptr; - switch (op) - { - case kIROp_BoolLit: - { - // TODO: Most of these cases could use the templated `_allocateInst<T>` - // *if* we had distinct `IRConstant` subtypes to represent these - // cases and their subtype-specific payloads. - - SLANG_ASSERT(srcInst.m_payloadType == PayloadType::UInt32); - irConst = static_cast<IRConstant*>(module->_allocateInst( - op, - operandCount, - prefixSize + sizeof(IRIntegerValue))); - irConst->value.intVal = srcInst.m_payload.m_uint32 != 0; - break; - } - case kIROp_IntLit: - { - SLANG_ASSERT(srcInst.m_payloadType == PayloadType::Int64); - irConst = static_cast<IRConstant*>(module->_allocateInst( - op, - operandCount, - prefixSize + sizeof(IRIntegerValue))); - irConst->value.intVal = srcInst.m_payload.m_int64; - break; - } - case kIROp_PtrLit: - { - SLANG_ASSERT(srcInst.m_payloadType == PayloadType::Int64); - irConst = static_cast<IRConstant*>( - module->_allocateInst(op, operandCount, prefixSize + sizeof(void*))); - irConst->value.ptrVal = (void*)(intptr_t)srcInst.m_payload.m_int64; - break; - } - case kIROp_FloatLit: - { - SLANG_ASSERT(srcInst.m_payloadType == PayloadType::Float64); - irConst = static_cast<IRConstant*>(module->_allocateInst( - op, - operandCount, - prefixSize + sizeof(IRFloatingPointValue))); - irConst->value.floatVal = srcInst.m_payload.m_float64; - break; - } - case kIROp_VoidLit: - { - SLANG_ASSERT(srcInst.m_payloadType == PayloadType::Empty); - irConst = static_cast<IRConstant*>( - module->_allocateInst(op, operandCount, prefixSize)); - break; - } - case kIROp_BlobLit: - case kIROp_StringLit: - { - SLANG_ASSERT(srcInst.m_payloadType == PayloadType::String_1); - - const UnownedStringSlice slice = m_stringTable.getSlice( - StringSlicePool::Handle(srcInst.m_payload.m_stringIndices[0])); - - const size_t sliceSize = slice.getLength(); - const size_t instSize = - prefixSize + SLANG_OFFSET_OF(IRConstant::StringValue, chars) + sliceSize; - - irConst = - static_cast<IRConstant*>(module->_allocateInst(op, operandCount, instSize)); - - IRConstant::StringValue& dstString = irConst->value.stringVal; - - dstString.numChars = uint32_t(sliceSize); - // Turn into pointer to avoid warning of array overrun - char* dstChars = dstString.chars; - // Copy the chars - memcpy(dstChars, slice.begin(), sliceSize); - break; - } - default: - { - SLANG_ASSERT(!"Unknown constant type"); - return SLANG_FAIL; - } - } - - insts[i] = irConst; - } - else - { - int numOperands = srcInst.getNumOperands(); - insts[i] = module->_allocateInst(op, numOperands); - } - } + void const* data = blob->getBufferPointer(); + size_t size = blob->getBufferSize(); + cursor.addDataChunk(PropertyKeys<IRModule>::IRModule, data, size); +} - // Patch up the operands - for (Index i = 1; i < numInsts; ++i) +// +// Read a module, this currently does not do any on-demand loading +// +void readSerializedModuleIR( + RIFF::Chunk const* chunk, + Session* session, + SerialSourceLocReader* sourceLocReader, + RefPtr<IRModule>& outIRModule) +{ + auto dataChunk = as<RIFF::DataChunk>(chunk); + if (!dataChunk) { - const Ser::Inst& srcInst = data.m_insts[i]; - const IROp op((IROp)srcInst.m_op); - SLANG_UNUSED(op); - - IRInst* dstInst = insts[i]; - - // Set the result type - if (srcInst.m_resultTypeIndex != Ser::InstIndex(0)) - { - IRInst* resultInst = insts[int(srcInst.m_resultTypeIndex)]; - // NOTE! Counter intuitively the IRType* paramter may not be IRType* derived for example - // IRGlobalGenericParam is valid, but isn't IRType* derived - - // SLANG_RELEASE_ASSERT(as<IRType>(resultInst)); - dstInst->setFullType(static_cast<IRType*>(resultInst)); - } - - // if (!isParentDerived(op)) - { - const Ser::InstIndex* srcOperandIndices; - const int numOperands = data.getOperands(srcInst, &srcOperandIndices); - - auto dstOperands = dstInst->getOperands(); - - for (int j = 0; j < numOperands; j++) - { - dstOperands[j].init(dstInst, insts[int(srcOperandIndices[j])]); - } - } + SLANG_UNEXPECTED("invalid format for serialized module IR"); } - // Patch up the children + Fossil::AnyValPtr rootValPtr = + Fossil::getRootValue(dataChunk->getPayload(), dataChunk->getPayloadSize()); + if (!rootValPtr) { - const Index numChildRuns = data.m_childRuns.getCount(); - for (Index i = 0; i < numChildRuns; i++) - { - const auto& run = data.m_childRuns[i]; - - IRInst* inst = insts[int(run.m_parentIndex)]; - - for (int j = 0; j < int(run.m_numChildren); ++j) - { - IRInst* child = insts[j + int(run.m_startInstIndex)]; - SLANG_ASSERT(child->parent == nullptr); - child->insertAtEnd(inst); - } - } + SLANG_UNEXPECTED("invalid format for serialized module IR"); } - // Re-add source locations, if they are defined - if (m_serialData->m_rawSourceLocs.getCount() == numInsts) + IRModuleInfo info; { - const Ser::RawSourceLoc* srcLocs = m_serialData->m_rawSourceLocs.begin(); - for (Index i = 1; i < numInsts; ++i) - { - IRInst* dstInst = insts[i]; - - dstInst->sourceLoc.setRaw(Slang::SourceLoc::RawValue(srcLocs[i])); - } + auto sharedDecodingContext = RefPtr(new IRSerialReadContext(session, sourceLocReader)); + Fossil::ReadContext readContext; + Fossil::SerialReader reader( + readContext, + rootValPtr, + Fossil::SerialReader::InitialStateType::Root); + + IRSerializer serializer(&reader, sharedDecodingContext); + serialize(serializer, info); } + SLANG_ASSERT(info.module); - // We now need to apply the runs - if (sourceLocReader && m_serialData->m_debugSourceLocRuns.getCount()) + // + // Now that everything is loaded, we can traverse the module and fix up the + // parents which we didn't do before because due to deferred + // deserialization we didn't necessarily have this information handy at the + // time. + // + auto go = [](auto&& go, IRInst* parent, IRInst* inst) -> void { - List<IRSerialData::SourceLocRun> sourceRuns(m_serialData->m_debugSourceLocRuns); - // They are now in source location order - sourceRuns.sort(); - - // Just guess initially 0 for the source file that contains the initial run - SerialSourceLocData::SourceRange range = SerialSourceLocData::SourceRange::getInvalid(); - int fix = 0; - - const Index numRuns = sourceRuns.getCount(); - for (Index i = 0; i < numRuns; ++i) - { - const auto& run = sourceRuns[i]; - - // Work out the fixed source location - SourceLoc sourceLoc; - if (run.m_sourceLoc) - { - if (!range.contains(run.m_sourceLoc)) - { - fix = sourceLocReader->calcFixSourceLoc(run.m_sourceLoc, range); - } - sourceLoc = sourceLocReader->calcFixedLoc(run.m_sourceLoc, fix, range); - } - - // Write to all the instructions - SLANG_ASSERT(Index(uint32_t(run.m_startInstIndex) + run.m_numInst) <= insts.getCount()); - IRInst** dstInsts = insts.getBuffer() + int(run.m_startInstIndex); - - const int runSize = int(run.m_numInst); - for (int j = 0; j < runSize; ++j) - { - dstInsts[j]->sourceLoc = sourceLoc; - } - } - } - - outModule->buildMangledNameToGlobalInstMap(); + inst->parent = parent; + for (const auto child : inst->getDecorationsAndChildren()) + go(go, inst, child); + }; + go(go, nullptr, info.module->getModuleInst()); - return SLANG_OK; + // + // Module is finally valid (or at least as much as it was going it) and + // ready to be used + // + info.module->buildMangledNameToGlobalInstMap(); + outIRModule = info.module; } } // namespace Slang diff --git a/source/slang/slang-serialize-ir.h b/source/slang/slang-serialize-ir.h index f6ab7e9ad..0ac188df7 100644 --- a/source/slang/slang-serialize-ir.h +++ b/source/slang/slang-serialize-ir.h @@ -1,121 +1,24 @@ -// slang-serialize-ir.h -#ifndef SLANG_SERIALIZE_IR_H_INCLUDED -#define SLANG_SERIALIZE_IR_H_INCLUDED +#pragma once -#include "../core/slang-riff.h" -#include "slang-ir.h" -#include "slang-serialize-ir-types.h" -#include "slang-serialize-source-loc.h" - -// For TranslationUnitRequest -// and FrontEndCompileRequest::ExtraEntryPointInfo -#include "slang-compiler.h" +#include "core/slang-riff.h" namespace Slang { -struct IRModuleChunk : RIFF::ListChunk -{ -}; - -struct IRSerialWriter -{ - typedef IRSerialData Ser; - typedef IRSerialBinary Bin; - - Result write( - IRModule* module, - SerialSourceLocWriter* sourceLocWriter, - IRSerialData* serialData); - - /// Write to a container - static Result writeTo(const IRSerialData& data, RIFF::BuildCursor& cursor); - - /// Get an instruction index from an instruction - Ser::InstIndex getInstIndex(IRInst* inst) const - { - return inst ? Ser::InstIndex(m_instMap.getValue(inst)) : Ser::InstIndex(0); - } - - /// Get a slice from an index - UnownedStringSlice getStringSlice(Ser::StringIndex index) const - { - return m_stringSlicePool.getSlice(StringSlicePool::Handle(index)); - } - /// Get index from string representations - Ser::StringIndex getStringIndex(StringRepresentation* string) - { - return Ser::StringIndex(m_stringSlicePool.add(string)); - } - Ser::StringIndex getStringIndex(const UnownedStringSlice& slice) - { - return Ser::StringIndex(m_stringSlicePool.add(slice)); - } - Ser::StringIndex getStringIndex(Name* name) - { - return name ? getStringIndex(name->text) : SerialStringData::kNullStringIndex; - } - Ser::StringIndex getStringIndex(const char* chars) - { - return Ser::StringIndex(m_stringSlicePool.add(chars)); - } - Ser::StringIndex getStringIndex(const String& string) - { - return Ser::StringIndex(m_stringSlicePool.add(string.getUnownedSlice())); - } - - StringSlicePool& getStringPool() { return m_stringSlicePool; } - - IRSerialWriter() - : m_serialData(nullptr), m_stringSlicePool(StringSlicePool::Style::Default) - { - } - - /// Produces an instruction list which is in same order as written through IRSerialWriter - static void calcInstructionList(IRModule* module, List<IRInst*>& instsOut); -protected: - void _addInstruction(IRInst* inst); - Result _calcDebugInfo(SerialSourceLocWriter* sourceLocWriter); +struct IRModule; +class Session; +class SerialSourceLocReader; +class SerialSourceLocWriter; - List<IRInst*> m_insts; ///< Instructions in same order as stored in the +void writeSerializedModuleIR( + RIFF::BuildCursor& cursor, + IRModule* moduleDecl, + SerialSourceLocWriter* sourceLocWriter); - List<IRDecoration*> - m_decorations; ///< Holds all decorations in order of the instructions as found - List<IRInst*> m_instWithFirstDecoration; ///< All decorations are held in this order after all - ///< the regular instructions - - Dictionary<IRInst*, Ser::InstIndex> m_instMap; ///< Map an instruction to an instruction index - - StringSlicePool m_stringSlicePool; - IRSerialData* m_serialData; ///< Where the data is stored -}; - -struct IRSerialReader -{ - typedef IRSerialData Ser; - - /// Read a stream to fill in dataOut IRSerialData - static Result readFrom(IRModuleChunk const* irModuleChunk, IRSerialData* outData); - - /// Read a module from serial data - Result read( - const IRSerialData& data, - Session* session, - SerialSourceLocReader* sourceLocReader, - RefPtr<IRModule>& outModule); - - IRSerialReader() - : m_serialData(nullptr), m_module(nullptr), m_stringTable(StringSlicePool::Style::Default) - { - } - -protected: - StringSlicePool m_stringTable; - - const IRSerialData* m_serialData; - IRModule* m_module; -}; +void readSerializedModuleIR( + RIFF::Chunk const* chunk, + Session* session, + SerialSourceLocReader* sourceLocReader, + RefPtr<IRModule>& outIRModule); } // namespace Slang - -#endif diff --git a/source/slang/slang-serialize-source-loc.h b/source/slang/slang-serialize-source-loc.h index c6ef982c1..ea2a06d96 100644 --- a/source/slang/slang-serialize-source-loc.h +++ b/source/slang/slang-serialize-source-loc.h @@ -8,6 +8,7 @@ #include "../core/slang-riff.h" #include "../core/slang-string-slice-pool.h" #include "slang-serialize-types.h" +#include "slang-serialize.h" namespace Slang { @@ -233,6 +234,81 @@ public: Dictionary<SourceFile*, RefPtr<Source>> m_sourceFileMap; }; +// A class a custom serialization context can inherit from to enable +// serializing SourceLoc +struct SourceLocSerialContext +{ + virtual SerialSourceLocReader* getSourceLocReader() { return nullptr; } + virtual SerialSourceLocWriter* getSourceLocWriter() { return nullptr; } +}; + +template<typename S> +void serialize(S const& serializer, SourceLoc& value) +{ + static_assert( + std::is_convertible_v<decltype(serializer.getContext()), SourceLocSerialContext*>, + "getContext() must return a SourceLocSerialContext*"); + + SourceLocSerialContext* context = serializer.getContext(); + + // Writing of source location information can be disabled by + // compiler options, and in that case the `_sourceLocWriter` + // may be null. + // + // In order to handle that possibility, we serialize a `SourceLoc` + // as an optional value, dependent on whether we have a + // `_sourceLocWriter` that can be used. + // + SLANG_SCOPED_SERIALIZER_OPTIONAL(serializer); + if (isWriting(serializer)) + { + // The `SourceLoc` type is implemented under the hood as an + // integer offset that can only be decoded using the specific + // `SourceManager` that created it. + // + // The source location writer handles the task of translating + // the under-the-hood representation to a single integer value + // (represented as `SerialSourceLocData::SourceLoc`) that can + // be decoded on the other side using other data that the + // source location writer will write out as part of its own + // representation (all of which goes into the dedicated debug + // data chunk, distinct from the AST/IR module). + // + if (const auto sourceLocWriter = context->getSourceLocWriter()) + { + SerialSourceLocData::SourceLoc rawValue = sourceLocWriter->addSourceLoc(value); + serialize(serializer, rawValue); + } + } + else + { + if (hasElements(serializer)) + { + SerialSourceLocData::SourceLoc rawValue; + serialize(serializer, rawValue); + + // Even if the serialized optional had a value, it is + // possible that the debug-data chunk got stripped from + // the compiled module file, in which case we wouldn't + // have access to the data needed to decode it. + // + // In that case, the `_sourceLocReader` member would be + // null, so we handle that possibility here. + // + if (const auto sourceLocReader = context->getSourceLocReader()) + { + value = sourceLocReader->getSourceLoc(rawValue); + } + } + } +} + +// Now that we've seen the relevant serialization logic, it is clear that +// a `SourceLoc` gets fossilized the same way that an optional wrapping +// an integer (of type `SerialSourceLocData::SourceLoc`) would. +// +SLANG_DECLARE_FOSSILIZED_AS(SourceLoc, std::optional<SerialSourceLocData::SourceLoc>); + } // namespace Slang #endif diff --git a/source/slang/slang-serialize-types.h b/source/slang/slang-serialize-types.h index be49be1ea..ca54848c2 100644 --- a/source/slang/slang-serialize-types.h +++ b/source/slang/slang-serialize-types.h @@ -5,6 +5,7 @@ #include "../core/slang-array-view.h" #include "../core/slang-riff.h" #include "../core/slang-string-slice-pool.h" +#include "slang-ir.h" // #include "slang-name.h" // #include "slang-source-loc.h" @@ -109,6 +110,12 @@ struct PropertyKeys<Module> static const FourCC::RawValue FileDependencies = SLANG_FOUR_CC('f', 'd', 'e', 'p'); }; +template<> +struct PropertyKeys<IRModule> +{ + static const FourCC::RawValue IRModule = SLANG_FOUR_CC('i', 'r', ' ', ' '); +}; + // For types/FourCC that work for serializing in general (not just IR). struct SerialBinary { diff --git a/source/slang/slang.cpp b/source/slang/slang.cpp index e76ff7436..b616eb555 100644 --- a/source/slang/slang.cpp +++ b/source/slang/slang.cpp @@ -763,7 +763,7 @@ SlangResult Session::_readBuiltinModule( // to deserialize the IR module. // RefPtr<IRModule> irModule; - SLANG_RETURN_ON_FAIL(decodeModuleIR(irModule, irChunk, this, sourceLocReader)); + readSerializedModuleIR(irChunk, this, sourceLocReader, irModule); irModule->setName(module->getNameObj()); module->setIRModule(irModule); @@ -6801,7 +6801,7 @@ SlangResult Linkage::loadSerializedModuleContents( module->setModuleDecl(moduleDecl); RefPtr<IRModule> irModule; - SLANG_RETURN_ON_FAIL(decodeModuleIR(irModule, irChunk, session, sourceLocReader)); + readSerializedModuleIR(irChunk, session, sourceLocReader, irModule); module->setIRModule(irModule); // The handling of file dependencies is complicated, because of |
