Skip to content

Commit 37315c9

Browse files
author
Tim Foley
authored
IR: support global variable with initializers (shader-slang#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.
1 parent 3dff5a5 commit 37315c9

File tree

7 files changed

+156
-57
lines changed

7 files changed

+156
-57
lines changed

source/slang/emit.cpp

+41-11
Original file line numberDiff line numberDiff line change
@@ -6461,7 +6461,23 @@ emitDeclImpl(decl, nullptr);
64616461
{
64626462
auto allocatedType = varDecl->getType();
64636463
auto varType = allocatedType->getValueType();
6464-
// auto addressSpace = allocatedType->getAddressSpace();
6464+
6465+
String initFuncName;
6466+
if (varDecl->firstBlock)
6467+
{
6468+
// A global variable with code means it has an initializer
6469+
// associated with it. Eventually we'd like to emit that
6470+
// initializer directly as an expression here, but for
6471+
// now we'll emit it as a separate function.
6472+
6473+
initFuncName = getIRName(varDecl);
6474+
initFuncName.append("_init");
6475+
emitIRType(ctx, varType, initFuncName);
6476+
Emit("()\n{\n");
6477+
emitIRStmtsForBlocks(ctx, varDecl->firstBlock, nullptr);
6478+
Emit("}\n");
6479+
}
6480+
64656481

64666482
if (auto paramBlockType = varType->As<UniformParameterGroupType>())
64676483
{
@@ -6475,27 +6491,41 @@ emitDeclImpl(decl, nullptr);
64756491
// Need to emit appropriate modifiers here.
64766492

64776493
auto layout = getVarLayout(ctx, varDecl);
6478-
6479-
emitIRVarModifiers(ctx, layout);
64806494

6481-
#if 0
6482-
switch (addressSpace)
6495+
if (!layout)
64836496
{
6484-
default:
6485-
break;
6497+
// A global variable without a layout is just an
6498+
// ordinary global variable, and may need special
6499+
// modifiers to indicate it as such.
6500+
switch (getTarget(ctx))
6501+
{
6502+
case CodeGenTarget::HLSL:
6503+
// HLSL requires the `static` modifier on any
6504+
// global variables; otherwise they are assumed
6505+
// to be uniforms.
6506+
Emit("static ");
6507+
break;
64866508

6487-
case kIRAddressSpace_GroupShared:
6488-
emit("groupshared ");
6489-
break;
6509+
default:
6510+
break;
6511+
}
64906512
}
6491-
#endif
6513+
6514+
emitIRVarModifiers(ctx, layout);
64926515

64936516
emitIRType(ctx, varType, getIRName(varDecl));
64946517

64956518
emitIRSemantics(ctx, varDecl);
64966519

64976520
emitIRLayoutSemantics(ctx, varDecl);
64986521

6522+
if (varDecl->firstBlock)
6523+
{
6524+
Emit(" = ");
6525+
emit(initFuncName);
6526+
Emit("()");
6527+
}
6528+
64996529
emit(";\n");
65006530
}
65016531

source/slang/ir-insts.h

+11-13
Original file line numberDiff line numberDiff line change
@@ -276,16 +276,14 @@ struct IRVar : IRInst
276276
}
277277
};
278278

279-
struct IRGlobalVar : IRGlobalValue
279+
/// @brief A global variable.
280+
///
281+
/// Represents a global variable in the IR.
282+
/// If the variable has an initializer, then
283+
/// it is represented by the code in the basic
284+
/// blocks nested inside this value.
285+
struct IRGlobalVar : IRGlobalValueWithCode
280286
{
281-
// TODO: should contain information
282-
// for use in initializing the variable
283-
// (e.g., a reference to a function
284-
// that is to be evaluated to provide
285-
// the initial value, or a basic block
286-
// that defines a DAG of constant
287-
// values to use as initial values...)
288-
289287
PtrType* getType() { return type.As<PtrType>(); }
290288
};
291289

@@ -359,14 +357,14 @@ struct IRBuilder
359357

360358
// The current function and block being inserted into
361359
// (or `null` if we aren't inserting).
362-
IRFunc* curFunc = nullptr;
363-
IRBlock* curBlock = nullptr;
360+
IRGlobalValueWithCode* curFunc = nullptr;
361+
IRBlock* curBlock = nullptr;
364362
//
365363
// An instruction in the current block that we should insert before
366364
IRInst* insertBeforeInst = nullptr;
367365

368-
IRFunc* getFunc() { return curFunc; }
369-
IRBlock* getBlock() { return curBlock; }
366+
IRGlobalValueWithCode* getFunc() { return curFunc; }
367+
IRBlock* getBlock() { return curBlock; }
370368

371369
void addInst(IRBlock* block, IRInst* inst);
372370
void addInst(IRInst* inst);

source/slang/ir.cpp

