Skip to content

Commit ba572ab

Browse files
authored
[SPIRV] Add reads from image buffer for shaders. (#115178)
This commit adds an intrinsic that will read from an image buffer. We chose to match the name of the DXIL intrinsic for simplicity in clang. We cannot reuse the existing openCL readimage function because that is not a reserved name in HLSL. I considered trying to refactor generateReadImageInst, so that we could share code between the two implementations. However, most of the code in generateReadImageInst is concerned with trying to figure out which type of image read is being done. Once we factor out the code that will be common, then we end up with just a single call to the MIRBuilder being common.
1 parent aaa37d6 commit ba572ab

File tree

11 files changed

+269
-1
lines changed

11 files changed

+269
-1
lines changed

llvm/docs/SPIRVUsage.rst

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -392,6 +392,14 @@ SPIR-V backend, along with their descriptions and argument details.
392392
If `arraySize > 1`, then the binding represents an array of resources\
393393
of the given size, and the handle for the resource at the given index is returned.\
394394
If the index is possibly non-uniform, then `isUniformIndex` must get set to true.
395+
* - `int_spv_typeBufferLoad`
396+
- Scalar or vector
397+
- `[spirv.Image ImageBuffer, 32-bit Integer coordinate]`
398+
- Loads a value from a Vulkan image buffer at the given coordinate. The \
399+
image buffer data is assumed to be stored as a 4-element vector. If the \
400+
return type is a scalar, then the first element of the vector is \
401+
returned. If the return type is an n-element vector, then the first \
402+
n-elements of the 4-element vector are returned.
395403

396404
.. _spirv-builtin-functions:
397405

llvm/include/llvm/IR/IntrinsicsSPIRV.td

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -106,4 +106,10 @@ let TargetPrefix = "spv" in {
106106
[IntrNoMem]>;
107107
def int_spv_firstbituhigh : DefaultAttrsIntrinsic<[LLVMScalarOrSameVectorWidth<0, llvm_i32_ty>], [llvm_anyint_ty], [IntrNoMem]>;
108108
def int_spv_firstbitshigh : DefaultAttrsIntrinsic<[LLVMScalarOrSameVectorWidth<0, llvm_i32_ty>], [llvm_anyint_ty], [IntrNoMem]>;
109+
110+
// Read a value from the image buffer. It does not translate directly to a
111+
// single OpImageRead because the result type is not necessarily a 4 element
112+
// vector.
113+
def int_spv_typedBufferLoad
114+
: DefaultAttrsIntrinsic<[llvm_any_ty], [llvm_any_ty, llvm_i32_ty]>;
109115
}

llvm/lib/Target/SPIRV/SPIRVGlobalRegistry.cpp

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1056,6 +1056,11 @@ SPIRVGlobalRegistry::getSPIRVTypeForVReg(Register VReg,
10561056
return nullptr;
10571057
}
10581058

1059+
SPIRVType *SPIRVGlobalRegistry::getResultType(Register VReg) {
1060+
MachineInstr *Instr = getVRegDef(CurMF->getRegInfo(), VReg);
1061+
return getSPIRVTypeForVReg(Instr->getOperand(1).getReg());
1062+
}
1063+
10591064
SPIRVType *SPIRVGlobalRegistry::getOrCreateSPIRVType(
10601065
const Type *Ty, MachineIRBuilder &MIRBuilder,
10611066
SPIRV::AccessQualifier::AccessQualifier AccessQual, bool EmitIR) {
@@ -1126,6 +1131,24 @@ SPIRVGlobalRegistry::getScalarOrVectorComponentCount(SPIRVType *Type) const {
11261131
: 1;
11271132
}
11281133

1134+
SPIRVType *
1135+
SPIRVGlobalRegistry::getScalarOrVectorComponentType(Register VReg) const {
1136+
return getScalarOrVectorComponentType(getSPIRVTypeForVReg(VReg));
1137+
}
1138+
1139+
SPIRVType *
1140+
SPIRVGlobalRegistry::getScalarOrVectorComponentType(SPIRVType *Type) const {
1141+
if (!Type)
1142+
return nullptr;
1143+
Register ScalarReg = Type->getOpcode() == SPIRV::OpTypeVector
1144+
? Type->getOperand(1).getReg()
1145+
: Type->getOperand(0).getReg();
1146+
SPIRVType *ScalarType = getSPIRVTypeForVReg(ScalarReg);
1147+
assert(isScalarOrVectorOfType(Type->getOperand(0).getReg(),
1148+
ScalarType->getOpcode()));
1149+
return ScalarType;
1150+
}
1151+
11291152
unsigned
11301153
SPIRVGlobalRegistry::getScalarOrVectorBitWidth(const SPIRVType *Type) const {
11311154
assert(Type && "Invalid Type pointer");

llvm/lib/Target/SPIRV/SPIRVGlobalRegistry.h

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -353,6 +353,9 @@ class SPIRVGlobalRegistry {
353353
SPIRVType *getSPIRVTypeForVReg(Register VReg,
354354
const MachineFunction *MF = nullptr) const;
355355

356+
// Return the result type of the instruction defining the register.
357+
SPIRVType *getResultType(Register VReg);
358+
356359
// Whether the given VReg has a SPIR-V type mapped to it yet.
357360
bool hasSPIRVTypeForVReg(Register VReg) const {
358361
return getSPIRVTypeForVReg(VReg) != nullptr;
@@ -388,6 +391,12 @@ class SPIRVGlobalRegistry {
388391
unsigned getScalarOrVectorComponentCount(Register VReg) const;
389392
unsigned getScalarOrVectorComponentCount(SPIRVType *Type) const;
390393

394+
// Return the component type in a vector if the argument is associated with
395+
// a vector type. Returns the argument itself for other types, and nullptr
396+
// for a missing type.
397+
SPIRVType *getScalarOrVectorComponentType(Register VReg) const;
398+
SPIRVType *getScalarOrVectorComponentType(SPIRVType *Type) const;
399+
391400
// For vectors or scalars of booleans, integers and floats, return the scalar
392401
// type's bitwidth. Otherwise calls llvm_unreachable().
393402
unsigned getScalarOrVectorBitWidth(const SPIRVType *Type) const;

llvm/lib/Target/SPIRV/SPIRVInstructionSelector.cpp

Lines changed: 103 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@
2222
#include "SPIRVTargetMachine.h"
2323
#include "SPIRVUtils.h"
2424
#include "llvm/ADT/APFloat.h"
25+
#include "llvm/ADT/StringExtras.h"
2526
#include "llvm/CodeGen/GlobalISel/GIMatchTableExecutorImpl.h"
2627
#include "llvm/CodeGen/GlobalISel/GenericMachineInstrs.h"
2728
#include "llvm/CodeGen/GlobalISel/InstructionSelector.h"
@@ -267,6 +268,9 @@ class SPIRVInstructionSelector : public InstructionSelector {
267268
bool selectHandleFromBinding(Register &ResVReg, const SPIRVType *ResType,
268269
MachineInstr &I) const;
269270

271+
void selectReadImageIntrinsic(Register &ResVReg, const SPIRVType *ResType,
272+
MachineInstr &I) const;
273+
270274
// Utilities
271275
std::pair<Register, bool>
272276
buildI32Constant(uint32_t Val, MachineInstr &I,
@@ -292,6 +296,9 @@ class SPIRVInstructionSelector : public InstructionSelector {
292296
uint32_t Binding, uint32_t ArraySize,
293297
Register IndexReg, bool IsNonUniform,
294298
MachineIRBuilder MIRBuilder) const;
299+
SPIRVType *widenTypeToVec4(const SPIRVType *Type, MachineInstr &I) const;
300+
void extractSubvector(Register &ResVReg, const SPIRVType *ResType,
301+
Register &ReadReg, MachineInstr &InsertionPoint) const;
295302
};
296303

297304
} // end anonymous namespace
@@ -2846,6 +2853,10 @@ bool SPIRVInstructionSelector::selectIntrinsic(Register ResVReg,
28462853
case Intrinsic::spv_handle_fromBinding: {
28472854
return selectHandleFromBinding(ResVReg, ResType, I);
28482855
}
2856+
case Intrinsic::spv_typedBufferLoad: {
2857+
selectReadImageIntrinsic(ResVReg, ResType, I);
2858+
return true;
2859+
}
28492860
default: {
28502861
std::string DiagMsg;
28512862
raw_string_ostream OS(DiagMsg);
@@ -2883,6 +2894,83 @@ bool SPIRVInstructionSelector::selectHandleFromBinding(Register &ResVReg,
28832894
.constrainAllUses(TII, TRI, RBI);
28842895
}
28852896

2897+
void SPIRVInstructionSelector::selectReadImageIntrinsic(
2898+
Register &ResVReg, const SPIRVType *ResType, MachineInstr &I) const {
2899+
2900+
// If the load of the image is in a different basic block, then
2901+
// this will generate invalid code. A proper solution is to move
2902+
// the OpLoad from selectHandleFromBinding here. However, to do
2903+
// that we will need to change the return type of the intrinsic.
2904+
// We will do that when we can, but for now trying to move forward with other
2905+
// issues.
2906+
Register ImageReg = I.getOperand(2).getReg();
2907+
assert(MRI->getVRegDef(ImageReg)->getParent() == I.getParent() &&
2908+
"The image must be loaded in the same basic block as its use.");
2909+
2910+
uint64_t ResultSize = GR.getScalarOrVectorComponentCount(ResType);
2911+
if (ResultSize == 4) {
2912+
BuildMI(*I.getParent(), I, I.getDebugLoc(), TII.get(SPIRV::OpImageRead))
2913+
.addDef(ResVReg)
2914+
.addUse(GR.getSPIRVTypeID(ResType))
2915+
.addUse(ImageReg)
2916+
.addUse(I.getOperand(3).getReg());
2917+
return;
2918+
}
2919+
2920+
SPIRVType *ReadType = widenTypeToVec4(ResType, I);
2921+
Register ReadReg = MRI->createVirtualRegister(GR.getRegClass(ReadType));
2922+
BuildMI(*I.getParent(), I, I.getDebugLoc(), TII.get(SPIRV::OpImageRead))
2923+
.addDef(ReadReg)
2924+
.addUse(GR.getSPIRVTypeID(ReadType))
2925+
.addUse(ImageReg)
2926+
.addUse(I.getOperand(3).getReg());
2927+
2928+
if (ResultSize == 1) {
2929+
BuildMI(*I.getParent(), I, I.getDebugLoc(),
2930+
TII.get(SPIRV::OpCompositeExtract))
2931+
.addDef(ResVReg)
2932+
.addUse(GR.getSPIRVTypeID(ResType))
2933+
.addUse(ReadReg)
2934+
.addImm(0);
2935+
return;
2936+
}
2937+
extractSubvector(ResVReg, ResType, ReadReg, I);
2938+
}
2939+
2940+
void SPIRVInstructionSelector::extractSubvector(
2941+
Register &ResVReg, const SPIRVType *ResType, Register &ReadReg,
2942+
MachineInstr &InsertionPoint) const {
2943+
SPIRVType *InputType = GR.getResultType(ReadReg);
2944+
uint64_t InputSize = GR.getScalarOrVectorComponentCount(InputType);
2945+
uint64_t ResultSize = GR.getScalarOrVectorComponentCount(ResType);
2946+
assert(InputSize > 1 && "The input must be a vector.");
2947+
assert(ResultSize > 1 && "The result must be a vector.");
2948+
assert(ResultSize < InputSize &&
2949+
"Cannot extract more element than there are in the input.");
2950+
SmallVector<Register> ComponentRegisters;
2951+
SPIRVType *ScalarType = GR.getScalarOrVectorComponentType(ResType);
2952+
const TargetRegisterClass *ScalarRegClass = GR.getRegClass(ScalarType);
2953+
for (uint64_t I = 0; I < ResultSize; I++) {
2954+
Register ComponentReg = MRI->createVirtualRegister(ScalarRegClass);
2955+
BuildMI(*InsertionPoint.getParent(), InsertionPoint,
2956+
InsertionPoint.getDebugLoc(), TII.get(SPIRV::OpCompositeExtract))
2957+
.addDef(ComponentReg)
2958+
.addUse(ScalarType->getOperand(0).getReg())
2959+
.addUse(ReadReg)
2960+
.addImm(I);
2961+
ComponentRegisters.emplace_back(ComponentReg);
2962+
}
2963+
2964+
MachineInstrBuilder MIB = BuildMI(*InsertionPoint.getParent(), InsertionPoint,
2965+
InsertionPoint.getDebugLoc(),
2966+
TII.get(SPIRV::OpCompositeConstruct))
2967+
.addDef(ResVReg)
2968+
.addUse(GR.getSPIRVTypeID(ResType));
2969+
2970+
for (Register ComponentReg : ComponentRegisters)
2971+
MIB.addUse(ComponentReg);
2972+
}
2973+
28862974
Register SPIRVInstructionSelector::buildPointerToResource(
28872975
const SPIRVType *ResType, uint32_t Set, uint32_t Binding,
28882976
uint32_t ArraySize, Register IndexReg, bool IsNonUniform,
@@ -3384,6 +3472,21 @@ bool SPIRVInstructionSelector::selectSpvThreadId(Register ResVReg,
33843472
return Result && MIB.constrainAllUses(TII, TRI, RBI);
33853473
}
33863474

3475+
SPIRVType *SPIRVInstructionSelector::widenTypeToVec4(const SPIRVType *Type,
3476+
MachineInstr &I) const {
3477+
MachineIRBuilder MIRBuilder(I);
3478+
if (Type->getOpcode() != SPIRV::OpTypeVector)
3479+
return GR.getOrCreateSPIRVVectorType(Type, 4, MIRBuilder);
3480+
3481+
uint64_t VectorSize = Type->getOperand(2).getImm();
3482+
if (VectorSize == 4)
3483+
return Type;
3484+
3485+
Register ScalarTypeReg = Type->getOperand(1).getReg();
3486+
const SPIRVType *ScalarType = GR.getSPIRVTypeForVReg(ScalarTypeReg);
3487+
return GR.getOrCreateSPIRVVectorType(ScalarType, 4, MIRBuilder);
3488+
}
3489+
33873490
namespace llvm {
33883491
InstructionSelector *
33893492
createSPIRVInstructionSelector(const SPIRVTargetMachine &TM,

llvm/lib/Target/SPIRV/SPIRVModuleAnalysis.cpp

Lines changed: 21 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -660,7 +660,8 @@ void RequirementHandler::initAvailableCapabilitiesForOpenCL(
660660
// Add the min requirements for different OpenCL and SPIR-V versions.
661661
addAvailableCaps({Capability::Addresses, Capability::Float16Buffer,
662662
Capability::Kernel, Capability::Vector16,
663-
Capability::Groups, Capability::GenericPointer});
663+
Capability::Groups, Capability::GenericPointer,
664+
Capability::StorageImageReadWithoutFormat});
664665
if (ST.hasOpenCLFullProfile())
665666
addAvailableCaps({Capability::Int64, Capability::Int64Atomics});
666667
if (ST.hasOpenCLImageSupport()) {
@@ -719,6 +720,10 @@ void RequirementHandler::initAvailableCapabilitiesForVulkan(
719720
Capability::UniformTexelBufferArrayNonUniformIndexingEXT,
720721
Capability::StorageTexelBufferArrayNonUniformIndexingEXT});
721722
}
723+
724+
// Became core in Vulkan 1.3
725+
if (ST.isAtLeastSPIRVVer(VersionTuple(1, 6)))
726+
addAvailableCaps({Capability::StorageImageReadWithoutFormat});
722727
}
723728

724729
} // namespace SPIRV
@@ -1005,6 +1010,13 @@ void addOpAccessChainReqs(const MachineInstr &Instr,
10051010
}
10061011
}
10071012

1013+
static bool isImageTypeWithUnknownFormat(SPIRVType *TypeInst) {
1014+
if (TypeInst->getOpcode() != SPIRV::OpTypeImage)
1015+
return false;
1016+
assert(TypeInst->getOperand(7).isImm() && "The image format must be an imm.");
1017+
return TypeInst->getOperand(7).getImm() == 0;
1018+
}
1019+
10081020
static void AddDotProductRequirements(const MachineInstr &MI,
10091021
SPIRV::RequirementHandler &Reqs,
10101022
const SPIRVSubtarget &ST) {
@@ -1411,6 +1423,14 @@ void addInstrRequirements(const MachineInstr &MI,
14111423
case SPIRV::OpUDot:
14121424
AddDotProductRequirements(MI, Reqs, ST);
14131425
break;
1426+
case SPIRV::OpImageRead: {
1427+
Register ImageReg = MI.getOperand(2).getReg();
1428+
SPIRVType *TypeDef = ST.getSPIRVGlobalRegistry()->getResultType(ImageReg);
1429+
if (isImageTypeWithUnknownFormat(TypeDef))
1430+
Reqs.addCapability(SPIRV::Capability::StorageImageReadWithoutFormat);
1431+
break;
1432+
}
1433+
14141434
default:
14151435
break;
14161436
}
Lines changed: 66 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,66 @@
1+
; RUN: llc -O0 -verify-machineinstrs -mtriple=spirv-vulkan-library %s -o - | FileCheck %s
2+
; RUN: %if spirv-tools %{ llc -O0 -mtriple=spirv-vulkan-library %s -o - -filetype=obj | spirv-val %}
3+
4+
; CHECK-NOT: OpCapability StorageImageReadWithoutFormat
5+
6+
; CHECK-DAG: OpDecorate [[IntBufferVar:%[0-9]+]] DescriptorSet 16
7+
; CHECK-DAG: OpDecorate [[IntBufferVar]] Binding 7
8+
9+
; CHECK-DAG: [[int:%[0-9]+]] = OpTypeInt 32 0
10+
; CHECK-DAG: [[zero:%[0-9]+]] = OpConstant [[int]] 0
11+
; CHECK-DAG: [[v4_int:%[0-9]+]] = OpTypeVector [[int]] 4
12+
; CHECK-DAG: [[v2_int:%[0-9]+]] = OpTypeVector [[int]] 2
13+
; CHECK-DAG: [[RWBufferTypeInt:%[0-9]+]] = OpTypeImage [[int]] Buffer 2 0 0 2 R32i {{$}}
14+
; CHECK-DAG: [[IntBufferPtrType:%[0-9]+]] = OpTypePointer UniformConstant [[RWBufferTypeInt]]
15+
; CHECK-DAG: [[IntBufferVar]] = OpVariable [[IntBufferPtrType]] UniformConstant
16+
17+
; CHECK: {{%[0-9]+}} = OpFunction {{%[0-9]+}} DontInline {{%[0-9]+}}
18+
; CHECK-NEXT: OpLabel
19+
define void @RWBufferLoad_Vec4_I32() #0 {
20+
; CHECK: [[buffer:%[0-9]+]] = OpLoad [[RWBufferTypeInt]] [[IntBufferVar]]
21+
%buffer0 = call target("spirv.Image", i32, 5, 2, 0, 0, 2, 24)
22+
@llvm.spv.handle.fromBinding.tspirv.Image_i32_5_2_0_0_2_24(
23+
i32 16, i32 7, i32 1, i32 0, i1 false)
24+
25+
; CHECK: OpImageRead [[v4_int]] [[buffer]] [[zero]]
26+
%data0 = call <4 x i32> @llvm.spv.typedBufferLoad(
27+
target("spirv.Image", i32, 5, 2, 0, 0, 2, 24) %buffer0, i32 0)
28+
29+
ret void
30+
}
31+
32+
; CHECK: {{%[0-9]+}} = OpFunction {{%[0-9]+}} DontInline {{%[0-9]+}}
33+
; CHECK-NEXT: OpLabel
34+
define void @RWBufferLoad_I32() #0 {
35+
; CHECK: [[buffer:%[0-9]+]] = OpLoad [[RWBufferTypeInt]] [[IntBufferVar]]
36+
%buffer1 = call target("spirv.Image", i32, 5, 2, 0, 0, 2, 24)
37+
@llvm.spv.handle.fromBinding.tspirv.Image_i32_5_2_0_0_2_24(
38+
i32 16, i32 7, i32 1, i32 0, i1 false)
39+
40+
; CHECK: [[V:%[0-9]+]] = OpImageRead [[v4_int]] [[buffer]] [[zero]]
41+
; CHECK: OpCompositeExtract [[int]] [[V]] 0
42+
%data1 = call i32 @llvm.spv.typedBufferLoad(
43+
target("spirv.Image", i32, 5, 2, 0, 0, 2, 24) %buffer1, i32 0)
44+
45+
ret void
46+
}
47+
48+
; CHECK: {{%[0-9]+}} = OpFunction {{%[0-9]+}} DontInline {{%[0-9]+}}
49+
; CHECK-NEXT: OpLabel
50+
define void @RWBufferLoad_Vec2_I32() #0 {
51+
; CHECK: [[buffer:%[0-9]+]] = OpLoad [[RWBufferTypeInt]] [[IntBufferVar]]
52+
%buffer0 = call target("spirv.Image", i32, 5, 2, 0, 0, 2, 24)
53+
@llvm.spv.handle.fromBinding.tspirv.Image_i32_5_2_0_0_2_24(
54+
i32 16, i32 7, i32 1, i32 0, i1 false)
55+
56+
; CHECK: [[V:%[0-9]+]] = OpImageRead [[v4_int]] [[buffer]] [[zero]]
57+
; CHECK: [[e0:%[0-9]+]] = OpCompositeExtract [[int]] [[V]] 0
58+
; CHECK: [[e1:%[0-9]+]] = OpCompositeExtract [[int]] [[V]] 1
59+
; CHECK: OpCompositeConstruct [[v2_int]] [[e0]] [[e1]]
60+
%data0 = call <2 x i32> @llvm.spv.typedBufferLoad(
61+
target("spirv.Image", i32, 5, 2, 0, 0, 2, 24) %buffer0, i32 0)
62+
63+
ret void
64+
}
65+
66+
attributes #0 = { convergent noinline norecurse "frame-pointer"="all" "hlsl.numthreads"="1,1,1" "hlsl.shader"="compute" "no-trapping-math"="true" "stack-protector-buffer-size"="8" }
Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
; RUN: llc -O0 -verify-machineinstrs -mtriple=spirv1.6-vulkan1.3-library %s -o - | FileCheck %s
2+
; RUN: %if spirv-tools %{ llc -O0 -mtriple=spirv1.6-vulkan1.3-library %s -o - -filetype=obj | spirv-val %}
3+
4+
; CHECK: OpCapability StorageImageReadWithoutFormat
5+
; CHECK-DAG: OpDecorate [[IntBufferVar:%[0-9]+]] DescriptorSet 16
6+
; CHECK-DAG: OpDecorate [[IntBufferVar]] Binding 7
7+
8+
; CHECK-DAG: [[int:%[0-9]+]] = OpTypeInt 32 0
9+
; CHECK-DAG: [[zero:%[0-9]+]] = OpConstant [[int]] 0
10+
; CHECK-DAG: [[v4_int:%[0-9]+]] = OpTypeVector [[int]] 4
11+
; CHECK-DAG: [[RWBufferTypeInt:%[0-9]+]] = OpTypeImage [[int]] Buffer 2 0 0 2 Unknown {{$}}
12+
; CHECK-DAG: [[IntBufferPtrType:%[0-9]+]] = OpTypePointer UniformConstant [[RWBufferTypeInt]]
13+
; CHECK-DAG: [[IntBufferVar]] = OpVariable [[IntBufferPtrType]] UniformConstant
14+
15+
; CHECK: {{%[0-9]+}} = OpFunction {{%[0-9]+}} DontInline {{%[0-9]+}}
16+
; CHECK-NEXT: OpLabel
17+
define void @RWBufferLoad_Vec4_I32() #0 {
18+
; CHECK: [[buffer:%[0-9]+]] = OpLoad [[RWBufferTypeInt]] [[IntBufferVar]]
19+
%buffer0 = call target("spirv.Image", i32, 5, 2, 0, 0, 2, 0)
20+
@llvm.spv.handle.fromBinding.tspirv.Image_f32_5_2_0_0_2_0(
21+
i32 16, i32 7, i32 1, i32 0, i1 false)
22+
23+
; CHECK: OpImageRead [[v4_int]] [[buffer]] [[zero]]
24+
%data0 = call <4 x i32> @llvm.spv.typedBufferLoad(
25+
target("spirv.Image", i32, 5, 2, 0, 0, 2, 0) %buffer0, i32 0)
26+
27+
ret void
28+
}
29+
30+
attributes #0 = { convergent noinline norecurse "frame-pointer"="all" "hlsl.numthreads"="1,1,1" "hlsl.shader"="compute" "no-trapping-math"="true" "stack-protector-buffer-size"="8" }

llvm/test/CodeGen/SPIRV/read_image.ll

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
; RUN: llc -O0 -mtriple=spirv64-unknown-unknown %s -o - | FileCheck %s --check-prefix=CHECK-SPIRV
22

3+
; CHECK-SPIRV: OpCapability StorageImageReadWithoutFormat
4+
35
; CHECK-SPIRV: %[[#IntTy:]] = OpTypeInt
46
; CHECK-SPIRV: %[[#IVecTy:]] = OpTypeVector %[[#IntTy]]
57
; CHECK-SPIRV: %[[#FloatTy:]] = OpTypeFloat

llvm/test/CodeGen/SPIRV/transcoding/OpImageReadMS.ll

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
; RUN: llc -O0 -mtriple=spirv32-unknown-unknown %s -o - | FileCheck %s --check-prefix=CHECK-SPIRV
22

3+
; CHECK-SPIRV: OpCapability StorageImageReadWithoutFormat
34
; CHECK-SPIRV: %[[#]] = OpImageRead %[[#]] %[[#]] %[[#]] Sample %[[#]]
45

56
define spir_kernel void @sample_test(target("spirv.Image", void, 1, 0, 0, 1, 0, 0, 0) %source, i32 %sampler, <4 x float> addrspace(1)* nocapture %results) {

0 commit comments

Comments
 (0)