Skip to content

Commit 3e69411

Browse files
s-perronNoumanAmir657
authored andcommitted
[SPIRV][HLSL] Handle arrays of resources (llvm#111564)
This commit adds the ability to get a particular resource from an array of resources using the handle_fromBinding intrinsic. The main changes are: 1. Create an array when generating the type. 2. Add capabilities from [SPV_EXT_descriptor_indexing](https://htmlpreview.github.io/?https://github.com/KhronosGroup/SPIRV-Registry/blob/main/extensions/EXT/SPV_EXT_descriptor_indexing.html). We are still missing the ability to declare a runtime array. That will be done in a follow up PR.
1 parent 504052f commit 3e69411

19 files changed

+871
-18
lines changed

llvm/lib/Target/SPIRV/SPIRVGlobalRegistry.cpp

Lines changed: 35 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -713,21 +713,36 @@ Register SPIRVGlobalRegistry::buildGlobalVariable(
713713
return Reg;
714714
}
715715

716+
static std::string GetSpirvImageTypeName(const SPIRVType *Type,
717+
MachineIRBuilder &MIRBuilder,
718+
const std::string &Prefix);
719+
716720
static std::string buildSpirvTypeName(const SPIRVType *Type,
717721
MachineIRBuilder &MIRBuilder) {
718722
switch (Type->getOpcode()) {
723+
case SPIRV::OpTypeSampledImage: {
724+
return GetSpirvImageTypeName(Type, MIRBuilder, "sampled_image_");
725+
}
719726
case SPIRV::OpTypeImage: {
720-
Register SampledTypeReg = Type->getOperand(1).getReg();
721-
auto *SampledType = MIRBuilder.getMRI()->getUniqueVRegDef(SampledTypeReg);
722-
std::string TypeName =
723-
"image_" + buildSpirvTypeName(SampledType, MIRBuilder);
724-
for (uint32_t I = 2; I < Type->getNumOperands(); ++I) {
725-
TypeName = (TypeName + '_' + Twine(Type->getOperand(I).getImm())).str();
726-
}
727-
return TypeName;
727+
return GetSpirvImageTypeName(Type, MIRBuilder, "image_");
728+
}
729+
case SPIRV::OpTypeArray: {
730+
MachineRegisterInfo *MRI = MIRBuilder.getMRI();
731+
Register ElementTypeReg = Type->getOperand(1).getReg();
732+
auto *ElementType = MRI->getUniqueVRegDef(ElementTypeReg);
733+
const SPIRVType *TypeInst = MRI->getVRegDef(Type->getOperand(2).getReg());
734+
assert(TypeInst->getOpcode() != SPIRV::OpConstantI);
735+
MachineInstr *ImmInst = MRI->getVRegDef(TypeInst->getOperand(1).getReg());
736+
assert(ImmInst->getOpcode() == TargetOpcode::G_CONSTANT);
737+
uint32_t ArraySize = ImmInst->getOperand(1).getCImm()->getZExtValue();
738+
return (buildSpirvTypeName(ElementType, MIRBuilder) + Twine("[") +
739+
Twine(ArraySize) + Twine("]"))
740+
.str();
728741
}
729742
case SPIRV::OpTypeFloat:
730743
return ("f" + Twine(Type->getOperand(1).getImm())).str();
744+
case SPIRV::OpTypeSampler:
745+
return ("sampler");
731746
case SPIRV::OpTypeInt:
732747
if (Type->getOperand(2).getImm())
733748
return ("i" + Twine(Type->getOperand(1).getImm())).str();
@@ -737,6 +752,18 @@ static std::string buildSpirvTypeName(const SPIRVType *Type,
737752
}
738753
}
739754

755+
static std::string GetSpirvImageTypeName(const SPIRVType *Type,
756+
MachineIRBuilder &MIRBuilder,
757+
const std::string &Prefix) {
758+
Register SampledTypeReg = Type->getOperand(1).getReg();
759+
auto *SampledType = MIRBuilder.getMRI()->getUniqueVRegDef(SampledTypeReg);
760+
std::string TypeName = Prefix + buildSpirvTypeName(SampledType, MIRBuilder);
761+
for (uint32_t I = 2; I < Type->getNumOperands(); ++I) {
762+
TypeName = (TypeName + '_' + Twine(Type->getOperand(I).getImm())).str();
763+
}
764+
return TypeName;
765+
}
766+
740767
Register SPIRVGlobalRegistry::getOrCreateGlobalVariableWithBinding(
741768
const SPIRVType *VarType, uint32_t Set, uint32_t Binding,
742769
MachineIRBuilder &MIRBuilder) {

llvm/lib/Target/SPIRV/SPIRVInstructionSelector.cpp

Lines changed: 37 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -260,6 +260,7 @@ class SPIRVInstructionSelector : public InstructionSelector {
260260
SPIRVType *SrcPtrTy) const;
261261
Register buildPointerToResource(const SPIRVType *ResType, uint32_t Set,
262262
uint32_t Binding, uint32_t ArraySize,
263+
Register IndexReg, bool IsNonUniform,
263264
MachineIRBuilder MIRBuilder) const;
264265
};
265266

@@ -2616,10 +2617,15 @@ void SPIRVInstructionSelector::selectHandleFromBinding(Register &ResVReg,
26162617
uint32_t Set = foldImm(I.getOperand(2), MRI);
26172618
uint32_t Binding = foldImm(I.getOperand(3), MRI);
26182619
uint32_t ArraySize = foldImm(I.getOperand(4), MRI);
2620+
Register IndexReg = I.getOperand(5).getReg();
2621+
bool IsNonUniform = ArraySize > 1 && foldImm(I.getOperand(6), MRI);
26192622

26202623
MachineIRBuilder MIRBuilder(I);
2621-
Register VarReg =
2622-
buildPointerToResource(ResType, Set, Binding, ArraySize, MIRBuilder);
2624+
Register VarReg = buildPointerToResource(ResType, Set, Binding, ArraySize,
2625+
IndexReg, IsNonUniform, MIRBuilder);
2626+
2627+
if (IsNonUniform)
2628+
buildOpDecorate(ResVReg, I, TII, SPIRV::Decoration::NonUniformEXT, {});
26232629

26242630
// TODO: For now we assume the resource is an image, which needs to be
26252631
// loaded to get the handle. That will not be true for storage buffers.
@@ -2631,10 +2637,35 @@ void SPIRVInstructionSelector::selectHandleFromBinding(Register &ResVReg,
26312637

26322638
Register SPIRVInstructionSelector::buildPointerToResource(
26332639
const SPIRVType *ResType, uint32_t Set, uint32_t Binding,
2634-
uint32_t ArraySize, MachineIRBuilder MIRBuilder) const {
2635-
assert(ArraySize == 1 && "Resource arrays are not implemented yet.");
2636-
return GR.getOrCreateGlobalVariableWithBinding(ResType, Set, Binding,
2637-
MIRBuilder);
2640+
uint32_t ArraySize, Register IndexReg, bool IsNonUniform,
2641+
MachineIRBuilder MIRBuilder) const {
2642+
if (ArraySize == 1)
2643+
return GR.getOrCreateGlobalVariableWithBinding(ResType, Set, Binding,
2644+
MIRBuilder);
2645+
2646+
const SPIRVType *VarType = GR.getOrCreateSPIRVArrayType(
2647+
ResType, ArraySize, *MIRBuilder.getInsertPt(), TII);
2648+
Register VarReg = GR.getOrCreateGlobalVariableWithBinding(
2649+
VarType, Set, Binding, MIRBuilder);
2650+
2651+
SPIRVType *ResPointerType = GR.getOrCreateSPIRVPointerType(
2652+
ResType, MIRBuilder, SPIRV::StorageClass::UniformConstant);
2653+
2654+
Register AcReg = MRI->createVirtualRegister(&SPIRV::iIDRegClass);
2655+
if (IsNonUniform) {
2656+
// It is unclear which value needs to be marked an non-uniform, so both
2657+
// the index and the access changed are decorated as non-uniform.
2658+
buildOpDecorate(IndexReg, MIRBuilder, SPIRV::Decoration::NonUniformEXT, {});
2659+
buildOpDecorate(AcReg, MIRBuilder, SPIRV::Decoration::NonUniformEXT, {});
2660+
}
2661+
2662+
MIRBuilder.buildInstr(SPIRV::OpAccessChain)
2663+
.addDef(AcReg)
2664+
.addUse(GR.getSPIRVTypeID(ResPointerType))
2665+
.addUse(VarReg)
2666+
.addUse(IndexReg);
2667+
2668+
return AcReg;
26382669
}
26392670

26402671
bool SPIRVInstructionSelector::selectAllocaArray(Register ResVReg,

llvm/lib/Target/SPIRV/SPIRVModuleAnalysis.cpp

Lines changed: 161 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -689,11 +689,31 @@ void RequirementHandler::initAvailableCapabilitiesForVulkan(
689689
const SPIRVSubtarget &ST) {
690690
addAvailableCaps({Capability::Shader, Capability::Linkage});
691691

692-
// Provided by all supported Vulkan versions.
692+
// Core in Vulkan 1.1 and earlier.
693693
addAvailableCaps({Capability::Int16, Capability::Int64, Capability::Float16,
694694
Capability::Float64, Capability::GroupNonUniform,
695695
Capability::Image1D, Capability::SampledBuffer,
696-
Capability::ImageBuffer});
696+
Capability::ImageBuffer,
697+
Capability::UniformBufferArrayDynamicIndexing,
698+
Capability::SampledImageArrayDynamicIndexing,
699+
Capability::StorageBufferArrayDynamicIndexing,
700+
Capability::StorageImageArrayDynamicIndexing});
701+
702+
// Became core in Vulkan 1.2
703+
if (ST.isAtLeastSPIRVVer(VersionTuple(1, 5))) {
704+
addAvailableCaps(
705+
{Capability::ShaderNonUniformEXT, Capability::RuntimeDescriptorArrayEXT,
706+
Capability::InputAttachmentArrayDynamicIndexingEXT,
707+
Capability::UniformTexelBufferArrayDynamicIndexingEXT,
708+
Capability::StorageTexelBufferArrayDynamicIndexingEXT,
709+
Capability::UniformBufferArrayNonUniformIndexingEXT,
710+
Capability::SampledImageArrayNonUniformIndexingEXT,
711+
Capability::StorageBufferArrayNonUniformIndexingEXT,
712+
Capability::StorageImageArrayNonUniformIndexingEXT,
713+
Capability::InputAttachmentArrayNonUniformIndexingEXT,
714+
Capability::UniformTexelBufferArrayNonUniformIndexingEXT,
715+
Capability::StorageTexelBufferArrayNonUniformIndexingEXT});
716+
}
697717
}
698718

699719
} // namespace SPIRV
@@ -729,6 +749,8 @@ static void addOpDecorateReqs(const MachineInstr &MI, unsigned DecIndex,
729749
Dec == SPIRV::Decoration::ImplementInRegisterMapINTEL) {
730750
Reqs.addExtension(
731751
SPIRV::Extension::SPV_INTEL_global_variable_fpga_decorations);
752+
} else if (Dec == SPIRV::Decoration::NonUniformEXT) {
753+
Reqs.addRequirements(SPIRV::Capability::ShaderNonUniformEXT);
732754
}
733755
}
734756

@@ -848,6 +870,136 @@ static void AddAtomicFloatRequirements(const MachineInstr &MI,
848870
}
849871
}
850872

873+
bool isUniformTexelBuffer(MachineInstr *ImageInst) {
874+
if (ImageInst->getOpcode() != SPIRV::OpTypeImage)
875+
return false;
876+
uint32_t Dim = ImageInst->getOperand(2).getImm();
877+
uint32_t Sampled = ImageInst->getOperand(6).getImm();
878+
return Dim == SPIRV::Dim::DIM_Buffer && Sampled == 1;
879+
}
880+
881+
bool isStorageTexelBuffer(MachineInstr *ImageInst) {
882+
if (ImageInst->getOpcode() != SPIRV::OpTypeImage)
883+
return false;
884+
uint32_t Dim = ImageInst->getOperand(2).getImm();
885+
uint32_t Sampled = ImageInst->getOperand(6).getImm();
886+
return Dim == SPIRV::Dim::DIM_Buffer && Sampled == 2;
887+
}
888+
889+
bool isSampledImage(MachineInstr *ImageInst) {
890+
if (ImageInst->getOpcode() != SPIRV::OpTypeImage)
891+
return false;
892+
uint32_t Dim = ImageInst->getOperand(2).getImm();
893+
uint32_t Sampled = ImageInst->getOperand(6).getImm();
894+
return Dim != SPIRV::Dim::DIM_Buffer && Sampled == 1;
895+
}
896+
897+
bool isInputAttachment(MachineInstr *ImageInst) {
898+
if (ImageInst->getOpcode() != SPIRV::OpTypeImage)
899+
return false;
900+
uint32_t Dim = ImageInst->getOperand(2).getImm();
901+
uint32_t Sampled = ImageInst->getOperand(6).getImm();
902+
return Dim == SPIRV::Dim::DIM_SubpassData && Sampled == 2;
903+
}
904+
905+
bool isStorageImage(MachineInstr *ImageInst) {
906+
if (ImageInst->getOpcode() != SPIRV::OpTypeImage)
907+
return false;
908+
uint32_t Dim = ImageInst->getOperand(2).getImm();
909+
uint32_t Sampled = ImageInst->getOperand(6).getImm();
910+
return Dim != SPIRV::Dim::DIM_Buffer && Sampled == 2;
911+
}
912+
913+
bool isCombinedImageSampler(MachineInstr *SampledImageInst) {
914+
if (SampledImageInst->getOpcode() != SPIRV::OpTypeSampledImage)
915+
return false;
916+
917+
const MachineRegisterInfo &MRI = SampledImageInst->getMF()->getRegInfo();
918+
Register ImageReg = SampledImageInst->getOperand(1).getReg();
919+
auto *ImageInst = MRI.getUniqueVRegDef(ImageReg);
920+
return isSampledImage(ImageInst);
921+
}
922+
923+
bool hasNonUniformDecoration(Register Reg, const MachineRegisterInfo &MRI) {
924+
for (const auto &MI : MRI.reg_instructions(Reg)) {
925+
if (MI.getOpcode() != SPIRV::OpDecorate)
926+
continue;
927+
928+
uint32_t Dec = MI.getOperand(1).getImm();
929+
if (Dec == SPIRV::Decoration::NonUniformEXT)
930+
return true;
931+
}
932+
return false;
933+
}
934+
935+
void addOpAccessChainReqs(const MachineInstr &Instr,
936+
SPIRV::RequirementHandler &Handler,
937+
const SPIRVSubtarget &Subtarget) {
938+
const MachineRegisterInfo &MRI = Instr.getMF()->getRegInfo();
939+
// Get the result type. If it is an image type, then the shader uses
940+
// descriptor indexing. The appropriate capabilities will be added based
941+
// on the specifics of the image.
942+
Register ResTypeReg = Instr.getOperand(1).getReg();
943+
MachineInstr *ResTypeInst = MRI.getUniqueVRegDef(ResTypeReg);
944+
945+
assert(ResTypeInst->getOpcode() == SPIRV::OpTypePointer);
946+
uint32_t StorageClass = ResTypeInst->getOperand(1).getImm();
947+
if (StorageClass != SPIRV::StorageClass::StorageClass::UniformConstant &&
948+
StorageClass != SPIRV::StorageClass::StorageClass::Uniform &&
949+
StorageClass != SPIRV::StorageClass::StorageClass::StorageBuffer) {
950+
return;
951+
}
952+
953+
Register PointeeTypeReg = ResTypeInst->getOperand(2).getReg();
954+
MachineInstr *PointeeType = MRI.getUniqueVRegDef(PointeeTypeReg);
955+
if (PointeeType->getOpcode() != SPIRV::OpTypeImage &&
956+
PointeeType->getOpcode() != SPIRV::OpTypeSampledImage &&
957+
PointeeType->getOpcode() != SPIRV::OpTypeSampler) {
958+
return;
959+
}
960+
961+
bool IsNonUniform =
962+
hasNonUniformDecoration(Instr.getOperand(0).getReg(), MRI);
963+
if (isUniformTexelBuffer(PointeeType)) {
964+
if (IsNonUniform)
965+
Handler.addRequirements(
966+
SPIRV::Capability::UniformTexelBufferArrayNonUniformIndexingEXT);
967+
else
968+
Handler.addRequirements(
969+
SPIRV::Capability::UniformTexelBufferArrayDynamicIndexingEXT);
970+
} else if (isInputAttachment(PointeeType)) {
971+
if (IsNonUniform)
972+
Handler.addRequirements(
973+
SPIRV::Capability::InputAttachmentArrayNonUniformIndexingEXT);
974+
else
975+
Handler.addRequirements(
976+
SPIRV::Capability::InputAttachmentArrayDynamicIndexingEXT);
977+
} else if (isStorageTexelBuffer(PointeeType)) {
978+
if (IsNonUniform)
979+
Handler.addRequirements(
980+
SPIRV::Capability::StorageTexelBufferArrayNonUniformIndexingEXT);
981+
else
982+
Handler.addRequirements(
983+
SPIRV::Capability::StorageTexelBufferArrayDynamicIndexingEXT);
984+
} else if (isSampledImage(PointeeType) ||
985+
isCombinedImageSampler(PointeeType) ||
986+
PointeeType->getOpcode() == SPIRV::OpTypeSampler) {
987+
if (IsNonUniform)
988+
Handler.addRequirements(
989+
SPIRV::Capability::SampledImageArrayNonUniformIndexingEXT);
990+
else
991+
Handler.addRequirements(
992+
SPIRV::Capability::SampledImageArrayDynamicIndexing);
993+
} else if (isStorageImage(PointeeType)) {
994+
if (IsNonUniform)
995+
Handler.addRequirements(
996+
SPIRV::Capability::StorageImageArrayNonUniformIndexingEXT);
997+
else
998+
Handler.addRequirements(
999+
SPIRV::Capability::StorageImageArrayDynamicIndexing);
1000+
}
1001+
}
1002+
8511003
void addInstrRequirements(const MachineInstr &MI,
8521004
SPIRV::RequirementHandler &Reqs,
8531005
const SPIRVSubtarget &ST) {
@@ -967,11 +1119,17 @@ void addInstrRequirements(const MachineInstr &MI,
9671119
case SPIRV::OpConstantSampler:
9681120
Reqs.addCapability(SPIRV::Capability::LiteralSampler);
9691121
break;
1122+
case SPIRV::OpInBoundsAccessChain:
1123+
case SPIRV::OpAccessChain:
1124+
addOpAccessChainReqs(MI, Reqs, ST);
1125+
break;
9701126
case SPIRV::OpTypeImage:
9711127
addOpTypeImageReqs(MI, Reqs, ST);
9721128
break;
9731129
case SPIRV::OpTypeSampler:
974-
Reqs.addCapability(SPIRV::Capability::ImageBasic);
1130+
if (!ST.isVulkanEnv()) {
1131+
Reqs.addCapability(SPIRV::Capability::ImageBasic);
1132+
}
9751133
break;
9761134
case SPIRV::OpTypeForwardPointer:
9771135
// TODO: check if it's OpenCL's kernel.

llvm/lib/Target/SPIRV/SPIRVSymbolicOperands.td

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -355,7 +355,9 @@ defm GeometryPointSize : CapabilityOperand<24, 0, 0, [], [Geometry]>;
355355
defm ImageGatherExtended : CapabilityOperand<25, 0, 0, [], [Shader]>;
356356
defm StorageImageMultisample : CapabilityOperand<27, 0, 0, [], [Shader]>;
357357
defm UniformBufferArrayDynamicIndexing : CapabilityOperand<28, 0, 0, [], [Shader]>;
358-
defm SampledImageArrayDymnamicIndexing : CapabilityOperand<29, 0, 0, [], [Shader]>;
358+
defm SampledImageArrayDynamicIndexing : CapabilityOperand<29, 0, 0, [], [Shader]>;
359+
defm StorageBufferArrayDynamicIndexing : CapabilityOperand<30, 0, 0, [], [Shader]>;
360+
defm StorageImageArrayDynamicIndexing : CapabilityOperand<31, 0, 0, [], [Shader]>;
359361
defm ClipDistance : CapabilityOperand<32, 0, 0, [], [Shader]>;
360362
defm CullDistance : CapabilityOperand<33, 0, 0, [], [Shader]>;
361363
defm SampleRateShading : CapabilityOperand<35, 0, 0, [], [Shader]>;
Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
; TODO(pull/110270): verifier, fix G_BITCAST error "bitcast must change type"
2+
; RUN: llc -O0 -mtriple=spirv1.5-vulkan-library %s -o - | FileCheck %s
3+
; RUN: %if spirv-tools %{ llc -O0 -mtriple=spirv1.5-vulkan-library %s -o - -filetype=obj | spirv-val %}
4+
5+
; CHECK: OpCapability Shader
6+
; CHECK-NEXT: OpCapability SampledImageArrayDynamicIndexing
7+
; CHECK-NEXT: OpCapability Sampled1D
8+
; CHECK-NOT: OpCapability
9+
10+
; CHECK-DAG: OpDecorate [[Var:%[0-9]+]] DescriptorSet 3
11+
; CHECK-DAG: OpDecorate [[Var]] Binding 4
12+
13+
; CHECK-DAG: [[int:%[0-9]+]] = OpTypeInt 32 0
14+
; CHECK-DAG: [[BufferType:%[0-9]+]] = OpTypeImage [[int]] 1D 2 0 0 1 R32i {{$}}
15+
; CHECK-DAG: [[CombindedType:%[0-9]+]] = OpTypeSampledImage [[BufferType]]
16+
; CHECK-DAG: [[BufferPtrType:%[0-9]+]] = OpTypePointer UniformConstant [[CombindedType]]
17+
; CHECK-DAG: [[ArraySize:%[0-9]+]] = OpConstant [[int]] 3
18+
; CHECK-DAG: [[One:%[0-9]+]] = OpConstant [[int]] 1
19+
; CHECK-DAG: [[Zero:%[0-9]+]] = OpConstant [[int]] 0
20+
; CHECK-DAG: [[BufferArrayType:%[0-9]+]] = OpTypeArray [[CombindedType]] [[ArraySize]]
21+
; CHECK-DAG: [[ArrayPtrType:%[0-9]+]] = OpTypePointer UniformConstant [[BufferArrayType]]
22+
; CHECK-DAG: [[Var]] = OpVariable [[ArrayPtrType]] UniformConstant
23+
24+
; CHECK: {{%[0-9]+}} = OpFunction {{%[0-9]+}} DontInline {{%[0-9]+}}
25+
; CHECK-NEXT: OpLabel
26+
define void @main() #0 {
27+
; CHECK: [[ac:%[0-9]+]] = OpAccessChain [[BufferPtrType]] [[Var]] [[Zero]]
28+
; CHECK: [[buffer:%[0-9]+]] = OpLoad [[CombindedType]] [[ac]]
29+
%buffer0 = call target("spirv.SampledImage", i32, 0, 2, 0, 0, 1, 24)
30+
@llvm.spv.handle.fromBinding.tspirv.Image_f32_0_2_0_0_1_24(
31+
i32 3, i32 4, i32 3, i32 0, i1 false)
32+
33+
; CHECK: [[ac:%[0-9]+]] = OpAccessChain [[BufferPtrType]] [[Var]] [[One]]
34+
; CHECK: [[buffer:%[0-9]+]] = OpLoad [[CombindedType]] [[ac]]
35+
%buffer1 = call target("spirv.SampledImage", i32, 0, 2, 0, 0, 1, 24)
36+
@llvm.spv.handle.fromBinding.tspirv.Image_f32_0_2_0_0_1_24(
37+
i32 3, i32 4, i32 3, i32 1, i1 false)
38+
ret void
39+
}
40+
41+
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)