summaryrefslogtreecommitdiffstats
path: root/source/slang/ir-serialize.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'source/slang/ir-serialize.cpp')
-rw-r--r--source/slang/ir-serialize.cpp1181
1 files changed, 1181 insertions, 0 deletions
diff --git a/source/slang/ir-serialize.cpp b/source/slang/ir-serialize.cpp
new file mode 100644
index 000000000..854b60419
--- /dev/null
+++ b/source/slang/ir-serialize.cpp
@@ -0,0 +1,1181 @@
+// 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 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);
+}
+
+// !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! 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, IRSerialData* serialData)
+{
+ 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<IRParentInst*> 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<IRParentInst>(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 = Ser::Inst::PayloadType::Empty;
+
+ dstInst.m_resultTypeIndex = getInstIndex(srcInst->getFullType());
+
+ IRConstant* irConst = as<IRConstant>(srcInst);
+ if (irConst)
+ {
+ switch (srcInst->op)
+ {
+ // Special handling for the ir const derived types
+ case kIROp_StringLit:
+ {
+ auto stringLit = static_cast<IRStringLit*>(srcInst);
+ dstInst.m_payloadType = Ser::Inst::PayloadType::String_1;
+ dstInst.m_payload.m_stringIndices[0] = getStringIndex(stringLit->getStringSlice());
+ break;
+ }
+ case kIROp_IntLit:
+ {
+ dstInst.m_payloadType = Ser::Inst::PayloadType::Int64;
+ dstInst.m_payload.m_int64 = irConst->value.intVal;
+ break;
+ }
+ case kIROp_FloatLit:
+ {
+ dstInst.m_payloadType = Ser::Inst::PayloadType::Float64;
+ dstInst.m_payload.m_float64 = irConst->value.floatVal;
+ break;
+ }
+ case kIROp_boolConst:
+ {
+ dstInst.m_payloadType = Ser::Inst::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<IRGlobalValue>(srcInst);
+ if (globValue)
+ {
+ dstInst.m_payloadType = Ser::Inst::PayloadType::String_1;
+ dstInst.m_payload.m_stringIndices[0] = getStringIndex(globValue->mangledName);
+ continue;
+ }
+
+ IRTextureTypeBase* textureBase = as<IRTextureTypeBase>(srcInst);
+ if (textureBase)
+ {
+ dstInst.m_payloadType = Ser::Inst::PayloadType::UInt32;
+ dstInst.m_payload.m_uint32 = uint32_t(srcInst->op) >> kIROpMeta_OtherShift;
+ 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::kNumOperands)
+ {
+ dstOperands = dstInst.m_payload.m_operands;
+ dstInst.m_payloadType = Ser::Inst::PayloadType(numOperands);
+ }
+ else
+ {
+ dstInst.m_payloadType = Ser::Inst::PayloadType::ExternalOperand;
+
+ 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<IRLoopControlDecoration*>(srcDecor);
+
+ dstInst.m_payloadType = Ser::Inst::PayloadType::UInt32;
+ dstInst.m_payload.m_uint32 = uint32_t(loopDecor->mode);
+ break;
+ }
+ case kIRDecorationOp_Target:
+ {
+ auto targetDecor = static_cast<IRTargetDecoration*>(srcDecor);
+
+ dstInst.m_payloadType = Ser::Inst::PayloadType::String_1;
+ dstInst.m_payload.m_stringIndices[0] = getStringIndex(targetDecor->targetName);
+ break;
+ }
+ case kIRDecorationOp_TargetIntrinsic:
+ {
+ auto targetDecor = static_cast<IRTargetIntrinsicDecoration*>(srcDecor);
+ dstInst.m_payloadType = Ser::Inst::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<IRGLSLOuterArrayDecoration*>(srcDecor);
+ dstInst.m_payloadType = Ser::Inst::PayloadType::String_1;
+
+ dstInst.m_payload.m_stringIndices[0] = getStringIndex(arrayDecor->outerArrayName);
+ break;
+ }
+ case kIRDecorationOp_Semantic:
+ {
+ auto semanticDecor = static_cast<IRSemanticDecoration*>(srcDecor);
+
+ dstInst.m_payloadType = Ser::Inst::PayloadType::String_1;
+ dstInst.m_payload.m_stringIndices[0] = getStringIndex(semanticDecor->semanticName);
+ break;
+ }
+ case kIRDecorationOp_NameHint:
+ {
+ auto nameDecor = static_cast<IRNameHintDecoration*>(srcDecor);
+
+ dstInst.m_payloadType = Ser::Inst::PayloadType::String_1;
+ dstInst.m_payload.m_stringIndices[0] = getStringIndex(nameDecor->name);
+ break;
+ }
+ default:
+ {
+ SLANG_ASSERT(!"Unhandled decoration type");
+ return SLANG_FAIL;
+ }
+ }
+ }
+ }
+
+ 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 <typename T>
+static size_t _calcChunkSize(const List<T>& 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 <typename T>
+Result _writeArrayChunk(uint32_t chunkId, const List<T>& 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);
+
+ {
+ 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);
+
+ return SLANG_OK;
+}
+
+// !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! IRSerialReader !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
+
+template <typename T>
+Result _readArrayChunk(const IRSerialBinary::Chunk& chunk, Stream* stream, List<T>& 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::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;
+ }
+ default:
+ {
+ remainingBytes -= _calcChunkTotalSize(chunk);
+
+ // Unhandled chunk... skip it
+ int skipSize = (chunk.m_size + 3) & ~3;
+ stream->Seek(SeekOrigin::Current, skipSize);
+ 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());
+}
+
+/* static */Result IRSerialReader::read(const IRSerialData& data, TranslationUnitRequest* translationUnit, IRModule** moduleOut)
+{
+ typedef Ser::Inst::PayloadType PayloadType;
+
+ *moduleOut = nullptr;
+
+ m_serialData = &data;
+ _calcStringStarts();
+
+ auto compileRequest = translationUnit->compileRequest;
+
+ //SharedIRGenContext sharedContextStorage;
+ //SharedIRGenContext* sharedContext = &sharedContextStorage;
+
+ //sharedContext->compileRequest = compileRequest;
+ //sharedContext->mainModuleDecl = translationUnit->SyntaxNode;
+
+ //IRGenContext contextStorage(sharedContext);
+ //IRGenContext* context = &contextStorage;
+
+ SharedIRBuilder sharedBuilderStorage;
+ SharedIRBuilder* sharedBuilder = &sharedBuilderStorage;
+ sharedBuilder->module = nullptr;
+ sharedBuilder->session = compileRequest->mSession;
+
+ IRBuilder builderStorage;
+ IRBuilder* builder = &builderStorage;
+ builder->sharedBuilder = sharedBuilder;
+
+ RefPtr<IRModule> module = builder->createModule();
+ sharedBuilder->module = module;
+
+ m_module = module;
+
+ //context->irBuilder = builder;
+
+ // Add all the instructions
+
+ List<IRInst*> insts;
+ List<IRDecoration*> 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
+ // The first instruction must be the module
+ {
+ // 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.getNumOperands() == 0);
+ SLANG_ASSERT(srcInst.m_payloadType == PayloadType::Empty);
+
+ // We don't need to create the moduleInst, because it's constructed via the createModule call
+ SLANG_ASSERT(module->moduleInst);
+ insts[1] = module->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<IRGlobalValue*>(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<IRParentInst*>(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<IRConstant*>(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<IRConstant*>(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<IRConstant*>(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<IRConstant*>(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<IRTextureTypeBase*>(createEmptyInstWithSize(module, op, sizeof(IRTextureTypeBase)));
+ SLANG_ASSERT(srcInst.m_payloadType == PayloadType::UInt32);
+
+ // Reintroduce the texture type bits into the the
+ const uint32_t other = srcInst.m_payload.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<IRType>(resultInst));
+ dstInst->setFullType(static_cast<IRType*>(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<IRParentInst>(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);
+ }
+ }
+ }
+
+ // Add the decorations
+ for (int i = 0; i < numDecorations; ++i)
+ {
+ const Ser::Inst& srcInst = data.m_insts[i + numInsts];
+ IRDecorationOp decorOp = IRDecorationOp(srcInst.m_op - kIROpCount);
+ SLANG_ASSERT(decorOp < kIRDecorationOp_CountOf);
+
+ switch (decorOp)
+ {
+ case kIRDecorationOp_HighLevelDecl:
+ {
+ auto decor = createEmptyDecoration<IRHighLevelDeclDecoration>(m_module);
+ decorations[i] = decor;
+
+ // TODO!
+ // Decl* decl;
+ break;
+ }
+ case kIRDecorationOp_Layout:
+ {
+ auto decor = createEmptyDecoration<IRLayoutDecoration>(m_module);
+ decorations[i] = decor;
+
+ // TODO!
+ // Layout* layout;
+ break;
+ }
+ case kIRDecorationOp_LoopControl:
+ {
+ auto decor = createEmptyDecoration<IRLoopControlDecoration>(m_module);
+ decorations[i] = decor;
+
+ SLANG_ASSERT(srcInst.m_payloadType == PayloadType::UInt32);
+ decor->mode = IRLoopControl(srcInst.m_payload.m_uint32);
+
+ break;
+ }
+ case kIRDecorationOp_Target:
+ {
+ auto decor = createEmptyDecoration<IRTargetDecoration>(m_module);
+ decorations[i] = decor;
+
+ SLANG_ASSERT(srcInst.m_payloadType == PayloadType::String_1);
+ decor->targetName = getStringRepresentation(srcInst.m_payload.m_stringIndices[0]);
+ break;
+ }
+ case kIRDecorationOp_TargetIntrinsic:
+ {
+ auto decor = createEmptyDecoration<IRTargetIntrinsicDecoration>(m_module);
+ decorations[i] = decor;
+
+ 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]);
+ break;
+ }
+ case kIRDecorationOp_GLSLOuterArray:
+ {
+ auto decor = createEmptyDecoration<IRGLSLOuterArrayDecoration>(m_module);
+ decorations[i] = decor;
+
+ SLANG_ASSERT(srcInst.m_payloadType == PayloadType::String_1);
+ decor->outerArrayName = getCStr(srcInst.m_payload.m_stringIndices[0]);
+ break;
+ }
+ case kIRDecorationOp_Semantic:
+ {
+ auto decor = createEmptyDecoration<IRSemanticDecoration>(m_module);
+ decorations[i] = decor;
+
+ SLANG_ASSERT(srcInst.m_payloadType == PayloadType::String_1);
+ decor->semanticName = getName(srcInst.m_payload.m_stringIndices[0]);
+ break;
+ }
+ case kIRDecorationOp_NameHint:
+ {
+ auto decor = createEmptyDecoration<IRNameHintDecoration>(m_module);
+ decorations[i] = decor;
+
+ SLANG_ASSERT(srcInst.m_payloadType == PayloadType::String_1);
+ decor->name = getName(srcInst.m_payload.m_stringIndices[0]);
+ break;
+ }
+ default:
+ {
+ SLANG_ASSERT(!"Unhandled decoration type");
+ return SLANG_FAIL;
+ }
+ }
+
+ // Make sure something is set
+ SLANG_ASSERT(decorations[i]);
+ }
+
+ // 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());
+
+ // 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[int(run.m_startInstIndex) + j - decorationBaseIndex];
+
+ // And to the linked list on the
+ decor->next = inst->firstDecoration;
+ inst->firstDecoration = decor;
+ }
+ }
+ }
+
+ *moduleOut = module.detach();
+ return SLANG_OK;
+}
+
+// !!!!!!!!!!!!!!!!!!!!!!!!!!!! Free functions !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
+
+
+Result serializeModule(IRModule* module, Stream* stream)
+{
+ IRSerialWriter serializer;
+ IRSerialData serialData;
+
+ SLANG_RETURN_ON_FAIL(serializer.write(module, &serialData));
+
+ if (stream)
+ {
+ SLANG_RETURN_ON_FAIL(IRSerialWriter::writeStream(serialData, stream));
+ }
+
+ return SLANG_OK;
+}
+
+Result readModule(TranslationUnitRequest* translationUnit, Stream* stream, IRModule** moduleOut)
+{
+ *moduleOut = nullptr;
+
+ IRSerialData serialData;
+ IRSerialReader::readStream(stream, &serialData);
+
+ RefPtr<IRModule> module;
+ IRSerialReader reader;
+
+ SLANG_RETURN_ON_FAIL(reader.read(serialData, translationUnit, module.writeRef()));
+
+ *moduleOut = module.detach();
+
+ return SLANG_OK;
+}
+
+
+} // namespace Slang