Skip to content

Commit 52e496d

Browse files
committed
[SPIRV] Emit HLSL structured buffers
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.
1 parent f3e1a97 commit 52e496d

File tree

9 files changed

+359
-136
lines changed

9 files changed

+359
-136
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, IsROV
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
.. _spirv-intrinsics:
270274

271275
Target Intrinsics

llvm/lib/Target/SPIRV/SPIRVBuiltins.cpp

Lines changed: 66 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -3041,6 +3041,34 @@ static SPIRVType *getSampledImageType(const TargetExtType *OpaqueType,
30413041
return GR->getOrCreateOpTypeSampledImage(OpaqueImageType, MIRBuilder);
30423042
}
30433043

3044+
static SPIRVType *getVulkanBufferType(const TargetExtType *ExtensionType,
3045+
MachineIRBuilder &MIRBuilder,
3046+
SPIRVGlobalRegistry *GR) {
3047+
assert(ExtensionType->getNumTypeParameters() == 1 &&
3048+
"Vulkan buffers have exactly one type for the type of the buffer.");
3049+
assert(ExtensionType->getNumIntParameters() == 3 &&
3050+
"Vulkan buffer have 3 integer parameters: storage class, is writable, "
3051+
"and is rov");
3052+
3053+
auto *T = StructType::create(ExtensionType->getTypeParameter(0));
3054+
auto *BlockType = GR->getOrCreateSPIRVType(
3055+
T, MIRBuilder, SPIRV::AccessQualifier::None, false);
3056+
buildOpDecorate(BlockType->defs().begin()->getReg(), MIRBuilder,
3057+
SPIRV::Decoration::Block, {});
3058+
buildOpMemberDecorate(BlockType->defs().begin()->getReg(), MIRBuilder,
3059+
SPIRV::Decoration::Offset, 0, {0});
3060+
3061+
bool IsWritable = ExtensionType->getIntParameter(1);
3062+
if (!IsWritable) {
3063+
buildOpMemberDecorate(BlockType->defs().begin()->getReg(), MIRBuilder,
3064+
SPIRV::Decoration::NonWritable, 0, {});
3065+
}
3066+
3067+
auto SC = static_cast<SPIRV::StorageClass::StorageClass>(
3068+
ExtensionType->getIntParameter(0));
3069+
return GR->getOrCreateSPIRVPointerType(BlockType, MIRBuilder, SC);
3070+
}
3071+
30443072
namespace SPIRV {
30453073
TargetExtType *parseBuiltinTypeNameToTargetExtType(std::string TypeName,
30463074
LLVMContext &Context) {
@@ -3113,39 +3141,45 @@ SPIRVType *lowerBuiltinType(const Type *OpaqueType,
31133141
const StringRef Name = BuiltinType->getName();
31143142
LLVM_DEBUG(dbgs() << "Lowering builtin type: " << Name << "\n");
31153143

3116-
// Lookup the demangled builtin type in the TableGen records.
3117-
const SPIRV::BuiltinType *TypeRecord = SPIRV::lookupBuiltinType(Name);
3118-
if (!TypeRecord)
3119-
report_fatal_error("Missing TableGen record for builtin type: " + Name);
3120-
3121-
// "Lower" the BuiltinType into TargetType. The following get<...>Type methods
3122-
// use the implementation details from TableGen records or TargetExtType
3123-
// parameters to either create a new OpType<...> machine instruction or get an
3124-
// existing equivalent SPIRVType from GlobalRegistry.
31253144
SPIRVType *TargetType;
3126-
switch (TypeRecord->Opcode) {
3127-
case SPIRV::OpTypeImage:
3128-
TargetType = getImageType(BuiltinType, AccessQual, MIRBuilder, GR);
3129-
break;
3130-
case SPIRV::OpTypePipe:
3131-
TargetType = getPipeType(BuiltinType, MIRBuilder, GR);
3132-
break;
3133-
case SPIRV::OpTypeDeviceEvent:
3134-
TargetType = GR->getOrCreateOpTypeDeviceEvent(MIRBuilder);
3135-
break;
3136-
case SPIRV::OpTypeSampler:
3137-
TargetType = getSamplerType(MIRBuilder, GR);
3138-
break;
3139-
case SPIRV::OpTypeSampledImage:
3140-
TargetType = getSampledImageType(BuiltinType, MIRBuilder, GR);
3141-
break;
3142-
case SPIRV::OpTypeCooperativeMatrixKHR:
3143-
TargetType = getCoopMatrType(BuiltinType, MIRBuilder, GR);
3144-
break;
3145-
default:
3146-
TargetType =
3147-
getNonParameterizedType(BuiltinType, TypeRecord, MIRBuilder, GR);
3148-
break;
3145+
if (Name == "spirv.VulkanBuffer") {
3146+
TargetType = getVulkanBufferType(BuiltinType, MIRBuilder, GR);
3147+
} else {
3148+
// Lookup the demangled builtin type in the TableGen records.
3149+
const SPIRV::BuiltinType *TypeRecord = SPIRV::lookupBuiltinType(Name);
3150+
if (!TypeRecord)
3151+
report_fatal_error("Missing TableGen record for builtin type: " + Name);
3152+
3153+
// "Lower" the BuiltinType into TargetType. The following get<...>Type
3154+
// methods use the implementation details from TableGen records or
3155+
// TargetExtType parameters to either create a new OpType<...> machine
3156+
// instruction or get an existing equivalent SPIRVType from
3157+
// GlobalRegistry.
3158+
3159+
switch (TypeRecord->Opcode) {
3160+
case SPIRV::OpTypeImage:
3161+
TargetType = getImageType(BuiltinType, AccessQual, MIRBuilder, GR);
3162+
break;
3163+
case SPIRV::OpTypePipe:
3164+
TargetType = getPipeType(BuiltinType, MIRBuilder, GR);
3165+
break;
3166+
case SPIRV::OpTypeDeviceEvent:
3167+
TargetType = GR->getOrCreateOpTypeDeviceEvent(MIRBuilder);
3168+
break;
3169+
case SPIRV::OpTypeSampler:
3170+
TargetType = getSamplerType(MIRBuilder, GR);
3171+
break;
3172+
case SPIRV::OpTypeSampledImage:
3173+
TargetType = getSampledImageType(BuiltinType, MIRBuilder, GR);
3174+
break;
3175+
case SPIRV::OpTypeCooperativeMatrixKHR:
3176+
TargetType = getCoopMatrType(BuiltinType, MIRBuilder, GR);
3177+
break;
3178+
default:
3179+
TargetType =
3180+
getNonParameterizedType(BuiltinType, TypeRecord, MIRBuilder, GR);
3181+
break;
3182+
}
31493183
}
31503184

31513185
// Emit OpName instruction if a new OpType<...> instruction was added

llvm/lib/Target/SPIRV/SPIRVEmitIntrinsics.cpp

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

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

llvm/lib/Target/SPIRV/SPIRVGlobalRegistry.cpp

Lines changed: 51 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -799,16 +799,18 @@ Register SPIRVGlobalRegistry::buildGlobalVariable(
799799

800800
static std::string GetSpirvImageTypeName(const SPIRVType *Type,
801801
MachineIRBuilder &MIRBuilder,
802-
const std::string &Prefix);
802+
const std::string &Prefix,
803+
SPIRVGlobalRegistry &GR);
803804

804805
static std::string buildSpirvTypeName(const SPIRVType *Type,
805-
MachineIRBuilder &MIRBuilder) {
806+
MachineIRBuilder &MIRBuilder,
807+
SPIRVGlobalRegistry &GR) {
806808
switch (Type->getOpcode()) {
807809
case SPIRV::OpTypeSampledImage: {
808-
return GetSpirvImageTypeName(Type, MIRBuilder, "sampled_image_");
810+
return GetSpirvImageTypeName(Type, MIRBuilder, "sampled_image_", GR);
809811
}
810812
case SPIRV::OpTypeImage: {
811-
return GetSpirvImageTypeName(Type, MIRBuilder, "image_");
813+
return GetSpirvImageTypeName(Type, MIRBuilder, "image_", GR);
812814
}
813815
case SPIRV::OpTypeArray: {
814816
MachineRegisterInfo *MRI = MIRBuilder.getMRI();
@@ -819,7 +821,7 @@ static std::string buildSpirvTypeName(const SPIRVType *Type,
819821
MachineInstr *ImmInst = MRI->getVRegDef(TypeInst->getOperand(1).getReg());
820822
assert(ImmInst->getOpcode() == TargetOpcode::G_CONSTANT);
821823
uint32_t ArraySize = ImmInst->getOperand(1).getCImm()->getZExtValue();
822-
return (buildSpirvTypeName(ElementType, MIRBuilder) + Twine("[") +
824+
return (buildSpirvTypeName(ElementType, MIRBuilder, GR) + Twine("[") +
823825
Twine(ArraySize) + Twine("]"))
824826
.str();
825827
}
@@ -831,17 +833,35 @@ static std::string buildSpirvTypeName(const SPIRVType *Type,
831833
if (Type->getOperand(2).getImm())
832834
return ("i" + Twine(Type->getOperand(1).getImm())).str();
833835
return ("u" + Twine(Type->getOperand(1).getImm())).str();
836+
case SPIRV::OpTypePointer: {
837+
uint32_t StorageClass = GR.getPointerStorageClass(Type);
838+
SPIRVType *PointeeType = GR.getPointeeType(Type);
839+
return ("p_" + Twine(StorageClass) + Twine("_") +
840+
buildSpirvTypeName(PointeeType, MIRBuilder, GR))
841+
.str();
842+
}
843+
case SPIRV::OpTypeStruct: {
844+
std::string TypeName = "{";
845+
for (uint32_t I = 2; I < Type->getNumOperands(); ++I) {
846+
SPIRVType *MemberType =
847+
GR.getSPIRVTypeForVReg(Type->getOperand(I).getReg());
848+
TypeName = '_' + buildSpirvTypeName(MemberType, MIRBuilder, GR);
849+
}
850+
return TypeName + "}";
851+
}
834852
default:
835853
llvm_unreachable("Trying to the the name of an unknown type.");
836854
}
837855
}
838856

839857
static std::string GetSpirvImageTypeName(const SPIRVType *Type,
840858
MachineIRBuilder &MIRBuilder,
841-
const std::string &Prefix) {
859+
const std::string &Prefix,
860+
SPIRVGlobalRegistry &GR) {
842861
Register SampledTypeReg = Type->getOperand(1).getReg();
843862
auto *SampledType = MIRBuilder.getMRI()->getUniqueVRegDef(SampledTypeReg);
844-
std::string TypeName = Prefix + buildSpirvTypeName(SampledType, MIRBuilder);
863+
std::string TypeName =
864+
Prefix + buildSpirvTypeName(SampledType, MIRBuilder, GR);
845865
for (uint32_t I = 2; I < Type->getNumOperands(); ++I) {
846866
TypeName = (TypeName + '_' + Twine(Type->getOperand(I).getImm())).str();
847867
}
@@ -851,20 +871,19 @@ static std::string GetSpirvImageTypeName(const SPIRVType *Type,
851871
Register SPIRVGlobalRegistry::getOrCreateGlobalVariableWithBinding(
852872
const SPIRVType *VarType, uint32_t Set, uint32_t Binding,
853873
MachineIRBuilder &MIRBuilder) {
854-
SPIRVType *VarPointerTypeReg = getOrCreateSPIRVPointerType(
855-
VarType, MIRBuilder, SPIRV::StorageClass::UniformConstant);
856874
Register VarReg =
857875
MIRBuilder.getMRI()->createVirtualRegister(&SPIRV::iIDRegClass);
858876

859877
// TODO: The name should come from the llvm-ir, but how that name will be
860878
// passed from the HLSL to the backend has not been decided. Using this place
861879
// holder for now.
862-
std::string Name = ("__resource_" + buildSpirvTypeName(VarType, MIRBuilder) +
863-
"_" + Twine(Set) + "_" + Twine(Binding))
864-
.str();
865-
buildGlobalVariable(VarReg, VarPointerTypeReg, Name, nullptr,
866-
SPIRV::StorageClass::UniformConstant, nullptr, false,
867-
false, SPIRV::LinkageType::Import, MIRBuilder, false);
880+
std::string Name =
881+
("__resource_" + buildSpirvTypeName(VarType, MIRBuilder, *this) + "_" +
882+
Twine(Set) + "_" + Twine(Binding))
883+
.str();
884+
buildGlobalVariable(VarReg, VarType, Name, nullptr,
885+
getPointerStorageClass(VarType), nullptr, false, false,
886+
SPIRV::LinkageType::Import, MIRBuilder, false);
868887

869888
buildOpDecorate(VarReg, MIRBuilder, SPIRV::Decoration::DescriptorSet, {Set});
870889
buildOpDecorate(VarReg, MIRBuilder, SPIRV::Decoration::Binding, {Binding});
@@ -878,14 +897,23 @@ SPIRVType *SPIRVGlobalRegistry::getOpTypeArray(uint32_t NumElems,
878897
assert((ElemType->getOpcode() != SPIRV::OpTypeVoid) &&
879898
"Invalid array element type");
880899
SPIRVType *SpvTypeInt32 = getOrCreateSPIRVIntegerType(32, MIRBuilder);
881-
Register NumElementsVReg =
882-
buildConstantInt(NumElems, MIRBuilder, SpvTypeInt32, EmitIR);
883-
return createOpType(MIRBuilder, [&](MachineIRBuilder &MIRBuilder) {
884-
return MIRBuilder.buildInstr(SPIRV::OpTypeArray)
885-
.addDef(createTypeVReg(MIRBuilder))
886-
.addUse(getSPIRVTypeID(ElemType))
887-
.addUse(NumElementsVReg);
888-
});
900+
901+
if (NumElems != 0) {
902+
Register NumElementsVReg =
903+
buildConstantInt(NumElems, MIRBuilder, SpvTypeInt32, EmitIR);
904+
return createOpType(MIRBuilder, [&](MachineIRBuilder &MIRBuilder) {
905+
return MIRBuilder.buildInstr(SPIRV::OpTypeArray)
906+
.addDef(createTypeVReg(MIRBuilder))
907+
.addUse(getSPIRVTypeID(ElemType))
908+
.addUse(NumElementsVReg);
909+
});
910+
} else {
911+
return createOpType(MIRBuilder, [&](MachineIRBuilder &MIRBuilder) {
912+
return MIRBuilder.buildInstr(SPIRV::OpTypeRuntimeArray)
913+
.addDef(createTypeVReg(MIRBuilder))
914+
.addUse(getSPIRVTypeID(ElemType));
915+
});
916+
}
889917
}
890918

891919
SPIRVType *SPIRVGlobalRegistry::getOpTypeOpaque(const StructType *Ty,

0 commit comments

Comments
 (0)