// ir-serialize.cpp #include "ir-serialize.h" #include "../core/text-io.h" #include "ir-insts.h" namespace Slang { // Needed for linkage with some compilers /* static */ const IRSerialData::StringIndex IRSerialData::kNullStringIndex; /* static */ const IRSerialData::StringIndex IRSerialData::kEmptyStringIndex; /* Note that an IRInst can be derived from, but when it derived from it's new members are IRUse variables, and they in effect alias over the operands - and reflected in the operand count. There _could_ be other members after these IRUse variables, but in practice there do not appear to be. The only difference to this is IRParentInst derived types, as it contains IRInstListBase children. Thus IRParentInst derived classes can have no operands - because it would write over the top of IRInstListBase. BUT they can contain members after the list types which do this are * IRModuleInst - Presumably we can just set to the module pointer on reconstruction * IRGlobalValue - There are types derived from this type, but they don't add a parameter Note! That on an IRInst there is an IRType* variable (accessed as getFullType()). As it stands it may NOT actually point to an IRType derived type. Its 'ok' as long as it's an instruction that can be used in the place of the type. So this code does not bother to check if it's correct, and just casts it. */ /* static */const IRSerialData::PayloadInfo IRSerialData::s_payloadInfos[int(Inst::PayloadType::CountOf)] = { { 0, 0 }, // Empty { 1, 0 }, // Operand_1 { 2, 0 }, // Operand_2 { 1, 0 }, // OperandAndUInt32, { 0, 0 }, // OperandExternal - This isn't correct, Operand has to be specially handled { 0, 1 }, // String_1, { 0, 2 }, // String_2, { 0, 0 }, // UInt32, { 0, 0 }, // Float64, { 0, 0 } // Int64, }; static bool isParentDerived(IROp opIn) { const int op = (kIROpMeta_PseudoOpMask & opIn); return op >= kIROp_FirstParentInst && op <= kIROp_LastParentInst; } static bool isGlobalValueDerived(IROp opIn) { const int op = (kIROpMeta_PseudoOpMask & opIn); return op >= kIROp_FirstGlobalValue && op <= kIROp_LastGlobalValue; } static bool isTextureTypeBase(IROp opIn) { const int op = (kIROpMeta_PseudoOpMask & opIn); return op >= kIROp_FirstTextureTypeBase && op <= kIROp_LastTextureTypeBase; } static bool isConstant(IROp opIn) { const int op = (kIROpMeta_PseudoOpMask & opIn); return op >= kIROp_FirstConstant && op <= kIROp_LastConstant; } struct PrefixString; namespace { // anonymous struct CharReader { char operator()(int pos) const { SLANG_UNUSED(pos); return *m_pos++; } CharReader(const char* pos) :m_pos(pos) {} mutable const char* m_pos; }; } // anonymous static UnownedStringSlice asStringSlice(const PrefixString* prefixString) { const char* prefix = (char*)prefixString; CharReader reader(prefix); const int len = GetUnicodePointFromUTF8(reader); return UnownedStringSlice(reader.m_pos, len); } // !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! IRSerialData !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! template static size_t _calcArraySize(const List& list) { return list.Count() * sizeof(T); } size_t IRSerialData::calcSizeInBytes() const { return _calcArraySize(m_insts) + _calcArraySize(m_childRuns) + _calcArraySize(m_decorationRuns) + _calcArraySize(m_externalOperands) + _calcArraySize(m_rawSourceLocs) + _calcArraySize(m_strings); } void IRSerialData::clear() { // First Instruction is null m_insts.SetSize(1); memset(&m_insts[0], 0, sizeof(Inst)); m_childRuns.Clear(); m_decorationRuns.Clear(); m_externalOperands.Clear(); m_rawSourceLocs.Clear(); m_strings.SetSize(2); m_strings[int(kNullStringIndex)] = 0; m_strings[int(kEmptyStringIndex)] = 0; m_decorationBaseIndex = 0; } // !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! IRSerialWriter !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! void IRSerialWriter::_addInstruction(IRInst* inst) { // 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.Count())); m_insts.Add(inst); // Add all the decorations, to the list. // We don't add to the decoration map, as we only want to do this once all the instructions have been hit if (inst->firstDecoration) { m_instWithFirstDecoration.Add(inst); IRDecoration* decor = inst->firstDecoration; const int initialNumDecor = int(m_decorations.Count()); while (decor) { m_decorations.Add(decor); decor = decor->next; } const Ser::SizeType numDecor = Ser::SizeType(int(m_decorations.Count()) - initialNumDecor); Ser::InstRun run; run.m_parentIndex = m_instMap[inst]; // NOTE! This isn't quite correct, as we need to correct for when all the instructions are added, this is done at the end run.m_startInstIndex = Ser::InstIndex(initialNumDecor); run.m_numChildren = numDecor; m_serialData->m_decorationRuns.Add(run); } } IRSerialData::StringIndex IRSerialWriter::getStringIndex(Name* name) { return name ? getStringIndex(name->text.getStringRepresentation()) : Ser::kNullStringIndex; } UnownedStringSlice IRSerialWriter::getStringSlice(Ser::StringIndex index) const { Ser::StringOffset offset = m_stringStarts[int(index)]; return asStringSlice((const PrefixString*)(m_serialData->m_strings.begin() + int(offset))); } Result IRSerialWriter::write(IRModule* module, SourceManager* sourceManager, OptionFlags options, IRSerialData* serialData) { typedef Ser::Inst::PayloadType PayloadType; SLANG_UNUSED(sourceManager); m_serialData = serialData; serialData->clear(); // Set up the stringStarts m_stringStarts.SetSize(2); m_stringStarts[0] = Ser::StringOffset(0); // Null m_stringStarts[1] = Ser::StringOffset(1); // Empty SLANG_ASSERT(serialData->m_strings.Count() == 2); // We'll keep StringRepresentations in scope (and for simplicity keep in order of StringIndex) m_scopeStrings.SetSize(2); m_scopeStrings[0] = nullptr; m_scopeStrings[1] = nullptr; m_stringMap.Clear(); // Add the empty string index. // We can't add the null string index, because that doesn't have any meaning as an UnownedStringSlice m_stringMap.Add(UnownedStringSlice(""), Ser::kEmptyStringIndex); // We reserve 0 for null m_insts.Clear(); m_insts.Add(nullptr); // Reset m_instMap.Clear(); m_decorations.Clear(); // Stack for parentInst List parentInstStack; IRModuleInst* moduleInst = module->getModuleInst(); parentInstStack.Add(moduleInst); // Add to the map _addInstruction(moduleInst); // Traverse all of the instructions while (parentInstStack.Count()) { // If it's in the stack it is assumed it is already in the inst map IRParentInst* parentInst = parentInstStack.Last(); 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.Count()); IRInstListBase childrenList = parentInst->getChildren(); for (IRInst* child : childrenList) { // This instruction can't be in the map... SLANG_ASSERT(!m_instMap.ContainsKey(child)); _addInstruction(child); IRParentInst* childAsParent = as(child); if (childAsParent) { parentInstStack.Add(childAsParent); } } // If it had any children, then store the information about it if (Ser::InstIndex(m_insts.Count()) != startChildInstIndex) { Ser::InstRun run; run.m_parentIndex = m_instMap[parentInst]; run.m_startInstIndex = startChildInstIndex; run.m_numChildren = Ser::SizeType(m_insts.Count() - int(startChildInstIndex)); m_serialData->m_childRuns.Add(run); } } // Now fix the decorations { const int decorationBaseIndex = int(m_insts.Count()); m_serialData->m_decorationBaseIndex = decorationBaseIndex; const int numDecorRuns = int(m_serialData->m_decorationRuns.Count()); // Work out the total num of decoration int totalNumDecorations = 0; if (numDecorRuns) { const auto& lastDecorInfo = m_serialData->m_decorationRuns.Last(); totalNumDecorations = int(lastDecorInfo.m_startInstIndex) + lastDecorInfo.m_numChildren; } // Fix the indices for (int i = 0; i < numDecorRuns; ++i) { Ser::InstRun& info = m_serialData->m_decorationRuns[i]; info.m_startInstIndex = Ser::InstIndex(decorationBaseIndex + int(info.m_startInstIndex)); } // Set to the right size m_serialData->m_insts.SetSize(decorationBaseIndex + totalNumDecorations); // Clear all instructions memset(m_serialData->m_insts.begin(), 0, sizeof(Ser::Inst) * m_serialData->m_insts.Count()); } // Need to set up the actual instructions { const int numInsts = int(m_insts.Count()); for (int i = 1; i < numInsts; ++i) { IRInst* srcInst = m_insts[i]; Ser::Inst& dstInst = m_serialData->m_insts[i]; // Can't be any pseudo ops SLANG_ASSERT(!isPseudoOp(srcInst->op)); dstInst.m_op = uint8_t(srcInst->op & kIROpMeta_OpMask); dstInst.m_payloadType = PayloadType::Empty; dstInst.m_resultTypeIndex = getInstIndex(srcInst->getFullType()); IRConstant* irConst = as(srcInst); if (irConst) { switch (srcInst->op) { // Special handling for the ir const derived types case kIROp_StringLit: { auto stringLit = static_cast(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_FloatLit: { dstInst.m_payloadType = PayloadType::Float64; dstInst.m_payload.m_float64 = irConst->value.floatVal; break; } case kIROp_boolConst: { dstInst.m_payloadType = PayloadType::UInt32; dstInst.m_payload.m_uint32 = irConst->value.intVal ? 1 : 0; break; } default: { SLANG_RELEASE_ASSERT(!"Unhandled constant type"); return SLANG_FAIL; } } continue; } IRGlobalValue* globValue = as(srcInst); if (globValue) { dstInst.m_payloadType = PayloadType::String_1; dstInst.m_payload.m_stringIndices[0] = getStringIndex(globValue->mangledName); continue; } IRTextureTypeBase* textureBase = as(srcInst); if (textureBase) { dstInst.m_payloadType = PayloadType::OperandAndUInt32; dstInst.m_payload.m_operandAndUInt32.m_uint32 = uint32_t(srcInst->op) >> kIROpMeta_OtherShift; dstInst.m_payload.m_operandAndUInt32.m_operand = getInstIndex(textureBase->getElementType()); 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 { dstInst.m_payloadType = PayloadType::OperandExternal; int operandArrayBaseIndex = int(m_serialData->m_externalOperands.Count()); m_serialData->m_externalOperands.SetSize(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); } for (int j = 0; j < numOperands; ++j) { const Ser::InstIndex dstInstIndex = getInstIndex(srcInst->getOperand(j)); dstOperands[j] = dstInstIndex; } } } // Now need to do the decorations { const int decorationBaseIndex = m_serialData->m_decorationBaseIndex; const int numDecor = int(m_decorations.Count()); SLANG_ASSERT(decorationBaseIndex + numDecor == int(m_serialData->m_insts.Count())); // Have to be able to store in a byte! SLANG_COMPILE_TIME_ASSERT(kIROpCount + kIRDecorationOp_CountOf < 0x100); for (int i = 0; i < numDecor; ++i) { IRDecoration* srcDecor = m_decorations[i]; Ser::Inst& dstInst = m_serialData->m_insts[decorationBaseIndex + i]; dstInst.m_op = uint8_t(kIROpCount + srcDecor->op); switch (srcDecor->op) { case kIRDecorationOp_HighLevelDecl: { // TODO! // Decl* decl; break; } case kIRDecorationOp_Layout: { // TODO! // Layout* layout; break; } case kIRDecorationOp_LoopControl: { auto loopDecor = static_cast(srcDecor); dstInst.m_payloadType = PayloadType::UInt32; dstInst.m_payload.m_uint32 = uint32_t(loopDecor->mode); break; } case kIRDecorationOp_Target: { auto targetDecor = static_cast(srcDecor); dstInst.m_payloadType = PayloadType::String_1; dstInst.m_payload.m_stringIndices[0] = getStringIndex(targetDecor->targetName); break; } case kIRDecorationOp_TargetIntrinsic: { auto targetDecor = static_cast(srcDecor); dstInst.m_payloadType = PayloadType::String_2; dstInst.m_payload.m_stringIndices[0] = getStringIndex(targetDecor->targetName); dstInst.m_payload.m_stringIndices[1] = getStringIndex(targetDecor->definition); break; } case kIRDecorationOp_GLSLOuterArray: { auto arrayDecor = static_cast(srcDecor); dstInst.m_payloadType = PayloadType::String_1; dstInst.m_payload.m_stringIndices[0] = getStringIndex(arrayDecor->outerArrayName); break; } case kIRDecorationOp_Semantic: { auto semanticDecor = static_cast(srcDecor); dstInst.m_payloadType = PayloadType::String_1; dstInst.m_payload.m_stringIndices[0] = getStringIndex(semanticDecor->semanticName); break; } case kIRDecorationOp_InterpolationMode: { auto semanticDecor = static_cast(srcDecor); dstInst.m_payloadType = PayloadType::UInt32; dstInst.m_payload.m_uint32 = uint32_t(semanticDecor->mode); break; } case kIRDecorationOp_NameHint: { auto nameDecor = static_cast(srcDecor); dstInst.m_payloadType = PayloadType::String_1; dstInst.m_payload.m_stringIndices[0] = getStringIndex(nameDecor->name); break; } default: { SLANG_ASSERT(!"Unhandled decoration type"); return SLANG_FAIL; } } } } // If the option to use RawSourceLocations is enabled, serialize out as is if (options & OptionFlag::RawSourceLocation) { const int numInsts = int(m_insts.Count()); serialData->m_rawSourceLocs.SetSize(numInsts); Ser::RawSourceLoc* dstLocs = serialData->m_rawSourceLocs.begin(); // 0 is null, just mark as no location dstLocs[0] = Ser::RawSourceLoc(0); for (int i = 1; i < numInsts; ++i) { IRInst* srcInst = m_insts[i]; dstLocs[i] = Ser::RawSourceLoc(srcInst->sourceLoc.getRaw()); } } m_serialData = nullptr; return SLANG_OK; } IRSerialData::StringIndex IRSerialWriter::getStringIndex(StringRepresentation* rep) { if (rep == nullptr) { return Ser::kNullStringIndex; } const UnownedStringSlice slice(rep->getData(), rep->getLength()); const int len = int(rep->getLength()); Ser::StringIndex index; if (m_stringMap.TryGetValue(slice, index)) { return index; } // We need to write into the the string array char prefixBytes[6]; const int numPrefixBytes = EncodeUnicodePointToUTF8(prefixBytes, len); const int baseIndex = int(m_serialData->m_strings.Count()); m_serialData->m_strings.SetSize(baseIndex + numPrefixBytes + len); char* dst = m_serialData->m_strings.begin() + baseIndex; memcpy(dst, prefixBytes, numPrefixBytes); memcpy(dst + numPrefixBytes, slice.begin(), len); // We need to add 1, because the 0 is used for null, which is not in the map const Ser::StringIndex stringIndex = Ser::StringIndex(m_stringMap.Count() + 1); SLANG_ASSERT(stringIndex == Ser::StringIndex(m_scopeStrings.Count())); // Make sure the rep stays in scope (because the UnownedStringSlice is pointing to it's contents) m_scopeStrings.Add(rep); // Add the start offset m_stringStarts.Add(Ser::StringOffset(baseIndex)); m_stringMap.Add(slice, stringIndex); return stringIndex; } IRSerialData::StringIndex IRSerialWriter::getStringIndex(const char* chars) { if (!chars) { return Ser::kNullStringIndex; } if (chars[0] == 0) { return Ser::kEmptyStringIndex; } // Get as a StringRepresentation String string(chars); return getStringIndex(string.getStringRepresentation()); } IRSerialData::StringIndex IRSerialWriter::getStringIndex(const UnownedStringSlice& slice) { const int len = int(slice.size()); if (len <= 0) { return Ser::kEmptyStringIndex; } String string(slice); return getStringIndex(string.getStringRepresentation()); } template static size_t _calcChunkSize(const List& array) { typedef IRSerialBinary Bin; if (array.Count()) { const size_t size = sizeof(Bin::ArrayHeader) + sizeof(T) * array.Count(); return (size + 3) & ~size_t(3); } else { return 0; } } template Result _writeArrayChunk(uint32_t chunkId, const List& array, Stream* stream) { typedef IRSerialBinary Bin; if (array.Count() == 0) { return SLANG_OK; } size_t payloadSize = sizeof(Bin::ArrayHeader) - sizeof(Bin::Chunk) + sizeof(T) * array.Count(); Bin::ArrayHeader header; header.m_chunk.m_type = chunkId; header.m_chunk.m_size = uint32_t(payloadSize); header.m_numEntries = uint32_t(array.Count()); stream->Write(&header, sizeof(header)); stream->Write(array.begin(), sizeof(T) * array.Count()); // All chunks have sizes rounded to dword size if (payloadSize & 3) { const uint8_t pad[4] = { 0, 0, 0, 0 }; // Pad outs int padSize = 4 - (payloadSize & 3); stream->Write(pad, padSize); } return SLANG_OK; } /* static */Result IRSerialWriter::writeStream(const IRSerialData& data, Stream* stream) { typedef IRSerialBinary Bin; size_t totalSize = 0; totalSize += sizeof(Bin::SlangHeader) + _calcChunkSize(data.m_insts) + _calcChunkSize(data.m_childRuns) + _calcChunkSize(data.m_decorationRuns) + _calcChunkSize(data.m_externalOperands) + _calcChunkSize(data.m_strings) + _calcChunkSize(data.m_rawSourceLocs); { Bin::Chunk riffHeader; riffHeader.m_type = Bin::kRiffFourCc; riffHeader.m_size = uint32_t(totalSize); stream->Write(&riffHeader, sizeof(riffHeader)); } { Bin::SlangHeader slangHeader; slangHeader.m_chunk.m_type = Bin::kSlangFourCc; slangHeader.m_chunk.m_size = uint32_t(sizeof(slangHeader) - sizeof(Bin::Chunk)); slangHeader.m_decorationBase = uint32_t(data.m_decorationBaseIndex); stream->Write(&slangHeader, sizeof(slangHeader)); } _writeArrayChunk(Bin::kInstFourCc, data.m_insts, stream); _writeArrayChunk(Bin::kChildRunFourCc, data.m_childRuns, stream); _writeArrayChunk(Bin::kDecoratorRunFourCc, data.m_decorationRuns, stream); _writeArrayChunk(Bin::kExternalOperandsFourCc, data.m_externalOperands, stream); _writeArrayChunk(Bin::kStringFourCc, data.m_strings, stream); { uint32_t fourCc = sizeof(IRSerialData::RawSourceLoc) == 4 ? Bin::kUInt32SourceLocFourCc : Bin::kUInt64SourceLocFourCc; _writeArrayChunk(fourCc, data.m_rawSourceLocs, stream); } return SLANG_OK; } // !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! IRSerialReader !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! template Result _readArrayChunk(const IRSerialBinary::Chunk& chunk, Stream* stream, List& arrayOut) { typedef IRSerialBinary Bin; Bin::ArrayHeader header; header.m_chunk = chunk; stream->Read(&header.m_chunk + 1, sizeof(header) - sizeof(Bin::Chunk)); size_t payloadSize = sizeof(Bin::ArrayHeader) - sizeof(Bin::Chunk) + sizeof(T) * header.m_numEntries; if (payloadSize != header.m_chunk.m_size) { return SLANG_FAIL; } arrayOut.SetSize(header.m_numEntries); stream->Read(arrayOut.begin(), sizeof(T) * header.m_numEntries); // All chunks have sizes rounded to dword size if (payloadSize & 3) { const uint8_t pad[4] = { 0, 0, 0, 0 }; // Pad outs int padSize = 4 - (payloadSize & 3); stream->Seek(SeekOrigin::Current, padSize); } return SLANG_OK; } int64_t _calcChunkTotalSize(const IRSerialBinary::Chunk& chunk) { int64_t size = chunk.m_size + sizeof(IRSerialBinary::Chunk); return (size + 3) & ~int64_t(3); } /* static */Result IRSerialReader::_skip(const IRSerialBinary::Chunk& chunk, Stream* stream, int64_t* remainingBytesInOut) { typedef IRSerialBinary Bin; int64_t chunkSize = _calcChunkTotalSize(chunk); if (remainingBytesInOut) { *remainingBytesInOut -= chunkSize; } // Skip the payload (we don't need to skip the Chunk because that was already read stream->Seek(SeekOrigin::Current, chunkSize - sizeof(IRSerialBinary::Chunk)); return SLANG_OK; } /* static */Result IRSerialReader::readStream(Stream* stream, IRSerialData* dataOut) { typedef IRSerialBinary Bin; dataOut->clear(); int64_t remainingBytes = 0; { Bin::Chunk header; stream->Read(&header, sizeof(header)); if (header.m_type != Bin::kRiffFourCc) { return SLANG_FAIL; } remainingBytes = header.m_size; } while (remainingBytes > 0) { Bin::Chunk chunk; stream->Read(&chunk, sizeof(chunk)); switch (chunk.m_type) { case Bin::kSlangFourCc: { // Slang header Bin::SlangHeader header; header.m_chunk = chunk; // NOTE! Really we should only read what we know the size to be... // and skip if it's larger stream->Read(&header.m_chunk + 1, sizeof(header) - sizeof(chunk)); dataOut->m_decorationBaseIndex = header.m_decorationBase; remainingBytes -= _calcChunkTotalSize(chunk); break; } case Bin::kInstFourCc: { SLANG_RETURN_ON_FAIL(_readArrayChunk(chunk, stream, dataOut->m_insts)); remainingBytes -= _calcChunkTotalSize(chunk); break; } case Bin::kDecoratorRunFourCc: { SLANG_RETURN_ON_FAIL(_readArrayChunk(chunk, stream, dataOut->m_decorationRuns)); remainingBytes -= _calcChunkTotalSize(chunk); break; } case Bin::kChildRunFourCc: { SLANG_RETURN_ON_FAIL(_readArrayChunk(chunk, stream, dataOut->m_childRuns)); remainingBytes -= _calcChunkTotalSize(chunk); break; } case Bin::kExternalOperandsFourCc: { SLANG_RETURN_ON_FAIL(_readArrayChunk(chunk, stream, dataOut->m_externalOperands)); remainingBytes -= _calcChunkTotalSize(chunk); break; } case Bin::kStringFourCc: { SLANG_RETURN_ON_FAIL(_readArrayChunk(chunk, stream, dataOut->m_strings)); remainingBytes -= _calcChunkTotalSize(chunk); break; } case Bin::kUInt32SourceLocFourCc: case Bin::kUInt64SourceLocFourCc: { if ((sizeof(IRSerialData::RawSourceLoc) == 4 && chunk.m_type == Bin::kUInt32SourceLocFourCc) || (sizeof(IRSerialData::RawSourceLoc) == 8 && chunk.m_type == Bin::kUInt64SourceLocFourCc)) { SLANG_RETURN_ON_FAIL(_readArrayChunk(chunk, stream, dataOut->m_rawSourceLocs)); remainingBytes -= _calcChunkTotalSize(chunk); } else { SLANG_RETURN_ON_FAIL(_skip(chunk, stream, &remainingBytes)); } break; } default: { SLANG_RETURN_ON_FAIL(_skip(chunk, stream, &remainingBytes)); break; } } } return SLANG_OK; } Name* IRSerialReader::getName(Ser::StringIndex index) { if (index == Ser::kNullStringIndex) { return nullptr; } StringRepresentation* rep = getStringRepresentation(index); String string(rep); Session* session = m_module->getSession(); return session->getNameObj(string); } String IRSerialReader::getString(Ser::StringIndex index) { return String(getStringRepresentation(index)); } UnownedStringSlice IRSerialReader::getStringSlice(Ser::StringOffset offset) { return asStringSlice((PrefixString*)(m_serialData->m_strings.begin() + int(offset))); } StringRepresentation* IRSerialReader::getStringRepresentation(Ser::StringIndex index) { if (index == Ser::kNullStringIndex) { return nullptr; } StringRepresentation* rep = m_stringRepresentationCache[int(index)]; if (rep) { return rep; } const UnownedStringSlice slice = getStringSlice(index); String string(slice); StringRepresentation* stringRep = string.getStringRepresentation(); m_module->addRefObjectToFree(stringRep); m_stringRepresentationCache[int(index)] = stringRep; return stringRep; } char* IRSerialReader::getCStr(Ser::StringIndex index) { // It turns out StringRepresentation is always 0 terminated, so can just use that StringRepresentation* rep = getStringRepresentation(index); return rep->getData(); } void IRSerialReader::_calcStringStarts() { m_stringStarts.Clear(); const char* start = m_serialData->m_strings.begin(); const char* cur = start; const char* end = m_serialData->m_strings.end(); while (cur < end) { m_stringStarts.Add(Ser::StringOffset(cur - start)); CharReader reader(cur); const int len = GetUnicodePointFromUTF8(reader); cur = reader.m_pos + len; } m_stringRepresentationCache.Clear(); // Resize cache m_stringRepresentationCache.SetSize(m_stringStarts.Count()); // Make sure all values are null initially memset(m_stringRepresentationCache.begin(), 0, sizeof(StringRepresentation*) * m_stringStarts.Count()); } IRDecoration* IRSerialReader::_createDecoration(const Ser::Inst& srcInst) { typedef Ser::Inst::PayloadType PayloadType; IRDecorationOp decorOp = IRDecorationOp(srcInst.m_op - kIROpCount); SLANG_ASSERT(decorOp < kIRDecorationOp_CountOf); switch (decorOp) { case kIRDecorationOp_HighLevelDecl: { // TODO! // Decl* decl; return createEmptyDecoration(m_module); } case kIRDecorationOp_Layout: { // TODO! // Layout* layout; return createEmptyDecoration(m_module); } case kIRDecorationOp_LoopControl: { auto decor = createEmptyDecoration(m_module); SLANG_ASSERT(srcInst.m_payloadType == PayloadType::UInt32); decor->mode = IRLoopControl(srcInst.m_payload.m_uint32); return decor; } case kIRDecorationOp_Target: { auto decor = createEmptyDecoration(m_module); SLANG_ASSERT(srcInst.m_payloadType == PayloadType::String_1); decor->targetName = getStringRepresentation(srcInst.m_payload.m_stringIndices[0]); return decor; } case kIRDecorationOp_TargetIntrinsic: { auto decor = createEmptyDecoration(m_module); SLANG_ASSERT(srcInst.m_payloadType == PayloadType::String_2); decor->targetName = getStringRepresentation(srcInst.m_payload.m_stringIndices[0]); decor->definition = getStringRepresentation(srcInst.m_payload.m_stringIndices[1]); return decor; } case kIRDecorationOp_GLSLOuterArray: { auto decor = createEmptyDecoration(m_module); SLANG_ASSERT(srcInst.m_payloadType == PayloadType::String_1); decor->outerArrayName = getCStr(srcInst.m_payload.m_stringIndices[0]); return decor; } case kIRDecorationOp_Semantic: { auto decor = createEmptyDecoration(m_module); SLANG_ASSERT(srcInst.m_payloadType == PayloadType::String_1); decor->semanticName = getName(srcInst.m_payload.m_stringIndices[0]); return decor; } case kIRDecorationOp_InterpolationMode: { auto decor = createEmptyDecoration(m_module); SLANG_ASSERT(srcInst.m_payloadType == Ser::Inst::PayloadType::UInt32); decor->mode = IRInterpolationMode(srcInst.m_payload.m_uint32); return decor; } case kIRDecorationOp_NameHint: { auto decor = createEmptyDecoration(m_module); SLANG_ASSERT(srcInst.m_payloadType == PayloadType::String_1); decor->name = getName(srcInst.m_payload.m_stringIndices[0]); return decor; } default: { SLANG_ASSERT(!"Unhandled decoration type"); return nullptr; } } } /* static */Result IRSerialReader::read(const IRSerialData& data, Session* session, RefPtr& moduleOut) { typedef Ser::Inst::PayloadType PayloadType; m_serialData = &data; _calcStringStarts(); auto module = new IRModule(); moduleOut = module; m_module = module; module->session = session; // Add all the instructions List insts; List decorations; const int numInsts = data.m_decorationBaseIndex; const int numDecorations = int(data.m_insts.Count() - numInsts); SLANG_ASSERT(numInsts > 0); insts.SetSize(numInsts); insts[0] = nullptr; decorations.SetSize(numDecorations); // 0 holds null // 1 holds the IRModuleInst { // Check that insts[1] is the module inst const Ser::Inst& srcInst = data.m_insts[1]; SLANG_RELEASE_ASSERT(srcInst.m_op == kIROp_Module); SLANG_ASSERT(srcInst.m_payloadType == PayloadType::Empty); // Create the module inst auto moduleInst = static_cast(createEmptyInstWithSize(module, kIROp_Module, sizeof(IRModuleInst))); module->moduleInst = moduleInst; moduleInst->module = module; // Set the IRModuleInst insts[1] = moduleInst; } for (int i = 2; i < numInsts; ++i) { const Ser::Inst& srcInst = data.m_insts[i]; const IROp op((IROp)srcInst.m_op); if (isParentDerived(op)) { // Cannot have operands SLANG_ASSERT(srcInst.getNumOperands() == 0); if (isGlobalValueDerived(op)) { IRGlobalValue* globalValueInst = static_cast(createEmptyInstWithSize(module, op, sizeof(IRGlobalValue))); insts[i] = globalValueInst; // Set the global value SLANG_ASSERT(srcInst.m_payloadType == PayloadType::String_1); globalValueInst->mangledName = getName(srcInst.m_payload.m_stringIndices[0]); } else { // Just needs to big enough to hold IRParentInst IRParentInst* parentInst = static_cast(createEmptyInstWithSize(module, op, sizeof(IRParentInst))); insts[i] = parentInst; } } else { 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); IRConstant* irConst = nullptr; switch (op) { case kIROp_boolConst: { SLANG_ASSERT(srcInst.m_payloadType == PayloadType::UInt32); irConst = static_cast(createEmptyInstWithSize(module, op, 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(createEmptyInstWithSize(module, op, prefixSize + sizeof(IRIntegerValue))); irConst->value.intVal = srcInst.m_payload.m_int64; break; } case kIROp_FloatLit: { SLANG_ASSERT(srcInst.m_payloadType == PayloadType::Float64); irConst = static_cast(createEmptyInstWithSize(module, op, prefixSize + sizeof(IRFloatingPointValue))); irConst->value.floatVal = srcInst.m_payload.m_float64; break; } case kIROp_StringLit: { SLANG_ASSERT(srcInst.m_payloadType == PayloadType::String_1); const UnownedStringSlice slice = getStringSlice(srcInst.m_payload.m_stringIndices[0]); const size_t sliceSize = slice.size(); const size_t instSize = prefixSize + SLANG_OFFSET_OF(IRConstant::StringValue, chars) + sliceSize; irConst = static_cast(createEmptyInstWithSize(module, op, 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 if (isTextureTypeBase(op)) { IRTextureTypeBase* inst = static_cast(createEmptyInst(module, op, 1)); SLANG_ASSERT(srcInst.m_payloadType == PayloadType::OperandAndUInt32); // Reintroduce the texture type bits into the the const uint32_t other = srcInst.m_payload.m_operandAndUInt32.m_uint32; inst->op = IROp(uint32_t(inst->op) | (other << kIROpMeta_OtherShift)); insts[i] = inst; } else { int numOperands = srcInst.getNumOperands(); insts[i] = createEmptyInst(module, op, numOperands); } } } // Patch up the operands for (int i = 1; i < numInsts; ++i) { const Ser::Inst& srcInst = data.m_insts[i]; const IROp op((IROp)srcInst.m_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(resultInst)); dstInst->setFullType(static_cast(resultInst)); } //if (!isParentDerived(op)) { const Ser::InstIndex* srcOperandIndices; const int numOperands = data.getOperands(srcInst, &srcOperandIndices); for (int j = 0; j < numOperands; j++) { dstInst->setOperand(j, insts[int(srcOperandIndices[j])]); } } } // Patch up the children { const int numChildRuns = int(data.m_childRuns.Count()); for (int i = 0; i < numChildRuns; i++) { const auto& run = data.m_childRuns[i]; IRInst* inst = insts[int(run.m_parentIndex)]; IRParentInst* parentInst = as(inst); SLANG_ASSERT(parentInst); for (int j = 0; j < int(run.m_numChildren); ++j) { IRInst* child = insts[j + int(run.m_startInstIndex)]; SLANG_ASSERT(child->parent == nullptr); //child->parent = parentInst; child->insertAtEnd(parentInst); } } } // Create the decorations for (int i = 0; i < numDecorations; ++i) { IRDecoration* decor = _createDecoration(data.m_insts[i + numInsts]); if (!decor) { return SLANG_FAIL; } decorations[i] = decor; } // Associate the decorations with the instructions { const int decorationBaseIndex = m_serialData->m_decorationBaseIndex; const int numRuns = int(m_serialData->m_decorationRuns.Count()); for (int i = 0; i < numRuns; ++i) { const Ser::InstRun& run = m_serialData->m_decorationRuns[i]; // Decorations must be associated with instructions SLANG_ASSERT(int(run.m_parentIndex) < decorationBaseIndex); IRInst* inst = insts[int(run.m_parentIndex)]; SLANG_ASSERT(int(run.m_startInstIndex) >= decorationBaseIndex && int(run.m_startInstIndex) + run.m_numChildren <= m_serialData->m_insts.Count()); // Calculate the offset in the decoration list, which index 0, is decorationBaseIndex in instruction indices const int decorStartIndex = int(run.m_startInstIndex) - decorationBaseIndex; // Go in reverse order so that linked list is in same order as original for (int j = int(run.m_numChildren) - 1; j >= 0; --j) { IRDecoration* decor = decorations[decorStartIndex + j]; // And to the linked list on the decor->next = inst->firstDecoration; inst->firstDecoration = decor; } } } // Re-add source locations, if they are defined if (int(m_serialData->m_rawSourceLocs.Count()) == numInsts) { const Ser::RawSourceLoc* srcLocs = m_serialData->m_rawSourceLocs.begin(); for (int i = 1; i < numInsts; ++i) { IRInst* dstInst = insts[i]; dstInst->sourceLoc.setRaw(Slang::SourceLoc::RawValue(srcLocs[i])); } } return SLANG_OK; } // !!!!!!!!!!!!!!!!!!!!!!!!!!!! Free functions !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! Result serializeModule(IRModule* module, SourceManager* sourceManager, Stream* stream) { IRSerialWriter serializer; IRSerialData serialData; SLANG_RETURN_ON_FAIL(serializer.write(module, sourceManager, IRSerialWriter::OptionFlag::RawSourceLocation, &serialData)); if (stream) { SLANG_RETURN_ON_FAIL(IRSerialWriter::writeStream(serialData, stream)); } return SLANG_OK; } Result readModule(Session* session, Stream* stream, RefPtr& moduleOut) { IRSerialData serialData; IRSerialReader::readStream(stream, &serialData); IRSerialReader reader; return reader.read(serialData, session, moduleOut); } } // namespace Slang