Skip to content

Commit 4272bec

Browse files
committed
Support link time constants in embedded SPIR-V
When modules contain extern references to globals they don't define, or when the contain exports of globals they may or may not use, utilize SPIR-V linkage attributes to define and use the variables in embedded SPIR-V IR. Fixes shader-slang#6524
1 parent 9e12e56 commit 4272bec

6 files changed

+112
-34
lines changed

source/slang/slang-compiler-tu.cpp

+52-21
Original file line numberDiff line numberDiff line change
@@ -10,36 +10,48 @@
1010

1111
namespace Slang
1212
{
13-
// Only attempt to precompile functions:
13+
// Only attempt to precompile functions and global variables:
1414
// 1) With function bodies (not just empty decls)
1515
// 2) Not marked with unsafeForceInlineDecoration
1616
// 3) Have a simple HLSL data type as the return or parameter type
1717
static bool attemptPrecompiledExport(IRInst* inst)
1818
{
19-
if (inst->getOp() != kIROp_Func)
19+
if (inst->getOp() != kIROp_Func && inst->getOp() != kIROp_GlobalVar)
2020
{
2121
return false;
2222
}
2323

24-
// Skip functions with no body
25-
bool hasBody = false;
26-
for (auto child : inst->getChildren())
24+
if (inst->getOp() == kIROp_GlobalVar)
2725
{
28-
if (child->getOp() == kIROp_Block)
26+
inst->dump();
27+
if (inst->findDecoration<IRExportDecoration>())
2928
{
30-
hasBody = true;
31-
break;
29+
return true;
3230
}
33-
}
34-
if (!hasBody)
35-
{
3631
return false;
3732
}
38-
39-
// Skip functions marked with unsafeForceInlineDecoration
40-
if (inst->findDecoration<IRUnsafeForceInlineEarlyDecoration>())
33+
else if (inst->getOp() == kIROp_Func)
4134
{
42-
return false;
35+
// Skip functions with no body
36+
bool hasBody = false;
37+
for (auto child : inst->getChildren())
38+
{
39+
if (child->getOp() == kIROp_Block)
40+
{
41+
hasBody = true;
42+
break;
43+
}
44+
}
45+
if (!hasBody)
46+
{
47+
return false;
48+
}
49+
50+
// Skip functions marked with unsafeForceInlineDecoration
51+
if (inst->findDecoration<IRUnsafeForceInlineEarlyDecoration>())
52+
{
53+
return false;
54+
}
4355
}
4456

4557
// Skip non-simple HLSL data types, filters out generics
@@ -51,6 +63,18 @@ static bool attemptPrecompiledExport(IRInst* inst)
5163
return true;
5264
}
5365

66+
static bool needsImport(IRInst* inst)
67+
{
68+
if (inst->getOp() == kIROp_GlobalVar)
69+
{
70+
if (inst->findDecoration<IRUserExternDecoration>())
71+
{
72+
return true;
73+
}
74+
}
75+
return false;
76+
}
77+
5478
/*
5579
* Precompile the module for the given target.
5680
*
@@ -160,24 +184,31 @@ Module::precompileForTarget(SlangCompileTarget target, slang::IBlob** outDiagnos
160184
// the linked result to see which functions survived the pruning and are included in the
161185
// precompiled blob.
162186
Dictionary<String, IRInst*> nameToFunction;
163-
bool hasAtLeastOneFunction = false;
187+
bool hasAtLeastOneExport = false;
164188
for (auto inst : module->getGlobalInsts())
165189
{
166190
if (attemptPrecompiledExport(inst))
167191
{
168-
hasAtLeastOneFunction = true;
192+
hasAtLeastOneExport = true;
169193
builder.addDecoration(inst, kIROp_DownstreamModuleExportDecoration);
170194
nameToFunction[inst->findDecoration<IRExportDecoration>()->getMangledName()] = inst;
171195
}
196+
if (needsImport(inst))
197+
{
198+
builder.addDecoration(inst, kIROp_DownstreamModuleImportDecoration);
199+
}
172200
}
173201

174-
// Bail if there are no functions to export. That's not treated as an error
175-
// because it's possible that the module just doesn't have any simple HLSL.
176-
if (!hasAtLeastOneFunction)
202+
// Bail if there is nothing to export. That's not treated as an error
203+
// because it's possible that the module just doesn't have any simple code.
204+
if (!hasAtLeastOneExport)
177205
{
178206
return SLANG_OK;
179207
}
180208

209+
// dump module
210+
module->getModuleInst()->dump();
211+
181212
ComPtr<IArtifact> outArtifact;
182213
SlangResult res = codeGenContext.emitPrecompiledDownstreamIR(outArtifact);
183214

@@ -208,7 +239,7 @@ Module::precompileForTarget(SlangCompileTarget target, slang::IBlob** outDiagnos
208239
// represent functions that were pruned from the IR after linking, before target generation.
209240
for (auto moduleInst : module->getGlobalInsts())
210241
{
211-
if (moduleInst->getOp() == kIROp_Func)
242+
//if (moduleInst->getOp() == kIROp_Func)
212243
{
213244
if (auto dec = moduleInst->findDecoration<IRDownstreamModuleExportDecoration>())
214245
{

source/slang/slang-emit-spirv.cpp

+27-5
Original file line numberDiff line numberDiff line change
@@ -2556,7 +2556,7 @@ struct SPIRVEmitContext : public SourceEmitterBase, public SPIRVEmitSharedContex
25562556
varInst,
25572557
SpvLiteralInteger::from32(int32_t(0)));
25582558
}
2559-
2559+
25602560
bool anyModifiers = (var->findDecoration<IRInterpolationModeDecoration>() != nullptr);
25612561

25622562
// If the user didn't explicitly qualify a varying
@@ -2736,6 +2736,15 @@ struct SPIRVEmitContext : public SourceEmitterBase, public SPIRVEmitSharedContex
27362736
if (layout)
27372737
emitVarLayout(globalVar, varInst, layout);
27382738
emitDecorations(globalVar, getID(varInst));
2739+
2740+
// Emit the initializer for the global variable
2741+
if (auto initVal = globalVar->getFirstBlock())
2742+
{
2743+
for (auto inst : initVal->getChildren())
2744+
{
2745+
ensureInst(inst);
2746+
}
2747+
}
27392748
return varInst;
27402749
}
27412750

@@ -4860,8 +4869,19 @@ struct SPIRVEmitContext : public SourceEmitterBase, public SPIRVEmitSharedContex
48604869
case kIROp_DownstreamModuleImportDecoration:
48614870
{
48624871
requireSPIRVCapability(SpvCapabilityLinkage);
4863-
auto name =
4864-
decoration->getParent()->findDecoration<IRExportDecoration>()->getMangledName();
4872+
printf("DownstreamModuleImportDecoration\n");
4873+
decoration->dump();
4874+
printf("\nParent to decoration\n");
4875+
decoration->getParent()->dump();
4876+
UnownedStringSlice name;
4877+
if (decoration->getParent()->findDecoration<IRImportDecoration>())
4878+
{
4879+
name = decoration->getParent()->findDecoration<IRImportDecoration>()->getMangledName();
4880+
}
4881+
else
4882+
{
4883+
name = decoration->getParent()->findDecoration<IRExportDecoration>()->getMangledName();
4884+
}
48654885
emitInst(
48664886
getSection(SpvLogicalSectionID::Annotations),
48674887
decoration,
@@ -8329,10 +8349,12 @@ SlangResult emitSPIRVFromIR(
83298349
}
83308350
if (generateWholeProgram)
83318351
{
8332-
if (auto func = as<IRFunc>(inst))
8352+
//if (auto func = as<IRFunc>(inst))
83338353
{
8334-
if (func->findDecoration<IRDownstreamModuleExportDecoration>())
8354+
if (inst->findDecoration<IRDownstreamModuleExportDecoration>())
83358355
{
8356+
printf("exporting...............\n");
8357+
inst->dump();
83368358
context.ensureInst(inst);
83378359
symbolsEmitted = true;
83388360
}

source/slang/slang-ir-explicit-global-init.cpp

+16
Original file line numberDiff line numberDiff line change
@@ -72,6 +72,22 @@ struct MoveGlobalVarInitializationToEntryPointsPass
7272
m_module = module;
7373
m_targetProgram = targetProgram;
7474

75+
// Hack. If there are no entrypoint functions, we don't need to do anything.
76+
bool hasEntryPoints = false;
77+
for (auto inst : m_module->getGlobalInsts())
78+
{
79+
auto func = as<IRFunc>(inst);
80+
if (!func)
81+
continue;
82+
83+
if (!func->findDecoration<IREntryPointDecoration>())
84+
continue;
85+
86+
hasEntryPoints = true;
87+
}
88+
if (!hasEntryPoints)
89+
return;
90+
7591
// We start by looking for global variables with
7692
// initialization logic in the IR, and processing
7793
// each to produce a split variable (now without

source/slang/slang-ir.cpp

+5
Original file line numberDiff line numberDiff line change
@@ -2367,6 +2367,11 @@ IRStringLit* IRBuilder::getStringValue(const UnownedStringSlice& inSlice)
23672367

23682368
IRBlobLit* IRBuilder::getBlobValue(ISlangBlob* blob)
23692369
{
2370+
if (!blob || blob->getBufferSize() == 0)
2371+
{
2372+
return nullptr;
2373+
}
2374+
23702375
IRConstant keyInst;
23712376
memset(&keyInst, 0, sizeof(keyInst));
23722377

+5-5
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
1-
// TEST_IGNORE_FILE
2-
3-
// export-library-global.slang
4-
5-
export static int myGlobalVar = 42;
1+
//TEST:SIMPLE(filecheck=CHECK): -o tests/library/export-library-global.slang-module -target spirv -embed-downstream-ir -dump-ir
62

3+
// CHECK: EmbeddedDownstreamIR(6 : Int,
4+
// CHECK: OpCapability Linkage
5+
// CHECK: OpDecorate %myGlobalVar LinkageAttributes "_SV11myGlobalVar" Export
76

7+
export static int myGlobalVar = 42;

tests/library/precompiled-spirv-global.slang

+7-3
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,12 @@
1-
//TEST:COMPILE: tests/library/precompiled-spirv-global.slang -o tests/library/precompiled-spirv-global.slang-module -target spirv -embed-downstream-ir
1+
//TEST:COMPILE: tests/library/export-library-global.slang -o tests/library/export-library-global.slang-module -target spirv -embed-downstream-ir
2+
//TEST:SIMPLE(filecheck=CHECK): -o tests/library/precompiled-spirv-global.slang-module -target spirv -embed-downstream-ir -dump-ir
3+
//TEST:COMPILE: tests/library/export-library-global.slang-module tests/library/precompiled-spirv-global.slang-module -target spirv
24

3-
//import "export-library-global.slang"; //This would make the test pass
5+
// CHECK: EmbeddedDownstreamIR(6 : Int,
6+
// CHECK: OpCapability Linkage
7+
// CHECK: OpDecorate %myGlobalVar LinkageAttributes "_SV11myGlobalVar" Import
48

5-
extern int myGlobalVar; //This makes the test fail
9+
extern static int myGlobalVar;
610

711
layout(location = 0) out float4 fragColor;
812

0 commit comments

Comments
 (0)