Skip to content

Commit 56c6049

Browse files
authored
[SPIR-V] Support nonuniformindex intrsinsic in SPIRV CodeGen. (#162540)
Support `@llvm.spv.resource.nonuniformindex` in SPIRV Codegen. - Add `NonUniformEXT` decoration to the registers marked as `nonuniformindex`, and recursively decorate its child registers (e.g. Copy, AccessChain, Load) that access such index. - `OpCapability ShaderNonUniformEXT` is already added in the code. - [SPV_EXT_descriptor_indexing](https://github.khronos.org/SPIRV-Registry/extensions/EXT/SPV_EXT_descriptor_indexing.html) is skipped because it's added to SPIRV Core in 1.5. ## Unit test - The unit test checks that the register being used in the final Store/Load/Write instruction is decorated, as required by the spec. - The implementation follows [DXC](https://godbolt.org/z/zhqGThcaf) in that it recursively decorates all the child elements until the end. ```hlsl RWStructuredBuffer<uint4> StructuredOut[64]; RWBuffer<uint> UnStructuredOut[64]; [numthreads(64,1,1)] void main(uint3 GTID: SV_GroupThreadID) { StructuredOut[(NonUniformResourceIndex(GTID.x + 1))][98][0] = 99; UnStructuredOut[(NonUniformResourceIndex(GTID.x))][96] = 95; } ``` Resolves #160231, #161852. Verified [offload-test-suite](https://github.com/llvm/offload-test-suite/blob/cfc37840c8ad0d9c08ee900ecbc0b02cc56478ae/test/Feature/ResourceArrays/unbounded-array-nuri.test) started passing for clang.
1 parent c37c82d commit 56c6049

File tree

4 files changed

+111
-77
lines changed

4 files changed

+111
-77
lines changed

llvm/lib/Target/SPIRV/SPIRVInstructionSelector.cpp

Lines changed: 61 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,7 @@ class SPIRVInstructionSelector : public InstructionSelector {
364367
MachineInstr &I) const;
365368
bool loadHandleBeforePosition(Register &HandleReg, const SPIRVType *ResType,
366369
GIntrinsic &HandleDef, MachineInstr &Pos) const;
370+
void decorateUsesAsNonUniform(Register &NonUniformReg) const;
367371
};
368372

369373
bool sampledTypeIsSignedInteger(const llvm::Type *HandleType) {
@@ -3465,6 +3469,9 @@ bool SPIRVInstructionSelector::selectIntrinsic(Register ResVReg,
34653469
case Intrinsic::spv_discard: {
34663470
return selectDiscard(ResVReg, ResType, I);
34673471
}
3472+
case Intrinsic::spv_resource_nonuniformindex: {
3473+
return selectResourceNonUniformIndex(ResVReg, ResType, I);
3474+
}
34683475
default: {
34693476
std::string DiagMsg;
34703477
raw_string_ostream OS(DiagMsg);
@@ -3504,7 +3511,6 @@ bool SPIRVInstructionSelector::selectCounterHandleFromBinding(
35043511
uint32_t Binding = getIConstVal(Intr.getOperand(3).getReg(), MRI);
35053512
uint32_t ArraySize = getIConstVal(MainHandleDef->getOperand(4).getReg(), MRI);
35063513
Register IndexReg = MainHandleDef->getOperand(5).getReg();
3507-
const bool IsNonUniform = false;
35083514
std::string CounterName =
35093515
getStringValueFromReg(MainHandleDef->getOperand(6).getReg(), *MRI) +
35103516
".counter";
@@ -3513,7 +3519,7 @@ bool SPIRVInstructionSelector::selectCounterHandleFromBinding(
35133519
MachineIRBuilder MIRBuilder(I);
35143520
Register CounterVarReg = buildPointerToResource(
35153521
GR.getPointeeType(ResType), GR.getPointerStorageClass(ResType), Set,
3516-
Binding, ArraySize, IndexReg, IsNonUniform, CounterName, MIRBuilder);
3522+
Binding, ArraySize, IndexReg, CounterName, MIRBuilder);
35173523

35183524
return BuildCOPY(ResVReg, CounterVarReg, I);
35193525
}
@@ -3713,6 +3719,55 @@ bool SPIRVInstructionSelector::selectResourceGetPointer(
37133719
.constrainAllUses(TII, TRI, RBI);
37143720
}
37153721

3722+
bool SPIRVInstructionSelector::selectResourceNonUniformIndex(
3723+
Register &ResVReg, const SPIRVType *ResType, MachineInstr &I) const {
3724+
Register ObjReg = I.getOperand(2).getReg();
3725+
if (!BuildCOPY(ResVReg, ObjReg, I))
3726+
return false;
3727+
3728+
buildOpDecorate(ResVReg, I, TII, SPIRV::Decoration::NonUniformEXT, {});
3729+
// Check for the registers that use the index marked as non-uniform
3730+
// and recursively mark them as non-uniform.
3731+
// Per the spec, it's necessary that the final argument used for
3732+
// load/store/sample/atomic must be decorated, so we need to propagate the
3733+
// decoration through access chains and copies.
3734+
// https://docs.vulkan.org/samples/latest/samples/extensions/descriptor_indexing/README.html#_when_to_use_non_uniform_indexing_qualifier
3735+
decorateUsesAsNonUniform(ResVReg);
3736+
return true;
3737+
}
3738+
3739+
void SPIRVInstructionSelector::decorateUsesAsNonUniform(
3740+
Register &NonUniformReg) const {
3741+
llvm::SmallVector<Register> WorkList = {NonUniformReg};
3742+
while (WorkList.size() > 0) {
3743+
Register CurrentReg = WorkList.back();
3744+
WorkList.pop_back();
3745+
3746+
bool IsDecorated = false;
3747+
for (MachineInstr &Use : MRI->use_instructions(CurrentReg)) {
3748+
if (Use.getOpcode() == SPIRV::OpDecorate &&
3749+
Use.getOperand(1).getImm() == SPIRV::Decoration::NonUniformEXT) {
3750+
IsDecorated = true;
3751+
continue;
3752+
}
3753+
// Check if the instruction has the result register and add it to the
3754+
// worklist.
3755+
if (Use.getOperand(0).isReg() && Use.getOperand(0).isDef()) {
3756+
Register ResultReg = Use.getOperand(0).getReg();
3757+
if (ResultReg == CurrentReg)
3758+
continue;
3759+
WorkList.push_back(ResultReg);
3760+
}
3761+
}
3762+
3763+
if (!IsDecorated) {
3764+
buildOpDecorate(CurrentReg, *MRI->getVRegDef(CurrentReg), TII,
3765+
SPIRV::Decoration::NonUniformEXT, {});
3766+
}
3767+
}
3768+
return;
3769+
}
3770+
37163771
bool SPIRVInstructionSelector::extractSubvector(
37173772
Register &ResVReg, const SPIRVType *ResType, Register &ReadReg,
37183773
MachineInstr &InsertionPoint) const {
@@ -3784,7 +3839,7 @@ bool SPIRVInstructionSelector::selectImageWriteIntrinsic(
37843839
Register SPIRVInstructionSelector::buildPointerToResource(
37853840
const SPIRVType *SpirvResType, SPIRV::StorageClass::StorageClass SC,
37863841
uint32_t Set, uint32_t Binding, uint32_t ArraySize, Register IndexReg,
3787-
bool IsNonUniform, StringRef Name, MachineIRBuilder MIRBuilder) const {
3842+
StringRef Name, MachineIRBuilder MIRBuilder) const {
37883843
const Type *ResType = GR.getTypeForSPIRVType(SpirvResType);
37893844
if (ArraySize == 1) {
37903845
SPIRVType *PtrType =
@@ -3803,14 +3858,7 @@ Register SPIRVInstructionSelector::buildPointerToResource(
38033858

38043859
SPIRVType *ResPointerType =
38053860
GR.getOrCreateSPIRVPointerType(ResType, MIRBuilder, SC);
3806-
38073861
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-
}
38143862

38153863
MIRBuilder.buildInstr(SPIRV::OpAccessChain)
38163864
.addDef(AcReg)
@@ -4560,9 +4608,6 @@ bool SPIRVInstructionSelector::loadHandleBeforePosition(
45604608
uint32_t Binding = foldImm(HandleDef.getOperand(3), MRI);
45614609
uint32_t ArraySize = foldImm(HandleDef.getOperand(4), MRI);
45624610
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;
45664611
std::string Name =
45674612
getStringValueFromReg(HandleDef.getOperand(6).getReg(), *MRI);
45684613

@@ -4576,13 +4621,8 @@ bool SPIRVInstructionSelector::loadHandleBeforePosition(
45764621
SC = GR.getPointerStorageClass(ResType);
45774622
}
45784623

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-
{});
4624+
Register VarReg = buildPointerToResource(VarType, SC, Set, Binding, ArraySize,
4625+
IndexReg, Name, MIRBuilder);
45864626

45874627
// The handle for the buffer is the pointer to the resource. For an image, the
45884628
// handle is the image object. So images get an extra load.
Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
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 %}
3+
4+
; CHECK-DAG: OpCapability Shader
5+
; CHECK-DAG: OpCapability ShaderNonUniformEXT
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+
@ReadWriteStructuredBuf.str = private unnamed_addr constant [23 x i8] c"ReadWriteStructuredBuf\00", align 1
12+
13+
define void @main() local_unnamed_addr #0 {
14+
entry:
15+
%0 = tail call i32 @llvm.spv.thread.id.in.group.i32(i32 0)
16+
%add.i = add i32 %0, 1
17+
%1 = tail call noundef i32 @llvm.spv.resource.nonuniformindex(i32 %add.i)
18+
%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 @ReadWriteStructuredBuf.str)
19+
%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)
20+
%4 = load <4 x i32>, ptr addrspace(11) %3, align 16
21+
%vecins.i = insertelement <4 x i32> %4, i32 99, i64 0
22+
; CHECK: %[[#access1]] = OpAccessChain {{.*}}
23+
; CHECK: OpStore %[[#access1]] {{%[0-9]+}} Aligned 16
24+
store <4 x i32> %vecins.i, ptr addrspace(11) %3, align 16
25+
ret void
26+
}
Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
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 %}
3+
4+
; CHECK-DAG: OpCapability Shader
5+
; CHECK-DAG: OpCapability ShaderNonUniformEXT
6+
; CHECK-DAG: OpCapability StorageTexelBufferArrayNonUniformIndexingEXT
7+
; CHECK-DAG: OpDecorate {{%[0-9]+}} NonUniformEXT
8+
; CHECK-DAG: OpDecorate %[[#access:]] NonUniformEXT
9+
; CHECK-DAG: OpDecorate %[[#load:]] NonUniformEXT
10+
@ReadWriteBuf.str = private unnamed_addr constant [13 x i8] c"ReadWriteBuf\00", align 1
11+
12+
define void @main() local_unnamed_addr #0 {
13+
entry:
14+
%0 = tail call i32 @llvm.spv.thread.id.in.group.i32(i32 0)
15+
%1 = tail call noundef i32 @llvm.spv.resource.nonuniformindex(i32 %0)
16+
%2 = 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 0, i32 0, i32 64, i32 %1, ptr nonnull @ReadWriteBuf.str)
17+
%3 = 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) %2, i32 96)
18+
; CHECK: {{%[0-9]+}} = OpCompositeExtract {{.*}}
19+
; CHECK: %[[#access]] = OpAccessChain {{.*}}
20+
; CHECK: %[[#load]] = OpLoad {{%[0-9]+}} %[[#access]]
21+
; CHECK: OpImageWrite %[[#load]] {{%[0-9]+}} {{%[0-9]+}}
22+
store i32 95, ptr addrspace(11) %3, align 4
23+
ret void
24+
}

llvm/test/CodeGen/SPIRV/hlsl-resources/StorageImageNonUniformIdx.ll

Lines changed: 0 additions & 56 deletions
This file was deleted.

0 commit comments

Comments
 (0)