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.cpp | 785 ++++++++++++++++++++++++++++++---------------------- 1 file changed, 456 insertions(+), 329 deletions(-) (limited to 'source/slang/ir.cpp') diff --git a/source/slang/ir.cpp b/source/slang/ir.cpp index 784c5034d..b8892cc02 100644 --- a/source/slang/ir.cpp +++ b/source/slang/ir.cpp @@ -11,10 +11,10 @@ namespace Slang { struct IRSpecContext; - IRGlobalValue* cloneGlobalValueWithMangledName( - IRSpecContext* context, - Name* mangledName, - IRGlobalValue* originalVal); + IRInst* cloneGlobalValueWithLinkage( + IRSpecContext* context, + IRInst* originalVal, + IRLinkageDecoration* originalLinkage); struct IROpMapEntry { @@ -1876,8 +1876,8 @@ namespace Slang } void addGlobalValue( - IRBuilder* builder, - IRGlobalValue* value) + IRBuilder* builder, + IRInst* value) { // Try to find a suitable parent for the // global value we are emitting. @@ -2057,21 +2057,6 @@ namespace Slang return irGeneric; } - - IRWitnessTable * IRBuilder::lookupWitnessTable(Name* mangledName) - { - IRWitnessTable * result; - if (sharedBuilder->witnessTableMap.TryGetValue(mangledName, result)) - return result; - return nullptr; - } - - - void IRBuilder::registerWitnessTable(IRWitnessTable * table) - { - sharedBuilder->witnessTableMap[table->mangledName] = table; - } - IRBlock* IRBuilder::createBlock() { return createInst( @@ -2628,11 +2613,13 @@ namespace Slang struct IRDumpContext { - StringBuilder* builder; - int indent = 0; + StringBuilder* builder = nullptr; + int indent = 0; + IRDumpMode mode = IRDumpMode::Simplified; - UInt idCounter = 1; - Dictionary mapValueToID; + Dictionary mapValueToName; + Dictionary uniqueNameCounters; + UInt uniqueIDCounter = 1; }; static void dump( @@ -2642,21 +2629,27 @@ namespace Slang context->builder->append(text); } + static void dump( + IRDumpContext* context, + String const& text) + { + context->builder->append(text); + } + + /* static void dump( IRDumpContext* context, UInt val) { context->builder->append(UnambigousUInt(val)); -// fprintf(context->file, "%llu", (unsigned long long)val); } + */ static void dump( IRDumpContext* context, IntegerLiteralValue val) { context->builder->append(val); - -// fprintf(context->file, "%llu", (unsigned long long)val); } static void dump( @@ -2664,8 +2657,6 @@ namespace Slang FloatingPointLiteralValue val) { context->builder->append(val); - -// fprintf(context->file, "%llu", (unsigned long long)val); } static void dumpIndent( @@ -2697,21 +2688,115 @@ namespace Slang return inst->firstUse != nullptr; } - static UInt getID( + static void scrubName( + String const& name, + StringBuilder& sb) + { + // Note: this function duplicates a lot of the logic + // in `EmitVisitor::scrubName`, so we should consider + // whether they can share code at some point. + // + // There is no requirement that assembly dumps and output + // code follow the same model, though, so this is just + // a nice-to-have rather than a maintenance problem + // waiting to happen. + + // Allow an empty nam + // Special case a name that is the empty string, just in case. + if(name.Length() == 0) + { + sb.append('_'); + } + + int prevChar = -1; + for(auto c : name) + { + if(c == '.') + { + c = '_'; + } + + if(((c >= 'a') && (c <= 'z')) + || ((c >= 'A') && (c <= 'Z'))) + { + // Ordinary ASCII alphabetic characters are assumed + // to always be okay. + } + else if((c >= '0') && (c <= '9')) + { + // We don't want to allow a digit as the first + // byte in a name. + if(prevChar == -1) + { + sb.append('_'); + } + } + else + { + // If we run into a character that wouldn't normally + // be allowed in an identifier, we need to translate + // it into something that *is* valid. + // + // Our solution for now will be very clumsy: we will + // emit `x` and then the hexadecimal version of + // the byte we were given. + sb.append("x"); + sb.append(uint32_t((unsigned char) c), 16); + + // We don't want to apply the default handling below, + // so skip to the top of the loop now. + prevChar = c; + continue; + } + + sb.append(c); + prevChar = c; + } + + // If the whole thing ended with a digit, then add + // a final `_` just to make sure that we can append + // a unique ID suffix without risk of collisions. + if(('0' <= prevChar) && (prevChar <= '9')) + { + sb.append('_'); + } + } + + + static String getName( IRDumpContext* context, IRInst* value) { - UInt id = 0; - if (context->mapValueToID.TryGetValue(value, id)) - return id; + String name = 0; + if (context->mapValueToName.TryGetValue(value, name)) + return name; - if (opHasResult(value) || instHasUses(value)) + if(auto nameHintDecoration = value->findDecoration()) { - id = context->idCounter++; - } + String nameHint = nameHintDecoration->getName(); + + StringBuilder sb; + scrubName(nameHint, sb); + + String key = sb.ProduceString(); + UInt count = 0; + context->uniqueNameCounters.TryGetValue(key, count); - context->mapValueToID.Add(value, id); - return id; + context->uniqueNameCounters[key] = count+1; + + if(count) + { + sb.append(count); + } + return sb.ProduceString(); + } + else + { + StringBuilder sb; + auto id = context->uniqueIDCounter++; + sb.append(id); + return sb.ProduceString(); + } } static void dumpID( @@ -2724,26 +2809,10 @@ namespace Slang return; } - if (auto globalValue = as(inst)) - { - auto mangledName = globalValue->mangledName; - if(mangledName) - { - auto mangledNameText = getText(mangledName); - if (mangledNameText.Length() > 0) - { - dump(context, "@"); - dump(context, mangledNameText.Buffer()); - return; - } - } - } - - UInt id = getID(context, inst); - if (id) + if( opHasResult(inst) || instHasUses(inst) ) { dump(context, "%"); - dump(context, id); + dump(context, getName(context, inst)); } else { @@ -2859,6 +2928,38 @@ namespace Slang IRDumpContext* context, IRType* type); + static bool shouldFoldInstIntoUses( + IRDumpContext* context, + IRInst* inst) + { + // Never fold an instruction into its use site + // in the "detailed" mode, so that we always + // accurately reflect the structure of the IR. + // + if(context->mode == IRDumpMode::Detailed) + return false; + + if(as(inst)) + return true; + + if(as(inst)) + return true; + + return false; + } + + static void dumpInst( + IRDumpContext* context, + IRInst* inst); + + static void dumpInstBody( + IRDumpContext* context, + IRInst* inst); + + static void dumpInstExpr( + IRDumpContext* context, + IRInst* inst); + static void dumpOperand( IRDumpContext* context, IRInst* inst) @@ -2870,25 +2971,10 @@ namespace Slang return; } - switch (inst->op) + if(shouldFoldInstIntoUses(context, inst)) { - case kIROp_IntLit: - dump(context, ((IRConstant*)inst)->value.intVal); - return; - - case kIROp_FloatLit: - dump(context, ((IRConstant*)inst)->value.floatVal); + dumpInstExpr(context, inst); return; - - case kIROp_BoolLit: - dump(context, ((IRConstant*)inst)->value.intVal ? "true" : "false"); - return; - case kIROp_StringLit: - dumpEncodeString(context, static_cast(inst)->getStringSlice()); - return; - - default: - break; } dumpID(context, inst); @@ -2919,9 +3005,39 @@ namespace Slang } - static void dumpInst( + void dumpIRDecorations( IRDumpContext* context, - IRInst* inst); + IRInst* inst) + { + for(auto dd : inst->getDecorations()) + { + // Certain decorations aren't helpful to appear + // in output dumps, so we will only include them + // in the "detailed" dumping mode. + // + // For all other modes, we will check the opcode + // and skip selected decorations. + // + if(context->mode != IRDumpMode::Detailed) + { + switch(dd->op) + { + default: + break; + + case kIROp_HighLevelDeclDecoration: + case kIROp_LayoutDecoration: + continue; + } + } + + dump(context, "["); + dumpInstBody(context, dd); + dump(context, "]\n"); + + dumpIndent(context); + } + } static void dumpBlock( IRDumpContext* context, @@ -2952,6 +3068,7 @@ namespace Slang inst = inst->getNextInst(); dumpIndent(context); + dumpIRDecorations(context, param); dump(context, "param "); dumpID(context, param); dumpInstTypeClause(context, param->getFullType()); @@ -2965,25 +3082,6 @@ namespace Slang for(; inst; inst = inst->getNextInst()) { dumpInst(context, inst); - dump(context, "\n"); - } - } - - static void dumpInstBody( - IRDumpContext* context, - IRInst* inst); - - void dumpIRDecorations( - IRDumpContext* context, - IRInst* inst) - { - for(auto dd : inst->getDecorations()) - { - dump(context, "["); - dumpInstBody(context, dd); - dump(context, "]\n"); - - dumpIndent(context); } } @@ -3021,20 +3119,10 @@ namespace Slang } context->indent--; - dump(context, "}\n"); + dump(context, "}"); } - String dumpIRFunc(IRFunc* func) - { - IRDumpContext dumpContext; - StringBuilder sbDump; - dumpContext.builder = &sbDump; - dumpIRGlobalValueWithCode(&dumpContext, func); - auto strFunc = sbDump.ToString(); - return strFunc; - } - void dumpIRWitnessTableEntry( IRDumpContext* context, IRWitnessTableEntry* entry) @@ -3075,7 +3163,6 @@ namespace Slang for(auto child : inst->getChildren()) { dumpInst(context, child); - dump(context, "\n"); } context->indent--; @@ -3096,13 +3183,91 @@ namespace Slang for (auto ii : witnessTable->getChildren()) { dumpInst(context, ii); - dump(context, "\n"); } context->indent--; dump(context, "}\n"); } + static void dumpInstExpr( + IRDumpContext* context, + IRInst* inst) + { + if (!inst) + { + dump(context, ""); + return; + } + + auto op = inst->op; + auto opInfo = getIROpInfo(op); + + // Special-case the literal instructions. + if(auto irConst = as(inst)) + { + switch (op) + { + case kIROp_IntLit: + dump(context, irConst->value.intVal); + return; + + case kIROp_FloatLit: + dump(context, irConst->value.floatVal); + return; + + case kIROp_BoolLit: + dump(context, irConst->value.intVal ? "true" : "false"); + return; + + case kIROp_StringLit: + dumpEncodeString(context, irConst->getStringSlice()); + return; + + case kIROp_PtrLit: + dump(context, ""); + return; + + default: + break; + } + } + + dump(context, opInfo.name); + + UInt argCount = inst->getOperandCount(); + + if(argCount == 0) + return; + + UInt ii = 0; + + // Special case: make printing of `call` a bit + // nicer to look at + if (inst->op == kIROp_Call && argCount > 0) + { + dump(context, " "); + auto argVal = inst->getOperand(ii++); + dumpOperand(context, argVal); + } + + bool first = true; + dump(context, "("); + for (; ii < argCount; ++ii) + { + if (!first) + dump(context, ", "); + + auto argVal = inst->getOperand(ii); + + dumpOperand(context, argVal); + + first = false; + } + + dump(context, ")"); + + } + static void dumpInstBody( IRDumpContext* context, IRInst* inst) @@ -3143,7 +3308,6 @@ namespace Slang } // Okay, we have a seemingly "ordinary" op now - auto opInfo = getIROpInfo(op); auto dataType = inst->getDataType(); auto rate = inst->getRate(); @@ -3166,57 +3330,19 @@ namespace Slang // No result, okay... } - dump(context, opInfo.name); - - UInt argCount = inst->getOperandCount(); - UInt ii = 0; - - // Special case: make printing of `call` a bit - // nicer to look at - if (inst->op == kIROp_Call && argCount > 0) - { - dump(context, " "); - auto argVal = inst->getOperand(ii++); - dumpOperand(context, argVal); - } - - bool first = true; - dump(context, "("); - for (; ii < argCount; ++ii) - { - if (!first) - dump(context, ", "); - - auto argVal = inst->getOperand(ii); - - dumpOperand(context, argVal); - - first = false; - } - - // Special cases: literals and other instructions with no real operands - switch (inst->op) - { - case kIROp_IntLit: - case kIROp_FloatLit: - case kIROp_BoolLit: - case kIROp_StringLit: - dumpOperand(context, inst); - break; - - default: - break; - } - - dump(context, ")"); + dumpInstExpr(context, inst); } static void dumpInst( IRDumpContext* context, IRInst* inst) { + if(shouldFoldInstIntoUses(context, inst)) + return; + dumpIndent(context); dumpInstBody(context, inst); + dump(context, "\n"); } void dumpIRModule( @@ -3226,51 +3352,45 @@ namespace Slang for(auto ii : module->getGlobalInsts()) { dumpInst(context, ii); - dump(context, "\n"); } } - void printSlangIRAssembly(StringBuilder& builder, IRModule* module) + void printSlangIRAssembly(StringBuilder& builder, IRModule* module, IRDumpMode mode) { IRDumpContext context; context.builder = &builder; context.indent = 0; + context.mode = mode; dumpIRModule(&context, module); } - void dumpIR(IRGlobalValue* globalVal, ISlangWriter* writer) + void dumpIR(IRInst* globalVal, ISlangWriter* writer, IRDumpMode mode) { StringBuilder sb; IRDumpContext context; context.builder = &sb; context.indent = 0; + context.mode = mode; dumpInst(&context, globalVal); writer->write(sb.Buffer(), sb.Length()); - char cr[] = "\n"; - writer->write(cr, 1); - writer->flush(); } - String getSlangIRAssembly(IRModule* module) + String getSlangIRAssembly(IRModule* module, IRDumpMode mode) { StringBuilder sb; - printSlangIRAssembly(sb, module); + printSlangIRAssembly(sb, module, mode); return sb; } - void dumpIR(IRModule* module, ISlangWriter* writer) + void dumpIR(IRModule* module, ISlangWriter* writer, IRDumpMode mode) { - String ir = getSlangIRAssembly(module); - + String ir = getSlangIRAssembly(module, mode); writer->write(ir.Buffer(), ir.Length()); - char cr[] = "\n"; - writer->write(cr, 1); - writer->flush(); } @@ -3566,9 +3686,6 @@ namespace Slang if(as(this)) return false; - if(as(this)) - return false; - if(as(this)) return false; @@ -3606,6 +3723,19 @@ namespace Slang } return true; + // All of the cases for "global values" are side-effect-free. + case kIROp_StructType: + case kIROp_StructField: + case kIROp_Func: + case kIROp_Generic: + case kIROp_GlobalVar: + case kIROp_GlobalConstant: + case kIROp_StructKey: + case kIROp_GlobalGenericParam: + case kIROp_WitnessTable: + case kIROp_WitnessTableEntry: + return false; + case kIROp_Nop: case kIROp_Specialize: case kIROp_lookup_interface_method: @@ -3686,8 +3816,8 @@ namespace Slang } void moveValueBefore( - IRGlobalValue* valueToMove, - IRGlobalValue* placeBefore) + IRInst* valueToMove, + IRInst* placeBefore) { valueToMove->removeFromParent(); valueToMove->insertBefore(placeBefore); @@ -4158,7 +4288,7 @@ namespace Slang if( systemValueInfo ) { - globalVariable->mangledName = builder->getSession()->getNameObj(systemValueInfo->name); + builder->addImportDecoration(globalVariable, UnownedTerminatedStringSlice(systemValueInfo->name)); if( auto fromType = systemValueInfo->requiredType ) { @@ -5214,7 +5344,7 @@ namespace Slang struct IRSpecSymbol : RefObject { - IRGlobalValue* irGlobalValue; + IRInst* irGlobalValue; RefPtr nextWithSameName; }; @@ -5241,7 +5371,7 @@ namespace Slang // A map from mangled symbol names to zero or // more global IR values that have that name, // in the *original* module. - typedef Dictionary> SymbolDictionary; + typedef Dictionary> SymbolDictionary; SymbolDictionary symbols; SharedIRBuilder sharedBuilderStorage; @@ -5251,6 +5381,47 @@ namespace Slang IRSpecEnv globalEnv; }; + struct IRGenericSpecKey + { + // Note: Slang::Dictionary requires key types to have default constructors + IRGenericSpecKey() + {} + + IRGenericSpecKey(IRSpecialize* specializeInst) + { + m_values.Add(specializeInst->getBase()); + auto argCount = specializeInst->getArgCount(); + for(UInt aa = 0; aa < argCount; ++aa) + { + m_values.Add(specializeInst->getArg(aa)); + } + } + + List m_values; + + bool operator==(IRGenericSpecKey const& other) const + { + auto valueCount = m_values.Count(); + if(valueCount != other.m_values.Count()) return false; + for(UInt ii = 0; ii < valueCount; ++ii) + { + if(m_values[ii] != other.m_values[ii]) return false; + } + return true; + } + + UInt GetHashCode() const + { + auto hash = 0; + auto valueCount = m_values.Count(); + for(UInt ii = 0; ii < valueCount; ++ii) + { + hash = combineHash(hash, Slang::GetHashCode(m_values[ii])); + } + return hash; + } + }; + struct IRSharedGenericSpecContext : IRSharedSpecContext { // Instructions to be processed (for generic specialization context) @@ -5276,13 +5447,15 @@ namespace Slang } return nullptr; } + + Dictionary specializations; }; struct IRSpecContextBase { // A map from the mangled name of a global variable // to the layout to use for it. - Dictionary globalVarLayouts; + Dictionary globalVarLayouts; IRSharedSpecContext* shared; @@ -5405,6 +5578,26 @@ namespace Slang clonedValue->sourceLoc = originalValue->sourceLoc; } + /// Clone any decorations and children from `originalValue` onto `clonedValue` + void cloneDecorationsAndChildren( + IRSpecContextBase* context, + IRInst* clonedValue, + IRInst* originalValue) + { + IRBuilder builderStorage = *context->builder; + IRBuilder* builder = &builderStorage; + builder->setInsertInto(clonedValue); + + SLANG_UNUSED(context); + for(auto originalItem : originalValue->getDecorationsAndChildren()) + { + cloneInst(context, builder, originalItem); + } + + // We will also clone the location here, just because this is a convenient bottleneck + clonedValue->sourceLoc = originalValue->sourceLoc; + } + // We use an `IRSpecContext` for the case where we are cloning // code from one or more input modules to create a "linked" output // module. Along the way, we will resolve profile-specific functions @@ -5417,7 +5610,7 @@ namespace Slang }; - IRGlobalValue* cloneGlobalValue(IRSpecContext* context, IRGlobalValue* originalVal); + IRInst* cloneGlobalValue(IRSpecContext* context, IRInst* originalVal); IRInst* cloneValue( IRSpecContextBase* context, @@ -5429,13 +5622,18 @@ namespace Slang IRInst* IRSpecContext::maybeCloneValue(IRInst* originalValue) { - if (auto globalValue = as(originalValue)) - { - return cloneGlobalValue(this, globalValue); - } - switch (originalValue->op) { + case kIROp_StructType: + case kIROp_Func: + case kIROp_Generic: + case kIROp_GlobalVar: + case kIROp_GlobalConstant: + case kIROp_StructKey: + case kIROp_GlobalGenericParam: + case kIROp_WitnessTable: + return cloneGlobalValue(this, originalValue); + case kIROp_BoolLit: { IRConstant* c = (IRConstant*)originalValue; @@ -5491,7 +5689,7 @@ namespace Slang IRInst* clonedArg = cloneValue(this, originalArg); clonedValue->getOperands()[aa].init(clonedValue, clonedArg); } - cloneDecorations(this, clonedValue, originalValue); + cloneDecorationsAndChildren(this, clonedValue, originalValue); addHoistableInst(builder, clonedValue); @@ -5542,22 +5740,6 @@ namespace Slang return (IRType*)cloneValue(context, originalType); } - IRInst* maybeCloneValueWithMangledName( - IRSpecContextBase* context, - IRGlobalValue* originalValue) - { - for(auto ii : context->shared->module->getGlobalInsts()) - { - auto gv = as(ii); - if (!gv) - continue; - - if (gv->mangledName == originalValue->mangledName) - return gv; - } - return cloneValue(context, originalValue); - } - void cloneGlobalValueWithCodeCommon( IRSpecContextBase* context, IRGlobalValueWithCode* clonedValue, @@ -5597,13 +5779,19 @@ namespace Slang registerClonedValue(context, clonedVar, originalValues); +#if 0 auto mangledName = originalVar->mangledName; clonedVar->mangledName = mangledName; +#endif - VarLayout* layout = nullptr; - if (context->globalVarLayouts.TryGetValue(mangledName, layout)) + if(auto linkage = originalVar->findDecoration()) { - builder->addLayoutDecoration(clonedVar, layout); + auto mangledName = String(linkage->getMangledName()); + VarLayout* layout = nullptr; + if (context->globalVarLayouts.TryGetValue(mangledName, layout)) + { + builder->addLayoutDecoration(clonedVar, layout); + } } // Clone any code in the body of the variable, since this @@ -5626,9 +5814,6 @@ namespace Slang cloneType(context, originalVal->getFullType())); registerClonedValue(context, clonedVal, originalValues); - auto mangledName = originalVal->mangledName; - clonedVal->mangledName = mangledName; - // Clone any code in the body of the constant, since this // represents the initializer. cloneGlobalValueWithCodeCommon( @@ -5648,9 +5833,6 @@ namespace Slang auto clonedVal = builder->emitGeneric(); registerClonedValue(context, clonedVal, originalValues); - auto mangledName = originalVal->mangledName; - clonedVal->mangledName = mangledName; - // Clone any code in the body of the generic, since this // computes its result value. cloneGlobalValueWithCodeCommon( @@ -5663,17 +5845,14 @@ namespace Slang void cloneSimpleGlobalValueImpl( IRSpecContextBase* context, - IRGlobalValue* originalInst, + IRInst* originalInst, IROriginalValuesForClone const& originalValues, - IRGlobalValue* clonedInst, + IRInst* clonedInst, bool registerValue = true) { if (registerValue) registerClonedValue(context, clonedInst, originalValues); - auto mangledName = originalInst->mangledName; - clonedInst->mangledName = mangledName; - // Set up an IR builder for inserting into the inst IRBuilder builderStorage = *context->builder; IRBuilder* builder = &builderStorage; @@ -5805,7 +5984,7 @@ namespace Slang } - void checkIRDuplicate(IRInst* inst, IRInst* moduleInst, Name* mangledName) + void checkIRDuplicate(IRInst* inst, IRInst* moduleInst, UnownedStringSlice const& mangledName) { #ifdef _DEBUG for (auto child : moduleInst->getDecorationsAndChildren()) @@ -5813,13 +5992,12 @@ namespace Slang if (child == inst) continue; - if (child->op == kIROp_Func) + if(auto childLinkage = child->findDecoration()) { - auto extName = ((IRGlobalValue*)child)->mangledName; - if (extName == mangledName || - (extName && mangledName && - extName->text == mangledName->text)) - SLANG_UNEXPECTED("duplicate global var"); + if(mangledName == childLinkage->getMangledName()) + { + SLANG_UNEXPECTED("duplicate global instruction"); + } } } #else @@ -5836,7 +6014,6 @@ namespace Slang bool checkDuplicate = true) { // First clone all the simple properties. - clonedFunc->mangledName = originalFunc->mangledName; clonedFunc->setFullType(cloneType(context, originalFunc->getFullType())); cloneGlobalValueWithCodeCommon( @@ -5849,8 +6026,14 @@ namespace Slang // // TODO: This isn't really a good requirement to place on the IR... clonedFunc->moveToEnd(); - if (checkDuplicate) - checkIRDuplicate(clonedFunc, context->getModule()->getModuleInst(), clonedFunc->mangledName); + + if( checkDuplicate ) + { + if( auto linkage = clonedFunc->findDecoration() ) + { + checkIRDuplicate(clonedFunc, context->getModule()->getModuleInst(), linkage->getMangledName()); + } + } } IRFunc* specializeIRForEntryPoint( @@ -5859,7 +6042,7 @@ namespace Slang EntryPointLayout* entryPointLayout) { // Look up the IR symbol by name - auto mangledName = context->getModule()->session->getNameObj(getMangledName(entryPointRequest->decl)); + auto mangledName = getMangledName(entryPointRequest->decl); RefPtr sym; if (!context->getSymbols().TryGetValue(mangledName, sym)) { @@ -5970,7 +6153,7 @@ namespace Slang }; TargetSpecializationLevel getTargetSpecialiationLevel( - IRGlobalValue* inVal, + IRInst* inVal, String const& targetName) { // HACK: Currently the front-end is placing modifiers related @@ -6039,7 +6222,7 @@ namespace Slang } bool isDefinition( - IRGlobalValue* inVal) + IRInst* inVal) { IRInst* val = inVal; // unwrap any generic declarations to see @@ -6057,6 +6240,10 @@ namespace Slang val = returnVal; } + // TODO: the logic here should probably + // be that anything with an `IRImportDecoration` + // is considered to be a declaration rather than definition. + switch (val->op) { case kIROp_WitnessTable: @@ -6081,8 +6268,8 @@ namespace Slang // to check if things are even available in the first place... bool isBetterForTarget( IRSpecContext* context, - IRGlobalValue* newVal, - IRGlobalValue* oldVal) + IRInst* newVal, + IRInst* oldVal) { String targetName = getTargetName(context); @@ -6207,24 +6394,26 @@ namespace Slang return clonedInst; } - IRGlobalValue* cloneGlobalValueImpl( + IRInst* cloneGlobalValueImpl( IRSpecContext* context, - IRGlobalValue* originalInst, + IRInst* originalInst, IROriginalValuesForClone const& originalValues) { auto clonedValue = cloneInst(context, &context->shared->builderStorage, originalInst, originalValues); clonedValue->moveToEnd(); - return cast(clonedValue); + return clonedValue; } - // Clone a global value, which has the given `mangledName`. - // The `originalVal` is a known global IR value with that name, if one is available. - // (It is okay for this parameter to be null). - IRGlobalValue* cloneGlobalValueWithMangledName( - IRSpecContext* context, - Name* mangledName, - IRGlobalValue* originalVal) + /// Clone a global value, which has the given `originalLinkage`. + /// + /// The `originalVal` is a known global IR value with that linkage, if one is available. + /// (It is okay for this parameter to be null). + /// + IRInst* cloneGlobalValueWithLinkage( + IRSpecContext* context, + IRInst* originalVal, + IRLinkageDecoration* originalLinkage) { // If the global value being cloned is already in target module, don't clone // Why checking this? @@ -6241,11 +6430,11 @@ namespace Slang { if (IRInst* clonedVal = findClonedValue(context, originalVal)) { - return cast(clonedVal); + return clonedVal; } } - if(getText(mangledName).Length() == 0) + if(!originalLinkage) { // If there is no mangled name, then we assume this is a local symbol, // and it can't possibly have multiple declarations. @@ -6257,6 +6446,7 @@ namespace Slang // with the same mangled name as `originalVal` and try // to pick the "best" one for our target. + auto mangledName = String(originalLinkage->getMangledName()); RefPtr sym; if( !context->getSymbols().TryGetValue(mangledName, sym) ) { @@ -6274,10 +6464,10 @@ namespace Slang // more specialized for the chosen target. Otherwise, we simply favor // definitions over declarations. // - IRGlobalValue* bestVal = sym->irGlobalValue; + IRInst* bestVal = sym->irGlobalValue; for( auto ss = sym->nextWithSameName; ss; ss = ss->nextWithSameName ) { - IRGlobalValue* newVal = ss->irGlobalValue; + IRInst* newVal = ss->irGlobalValue; if(isBetterForTarget(context, newVal, bestVal)) bestVal = newVal; } @@ -6289,30 +6479,25 @@ namespace Slang { if (IRInst* clonedVal = findClonedValue(context, bestVal)) { - return cast(clonedVal); + return clonedVal; } } return cloneGlobalValueImpl(context, bestVal, IROriginalValuesForClone(sym)); } - IRGlobalValue* cloneGlobalValueWithMangledName(IRSpecContext* context, Name* mangledName) - { - return cloneGlobalValueWithMangledName(context, mangledName, nullptr); - } - // Clone a global value, where `originalVal` is one declaration/definition, but we might // have to consider others, in order to find the "best" version of the symbol. - IRGlobalValue* cloneGlobalValue(IRSpecContext* context, IRGlobalValue* originalVal) + IRInst* cloneGlobalValue(IRSpecContext* context, IRInst* originalVal) { // We are being asked to clone a particular global value, but in // the IR that comes out of the front-end there could still // be multiple, target-specific, declarations of any given // global value, all of which share the same mangled name. - return cloneGlobalValueWithMangledName( + return cloneGlobalValueWithLinkage( context, - originalVal->mangledName, - originalVal); + originalVal, + originalVal->findDecoration()); } StructTypeLayout* getGlobalStructLayout( @@ -6320,16 +6505,18 @@ namespace Slang void insertGlobalValueSymbol( IRSharedSpecContext* sharedContext, - IRGlobalValue* gv) + IRInst* gv) { - auto mangledName = gv->mangledName; + auto linkage = gv->findDecoration(); // Don't try to register a symbol for global values - // with no mangled name, since these represent symbols - // that shouldn't get "linkage" - if (!getText(mangledName).Length()) + // that don't have linkage. + // + if (!linkage) return; + auto mangledName = String(linkage->getMangledName()); + RefPtr sym = new IRSpecSymbol(); sym->irGlobalValue = gv; @@ -6354,10 +6541,7 @@ namespace Slang for(auto ii : originalModule->getGlobalInsts()) { - auto gv = as(ii); - if (!gv) - continue; - insertGlobalValueSymbol(sharedContext, gv); + insertGlobalValueSymbol(sharedContext, ii); } } @@ -6489,7 +6673,7 @@ namespace Slang auto globalStructLayout = getGlobalStructLayout(newProgramLayout); for (auto globalVarLayout : globalStructLayout->fields) { - auto mangledName = compileRequest->mSession->getNameObj(getMangledName(globalVarLayout->varDecl)); + auto mangledName = getMangledName(globalVarLayout->varDecl); context->globalVarLayouts.AddIfNotExists(mangledName, globalVarLayout); } @@ -6512,32 +6696,6 @@ namespace Slang return state->irModule; } - IRGlobalValue* getSpecializedGlobalValueForDeclRef( - IRSpecializationState* state, - DeclRef const& declRef) - { - // We will start be ensuring that we have code for - // the declaration itself. - auto decl = declRef.getDecl(); - auto mangledDeclName = getMangledName(decl); - - IRGlobalValue* irDeclVal = cloneGlobalValueWithMangledName( - state->getContext(), - state->getContext()->getModule()->session->getNameObj(mangledDeclName)); - if(!irDeclVal) - return nullptr; - - // Now we need to deal with specializing the given - // IR value based on the substitutions applied to - // our declaration reference. - - if(!declRef.substitutions) - return irDeclVal; - - SLANG_UNEXPECTED("unhandled"); - UNREACHABLE_RETURN(nullptr); - } - void specializeIRForEntryPoint( IRSpecializationState* state, EntryPointRequest* entryPointRequest, @@ -6662,31 +6820,14 @@ namespace Slang IRSpecialize* specializeInst) { // First, we want to see if an existing specialization - // has already been made. To do that we will need to - // compute the mangled name of the specialized value, - // so that we can look for existing declarations. - String specMangledName = mangleSpecializedFuncName(getText(genericVal->mangledName), specializeInst); - auto specMangledNameObj = sharedContext->module->session->getNameObj(specMangledName); - - // Now look up an existing symbol with a matching name - RefPtr symb; - if (sharedContext->symbols.TryGetValue(specMangledNameObj, symb)) - { - return symb->irGlobalValue; - } - - // TODO: This is a terrible linear search, and we should - // avoid it by building a dictionary ahead of time, - // as is being done for the `IRSpecContext` used above. - // We can probalby use the same basic context, actually. - for(auto ii : sharedContext->module->getGlobalInsts()) + // has already been made. To do that we will construct a key + // for lookup in the generic specialization context. + // + IRGenericSpecKey specializationKey(specializeInst); { - auto gv = as(ii); - if (!gv) - continue; - - if (gv->mangledName == specMangledNameObj) - return gv; + IRInst* specializedValue = nullptr; + if(sharedContext->specializations.TryGetValue(specializationKey, specializedValue)) + return specializedValue; } // If we get to this point, then we need to construct a @@ -6752,12 +6893,8 @@ namespace Slang if (auto returnValInst = as(ii)) { auto clonedResult = cloneValue(&context, returnValInst->getVal()); - if (auto clonedGlobalValue = as(clonedResult)) - { - clonedGlobalValue->mangledName = specMangledNameObj; - // TODO: create a symbol for it and add it to the map. - } + sharedContext->specializations.Add(specializationKey, clonedResult); return clonedResult; } @@ -6823,20 +6960,10 @@ namespace Slang // We've found the leaf value that will be produced after // all of the specialization is done. Now we want to know // if that is a value suitable for actually specializing - - if (auto globalValue = as(val)) - { - if (isDefinition(globalValue)) - return true; - return false; - } - else - { - // There might be other cases with a declaration-vs-definition - // thing that we need to handle. - + // + if (isDefinition(val)) return true; - } + return false; } } -- cgit v1.2.3