Skip to content

Commit a77d807

Browse files
authored
[SPIRV] Add spirv.VulkanBuffer types to the backend (llvm#133475)
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. This is implementing part of https://github.com/llvm/wg-hlsl/blob/main/proposals/0018-spirv-resource-representation.md.
1 parent 9df324e commit a77d807

13 files changed

+404
-102
lines changed

llvm/docs/SPIRVUsage.rst

Lines changed: 17 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -243,19 +243,20 @@ using target extension types and are represented as follows:
243243

244244
.. table:: SPIR-V Opaque Types
245245

246-
================== ====================== ===========================================================================================
247-
SPIR-V Type LLVM type name LLVM type arguments
248-
================== ====================== ===========================================================================================
249-
OpTypeImage ``spirv.Image`` sampled type, dimensionality, depth, arrayed, MS, sampled, image format, [access qualifier]
250-
OpTypeSampler ``spirv.Sampler`` (none)
251-
OpTypeSampledImage ``spirv.SampledImage`` sampled type, dimensionality, depth, arrayed, MS, sampled, image format, [access qualifier]
252-
OpTypeEvent ``spirv.Event`` (none)
253-
OpTypeDeviceEvent ``spirv.DeviceEvent`` (none)
254-
OpTypeReserveId ``spirv.ReserveId`` (none)
255-
OpTypeQueue ``spirv.Queue`` (none)
256-
OpTypePipe ``spirv.Pipe`` access qualifier
257-
OpTypePipeStorage ``spirv.PipeStorage`` (none)
258-
================== ====================== ===========================================================================================
246+
================== ======================= ===========================================================================================
247+
SPIR-V Type LLVM type name LLVM type arguments
248+
================== ======================= ===========================================================================================
249+
OpTypeImage ``spirv.Image`` sampled type, dimensionality, depth, arrayed, MS, sampled, image format, [access qualifier]
250+
OpTypeSampler ``spirv.Sampler`` (none)
251+
OpTypeSampledImage ``spirv.SampledImage`` sampled type, dimensionality, depth, arrayed, MS, sampled, image format, [access qualifier]
252+
OpTypeEvent ``spirv.Event`` (none)
253+
OpTypeDeviceEvent ``spirv.DeviceEvent`` (none)
254+
OpTypeReserveId ``spirv.ReserveId`` (none)
255+
OpTypeQueue ``spirv.Queue`` (none)
256+
OpTypePipe ``spirv.Pipe`` access qualifier
257+
OpTypePipeStorage ``spirv.PipeStorage`` (none)
258+
NA ``spirv.VulkanBuffer`` ElementType, StorageClass, IsWriteable
259+
================== ======================= ===========================================================================================
259260

260261
All integer arguments take the same value as they do in their `corresponding
261262
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
266267
previous type has the representation
267268
``target("spirv.SampledImage, void, 1, 1, 0, 0, 0, 0, 0)``.
268269

270+
See `wg-hlsl proposal 0018 <https://github.com/llvm/wg-hlsl/blob/main/proposals/0018-spirv-resource-representation.md>`_
271+
for details on ``spirv.VulkanBuffer``.
272+
269273
.. _inline-spirv-types:
270274

271275
Inline SPIR-V Types

llvm/lib/Target/SPIRV/MCTargetDesc/SPIRVInstPrinter.cpp

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -144,6 +144,9 @@ void SPIRVInstPrinter::printInst(const MCInst *MI, uint64_t Address,
144144
printRemainingVariableOps(MI, NumFixedOps, OS, false, true);
145145
break;
146146
}
147+
case SPIRV::OpMemberDecorate:
148+
printRemainingVariableOps(MI, NumFixedOps, OS);
149+
break;
147150
case SPIRV::OpExecutionMode:
148151
case SPIRV::OpExecutionModeId:
149152
case SPIRV::OpLoopMerge: {

llvm/lib/Target/SPIRV/SPIRVBuiltins.cpp

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3090,6 +3090,22 @@ static SPIRVType *getInlineSpirvType(const TargetExtType *ExtensionType,
30903090
Operands);
30913091
}
30923092

3093+
static SPIRVType *getVulkanBufferType(const TargetExtType *ExtensionType,
3094+
MachineIRBuilder &MIRBuilder,
3095+
SPIRVGlobalRegistry *GR) {
3096+
assert(ExtensionType->getNumTypeParameters() == 1 &&
3097+
"Vulkan buffers have exactly one type for the type of the buffer.");
3098+
assert(ExtensionType->getNumIntParameters() == 2 &&
3099+
"Vulkan buffer have 2 integer parameters: storage class and is "
3100+
"writable.");
3101+
3102+
auto *T = ExtensionType->getTypeParameter(0);
3103+
auto SC = static_cast<SPIRV::StorageClass::StorageClass>(
3104+
ExtensionType->getIntParameter(0));
3105+
bool IsWritable = ExtensionType->getIntParameter(1);
3106+
return GR->getOrCreateVulkanBufferType(MIRBuilder, T, SC, IsWritable);
3107+
}
3108+
30933109
namespace SPIRV {
30943110
TargetExtType *parseBuiltinTypeNameToTargetExtType(std::string TypeName,
30953111
LLVMContext &Context) {
@@ -3165,6 +3181,8 @@ SPIRVType *lowerBuiltinType(const Type *OpaqueType,
31653181
SPIRVType *TargetType;
31663182
if (Name == "spirv.Type") {
31673183
TargetType = getInlineSpirvType(BuiltinType, MIRBuilder, GR);
3184+
} else if (Name == "spirv.VulkanBuffer") {
3185+
TargetType = getVulkanBufferType(BuiltinType, MIRBuilder, GR);
31683186
} else {
31693187
// Lookup the demangled builtin type in the TableGen records.
31703188
const SPIRV::BuiltinType *TypeRecord = SPIRV::lookupBuiltinType(Name);

llvm/lib/Target/SPIRV/SPIRVEmitIntrinsics.cpp

Lines changed: 16 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -670,13 +670,22 @@ Type *SPIRVEmitIntrinsics::deduceElementTypeHelper(
670670

671671
auto *II = dyn_cast<IntrinsicInst>(I);
672672
if (II && II->getIntrinsicID() == Intrinsic::spv_resource_getpointer) {
673-
auto *ImageType = cast<TargetExtType>(II->getOperand(0)->getType());
674-
assert(ImageType->getTargetExtName() == "spirv.Image");
675-
(void)ImageType;
676-
if (II->hasOneUse()) {
677-
auto *U = *II->users().begin();
678-
Ty = cast<Instruction>(U)->getAccessType();
679-
assert(Ty && "Unable to get type for resource pointer.");
673+
auto *HandleType = cast<TargetExtType>(II->getOperand(0)->getType());
674+
if (HandleType->getTargetExtName() == "spirv.Image") {
675+
if (II->hasOneUse()) {
676+
auto *U = *II->users().begin();
677+
Ty = cast<Instruction>(U)->getAccessType();
678+
assert(Ty && "Unable to get type for resource pointer.");
679+
}
680+
} else if (HandleType->getTargetExtName() == "spirv.VulkanBuffer") {
681+
// This call is supposed to index into an array
682+
Ty = HandleType->getTypeParameter(0);
683+
assert(Ty->isArrayTy() &&
684+
"spv_resource_getpointer indexes into an array, so the type of "
685+
"the buffer should be an array.");
686+
Ty = Ty->getArrayElementType();
687+
} else {
688+
llvm_unreachable("Unknown handle type for spv_resource_getpointer.");
680689
}
681690
} else if (Function *CalledF = CI->getCalledFunction()) {
682691
std::string DemangledName =

llvm/lib/Target/SPIRV/SPIRVGlobalRegistry.cpp

Lines changed: 76 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -762,23 +762,25 @@ Register SPIRVGlobalRegistry::buildGlobalVariable(
762762

763763
static std::string GetSpirvImageTypeName(const SPIRVType *Type,
764764
MachineIRBuilder &MIRBuilder,
765-
const std::string &Prefix);
765+
const std::string &Prefix,
766+
SPIRVGlobalRegistry &GR);
766767

767768
static std::string buildSpirvTypeName(const SPIRVType *Type,
768-
MachineIRBuilder &MIRBuilder) {
769+
MachineIRBuilder &MIRBuilder,
770+
SPIRVGlobalRegistry &GR) {
769771
switch (Type->getOpcode()) {
770772
case SPIRV::OpTypeSampledImage: {
771-
return GetSpirvImageTypeName(Type, MIRBuilder, "sampled_image_");
773+
return GetSpirvImageTypeName(Type, MIRBuilder, "sampled_image_", GR);
772774
}
773775
case SPIRV::OpTypeImage: {
774-
return GetSpirvImageTypeName(Type, MIRBuilder, "image_");
776+
return GetSpirvImageTypeName(Type, MIRBuilder, "image_", GR);
775777
}
776778
case SPIRV::OpTypeArray: {
777779
MachineRegisterInfo *MRI = MIRBuilder.getMRI();
778780
Register ElementTypeReg = Type->getOperand(1).getReg();
779781
auto *ElementType = MRI->getUniqueVRegDef(ElementTypeReg);
780782
uint32_t ArraySize = getArrayComponentCount(MRI, Type);
781-
return (buildSpirvTypeName(ElementType, MIRBuilder) + Twine("[") +
783+
return (buildSpirvTypeName(ElementType, MIRBuilder, GR) + Twine("[") +
782784
Twine(ArraySize) + Twine("]"))
783785
.str();
784786
}
@@ -790,17 +792,35 @@ static std::string buildSpirvTypeName(const SPIRVType *Type,
790792
if (Type->getOperand(2).getImm())
791793
return ("i" + Twine(Type->getOperand(1).getImm())).str();
792794
return ("u" + Twine(Type->getOperand(1).getImm())).str();
795+
case SPIRV::OpTypePointer: {
796+
uint32_t StorageClass = GR.getPointerStorageClass(Type);
797+
SPIRVType *PointeeType = GR.getPointeeType(Type);
798+
return ("p_" + Twine(StorageClass) + Twine("_") +
799+
buildSpirvTypeName(PointeeType, MIRBuilder, GR))
800+
.str();
801+
}
802+
case SPIRV::OpTypeStruct: {
803+
std::string TypeName = "{";
804+
for (uint32_t I = 2; I < Type->getNumOperands(); ++I) {
805+
SPIRVType *MemberType =
806+
GR.getSPIRVTypeForVReg(Type->getOperand(I).getReg());
807+
TypeName = '_' + buildSpirvTypeName(MemberType, MIRBuilder, GR);
808+
}
809+
return TypeName + "}";
810+
}
793811
default:
794812
llvm_unreachable("Trying to the the name of an unknown type.");
795813
}
796814
}
797815

798816
static std::string GetSpirvImageTypeName(const SPIRVType *Type,
799817
MachineIRBuilder &MIRBuilder,
800-
const std::string &Prefix) {
818+
const std::string &Prefix,
819+
SPIRVGlobalRegistry &GR) {
801820
Register SampledTypeReg = Type->getOperand(1).getReg();
802821
auto *SampledType = MIRBuilder.getMRI()->getUniqueVRegDef(SampledTypeReg);
803-
std::string TypeName = Prefix + buildSpirvTypeName(SampledType, MIRBuilder);
822+
std::string TypeName =
823+
Prefix + buildSpirvTypeName(SampledType, MIRBuilder, GR);
804824
for (uint32_t I = 2; I < Type->getNumOperands(); ++I) {
805825
TypeName = (TypeName + '_' + Twine(Type->getOperand(I).getImm())).str();
806826
}
@@ -810,20 +830,19 @@ static std::string GetSpirvImageTypeName(const SPIRVType *Type,
810830
Register SPIRVGlobalRegistry::getOrCreateGlobalVariableWithBinding(
811831
const SPIRVType *VarType, uint32_t Set, uint32_t Binding,
812832
MachineIRBuilder &MIRBuilder) {
813-
SPIRVType *VarPointerTypeReg = getOrCreateSPIRVPointerType(
814-
VarType, MIRBuilder, SPIRV::StorageClass::UniformConstant);
815833
Register VarReg =
816834
MIRBuilder.getMRI()->createVirtualRegister(&SPIRV::iIDRegClass);
817835

818836
// TODO: The name should come from the llvm-ir, but how that name will be
819837
// passed from the HLSL to the backend has not been decided. Using this place
820838
// holder for now.
821-
std::string Name = ("__resource_" + buildSpirvTypeName(VarType, MIRBuilder) +
822-
"_" + Twine(Set) + "_" + Twine(Binding))
823-
.str();
824-
buildGlobalVariable(VarReg, VarPointerTypeReg, Name, nullptr,
825-
SPIRV::StorageClass::UniformConstant, nullptr, false,
826-
false, SPIRV::LinkageType::Import, MIRBuilder, false);
839+
std::string Name =
840+
("__resource_" + buildSpirvTypeName(VarType, MIRBuilder, *this) + "_" +
841+
Twine(Set) + "_" + Twine(Binding))
842+
.str();
843+
buildGlobalVariable(VarReg, VarType, Name, nullptr,
844+
getPointerStorageClass(VarType), nullptr, false, false,
845+
SPIRV::LinkageType::Import, MIRBuilder, false);
827846

828847
buildOpDecorate(VarReg, MIRBuilder, SPIRV::Decoration::DescriptorSet, {Set});
829848
buildOpDecorate(VarReg, MIRBuilder, SPIRV::Decoration::Binding, {Binding});
@@ -837,13 +856,22 @@ SPIRVType *SPIRVGlobalRegistry::getOpTypeArray(uint32_t NumElems,
837856
assert((ElemType->getOpcode() != SPIRV::OpTypeVoid) &&
838857
"Invalid array element type");
839858
SPIRVType *SpvTypeInt32 = getOrCreateSPIRVIntegerType(32, MIRBuilder);
840-
Register NumElementsVReg =
841-
buildConstantInt(NumElems, MIRBuilder, SpvTypeInt32, EmitIR);
859+
860+
if (NumElems != 0) {
861+
Register NumElementsVReg =
862+
buildConstantInt(NumElems, MIRBuilder, SpvTypeInt32, EmitIR);
863+
return createOpType(MIRBuilder, [&](MachineIRBuilder &MIRBuilder) {
864+
return MIRBuilder.buildInstr(SPIRV::OpTypeArray)
865+
.addDef(createTypeVReg(MIRBuilder))
866+
.addUse(getSPIRVTypeID(ElemType))
867+
.addUse(NumElementsVReg);
868+
});
869+
}
870+
842871
return createOpType(MIRBuilder, [&](MachineIRBuilder &MIRBuilder) {
843-
return MIRBuilder.buildInstr(SPIRV::OpTypeArray)
872+
return MIRBuilder.buildInstr(SPIRV::OpTypeRuntimeArray)
844873
.addDef(createTypeVReg(MIRBuilder))
845-
.addUse(getSPIRVTypeID(ElemType))
846-
.addUse(NumElementsVReg);
874+
.addUse(getSPIRVTypeID(ElemType));
847875
});
848876
}
849877

@@ -1291,6 +1319,34 @@ SPIRVGlobalRegistry::getPointerStorageClass(const SPIRVType *Type) const {
12911319
Type->getOperand(1).getImm());
12921320
}
12931321

1322+
SPIRVType *SPIRVGlobalRegistry::getOrCreateVulkanBufferType(
1323+
MachineIRBuilder &MIRBuilder, Type *ElemType,
1324+
SPIRV::StorageClass::StorageClass SC, bool IsWritable, bool EmitIr) {
1325+
auto Key = SPIRV::irhandle_vkbuffer(ElemType, SC, IsWritable);
1326+
if (const MachineInstr *MI = findMI(Key, &MIRBuilder.getMF()))
1327+
return MI;
1328+
1329+
// TODO(134119): The SPIRVType for `ElemType` will not have an explicit
1330+
// layout. This generates invalid SPIR-V.
1331+
auto *T = StructType::create(ElemType);
1332+
auto *BlockType =
1333+
getOrCreateSPIRVType(T, MIRBuilder, SPIRV::AccessQualifier::None, EmitIr);
1334+
1335+
buildOpDecorate(BlockType->defs().begin()->getReg(), MIRBuilder,
1336+
SPIRV::Decoration::Block, {});
1337+
buildOpMemberDecorate(BlockType->defs().begin()->getReg(), MIRBuilder,
1338+
SPIRV::Decoration::Offset, 0, {0});
1339+
1340+
if (!IsWritable) {
1341+
buildOpMemberDecorate(BlockType->defs().begin()->getReg(), MIRBuilder,
1342+
SPIRV::Decoration::NonWritable, 0, {});
1343+
}
1344+
1345+
SPIRVType *R = getOrCreateSPIRVPointerType(BlockType, MIRBuilder, SC);
1346+
add(Key, R);
1347+
return R;
1348+
}
1349+
12941350
SPIRVType *SPIRVGlobalRegistry::getOrCreateOpTypeImage(
12951351
MachineIRBuilder &MIRBuilder, SPIRVType *SampledType, SPIRV::Dim::Dim Dim,
12961352
uint32_t Depth, uint32_t Arrayed, uint32_t Multisampled, uint32_t Sampled,

llvm/lib/Target/SPIRV/SPIRVGlobalRegistry.h

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -547,6 +547,11 @@ class SPIRVGlobalRegistry : public SPIRVIRMapping {
547547
SPIRVType *BaseType, MachineInstr &I, const SPIRVInstrInfo &TII,
548548
SPIRV::StorageClass::StorageClass SClass = SPIRV::StorageClass::Function);
549549

550+
SPIRVType *getOrCreateVulkanBufferType(MachineIRBuilder &MIRBuilder,
551+
Type *ElemType,
552+
SPIRV::StorageClass::StorageClass SC,
553+
bool IsWritable, bool EmitIr = false);
554+
550555
SPIRVType *
551556
getOrCreateOpTypeImage(MachineIRBuilder &MIRBuilder, SPIRVType *SampledType,
552557
SPIRV::Dim::Dim Dim, uint32_t Depth, uint32_t Arrayed,

llvm/lib/Target/SPIRV/SPIRVIRMapping.h

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -65,6 +65,7 @@ enum SpecialTypeKind {
6565
STK_Type,
6666
STK_Value,
6767
STK_MachineInstr,
68+
STK_VkBuffer,
6869
STK_Last = -1
6970
};
7071

@@ -142,6 +143,13 @@ inline IRHandle irhandle_ptr(const void *Ptr, unsigned Arg,
142143
return std::make_tuple(Ptr, Arg, STK);
143144
}
144145

146+
inline IRHandle irhandle_vkbuffer(const Type *ElementType,
147+
StorageClass::StorageClass SC,
148+
bool IsWriteable) {
149+
return std::make_tuple(ElementType, (SC << 1) | IsWriteable,
150+
SpecialTypeKind::STK_VkBuffer);
151+
}
152+
145153
inline IRHandle handle(const Type *Ty) {
146154
const Type *WrpTy = unifyPtrType(Ty);
147155
return irhandle_ptr(WrpTy, Ty->getTypeID(), STK_Type);

0 commit comments

Comments
 (0)