diff options
Diffstat (limited to 'source')
| -rw-r--r-- | source/core/slang-string.h | 5 | ||||
| -rw-r--r-- | source/slang/core.meta.slang | 4 | ||||
| -rw-r--r-- | source/slang/core.meta.slang.h | 4 | ||||
| -rw-r--r-- | source/slang/hlsl.meta.slang | 2 | ||||
| -rw-r--r-- | source/slang/hlsl.meta.slang.h | 2 | ||||
| -rw-r--r-- | source/slang/ir-serialize.cpp | 1181 | ||||
| -rw-r--r-- | source/slang/ir-serialize.h | 283 | ||||
| -rw-r--r-- | source/slang/ir.cpp | 49 | ||||
| -rw-r--r-- | source/slang/ir.h | 39 | ||||
| -rw-r--r-- | source/slang/slang.vcxproj | 4 | ||||
| -rw-r--r-- | source/slang/slang.vcxproj.filters | 8 |
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"> |
