Skip to content

Commit 613a29a

Browse files
cheneym2csyonghe
andauthored
Precompiled SPIR-V import support (shader-slang#5048)
* Precompiled SPIR-V import support Adds appropriate linkage and function declaration syntax for SPIR-V functions that are declared, to be imported from another SPIR-V module. Unlike DXIL, stripping the Slang IR for a function down to a declaration requires retaining a block of parameters, as the function declaration must be emitted to SPIR-V with the same parameters as a definition. Because that thwarts the logic in Slang to tell the difference between a declaration and definition, and explicit decoration is introduced to explicitly mark functions which need to be treated as declarations during emit phase. Fixes shader-slang#4992 Co-authored-by: Yong He <yonghe@outlook.com>
1 parent 99c728f commit 613a29a

9 files changed

+146
-21
lines changed

source/slang/slang-emit-spirv.cpp

+95-12
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
#include "slang-ir-call-graph.h"
77
#include "slang-ir-insts.h"
88
#include "slang-ir-layout.h"
9+
#include "slang-ir-redundancy-removal.h"
910
#include "slang-ir-spirv-legalize.h"
1011
#include "slang-ir-spirv-snippet.h"
1112
#include "slang-ir-util.h"
@@ -2628,14 +2629,75 @@ struct SPIRVEmitContext : public SourceEmitterBase, public SPIRVEmitSharedContex
26282629
/// Emit a declaration for the given `irFunc`
26292630
SpvInst* emitFuncDeclaration(IRFunc* irFunc)
26302631
{
2631-
if (irFunc->findDecorationImpl(kIROp_SPIRVOpDecoration))
2632-
return nullptr;
2633-
// For now we aren't handling function declarations;
2634-
// we expect to deal only with fully linked modules.
2632+
// [2.4: Logical Layout of a Module]
2633+
//
2634+
// > All function declarations("declarations" are functions without a
2635+
// body; there is no forward declaration to a function with a body).
2636+
//
2637+
auto section = getSection(SpvLogicalSectionID::FunctionDeclarations);
2638+
2639+
// > A function declaration is as follows.
2640+
// > * Function declaration, using OpFunction.
2641+
// > * Function parameter declarations, using OpFunctionParameter.
2642+
// > * Function end, using OpFunctionEnd.
2643+
//
2644+
2645+
// [3.24. Function Control]
2646+
//
2647+
// TODO: We should eventually support emitting the "function control"
2648+
// mask to include inline and other hint bits based on decorations
2649+
// set on `irFunc`.
2650+
//
2651+
SpvFunctionControlMask spvFunctionControl = SpvFunctionControlMaskNone;
2652+
2653+
// [3.32.9. Function Instructions]
2654+
//
2655+
// > OpFunction
2656+
//
2657+
// Note that the type <id> of a SPIR-V function uses the
2658+
// *result* type of the function, while the actual function
2659+
// type is given as a later operand. Slan IR instead uses
2660+
// the type of a function instruction store, you know, its *type*.
2661+
//
2662+
SpvInst* spvFunc = emitOpFunction(
2663+
section,
2664+
irFunc,
2665+
irFunc->getDataType()->getResultType(),
2666+
spvFunctionControl,
2667+
irFunc->getDataType());
2668+
2669+
// > OpFunctionParameter
2670+
//
2671+
// Though parameters always belong to blocks in Slang, there are no
2672+
// blocks in a function declaration, so we will emit the parameters
2673+
// as derived from the function's type.
2674+
//
2675+
auto funcType = irFunc->getDataType();
2676+
auto paramCount = funcType->getParamCount();
2677+
for (UInt pp = 0; pp < paramCount; ++pp)
2678+
{
2679+
auto paramType = funcType->getParamType(pp);
2680+
SpvInst* spvParam = emitOpFunctionParameter(spvFunc, nullptr, paramType);
2681+
maybeEmitPointerDecoration(spvParam, paramType, false, kIROp_Param);
2682+
}
2683+
2684+
// [3.32.9. Function Instructions]
2685+
//
2686+
// > OpFunctionEnd
2687+
//
2688+
// In the SPIR-V encoding a function is logically the parent of any
2689+
// instructions up to a matching `OpFunctionEnd`. In our intermediate
2690+
// structure we will make the `OpFunctionEnd` be the last child of
2691+
// the `OpFunction`.
26352692
//
2636-
m_sink->diagnose(irFunc, Diagnostics::internalCompilerError);
2637-
SLANG_UNEXPECTED("function declaration in SPIR-V emit");
2638-
UNREACHABLE_RETURN(nullptr);
2693+
emitOpFunctionEnd(spvFunc, nullptr);
2694+
2695+
// We will emit any decorations pertinent to the function to the
2696+
// appropriate section of the module.
2697+
//
2698+
emitDecorations(irFunc, getID(spvFunc));
2699+
2700+
return spvFunc;
26392701
}
26402702

26412703
/// Emit a SPIR-V function definition for the Slang IR function `irFunc`.
@@ -4358,6 +4420,21 @@ struct SPIRVEmitContext : public SourceEmitterBase, public SPIRVEmitSharedContex
43584420
SpvLinkageTypeExport);
43594421
break;
43604422
}
4423+
case kIROp_DownstreamModuleImportDecoration:
4424+
{
4425+
requireSPIRVCapability(SpvCapabilityLinkage);
4426+
auto name =
4427+
decoration->getParent()->findDecoration<IRExportDecoration>()->getMangledName();
4428+
emitInst(
4429+
getSection(SpvLogicalSectionID::Annotations),
4430+
decoration,
4431+
SpvOpDecorate,
4432+
dstID,
4433+
SpvDecorationLinkageAttributes,
4434+
name,
4435+
SpvLinkageTypeImport);
4436+
break;
4437+
}
43614438
// ...
43624439
}
43634440

