summaryrefslogtreecommitdiff
path: root/source
diff options
context:
space:
mode:
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">