From 17f0af327609d1507a71fc0174bc4270ba2b3022 Mon Sep 17 00:00:00 2001 From: Steven Perron Date: Fri, 24 Jan 2025 15:27:46 -0500 Subject: [PATCH 1/2] [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. --- llvm/docs/SPIRVUsage.rst | 30 ++-- .../SPIRV/MCTargetDesc/SPIRVInstPrinter.cpp | 3 + llvm/lib/Target/SPIRV/SPIRVBuiltins.cpp | 18 +++ llvm/lib/Target/SPIRV/SPIRVEmitIntrinsics.cpp | 23 ++- llvm/lib/Target/SPIRV/SPIRVGlobalRegistry.cpp | 96 +++++++++--- llvm/lib/Target/SPIRV/SPIRVGlobalRegistry.h | 5 + llvm/lib/Target/SPIRV/SPIRVIRMapping.h | 8 + .../Target/SPIRV/SPIRVInstructionSelector.cpp | 145 +++++++++++------- llvm/lib/Target/SPIRV/SPIRVLegalizerInfo.cpp | 16 +- llvm/lib/Target/SPIRV/SPIRVModuleAnalysis.cpp | 37 ++++- llvm/lib/Target/SPIRV/SPIRVUtils.cpp | 26 ++++ llvm/lib/Target/SPIRV/SPIRVUtils.h | 13 ++ .../SPIRV/hlsl-resources/StructuredBuffer.ll | 76 +++++++++ 13 files changed, 394 insertions(+), 102 deletions(-) create mode 100644 llvm/test/CodeGen/SPIRV/hlsl-resources/StructuredBuffer.ll diff --git a/llvm/docs/SPIRVUsage.rst b/llvm/docs/SPIRVUsage.rst index d3d9fbb3ba294..f58587314e6b5 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 + ================== ======================= =========================================================================================== All integer arguments take the same value as they do in their `corresponding SPIR-V instruction `_. @@ -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 `_ +for details on ``spirv.VulkanBuffer``. + .. _inline-spirv-types: Inline SPIR-V Types diff --git a/llvm/lib/Target/SPIRV/MCTargetDesc/SPIRVInstPrinter.cpp b/llvm/lib/Target/SPIRV/MCTargetDesc/SPIRVInstPrinter.cpp index cd65985a4229c..e559aa2483f26 100644 --- a/llvm/lib/Target/SPIRV/MCTargetDesc/SPIRVInstPrinter.cpp +++ b/llvm/lib/Target/SPIRV/MCTargetDesc/SPIRVInstPrinter.cpp @@ -144,6 +144,9 @@ void SPIRVInstPrinter::printInst(const MCInst *MI, uint64_t Address, printRemainingVariableOps(MI, NumFixedOps, OS, false, true); break; } + case SPIRV::OpMemberDecorate: + printRemainingVariableOps(MI, NumFixedOps, OS); + break; case SPIRV::OpExecutionMode: case SPIRV::OpExecutionModeId: case SPIRV::OpLoopMerge: { diff --git a/llvm/lib/Target/SPIRV/SPIRVBuiltins.cpp b/llvm/lib/Target/SPIRV/SPIRVBuiltins.cpp index 1ed92400fc577..984f1680f6aaa 100644 --- a/llvm/lib/Target/SPIRV/SPIRVBuiltins.cpp +++ b/llvm/lib/Target/SPIRV/SPIRVBuiltins.cpp @@ -3090,6 +3090,22 @@ static SPIRVType *getInlineSpirvType(const TargetExtType *ExtensionType, Operands); } +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() == 2 && + "Vulkan buffer have 2 integer parameters: storage class and is " + "writable."); + + auto *T = ExtensionType->getTypeParameter(0); + auto SC = static_cast( + ExtensionType->getIntParameter(0)); + bool IsWritable = ExtensionType->getIntParameter(1); + return GR->getOrCreateVulkanBufferType(MIRBuilder, T, SC, IsWritable); +} + namespace SPIRV { TargetExtType *parseBuiltinTypeNameToTargetExtType(std::string TypeName, LLVMContext &Context) { @@ -3165,6 +3181,8 @@ SPIRVType *lowerBuiltinType(const Type *OpaqueType, SPIRVType *TargetType; if (Name == "spirv.Type") { TargetType = getInlineSpirvType(BuiltinType, MIRBuilder, GR); + } else 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); diff --git a/llvm/lib/Target/SPIRV/SPIRVEmitIntrinsics.cpp b/llvm/lib/Target/SPIRV/SPIRVEmitIntrinsics.cpp index 939d9e920d05b..49470b243dcc1 100644 --- a/llvm/lib/Target/SPIRV/SPIRVEmitIntrinsics.cpp +++ b/llvm/lib/Target/SPIRV/SPIRVEmitIntrinsics.cpp @@ -671,13 +671,22 @@ Type *SPIRVEmitIntrinsics::deduceElementTypeHelper( auto *II = dyn_cast(I); if (II && II->getIntrinsicID() == Intrinsic::spv_resource_getpointer) { - auto *ImageType = cast(II->getOperand(0)->getType()); - assert(ImageType->getTargetExtName() == "spirv.Image"); - (void)ImageType; - if (II->hasOneUse()) { - auto *U = *II->users().begin(); - Ty = cast(U)->getAccessType(); - assert(Ty && "Unable to get type for resource pointer."); + auto *HandleType = cast(II->getOperand(0)->getType()); + if (HandleType->getTargetExtName() == "spirv.Image") { + if (II->hasOneUse()) { + auto *U = *II->users().begin(); + Ty = cast(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 439828d9759a5..70d648f116d44 100644 --- a/llvm/lib/Target/SPIRV/SPIRVGlobalRegistry.cpp +++ b/llvm/lib/Target/SPIRV/SPIRVGlobalRegistry.cpp @@ -767,23 +767,25 @@ 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(); Register ElementTypeReg = Type->getOperand(1).getReg(); auto *ElementType = MRI->getUniqueVRegDef(ElementTypeReg); uint32_t ArraySize = getArrayComponentCount(MRI, Type); - return (buildSpirvTypeName(ElementType, MIRBuilder) + Twine("[") + + return (buildSpirvTypeName(ElementType, MIRBuilder, GR) + Twine("[") + Twine(ArraySize) + Twine("]")) .str(); } @@ -795,6 +797,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."); } @@ -802,10 +820,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(); } @@ -815,20 +835,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}); @@ -842,13 +861,22 @@ 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); + + 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); + }); + } + return createOpType(MIRBuilder, [&](MachineIRBuilder &MIRBuilder) { - return MIRBuilder.buildInstr(SPIRV::OpTypeArray) + return MIRBuilder.buildInstr(SPIRV::OpTypeRuntimeArray) .addDef(createTypeVReg(MIRBuilder)) - .addUse(getSPIRVTypeID(ElemType)) - .addUse(NumElementsVReg); + .addUse(getSPIRVTypeID(ElemType)); }); } @@ -1296,6 +1324,34 @@ SPIRVGlobalRegistry::getPointerStorageClass(const SPIRVType *Type) const { Type->getOperand(1).getImm()); } +SPIRVType *SPIRVGlobalRegistry::getOrCreateVulkanBufferType( + MachineIRBuilder &MIRBuilder, Type *ElemType, + SPIRV::StorageClass::StorageClass SC, bool IsWritable, bool EmitIr) { + auto Key = SPIRV::irhandle_vkbuffer(ElemType, SC, IsWritable); + if (const MachineInstr *MI = findMI(Key, &MIRBuilder.getMF())) + return MI; + + // TODO: The SPIRVType for `ElemType` will not have an explicit layout. + // This generates invalid SPIR-V. + auto *T = StructType::create(ElemType); + auto *BlockType = + getOrCreateSPIRVType(T, MIRBuilder, SPIRV::AccessQualifier::None, EmitIr); + + buildOpDecorate(BlockType->defs().begin()->getReg(), MIRBuilder, + SPIRV::Decoration::Block, {}); + buildOpMemberDecorate(BlockType->defs().begin()->getReg(), MIRBuilder, + SPIRV::Decoration::Offset, 0, {0}); + + if (!IsWritable) { + buildOpMemberDecorate(BlockType->defs().begin()->getReg(), MIRBuilder, + SPIRV::Decoration::NonWritable, 0, {}); + } + + SPIRVType *R = getOrCreateSPIRVPointerType(BlockType, MIRBuilder, SC); + add(Key, R); + return R; +} + SPIRVType *SPIRVGlobalRegistry::getOrCreateOpTypeImage( MachineIRBuilder &MIRBuilder, SPIRVType *SampledType, SPIRV::Dim::Dim Dim, uint32_t Depth, uint32_t Arrayed, uint32_t Multisampled, uint32_t Sampled, diff --git a/llvm/lib/Target/SPIRV/SPIRVGlobalRegistry.h b/llvm/lib/Target/SPIRV/SPIRVGlobalRegistry.h index 09f567a9d1866..c18f17d1f3d23 100644 --- a/llvm/lib/Target/SPIRV/SPIRVGlobalRegistry.h +++ b/llvm/lib/Target/SPIRV/SPIRVGlobalRegistry.h @@ -547,6 +547,11 @@ class SPIRVGlobalRegistry : public SPIRVIRMapping { SPIRVType *BaseType, MachineInstr &I, const SPIRVInstrInfo &TII, SPIRV::StorageClass::StorageClass SClass = SPIRV::StorageClass::Function); + SPIRVType *getOrCreateVulkanBufferType(MachineIRBuilder &MIRBuilder, + Type *ElemType, + SPIRV::StorageClass::StorageClass SC, + bool IsWritable, bool EmitIr = false); + SPIRVType * getOrCreateOpTypeImage(MachineIRBuilder &MIRBuilder, SPIRVType *SampledType, SPIRV::Dim::Dim Dim, uint32_t Depth, uint32_t Arrayed, diff --git a/llvm/lib/Target/SPIRV/SPIRVIRMapping.h b/llvm/lib/Target/SPIRV/SPIRVIRMapping.h index 66d5d9ae9dad3..5e8e1c55d91c6 100644 --- a/llvm/lib/Target/SPIRV/SPIRVIRMapping.h +++ b/llvm/lib/Target/SPIRV/SPIRVIRMapping.h @@ -65,6 +65,7 @@ enum SpecialTypeKind { STK_Type, STK_Value, STK_MachineInstr, + STK_VkBuffer, STK_Last = -1 }; @@ -142,6 +143,13 @@ inline IRHandle irhandle_ptr(const void *Ptr, unsigned Arg, return std::make_tuple(Ptr, Arg, STK); } +inline IRHandle irhandle_vkbuffer(const Type *ElementType, + StorageClass::StorageClass SC, + bool IsWriteable) { + return std::make_tuple(ElementType, (SC << 1) | IsWriteable, + SpecialTypeKind::STK_VkBuffer); +} + inline IRHandle handle(const Type *Ty) { const Type *WrpTy = unifyPtrType(Ty); return irhandle_ptr(WrpTy, Ty->getTypeID(), STK_Type); diff --git a/llvm/lib/Target/SPIRV/SPIRVInstructionSelector.cpp b/llvm/lib/Target/SPIRV/SPIRVInstructionSelector.cpp index fb37f91af254f..e0936e3af553f 100644 --- a/llvm/lib/Target/SPIRV/SPIRVInstructionSelector.cpp +++ b/llvm/lib/Target/SPIRV/SPIRVInstructionSelector.cpp @@ -322,9 +322,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, @@ -1144,18 +1146,20 @@ bool SPIRVInstructionSelector::selectLoad(Register ResVReg, auto *IntPtrDef = dyn_cast(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(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(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)) @@ -1183,22 +1187,24 @@ bool SPIRVInstructionSelector::selectStore(MachineInstr &I) const { auto *IntPtrDef = dyn_cast(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(getVRegDef(*MRI, ImageReg)); - if (!loadHandleBeforePosition(NewImageReg, GR.getSPIRVTypeForVReg(ImageReg), - *ImageDef, I)) { + Register HandleReg = IntPtrDef->getOperand(2).getReg(); + Register NewHandleReg = + MRI->createVirtualRegister(MRI->getRegClass(HandleReg)); + auto *HandleDef = cast(getVRegDef(*MRI, HandleReg)); + SPIRVType *HandleType = GR.getSPIRVTypeForVReg(HandleReg); + if (!loadHandleBeforePosition(NewHandleReg, HandleType, *HandleDef, I)) { return false; } Register IdxReg = IntPtrDef->getOperand(3).getReg(); - return BuildMI(*I.getParent(), I, I.getDebugLoc(), - TII.get(SPIRV::OpImageWrite)) - .addUse(NewImageReg) - .addUse(IdxReg) - .addUse(StoreVal) - .constrainAllUses(TII, TRI, RBI); + if (HandleType->getOpcode() == SPIRV::OpTypeImage) { + return BuildMI(*I.getParent(), I, I.getDebugLoc(), + TII.get(SPIRV::OpImageWrite)) + .addUse(NewHandleReg) + .addUse(IdxReg) + .addUse(StoreVal) + .constrainAllUses(TII, TRI, RBI); + } } MachineBasicBlock &BB = *I.getParent(); @@ -3188,7 +3194,13 @@ bool SPIRVInstructionSelector::selectIntrinsic(Register ResVReg, bool SPIRVInstructionSelector::selectHandleFromBinding(Register &ResVReg, const SPIRVType *ResType, MachineInstr &I) const { - return true; + // The images need to be loaded in the same basic block as their use. We defer + // loading the image to the intrinsic that uses it. + if (ResType->getOpcode() == SPIRV::OpTypeImage) + return true; + + return loadHandleBeforePosition(ResVReg, GR.getSPIRVTypeForVReg(ResVReg), + *cast(&I), I); } bool SPIRVInstructionSelector::selectReadImageIntrinsic( @@ -3256,20 +3268,30 @@ bool SPIRVInstructionSelector::generateImageRead(Register &ResVReg, bool SPIRVInstructionSelector::selectResourceGetPointer( Register &ResVReg, const SPIRVType *ResType, MachineInstr &I) const { -#ifdef ASSERT - // For now, the operand is an image. This will change once we start handling - // more resource types. Register ResourcePtr = I.getOperand(2).getReg(); - SPIRVType *RegType = GR.getResultType(ResourcePtr); - assert(RegType->getOpcode() == SPIRV::OpTypeImage && - "Can only handle texel buffers for now."); -#endif - - // For texel buffers, the index into the image is part of the OpImageRead or - // OpImageWrite instructions. So we will do nothing in this case. This - // intrinsic will be combined with the load or store when selecting the load - // or store. - return true; + SPIRVType *RegType = GR.getSPIRVTypeForVReg(ResourcePtr, I.getMF()); + if (RegType->getOpcode() == SPIRV::OpTypeImage) { + // For texel buffers, the index into the image is part of the OpImageRead or + // OpImageWrite instructions. So we will do nothing in this case. This + // intrinsic will be combined with the load or store when selecting the load + // or store. + return true; + } + + assert(ResType->getOpcode() == SPIRV::OpTypePointer); + MachineIRBuilder MIRBuilder(I); + + Register IndexReg = I.getOperand(3).getReg(); + Register ZeroReg = + buildZerosVal(GR.getOrCreateSPIRVIntegerType(32, I, TII), I); + return BuildMI(*I.getParent(), I, I.getDebugLoc(), + TII.get(SPIRV::OpAccessChain)) + .addDef(ResVReg) + .addUse(GR.getSPIRVTypeID(ResType)) + .addUse(ResourcePtr) + .addUse(ZeroReg) + .addUse(IndexReg) + .constrainAllUses(TII, TRI, RBI); } bool SPIRVInstructionSelector::extractSubvector( @@ -3341,22 +3363,27 @@ bool SPIRVInstructionSelector::selectImageWriteIntrinsic( } Register SPIRVInstructionSelector::buildPointerToResource( - const SPIRVType *ResType, uint32_t Set, uint32_t Binding, - uint32_t ArraySize, Register IndexReg, bool IsNonUniform, - MachineIRBuilder MIRBuilder) const { - if (ArraySize == 1) - return GR.getOrCreateGlobalVariableWithBinding(ResType, Set, Binding, + const SPIRVType *ResType, SPIRV::StorageClass::StorageClass SC, + uint32_t Set, uint32_t Binding, uint32_t ArraySize, Register IndexReg, + bool IsNonUniform, MachineIRBuilder MIRBuilder) const { + if (ArraySize == 1) { + SPIRVType *PtrType = + GR.getOrCreateSPIRVPointerType(ResType, MIRBuilder, SC); + return GR.getOrCreateGlobalVariableWithBinding(PtrType, Set, Binding, MIRBuilder); + } const SPIRVType *VarType = GR.getOrCreateSPIRVArrayType( ResType, ArraySize, *MIRBuilder.getInsertPt(), TII); + SPIRVType *VarPointerType = + GR.getOrCreateSPIRVPointerType(VarType, MIRBuilder, SC); Register VarReg = GR.getOrCreateGlobalVariableWithBinding( - VarType, Set, Binding, MIRBuilder); + VarPointerType, Set, Binding, MIRBuilder); - SPIRVType *ResPointerType = GR.getOrCreateSPIRVPointerType( - ResType, MIRBuilder, SPIRV::StorageClass::UniformConstant); + SPIRVType *ResPointerType = + GR.getOrCreateSPIRVPointerType(ResType, MIRBuilder, SC); - Register AcReg = MRI->createVirtualRegister(&SPIRV::iIDRegClass); + Register AcReg = MRI->createVirtualRegister(GR.getRegClass(ResPointerType)); if (IsNonUniform) { // It is unclear which value needs to be marked an non-uniform, so both // the index and the access changed are decorated as non-uniform. @@ -4049,19 +4076,29 @@ bool SPIRVInstructionSelector::loadHandleBeforePosition( uint32_t ArraySize = foldImm(HandleDef.getOperand(4), MRI); Register IndexReg = HandleDef.getOperand(5).getReg(); bool IsNonUniform = ArraySize > 1 && foldImm(HandleDef.getOperand(6), MRI); - + bool IsStructuredBuffer = ResType->getOpcode() == SPIRV::OpTypePointer; MachineIRBuilder MIRBuilder(HandleDef); - Register VarReg = buildPointerToResource(ResType, Set, Binding, ArraySize, + SPIRVType *VarType = ResType; + SPIRV::StorageClass::StorageClass SC = SPIRV::StorageClass::UniformConstant; + + if (IsStructuredBuffer) { + VarType = GR.getPointeeType(ResType); + SC = GR.getPointerStorageClass(ResType); + } + + Register VarReg = buildPointerToResource(VarType, SC, Set, Binding, ArraySize, IndexReg, IsNonUniform, MIRBuilder); if (IsNonUniform) buildOpDecorate(HandleReg, HandleDef, TII, SPIRV::Decoration::NonUniformEXT, {}); - // TODO: For now we assume the resource is an image, which needs to be - // loaded to get the handle. That will not be true for storage buffers. + // The handle for the buffer is the pointer to the resource. For an image, the + // handle is the image object. So images get an extra load. + uint32_t LoadOpcode = + IsStructuredBuffer ? SPIRV::OpCopyObject : SPIRV::OpLoad; return BuildMI(*Pos.getParent(), Pos, HandleDef.getDebugLoc(), - TII.get(SPIRV::OpLoad)) + TII.get(LoadOpcode)) .addDef(HandleReg) .addUse(GR.getSPIRVTypeID(ResType)) .addUse(VarReg) diff --git a/llvm/lib/Target/SPIRV/SPIRVLegalizerInfo.cpp b/llvm/lib/Target/SPIRV/SPIRVLegalizerInfo.cpp index 008aecf4cda85..578e82881f6e8 100644 --- a/llvm/lib/Target/SPIRV/SPIRVLegalizerInfo.cpp +++ b/llvm/lib/Target/SPIRV/SPIRVLegalizerInfo.cpp @@ -85,13 +85,15 @@ SPIRVLegalizerInfo::SPIRVLegalizerInfo(const SPIRVSubtarget &ST) { const LLT p7 = LLT::pointer(7, PSize); // Input const LLT p8 = LLT::pointer(8, PSize); // Output const LLT p10 = LLT::pointer(10, PSize); // Private + const LLT p11 = LLT::pointer(11, PSize); // StorageBuffer // TODO: remove copy-pasting here by using concatenation in some way. auto allPtrsScalarsAndVectors = { - p0, p1, p2, p3, p4, p5, p6, p7, p8, p10, - s1, s8, s16, s32, s64, v2s1, v2s8, v2s16, v2s32, v2s64, - v3s1, v3s8, v3s16, v3s32, v3s64, v4s1, v4s8, v4s16, v4s32, v4s64, - v8s1, v8s8, v8s16, v8s32, v8s64, v16s1, v16s8, v16s16, v16s32, v16s64}; + p0, p1, p2, p3, p4, p5, p6, p7, p8, + p10, p11, s1, s8, s16, s32, s64, v2s1, v2s8, + v2s16, v2s32, v2s64, v3s1, v3s8, v3s16, v3s32, v3s64, v4s1, + v4s8, v4s16, v4s32, v4s64, v8s1, v8s8, v8s16, v8s32, v8s64, + v16s1, v16s8, v16s16, v16s32, v16s64}; auto allVectors = {v2s1, v2s8, v2s16, v2s32, v2s64, v3s1, v3s8, v3s16, v3s32, v3s64, v4s1, v4s8, v4s16, v4s32, @@ -118,10 +120,10 @@ SPIRVLegalizerInfo::SPIRVLegalizerInfo(const SPIRVSubtarget &ST) { s16, s32, s64, v2s16, v2s32, v2s64, v3s16, v3s32, v3s64, v4s16, v4s32, v4s64, v8s16, v8s32, v8s64, v16s16, v16s32, v16s64}; - auto allFloatAndIntScalarsAndPtrs = {s8, s16, s32, s64, p0, p1, p2, - p3, p4, p5, p6, p7, p8, p10}; + auto allFloatAndIntScalarsAndPtrs = {s8, s16, s32, s64, p0, p1, p2, p3, + p4, p5, p6, p7, p8, p10, p11}; - auto allPtrs = {p0, p1, p2, p3, p4, p5, p6, p7, p8, p10}; + auto allPtrs = {p0, p1, p2, p3, p4, p5, p6, p7, p8, p10, p11}; bool IsExtendedInts = ST.canUseExtension( diff --git a/llvm/lib/Target/SPIRV/SPIRVModuleAnalysis.cpp b/llvm/lib/Target/SPIRV/SPIRVModuleAnalysis.cpp index acc8c014cb26b..9f833e37c406a 100644 --- a/llvm/lib/Target/SPIRV/SPIRVModuleAnalysis.cpp +++ b/llvm/lib/Target/SPIRV/SPIRVModuleAnalysis.cpp @@ -214,6 +214,30 @@ void SPIRVModuleAnalysis::setBaseInfo(const Module &M) { } } +static InstrSignature instrToSignature(const MachineInstr &MI, + SPIRV::ModuleAnalysisInfo &MAI, + bool UseDefReg); + +// Appends the signature of the decoration instructions that decorate R to +// Signature. +static void appendDecorationsForReg(const MachineRegisterInfo &MRI, Register R, + InstrSignature &Signature) { + for (MachineInstr &UseMI : MRI.use_instructions(R)) { + // We don't handle OpDecorateId because getting the register alias for the + // ID can cause problems, and we do not need it for now. + if (UseMI.getOpcode() != SPIRV::OpDecorate && + UseMI.getOpcode() != SPIRV::OpMemberDecorate) + continue; + + for (unsigned I = 0; I < UseMI.getNumOperands(); ++I) { + const MachineOperand &MO = UseMI.getOperand(I); + if (MO.isReg()) + continue; + Signature.push_back(hash_value(MO)); + } + } +} + // Returns a representation of an instruction as a vector of MachineOperand // hash values, see llvm::hash_value(const MachineOperand &MO) for details. // This creates a signature of the instruction with the same content @@ -221,13 +245,17 @@ void SPIRVModuleAnalysis::setBaseInfo(const Module &M) { static InstrSignature instrToSignature(const MachineInstr &MI, SPIRV::ModuleAnalysisInfo &MAI, bool UseDefReg) { + Register DefReg; InstrSignature Signature{MI.getOpcode()}; for (unsigned i = 0; i < MI.getNumOperands(); ++i) { const MachineOperand &MO = MI.getOperand(i); size_t h; if (MO.isReg()) { - if (!UseDefReg && MO.isDef()) + if (!UseDefReg && MO.isDef()) { + assert(!DefReg.isValid() && "Multiple def registers."); + DefReg = MO.getReg(); continue; + } Register RegAlias = MAI.getRegisterAlias(MI.getMF(), MO.getReg()); if (!RegAlias.isValid()) { LLVM_DEBUG({ @@ -247,6 +275,13 @@ static InstrSignature instrToSignature(const MachineInstr &MI, } Signature.push_back(h); } + + if (DefReg.isValid()) { + // Decorations change the semantics of the current instruction. So two + // identical instruction with different decorations cannot be merged. That + // is why we add the decorations to the signature. + appendDecorationsForReg(MI.getMF()->getRegInfo(), DefReg, Signature); + } return Signature; } diff --git a/llvm/lib/Target/SPIRV/SPIRVUtils.cpp b/llvm/lib/Target/SPIRV/SPIRVUtils.cpp index 6bef6b7e9b16e..b013328f4ef2b 100644 --- a/llvm/lib/Target/SPIRV/SPIRVUtils.cpp +++ b/llvm/lib/Target/SPIRV/SPIRVUtils.cpp @@ -146,6 +146,30 @@ void buildOpDecorate(Register Reg, MachineInstr &I, const SPIRVInstrInfo &TII, finishBuildOpDecorate(MIB, DecArgs, StrImm); } +void buildOpMemberDecorate(Register Reg, MachineIRBuilder &MIRBuilder, + SPIRV::Decoration::Decoration Dec, uint32_t Member, + const std::vector &DecArgs, + StringRef StrImm) { + auto MIB = MIRBuilder.buildInstr(SPIRV::OpMemberDecorate) + .addUse(Reg) + .addImm(Member) + .addImm(static_cast(Dec)); + finishBuildOpDecorate(MIB, DecArgs, StrImm); +} + +void buildOpMemberDecorate(Register Reg, MachineInstr &I, + const SPIRVInstrInfo &TII, + SPIRV::Decoration::Decoration Dec, uint32_t Member, + const std::vector &DecArgs, + StringRef StrImm) { + MachineBasicBlock &MBB = *I.getParent(); + auto MIB = BuildMI(MBB, I, I.getDebugLoc(), TII.get(SPIRV::OpMemberDecorate)) + .addUse(Reg) + .addImm(Member) + .addImm(static_cast(Dec)); + finishBuildOpDecorate(MIB, DecArgs, StrImm); +} + void buildOpSpirvDecorations(Register Reg, MachineIRBuilder &MIRBuilder, const MDNode *GVarMD) { for (unsigned I = 0, E = GVarMD->getNumOperands(); I != E; ++I) { @@ -236,6 +260,8 @@ addressSpaceToStorageClass(unsigned AddrSpace, const SPIRVSubtarget &STI) { return SPIRV::StorageClass::CodeSectionINTEL; case 10: return SPIRV::StorageClass::Private; + case 11: + return SPIRV::StorageClass::StorageBuffer; default: report_fatal_error("Unknown address space"); } diff --git a/llvm/lib/Target/SPIRV/SPIRVUtils.h b/llvm/lib/Target/SPIRV/SPIRVUtils.h index b094184f34fb0..0498c7beb073c 100644 --- a/llvm/lib/Target/SPIRV/SPIRVUtils.h +++ b/llvm/lib/Target/SPIRV/SPIRVUtils.h @@ -144,6 +144,17 @@ void buildOpDecorate(Register Reg, MachineInstr &I, const SPIRVInstrInfo &TII, const std::vector &DecArgs, StringRef StrImm = ""); +// Add an OpDecorate instruction for the given Reg. +void buildOpMemberDecorate(Register Reg, MachineIRBuilder &MIRBuilder, + SPIRV::Decoration::Decoration Dec, uint32_t Member, + const std::vector &DecArgs, + StringRef StrImm = ""); +void buildOpMemberDecorate(Register Reg, MachineInstr &I, + const SPIRVInstrInfo &TII, + SPIRV::Decoration::Decoration Dec, uint32_t Member, + const std::vector &DecArgs, + StringRef StrImm = ""); + // Add an OpDecorate instruction by "spirv.Decorations" metadata node. void buildOpSpirvDecorations(Register Reg, MachineIRBuilder &MIRBuilder, const MDNode *GVarMD); @@ -184,6 +195,8 @@ storageClassToAddressSpace(SPIRV::StorageClass::StorageClass SC) { return 9; case SPIRV::StorageClass::Private: return 10; + case SPIRV::StorageClass::StorageBuffer: + return 11; default: report_fatal_error("Unable to get address space id"); } diff --git a/llvm/test/CodeGen/SPIRV/hlsl-resources/StructuredBuffer.ll b/llvm/test/CodeGen/SPIRV/hlsl-resources/StructuredBuffer.ll new file mode 100644 index 0000000000000..9a365a43facca --- /dev/null +++ b/llvm/test/CodeGen/SPIRV/hlsl-resources/StructuredBuffer.ll @@ -0,0 +1,76 @@ +; RUN: llc -O0 -verify-machineinstrs -mtriple=spirv1.6-vulkan1.3-library %s -o - | FileCheck %s +; RUN: %if spirv-tools %{ llc -O0 -mtriple=spirv1.6-vulkan1.3-library %s -o - -filetype=obj | spirv-val %} + +target datalayout = "e-i64:64-v16:16-v24:32-v32:32-v48:64-v96:128-v192:256-v256:256-v512:512-v1024:1024-n8:16:32:64-G1" + +; Function Attrs: mustprogress nocallback nofree nosync nounwind willreturn memory(none) +declare target("spirv.VulkanBuffer", [0 x i32], 12, 0) @llvm.spv.resource.handlefrombinding.tspirv.VulkanBuffer_a0i32_12_0t(i32, i32, i32, i32, i1) #0 + +; Function Attrs: mustprogress nocallback nofree nosync nounwind willreturn memory(none) +declare target("spirv.VulkanBuffer", [0 x i32], 12, 1) @llvm.spv.resource.handlefrombinding.tspirv.VulkanBuffer_a0i32_12_1t(i32, i32, i32, i32, i1) #0 + +; CHECK: OpDecorate [[BufferVar:%.+]] DescriptorSet 0 +; CHECK: OpDecorate [[BufferVar]] Binding 0 +; CHECK: OpDecorate [[BufferType:%.+]] Block +; CHECK: OpMemberDecorate [[BufferType]] 0 Offset 0 +; CHECK: OpMemberDecorate [[BufferType]] 0 NonWritable +; CHECK: OpDecorate [[RWBufferVar:%.+]] DescriptorSet 0 +; CHECK: OpDecorate [[RWBufferVar]] Binding 1 +; CHECK: OpDecorate [[RWBufferType:%.+]] Block +; CHECK: OpMemberDecorate [[RWBufferType]] 0 Offset 0 + + +; CHECK: [[int:%[0-9]+]] = OpTypeInt 32 0 +; CHECK: [[ArrayType:%.+]] = OpTypeRuntimeArray +; CHECK: [[RWBufferType]] = OpTypeStruct [[ArrayType]] +; CHECK: [[RWBufferPtrType:%.+]] = OpTypePointer StorageBuffer [[RWBufferType]] +; CHECK: [[BufferType]] = OpTypeStruct [[ArrayType]] +; CHECK: [[BufferPtrType:%.+]] = OpTypePointer StorageBuffer [[BufferType]] +; CHECK-DAG: [[zero:%[0-9]+]] = OpConstant [[int]] 0 +; CHECK-DAG: [[one:%[0-9]+]] = OpConstant [[int]] 1 +; CHECK-DAG: [[BufferVar]] = OpVariable [[BufferPtrType]] StorageBuffer +; CHECK-DAG: [[RWBufferVar]] = OpVariable [[RWBufferPtrType]] StorageBuffer + +; Function Attrs: mustprogress nofree noinline norecurse nosync nounwind willreturn memory(readwrite, inaccessiblemem: none) +define void @main() local_unnamed_addr #1 { +entry: + +; CHECK-DAG: [[BufferHandle:%.+]] = OpCopyObject [[BufferPtrType]] [[BufferVar]] +; CHECK-DAG: [[RWBufferHandle:%.+]] = OpCopyObject [[RWBufferPtrType]] [[RWBufferVar]] + %_ZL1i_h.i.i = tail call target("spirv.VulkanBuffer", [0 x i32], 12, 0) @llvm.spv.resource.handlefrombinding.tspirv.VulkanBuffer_a0i32_12_0t(i32 0, i32 0, i32 1, i32 0, i1 false) + + %_ZL1o_h.i.i = tail call target("spirv.VulkanBuffer", [0 x i32], 12, 1) @llvm.spv.resource.handlefrombinding.tspirv.VulkanBuffer_a0i32_12_1t(i32 0, i32 1, i32 1, i32 0, i1 false) + +; CHECK: [[AC:%.+]] = OpAccessChain {{.*}} [[BufferHandle]] [[zero]] [[one]] + %0 = tail call noundef nonnull align 4 dereferenceable(4) ptr addrspace(11) @llvm.spv.resource.getpointer.p11.tspirv.VulkanBuffer_a0i32_12_0t(target("spirv.VulkanBuffer", [0 x i32], 12, 0) %_ZL1i_h.i.i, i32 1) + +; CHECK: [[LD:%.+]] = OpLoad [[int]] [[AC]] Aligned 4 + %1 = load i32, ptr addrspace(11) %0, align 4, !tbaa !3 + +; CHECK: [[AC:%.+]] = OpAccessChain {{.*}} [[RWBufferHandle]] [[zero]] [[zero]] + %2 = tail call noundef nonnull align 4 dereferenceable(4) ptr addrspace(11) @llvm.spv.resource.getpointer.p11.tspirv.VulkanBuffer_a0i32_12_1t(target("spirv.VulkanBuffer", [0 x i32], 12, 1) %_ZL1o_h.i.i, i32 0) + +; CHECK: OpStore [[AC]] [[LD]] + store i32 %1, ptr addrspace(11) %2, align 4, !tbaa !3 + ret void +} + +; Function Attrs: mustprogress nocallback nofree nosync nounwind willreturn memory(none) +declare ptr addrspace(11) @llvm.spv.resource.getpointer.p11.tspirv.VulkanBuffer_a0i32_12_1t(target("spirv.VulkanBuffer", [0 x i32], 12, 1), i32) #0 + +; Function Attrs: mustprogress nocallback nofree nosync nounwind willreturn memory(none) +declare ptr addrspace(11) @llvm.spv.resource.getpointer.p11.tspirv.VulkanBuffer_a0i32_12_0t(target("spirv.VulkanBuffer", [0 x i32], 12, 0), i32) #0 + +attributes #0 = { mustprogress nocallback nofree nosync nounwind willreturn memory(none) } +attributes #1 = { mustprogress nofree noinline norecurse nosync nounwind willreturn memory(readwrite, inaccessiblemem: none) "approx-func-fp-math"="false" "frame-pointer"="all" "hlsl.numthreads"="1,1,1" "hlsl.shader"="compute" "no-infs-fp-math"="false" "no-nans-fp-math"="false" "no-signed-zeros-fp-math"="false" "no-trapping-math"="true" "stack-protector-buffer-size"="8" } + +!llvm.module.flags = !{!0, !1} +!llvm.ident = !{!2} + +!0 = !{i32 1, !"wchar_size", i32 4} +!1 = !{i32 7, !"frame-pointer", i32 2} +!2 = !{!"clang version 21.0.0git (git@github.com:s-perron/llvm-project.git 6e86add06c03e328dbb4b83f99406cc832a22f86)"} +!3 = !{!4, !4, i64 0} +!4 = !{!"int", !5, i64 0} +!5 = !{!"omnipotent char", !6, i64 0} +!6 = !{!"Simple C++ TBAA"} From cf94fd013027381274e6e3c6a7b5108ef58fd74f Mon Sep 17 00:00:00 2001 From: Steven Perron Date: Wed, 2 Apr 2025 13:26:58 -0400 Subject: [PATCH 2/2] Fixes from code review. --- llvm/lib/Target/SPIRV/SPIRVGlobalRegistry.cpp | 4 ++-- llvm/lib/Target/SPIRV/SPIRVModuleAnalysis.cpp | 4 ---- .../SPIRV/hlsl-resources/StructuredBuffer.ll | 24 +++++++++++++++---- 3 files changed, 21 insertions(+), 11 deletions(-) diff --git a/llvm/lib/Target/SPIRV/SPIRVGlobalRegistry.cpp b/llvm/lib/Target/SPIRV/SPIRVGlobalRegistry.cpp index 70d648f116d44..63e206651f270 100644 --- a/llvm/lib/Target/SPIRV/SPIRVGlobalRegistry.cpp +++ b/llvm/lib/Target/SPIRV/SPIRVGlobalRegistry.cpp @@ -1331,8 +1331,8 @@ SPIRVType *SPIRVGlobalRegistry::getOrCreateVulkanBufferType( if (const MachineInstr *MI = findMI(Key, &MIRBuilder.getMF())) return MI; - // TODO: The SPIRVType for `ElemType` will not have an explicit layout. - // This generates invalid SPIR-V. + // TODO(134119): The SPIRVType for `ElemType` will not have an explicit + // layout. This generates invalid SPIR-V. auto *T = StructType::create(ElemType); auto *BlockType = getOrCreateSPIRVType(T, MIRBuilder, SPIRV::AccessQualifier::None, EmitIr); diff --git a/llvm/lib/Target/SPIRV/SPIRVModuleAnalysis.cpp b/llvm/lib/Target/SPIRV/SPIRVModuleAnalysis.cpp index 9f833e37c406a..f9e64f118a277 100644 --- a/llvm/lib/Target/SPIRV/SPIRVModuleAnalysis.cpp +++ b/llvm/lib/Target/SPIRV/SPIRVModuleAnalysis.cpp @@ -214,10 +214,6 @@ void SPIRVModuleAnalysis::setBaseInfo(const Module &M) { } } -static InstrSignature instrToSignature(const MachineInstr &MI, - SPIRV::ModuleAnalysisInfo &MAI, - bool UseDefReg); - // Appends the signature of the decoration instructions that decorate R to // Signature. static void appendDecorationsForReg(const MachineRegisterInfo &MRI, Register R, diff --git a/llvm/test/CodeGen/SPIRV/hlsl-resources/StructuredBuffer.ll b/llvm/test/CodeGen/SPIRV/hlsl-resources/StructuredBuffer.ll index 9a365a43facca..fc8faa7300534 100644 --- a/llvm/test/CodeGen/SPIRV/hlsl-resources/StructuredBuffer.ll +++ b/llvm/test/CodeGen/SPIRV/hlsl-resources/StructuredBuffer.ll @@ -28,6 +28,7 @@ declare target("spirv.VulkanBuffer", [0 x i32], 12, 1) @llvm.spv.resource.handle ; CHECK: [[BufferPtrType:%.+]] = OpTypePointer StorageBuffer [[BufferType]] ; CHECK-DAG: [[zero:%[0-9]+]] = OpConstant [[int]] 0 ; CHECK-DAG: [[one:%[0-9]+]] = OpConstant [[int]] 1 +; CHECK-DAG: [[two:%[0-9]+]] = OpConstant [[int]] 2 ; CHECK-DAG: [[BufferVar]] = OpVariable [[BufferPtrType]] StorageBuffer ; CHECK-DAG: [[RWBufferVar]] = OpVariable [[RWBufferPtrType]] StorageBuffer @@ -36,22 +37,35 @@ define void @main() local_unnamed_addr #1 { entry: ; CHECK-DAG: [[BufferHandle:%.+]] = OpCopyObject [[BufferPtrType]] [[BufferVar]] +; CHECK-DAG: [[BufferHandle2:%.+]] = OpCopyObject [[BufferPtrType]] [[BufferVar]] ; CHECK-DAG: [[RWBufferHandle:%.+]] = OpCopyObject [[RWBufferPtrType]] [[RWBufferVar]] - %_ZL1i_h.i.i = tail call target("spirv.VulkanBuffer", [0 x i32], 12, 0) @llvm.spv.resource.handlefrombinding.tspirv.VulkanBuffer_a0i32_12_0t(i32 0, i32 0, i32 1, i32 0, i1 false) - - %_ZL1o_h.i.i = tail call target("spirv.VulkanBuffer", [0 x i32], 12, 1) @llvm.spv.resource.handlefrombinding.tspirv.VulkanBuffer_a0i32_12_1t(i32 0, i32 1, i32 1, i32 0, i1 false) + %BufferHandle = tail call target("spirv.VulkanBuffer", [0 x i32], 12, 0) @llvm.spv.resource.handlefrombinding.tspirv.VulkanBuffer_a0i32_12_0t(i32 0, i32 0, i32 1, i32 0, i1 false) + %BufferHandle2 = tail call target("spirv.VulkanBuffer", [0 x i32], 12, 0) @llvm.spv.resource.handlefrombinding.tspirv.VulkanBuffer_a0i32_12_0t(i32 0, i32 0, i32 1, i32 0, i1 false) + %RWBufferHandle = tail call target("spirv.VulkanBuffer", [0 x i32], 12, 1) @llvm.spv.resource.handlefrombinding.tspirv.VulkanBuffer_a0i32_12_1t(i32 0, i32 1, i32 1, i32 0, i1 false) ; CHECK: [[AC:%.+]] = OpAccessChain {{.*}} [[BufferHandle]] [[zero]] [[one]] - %0 = tail call noundef nonnull align 4 dereferenceable(4) ptr addrspace(11) @llvm.spv.resource.getpointer.p11.tspirv.VulkanBuffer_a0i32_12_0t(target("spirv.VulkanBuffer", [0 x i32], 12, 0) %_ZL1i_h.i.i, i32 1) + %0 = tail call noundef nonnull align 4 dereferenceable(4) ptr addrspace(11) @llvm.spv.resource.getpointer.p11.tspirv.VulkanBuffer_a0i32_12_0t(target("spirv.VulkanBuffer", [0 x i32], 12, 0) %BufferHandle, i32 1) ; CHECK: [[LD:%.+]] = OpLoad [[int]] [[AC]] Aligned 4 %1 = load i32, ptr addrspace(11) %0, align 4, !tbaa !3 ; CHECK: [[AC:%.+]] = OpAccessChain {{.*}} [[RWBufferHandle]] [[zero]] [[zero]] - %2 = tail call noundef nonnull align 4 dereferenceable(4) ptr addrspace(11) @llvm.spv.resource.getpointer.p11.tspirv.VulkanBuffer_a0i32_12_1t(target("spirv.VulkanBuffer", [0 x i32], 12, 1) %_ZL1o_h.i.i, i32 0) + %2 = tail call noundef nonnull align 4 dereferenceable(4) ptr addrspace(11) @llvm.spv.resource.getpointer.p11.tspirv.VulkanBuffer_a0i32_12_1t(target("spirv.VulkanBuffer", [0 x i32], 12, 1) %RWBufferHandle, i32 0) ; CHECK: OpStore [[AC]] [[LD]] store i32 %1, ptr addrspace(11) %2, align 4, !tbaa !3 + +; CHECK: [[AC:%.+]] = OpAccessChain {{.*}} [[BufferHandle2]] [[zero]] [[two]] + %3 = tail call noundef nonnull align 4 dereferenceable(4) ptr addrspace(11) @llvm.spv.resource.getpointer.p11.tspirv.VulkanBuffer_a0i32_12_0t(target("spirv.VulkanBuffer", [0 x i32], 12, 0) %BufferHandle2, i32 2) + +; CHECK: [[LD:%.+]] = OpLoad [[int]] [[AC]] Aligned 4 + %4 = load i32, ptr addrspace(11) %3, align 4, !tbaa !3 + +; CHECK: [[AC:%.+]] = OpAccessChain {{.*}} [[RWBufferHandle]] [[zero]] [[zero]] + %5 = tail call noundef nonnull align 4 dereferenceable(4) ptr addrspace(11) @llvm.spv.resource.getpointer.p11.tspirv.VulkanBuffer_a0i32_12_1t(target("spirv.VulkanBuffer", [0 x i32], 12, 1) %RWBufferHandle, i32 0) + +; CHECK: OpStore [[AC]] [[LD]] + store i32 %4, ptr addrspace(11) %5, align 4, !tbaa !3 ret void }