summaryrefslogtreecommitdiffstats
path: root/source/slang
diff options
context:
space:
mode:
authorYong He <yonghe@outlook.com>2023-12-13 15:15:19 -0800
committerGitHub <noreply@github.com>2023-12-13 15:15:19 -0800
commit3979660d4fe1fd6c1f1d9b8956e96817e17c3f4e (patch)
treea1777fc23e2983c5dbe630d39e529c798953484c /source/slang
parent1406aa2bc9e398e5e5565ba9c6adbb780c29fee1 (diff)
Fix GLSL static initialization bug. (#3409)
* Fix GLSL static initialization bug. Fixes #3408. * Update comment. * Fold global var initializer as an expression if possible. --------- Co-authored-by: Yong He <yhe@nvidia.com>
Diffstat (limited to 'source/slang')
-rw-r--r--source/slang/slang-emit-c-like.cpp91
-rw-r--r--source/slang/slang-emit.cpp2
-rw-r--r--source/slang/slang-ir-explicit-global-init.cpp27
-rw-r--r--source/slang/slang-lower-to-ir.cpp11
4 files changed, 100 insertions, 31 deletions
diff --git a/source/slang/slang-emit-c-like.cpp b/source/slang/slang-emit-c-like.cpp
index 7f7e23a1c..ba5606791 100644
--- a/source/slang/slang-emit-c-like.cpp
+++ b/source/slang/slang-emit-c-like.cpp
@@ -3719,31 +3719,80 @@ void CLikeSourceEmitter::_emitInstAsVarInitializerImpl(IRInst* inst)
emitOperand(inst, getInfo(EmitOp::General));
}
+bool _isFoldableValue(IRInst* val)
+{
+ if (val->getParent() && val->getParent()->getOp() == kIROp_Module)
+ return true;
+
+ switch (val->getOp())
+ {
+ case kIROp_MakeArray:
+ case kIROp_MakeVector:
+ case kIROp_MakeMatrix:
+ case kIROp_MakeStruct:
+ case kIROp_MakeVectorFromScalar:
+ case kIROp_MakeArrayFromElement:
+ case kIROp_MakeMatrixFromScalar:
+ case kIROp_CastIntToFloat:
+ case kIROp_CastFloatToInt:
+ case kIROp_IntCast:
+ case kIROp_FloatCast:
+ {
+ for (UInt i = 0; i < val->getOperandCount(); i++)
+ if (!_isFoldableValue(val->getOperand(i)))
+ return false;
+ return true;
+ }
+ default:
+ return false;
+ }
+}
+
void CLikeSourceEmitter::emitGlobalVar(IRGlobalVar* varDecl)
{
auto allocatedType = varDecl->getDataType();
auto varType = allocatedType->getValueType();
String initFuncName;
- if (varDecl->getFirstBlock())
- {
- emitFunctionPreambleImpl(varDecl);
+ IRInst* initVal = nullptr;
+ if (auto firstBlock = varDecl->getFirstBlock())
+ {
// A global variable with code means it has an initializer
- // associated with it. Eventually we'd like to emit that
- // initializer directly as an expression here, but for
- // now we'll emit it as a separate function.
+ // associated with it.
+
+ if (auto returnInst = as<IRReturn>(firstBlock->getTerminator()))
+ {
+ // If the initializer can be conveniently emitted as an
+ // expression, we will do that.
+ if (_isFoldableValue(returnInst->getVal()))
+ {
+ initVal = returnInst->getVal();
+ }
+ }
+ if (!initVal)
+ {
+ emitFunctionPreambleImpl(varDecl);
- initFuncName = getName(varDecl);
- initFuncName.append("_init");
+ // If we can't emit the initializer as an expression,
+ // we will emit it as a separate function.
+ //
+ // TODO: the C language does not allow defining
+ // functions that return arrays, so if we have an
+ // array type here, we are going to generate invalid
+ // code.
- m_writer->emit("\n");
- emitType(varType, initFuncName);
- m_writer->emit("()\n{\n");
- m_writer->indent();
- emitFunctionBody(varDecl);
- m_writer->dedent();
- m_writer->emit("}\n");
+ initFuncName = getName(varDecl);
+ initFuncName.append("_init");
+
+ m_writer->emit("\n");
+ emitType(varType, initFuncName);
+ m_writer->emit("()\n{\n");
+ m_writer->indent();
+ emitFunctionBody(varDecl);
+ m_writer->dedent();
+ m_writer->emit("}\n");
+ }
}
// An ordinary global variable won't have a layout
@@ -3784,10 +3833,16 @@ void CLikeSourceEmitter::emitGlobalVar(IRGlobalVar* varDecl)
if (varDecl->getFirstBlock())
{
m_writer->emit(" = ");
- m_writer->emit(initFuncName);
- m_writer->emit("()");
+ if (initVal)
+ {
+ emitInstExpr(initVal, EmitOpInfo());
+ }
+ else
+ {
+ m_writer->emit(initFuncName);
+ m_writer->emit("()");
+ }
}
-
m_writer->emit(";\n\n");
}
diff --git a/source/slang/slang-emit.cpp b/source/slang/slang-emit.cpp
index bd7db604f..0e71f50d4 100644
--- a/source/slang/slang-emit.cpp
+++ b/source/slang/slang-emit.cpp
@@ -834,7 +834,7 @@ Result linkAndOptimizeIR(
{
default:
break;
-
+ case CodeGenTarget::GLSL:
case CodeGenTarget::SPIRV:
case CodeGenTarget::SPIRVAssembly:
moveGlobalVarInitializationToEntryPoints(irModule);
diff --git a/source/slang/slang-ir-explicit-global-init.cpp b/source/slang/slang-ir-explicit-global-init.cpp
index a65118d5a..94733bf6d 100644
--- a/source/slang/slang-ir-explicit-global-init.cpp
+++ b/source/slang/slang-ir-explicit-global-init.cpp
@@ -187,11 +187,28 @@ struct MoveGlobalVarInitializationToEntryPointsPass
//
auto valType = globalVar->getDataType()->getValueType();
- // We compute the initial value for the variable by calling
- // the initial-value function with no arguments, and then
- // we store that value into the corresponding global.
- //
- auto initVal = builder.emitCallInst(valType, initFunc, 0, nullptr);
+ // To simplify the resulting code a bit, if we see the
+ // initFunc just returns a constant value, then we just
+ // use that inline.
+ IRInst* initVal = nullptr;
+ if (auto initFirstBlock = initFunc->getFirstBlock())
+ {
+ if (auto returnInst = as<IRReturn>(initFirstBlock->getTerminator()))
+ {
+ if (returnInst->getVal() && returnInst->getVal()->getParent() == m_module->getModuleInst())
+ {
+ initVal = returnInst->getVal();
+ }
+ }
+ }
+ if (!initVal)
+ {
+ // We compute the initial value for the variable by calling
+ // the initial-value function with no arguments, and then
+ // we store that value into the corresponding global.
+ //
+ initVal = builder.emitCallInst(valType, initFunc, 0, nullptr);
+ }
builder.emitStore(globalVar, initVal);
}
}
diff --git a/source/slang/slang-lower-to-ir.cpp b/source/slang/slang-lower-to-ir.cpp
index 146f54932..7621f3080 100644
--- a/source/slang/slang-lower-to-ir.cpp
+++ b/source/slang/slang-lower-to-ir.cpp
@@ -7661,23 +7661,20 @@ struct DeclLoweringVisitor : DeclVisitor<DeclLoweringVisitor, LoweredValInfo>
//
// if(!isInitialized) { <globalVal> = <initExpr>; isInitialized = true; }
//
- // TODO: we could conceivably optimize this by detecting
- // when the `initExpr` lowers to just a reference to a constant,
- // and then either deleting the extra code structure there,
- // or not generating it in the first place. That is a bit
- // more complexity than I'm ready for at the moment.
+ // This will generate a lot of boilterplate code, but we optimize out the
+ // boilerplate functions later during `moveGlobalVarInitializationToEntryPoints`
+ // if we see the init function is just returning a global constant.
//
auto boolBuilder = subBuilder;
auto irBoolType = boolBuilder->getBoolType();
auto irBool = boolBuilder->createGlobalVar(irBoolType);
boolBuilder->setInsertInto(irBool);
- boolBuilder->setInsertInto(boolBuilder->createBlock());
+ boolBuilder->emitBlock();
boolBuilder->emitReturn(boolBuilder->getBoolValue(false));
auto boolVal = LoweredValInfo::ptr(irBool);
-
// Okay, with our global Boolean created, we can move on to
// generating the code we actually care about, back in the original function.