+41-19
Original file line numberDiff line numberDiff line change
@@ -131,7 +131,7 @@ namespace Slang
131131
return entryBlock->getFirstParam();
132132
}
133133

134-
void IRFunc::addBlock(IRBlock* block)
134+
void IRGlobalValueWithCode::addBlock(IRBlock* block)
135135
{
136136
block->parentFunc = this;
137137

@@ -3317,6 +3317,11 @@ namespace Slang
33173317
}
33183318
}
33193319

3320+
void cloneGlobalValueWithCodeCommon(
3321+
IRSpecContextBase* context,
3322+
IRGlobalValueWithCode* clonedValue,
3323+
IRGlobalValueWithCode* originalValue);
3324+
33203325
IRGlobalVar* cloneGlobalVar(IRSpecContext* context, IRGlobalVar* originalVar)
33213326
{
33223327
auto clonedVar = context->builder->createGlobalVar(context->maybeCloneType(originalVar->getType()->getValueType()));
@@ -3333,8 +3338,12 @@ namespace Slang
33333338
context->builder->addLayoutDecoration(clonedVar, layout);
33343339
}
33353340

3336-
// TODO: once we support initializers on global variables,
3337-
// we'll need to handle cloning it here.
3341+
// Clone any code in the body of the variable, since this
3342+
// represents the initializer.
3343+
cloneGlobalValueWithCodeCommon(
3344+
context,
3345+
clonedVar,
3346+
originalVar);
33383347

33393348
return clonedVar;
33403349
}
@@ -3363,34 +3372,28 @@ namespace Slang
33633372
return clonedTable;
33643373
}
33653374

