diff options
Diffstat (limited to 'source/slang/slang-ir-explicit-global-init.cpp')
| -rw-r--r-- | source/slang/slang-ir-explicit-global-init.cpp | 207 |
1 files changed, 207 insertions, 0 deletions
diff --git a/source/slang/slang-ir-explicit-global-init.cpp b/source/slang/slang-ir-explicit-global-init.cpp new file mode 100644 index 000000000..07397902e --- /dev/null +++ b/source/slang/slang-ir-explicit-global-init.cpp @@ -0,0 +1,207 @@ +// slang-ir-explicit-global-init.cpp +#include "slang-ir-explicit-global-init.h" + +#include "slang-ir-insts.h" + +namespace Slang +{ + +// This pass is responsible for taking code in a form like: +// +// static int gCounter = 1; +// +// void computeMain() +// { +// ... +// int tmp = gCounter++; +// } +// +// and transforming it so that the initialization of global +// variables is performed explicitly at the start of each +// entry-point funciton: +// +// static int gCounter; +// +// void computeMain() +// { +// gCounter = 1; +// ... +// int tmp = gCounter++; +// } +// +// Transforming the code in this way may be required for targets +// that do not support initial-value expressions on global +// variables (e.g., SPIR-V is such a target). It can also be +// useful as a pre-process before other transformations that +// might work with global variables, because after this change +// there cannot be any global variables with initializers. + +struct MoveGlobalVarInitializationToEntryPointsPass +{ + IRModule* m_module; + + SharedIRBuilder* m_sharedBuilder; + + // In the Slang IR, a global variable represents a pointer + // to the storage for the variable but it *also* encodes + // the logic used to compute the initial value of that + // variable. This works because `IRGlobalVar` is a subtype + // of `IRGlobalValueWithCode`, which is also the base + // type of `IRFunc`. Thus a global variable behaves a + // bit like a function, which just happens to compute + // the initial value for the variable. + // + // Part of the work in this pass will be to split those + // two pars of the variable, so that we end up with + // a global variable with not initialization logic, + // plus an ordinary `IRFunc` to compute the initial + // value. + // + // We will compute this split representation and then + // hold onto it so that we can use it for injecting + // the initialization logic into entry points. + // + struct GlobalVarInfo + { + IRGlobalVar* globalVar = nullptr; + IRFunc* initFunc = nullptr; + }; + List<GlobalVarInfo> m_globalVarsWithInit; + + void processModule(IRModule* module) + { + m_module = module; + + SharedIRBuilder sharedBuilder(module); + m_sharedBuilder = &sharedBuilder; + + // We start by looking for global variables with + // initialization logic in the IR, and processing + // each to produce a split variable (now without + // initialization) and function (to compute the + // initial value). + // + for( auto inst : m_module->getGlobalInsts() ) + { + auto globalVar = as<IRGlobalVar>(inst); + if(!globalVar) + continue; + + auto firstBlock = globalVar->getFirstBlock(); + if(!firstBlock) + continue; + + processGlobalVarWithInit(globalVar, firstBlock); + } + + // Then we loop over all the entry points in the + // module and modify them to explicitly initialize + // all the global variables that were identified + // and processed in the first pass. + // + for( auto inst : m_module->getGlobalInsts() ) + { + auto func = as<IRFunc>(inst); + if(!func) + continue; + + if(!func->findDecoration<IREntryPointDecoration>()) + continue; + + processEntryPoint(func); + } + } + + void processGlobalVarWithInit(IRGlobalVar* globalVar, IRBlock* firstBlock) + { + IRBuilder builder(m_sharedBuilder); + builder.setInsertBefore(globalVar); + + // Becaue an `IRGlobalVar` reprsents a pointer to the storage + // for the variable, we need to extract the underlying value + // type from the pointer type. + // + auto valueType = globalVar->getDataType()->getValueType(); + + // We are going to construct an explicit IR function to compute + // the initial value of the variable. That function will alway + // take zero parameters. + // + auto initFunc = builder.createFunc(); + initFunc->setFullType(builder.getFuncType(0, nullptr, valueType)); + + // The basic blocks under teh `IRGlobalVar` define its initialization + // logic, and we can simply move those blocks over to the new + // `IRFunc` to define its behavior. + // + // As a result, the `globalVar` will no longer have its own + // initialization logic, which is a postcondition this pass + // needed to guarantee. + // + IRBlock* nextBlock = nullptr; + for( IRBlock* block = firstBlock; block; block = nextBlock ) + { + nextBlock = block->getNextBlock(); + + block->removeFromParent(); + block->insertAtEnd(initFunc); + } + + // We need to remember the variable and the assocaited + // initial-value function so that we can iterate over + // them in the per-entry-point logic below. + // + GlobalVarInfo info; + info.globalVar = globalVar; + info.initFunc = initFunc; + m_globalVarsWithInit.add(info); + } + + void processEntryPoint(IRFunc* entryPointFunc) + { + // We can only process entry point definitions, not declarations. + // + auto firstBlock = entryPointFunc->getFirstBlock(); + if(!firstBlock) + return; + + // We are going to insert initiailization logic at the start + // of the first block of the entry point. + // + IRBuilder builder(m_sharedBuilder); + builder.setInsertBefore(firstBlock->getFirstOrdinaryInst()); + + for( auto globalVarInfo : m_globalVarsWithInit ) + { + // The earlier step split each global variable into + // a variable with no initialization logic, plus a function + // that can be called to compute the initial value. + // + auto globalVar = globalVarInfo.globalVar; + auto initFunc = globalVarInfo.initFunc; + + // Because the `IRGlobalVar` represents a pointer to + // storage, we need to get the pointed-to type to + // get the type of the initial value. + // + 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); + builder.emitStore(globalVar, initVal); + } + } +}; + + /// Move initialization logic off of global variables and onto each entry point +void moveGlobalVarInitializationToEntryPoints( + IRModule* module) +{ + MoveGlobalVarInitializationToEntryPointsPass pass; + pass.processModule(module); +} + +} |
