diff options
| author | Yong He <yonghe@outlook.com> | 2023-12-13 15:15:19 -0800 |
|---|---|---|
| committer | GitHub <noreply@github.com> | 2023-12-13 15:15:19 -0800 |
| commit | 3979660d4fe1fd6c1f1d9b8956e96817e17c3f4e (patch) | |
| tree | a1777fc23e2983c5dbe630d39e529c798953484c /source/slang | |
| parent | 1406aa2bc9e398e5e5565ba9c6adbb780c29fee1 (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.cpp | 91 | ||||
| -rw-r--r-- | source/slang/slang-emit.cpp | 2 | ||||
| -rw-r--r-- | source/slang/slang-ir-explicit-global-init.cpp | 27 | ||||
| -rw-r--r-- | source/slang/slang-lower-to-ir.cpp | 11 |
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. |