@@ -5019,9 +5096,9 @@ struct SPIRVEmitContext : public SourceEmitterBase, public SPIRVEmitSharedContex
50195096
return nullptr;
50205097
}
50215098

5022-
void maybeEmitPointerDecoration(SpvInst* varInst, IRInst* inst)
5099+
void maybeEmitPointerDecoration(SpvInst* varInst, IRType* type, bool isVar, IROp op)
50235100
{
5024-
auto ptrType = as<IRPtrType>(unwrapArray(inst->getDataType()));
5101+
auto ptrType = as<IRPtrType>(unwrapArray(type));
50255102
if (!ptrType)
50265103
return;
50275104
if (addressSpaceToStorageClass(ptrType->getAddressSpace()) ==
@@ -5033,7 +5110,7 @@ struct SPIRVEmitContext : public SourceEmitterBase, public SPIRVEmitSharedContex
50335110
getSection(SpvLogicalSectionID::Annotations),
50345111
nullptr,
50355112
varInst,
5036-
(as<IRVar>(inst) ? SpvDecorationAliasedPointer : SpvDecorationAliased));
5113+
(isVar ? SpvDecorationAliasedPointer : SpvDecorationAliased));
50375114
}
50385115
else
50395116
{
@@ -5049,14 +5126,18 @@ struct SPIRVEmitContext : public SourceEmitterBase, public SPIRVEmitSharedContex
50495126
getSection(SpvLogicalSectionID::Annotations),
50505127
nullptr,
50515128
varInst,
5052-
(inst->getOp() == kIROp_GlobalVar || inst->getOp() == kIROp_Var ||
5053-
inst->getOp() == kIROp_DebugVar
5129+
(op == kIROp_GlobalVar || op == kIROp_Var || op == kIROp_DebugVar
50545130
? SpvDecorationAliasedPointer
50555131
: SpvDecorationAliased));
50565132
}
50575133
}
50585134
}
50595135

