Skip to content

Commit 17f0af3

Browse files
committed
[SPIRV] Add spirv.VulkanBuffer types to the backend
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 827f2ad commit 17f0af3

13 files changed

+394
-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
@@ -671,13 +671,22 @@ Type *SPIRVEmitIntrinsics::deduceElementTypeHelper(
671671

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

llvm/lib/Target/SPIRV/SPIRVGlobalRegistry.cpp

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

768768
static std::string GetSpirvImageTypeName(const SPIRVType *Type,
769769
MachineIRBuilder &MIRBuilder,
770-
const std::string &Prefix);
770+
const std::string &Prefix,
771+
SPIRVGlobalRegistry &GR);
771772

772773
static std::string buildSpirvTypeName(const SPIRVType *Type,
773-
MachineIRBuilder &MIRBuilder) {
774+
MachineIRBuilder &MIRBuilder,
775+
SPIRVGlobalRegistry &GR) {
774776
switch (Type->getOpcode()) {
775777
case SPIRV::OpTypeSampledImage: {
776-
return GetSpirvImageTypeName(Type, MIRBuilder, "sampled_image_");
778+
return GetSpirvImageTypeName(Type, MIRBuilder, "sampled_image_", GR);
777779
}
778780
case SPIRV::OpTypeImage: {
779-
return GetSpirvImageTypeName(Type, MIRBuilder, "image_");
781+
return GetSpirvImageTypeName(Type, MIRBuilder, "image_", GR);
780782
}
781783
case SPIRV::OpTypeArray: {
782784
MachineRegisterInfo *MRI = MIRBuilder.getMRI();
783785
Register ElementTypeReg = Type->getOperand(1).getReg();
784786
auto *ElementType = MRI->getUniqueVRegDef(ElementTypeReg);
785787
uint32_t ArraySize = getArrayComponentCount(MRI, Type);
786-
return (buildSpirvTypeName(ElementType, MIRBuilder) + Twine("[") +
788+
return (buildSpirvTypeName(ElementType, MIRBuilder, GR) + Twine("[") +
787789
Twine(ArraySize) + Twine("]"))
788790
.str();
789791
}
@@ -795,17 +797,35 @@ static std::string buildSpirvTypeName(const SPIRVType *Type,
795797
if (Type->getOperand(2).getImm())
796798
return ("i" + Twine(Type->getOperand(1).getImm())).str();
797799
return ("u" + Twine(Type->getOperand(1).getImm())).str();
800+
case SPIRV::OpTypePointer: {
801+
uint32_t StorageClass = GR.getPointerStorageClass(Type);
802+
SPIRVType *PointeeType = GR.getPointeeType(Type);
803+
return ("p_" + Twine(StorageClass) + Twine("_") +
804+
buildSpirvTypeName(PointeeType, MIRBuilder, GR))
805+
.str();
806+
}
807+
case SPIRV::OpTypeStruct: {
808+
std::string TypeName = "{";
809+
for (uint32_t I = 2; I < Type->getNumOperands(); ++I) {
810+
SPIRVType *MemberType =
811+
GR.getSPIRVTypeForVReg(Type->getOperand(I).getReg());
812+
TypeName = '_' + buildSpirvTypeName(MemberType, MIRBuilder, GR);
813+
}
814+
return TypeName + "}";
815+
}
798816
default:
799817
llvm_unreachable("Trying to the the name of an unknown type.");
800818
}
801819
}
802820

803821
static std::string GetSpirvImageTypeName(const SPIRVType *Type,
804822
MachineIRBuilder &MIRBuilder,
805-
const std::string &Prefix) {
823+
const std::string &Prefix,
824+
SPIRVGlobalRegistry &GR) {
806825
Register SampledTypeReg = Type->getOperand(1).getReg();
807826
auto *SampledType = MIRBuilder.getMRI()->getUniqueVRegDef(SampledTypeReg);
808-
std::string TypeName = Prefix + buildSpirvTypeName(SampledType, MIRBuilder);
827+
std::string TypeName =
828+
Prefix + buildSpirvTypeName(SampledType, MIRBuilder, GR);
809829
for (uint32_t I = 2; I < Type->getNumOperands(); ++I) {
810830
TypeName = (TypeName + '_' + Twine(Type->getOperand(I).getImm())).str();
811831
}
@@ -815,20 +835,19 @@ static std::string GetSpirvImageTypeName(const SPIRVType *Type,
815835
Register SPIRVGlobalRegistry::getOrCreateGlobalVariableWithBinding(
816836
const SPIRVType *VarType, uint32_t Set, uint32_t Binding,
817837
MachineIRBuilder &MIRBuilder) {
818-
SPIRVType *VarPointerTypeReg = getOrCreateSPIRVPointerType(
819-
VarType, MIRBuilder, SPIRV::StorageClass::UniformConstant);
820838
Register VarReg =
821839
MIRBuilder.getMRI()->createVirtualRegister(&SPIRV::iIDRegClass);
822840

823841
// TODO: The name should come from the llvm-ir, but how that name will be
824842
// passed from the HLSL to the backend has not been decided. Using this place
825843
// holder for now.
826-
std::string Name = ("__resource_" + buildSpirvTypeName(VarType, MIRBuilder) +
827-
"_" + Twine(Set) + "_" + Twine(Binding))
828-
.str();
829-
buildGlobalVariable(VarReg, VarPointerTypeReg, Name, nullptr,
830-
SPIRV::StorageClass::UniformConstant, nullptr, false,
831-
false, SPIRV::LinkageType::Import, MIRBuilder, false);
844+
std::string Name =
845+
("__resource_" + buildSpirvTypeName(VarType, MIRBuilder, *this) + "_" +
846+
Twine(Set) + "_" + Twine(Binding))
847+
.str();
848+
buildGlobalVariable(VarReg, VarType, Name, nullptr,
849+
getPointerStorageClass(VarType), nullptr, false, false,
850+
SPIRV::LinkageType::Import, MIRBuilder, false);
832851

833852
buildOpDecorate(VarReg, MIRBuilder, SPIRV::Decoration::DescriptorSet, {Set});
834853
buildOpDecorate(VarReg, MIRBuilder, SPIRV::Decoration::Binding, {Binding});
@@ -842,13 +861,22 @@ SPIRVType *SPIRVGlobalRegistry::getOpTypeArray(uint32_t NumElems,
842861
assert((ElemType->getOpcode() != SPIRV::OpTypeVoid) &&
843862
"Invalid array element type");
844863
SPIRVType *SpvTypeInt32 = getOrCreateSPIRVIntegerType(32, MIRBuilder);
845-
Register NumElementsVReg =
846-
buildConstantInt(NumElems, MIRBuilder, SpvTypeInt32, EmitIR);
864+
865+
if (NumElems != 0) {
866+
Register NumElementsVReg =
867+
buildConstantInt(NumElems, MIRBuilder, SpvTypeInt32, EmitIR);
868+
return createOpType(MIRBuilder, [&](MachineIRBuilder &MIRBuilder) {
869+
return MIRBuilder.buildInstr(SPIRV::OpTypeArray)
870+
.addDef(createTypeVReg(MIRBuilder))
871+
.addUse(getSPIRVTypeID(ElemType))
872+
.addUse(NumElementsVReg);
873+
});
874+
}
875+
847876
return createOpType(MIRBuilder, [&](MachineIRBuilder &MIRBuilder) {
848-
return MIRBuilder.buildInstr(SPIRV::OpTypeArray)
877+
return MIRBuilder.buildInstr(SPIRV::OpTypeRuntimeArray)
849878
.addDef(createTypeVReg(MIRBuilder))
850-
.addUse(getSPIRVTypeID(ElemType))
851-
.addUse(NumElementsVReg);
879+
.addUse(getSPIRVTypeID(ElemType));
852880
});
853881
}
854882

@@ -1296,6 +1324,34 @@ SPIRVGlobalRegistry::getPointerStorageClass(const SPIRVType *Type) const {
12961324
Type->getOperand(1).getImm());
12971325
}
12981326

1327+
SPIRVType *SPIRVGlobalRegistry::getOrCreateVulkanBufferType(
1328+
MachineIRBuilder &MIRBuilder, Type *ElemType,
1329+
SPIRV::StorageClass::StorageClass SC, bool IsWritable, bool EmitIr) {
1330+
auto Key = SPIRV::irhandle_vkbuffer(ElemType, SC, IsWritable);
1331+
if (const MachineInstr *MI = findMI(Key, &MIRBuilder.getMF()))
1332+
return MI;
1333+
1334+
// TODO: The SPIRVType for `ElemType` will not have an explicit layout.
1335+
// This generates invalid SPIR-V.
1336+
auto *T = StructType::create(ElemType);
1337+
auto *BlockType =
1338+
getOrCreateSPIRVType(T, MIRBuilder, SPIRV::AccessQualifier::None, EmitIr);
1339+
1340+
buildOpDecorate(BlockType->defs().begin()->getReg(), MIRBuilder,
1341+
SPIRV::Decoration::Block, {});
1342+
buildOpMemberDecorate(BlockType->defs().begin()->getReg(), MIRBuilder,
1343+
SPIRV::Decoration::Offset, 0, {0});
1344+
1345+
if (!IsWritable) {
1346+
buildOpMemberDecorate(BlockType->defs().begin()->getReg(), MIRBuilder,
1347+
SPIRV::Decoration::NonWritable, 0, {});
1348+
}
1349+
1350+
SPIRVType *R = getOrCreateSPIRVPointerType(BlockType, MIRBuilder, SC);
1351+
add(Key, R);
1352+
return R;
1353+
}
1354+
12991355
SPIRVType *SPIRVGlobalRegistry::getOrCreateOpTypeImage(
13001356
MachineIRBuilder &MIRBuilder, SPIRVType *SampledType, SPIRV::Dim::Dim Dim,
13011357
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)