summaryrefslogtreecommitdiffstats
path: root/source
diff options
context:
space:
mode:
authorjsmall-nvidia <jsmall@nvidia.com>2018-09-27 11:36:35 -0400
committerGitHub <noreply@github.com>2018-09-27 11:36:35 -0400
commitd06bd7d8ec8c132f5f63f2137c83506fc2e80617 (patch)
tree08bba293af9e10f074cfb7120886fd46cb29588f /source
parentee549942335add195df8e69a31f65a691d54aff9 (diff)
First pass implementation of IR serialization (#653)
* * Change the layout of IROp such that 'main' IROps are 0-x. * Removed MANUAL_RANGE instuction types, as no longer needed. * Work in prog on optimizing. * * Constant time lookup for IROpInfo * Refactor and document a little more the IROp layout * Mark ops that use 'other' bits * Fix typo in definition of kIROpFlag_UseOther * First pass at working out serialization structure. * Work in progress on ir-serialize * Storing strings in IRSerialInfo Split out IRSerialInfo from the IRSerializer - to make more explicit what is actually saved. * First pass at serializing out data. * First pass at serialize reading. * Fix riff fourcc mark order. * First pass at reconstructing IRInst / IRDecoration from serialized data. * Handling of TextureBaseType * Deserializing of constants. * Small changes around ir serialization. * Changed StringIndex indexing to not be an offset into the m_strings array, but an index into strings in order. Doing so makes cache lookup much faster, and makes the 'indicies' themselves smaller and therefore more compressible. * Removed the need for m_arena in IRSerialWriter. Previously it's purpose was to store the string contents that were being used to lookup UnownedStringSlice. Now we keep the StringRepresentation in scope and reference that, and so don't need the copy. * Don't need to construct the IRModuleInst as is created and set on createModule call. * Remove test code for testing serialization. * Fix problem with release build in ir-serialize causing warning. * Use SLANG_OFFSET_OF for offsets in non pod classes to avoid gcc/clang warning. Give storage to integral static variables to avoid linkage problems with gcc/clang. * Fix warnings under x86 win32 debug.
Diffstat (limited to 'source')
-rw-r--r--source/core/slang-string.h5
-rw-r--r--source/slang/core.meta.slang4
-rw-r--r--source/slang/core.meta.slang.h4
-rw-r--r--source/slang/hlsl.meta.slang2
-rw-r--r--source/slang/hlsl.meta.slang.h2
-rw-r--r--source/slang/ir-serialize.cpp1181
-rw-r--r--source/slang/ir-serialize.h283
-rw-r--r--source/slang/ir.cpp49
-rw-r--r--source/slang/ir.h39
-rw-r--r--source/slang/slang.vcxproj4
-rw-r--r--source/slang/slang.vcxproj.filters8
11 files changed, 1564 insertions, 17 deletions
diff --git a/source/core/slang-string.h b/source/core/slang-string.h
index df1055795..fb60ef562 100644
--- a/source/core/slang-string.h
+++ b/source/core/slang-string.h
@@ -137,6 +137,11 @@ namespace Slang
bool endsWith(UnownedStringSlice const& other) const;
bool endsWith(char const* str) const;
+ int GetHashCode() const
+ {
+ return Slang::GetHashCode(beginData, size_t(endData - beginData));
+ }
+
template <size_t SIZE>
SLANG_FORCE_INLINE static UnownedStringSlice fromLiteral(const char (&in)[SIZE]) { return UnownedStringSlice(in, SIZE - 1); }
diff --git a/source/slang/core.meta.slang b/source/slang/core.meta.slang
index 89a7f75db..05ecd5b37 100644
--- a/source/slang/core.meta.slang
+++ b/source/slang/core.meta.slang
@@ -461,7 +461,7 @@ for (int tt = 0; tt < kBaseTextureTypeCount; ++tt)
sb << "__generic<T = float4> ";
sb << "__magic_type(TextureSampler," << int(flavor) << ")\n";
- sb << "__intrinsic_type(" << (kIROp_TextureSamplerType + (int(flavor) << kIROpMeta_Shift)) << ")\n";
+ sb << "__intrinsic_type(" << (kIROp_TextureSamplerType + (int(flavor) << kIROpMeta_OtherShift)) << ")\n";
sb << "struct Sampler";
sb << kBaseTextureAccessLevels[accessLevel].name;
sb << name;
@@ -519,7 +519,7 @@ for (int tt = 0; tt < kBaseTextureTypeCount; ++tt)
sb << "__generic<T = float4> ";
sb << "__magic_type(Texture," << int(flavor) << ")\n";
- sb << "__intrinsic_type(" << (kIROp_TextureType + (int(flavor) << kIROpMeta_Shift)) << ")\n";
+ sb << "__intrinsic_type(" << (kIROp_TextureType + (int(flavor) << kIROpMeta_OtherShift)) << ")\n";
sb << "struct ";
sb << kBaseTextureAccessLevels[accessLevel].name;
sb << name;
diff --git a/source/slang/core.meta.slang.h b/source/slang/core.meta.slang.h
index 76a121fe8..07d997a6a 100644
--- a/source/slang/core.meta.slang.h
+++ b/source/slang/core.meta.slang.h
@@ -476,7 +476,7 @@ for (int tt = 0; tt < kBaseTextureTypeCount; ++tt)
sb << "__generic<T = float4> ";
sb << "__magic_type(TextureSampler," << int(flavor) << ")\n";
- sb << "__intrinsic_type(" << (kIROp_TextureSamplerType + (int(flavor) << kIROpMeta_Shift)) << ")\n";
+ sb << "__intrinsic_type(" << (kIROp_TextureSamplerType + (int(flavor) << kIROpMeta_OtherShift)) << ")\n";
sb << "struct Sampler";
sb << kBaseTextureAccessLevels[accessLevel].name;
sb << name;
@@ -534,7 +534,7 @@ for (int tt = 0; tt < kBaseTextureTypeCount; ++tt)
sb << "__generic<T = float4> ";
sb << "__magic_type(Texture," << int(flavor) << ")\n";
- sb << "__intrinsic_type(" << (kIROp_TextureType + (int(flavor) << kIROpMeta_Shift)) << ")\n";
+ sb << "__intrinsic_type(" << (kIROp_TextureType + (int(flavor) << kIROpMeta_OtherShift)) << ")\n";
sb << "struct ";
sb << kBaseTextureAccessLevels[accessLevel].name;
sb << name;
diff --git a/source/slang/hlsl.meta.slang b/source/slang/hlsl.meta.slang
index a9e2cc6df..a9609e13e 100644
--- a/source/slang/hlsl.meta.slang
+++ b/source/slang/hlsl.meta.slang
@@ -1250,7 +1250,7 @@ for (int aa = 0; aa < kBaseBufferAccessLevelCount; ++aa)
auto flavor = TextureFlavor::create(TextureFlavor::Shape::ShapeBuffer, kBaseBufferAccessLevels[aa].access).flavor;
sb << "__generic<T>\n";
sb << "__magic_type(Texture," << int(flavor) << ")\n";
- sb << "__intrinsic_type(" << (kIROp_TextureType + (int(flavor) << kIROpMeta_Shift)) << ")\n";
+ sb << "__intrinsic_type(" << (kIROp_TextureType + (int(flavor) << kIROpMeta_OtherShift)) << ")\n";
sb << "struct ";
sb << kBaseBufferAccessLevels[aa].name;
sb << "Buffer {\n";
diff --git a/source/slang/hlsl.meta.slang.h b/source/slang/hlsl.meta.slang.h
index a4ed6ec2c..54aa2710d 100644
--- a/source/slang/hlsl.meta.slang.h
+++ b/source/slang/hlsl.meta.slang.h
@@ -1295,7 +1295,7 @@ for (int aa = 0; aa < kBaseBufferAccessLevelCount; ++aa)
auto flavor = TextureFlavor::create(TextureFlavor::Shape::ShapeBuffer, kBaseBufferAccessLevels[aa].access).flavor;
sb << "__generic<T>\n";
sb << "__magic_type(Texture," << int(flavor) << ")\n";
- sb << "__intrinsic_type(" << (kIROp_TextureType + (int(flavor) << kIROpMeta_Shift)) << ")\n";
+ sb << "__intrinsic_type(" << (kIROp_TextureType + (int(flavor) << kIROpMeta_OtherShift)) << ")\n";
sb << "struct ";
sb << kBaseBufferAccessLevels[aa].name;
sb << "Buffer {\n";
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
diff --git a/source/slang/ir-serialize.h b/source/slang/ir-serialize.h
new file mode 100644
index 000000000..e949e1239
--- /dev/null
+++ b/source/slang/ir-serialize.h
@@ -0,0 +1,283 @@
+// ir-serialize.h
+#ifndef SLANG_IR_SERIALIZE_H_INCLUDED
+#define SLANG_IR_SERIALIZE_H_INCLUDED
+
+#include "../core/basic.h"
+#include "../core/stream.h"
+
+#include "ir.h"
+
+// For TranslationUnitRequest
+#include "compiler.h"
+
+namespace Slang {
+
+// Pre-declare
+class Name;
+
+struct IRSerialData
+{
+ enum class InstIndex : uint32_t;
+ enum class StringIndex : uint32_t;
+ enum class ArrayIndex : uint32_t;
+ enum class SourceLoc : uint32_t;
+ enum class StringOffset : uint32_t; ///< Offset into the m_stringsBuffer
+
+ typedef uint32_t SizeType;
+
+ static const StringIndex kNullStringIndex = StringIndex(0);
+ static const StringIndex kEmptyStringIndex = StringIndex(1);
+
+ enum
+ {
+ kNumOperands = 2,
+ };
+
+ /// A run of instructions
+ struct InstRun
+ {
+ InstIndex m_parentIndex; ///< The parent instruction
+ InstIndex m_startInstIndex; ///< The index to the first instruction
+ SizeType m_numChildren; ///< The number of children
+ };
+
+ // Instruction...
+ // We can store SourceLoc values separately. Just store per index information.
+ // Parent information is stored in m_childRuns
+ // Decoration information is stored in m_decorationRuns
+ struct Inst
+ {
+ enum class PayloadType : uint8_t
+ {
+ Empty, ///< Has no payload (or operands)
+ Operand_1, ///< 1 Operand
+ Operand_2, ///< 2 Operands
+ ExternalOperand, ///< Operands are held externally
+ String_1, ///< 1 String
+ String_2, ///< 2 Strings
+ UInt32, ///< Holds an unsigned 32 bit integral (might represent a type)
+ Float64,
+ Int64,
+ CountOf,
+ };
+
+ /// Get the number of operands
+ int getNumOperands() const
+ {
+ switch (m_payloadType)
+ {
+ default: /* fallthru */
+ case PayloadType::Empty: return 0;
+ case PayloadType::Operand_1: return 1;
+ case PayloadType::Operand_2: return 2;
+ case PayloadType::ExternalOperand: return m_payload.m_externalOperand.m_size;
+ }
+ }
+
+ uint8_t m_op; ///< For now one of IROp
+ PayloadType m_payloadType; ///< The type of payload
+ uint16_t m_pad0; ///< Not currently used
+
+ InstIndex m_resultTypeIndex; //< 0 if has no type. The result type of this instruction
+
+ struct ExternalOperandPayload
+ {
+ ArrayIndex m_arrayIndex; ///< Index into the m_externalOperands table
+ SizeType m_size; ///< The amount of entries in that table
+ };
+
+ union Payload
+ {
+ double m_float64;
+ int64_t m_int64;
+ uint32_t m_uint32; ///< Unsigned integral value
+ IRFloatingPointValue m_float; ///< Floating point value
+ IRIntegerValue m_int; ///< Integral value
+ InstIndex m_operands[kNumOperands]; ///< For items that 2 or less operands it can use this.
+ StringIndex m_stringIndices[kNumOperands];
+ ExternalOperandPayload m_externalOperand; ///< Operands are stored in an an index of an operand array
+ };
+
+ Payload m_payload;
+ };
+
+ /// Clear to initial state
+ void 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_strings.SetSize(2);
+ m_strings[int(kNullStringIndex)] = 0;
+ m_strings[int(kEmptyStringIndex)] = 0;
+
+ m_decorationBaseIndex = 0;
+ }
+
+ int getOperands(const Inst& inst, const InstIndex** operandsOut) const
+ {
+ switch (inst.m_payloadType)
+ {
+ default:
+ case Inst::PayloadType::Empty:
+ {
+ *operandsOut = nullptr;
+ return 0;
+ }
+ case Inst::PayloadType::Operand_1:
+ case Inst::PayloadType::Operand_2:
+ {
+ *operandsOut = inst.m_payload.m_operands;
+ return int(inst.m_payloadType) - int(Inst::PayloadType::Empty);
+ }
+ case Inst::PayloadType::ExternalOperand:
+ {
+ *operandsOut = m_externalOperands.begin() + int(inst.m_payload.m_externalOperand.m_arrayIndex);
+ return int(inst.m_payload.m_externalOperand.m_size);
+ }
+ }
+ }
+
+ /// Ctor
+ IRSerialData() :
+ m_decorationBaseIndex(0)
+ {}
+
+
+ List<Inst> m_insts; ///< The instructions
+
+ List<InstRun> m_childRuns; ///< Holds the information about children that belong to an instruction
+ List<InstRun> m_decorationRuns; ///< Holds instruction decorations
+
+ List<InstIndex> m_externalOperands; ///< Holds external operands (for instructions with more than kNumOperands)
+
+ List<char> m_strings; ///< All strings. Indexed into by StringIndex
+
+ int m_decorationBaseIndex; ///< All decorations insts are at indices >= to this value
+};
+
+#define SLANG_FOUR_CC(c0, c1, c2, c3) ((uint32_t(c0) << 0) | (uint32_t(c1) << 8) | (uint32_t(c2) << 16) | (uint32_t(c3) << 24))
+
+struct IRSerialBinary
+{
+ // http://fileformats.archiveteam.org/wiki/RIFF
+ // http://www.fileformat.info/format/riff/egff.htm
+
+ struct Chunk
+ {
+ uint32_t m_type;
+ uint32_t m_size;
+ };
+ static const uint32_t kRiffFourCc = SLANG_FOUR_CC('R', 'I', 'F', 'F');
+ static const uint32_t kSlangFourCc = SLANG_FOUR_CC('S', 'L', 'N', 'G'); ///< Holds all the slang specific chunks
+
+ static const uint32_t kInstFourCc = SLANG_FOUR_CC('S', 'L', 'i', 'n');
+ static const uint32_t kDecoratorRunFourCc = SLANG_FOUR_CC('S', 'L', 'd', 'r');
+ static const uint32_t kChildRunFourCc = SLANG_FOUR_CC('S', 'L', 'c', 'r');
+ static const uint32_t kExternalOperandsFourCc = SLANG_FOUR_CC('S', 'L', 'e', 'o');
+ static const uint32_t kStringFourCc = SLANG_FOUR_CC('S', 'L', 's', 't');
+
+ struct SlangHeader
+ {
+ Chunk m_chunk;
+ uint32_t m_decorationBase;
+ };
+ struct ArrayHeader
+ {
+ Chunk m_chunk;
+ uint32_t m_numEntries;
+ };
+};
+
+
+struct IRSerialWriter
+{
+ typedef IRSerialData Ser;
+
+ Result write(IRModule* module, IRSerialData* serialData);
+
+ static Result writeStream(const IRSerialData& data, Stream* stream);
+
+ /// Get a slice from an index
+ UnownedStringSlice getStringSlice(Ser::StringIndex index) const;
+
+ /// Get an instruction index from an instruction
+ Ser::InstIndex getInstIndex(IRInst* inst) const { return inst ? Ser::InstIndex(m_instMap[inst]) : Ser::InstIndex(0); }
+
+ Ser::StringIndex getStringIndex(StringRepresentation* string);
+ Ser::StringIndex getStringIndex(const UnownedStringSlice& string);
+ Ser::StringIndex getStringIndex(Name* name);
+ Ser::StringIndex getStringIndex(const char* chars);
+
+ IRSerialWriter() :
+ m_serialData(nullptr)
+ {}
+
+protected:
+ void _addInstruction(IRInst* inst);
+
+ List<IRInst*> m_insts; ///< Instructions in same order as stored in the
+
+ 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
+
+ List<Ser::StringOffset> m_stringStarts; ///< Offset for each string index into the m_strings
+
+ // TODO (JS):
+ // We could perhaps improve this, if we stored at string indices (when linearized) the StringRepresentation
+ // Doing so would mean if a String or Name was looked up we wouldn't have to re-allocate on the arena
+ Dictionary<UnownedStringSlice, Ser::StringIndex> m_stringMap; ///< String map
+ List<RefPtr<StringRepresentation> > m_scopeStrings; ///<
+
+ IRSerialData* m_serialData; ///< Where the data is stored
+};
+
+struct IRSerialReader
+{
+ typedef IRSerialData Ser;
+
+ /// Read a stream to fill in dataOut IRSerialData
+ static Result readStream(Stream* stream, IRSerialData* dataOut);
+
+ /// Read a module from serial data
+ Result read(const IRSerialData& data, TranslationUnitRequest* translationUnit, IRModule** moduleOut);
+
+ Name* getName(Ser::StringIndex index);
+ String getString(Ser::StringIndex index);
+ StringRepresentation* getStringRepresentation(Ser::StringIndex index);
+ UnownedStringSlice getStringSlice(Ser::StringIndex index) { return getStringSlice(m_stringStarts[int(index)]); }
+ char* getCStr(Ser::StringIndex index);
+
+ UnownedStringSlice getStringSlice(Ser::StringOffset offset);
+
+ IRSerialReader():
+ m_serialData(nullptr),
+ m_module(nullptr)
+ {
+ }
+
+ protected:
+
+ void _calcStringStarts();
+
+ List<Ser::StringOffset> m_stringStarts;
+ List<StringRepresentation*> m_stringRepresentationCache;
+
+ const IRSerialData* m_serialData;
+ IRModule* m_module;
+};
+
+
+Result serializeModule(IRModule* module, Stream* stream);
+Result readModule(TranslationUnitRequest* translationUnit, Stream* stream, IRModule** moduleOut);
+
+} // namespace Slang
+
+#endif
diff --git a/source/slang/ir.cpp b/source/slang/ir.cpp
index c72a4a705..72eea2e7c 100644
--- a/source/slang/ir.cpp
+++ b/source/slang/ir.cpp
@@ -49,7 +49,7 @@ namespace Slang
IROpInfo getIROpInfo(IROp opIn)
{
- const int op = opIn & kIROpMeta_OpMask;
+ const int op = opIn & kIROpMeta_PseudoOpMask;
if ((op & kIROpMeta_IsPseudoOp) && op < kIRPseudoOp_LastPlusOne)
{
// It's a pseudo op
@@ -712,6 +712,51 @@ namespace Slang
return parent;
}
+ IRInst* createEmptyInst(
+ IRModule* module,
+ IROp op,
+ int totalArgCount)
+ {
+ size_t size = sizeof(IRInst) + (totalArgCount) * sizeof(IRUse);
+
+ SLANG_ASSERT(module);
+ IRInst* inst = (IRInst*)module->memoryArena.allocateAndZero(size);
+
+ inst->operandCount = uint32_t(totalArgCount);
+ inst->op = op;
+
+ return inst;
+ }
+
+ IRInst* createEmptyInstWithSize(
+ IRModule* module,
+ IROp op,
+ size_t totalSizeInBytes)
+ {
+ SLANG_ASSERT(totalSizeInBytes >= sizeof(IRInst));
+
+ SLANG_ASSERT(module);
+ IRInst* inst = (IRInst*)module->memoryArena.allocateAndZero(totalSizeInBytes);
+
+ inst->operandCount = 0;
+ inst->op = op;
+
+ return inst;
+ }
+
+
+ IRDecoration* createEmptyDecoration(
+ IRModule* module,
+ IRDecorationOp op,
+ size_t sizeInBytes)
+ {
+ SLANG_ASSERT(sizeInBytes >= sizeof(IRDecoration));
+ SLANG_ASSERT(module);
+ IRDecoration* decor = (IRDecoration*)module->memoryArena.allocateAndZero(sizeInBytes);
+ decor->op = op;
+ return decor;
+ }
+
// Given an instruction that represents a constant, a type, etc.
// Try to "hoist" it as far toward the global scope as possible
// to insert it at a location where it will be maximally visible.
@@ -1187,7 +1232,7 @@ namespace Slang
}
// Calculate the minimum object size (ie not including the payload of value)
- const size_t prefixSize = offsetof(IRConstant, value);
+ const size_t prefixSize = SLANG_OFFSET_OF(IRConstant, value);
switch (keyInst.op)
{
diff --git a/source/slang/ir.h b/source/slang/ir.h
index 6627acd5d..4f36b6e7f 100644
--- a/source/slang/ir.h
+++ b/source/slang/ir.h
@@ -81,9 +81,10 @@ enum IROp : int32_t
/* IROpMeta describe values for layout of IROp, as well as values for accessing aspects of IROp bits. */
enum IROpMeta
{
- kIROpMeta_Shift = 9, ///< Number of bits for op/pseudo ops (shift right by this to get the other bits)
- kIROpMeta_OpMask = (int32_t(1) << kIROpMeta_Shift) - 1, ///< Mask for regular ops
- kIrOpMeta_OtherMask = ~kIROpMeta_OpMask, ///< Mask for bits that can be used for other purposes than 'op' ('other' bits)
+ kIROpMeta_OtherShift = 9, ///< Number of bits for op/pseudo ops (shift right by this to get the other bits)
+ kIROpMeta_PseudoOpMask = (int32_t(1) << kIROpMeta_OtherShift) - 1, ///< Mask for ops including pseudo ops
+ kIROpMeta_OpMask = 0xff, ///< Mask for just ops
+ kIrOpMeta_OtherMask = ~kIROpMeta_PseudoOpMask, ///< Mask for bits that can be used for other purposes than 'op' ('other' bits)
kIROpMeta_IsPseudoOp = kIROp_Invalid, ///< 'And' with op, if set, the op is a pseudo op
};
@@ -146,10 +147,13 @@ enum IRDecorationOp : uint16_t
kIRDecorationOp_Semantic,
kIRDecorationOp_InterpolationMode,
kIRDecorationOp_NameHint,
+
/** The _instruction_ is transitory. Such a decoration should NEVER be found on an output instruction a module.
Typically used mark an instruction so can be specially handled - say when creating a IRConstant literal, and the payload of
needs to be special cased for lookup. */
- kIRDecorationOp_Transitory,
+ kIRDecorationOp_Transitory,
+
+ kIRDecorationOp_CountOf
};
// represents an object allocated in an IR memory arena
@@ -423,8 +427,8 @@ struct IRInstList : IRInstListBase
// Types
-#define IR_LEAF_ISA(NAME) static bool isaImpl(IROp op) { return (kIROpMeta_OpMask & op) == kIROp_##NAME; }
-#define IR_PARENT_ISA(NAME) static bool isaImpl(IROp opIn) { const int op = (kIROpMeta_OpMask & opIn); return op >= kIROp_First##NAME && op <= kIROp_Last##NAME; }
+#define IR_LEAF_ISA(NAME) static bool isaImpl(IROp op) { return (kIROpMeta_PseudoOpMask & op) == kIROp_##NAME; }
+#define IR_PARENT_ISA(NAME) static bool isaImpl(IROp opIn) { const int op = (kIROpMeta_PseudoOpMask & opIn); return op >= kIROp_First##NAME && op <= kIROp_Last##NAME; }
#define SIMPLE_IR_TYPE(NAME, BASE) struct IR##NAME : IR##BASE { IR_LEAF_ISA(NAME) };
#define SIMPLE_IR_PARENT_TYPE(NAME, BASE) struct IR##NAME : IR##BASE { IR_PARENT_ISA(NAME) };
@@ -701,7 +705,7 @@ struct IRResourceTypeBase : IRType
{
TextureFlavor getFlavor() const
{
- return TextureFlavor((op >> kIROpMeta_Shift) & 0xFFFF);
+ return TextureFlavor((op >> kIROpMeta_OtherShift) & 0xFFFF);
}
TextureFlavor::Shape GetBaseShape() const
@@ -1098,6 +1102,27 @@ void dumpIR(IRGlobalValue* globalVal);
String dumpIRFunc(IRFunc* func);
+IRInst* createEmptyInst(
+ IRModule* module,
+ IROp op,
+ int totalArgCount);
+
+IRInst* createEmptyInstWithSize(
+ IRModule* module,
+ IROp op,
+ size_t totalSizeInBytes);
+
+IRDecoration* createEmptyDecoration(
+ IRModule* module,
+ IRDecorationOp op,
+ size_t sizeInBytes);
+
+template <typename T>
+T* createEmptyDecoration(IRModule* module)
+{
+ return static_cast<T*>(createEmptyDecoration(module, IRDecorationOp(T::kDecorationOp), sizeof(T)));
+}
+
}
diff --git a/source/slang/slang.vcxproj b/source/slang/slang.vcxproj
index 3be83b4f5..441bbea51 100644
--- a/source/slang/slang.vcxproj
+++ b/source/slang/slang.vcxproj
@@ -187,6 +187,7 @@
<ClInclude Include="ir-insts.h" />
<ClInclude Include="ir-restructure-scoping.h" />
<ClInclude Include="ir-restructure.h" />
+ <ClInclude Include="ir-serialize.h" />
<ClInclude Include="ir-ssa.h" />
<ClInclude Include="ir-validate.h" />
<ClInclude Include="ir.h" />
@@ -232,6 +233,7 @@
<ClCompile Include="ir-legalize-types.cpp" />
<ClCompile Include="ir-restructure-scoping.cpp" />
<ClCompile Include="ir-restructure.cpp" />
+ <ClCompile Include="ir-serialize.cpp" />
<ClCompile Include="ir-ssa.cpp" />
<ClCompile Include="ir-validate.cpp" />
<ClCompile Include="ir.cpp" />
@@ -295,4 +297,4 @@
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
<ImportGroup Label="ExtensionTargets">
</ImportGroup>
-</Project> \ No newline at end of file
+</Project> \ No newline at end of file
diff --git a/source/slang/slang.vcxproj.filters b/source/slang/slang.vcxproj.filters
index d2efaf191..4c47cd0f0 100644
--- a/source/slang/slang.vcxproj.filters
+++ b/source/slang/slang.vcxproj.filters
@@ -1,4 +1,4 @@
-<?xml version="1.0" encoding="utf-8"?>
+<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<ItemGroup>
<Filter Include="Header Files">
@@ -156,6 +156,9 @@
<ClInclude Include="vm.h">
<Filter>Header Files</Filter>
</ClInclude>
+ <ClInclude Include="ir-serialize.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
</ItemGroup>
<ItemGroup>
<ClCompile Include="bytecode.cpp">
@@ -260,6 +263,9 @@
<ClCompile Include="vm.cpp">
<Filter>Source Files</Filter>
</ClCompile>
+ <ClCompile Include="ir-serialize.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
</ItemGroup>
<ItemGroup>
<None Include="slang.natvis">