From 822ed708364b257b7d2f61ecb8a51a4c96f7edaa Mon Sep 17 00:00:00 2001 From: Tim Foley Date: Thu, 13 Dec 2018 12:13:58 -0800 Subject: Move mangled name out of IRGlobalValue (#752) * Move mangled name out of IRGlobalValue Previously the `IRGlobalValue` type was used as a root for all IR instructions that can have "linkage," in the sense that a definition in one module can satisfy a use in another module. The mangled symbol name was stored in state directly on each `IRGlobalValue`, which created some complications, and also forced IR instructions that wanted to support linkage to wedge into the hierarchy at that specific point. This change moves the mangled name out into a decoration: either an `IRImportDecoration` or an `IRExportDecoration`, both of which inherit from `IRLinkageDecoration` which exposes the mangled name. This change has a few benefits: * We can now have any kind of instruction be exported/imported, without having to inherit from `IRGlobalValue`. This could potentially let `IRStructType` and `IRWitnessTable` be simplified to just have operand lists instead of dummy chldren as they do today. * We can now easily have "global values" like functions that explicitly *don't* get linkage, instead of using a null or empty mangled name as a marker. * We can use the exact opcode on a linkage decoration to distinguish imports from exports, which could be used to more accurately resolve symbols during the linking step. Other than adding the decorations and making sure that AST->IR lowering adds them, the main changes here are around any code that used `IRGlobalValue`. Variables and parameters of type `IRGlobalValue*` were changed to `IRInst*` easily, so the main challenge was around code that *casts* to `IRGlobalValue*. In cases where a cast to `IRGlobalValue` also performed a test for the mangled name being non-null/non-empty, we simply switched the code to check for the presence of an `IRLinkageDecoration`, since that is the new way of indicating a value with linakge. Most of the serious complications arose in `ir.cpp` around the "linking"/target-specialization and generic specialization steps. The "linking" logic was checking for `IRGlobalValue` to opt into some more complicated cloning logic, and just checking for a linkage decoration here wasn't sufficient since the front-end *does* produce global values without linkage in some cases (e.g., for a function-`static` variable we produce a global variable without linkage). This logic was updated to just check for the cases that used to amount to `IRGlobalValue`s directly by opcode. It might be simpler in the short term to have kept `IRGlobalValue` around to make the existing casts Just Work, but I'm confident that this logic could actually be rewritten for much greater clarity and simplicity and that is the better way forward. The generic specialization logic was using some really messy code to generate a new mangled name to represent the specialized symbol, and then searching for an existing match for that name. The original idea there was that an IR module could include "pre-specialized" versions of certain generics to speed up back-end compilation by eliminating the need to specialize in some cases, but this feature has never been implemented so the overhead here is just a waste. Instead, I moved generic specialization to use a simpler dictionary to map the operands to a `specialize` instruction over to the resulting specialized value. This allows for some simplifications in the name mangling logic, because it no longer needs to figure out how to produce mangled names from IR instructions representing types/values. As part of this change I also overhauled the IR emit logic to produce cleaner output by default, borrowing some of the ideas from the logic in `emit.cpp`. IR values are now automatically given names based on their "name hint" decoration, if any, to make the code easier to follow, and I also made it so that types and literals get collapsed into their use sites in a new "simplified" IR dump mode (which is currently the default, with no way to opt into the other mode without tweaking the code). The resulting IR dumps are much nicer to look at, but as a result the one test that involves IR dumping (`ir/string-literal`) doesn't really test what it used to. One weird issue that came up during testing is that the `transitive-interface` test had previously been producing output that made no sense (that is, the expected output file wasn't really sensible), and somehow these changes were altering its behavior. Changing the test to use `int` values instead of `float` was enough to make the output be what I'd expect, and hand inspection of generating DXBC has me convinced we were compiling the `float` case correctly too. There appears to be some issue around tests with floating-point outputs that we should investigate. * fixup: C++ declaration order --- source/slang/ir-serialize.cpp | 172 ++++++++++++++++++------------------------ 1 file changed, 72 insertions(+), 100 deletions(-) (limited to 'source/slang/ir-serialize.cpp') diff --git a/source/slang/ir-serialize.cpp b/source/slang/ir-serialize.cpp index 0fc8a0806..f3ccbd6c1 100644 --- a/source/slang/ir-serialize.cpp +++ b/source/slang/ir-serialize.cpp @@ -20,7 +20,6 @@ variables, but only a few types include extra data, and these do not have any op * IRConstant - Needs special-case handling * 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 @@ -41,12 +40,6 @@ bother to check if it's correct, and just casts it. { 0, 0 } // Int64, }; -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); @@ -501,13 +494,6 @@ Result IRSerialWriter::write(IRModule* module, SourceManager* sourceManager, Opt } continue; } - IRGlobalValue* globValue = as(srcInst); - if (globValue) - { - dstInst.m_payloadType = PayloadType::String_1; - dstInst.m_payload.m_stringIndices[0] = getStringIndex(globValue->mangledName); - continue; - } IRTextureTypeBase* textureBase = as(srcInst); if (textureBase) @@ -1340,104 +1326,90 @@ int64_t _calcChunkTotalSize(const IRSerialBinary::Chunk& chunk) const IROp op((IROp)srcInst.m_op); - if (isGlobalValueDerived(op)) + if (isConstant(op)) { - // Cannot have operands - SLANG_ASSERT(srcInst.getNumOperands() == 0); - - IRGlobalValue* globalValueInst = static_cast(createEmptyInstWithSize(module, op, sizeof(IRGlobalValue))); - insts[i] = globalValueInst; - // Set the global value - SLANG_ASSERT(srcInst.m_payloadType == PayloadType::String_1); - globalValueInst->mangledName = m_stringRepresentationCache.getName(StringHandle(srcInst.m_payload.m_stringIndices[0])); - } - else - { - if (isConstant(op)) - { - // Handling of constants + // Handling of constants - // Calculate the minimum object size (ie not including the payload of value) - const size_t prefixSize = SLANG_OFFSET_OF(IRConstant, value); + // 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_BoolLit: - { - SLANG_ASSERT(srcInst.m_payloadType == PayloadType::UInt32); - irConst = static_cast(createEmptyInstWithSize(module, op, prefixSize + sizeof(IRIntegerValue))); - irConst->value.intVal = srcInst.m_payload.m_uint32 != 0; - break; - } - case kIROp_IntLit: - { - SLANG_ASSERT(srcInst.m_payloadType == PayloadType::Int64); - irConst = static_cast(createEmptyInstWithSize(module, op, prefixSize + sizeof(IRIntegerValue))); - irConst->value.intVal = srcInst.m_payload.m_int64; - break; - } - case kIROp_PtrLit: - { - SLANG_ASSERT(srcInst.m_payloadType == PayloadType::Int64); - irConst = static_cast(createEmptyInstWithSize(module, op, prefixSize + sizeof(void*))); - irConst->value.ptrVal = (void*) (intptr_t) srcInst.m_payload.m_int64; - break; - } - case kIROp_FloatLit: - { - SLANG_ASSERT(srcInst.m_payloadType == PayloadType::Float64); - irConst = static_cast(createEmptyInstWithSize(module, op, prefixSize + sizeof(IRFloatingPointValue))); - irConst->value.floatVal = srcInst.m_payload.m_float64; - break; - } - case kIROp_StringLit: - { - SLANG_ASSERT(srcInst.m_payloadType == PayloadType::String_1); + IRConstant* irConst = nullptr; + switch (op) + { + case kIROp_BoolLit: + { + SLANG_ASSERT(srcInst.m_payloadType == PayloadType::UInt32); + irConst = static_cast(createEmptyInstWithSize(module, op, prefixSize + sizeof(IRIntegerValue))); + irConst->value.intVal = srcInst.m_payload.m_uint32 != 0; + break; + } + case kIROp_IntLit: + { + SLANG_ASSERT(srcInst.m_payloadType == PayloadType::Int64); + irConst = static_cast(createEmptyInstWithSize(module, op, prefixSize + sizeof(IRIntegerValue))); + irConst->value.intVal = srcInst.m_payload.m_int64; + break; + } + case kIROp_PtrLit: + { + SLANG_ASSERT(srcInst.m_payloadType == PayloadType::Int64); + irConst = static_cast(createEmptyInstWithSize(module, op, prefixSize + sizeof(void*))); + irConst->value.ptrVal = (void*) (intptr_t) srcInst.m_payload.m_int64; + break; + } + case kIROp_FloatLit: + { + SLANG_ASSERT(srcInst.m_payloadType == PayloadType::Float64); + irConst = static_cast(createEmptyInstWithSize(module, op, prefixSize + sizeof(IRFloatingPointValue))); + irConst->value.floatVal = srcInst.m_payload.m_float64; + break; + } + case kIROp_StringLit: + { + SLANG_ASSERT(srcInst.m_payloadType == PayloadType::String_1); - const UnownedStringSlice slice = m_stringRepresentationCache.getStringSlice(StringHandle(srcInst.m_payload.m_stringIndices[0])); + const UnownedStringSlice slice = m_stringRepresentationCache.getStringSlice(StringHandle(srcInst.m_payload.m_stringIndices[0])); - const size_t sliceSize = slice.size(); - const size_t instSize = prefixSize + SLANG_OFFSET_OF(IRConstant::StringValue, chars) + sliceSize; + const size_t sliceSize = slice.size(); + const size_t instSize = prefixSize + SLANG_OFFSET_OF(IRConstant::StringValue, chars) + sliceSize; - irConst = static_cast(createEmptyInstWithSize(module, op, instSize)); + irConst = static_cast(createEmptyInstWithSize(module, op, instSize)); - IRConstant::StringValue& dstString = irConst->value.stringVal; + 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; - } + dstString.numChars = uint32_t(sliceSize); + // Turn into pointer to avoid warning of array overrun + char* dstChars = dstString.chars; + // Copy the chars + memcpy(dstChars, slice.begin(), sliceSize); + break; + } + default: + { + SLANG_ASSERT(!"Unknown constant type"); + return SLANG_FAIL; } - - insts[i] = irConst; } - else if (isTextureTypeBase(op)) - { - IRTextureTypeBase* inst = static_cast(createEmptyInst(module, op, 1)); - SLANG_ASSERT(srcInst.m_payloadType == PayloadType::OperandAndUInt32); - // Reintroduce the texture type bits into the the - const uint32_t other = srcInst.m_payload.m_operandAndUInt32.m_uint32; - inst->op = IROp(uint32_t(inst->op) | (other << kIROpMeta_OtherShift)); + insts[i] = irConst; + } + else if (isTextureTypeBase(op)) + { + IRTextureTypeBase* inst = static_cast(createEmptyInst(module, op, 1)); + SLANG_ASSERT(srcInst.m_payloadType == PayloadType::OperandAndUInt32); - insts[i] = inst; - } - else - { - int numOperands = srcInst.getNumOperands(); - insts[i] = createEmptyInst(module, op, numOperands); - } - } - } + // Reintroduce the texture type bits into the the + const uint32_t other = srcInst.m_payload.m_operandAndUInt32.m_uint32; + inst->op = IROp(uint32_t(inst->op) | (other << kIROpMeta_OtherShift)); + + insts[i] = inst; + } + else + { + int numOperands = srcInst.getNumOperands(); + insts[i] = createEmptyInst(module, op, numOperands); + } + } // Patch up the operands for (int i = 1; i < numInsts; ++i) -- cgit v1.2.3