From 37315c96ea48045fae60f0e1cb1a3293f3ddd962 Mon Sep 17 00:00:00 2001 From: Tim Foley Date: Mon, 20 Nov 2017 13:45:10 -0800 Subject: IR: support global variable with initializers (#294) The big change here is that the ability to contain basic blocks with instructions in them has been hoisted from `IRFunc` into a new base type `IRGlobalValueWithCode` shared with `IRGlobalVar`. The basic blocks of a global variable define initialization logic for it; they can be looked at like a function that returns the initial value. Places in the IR that used to assume functions contain all the code need to be updated, but so far I only handled the cloning step. The emit logic currently handles an initializer for a global variable by outputting its logic as a separate function, and then having the variable call that function to initialize itself. This should be cleaned up over time so that we generate an ordinary expression whenever possible. I also made the emit logic correctly label any global variable without a layout (that is, any that don't represent a shader parameter) as `static` so that the downstream HLSL compiler sees them as variables rather than parameters. --- source/slang/emit.cpp | 52 ++++++++++++++++++++++++++++++-------- source/slang/ir-insts.h | 24 ++++++++---------- source/slang/ir.cpp | 60 ++++++++++++++++++++++++++++++-------------- source/slang/ir.h | 32 +++++++++++++---------- source/slang/lower-to-ir.cpp | 18 ++++++++++++- 5 files changed, 129 insertions(+), 57 deletions(-) (limited to 'source/slang') diff --git a/source/slang/emit.cpp b/source/slang/emit.cpp index 383442236..4cad7b28d 100644 --- a/source/slang/emit.cpp +++ b/source/slang/emit.cpp @@ -6461,7 +6461,23 @@ emitDeclImpl(decl, nullptr); { auto allocatedType = varDecl->getType(); auto varType = allocatedType->getValueType(); -// auto addressSpace = allocatedType->getAddressSpace(); + + String initFuncName; + if (varDecl->firstBlock) + { + // 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. + + initFuncName = getIRName(varDecl); + initFuncName.append("_init"); + emitIRType(ctx, varType, initFuncName); + Emit("()\n{\n"); + emitIRStmtsForBlocks(ctx, varDecl->firstBlock, nullptr); + Emit("}\n"); + } + if (auto paramBlockType = varType->As()) { @@ -6475,20 +6491,27 @@ emitDeclImpl(decl, nullptr); // Need to emit appropriate modifiers here. auto layout = getVarLayout(ctx, varDecl); - - emitIRVarModifiers(ctx, layout); -#if 0 - switch (addressSpace) + if (!layout) { - default: - break; + // A global variable without a layout is just an + // ordinary global variable, and may need special + // modifiers to indicate it as such. + switch (getTarget(ctx)) + { + case CodeGenTarget::HLSL: + // HLSL requires the `static` modifier on any + // global variables; otherwise they are assumed + // to be uniforms. + Emit("static "); + break; - case kIRAddressSpace_GroupShared: - emit("groupshared "); - break; + default: + break; + } } -#endif + + emitIRVarModifiers(ctx, layout); emitIRType(ctx, varType, getIRName(varDecl)); @@ -6496,6 +6519,13 @@ emitDeclImpl(decl, nullptr); emitIRLayoutSemantics(ctx, varDecl); + if (varDecl->firstBlock) + { + Emit(" = "); + emit(initFuncName); + Emit("()"); + } + emit(";\n"); } diff --git a/source/slang/ir-insts.h b/source/slang/ir-insts.h index a91143a43..b95aea2fe 100644 --- a/source/slang/ir-insts.h +++ b/source/slang/ir-insts.h @@ -276,16 +276,14 @@ struct IRVar : IRInst } }; -struct IRGlobalVar : IRGlobalValue +/// @brief A global variable. +/// +/// Represents a global variable in the IR. +/// If the variable has an initializer, then +/// it is represented by the code in the basic +/// blocks nested inside this value. +struct IRGlobalVar : IRGlobalValueWithCode { - // TODO: should contain information - // for use in initializing the variable - // (e.g., a reference to a function - // that is to be evaluated to provide - // the initial value, or a basic block - // that defines a DAG of constant - // values to use as initial values...) - PtrType* getType() { return type.As(); } }; @@ -359,14 +357,14 @@ struct IRBuilder // The current function and block being inserted into // (or `null` if we aren't inserting). - IRFunc* curFunc = nullptr; - IRBlock* curBlock = nullptr; + IRGlobalValueWithCode* curFunc = nullptr; + IRBlock* curBlock = nullptr; // // An instruction in the current block that we should insert before IRInst* insertBeforeInst = nullptr; - IRFunc* getFunc() { return curFunc; } - IRBlock* getBlock() { return curBlock; } + IRGlobalValueWithCode* getFunc() { return curFunc; } + IRBlock* getBlock() { return curBlock; } void addInst(IRBlock* block, IRInst* inst); void addInst(IRInst* inst); diff --git a/source/slang/ir.cpp b/source/slang/ir.cpp index 2d3127a61..0f34d5585 100644 --- a/source/slang/ir.cpp +++ b/source/slang/ir.cpp @@ -131,7 +131,7 @@ namespace Slang return entryBlock->getFirstParam(); } - void IRFunc::addBlock(IRBlock* block) + void IRGlobalValueWithCode::addBlock(IRBlock* block) { block->parentFunc = this; @@ -3317,6 +3317,11 @@ namespace Slang } } + void cloneGlobalValueWithCodeCommon( + IRSpecContextBase* context, + IRGlobalValueWithCode* clonedValue, + IRGlobalValueWithCode* originalValue); + IRGlobalVar* cloneGlobalVar(IRSpecContext* context, IRGlobalVar* originalVar) { auto clonedVar = context->builder->createGlobalVar(context->maybeCloneType(originalVar->getType()->getValueType())); @@ -3333,8 +3338,12 @@ namespace Slang context->builder->addLayoutDecoration(clonedVar, layout); } - // TODO: once we support initializers on global variables, - // we'll need to handle cloning it here. + // Clone any code in the body of the variable, since this + // represents the initializer. + cloneGlobalValueWithCodeCommon( + context, + clonedVar, + originalVar); return clonedVar; } @@ -3363,34 +3372,28 @@ namespace Slang return clonedTable; } - void cloneFunctionCommon( - IRSpecContextBase* context, - IRFunc* clonedFunc, - IRFunc* originalFunc) + void cloneGlobalValueWithCodeCommon( + IRSpecContextBase* context, + IRGlobalValueWithCode* clonedValue, + IRGlobalValueWithCode* originalValue) { - // First clone all the simple properties. - clonedFunc->mangledName = originalFunc->mangledName; - clonedFunc->genericDecl = originalFunc->genericDecl; - clonedFunc->type = context->maybeCloneType(originalFunc->type); - - cloneDecorations(context, clonedFunc, originalFunc); - // Next we are going to clone the actual code. IRBuilder builderStorage = *context->builder; IRBuilder* builder = &builderStorage; - builder->curFunc = clonedFunc; + builder->curFunc = clonedValue; + // We will walk through the blocks of the function, and clone each of them. // // We need to create the cloned blocks first, and then walk through them, // because blocks might be forward referenced (this is not possible // for other cases of instructions). - for (auto originalBlock = originalFunc->getFirstBlock(); + for (auto originalBlock = originalValue->getFirstBlock(); originalBlock; originalBlock = originalBlock->getNextBlock()) { IRBlock* clonedBlock = builder->createBlock(); - clonedFunc->addBlock(clonedBlock); + clonedValue->addBlock(clonedBlock); registerClonedValue(context, clonedBlock, originalBlock); // We can go ahead and clone parameters here, while we are at it. @@ -3409,8 +3412,8 @@ namespace Slang // Okay, now we are in a good position to start cloning // the instructions inside the blocks. { - IRBlock* ob = originalFunc->getFirstBlock(); - IRBlock* cb = clonedFunc->getFirstBlock(); + IRBlock* ob = originalValue->getFirstBlock(); + IRBlock* cb = clonedValue->getFirstBlock(); while (ob) { assert(cb); @@ -3426,6 +3429,25 @@ namespace Slang } } + } + + void cloneFunctionCommon( + IRSpecContextBase* context, + IRFunc* clonedFunc, + IRFunc* originalFunc) + { + // First clone all the simple properties. + clonedFunc->mangledName = originalFunc->mangledName; + clonedFunc->genericDecl = originalFunc->genericDecl; + clonedFunc->type = context->maybeCloneType(originalFunc->type); + + cloneDecorations(context, clonedFunc, originalFunc); + + cloneGlobalValueWithCodeCommon( + context, + clonedFunc, + originalFunc); + // Shuffle the function to the end of the list, because // it needs to follow its dependencies. // diff --git a/source/slang/ir.h b/source/slang/ir.h index 12dc08e13..f2069e7c3 100644 --- a/source/slang/ir.h +++ b/source/slang/ir.h @@ -19,6 +19,7 @@ class Type; class Session; struct IRFunc; +struct IRGlobalValueWithCode; struct IRInst; struct IRModule; struct IRUser; @@ -364,9 +365,9 @@ struct IRBlock : IRValue void addParam(IRParam* param); // The parent function that contains this block - IRFunc* parentFunc; + IRGlobalValueWithCode* parentFunc; - IRFunc* getParent() { return parentFunc; } + IRGlobalValueWithCode* getParent() { return parentFunc; } }; @@ -405,11 +406,26 @@ struct IRGlobalValue : IRValue void moveToEnd(); }; +/// @brief A global value that potentially holds executable code. +/// +struct IRGlobalValueWithCode : IRGlobalValue +{ + // The list of basic blocks in this function + IRBlock* firstBlock = nullptr; + IRBlock* lastBlock = nullptr; + + IRBlock* getFirstBlock() { return firstBlock; } + IRBlock* getLastBlock() { return lastBlock; } + + // Add a block to the end of this function. + void addBlock(IRBlock* block); +}; + // A function is a parent to zero or more blocks of instructions. // // A function is itself a value, so that it can be a direct operand of // an instruction (e.g., a call). -struct IRFunc : IRGlobalValue +struct IRFunc : IRGlobalValueWithCode { // The type of the IR-level function IRFuncType* getType() { return (IRFuncType*) type.Ptr(); } @@ -425,16 +441,6 @@ struct IRFunc : IRGlobalValue UInt getParamCount(); Type* getParamType(UInt index); - // The list of basic blocks in this function - IRBlock* firstBlock = nullptr; - IRBlock* lastBlock = nullptr; - - IRBlock* getFirstBlock() { return firstBlock; } - IRBlock* getLastBlock() { return lastBlock; } - - // Add a block to the end of this function. - void addBlock(IRBlock* block); - // Convenience accessor for the IR parameters, // which are actually the parameters of the first // block. diff --git a/source/slang/lower-to-ir.cpp b/source/slang/lower-to-ir.cpp index 9e58d908b..c01059c61 100644 --- a/source/slang/lower-to-ir.cpp +++ b/source/slang/lower-to-ir.cpp @@ -2693,7 +2693,23 @@ struct DeclLoweringVisitor : DeclVisitor if( auto initExpr = decl->initExpr ) { - // TODO: need to handle global with initializer! + IRBuilder subBuilderStorage = *getBuilder(); + IRBuilder* subBuilder = &subBuilderStorage; + + subBuilder->curFunc = irGlobal; + + IRGenContext subContextStorage = *context; + IRGenContext* subContext = &subContextStorage; + + subContext->irBuilder = subBuilder; + + // TODO: set up a parent IR decl to put the instructions into + + IRBlock* entryBlock = subBuilder->emitBlock(); + subBuilder->curBlock = entryBlock; + + LoweredValInfo initVal = lowerLValueExpr(subContext, initExpr); + subContext->irBuilder->emitReturn(getSimpleVal(subContext, initVal)); } return globalVal; -- cgit v1.2.3