summaryrefslogtreecommitdiffstats
path: root/source/slang
diff options
context:
space:
mode:
authorTim Foley <tfoleyNV@users.noreply.github.com>2017-11-20 13:45:10 -0800
committerGitHub <noreply@github.com>2017-11-20 13:45:10 -0800
commit37315c96ea48045fae60f0e1cb1a3293f3ddd962 (patch)
treea96f139a7a2dae57f67956711be14bf773a50ec2 /source/slang
parent3dff5a53a21ab2404918e772962004767024c0f3 (diff)
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.
Diffstat (limited to 'source/slang')
-rw-r--r--source/slang/emit.cpp52
-rw-r--r--source/slang/ir-insts.h24
-rw-r--r--source/slang/ir.cpp60
-rw-r--r--source/slang/ir.h32
-rw-r--r--source/slang/lower-to-ir.cpp18
5 files changed, 129 insertions, 57 deletions
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<UniformParameterGroupType>())
{
@@ -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<PtrType>(); }
};
@@ -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<DeclLoweringVisitor, LoweredValInfo>
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;