5136+
void maybeEmitPointerDecoration(SpvInst* varInst, IRInst* inst)
5137+
{
5138+
maybeEmitPointerDecoration(varInst, inst->getDataType(), as<IRVar>(inst), inst->getOp());
5139+
}
5140+
50605141
SpvInst* emitParam(SpvInstParent* parent, IRInst* inst)
50615142
{
50625143
auto paramSpvInst = emitOpFunctionParameter(parent, inst, inst->getFullType());
@@ -7534,6 +7615,8 @@ SlangResult emitSPIRVFromIR(
75347615
}
75357616
#endif
75367617

7618+
removeAvailableInDownstreamModuleDecorations(CodeGenTarget::SPIRV, irModule);
7619+
75377620
auto shouldPreserveParams = codeGenContext->getTargetProgram()->getOptionSet().getBoolOption(
75387621
CompilerOptionName::PreserveParameters);
75397622
auto generateWholeProgram = codeGenContext->getTargetProgram()->getOptionSet().getBoolOption(

source/slang/slang-ir-inst-defs.h

+1
Original file line numberDiff line numberDiff line change
@@ -822,6 +822,7 @@ INST_RANGE(BindingQuery, GetRegisterIndex, GetRegisterSpace)
822822
INST(PublicDecoration, public, 0, 0)
823823
INST(HLSLExportDecoration, hlslExport, 0, 0)
824824
INST(DownstreamModuleExportDecoration, downstreamModuleExport, 0, 0)
825+
INST(DownstreamModuleImportDecoration, downstreamModuleImport, 0, 0)
825826
INST(PatchConstantFuncDecoration, patchConstantFunc, 1, 0)
826827
INST(OutputControlPointsDecoration, outputControlPoints, 1, 0)
827828
INST(OutputTopologyDecoration, outputTopology, 1, 0)

source/slang/slang-ir-insts.h

+1
Original file line numberDiff line numberDiff line change
@@ -452,6 +452,7 @@ IR_SIMPLE_DECORATION(HLSLMeshPayloadDecoration)
452452
IR_SIMPLE_DECORATION(GlobalInputDecoration)
453453
IR_SIMPLE_DECORATION(GlobalOutputDecoration)
454454
IR_SIMPLE_DECORATION(DownstreamModuleExportDecoration)
455+
IR_SIMPLE_DECORATION(DownstreamModuleImportDecoration)
455456

456457
struct IRAvailableInDownstreamIRDecoration : IRDecoration
457458
{

source/slang/slang-ir-redundancy-removal.cpp

+4-5
Original file line numberDiff line numberDiff line change
@@ -171,6 +171,7 @@ bool removeRedundancyInFunc(IRGlobalValueWithCode* func)
171171
void removeAvailableInDownstreamModuleDecorations(CodeGenTarget target, IRModule* module)
172172
{
173173
List<IRInst*> toRemove;
174+
auto builder = IRBuilder(module);
174175
for (auto globalInst : module->getGlobalInsts())
175176
{
176177
if (auto funcInst = as<IRFunc>(globalInst))
@@ -181,13 +182,11 @@ void removeAvailableInDownstreamModuleDecorations(CodeGenTarget target, IRModule
181182
(dec->getTarget() == target))
182183
{
183184
// Gut the function definition, turning it into a declaration
184-
for (auto inst : funcInst->getChildren())
185+
for (auto block : funcInst->getBlocks())
185186
{
186-
if (inst->getOp() == kIROp_Block)
187-
{
188-
toRemove.add(inst);
189-
}
187+
toRemove.add(block);
190188
}
189+
builder.addDecoration(funcInst, kIROp_DownstreamModuleImportDecoration);
191190
}
192191
}
193192
}

tests/library/export-library-generics.slang

+2-2
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,7 @@ public int normalFuncUsesGeneric(int a)
3333
return genericFunc(obj);
3434
}
3535

36-
public int normalFunc(int a)
36+
public int normalFunc(int a, float b)
3737
{
38-
return a - 2;
38+
return a - floor(b);
3939
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
//TEST_IGNORE_FILE:
2+
3+
// module-library-pointer-param.slang
4+
5+
module "module-library-pointer-param";
6+
7+
public int ptrFunc(int* a)
8+
{
9+
return *a;
10+
}

tests/library/precompiled-dxil-generics.slang

+1-1
Original file line numberDiff line numberDiff line change
@@ -24,5 +24,5 @@ struct Attributes
2424
[shader("anyhit")]
2525
void anyhit(inout Payload payload, Attributes attrib)
2626
{
27-
payload.val = normalFunc(x * y) + normalFuncUsesGeneric(y);
27+
payload.val = normalFunc(floor(x * y), x) + normalFuncUsesGeneric(y);
2828
}

tests/library/precompiled-spirv-generics.slang

+1-1
Original file line numberDiff line numberDiff line change
@@ -24,5 +24,5 @@ struct Attributes
2424
[shader("anyhit")]
2525
void anyhit(inout Payload payload, Attributes attrib)
2626
{
27-
payload.val = normalFunc(x * y) + normalFuncUsesGeneric(y);
27+
payload.val = normalFunc(floor(x * y), x) + normalFuncUsesGeneric(y);
2828
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
// precompiled-spirv-pointer-param.slang
2+
3+
// A test that uses slang-modules with embedded precompiled SPIRV and a library containing
4+
// a function with a pointer parameter.
5+
// The test compiles a library slang (module-library-pointer-param.slang) with -embed-downstream-ir then links the
6+
// library to entrypoint slang (this file).
7+
// The test passes if there is no errror thrown.
8+
// TODO: Check if final linkage used only the precompiled spirv.
9+
10+
//TEST:COMPILE: tests/library/module-library-pointer-param.slang -o tests/library/module-library-pointer-param.slang-module -target spirv -embed-downstream-ir -incomplete-library
11+
//TEST:COMPILE: tests/library/precompiled-spirv-pointer-param.slang -target spirv -stage anyhit -entry anyhit -o tests/library/linked.spirv
12+
13+
import "module-library-pointer-param";
14+
15+
struct Payload
16+
{
17+
int val;
18+
}
19+
20+
struct Attributes
21+
{
22+
float2 bary;
23+
}
24+
25+
[vk::push_constant] int* g_int;
26+
27+
[shader("anyhit")]
28+
void anyhit(inout Payload payload, Attributes attrib)
29+
{
30+
payload.val = ptrFunc(g_int);
31+
}

0 commit comments

Comments
 (0)