Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
82 changes: 61 additions & 21 deletions llvm/lib/Target/SPIRV/SPIRVInstructionSelector.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -316,6 +316,9 @@ class SPIRVInstructionSelector : public InstructionSelector {
bool selectImageWriteIntrinsic(MachineInstr &I) const;
bool selectResourceGetPointer(Register &ResVReg, const SPIRVType *ResType,
MachineInstr &I) const;
bool selectResourceNonUniformIndex(Register &ResVReg,
const SPIRVType *ResType,
MachineInstr &I) const;
bool selectModf(Register ResVReg, const SPIRVType *ResType,
MachineInstr &I) const;
bool selectUpdateCounter(Register &ResVReg, const SPIRVType *ResType,
Expand Down Expand Up @@ -347,7 +350,7 @@ class SPIRVInstructionSelector : public InstructionSelector {
SPIRV::StorageClass::StorageClass SC,
uint32_t Set, uint32_t Binding,
uint32_t ArraySize, Register IndexReg,
bool IsNonUniform, StringRef Name,
StringRef Name,
MachineIRBuilder MIRBuilder) const;
SPIRVType *widenTypeToVec4(const SPIRVType *Type, MachineInstr &I) const;
bool extractSubvector(Register &ResVReg, const SPIRVType *ResType,
Expand All @@ -364,6 +367,7 @@ class SPIRVInstructionSelector : public InstructionSelector {
MachineInstr &I) const;
bool loadHandleBeforePosition(Register &HandleReg, const SPIRVType *ResType,
GIntrinsic &HandleDef, MachineInstr &Pos) const;
void decorateUsesAsNonUniform(Register &NonUniformReg) const;
};

bool sampledTypeIsSignedInteger(const llvm::Type *HandleType) {
Expand Down Expand Up @@ -3465,6 +3469,9 @@ bool SPIRVInstructionSelector::selectIntrinsic(Register ResVReg,
case Intrinsic::spv_discard: {
return selectDiscard(ResVReg, ResType, I);
}
case Intrinsic::spv_resource_nonuniformindex: {
return selectResourceNonUniformIndex(ResVReg, ResType, I);
}
default: {
std::string DiagMsg;
raw_string_ostream OS(DiagMsg);
Expand Down Expand Up @@ -3504,7 +3511,6 @@ bool SPIRVInstructionSelector::selectCounterHandleFromBinding(
uint32_t Binding = getIConstVal(Intr.getOperand(3).getReg(), MRI);
uint32_t ArraySize = getIConstVal(MainHandleDef->getOperand(4).getReg(), MRI);
Register IndexReg = MainHandleDef->getOperand(5).getReg();
const bool IsNonUniform = false;
std::string CounterName =
getStringValueFromReg(MainHandleDef->getOperand(6).getReg(), *MRI) +
".counter";
Expand All @@ -3513,7 +3519,7 @@ bool SPIRVInstructionSelector::selectCounterHandleFromBinding(
MachineIRBuilder MIRBuilder(I);
Register CounterVarReg = buildPointerToResource(
GR.getPointeeType(ResType), GR.getPointerStorageClass(ResType), Set,
Binding, ArraySize, IndexReg, IsNonUniform, CounterName, MIRBuilder);
Binding, ArraySize, IndexReg, CounterName, MIRBuilder);

return BuildCOPY(ResVReg, CounterVarReg, I);
}
Expand Down Expand Up @@ -3713,6 +3719,55 @@ bool SPIRVInstructionSelector::selectResourceGetPointer(
.constrainAllUses(TII, TRI, RBI);
}

bool SPIRVInstructionSelector::selectResourceNonUniformIndex(
Register &ResVReg, const SPIRVType *ResType, MachineInstr &I) const {
Register ObjReg = I.getOperand(2).getReg();
if (!BuildCOPY(ResVReg, ObjReg, I))
return false;

buildOpDecorate(ResVReg, I, TII, SPIRV::Decoration::NonUniformEXT, {});
// Check for the registers that use the index marked as non-uniform
// and recursively mark them as non-uniform.
// Per the spec, it's necessary that the final argument used for
// load/store/sample/atomic must be decorated, so we need to propagate the
// decoration through access chains and copies.
// https://docs.vulkan.org/samples/latest/samples/extensions/descriptor_indexing/README.html#_when_to_use_non_uniform_indexing_qualifier
decorateUsesAsNonUniform(ResVReg);
return true;
}

void SPIRVInstructionSelector::decorateUsesAsNonUniform(
Register &NonUniformReg) const {
llvm::SmallVector<Register> WorkList = {NonUniformReg};
while (WorkList.size() > 0) {
Register CurrentReg = WorkList.back();
WorkList.pop_back();

bool IsDecorated = false;
for (MachineInstr &Use : MRI->use_instructions(CurrentReg)) {
if (Use.getOpcode() == SPIRV::OpDecorate &&
Use.getOperand(1).getImm() == SPIRV::Decoration::NonUniformEXT) {
IsDecorated = true;
continue;
}
// Check if the instruction has the result register and add it to the
// worklist.
if (Use.getOperand(0).isReg() && Use.getOperand(0).isDef()) {
Register ResultReg = Use.getOperand(0).getReg();
if (ResultReg == CurrentReg)
continue;
WorkList.push_back(ResultReg);
}
}

if (!IsDecorated) {
buildOpDecorate(CurrentReg, *MRI->getVRegDef(CurrentReg), TII,
SPIRV::Decoration::NonUniformEXT, {});
}
}
return;
}

bool SPIRVInstructionSelector::extractSubvector(
Register &ResVReg, const SPIRVType *ResType, Register &ReadReg,
MachineInstr &InsertionPoint) const {
Expand Down Expand Up @@ -3784,7 +3839,7 @@ bool SPIRVInstructionSelector::selectImageWriteIntrinsic(
Register SPIRVInstructionSelector::buildPointerToResource(
const SPIRVType *SpirvResType, SPIRV::StorageClass::StorageClass SC,
uint32_t Set, uint32_t Binding, uint32_t ArraySize, Register IndexReg,
bool IsNonUniform, StringRef Name, MachineIRBuilder MIRBuilder) const {
StringRef Name, MachineIRBuilder MIRBuilder) const {
const Type *ResType = GR.getTypeForSPIRVType(SpirvResType);
if (ArraySize == 1) {
SPIRVType *PtrType =
Expand All @@ -3803,14 +3858,7 @@ Register SPIRVInstructionSelector::buildPointerToResource(

SPIRVType *ResPointerType =
GR.getOrCreateSPIRVPointerType(ResType, MIRBuilder, SC);

Register AcReg = MRI->createVirtualRegister(GR.getRegClass(ResPointerType));
if (IsNonUniform) {
// It is unclear which value needs to be marked an non-uniform, so both
// the index and the access changed are decorated as non-uniform.
buildOpDecorate(IndexReg, MIRBuilder, SPIRV::Decoration::NonUniformEXT, {});
buildOpDecorate(AcReg, MIRBuilder, SPIRV::Decoration::NonUniformEXT, {});
}

MIRBuilder.buildInstr(SPIRV::OpAccessChain)
.addDef(AcReg)
Expand Down Expand Up @@ -4560,9 +4608,6 @@ bool SPIRVInstructionSelector::loadHandleBeforePosition(
uint32_t Binding = foldImm(HandleDef.getOperand(3), MRI);
uint32_t ArraySize = foldImm(HandleDef.getOperand(4), MRI);
Register IndexReg = HandleDef.getOperand(5).getReg();
// FIXME: The IsNonUniform flag needs to be set based on resource analysis.
// https://github.com/llvm/llvm-project/issues/155701
bool IsNonUniform = false;
std::string Name =
getStringValueFromReg(HandleDef.getOperand(6).getReg(), *MRI);

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

Register VarReg =
buildPointerToResource(VarType, SC, Set, Binding, ArraySize, IndexReg,
IsNonUniform, Name, MIRBuilder);

if (IsNonUniform)
buildOpDecorate(HandleReg, HandleDef, TII, SPIRV::Decoration::NonUniformEXT,
{});
Register VarReg = buildPointerToResource(VarType, SC, Set, Binding, ArraySize,
IndexReg, Name, MIRBuilder);

// The handle for the buffer is the pointer to the resource. For an image, the
// handle is the image object. So images get an extra load.
Expand Down
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You changed this test from a non uniform index into an array of storage images (RWBuffer) into an array of storage buffer (RWStructuredBuffers). Can you look at the full set of tests that we will need? See #122355.

You cannot generate all of these from HLSL yet. For now add that can be generated from HLSL. Look at the tables in https://github.com/microsoft/DirectXShaderCompiler/blob/main/docs/SPIR-V.rst#textures and https://github.com/microsoft/DirectXShaderCompiler/blob/main/docs/SPIR-V.rst#constanttexturestructuredbyte-buffers to see the links between HLSL type and the Vulkan type.

Copy link
Contributor Author

@luciechoi luciechoi Oct 10, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for pointing out!

Based on our discussion

  • Separated the tests for RWStructuredBuffer and RWBuffer.
  • Other resource types https://godbolt.org/z/johhnWse1 and https://godbolt.org/z/Gr8oKjxMb aren't yet added to the frontend or have bugs in their implementation, will open up the issues!
    • RWStructuredBuffer should have StorageBufferArrayNonUniformIndexing extension added (both in dxc and llvm)
    • The SPIRV instructions for using Buffer doesn't pass spirv-val, we need to replace OpTypeImageRead with OpTypeImageFetch.

Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
; RUN: llc -O0 -mtriple=spirv1.6-unknown-vulkan1.3-compute %s -o - | FileCheck %s --match-full-lines
; RUN: %if spirv-tools %{ llc -O0 -mtriple=spirv1.6-unknown-vulkan1.3-compute %s -o - -filetype=obj | spirv-val %}

; CHECK-DAG: OpCapability Shader
; CHECK-DAG: OpCapability ShaderNonUniformEXT
; CHECK-DAG: OpDecorate {{%[0-9]+}} NonUniformEXT
; CHECK-DAG: OpDecorate {{%[0-9]+}} NonUniformEXT
; CHECK-DAG: OpDecorate {{%[0-9]+}} NonUniformEXT
; CHECK-DAG: OpDecorate {{%[0-9]+}} NonUniformEXT
; CHECK-DAG: OpDecorate %[[#access1:]] NonUniformEXT
@ReadWriteStructuredBuf.str = private unnamed_addr constant [23 x i8] c"ReadWriteStructuredBuf\00", align 1

define void @main() local_unnamed_addr #0 {
entry:
%0 = tail call i32 @llvm.spv.thread.id.in.group.i32(i32 0)
%add.i = add i32 %0, 1
%1 = tail call noundef i32 @llvm.spv.resource.nonuniformindex(i32 %add.i)
%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)
%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)
%4 = load <4 x i32>, ptr addrspace(11) %3, align 16
%vecins.i = insertelement <4 x i32> %4, i32 99, i64 0
; CHECK: %[[#access1]] = OpAccessChain {{.*}}
; CHECK: OpStore %[[#access1]] {{%[0-9]+}} Aligned 16
store <4 x i32> %vecins.i, ptr addrspace(11) %3, align 16
ret void
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
; RUN: llc -O0 -mtriple=spirv1.6-unknown-vulkan1.3-compute %s -o - | FileCheck %s --match-full-lines
; RUN: %if spirv-tools %{ llc -O0 -mtriple=spirv1.6-unknown-vulkan1.3-compute %s -o - -filetype=obj | spirv-val %}

; CHECK-DAG: OpCapability Shader
; CHECK-DAG: OpCapability ShaderNonUniformEXT
; CHECK-DAG: OpCapability StorageTexelBufferArrayNonUniformIndexingEXT
; CHECK-DAG: OpDecorate {{%[0-9]+}} NonUniformEXT
; CHECK-DAG: OpDecorate %[[#access:]] NonUniformEXT
; CHECK-DAG: OpDecorate %[[#load:]] NonUniformEXT
@ReadWriteBuf.str = private unnamed_addr constant [13 x i8] c"ReadWriteBuf\00", align 1

define void @main() local_unnamed_addr #0 {
entry:
%0 = tail call i32 @llvm.spv.thread.id.in.group.i32(i32 0)
%1 = tail call noundef i32 @llvm.spv.resource.nonuniformindex(i32 %0)
%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)
%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)
; CHECK: {{%[0-9]+}} = OpCompositeExtract {{.*}}
; CHECK: %[[#access]] = OpAccessChain {{.*}}
; CHECK: %[[#load]] = OpLoad {{%[0-9]+}} %[[#access]]
; CHECK: OpImageWrite %[[#load]] {{%[0-9]+}} {{%[0-9]+}}
store i32 95, ptr addrspace(11) %3, align 4
ret void
}

This file was deleted.