summaryrefslogtreecommitdiffstats
path: root/source
diff options
context:
space:
mode:
authorTim Foley <tfoleyNV@users.noreply.github.com>2019-07-18 11:10:56 -0700
committerGitHub <noreply@github.com>2019-07-18 11:10:56 -0700
commitd2243107e582e2388b549d663f0931a742b6f353 (patch)
tree54d679f58659b099b73e4e726355a27873efacf7 /source
parentddf3d5550f06d19606e5d148edca308f852dbe03 (diff)
Add back a notion of IR global constants (#1002)
This change adds back a little bit of explicit support for global constants in the IR, after a previous change completely removed the existing `IRGlobalConstant` node type. The new `IRGlobalConstant` is *not* a parent instruction, and doesn't function at all like the old one. Instead it is effectively a simple instruction that takes zero or one operands: * The zero-operand case represents a constant with unknown value. This would usually come from another module, and thus would have an `[import(...)]` linkage decoration, so that after linking it resolves to a constant with a known value. * In the one-operand case, the single operand represents the value of the constant, so that the operation semantically behaves like an identity function. It exists just to give decorations something to "attach" to, so that a global constant with a value can have, e.g., an `[export(...)]` decoration to establish linkage. The IR lowering pass was updated to create the new node type to wrap any global constants. For now we do this both for global `static const` variables and function-scope `static const`, although the latter doesn't really need the extra indirection. The IR linking logic was extended to handle linking of global constants akin to how other global instructions are handled. The new logic is mostly boilerplate, and it is likely that a refactor of the linking logic would eliminate the need for this kind of per-instruction-opcode handling of IR instructions that can have linkage. A custom pass was added that is intended to be run right after linking (it could arguably be folded into `linkIR()`, but I thought it was safer to keep each pass as small as possible). This pass replaces any `IRGlobalConstant` that has a value (operand) with that value, so that global constants should be eliminated after the linking step. This ensures that downstream optimization/transformation passes don't have to deal with the possibility of global constants. Almost all the existing passes would Just Work if global constants were left in the IR. The two big exceptions are: * Anything that relies on testing `IRInst*` identity as a way to test for things having the same value would break, since a global constant is a distinct `IRInst*` from its value. * The type legalization pass doesn't handle `IRGlobalConstant` instructions with non-simple types. This could be added if we ever wanted it, but it seemed silly to write this code now if it would always be dead (and thus untested). I went ahead and updated the emit logic to handle an `IRGlobalConstant`s that still existing in the IR module at emit time, since the amount of code required was small so that being robust to that case seemed safest (e.g., in case we ever want to have a path that emits code directly while skipping some/all of our IR transformation passes). There should be no visible changes to the functionality of the compiler with this change, but it should help make IR dumps from the front-end more clear/explicit (since each constant will be a distinct instruction with its own name), and paves the way for supporting proper cross-module linkage of constants.
Diffstat (limited to 'source')
-rw-r--r--source/slang/slang-emit-c-like.cpp5
-rw-r--r--source/slang/slang-emit.cpp9
-rw-r--r--source/slang/slang-ir-inst-defs.h1
-rw-r--r--source/slang/slang-ir-insts.h26
-rw-r--r--source/slang/slang-ir-link.cpp77
-rw-r--r--source/slang/slang-ir-link.h9
-rw-r--r--source/slang/slang-ir.cpp30
-rw-r--r--source/slang/slang-lower-to-ir.cpp81
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)