Skip to content

Commit 0a4b9d0

Browse files
committed
[SPIR-V] Support intrsinsic in SPIRV CodeGen.
1 parent f5907df commit 0a4b9d0

File tree

2 files changed

+99
-72
lines changed

2 files changed

+99
-72
lines changed

llvm/lib/Target/SPIRV/SPIRVInstructionSelector.cpp

Lines changed: 66 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -316,6 +316,9 @@ class SPIRVInstructionSelector : public InstructionSelector {
316316
bool selectImageWriteIntrinsic(MachineInstr &I) const;
317317
bool selectResourceGetPointer(Register &ResVReg, const SPIRVType *ResType,
318318
MachineInstr &I) const;
319+
bool selectResourceNonUniformIndex(Register &ResVReg,
320+
const SPIRVType *ResType,
321+
MachineInstr &I) const;
319322
bool selectModf(Register ResVReg, const SPIRVType *ResType,
320323
MachineInstr &I) const;
321324
bool selectUpdateCounter(Register &ResVReg, const SPIRVType *ResType,
@@ -347,7 +350,7 @@ class SPIRVInstructionSelector : public InstructionSelector {
347350
SPIRV::StorageClass::StorageClass SC,
348351
uint32_t Set, uint32_t Binding,
349352
uint32_t ArraySize, Register IndexReg,
350-
bool IsNonUniform, StringRef Name,
353+
StringRef Name,
351354
MachineIRBuilder MIRBuilder) const;
352355
SPIRVType *widenTypeToVec4(const SPIRVType *Type, MachineInstr &I) const;
353356
bool extractSubvector(Register &ResVReg, const SPIRVType *ResType,
@@ -364,6 +367,9 @@ class SPIRVInstructionSelector : public InstructionSelector {
364367
MachineInstr &I) const;
365368
bool loadHandleBeforePosition(Register &HandleReg, const SPIRVType *ResType,
366369
GIntrinsic &HandleDef, MachineInstr &Pos) const;
370+
void recursivelyDecorateChildAsNonUniform(Register &NonUniformReg,
371+
const SPIRVType *RegType,
372+
MachineInstr &I) const;
367373
};
368374

369375
bool sampledTypeIsSignedInteger(const llvm::Type *HandleType) {
@@ -3465,6 +3471,9 @@ bool SPIRVInstructionSelector::selectIntrinsic(Register ResVReg,
34653471
case Intrinsic::spv_discard: {
34663472
return selectDiscard(ResVReg, ResType, I);
34673473
}
3474+
case Intrinsic::spv_resource_nonuniformindex: {
3475+
return selectResourceNonUniformIndex(ResVReg, ResType, I);
3476+
}
34683477
default: {
34693478
std::string DiagMsg;
34703479
raw_string_ostream OS(DiagMsg);
@@ -3504,7 +3513,6 @@ bool SPIRVInstructionSelector::selectCounterHandleFromBinding(
35043513
uint32_t Binding = getIConstVal(Intr.getOperand(3).getReg(), MRI);
35053514
uint32_t ArraySize = getIConstVal(MainHandleDef->getOperand(4).getReg(), MRI);
35063515
Register IndexReg = MainHandleDef->getOperand(5).getReg();
3507-
const bool IsNonUniform = false;
35083516
std::string CounterName =
35093517
getStringValueFromReg(MainHandleDef->getOperand(6).getReg(), *MRI) +
35103518
".counter";
@@ -3513,7 +3521,7 @@ bool SPIRVInstructionSelector::selectCounterHandleFromBinding(
35133521
MachineIRBuilder MIRBuilder(I);
35143522
Register CounterVarReg = buildPointerToResource(
35153523
GR.getPointeeType(ResType), GR.getPointerStorageClass(ResType), Set,
3516-
Binding, ArraySize, IndexReg, IsNonUniform, CounterName, MIRBuilder);
3524+
Binding, ArraySize, IndexReg, CounterName, MIRBuilder);
35173525

35183526
return BuildCOPY(ResVReg, CounterVarReg, I);
35193527
}
@@ -3713,6 +3721,58 @@ bool SPIRVInstructionSelector::selectResourceGetPointer(
37133721
.constrainAllUses(TII, TRI, RBI);
37143722
}
37153723

3724+
bool SPIRVInstructionSelector::selectResourceNonUniformIndex(
3725+
Register &ResVReg, const SPIRVType *ResType, MachineInstr &I) const {
3726+
Register ObjReg = I.getOperand(2).getReg();
3727+
if (!BuildCOPY(ResVReg, ObjReg, I))
3728+
return false;
3729+
3730+
buildOpDecorate(ResVReg, I, TII, SPIRV::Decoration::NonUniformEXT, {});
3731+
// Check for the registers that use the index marked as non-uniform
3732+
// and recursively mark them as non-uniform.
3733+
// Per the spec, it's necessary that the final argument used for
3734+
// load/store/sample/atomic must be decorated, so we need to propagate the
3735+
// decoration through access chains and copies.
3736+
// https://docs.vulkan.org/samples/latest/samples/extensions/descriptor_indexing/README.html#_when_to_use_non_uniform_indexing_qualifier
3737+
recursivelyDecorateChildAsNonUniform(ResVReg, ResType, I);
3738+
return true;
3739+
}
3740+
3741+
void SPIRVInstructionSelector::recursivelyDecorateChildAsNonUniform(
3742+
Register &NonUniformReg, const SPIRVType *RegType, MachineInstr &I) const {
3743+
std::vector<std::tuple<Register, const SPIRVType *, MachineInstr *>> WorkList;
3744+
bool isDecorated = false;
3745+
for (MachineInstr &Use :
3746+
RegType->getMF()->getRegInfo().use_instructions(NonUniformReg)) {
3747+
if (Use.getOpcode() != SPIRV::OpDecorate &&
3748+
Use.getOpcode() != SPIRV::OpAccessChain &&
3749+
Use.getOpcode() != SPIRV::OpCopyObject &&
3750+
Use.getOpcode() != SPIRV::OpLoad)
3751+
continue;
3752+
3753+
if (Use.getOpcode() == SPIRV::OpDecorate &&
3754+
Use.getOperand(1).getImm() == SPIRV::Decoration::NonUniformEXT) {
3755+
isDecorated = true;
3756+
continue;
3757+
}
3758+
3759+
Register ResultReg = Use.getOperand(0).getReg();
3760+
SPIRVType *ResultType = GR.getResultType(ResultReg);
3761+
WorkList.push_back(std::make_tuple(ResultReg, ResultType, &Use));
3762+
}
3763+
3764+
if (!isDecorated) {
3765+
buildOpDecorate(NonUniformReg, I, TII, SPIRV::Decoration::NonUniformEXT,
3766+
{});
3767+
}
3768+
3769+
for (auto &Item : WorkList) {
3770+
recursivelyDecorateChildAsNonUniform(std::get<0>(Item), std::get<1>(Item),
3771+
*std::get<2>(Item));
3772+
}
3773+
return;
3774+
}
3775+
37163776
bool SPIRVInstructionSelector::extractSubvector(
37173777
Register &ResVReg, const SPIRVType *ResType, Register &ReadReg,
37183778
MachineInstr &InsertionPoint) const {
@@ -3784,7 +3844,7 @@ bool SPIRVInstructionSelector::selectImageWriteIntrinsic(
37843844
Register SPIRVInstructionSelector::buildPointerToResource(
37853845
const SPIRVType *SpirvResType, SPIRV::StorageClass::StorageClass SC,
37863846
uint32_t Set, uint32_t Binding, uint32_t ArraySize, Register IndexReg,
3787-
bool IsNonUniform, StringRef Name, MachineIRBuilder MIRBuilder) const {
3847+
StringRef Name, MachineIRBuilder MIRBuilder) const {
37883848
const Type *ResType = GR.getTypeForSPIRVType(SpirvResType);
37893849
if (ArraySize == 1) {
37903850
SPIRVType *PtrType =
@@ -3803,14 +3863,7 @@ Register SPIRVInstructionSelector::buildPointerToResource(
38033863

38043864
SPIRVType *ResPointerType =
38053865
GR.getOrCreateSPIRVPointerType(ResType, MIRBuilder, SC);
3806-
38073866
Register AcReg = MRI->createVirtualRegister(GR.getRegClass(ResPointerType));
3808-
if (IsNonUniform) {
3809-
// It is unclear which value needs to be marked an non-uniform, so both
3810-
// the index and the access changed are decorated as non-uniform.
3811-
buildOpDecorate(IndexReg, MIRBuilder, SPIRV::Decoration::NonUniformEXT, {});
3812-
buildOpDecorate(AcReg, MIRBuilder, SPIRV::Decoration::NonUniformEXT, {});
3813-
}
38143867

38153868
MIRBuilder.buildInstr(SPIRV::OpAccessChain)
38163869
.addDef(AcReg)
@@ -4560,9 +4613,6 @@ bool SPIRVInstructionSelector::loadHandleBeforePosition(
45604613
uint32_t Binding = foldImm(HandleDef.getOperand(3), MRI);
45614614
uint32_t ArraySize = foldImm(HandleDef.getOperand(4), MRI);
45624615
Register IndexReg = HandleDef.getOperand(5).getReg();
4563-
// FIXME: The IsNonUniform flag needs to be set based on resource analysis.
4564-
// https://github.com/llvm/llvm-project/issues/155701
4565-
bool IsNonUniform = false;
45664616
std::string Name =
45674617
getStringValueFromReg(HandleDef.getOperand(6).getReg(), *MRI);
45684618

@@ -4576,13 +4626,8 @@ bool SPIRVInstructionSelector::loadHandleBeforePosition(
45764626
SC = GR.getPointerStorageClass(ResType);
45774627
}
45784628

4579-
Register VarReg =
4580-
buildPointerToResource(VarType, SC, Set, Binding, ArraySize, IndexReg,
4581-
IsNonUniform, Name, MIRBuilder);
4582-
4583-
if (IsNonUniform)
4584-
buildOpDecorate(HandleReg, HandleDef, TII, SPIRV::Decoration::NonUniformEXT,
4585-
{});
4629+
Register VarReg = buildPointerToResource(VarType, SC, Set, Binding, ArraySize,
4630+
IndexReg, Name, MIRBuilder);
45864631

45874632
// The handle for the buffer is the pointer to the resource. For an image, the
45884633
// handle is the image object. So images get an extra load.
Lines changed: 33 additions & 51 deletions
Original file line numberDiff line numberDiff line change
@@ -1,56 +1,38 @@
1-
; RUN: llc -O0 -verify-machineinstrs -mtriple=spirv1.5-vulkan-library %s -o - | FileCheck %s
2-
; RUN: %if spirv-tools %{ llc -O0 -mtriple=spirv1.5-vulkan-library %s -o - -filetype=obj | spirv-val %}
3-
4-
; This test depends on llvm.svp.resource.nonuniformindex support (not yet implemented)
5-
; https://github.com/llvm/llvm-project/issues/160231
6-
; XFAIL: *
7-
8-
@.str.b0 = private unnamed_addr constant [3 x i8] c"B0\00", align 1
1+
; RUN: llc -O0 -mtriple=spirv1.6-unknown-vulkan1.3-compute %s -o - | FileCheck %s --match-full-lines
2+
; RUN: %if spirv-tools %{ llc -O0 -mtriple=spirv1.6-unknown-vulkan1.3-compute %s -o - -filetype=obj | spirv-val %}
93

104
; CHECK-DAG: OpCapability Shader
115
; CHECK-DAG: OpCapability ShaderNonUniformEXT
12-
; CHECK-DAG: OpCapability StorageImageArrayNonUniformIndexing
13-
; CHECK-DAG: OpCapability Image1D
14-
; CHECK-NOT: OpCapability
15-
16-
; CHECK-DAG: OpDecorate [[Var:%[0-9]+]] DescriptorSet 3
17-
; CHECK-DAG: OpDecorate [[Var]] Binding 4
18-
; CHECK: OpDecorate [[Zero:%[0-9]+]] NonUniform
19-
; CHECK: OpDecorate [[ac0:%[0-9]+]] NonUniform
20-
; CHECK: OpDecorate [[ld0:%[0-9]+]] NonUniform
21-
; CHECK: OpDecorate [[One:%[0-9]+]] NonUniform
22-
; CHECK: OpDecorate [[ac1:%[0-9]+]] NonUniform
23-
; CHECK: OpDecorate [[ld1:%[0-9]+]] NonUniform
24-
25-
; CHECK-DAG: [[int:%[0-9]+]] = OpTypeInt 32 0
26-
; CHECK-DAG: [[BufferType:%[0-9]+]] = OpTypeImage [[int]] 1D 2 0 0 2 R32i {{$}}
27-
; CHECK-DAG: [[BufferPtrType:%[0-9]+]] = OpTypePointer UniformConstant [[BufferType]]
28-
; CHECK-DAG: [[ArraySize:%[0-9]+]] = OpConstant [[int]] 3
29-
; CHECK-DAG: [[One]] = OpConstant [[int]] 1
30-
; CHECK-DAG: [[Zero]] = OpConstant [[int]] 0{{$}}
31-
; CHECK-DAG: [[BufferArrayType:%[0-9]+]] = OpTypeArray [[BufferType]] [[ArraySize]]
32-
; CHECK-DAG: [[ArrayPtrType:%[0-9]+]] = OpTypePointer UniformConstant [[BufferArrayType]]
33-
; CHECK-DAG: [[Var]] = OpVariable [[ArrayPtrType]] UniformConstant
34-
35-
; CHECK: {{%[0-9]+}} = OpFunction {{%[0-9]+}} DontInline {{%[0-9]+}}
36-
; CHECK-NEXT: OpLabel
37-
define void @main() #0 {
38-
; CHECK: [[ac0]] = OpAccessChain [[BufferPtrType]] [[Var]] [[Zero]]
39-
; CHECK: [[ld0]] = OpLoad [[BufferType]] [[ac0]]
40-
%buffer0 = call target("spirv.Image", i32, 0, 2, 0, 0, 2, 24)
41-
@llvm.spv.resource.handlefrombinding.tspirv.Image_f32_0_2_0_0_2_24(
42-
i32 3, i32 4, i32 3, i32 0, ptr nonnull @.str.b0)
43-
%ptr0 = tail call noundef nonnull align 4 dereferenceable(4) ptr @llvm.spv.resource.getpointer.p0.tspirv.Image_f32_5_2_0_0_2_0t(target("spirv.Image", i32, 0, 2, 0, 0, 2, 24) %buffer0, i32 0)
44-
store i32 0, ptr %ptr0, align 4
45-
46-
; CHECK: [[ac1:%[0-9]+]] = OpAccessChain [[BufferPtrType]] [[Var]] [[One]]
47-
; CHECK: [[ld1]] = OpLoad [[BufferType]] [[ac1]]
48-
%buffer1 = call target("spirv.Image", i32, 0, 2, 0, 0, 2, 24)
49-
@llvm.spv.resource.handlefrombinding.tspirv.Image_f32_0_2_0_0_2_24(
50-
i32 3, i32 4, i32 3, i32 1, ptr nonnull @.str.b0)
51-
%ptr1 = tail call noundef nonnull align 4 dereferenceable(4) ptr @llvm.spv.resource.getpointer.p0.tspirv.Image_f32_5_2_0_0_2_0t(target("spirv.Image", i32, 0, 2, 0, 0, 2, 24) %buffer1, i32 0)
52-
store i32 0, ptr %ptr1, align 4
6+
; CHECK-DAG: OpDecorate {{%[0-9]+}} NonUniformEXT
7+
; CHECK-DAG: OpDecorate {{%[0-9]+}} NonUniformEXT
8+
; CHECK-DAG: OpDecorate {{%[0-9]+}} NonUniformEXT
9+
; CHECK-DAG: OpDecorate {{%[0-9]+}} NonUniformEXT
10+
; CHECK-DAG: OpDecorate %[[#access1:]] NonUniformEXT
11+
@StructuredOut.str = private unnamed_addr constant [14 x i8] c"StructuredOut\00", align 1
12+
; CHECK-DAG: OpDecorate {{%[0-9]+}} NonUniformEXT
13+
; CHECK-DAG: OpDecorate {{%[0-9]+}} NonUniformEXT
14+
; CHECK-DAG: OpDecorate {{%[0-9]+}} NonUniformEXT
15+
; CHECK-DAG: OpDecorate %[[#access2:]] NonUniformEXT
16+
; CHECK-DAG: OpDecorate %[[#load:]] NonUniformEXT
17+
@UnStructuredOut.str = private unnamed_addr constant [16 x i8] c"UnStructuredOut\00", align 1
18+
19+
define void @main() local_unnamed_addr #0 {
20+
entry:
21+
%0 = tail call i32 @llvm.spv.thread.id.in.group.i32(i32 0)
22+
%add.i = add i32 %0, 1
23+
%1 = tail call noundef i32 @llvm.spv.resource.nonuniformindex(i32 %add.i)
24+
%2 = tail call target("spirv.VulkanBuffer", [0 x <4 x i32>], 12, 1) @llvm.spv.resource.handlefromimplicitbinding.tspirv.VulkanBuffer_a0v4i32_12_1t(i32 0, i32 0, i32 64, i32 %1, ptr nonnull @StructuredOut.str)
25+
%3 = tail call noundef align 16 dereferenceable(16) ptr addrspace(11) @llvm.spv.resource.getpointer.p11.tspirv.VulkanBuffer_a0v4i32_12_1t(target("spirv.VulkanBuffer", [0 x <4 x i32>], 12, 1) %2, i32 98)
26+
%4 = load <4 x i32>, ptr addrspace(11) %3, align 16
27+
%vecins.i = insertelement <4 x i32> %4, i32 99, i64 0
28+
; CHECK: OpStore %[[#access1]] {{%[0-9]+}} Aligned 16
29+
store <4 x i32> %vecins.i, ptr addrspace(11) %3, align 16
30+
%5 = tail call noundef i32 @llvm.spv.resource.nonuniformindex(i32 %0)
31+
%6 = tail call target("spirv.Image", i32, 5, 2, 0, 0, 2, 33) @llvm.spv.resource.handlefromimplicitbinding.tspirv.Image_i32_5_2_0_0_2_33t(i32 1, i32 0, i32 64, i32 %5, ptr nonnull @UnStructuredOut.str)
32+
%7 = tail call noundef align 4 dereferenceable(4) ptr addrspace(11) @llvm.spv.resource.getpointer.p11.tspirv.Image_i32_5_2_0_0_2_33t(target("spirv.Image", i32, 5, 2, 0, 0, 2, 33) %6, i32 96)
33+
; CHECK: %[[#access2:]] = OpAccessChain {{.*}}
34+
; CHECK: %[[#load]] = OpLoad {{.*}}
35+
; CHECK: OpImageWrite %[[#load]] {{%[0-9]+}} {{%[0-9]+}}
36+
store i32 95, ptr addrspace(11) %7, align 4
5337
ret void
5438
}
55-
56-
attributes #0 = { convergent noinline norecurse "frame-pointer"="all" "hlsl.numthreads"="1,1,1" "hlsl.shader"="compute" "no-trapping-math"="true" "stack-protector-buffer-size"="8" }

0 commit comments

Comments
 (0)