3366-
void cloneFunctionCommon(
3367-
IRSpecContextBase* context,
3368-
IRFunc* clonedFunc,
3369-
IRFunc* originalFunc)
3375+
void cloneGlobalValueWithCodeCommon(
3376+
IRSpecContextBase* context,
3377+
IRGlobalValueWithCode* clonedValue,
3378+
IRGlobalValueWithCode* originalValue)
33703379
{
3371-
// First clone all the simple properties.
3372-
clonedFunc->mangledName = originalFunc->mangledName;
3373-
clonedFunc->genericDecl = originalFunc->genericDecl;
3374-
clonedFunc->type = context->maybeCloneType(originalFunc->type);
3375-
3376-
cloneDecorations(context, clonedFunc, originalFunc);
3377-
33783380
// Next we are going to clone the actual code.
33793381
IRBuilder builderStorage = *context->builder;
33803382
IRBuilder* builder = &builderStorage;
3381-
builder->curFunc = clonedFunc;
3383+
builder->curFunc = clonedValue;
3384+
33823385

33833386
// We will walk through the blocks of the function, and clone each of them.
33843387
//
33853388
// We need to create the cloned blocks first, and then walk through them,
33863389
// because blocks might be forward referenced (this is not possible
33873390
// for other cases of instructions).
3388-
for (auto originalBlock = originalFunc->getFirstBlock();
3391+
for (auto originalBlock = originalValue->getFirstBlock();
33893392
originalBlock;
33903393
originalBlock = originalBlock->getNextBlock())
33913394
{
33923395
IRBlock* clonedBlock = builder->createBlock();
3393-
clonedFunc->addBlock(clonedBlock);
3396+
clonedValue->addBlock(clonedBlock);
33943397
registerClonedValue(context, clonedBlock, originalBlock);
33953398

33963399
// We can go ahead and clone parameters here, while we are at it.
@@ -3409,8 +3412,8 @@ namespace Slang
34093412
// Okay, now we are in a good position to start cloning
34103413
// the instructions inside the blocks.
34113414
{
3412-
IRBlock* ob = originalFunc->getFirstBlock();
3413-
IRBlock* cb = clonedFunc->getFirstBlock();
3415+
IRBlock* ob = originalValue->getFirstBlock();
3416+
IRBlock* cb = clonedValue->getFirstBlock();
34143417
while (ob)
34153418
{
34163419
assert(cb);
@@ -3426,6 +3429,25 @@ namespace Slang
34263429
}
34273430
}
34283431

3432+
}
3433+
3434+
void cloneFunctionCommon(
3435+
IRSpecContextBase* context,
3436+
IRFunc* clonedFunc,
3437+
IRFunc* originalFunc)
3438+
{
3439+
// First clone all the simple properties.
3440+
clonedFunc->mangledName = originalFunc->mangledName;
3441+
clonedFunc->genericDecl = originalFunc->genericDecl;
3442+
clonedFunc->type = context->maybeCloneType(originalFunc->type);
3443+
3444+
cloneDecorations(context, clonedFunc, originalFunc);
3445+
3446+
cloneGlobalValueWithCodeCommon(
3447+
context,
3448+
clonedFunc,
3449+
originalFunc);
3450+
34293451
// Shuffle the function to the end of the list, because
34303452
// it needs to follow its dependencies.
34313453
//

source/slang/ir.h

+19-13
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ class Type;
1919
class Session;
2020

2121
struct IRFunc;
22+
struct IRGlobalValueWithCode;
2223
struct IRInst;
2324
struct IRModule;
2425
struct IRUser;
@@ -364,9 +365,9 @@ struct IRBlock : IRValue
364365
void addParam(IRParam* param);
365366

366367
// The parent function that contains this block
367-
IRFunc* parentFunc;
368+
IRGlobalValueWithCode* parentFunc;
368369

369-
IRFunc* getParent() { return parentFunc; }
370+
IRGlobalValueWithCode* getParent() { return parentFunc; }
370371

371372
};
372373

@@ -405,11 +406,26 @@ struct IRGlobalValue : IRValue
405406
void moveToEnd();
406407
};
407408

409+
/// @brief A global value that potentially holds executable code.
410+
///
411+
struct IRGlobalValueWithCode : IRGlobalValue
412+
{
413+
// The list of basic blocks in this function
414+
IRBlock* firstBlock = nullptr;
415+
IRBlock* lastBlock = nullptr;
416+
417+
IRBlock* getFirstBlock() { return firstBlock; }
418+
IRBlock* getLastBlock() { return lastBlock; }
419+
420+
// Add a block to the end of this function.
421+
void addBlock(IRBlock* block);
422+
};
423+
408424
// A function is a parent to zero or more blocks of instructions.
409425
//
410426
// A function is itself a value, so that it can be a direct operand of
411427
// an instruction (e.g., a call).
412-
struct IRFunc : IRGlobalValue
428+
struct IRFunc : IRGlobalValueWithCode
413429
{
414430
// The type of the IR-level function
415431
IRFuncType* getType() { return (IRFuncType*) type.Ptr(); }
@@ -425,16 +441,6 @@ struct IRFunc : IRGlobalValue
425441
UInt getParamCount();
426442
Type* getParamType(UInt index);
427443

428-
// The list of basic blocks in this function
429-
IRBlock* firstBlock = nullptr;
430-
IRBlock* lastBlock = nullptr;
431-
432-
IRBlock* getFirstBlock() { return firstBlock; }
433-
IRBlock* getLastBlock() { return lastBlock; }
434-
435-
// Add a block to the end of this function.
436-
void addBlock(IRBlock* block);
437-
438444
// Convenience accessor for the IR parameters,
439445
// which are actually the parameters of the first
440446
// block.

source/slang/lower-to-ir.cpp

+17-1
Original file line numberDiff line numberDiff line change
@@ -2693,7 +2693,23 @@ struct DeclLoweringVisitor : DeclVisitor<DeclLoweringVisitor, LoweredValInfo>
26932693

26942694
if( auto initExpr = decl->initExpr )
26952695
{
2696-
// TODO: need to handle global with initializer!
2696+
IRBuilder subBuilderStorage = *getBuilder();
2697+
IRBuilder* subBuilder = &subBuilderStorage;
2698+
2699+
subBuilder->curFunc = irGlobal;
2700+
2701+
IRGenContext subContextStorage = *context;
2702+
IRGenContext* subContext = &subContextStorage;
2703+
2704+
subContext->irBuilder = subBuilder;
2705+
2706+
// TODO: set up a parent IR decl to put the instructions into
2707+
2708+
IRBlock* entryBlock = subBuilder->emitBlock();
2709+
subBuilder->curBlock = entryBlock;
2710+
2711+
LoweredValInfo initVal = lowerLValueExpr(subContext, initExpr);
2712+
subContext->irBuilder->emitReturn(getSimpleVal(subContext, initVal));
26972713
}
26982714

26992715
return globalVal;

tests/compute/global-init.slang

+23
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
//TEST(compute):COMPARE_COMPUTE:-xslang -use-ir
2+
//TEST_INPUT:ubuffer(data=[0 1 2 3], stride=4):dxbinding(0),glbinding(0),out
3+
4+
// Test that a global variable (not a shader parameter)
5+
// with an initializer works.
6+
7+
static int gVar = 16;
8+
9+
int test(int inVal)
10+
{
11+
return inVal + gVar;
12+
}
13+
14+
RWStructuredBuffer<int> outputBuffer : register(u0);
15+
16+
[numthreads(4, 1, 1)]
17+
void computeMain(uint3 dispatchThreadID : SV_DispatchThreadID)
18+
{
19+
uint tid = dispatchThreadID.x;
20+
int inVal = outputBuffer[tid];
21+
int outVal = test(inVal);
22+
outputBuffer[tid] = outVal;
23+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
10
2+
11
3+
12
4+
13

0 commit comments

Comments
 (0)