diff options
Diffstat (limited to 'source')
| -rw-r--r-- | source/slang/emit.cpp | 198 | ||||
| -rw-r--r-- | source/slang/ir-insts.h | 11 | ||||
| -rw-r--r-- | source/slang/ir-legalize-types.cpp | 67 | ||||
| -rw-r--r-- | source/slang/ir-ssa.cpp | 21 | ||||
| -rw-r--r-- | source/slang/ir.cpp | 8 | ||||
| -rw-r--r-- | source/slang/ir.h | 1 | ||||
| -rw-r--r-- | source/slang/legalize-types.cpp | 5 | ||||
| -rw-r--r-- | source/slang/lower-to-ir.cpp | 111 |
8 files changed, 411 insertions, 11 deletions
diff --git a/source/slang/emit.cpp b/source/slang/emit.cpp index 9f9a089c8..4ed7dd5a7 100644 --- a/source/slang/emit.cpp +++ b/source/slang/emit.cpp @@ -136,6 +136,14 @@ struct SharedEmitContext // How far are we indented? Int indentLevel = 0; + + // Map a string name to the number of times we have seen this + // name used so far during code emission. + Dictionary<String, UInt> uniqueNameCounters; + + // Map an IR instruction to the name that we've decided + // to use for it when emitting code. + Dictionary<IRInst*, String> mapInstToName; }; struct EmitContext @@ -1895,8 +1903,128 @@ struct EmitVisitor return id; } - String getIRName( - IRInst* inst) + /// "Scrub" a name so that it complies with restrictions of the target language. + String scrubName( + String const& name) + { + // We will use a plain `U` as a dummy character to insert + // whenever we need to insert things to make a string into + // valid name. + // + char const* dummyChar = "U"; + + // Special case a name that is the empty string, just in case. + if(name.Length() == 0) + return dummyChar; + + // Otherwise, we are going to walk over the name byte by byte + // and write some legal characters to the output as we go. + StringBuilder sb; + + if(getTarget(context) == CodeGenTarget::GLSL) + { + // GLSL reserverse all names that start with `gl_`, + // so if we are in danger of collision, then make + // our name start with a dummy character instead. + if(name.StartsWith("gl_")) + { + sb.append(dummyChar); + } + } + + // We will also detect user-defined names that + // might overlap with our convention for mangled names, + // to avoid an possible collision. + if(name.StartsWith("_S")) + { + sb.Append(dummyChar); + } + + // TODO: This is where we might want to consult + // a dictionary of reserved words for the chosen target + // + // if(isReservedWord(name)) { sb.Append(dummyChar); } + // + + // We need to track the previous byte in + // order to detect consecutive underscores for GLSL. + int prevChar = -1; + + for(auto c : name) + { + // We will treat a dot character just like an underscore + // for the purposes of producing a scrubbed name, so + // that we translate `SomeType.someMethod` into + // `SomeType_someMethod`. + // + // By handling this case at the top of this loop, we + // ensure that a `.`-turned-`_` is handled just like + // a `_` in the original name, and will be properly + // scrubbed for GLSL output. + // + 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, since the result wouldn't + // be a valid identifier in many target languages. + if(prevChar == -1) + { + sb.append(dummyChar); + } + } + else if(c == '_') + { + // We will collapse any consecutive sequence of `_` + // characters into a single one (this means that + // some names that were unique in the original + // code might not resolve to unique names after + // scrubbing, but that was true in general). + + if(prevChar == '_') + { + // Skip this underscore, so we don't output + // more than one in a row. + continue; + } + } + 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; + } + + return sb.ProduceString(); + } + + String generateIRName( + IRInst* inst) { // If the instruction names something // that should be emitted as a target intrinsic, @@ -1906,6 +2034,58 @@ struct EmitVisitor return intrinsicDecoration->definition; } + // If we have a name hint on the instruction, then we will try to use that + // to provide the actual name in the output code. + // + // We need to be careful that the name follows the rules of the target language, + // so there is a "scrubbing" step that needs to be applied here. + // + // We also need to make sure that the name won't collide with other declarations + // that might have the same name hint applied, so we will still unique + // them by appending the numeric ID of the instruction. + // + // TODO: Find cases where we can drop the suffix safely. + // + // TODO: When we start having to handle symbols with external linkage for + // things like DXIL libraries, we will need to *not* use the friendly + // names for stuff that should be link-able. + // + if(auto nameHintDecoration = inst->findDecoration<IRNameHintDecoration>()) + { + // The name we output will basically be: + // + // <nameHint>_<uniqueID> + // + // Except that we will "scrub" the name hint first, + // and we will omit the underscore if the (scrubbed) + // name hint already ends with one. + // + + String nameHint = nameHintDecoration->name->text; + nameHint = scrubName(nameHint); + + StringBuilder sb; + sb.append(nameHint); + + // Avoid introducing a double underscore + if(!nameHint.EndsWith("_")) + { + sb.append("_"); + } + + String key = sb.ProduceString(); + UInt count = 0; + context->shared->uniqueNameCounters.TryGetValue(key, count); + + context->shared->uniqueNameCounters[key] = count+1; + + sb.append(count); + return sb.ProduceString(); + } + + + + // If the instruction has a mangled name, then emit using that. if (auto globalValue = as<IRGlobalValue>(inst)) { @@ -1925,9 +2105,23 @@ struct EmitVisitor StringBuilder sb; sb << "_S"; sb << getID(inst); + + return sb.ProduceString(); } + String getIRName( + IRInst* inst) + { + String name; + if(!context->shared->mapInstToName.TryGetValue(inst, name)) + { + name = generateIRName(inst); + context->shared->mapInstToName.Add(inst, name); + } + return name; + } + struct IRDeclaratorInfo { enum class Flavor diff --git a/source/slang/ir-insts.h b/source/slang/ir-insts.h index 22685f316..804c393c2 100644 --- a/source/slang/ir-insts.h +++ b/source/slang/ir-insts.h @@ -121,6 +121,17 @@ struct IRInterpolationModeDecoration : IRDecoration IRInterpolationMode mode; }; +/// A decoration that provides a desired name to be used +/// in conjunction with the given instruction. Back-end +/// code generation may use this to help derive symbol +/// names, emit debug information, etc. +struct IRNameHintDecoration : IRDecoration +{ + enum { kDecorationOp = kIRDecorationOp_NameHint }; + + Name* name; +}; + // // An IR node to represent a reference to an AST-level diff --git a/source/slang/ir-legalize-types.cpp b/source/slang/ir-legalize-types.cpp index 25bd498b8..3465662f3 100644 --- a/source/slang/ir-legalize-types.cpp +++ b/source/slang/ir-legalize-types.cpp @@ -117,6 +117,7 @@ static LegalVal declareVars( LegalType type, TypeLayout* typeLayout, LegalVarChain* varChain, + String const* nameHint, IRGlobalNameInfo* globalNameInfo); static LegalType legalizeType( @@ -708,6 +709,15 @@ RefPtr<VarLayout> findVarLayout(IRInst* value) return nullptr; } +static String const* findNameHint(IRInst* inst) +{ + if( auto nameHintDecoration = inst->findDecoration<IRNameHintDecoration>() ) + { + return &nameHintDecoration->name->text; + } + return nullptr; +} + static LegalVal legalizeLocalVar( IRTypeLegalizationContext* context, IRVar* irLocalVar) @@ -763,7 +773,8 @@ static LegalVal legalizeLocalVar( varChain = &varChainStorage; } - LegalVal newVal = declareVars(context, kIROp_Var, legalValueType, typeLayout, varChain, nullptr); + String const* nameHint = findNameHint(irLocalVar); + LegalVal newVal = declareVars(context, kIROp_Var, legalValueType, typeLayout, varChain, nameHint, nullptr); // Remove the old local var. irLocalVar->removeFromParent(); @@ -794,7 +805,8 @@ static LegalVal legalizeParam( context->insertBeforeParam = originalParam; - auto newVal = declareVars(context, kIROp_Param, legalParamType, nullptr, nullptr, nullptr); + String const* nameHint = findNameHint(originalParam); + auto newVal = declareVars(context, kIROp_Param, legalParamType, nullptr, nullptr, nameHint, nullptr); originalParam->removeFromParent(); context->replacedInstructions.Add(originalParam); @@ -996,12 +1008,25 @@ static LegalVal legalizeFunc( return LegalVal::simple(irFunc); } +static void addNameHint( + IRTypeLegalizationContext* context, + IRInst* inst, + String const& text) +{ + if(text.Length() == 0) + return; + + auto name = context->session->getNameObj(text); + context->builder->addDecoration<IRNameHintDecoration>(inst)->name = name; +} + static LegalVal declareSimpleVar( IRTypeLegalizationContext* context, IROp op, IRType* type, TypeLayout* typeLayout, LegalVarChain* varChain, + String const* nameHint, IRGlobalNameInfo* globalNameInfo) { RefPtr<VarLayout> varLayout = createVarLayout(varChain, typeLayout); @@ -1087,17 +1112,23 @@ static LegalVal declareSimpleVar( { builder->addHighLevelDeclDecoration(irVar, varDeclRef.getDecl()); } + + if( nameHint ) + { + addNameHint(context, irVar, *nameHint); + } } return legalVarVal; } static LegalVal declareVars( - IRTypeLegalizationContext* context, + IRTypeLegalizationContext* context, IROp op, LegalType type, TypeLayout* typeLayout, LegalVarChain* varChain, + String const* nameHint, IRGlobalNameInfo* globalNameInfo) { switch (type.flavor) @@ -1106,7 +1137,7 @@ static LegalVal declareVars( return LegalVal(); case LegalType::Flavor::simple: - return declareSimpleVar(context, op, type.getSimple(), typeLayout, varChain, globalNameInfo); + return declareSimpleVar(context, op, type.getSimple(), typeLayout, varChain, nameHint, globalNameInfo); break; case LegalType::Flavor::implicitDeref: @@ -1120,6 +1151,7 @@ static LegalVal declareVars( type.getImplicitDeref()->valueType, getDerefTypeLayout(typeLayout), varChain, + nameHint, globalNameInfo); return LegalVal::implicitDeref(val); } @@ -1128,8 +1160,8 @@ static LegalVal declareVars( case LegalType::Flavor::pair: { auto pairType = type.getPair(); - auto ordinaryVal = declareVars(context, op, pairType->ordinaryType, typeLayout, varChain, globalNameInfo); - auto specialVal = declareVars(context, op, pairType->specialType, typeLayout, varChain, globalNameInfo); + auto ordinaryVal = declareVars(context, op, pairType->ordinaryType, typeLayout, varChain, nameHint, globalNameInfo); + auto specialVal = declareVars(context, op, pairType->specialType, typeLayout, varChain, nameHint, globalNameInfo); return LegalVal::pair(ordinaryVal, specialVal, pairType->pairInfo); } @@ -1158,12 +1190,28 @@ static LegalVal declareVars( newVarChain = &newVarChainStorage; } + String* fieldNameHint = nullptr; + String joinedNameHintStorage; + if( nameHint ) + { + if( auto fieldNameHintDecoration = ee.key->findDecoration<IRNameHintDecoration>() ) + { + joinedNameHintStorage.append(*nameHint); + joinedNameHintStorage.append("."); + joinedNameHintStorage.append(fieldNameHintDecoration->name->text); + + fieldNameHint = &joinedNameHintStorage; + } + + } + LegalVal fieldVal = declareVars( context, op, ee.type, fieldTypeLayout, newVarChain, + fieldNameHint, globalNameInfo); TuplePseudoVal::Element element; @@ -1222,7 +1270,8 @@ static LegalVal legalizeGlobalVar( globalNameInfo.globalVar = irGlobalVar; globalNameInfo.counter = 0; - LegalVal newVal = declareVars(context, kIROp_GlobalVar, legalValueType, typeLayout, varChain, &globalNameInfo); + String const* nameHint = findNameHint(irGlobalVar); + LegalVal newVal = declareVars(context, kIROp_GlobalVar, legalValueType, typeLayout, varChain, nameHint, &globalNameInfo); // Register the new value as the replacement for the old registerLegalizedValue(context, irGlobalVar, newVal); @@ -1263,7 +1312,9 @@ static LegalVal legalizeGlobalConstant( globalNameInfo.counter = 0; // TODO: need to handle initializer here! - LegalVal newVal = declareVars(context, kIROp_GlobalConstant, legalValueType, nullptr, nullptr, &globalNameInfo); + + String const* nameHint = findNameHint(irGlobalConstant); + LegalVal newVal = declareVars(context, kIROp_GlobalConstant, legalValueType, nullptr, nullptr, nameHint, &globalNameInfo); // Register the new value as the replacement for the old registerLegalizedValue(context, irGlobalConstant, newVal); diff --git a/source/slang/ir-ssa.cpp b/source/slang/ir-ssa.cpp index e97298969..6c4cfa8ff 100644 --- a/source/slang/ir-ssa.cpp +++ b/source/slang/ir-ssa.cpp @@ -350,6 +350,24 @@ IRInst* readVar( SSABlockInfo* blockInfo, IRVar* var); +/// Try to take any name hint on `var` and apply it to `val`. +/// +/// Doesn't do anything if `val` already has a name hint, +/// or if `var` doesn't have one to transfer over. +/// +void maybeApplyNameHint( + ConstructSSAContext* context, + IRVar* var, + IRInst* val) +{ + if( auto nameHint = var->findDecoration<IRNameHintDecoration>() ) + { + if( !val->findDecoration<IRNameHintDecoration>() ) + { + context->getBuilder()->addDecoration<IRNameHintDecoration>(val)->name = nameHint->name; + } + } +} // Add a phi node to represent the given variable PhiInfo* addPhi( @@ -365,6 +383,7 @@ PhiInfo* addPhi( valueType = context->getBuilder()->getRateQualifiedType(rate, valueType); } IRParam* phi = builder->createParam(valueType); + maybeApplyNameHint(context, var, phi); RefPtr<PhiInfo> phiInfo = new PhiInfo(); context->phiInfos.Add(phi, phiInfo); @@ -755,6 +774,8 @@ void processBlock( // block. auto val = readVar(context, blockInfo, var); + maybeApplyNameHint(context, var, val); + val = applyAccessChain(context, &blockInfo->builder, ptrArg, val); // We can just replace all uses of this diff --git a/source/slang/ir.cpp b/source/slang/ir.cpp index 53050b6b9..f8ca54639 100644 --- a/source/slang/ir.cpp +++ b/source/slang/ir.cpp @@ -4713,6 +4713,14 @@ namespace Slang } break; + case kIRDecorationOp_NameHint: + { + auto originalDecoration = (IRNameHintDecoration*)dd; + auto newDecoration = context->builder->addDecoration<IRNameHintDecoration>(clonedValue); + newDecoration->name = originalDecoration->name; + } + break; + default: // Don't clone any decorations we don't understand. break; diff --git a/source/slang/ir.h b/source/slang/ir.h index 4a393cae0..5d9948b70 100644 --- a/source/slang/ir.h +++ b/source/slang/ir.h @@ -125,6 +125,7 @@ enum IRDecorationOp : uint16_t kIRDecorationOp_GLSLOuterArray, kIRDecorationOp_Semantic, kIRDecorationOp_InterpolationMode, + kIRDecorationOp_NameHint, }; // represents an object allocated in an IR memory pool diff --git a/source/slang/legalize-types.cpp b/source/slang/legalize-types.cpp index 6922a5174..bbfa6264d 100644 --- a/source/slang/legalize-types.cpp +++ b/source/slang/legalize-types.cpp @@ -341,6 +341,11 @@ struct TupleTypeBuilder ordinaryStructType->sourceLoc = originalStructType->sourceLoc; ordinaryStructType->mangledName = originalStructType->mangledName; + if(auto nameHintDecoration = originalStructType->findDecoration<IRNameHintDecoration>()) + { + builder->addDecoration<IRNameHintDecoration>(ordinaryStructType)->name = nameHintDecoration->name; + } + // The new struct type will appear right after the original in the IR, // so that we can be sure any instruction that could reference the // original can also reference the new one. diff --git a/source/slang/lower-to-ir.cpp b/source/slang/lower-to-ir.cpp index 2d5f169f3..ea82e4b70 100644 --- a/source/slang/lower-to-ir.cpp +++ b/source/slang/lower-to-ir.cpp @@ -1232,7 +1232,92 @@ void maybeSetRate( } } +static Name* getNameForNameHint( + IRGenContext* context, + Decl* decl) +{ + // We will use a bit of an ad hoc convention here for now. + + Name* leafName = decl->getName(); + + // Handle custom name for a global parameter group (e.g., a `cbuffer`) + if(auto reflectionNameModifier = decl->FindModifier<ParameterGroupReflectionName>()) + { + leafName = reflectionNameModifier->nameAndLoc.name; + } + + // There is no point in trying to provide a name hint for something with no name, + // or with an empty name + if(!leafName) + return nullptr; + if(leafName->text.Length() == 0) + return nullptr; + + + if(auto varDecl = dynamic_cast<VarDeclBase*>(decl)) + { + // For an ordinary local variable, global variable, + // parameter, or field, we will just use the name + // as declared, and now work in anything from + // its parent declaration(s). + // + // TODO: consider whether global/static variables should + // follow different rules. + // + return leafName; + } + + // For other cases of declaration, we want to consider + // merging its name with the name of its parent declaration. + auto parentDecl = decl->ParentDecl; + + // Skip past a generic parent, if we are a declaration nested in a generic. + if(auto genericParentDecl = dynamic_cast<GenericDecl*>(parentDecl)) + parentDecl = genericParentDecl->ParentDecl; + + auto parentName = getNameForNameHint(context, parentDecl); + if(!parentName) + { + return leafName; + } + + // TODO: at some point we will start giving `ModuleDecl`s names, + // and in that case we need to think carefully about whether to + // include their names here or not. + // We will now construct a new `Name` to use as the hint, + // combining the name of the parent and the leaf declaration. + + StringBuilder sb; + sb.append(parentName->text); + sb.append("."); + sb.append(leafName->text); + + return context->getSession()->getNameObj(sb.ProduceString()); +} + +/// Try to add an appropriate name hint to the instruction, +/// that can be used for back-end code emission or debug info. +static void addNameHint( + IRGenContext* context, + IRInst* inst, + Decl* decl) +{ + Name* name = getNameForNameHint(context, decl); + if(!name) + return; + context->irBuilder->addDecoration<IRNameHintDecoration>(inst)->name = name; +} + +/// Add a name hint based on a fixed string. +static void addNameHint( + IRGenContext* context, + IRInst* inst, + char const* text) +{ + Name* name = context->getSession()->getNameObj(text); + context->irBuilder->addDecoration<IRNameHintDecoration>(inst)->name = name; +} LoweredValInfo createVar( IRGenContext* context, @@ -1249,6 +1334,8 @@ LoweredValInfo createVar( addVarDecorations(context, irAlloc, decl); builder->addHighLevelDeclDecoration(irAlloc, decl); + + addNameHint(context, irAlloc, decl); } return LoweredValInfo::ptr(irAlloc); @@ -3406,6 +3493,7 @@ struct DeclLoweringVisitor : DeclVisitor<DeclLoweringVisitor, LoweredValInfo> globalVal = LoweredValInfo::ptr(irGlobal); } irGlobal->mangledName = context->getSession()->getNameObj(getMangledName(decl)); + addNameHint(context, irGlobal, decl); maybeSetRate(context, irGlobal, decl); @@ -3605,6 +3693,7 @@ struct DeclLoweringVisitor : DeclVisitor<DeclLoweringVisitor, LoweredValInfo> subContext->irBuilder = subBuilder; IRStructType* irStruct = subBuilder->createStructType(); + addNameHint(context, irStruct, decl); setMangledName(irStruct, getMangledName(decl)); @@ -3664,6 +3753,7 @@ struct DeclLoweringVisitor : DeclVisitor<DeclLoweringVisitor, LoweredValInfo> auto builder = getBuilder(); auto irFieldKey = builder->createStructKey(); + addNameHint(context, irFieldKey, fieldDecl); addVarDecorations(context, irFieldKey, fieldDecl); @@ -4074,12 +4164,14 @@ struct DeclLoweringVisitor : DeclVisitor<DeclLoweringVisitor, LoweredValInfo> // TODO: use a `TypeKind` to represent the // classifier of the parameter. auto param = subBuilder->emitParam(nullptr); + addNameHint(context, param, typeParamDecl); setValue(subContext, typeParamDecl, LoweredValInfo::simple(param)); } else if (auto valDecl = member.As<GenericValueParamDecl>()) { auto paramType = lowerType(subContext, valDecl->getType()); auto param = subBuilder->emitParam(paramType); + addNameHint(context, param, valDecl); setValue(subContext, valDecl, LoweredValInfo::simple(param)); } } @@ -4092,6 +4184,7 @@ struct DeclLoweringVisitor : DeclVisitor<DeclLoweringVisitor, LoweredValInfo> // TODO: use a `WitnessTableKind` to represent the // classifier of the parameter. auto param = subBuilder->emitParam(nullptr); + addNameHint(context, param, constraintDecl); setValue(subContext, constraintDecl, LoweredValInfo::simple(param)); } } @@ -4172,6 +4265,18 @@ struct DeclLoweringVisitor : DeclVisitor<DeclLoweringVisitor, LoweredValInfo> } } + void addParamNameHint(IRInst* inst, ParameterInfo info) + { + if(auto decl = info.decl) + { + addNameHint(context, inst, decl); + } + else if( info.isThisParam ) + { + addNameHint(context, inst, "this"); + } + } + LoweredValInfo lowerFuncDecl(FunctionDeclBase* decl) { // We are going to use a nested builder, because we will @@ -4221,6 +4326,7 @@ struct DeclLoweringVisitor : DeclVisitor<DeclLoweringVisitor, LoweredValInfo> // need to create an IR function here IRFunc* irFunc = subBuilder->createFunc(); + addNameHint(context, irFunc, decl); setMangledName(irFunc, getMangledName(decl)); @@ -4335,6 +4441,7 @@ struct DeclLoweringVisitor : DeclVisitor<DeclLoweringVisitor, LoweredValInfo> { subBuilder->addHighLevelDeclDecoration(irParamPtr, paramDecl); } + addParamNameHint(irParamPtr, paramInfo); paramVal = LoweredValInfo::ptr(irParamPtr); @@ -4361,6 +4468,7 @@ struct DeclLoweringVisitor : DeclVisitor<DeclLoweringVisitor, LoweredValInfo> { subBuilder->addHighLevelDeclDecoration(irParam, paramDecl); } + addParamNameHint(irParam, paramInfo); paramVal = LoweredValInfo::simple(irParam); // // HLSL allows a function parameter to be used as a local @@ -4416,7 +4524,8 @@ struct DeclLoweringVisitor : DeclVisitor<DeclLoweringVisitor, LoweredValInfo> { // Add the IR parameter for the new value IRType* irParamType = irResultType; - subBuilder->emitParam(irParamType); + auto irParam = subBuilder->emitParam(irParamType); + addNameHint(context, irParam, "newValue"); // TODO: we need some way to wire this up to the `newValue` // or whatever name we give for that parameter inside |
