diff options
Diffstat (limited to 'source/slang')
| -rw-r--r-- | source/slang/slang-emit-c-like.cpp | 5 | ||||
| -rw-r--r-- | source/slang/slang-emit.cpp | 9 | ||||
| -rw-r--r-- | source/slang/slang-ir-inst-defs.h | 1 | ||||
| -rw-r--r-- | source/slang/slang-ir-insts.h | 26 | ||||
| -rw-r--r-- | source/slang/slang-ir-link.cpp | 77 | ||||
| -rw-r--r-- | source/slang/slang-ir-link.h | 9 | ||||
| -rw-r--r-- | source/slang/slang-ir.cpp | 30 | ||||
| -rw-r--r-- | source/slang/slang-lower-to-ir.cpp | 81 |
8 files changed, 207 insertions, 31 deletions
diff --git a/source/slang/slang-emit-c-like.cpp b/source/slang/slang-emit-c-like.cpp index a7a427f4a..d7930130e 100644 --- a/source/slang/slang-emit-c-like.cpp +++ b/source/slang/slang-emit-c-like.cpp @@ -743,6 +743,7 @@ bool CLikeSourceEmitter::shouldFoldInstIntoUseSites(IRInst* inst) // case kIROp_Var: case kIROp_GlobalVar: + case kIROp_GlobalConstant: case kIROp_GlobalParam: case kIROp_Param: case kIROp_Func: @@ -1960,6 +1961,10 @@ void CLikeSourceEmitter::defaultEmitInstExpr(IRInst* inst, const EmitOpInfo& inO } break; + case kIROp_GlobalConstant: + emitOperand(inst->getOperand(0), outerPrec); + break; + default: m_writer->emit("/* unhandled */"); break; diff --git a/source/slang/slang-emit.cpp b/source/slang/slang-emit.cpp index cc532b9f7..c51040b98 100644 --- a/source/slang/slang-emit.cpp +++ b/source/slang/slang-emit.cpp @@ -271,6 +271,15 @@ String emitEntryPoint( // un-specialized IR. dumpIRIfEnabled(compileRequest, irModule); + // Replace any global constants with their values. + // + replaceGlobalConstants(irModule); +#if 0 + dumpIRIfEnabled(compileRequest, irModule, "GLOBAL CONSTANTS REPLACED"); +#endif + validateIRModuleIfEnabled(compileRequest, irModule); + + // When there are top-level existential-type parameters // to the shader, we need to take the side-band information // on how the existential "slots" were bound to concrete diff --git a/source/slang/slang-ir-inst-defs.h b/source/slang/slang-ir-inst-defs.h index ed8bfeaa3..0c218f2aa 100644 --- a/source/slang/slang-ir-inst-defs.h +++ b/source/slang/slang-ir-inst-defs.h @@ -182,6 +182,7 @@ INST_RANGE(Type, VoidType, InterfaceType) INST_RANGE(GlobalValueWithCode, Func, GlobalVar) INST(GlobalParam, global_param, 0, 0) +INST(GlobalConstant, globalConstant, 0, 0) INST(StructKey, key, 0, 0) INST(GlobalGenericParam, global_generic_param, 0, 0) diff --git a/source/slang/slang-ir-insts.h b/source/slang/slang-ir-insts.h index 4704bb5a3..359c8e98d 100644 --- a/source/slang/slang-ir-insts.h +++ b/source/slang/slang-ir-insts.h @@ -579,6 +579,25 @@ struct IRGlobalParam : IRInst IR_LEAF_ISA(GlobalParam) }; +/// @brief A global constnat. +/// +/// Represents a global constant that may have a name and linkage. +/// If it has an operand, then this operand is the value of +/// the constants. If there is no operand, then the instruction +/// represents an "extern" constant that will be defined in another +/// module, and which is thus expected to have linkage. +/// +struct IRGlobalConstant : IRInst +{ + IR_LEAF_ISA(GlobalConstant); + + /// Get the value of this global constant, or null if the value is not known. + IRInst* getValue() + { + return getOperandCount() != 0 ? getOperand(0) : nullptr; + } + +}; // An entry in a witness table (see below) struct IRWitnessTableEntry : IRInst @@ -1177,6 +1196,13 @@ struct IRBuilder IRType* type, IRInst* val); + IRGlobalConstant* emitGlobalConstant( + IRType* type); + + IRGlobalConstant* emitGlobalConstant( + IRType* type, + IRInst* val); + // // Decorations // diff --git a/source/slang/slang-ir-link.cpp b/source/slang/slang-ir-link.cpp index 0ee068993..7e2ac1f98 100644 --- a/source/slang/slang-ir-link.cpp +++ b/source/slang/slang-ir-link.cpp @@ -432,6 +432,29 @@ IRGlobalParam* cloneGlobalParamImpl( return clonedVal; } +IRGlobalConstant* cloneGlobalConstantImpl( + IRSpecContextBase* context, + IRBuilder* builder, + IRGlobalConstant* originalVal, + IROriginalValuesForClone const& originalValues) +{ + auto clonedType = cloneType(context, originalVal->getFullType()); + IRGlobalConstant* clonedVal = nullptr; + if(auto originalInitVal = originalVal->getValue()) + { + auto clonedInitVal = cloneValue(context, originalInitVal); + clonedVal = builder->emitGlobalConstant(clonedType, clonedInitVal); + } + else + { + clonedVal = builder->emitGlobalConstant(clonedType); + } + + cloneSimpleGlobalValueImpl(context, originalVal, originalValues, clonedVal); + + return clonedVal; +} + IRGeneric* cloneGenericImpl( IRSpecContextBase* context, IRBuilder* builder, @@ -948,6 +971,9 @@ IRInst* cloneInst( case kIROp_GlobalParam: return cloneGlobalParamImpl(context, builder, cast<IRGlobalParam>(originalInst), originalValues); + case kIROp_GlobalConstant: + return cloneGlobalConstantImpl(context, builder, cast<IRGlobalConstant>(originalInst), originalValues); + case kIROp_WitnessTable: return cloneWitnessTableImpl(context, builder, cast<IRWitnessTable>(originalInst), originalValues); @@ -1362,6 +1388,57 @@ LinkedIR linkIR( return linkedIR; } +struct ReplaceGlobalConstantsPass +{ + void process(IRModule* module) + { + _processInstRec(module->getModuleInst()); + + for(auto inst : instsToRemove) + inst->removeAndDeallocate(); + } + + List<IRInst*> instsToRemove; + + void _processInstRec(IRInst* inst) + { + if( auto globalConstant = as<IRGlobalConstant>(inst) ) + { + if( auto val = globalConstant->getValue() ) + { + // Attempt to transfer the name hint from the global + // constant to its value. If multiple constants + // have the same value, then the first one will "win" + // and transfer its name over. + // + if( auto nameHint = globalConstant->findDecoration<IRNameHintDecoration>() ) + { + if( !val->findDecoration<IRNameHintDecoration>() ) + { + nameHint->removeFromParent(); + nameHint->insertAtStart(val); + } + } + + // Replace the global constant + globalConstant->replaceUsesWith(val); + instsToRemove.add(globalConstant); + } + } + + for( auto child : inst->getDecorationsAndChildren() ) + { + _processInstRec(child); + } + } +}; + +void replaceGlobalConstants(IRModule* module) +{ + ReplaceGlobalConstantsPass pass; + pass.process(module); +} + } // namespace Slang diff --git a/source/slang/slang-ir-link.h b/source/slang/slang-ir-link.h index 89486b28f..4cea3941e 100644 --- a/source/slang/slang-ir-link.h +++ b/source/slang/slang-ir-link.h @@ -24,4 +24,13 @@ namespace Slang ProgramLayout* programLayout, CodeGenTarget target, TargetRequest* targetReq); + + // Replace any global constants in the IR module with their + // definitions, if possible. + // + // This pass should always be run shortly after linking the + // IR, to ensure that constants with identical values are + // treated as identical for the purposes of specialization. + // + void replaceGlobalConstants(IRModule* module); } diff --git a/source/slang/slang-ir.cpp b/source/slang/slang-ir.cpp index a718470ff..ed86e4d6d 100644 --- a/source/slang/slang-ir.cpp +++ b/source/slang/slang-ir.cpp @@ -2955,6 +2955,30 @@ namespace Slang return inst; } + IRGlobalConstant* IRBuilder::emitGlobalConstant( + IRType* type) + { + auto inst = createInst<IRGlobalConstant>( + this, + kIROp_GlobalConstant, + type); + addInst(inst); + return inst; + } + + IRGlobalConstant* IRBuilder::emitGlobalConstant( + IRType* type, + IRInst* val) + { + auto inst = createInst<IRGlobalConstant>( + this, + kIROp_GlobalConstant, + type, + val); + addInst(inst); + return inst; + } + // // Decorations // @@ -4284,7 +4308,8 @@ namespace Slang case kIROp_StructField: case kIROp_Func: case kIROp_Generic: - case kIROp_GlobalVar: + case kIROp_GlobalVar: // Note: the IRGlobalVar represents the *address*, so only a load/store would have side effects + case kIROp_GlobalConstant: case kIROp_GlobalParam: case kIROp_StructKey: case kIROp_GlobalGenericParam: @@ -4459,6 +4484,9 @@ namespace Slang case kIROp_Generic: return val->getFirstChild() != nullptr; + case kIROp_GlobalConstant: + return cast<IRGlobalConstant>(val)->getValue() != nullptr; + case kIROp_StructType: case kIROp_GlobalVar: case kIROp_GlobalParam: diff --git a/source/slang/slang-lower-to-ir.cpp b/source/slang/slang-lower-to-ir.cpp index 5387688b6..eef69b1b5 100644 --- a/source/slang/slang-lower-to-ir.cpp +++ b/source/slang/slang-lower-to-ir.cpp @@ -4463,49 +4463,70 @@ struct DeclLoweringVisitor : DeclVisitor<DeclLoweringVisitor, LoweredValInfo> LoweredValInfo lowerConstantDeclCommon(VarDeclBase* decl, IRGenContext* subContext) { + auto builder = subContext->irBuilder; auto initExpr = decl->initExpr; + + // We want to be able to support cases where a global constant is defined in + // another module and we should not bind to its value at (front-end) compile + // time. We handle this by adding a level of indirection where a global constant + // is represented as an IR node with zero or one operands. In the zero-operand + // case the node represents a global constant with an unknown value (perhaps + // an imported constant), while in the one-operand case the operand gives us + // the concrete value to use for the constant. + // + // Using a level of indirection also gives us a well-defined place to attach + // annotation information like name hints, since otherwise two constants + // with the same value would map to identical IR nodes. + // + // TODO: For now we detect whether or not to include the value operand based on + // whether we see an initial-value expression in the AST declaration, but + // eventually we might base this on whether or not the value should be accessible + // to the module we are lowering. + + IRInst* irConstant = nullptr; if(!initExpr) { - // TODO: what to do in this case? - return LoweredValInfo(); + // If we don't know the value we want to use, then we just create + // a global constant IR node with the right type. + // + auto irType = lowerType(subContext, decl->getType()); + irConstant = builder->emitGlobalConstant(irType); } - - LoweredValInfo initVal = lowerRValueExpr(subContext, decl->initExpr); - - switch(initVal.flavor) + else { - case LoweredValInfo::Flavor::Simple: - case LoweredValInfo::Flavor::Subscript: - { - // If the lowered value is a full instruction, - // then we will attach whatever extended information - // from the declaration that we can. - // - // TODO: We might want to introduce a distinct - // IR node to represent the constant, just with - // an operand for the value, and then simplify - // it away in later passes rather than handle - // things this way. - // - auto irInst = initVal.val; - addLinkageDecoration(context, irInst, decl); - addNameHint(context, irInst, decl); - addVarDecorations(context, irInst, decl); - getBuilder()->addHighLevelDeclDecoration(irInst, decl); - } - break; + // We lower the value expression directly, which yields a + // global instruction to represent the value. There is + // no guarantee that this instruction is unique (e.g., + // if we have two different constants definitions both + // with the value `5`, then we might have only a single + // instruction to represent `5`. + // + auto irInitVal = getSimpleVal(subContext, lowerRValueExpr(subContext, initExpr)); - default: - break; + // We construct a distinct IR instruction to represent the + // constant itself, with the value as an operand. + // + irConstant = builder->emitGlobalConstant( + irInitVal->getFullType(), + irInitVal); } + // All of the attributes/decorations we can attach + // belong on the IR constant node. + // + addLinkageDecoration(context, irConstant, decl); + addNameHint(context, irConstant, decl); + addVarDecorations(context, irConstant, decl); + getBuilder()->addHighLevelDeclDecoration(irConstant, decl); + // Register the value that was emitted as the value // for any references to the constant from elsewhere // in the code. // - setGlobalValue(context, decl, initVal); + auto constantVal = LoweredValInfo::simple(irConstant); + setGlobalValue(context, decl, constantVal); - return initVal; + return constantVal; } LoweredValInfo lowerGlobalConstantDecl(VarDecl* decl) |
