summaryrefslogtreecommitdiff
path: root/source/slang
diff options
context:
space:
mode:
Diffstat (limited to 'source/slang')
-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)