[llvm] [SPIRV] Emit HLSL structured buffers (PR #132034)
via llvm-commits
llvm-commits at lists.llvm.org
Wed Mar 19 07:02:07 PDT 2025
llvmbot wrote:
<!--LLVM PR SUMMARY COMMENT-->
@llvm/pr-subscribers-backend-spir-v
Author: Steven Perron (s-perron)
<details>
<summary>Changes</summary>
Adds code to expand the `llvm.spv.resource.handlefrombinding` and
`llvm.spv.resource.getpointer` when the resource type is
`spirv.VulkanBuffer`.
It gets expanded as a storage buffer or uniform buffer denpending on the
storage class used.
---
Patch is 36.99 KiB, truncated to 20.00 KiB below, full version: https://github.com/llvm/llvm-project/pull/132034.diff
9 Files Affected:
- (modified) llvm/docs/SPIRVUsage.rst (+17-13)
- (modified) llvm/lib/Target/SPIRV/SPIRVBuiltins.cpp (+66-32)
- (modified) llvm/lib/Target/SPIRV/SPIRVEmitIntrinsics.cpp (+16-7)
- (modified) llvm/lib/Target/SPIRV/SPIRVGlobalRegistry.cpp (+51-23)
- (modified) llvm/lib/Target/SPIRV/SPIRVInstructionSelector.cpp (+91-54)
- (modified) llvm/lib/Target/SPIRV/SPIRVLegalizerInfo.cpp (+9-7)
- (modified) llvm/lib/Target/SPIRV/SPIRVUtils.cpp (+26)
- (modified) llvm/lib/Target/SPIRV/SPIRVUtils.h (+13)
- (added) llvm/test/CodeGen/SPIRV/hlsl-resources/StructuredBuffer.ll (+70)
``````````diff
diff --git a/llvm/docs/SPIRVUsage.rst b/llvm/docs/SPIRVUsage.rst
index 3e19ff881dffc..03ed93cdc9fc3 100644
--- a/llvm/docs/SPIRVUsage.rst
+++ b/llvm/docs/SPIRVUsage.rst
@@ -243,19 +243,20 @@ using target extension types and are represented as follows:
.. table:: SPIR-V Opaque Types
- ================== ====================== ===========================================================================================
- SPIR-V Type LLVM type name LLVM type arguments
- ================== ====================== ===========================================================================================
- OpTypeImage ``spirv.Image`` sampled type, dimensionality, depth, arrayed, MS, sampled, image format, [access qualifier]
- OpTypeSampler ``spirv.Sampler`` (none)
- OpTypeSampledImage ``spirv.SampledImage`` sampled type, dimensionality, depth, arrayed, MS, sampled, image format, [access qualifier]
- OpTypeEvent ``spirv.Event`` (none)
- OpTypeDeviceEvent ``spirv.DeviceEvent`` (none)
- OpTypeReserveId ``spirv.ReserveId`` (none)
- OpTypeQueue ``spirv.Queue`` (none)
- OpTypePipe ``spirv.Pipe`` access qualifier
- OpTypePipeStorage ``spirv.PipeStorage`` (none)
- ================== ====================== ===========================================================================================
+ ================== ======================= ===========================================================================================
+ SPIR-V Type LLVM type name LLVM type arguments
+ ================== ======================= ===========================================================================================
+ OpTypeImage ``spirv.Image`` sampled type, dimensionality, depth, arrayed, MS, sampled, image format, [access qualifier]
+ OpTypeSampler ``spirv.Sampler`` (none)
+ OpTypeSampledImage ``spirv.SampledImage`` sampled type, dimensionality, depth, arrayed, MS, sampled, image format, [access qualifier]
+ OpTypeEvent ``spirv.Event`` (none)
+ OpTypeDeviceEvent ``spirv.DeviceEvent`` (none)
+ OpTypeReserveId ``spirv.ReserveId`` (none)
+ OpTypeQueue ``spirv.Queue`` (none)
+ OpTypePipe ``spirv.Pipe`` access qualifier
+ OpTypePipeStorage ``spirv.PipeStorage`` (none)
+ NA ``spirv.VulkanBuffer`` ElementType, StorageClass, IsWriteable, IsROV
+ ================== ======================= ===========================================================================================
All integer arguments take the same value as they do in their `corresponding
SPIR-V instruction <https://registry.khronos.org/SPIR-V/specs/unified1/SPIRV.html#_type_declaration_instructions>`_.
@@ -266,6 +267,9 @@ parameters of its underlying image type, so that a sampled image for the
previous type has the representation
``target("spirv.SampledImage, void, 1, 1, 0, 0, 0, 0, 0)``.
+See `wg-hlsl proposal 0018 <https://github.com/llvm/wg-hlsl/blob/main/proposals/0018-spirv-resource-representation.md>`_
+for details on ``spirv.VulkanBuffer``.
+
.. _spirv-intrinsics:
Target Intrinsics
diff --git a/llvm/lib/Target/SPIRV/SPIRVBuiltins.cpp b/llvm/lib/Target/SPIRV/SPIRVBuiltins.cpp
index 579e37f68d5d8..c4e681c887191 100644
--- a/llvm/lib/Target/SPIRV/SPIRVBuiltins.cpp
+++ b/llvm/lib/Target/SPIRV/SPIRVBuiltins.cpp
@@ -3041,6 +3041,34 @@ static SPIRVType *getSampledImageType(const TargetExtType *OpaqueType,
return GR->getOrCreateOpTypeSampledImage(OpaqueImageType, MIRBuilder);
}
+static SPIRVType *getVulkanBufferType(const TargetExtType *ExtensionType,
+ MachineIRBuilder &MIRBuilder,
+ SPIRVGlobalRegistry *GR) {
+ assert(ExtensionType->getNumTypeParameters() == 1 &&
+ "Vulkan buffers have exactly one type for the type of the buffer.");
+ assert(ExtensionType->getNumIntParameters() == 3 &&
+ "Vulkan buffer have 3 integer parameters: storage class, is writable, "
+ "and is rov");
+
+ auto *T = StructType::create(ExtensionType->getTypeParameter(0));
+ auto *BlockType = GR->getOrCreateSPIRVType(
+ T, MIRBuilder, SPIRV::AccessQualifier::None, false);
+ buildOpDecorate(BlockType->defs().begin()->getReg(), MIRBuilder,
+ SPIRV::Decoration::Block, {});
+ buildOpMemberDecorate(BlockType->defs().begin()->getReg(), MIRBuilder,
+ SPIRV::Decoration::Offset, 0, {0});
+
+ bool IsWritable = ExtensionType->getIntParameter(1);
+ if (!IsWritable) {
+ buildOpMemberDecorate(BlockType->defs().begin()->getReg(), MIRBuilder,
+ SPIRV::Decoration::NonWritable, 0, {});
+ }
+
+ auto SC = static_cast<SPIRV::StorageClass::StorageClass>(
+ ExtensionType->getIntParameter(0));
+ return GR->getOrCreateSPIRVPointerType(BlockType, MIRBuilder, SC);
+}
+
namespace SPIRV {
TargetExtType *parseBuiltinTypeNameToTargetExtType(std::string TypeName,
LLVMContext &Context) {
@@ -3113,39 +3141,45 @@ SPIRVType *lowerBuiltinType(const Type *OpaqueType,
const StringRef Name = BuiltinType->getName();
LLVM_DEBUG(dbgs() << "Lowering builtin type: " << Name << "\n");
- // Lookup the demangled builtin type in the TableGen records.
- const SPIRV::BuiltinType *TypeRecord = SPIRV::lookupBuiltinType(Name);
- if (!TypeRecord)
- report_fatal_error("Missing TableGen record for builtin type: " + Name);
-
- // "Lower" the BuiltinType into TargetType. The following get<...>Type methods
- // use the implementation details from TableGen records or TargetExtType
- // parameters to either create a new OpType<...> machine instruction or get an
- // existing equivalent SPIRVType from GlobalRegistry.
SPIRVType *TargetType;
- switch (TypeRecord->Opcode) {
- case SPIRV::OpTypeImage:
- TargetType = getImageType(BuiltinType, AccessQual, MIRBuilder, GR);
- break;
- case SPIRV::OpTypePipe:
- TargetType = getPipeType(BuiltinType, MIRBuilder, GR);
- break;
- case SPIRV::OpTypeDeviceEvent:
- TargetType = GR->getOrCreateOpTypeDeviceEvent(MIRBuilder);
- break;
- case SPIRV::OpTypeSampler:
- TargetType = getSamplerType(MIRBuilder, GR);
- break;
- case SPIRV::OpTypeSampledImage:
- TargetType = getSampledImageType(BuiltinType, MIRBuilder, GR);
- break;
- case SPIRV::OpTypeCooperativeMatrixKHR:
- TargetType = getCoopMatrType(BuiltinType, MIRBuilder, GR);
- break;
- default:
- TargetType =
- getNonParameterizedType(BuiltinType, TypeRecord, MIRBuilder, GR);
- break;
+ if (Name == "spirv.VulkanBuffer") {
+ TargetType = getVulkanBufferType(BuiltinType, MIRBuilder, GR);
+ } else {
+ // Lookup the demangled builtin type in the TableGen records.
+ const SPIRV::BuiltinType *TypeRecord = SPIRV::lookupBuiltinType(Name);
+ if (!TypeRecord)
+ report_fatal_error("Missing TableGen record for builtin type: " + Name);
+
+ // "Lower" the BuiltinType into TargetType. The following get<...>Type
+ // methods use the implementation details from TableGen records or
+ // TargetExtType parameters to either create a new OpType<...> machine
+ // instruction or get an existing equivalent SPIRVType from
+ // GlobalRegistry.
+
+ switch (TypeRecord->Opcode) {
+ case SPIRV::OpTypeImage:
+ TargetType = getImageType(BuiltinType, AccessQual, MIRBuilder, GR);
+ break;
+ case SPIRV::OpTypePipe:
+ TargetType = getPipeType(BuiltinType, MIRBuilder, GR);
+ break;
+ case SPIRV::OpTypeDeviceEvent:
+ TargetType = GR->getOrCreateOpTypeDeviceEvent(MIRBuilder);
+ break;
+ case SPIRV::OpTypeSampler:
+ TargetType = getSamplerType(MIRBuilder, GR);
+ break;
+ case SPIRV::OpTypeSampledImage:
+ TargetType = getSampledImageType(BuiltinType, MIRBuilder, GR);
+ break;
+ case SPIRV::OpTypeCooperativeMatrixKHR:
+ TargetType = getCoopMatrType(BuiltinType, MIRBuilder, GR);
+ break;
+ default:
+ TargetType =
+ getNonParameterizedType(BuiltinType, TypeRecord, MIRBuilder, GR);
+ break;
+ }
}
// Emit OpName instruction if a new OpType<...> instruction was added
diff --git a/llvm/lib/Target/SPIRV/SPIRVEmitIntrinsics.cpp b/llvm/lib/Target/SPIRV/SPIRVEmitIntrinsics.cpp
index 68651f4ee4d2f..ed96602e46ad0 100644
--- a/llvm/lib/Target/SPIRV/SPIRVEmitIntrinsics.cpp
+++ b/llvm/lib/Target/SPIRV/SPIRVEmitIntrinsics.cpp
@@ -667,13 +667,22 @@ Type *SPIRVEmitIntrinsics::deduceElementTypeHelper(
auto *II = dyn_cast<IntrinsicInst>(I);
if (II && II->getIntrinsicID() == Intrinsic::spv_resource_getpointer) {
- auto *ImageType = cast<TargetExtType>(II->getOperand(0)->getType());
- assert(ImageType->getTargetExtName() == "spirv.Image");
- (void)ImageType;
- if (II->hasOneUse()) {
- auto *U = *II->users().begin();
- Ty = cast<Instruction>(U)->getAccessType();
- assert(Ty && "Unable to get type for resource pointer.");
+ auto *HandleType = cast<TargetExtType>(II->getOperand(0)->getType());
+ if (HandleType->getTargetExtName() == "spirv.Image") {
+ if (II->hasOneUse()) {
+ auto *U = *II->users().begin();
+ Ty = cast<Instruction>(U)->getAccessType();
+ assert(Ty && "Unable to get type for resource pointer.");
+ }
+ } else if (HandleType->getTargetExtName() == "spirv.VulkanBuffer") {
+ // This call is supposed to index into an array
+ Ty = HandleType->getTypeParameter(0);
+ assert(Ty->isArrayTy() &&
+ "spv_resource_getpointer indexes into an array, so the type of "
+ "the buffer should be an array.");
+ Ty = Ty->getArrayElementType();
+ } else {
+ llvm_unreachable("Unknown handle type for spv_resource_getpointer.");
}
} else if (Function *CalledF = CI->getCalledFunction()) {
std::string DemangledName =
diff --git a/llvm/lib/Target/SPIRV/SPIRVGlobalRegistry.cpp b/llvm/lib/Target/SPIRV/SPIRVGlobalRegistry.cpp
index cbec1c95eadc3..b3aa477bfab94 100644
--- a/llvm/lib/Target/SPIRV/SPIRVGlobalRegistry.cpp
+++ b/llvm/lib/Target/SPIRV/SPIRVGlobalRegistry.cpp
@@ -799,16 +799,18 @@ Register SPIRVGlobalRegistry::buildGlobalVariable(
static std::string GetSpirvImageTypeName(const SPIRVType *Type,
MachineIRBuilder &MIRBuilder,
- const std::string &Prefix);
+ const std::string &Prefix,
+ SPIRVGlobalRegistry &GR);
static std::string buildSpirvTypeName(const SPIRVType *Type,
- MachineIRBuilder &MIRBuilder) {
+ MachineIRBuilder &MIRBuilder,
+ SPIRVGlobalRegistry &GR) {
switch (Type->getOpcode()) {
case SPIRV::OpTypeSampledImage: {
- return GetSpirvImageTypeName(Type, MIRBuilder, "sampled_image_");
+ return GetSpirvImageTypeName(Type, MIRBuilder, "sampled_image_", GR);
}
case SPIRV::OpTypeImage: {
- return GetSpirvImageTypeName(Type, MIRBuilder, "image_");
+ return GetSpirvImageTypeName(Type, MIRBuilder, "image_", GR);
}
case SPIRV::OpTypeArray: {
MachineRegisterInfo *MRI = MIRBuilder.getMRI();
@@ -819,7 +821,7 @@ static std::string buildSpirvTypeName(const SPIRVType *Type,
MachineInstr *ImmInst = MRI->getVRegDef(TypeInst->getOperand(1).getReg());
assert(ImmInst->getOpcode() == TargetOpcode::G_CONSTANT);
uint32_t ArraySize = ImmInst->getOperand(1).getCImm()->getZExtValue();
- return (buildSpirvTypeName(ElementType, MIRBuilder) + Twine("[") +
+ return (buildSpirvTypeName(ElementType, MIRBuilder, GR) + Twine("[") +
Twine(ArraySize) + Twine("]"))
.str();
}
@@ -831,6 +833,22 @@ static std::string buildSpirvTypeName(const SPIRVType *Type,
if (Type->getOperand(2).getImm())
return ("i" + Twine(Type->getOperand(1).getImm())).str();
return ("u" + Twine(Type->getOperand(1).getImm())).str();
+ case SPIRV::OpTypePointer: {
+ uint32_t StorageClass = GR.getPointerStorageClass(Type);
+ SPIRVType *PointeeType = GR.getPointeeType(Type);
+ return ("p_" + Twine(StorageClass) + Twine("_") +
+ buildSpirvTypeName(PointeeType, MIRBuilder, GR))
+ .str();
+ }
+ case SPIRV::OpTypeStruct: {
+ std::string TypeName = "{";
+ for (uint32_t I = 2; I < Type->getNumOperands(); ++I) {
+ SPIRVType *MemberType =
+ GR.getSPIRVTypeForVReg(Type->getOperand(I).getReg());
+ TypeName = '_' + buildSpirvTypeName(MemberType, MIRBuilder, GR);
+ }
+ return TypeName + "}";
+ }
default:
llvm_unreachable("Trying to the the name of an unknown type.");
}
@@ -838,10 +856,12 @@ static std::string buildSpirvTypeName(const SPIRVType *Type,
static std::string GetSpirvImageTypeName(const SPIRVType *Type,
MachineIRBuilder &MIRBuilder,
- const std::string &Prefix) {
+ const std::string &Prefix,
+ SPIRVGlobalRegistry &GR) {
Register SampledTypeReg = Type->getOperand(1).getReg();
auto *SampledType = MIRBuilder.getMRI()->getUniqueVRegDef(SampledTypeReg);
- std::string TypeName = Prefix + buildSpirvTypeName(SampledType, MIRBuilder);
+ std::string TypeName =
+ Prefix + buildSpirvTypeName(SampledType, MIRBuilder, GR);
for (uint32_t I = 2; I < Type->getNumOperands(); ++I) {
TypeName = (TypeName + '_' + Twine(Type->getOperand(I).getImm())).str();
}
@@ -851,20 +871,19 @@ static std::string GetSpirvImageTypeName(const SPIRVType *Type,
Register SPIRVGlobalRegistry::getOrCreateGlobalVariableWithBinding(
const SPIRVType *VarType, uint32_t Set, uint32_t Binding,
MachineIRBuilder &MIRBuilder) {
- SPIRVType *VarPointerTypeReg = getOrCreateSPIRVPointerType(
- VarType, MIRBuilder, SPIRV::StorageClass::UniformConstant);
Register VarReg =
MIRBuilder.getMRI()->createVirtualRegister(&SPIRV::iIDRegClass);
// TODO: The name should come from the llvm-ir, but how that name will be
// passed from the HLSL to the backend has not been decided. Using this place
// holder for now.
- std::string Name = ("__resource_" + buildSpirvTypeName(VarType, MIRBuilder) +
- "_" + Twine(Set) + "_" + Twine(Binding))
- .str();
- buildGlobalVariable(VarReg, VarPointerTypeReg, Name, nullptr,
- SPIRV::StorageClass::UniformConstant, nullptr, false,
- false, SPIRV::LinkageType::Import, MIRBuilder, false);
+ std::string Name =
+ ("__resource_" + buildSpirvTypeName(VarType, MIRBuilder, *this) + "_" +
+ Twine(Set) + "_" + Twine(Binding))
+ .str();
+ buildGlobalVariable(VarReg, VarType, Name, nullptr,
+ getPointerStorageClass(VarType), nullptr, false, false,
+ SPIRV::LinkageType::Import, MIRBuilder, false);
buildOpDecorate(VarReg, MIRBuilder, SPIRV::Decoration::DescriptorSet, {Set});
buildOpDecorate(VarReg, MIRBuilder, SPIRV::Decoration::Binding, {Binding});
@@ -878,14 +897,23 @@ SPIRVType *SPIRVGlobalRegistry::getOpTypeArray(uint32_t NumElems,
assert((ElemType->getOpcode() != SPIRV::OpTypeVoid) &&
"Invalid array element type");
SPIRVType *SpvTypeInt32 = getOrCreateSPIRVIntegerType(32, MIRBuilder);
- Register NumElementsVReg =
- buildConstantInt(NumElems, MIRBuilder, SpvTypeInt32, EmitIR);
- return createOpType(MIRBuilder, [&](MachineIRBuilder &MIRBuilder) {
- return MIRBuilder.buildInstr(SPIRV::OpTypeArray)
- .addDef(createTypeVReg(MIRBuilder))
- .addUse(getSPIRVTypeID(ElemType))
- .addUse(NumElementsVReg);
- });
+
+ if (NumElems != 0) {
+ Register NumElementsVReg =
+ buildConstantInt(NumElems, MIRBuilder, SpvTypeInt32, EmitIR);
+ return createOpType(MIRBuilder, [&](MachineIRBuilder &MIRBuilder) {
+ return MIRBuilder.buildInstr(SPIRV::OpTypeArray)
+ .addDef(createTypeVReg(MIRBuilder))
+ .addUse(getSPIRVTypeID(ElemType))
+ .addUse(NumElementsVReg);
+ });
+ } else {
+ return createOpType(MIRBuilder, [&](MachineIRBuilder &MIRBuilder) {
+ return MIRBuilder.buildInstr(SPIRV::OpTypeRuntimeArray)
+ .addDef(createTypeVReg(MIRBuilder))
+ .addUse(getSPIRVTypeID(ElemType));
+ });
+ }
}
SPIRVType *SPIRVGlobalRegistry::getOpTypeOpaque(const StructType *Ty,
diff --git a/llvm/lib/Target/SPIRV/SPIRVInstructionSelector.cpp b/llvm/lib/Target/SPIRV/SPIRVInstructionSelector.cpp
index 6e0349a463aad..43bc3fbe4a872 100644
--- a/llvm/lib/Target/SPIRV/SPIRVInstructionSelector.cpp
+++ b/llvm/lib/Target/SPIRV/SPIRVInstructionSelector.cpp
@@ -327,9 +327,11 @@ class SPIRVInstructionSelector : public InstructionSelector {
uint32_t Opcode) const;
MachineInstrBuilder buildConstGenericPtr(MachineInstr &I, Register SrcPtr,
SPIRVType *SrcPtrTy) const;
- Register buildPointerToResource(const SPIRVType *ResType, uint32_t Set,
- uint32_t Binding, uint32_t ArraySize,
- Register IndexReg, bool IsNonUniform,
+ Register buildPointerToResource(const SPIRVType *ResType,
+ SPIRV::StorageClass::StorageClass SC,
+ uint32_t Set, uint32_t Binding,
+ uint32_t ArraySize, Register IndexReg,
+ bool IsNonUniform,
MachineIRBuilder MIRBuilder) const;
SPIRVType *widenTypeToVec4(const SPIRVType *Type, MachineInstr &I) const;
bool extractSubvector(Register &ResVReg, const SPIRVType *ResType,
@@ -1088,18 +1090,20 @@ bool SPIRVInstructionSelector::selectLoad(Register ResVReg,
auto *IntPtrDef = dyn_cast<GIntrinsic>(PtrDef);
if (IntPtrDef &&
IntPtrDef->getIntrinsicID() == Intrinsic::spv_resource_getpointer) {
- Register ImageReg = IntPtrDef->getOperand(2).getReg();
- Register NewImageReg =
- MRI->createVirtualRegister(MRI->getRegClass(ImageReg));
- auto *ImageDef = cast<GIntrinsic>(getVRegDef(*MRI, ImageReg));
- if (!loadHandleBeforePosition(NewImageReg, GR.getSPIRVTypeForVReg(ImageReg),
- *ImageDef, I)) {
- return false;
- }
+ Register HandleReg = IntPtrDef->getOperand(2).getReg();
+ SPIRVType *HandleType = GR.getSPIRVTypeForVReg(HandleReg);
+ if (HandleType->getOpcode() == SPIRV::OpTypeImage) {
+ Register NewHandleReg =
+ MRI->createVirtualRegister(MRI->getRegClass(HandleReg));
+ auto *HandleDef = cast<GIntrinsic>(getVRegDef(*MRI, HandleReg));
+ if (!loadHandleBeforePosition(NewHandleReg, HandleType, *HandleDef, I)) {
+ return false;
+ }
- Register IdxReg = IntPtrDef->getOperand(3).getReg();
- return generateImageRead(ResVReg, ResType, NewImageReg, IdxReg,
- I.getDebugLoc(), I);
+ Register IdxReg = IntPtrDef->getOperand(3).getReg();
+ return generateImageRead(ResVReg, ResType, NewHandleReg, IdxReg,
+ I.getDebugLoc(), I);
+ }
}
auto MIB = BuildMI(*I.getParent(), I, I.getDebugLoc(), TII.get(SPIRV::OpLoad))
@@ -1127,22 +1131,24 @@ bool SPIRVInstructionSelector::selectStore(MachineInstr &I) const {
auto *IntPtrDef = dyn_cast<GIntrinsic>(PtrDef);
if (IntPtrDef &&
IntPtrDef->getIntrinsicID() == Intrinsic::spv_resource_getpointer) {
- Register ImageReg = IntPtrDef->getOperand(2).getReg();
- Register NewImageReg =
- MRI->createVirtualRegister(MRI->getRegClass(ImageReg));
- auto *ImageDef = cast<GIntrinsic>(getVRegDef(*MRI, ImageReg));
- if (!loadHandleBeforePosition(NewImageReg, GR.getSPIRVTypeForVReg(ImageReg),
- *ImageDef, I)) {
+ Register HandleReg = IntPtrDef->getOperand(2).getReg();
+ Register NewHandleReg =
+ MRI->createVirtualRegister(MRI->getRegClass(HandleReg));
+ ...
[truncated]
``````````
</details>
https://github.com/llvm/llvm-project/pull/132034
More information about the llvm-commits
mailing list