Skip to content

Commit 00269a3

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 00269a3

7 files changed

+141
-41
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

+54-10
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
@@ -2727,15 +2727,46 @@ struct SPIRVEmitContext : public SourceEmitterBase, public SPIRVEmitSharedContex
27272727
if (ptrType->hasAddressSpace())
27282728
storageClass = addressSpaceToStorageClass(ptrType->getAddressSpace());
27292729
}
2730-
auto varInst = emitOpVariable(
2731-
getSection(SpvLogicalSectionID::GlobalVariables),
2732-
globalVar,
2733-
globalVar->getDataType(),
2734-
storageClass);
2730+
2731+
// iterate through block children looking for constant initializers
2732+
IRInst* initializer = nullptr;
2733+
for (auto child : globalVar->getChildren())
2734+
{
2735+
if (auto blockInst = as<IRBlock>(child))
2736+
{
2737+
for (auto inst : blockInst->getChildren())
2738+
{
2739+
inst->dump();
2740+
if (auto var = as<IRReturn>(inst))
2741+
{
2742+
initializer = var->getVal();
2743+
}
2744+
}
2745+
}
2746+
}
2747+
SpvInst* varInst;
2748+
if (initializer)
2749+
{
2750+
varInst = emitOpVariable(
2751+
getSection(SpvLogicalSectionID::GlobalVariables),
2752+
globalVar,
2753+
globalVar->getDataType(),
2754+
storageClass,
2755+
initializer);
2756+
}
2757+
else
2758+
{
2759+
varInst = emitOpVariable(
2760+
getSection(SpvLogicalSectionID::GlobalVariables),
2761+
globalVar,
2762+
globalVar->getDataType(),
2763+
storageClass);
2764+
}
27352765
maybeEmitPointerDecoration(varInst, globalVar);
27362766
if (layout)
27372767
emitVarLayout(globalVar, varInst, layout);
27382768
emitDecorations(globalVar, getID(varInst));
2769+
27392770
return varInst;
27402771
}
27412772

@@ -4860,8 +4891,19 @@ struct SPIRVEmitContext : public SourceEmitterBase, public SPIRVEmitSharedContex
48604891
case kIROp_DownstreamModuleImportDecoration:
48614892
{
48624893
requireSPIRVCapability(SpvCapabilityLinkage);
4863-
auto name =
4864-
decoration->getParent()->findDecoration<IRExportDecoration>()->getMangledName();
4894+
printf("DownstreamModuleImportDecoration\n");
4895+
decoration->dump();
4896+
printf("\nParent to decoration\n");
4897+
decoration->getParent()->dump();
4898+
UnownedStringSlice name;
4899+
if (decoration->getParent()->findDecoration<IRImportDecoration>())
4900+
{
4901+
name = decoration->getParent()->findDecoration<IRImportDecoration>()->getMangledName();
4902+
}
4903+
else
4904+
{
4905+
name = decoration->getParent()->findDecoration<IRExportDecoration>()->getMangledName();
4906+
}
48654907
emitInst(
48664908
getSection(SpvLogicalSectionID::Annotations),
48674909
decoration,
@@ -8329,10 +8371,12 @@ SlangResult emitSPIRVFromIR(
83298371
}
83308372
if (generateWholeProgram)
83318373
{
8332-
if (auto func = as<IRFunc>(inst))
8374+
//if (auto func = as<IRFunc>(inst))
83338375
{
8334-
if (func->findDecoration<IRDownstreamModuleExportDecoration>())
8376+
if (inst->findDecoration<IRDownstreamModuleExportDecoration>())
83358377
{
8378+
printf("exporting...............\n");
8379+
inst->dump();
83368380
context.ensureInst(inst);
83378381
symbolsEmitted = true;
83388382
}

source/slang/slang-emit.cpp

+2-2
Original file line numberDiff line numberDiff line change
@@ -2191,8 +2191,8 @@ SlangResult emitSPIRVForEntryPointsDirectly(
21912191
downstreamOptions.optimizationLevel = DownstreamCompileOptions::OptimizationLevel::None;
21922192
break;
21932193
case OptimizationLevel::Default:
2194-
downstreamOptions.optimizationLevel =
2195-
DownstreamCompileOptions::OptimizationLevel::Default;
2194+
downstreamOptions.optimizationLevel = DownstreamCompileOptions::OptimizationLevel::None;
2195+
//Default;
21962196
break;
21972197
case OptimizationLevel::High:
21982198
downstreamOptions.optimizationLevel = DownstreamCompileOptions::OptimizationLevel::High;

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)