[llvm] 756fe54 - [SPIRV] Add write to image buffer for shaders. (#115927)
via llvm-commits
llvm-commits at lists.llvm.org
Mon Nov 18 06:06:09 PST 2024
Author: Steven Perron
Date: 2024-11-18T09:06:05-05:00
New Revision: 756fe54dc7f7e7fcdfefb11d8f51b1687322daf7
URL: https://github.com/llvm/llvm-project/commit/756fe54dc7f7e7fcdfefb11d8f51b1687322daf7
DIFF: https://github.com/llvm/llvm-project/commit/756fe54dc7f7e7fcdfefb11d8f51b1687322daf7.diff
LOG: [SPIRV] Add write to image buffer for shaders. (#115927)
This commit adds an intrinsic that will write to an image buffer. We
chose to match the name of the DXIL intrinsic for simplicity in clang.
We cannot reuse the existing openCL write_image function because that is
not a reserved name in HLSL. There is not much common code to factor
out.
Added:
llvm/test/CodeGen/SPIRV/hlsl-resources/BufferStore.ll
llvm/test/CodeGen/SPIRV/hlsl-resources/UnknownBufferStore.ll
Modified:
llvm/docs/SPIRVUsage.rst
llvm/include/llvm/IR/IntrinsicsSPIRV.td
llvm/lib/Target/SPIRV/SPIRVInstructionSelector.cpp
llvm/lib/Target/SPIRV/SPIRVModuleAnalysis.cpp
Removed:
################################################################################
diff --git a/llvm/docs/SPIRVUsage.rst b/llvm/docs/SPIRVUsage.rst
index 70eae5b760427b..ffb04dca00fc5c 100644
--- a/llvm/docs/SPIRVUsage.rst
+++ b/llvm/docs/SPIRVUsage.rst
@@ -402,6 +402,11 @@ SPIR-V backend, along with their descriptions and argument details.
return type is a scalar, then the first element of the vector is \
returned. If the return type is an n-element vector, then the first \
n-elements of the 4-element vector are returned.
+ * - `int_spv_typedBufferStore`
+ - void
+ - `[spirv.Image Image, 32-bit Integer coordinate, vec4 data]`
+ - Stores the data to the image buffer at the given coordinate. The \
+ data must be a 4-element vector.
.. _spirv-builtin-functions:
diff --git a/llvm/include/llvm/IR/IntrinsicsSPIRV.td b/llvm/include/llvm/IR/IntrinsicsSPIRV.td
index 20e5e026b9281f..d583ea0f603c59 100644
--- a/llvm/include/llvm/IR/IntrinsicsSPIRV.td
+++ b/llvm/include/llvm/IR/IntrinsicsSPIRV.td
@@ -113,4 +113,10 @@ let TargetPrefix = "spv" in {
// vector.
def int_spv_typedBufferLoad
: DefaultAttrsIntrinsic<[llvm_any_ty], [llvm_any_ty, llvm_i32_ty]>;
+
+ // Write a value to the image buffer. Translates directly to a single
+ // OpImageWrite.
+ def int_spv_typedBufferStore
+ : DefaultAttrsIntrinsic<[], [llvm_any_ty, llvm_i32_ty, llvm_anyvector_ty]>;
+
}
diff --git a/llvm/lib/Target/SPIRV/SPIRVInstructionSelector.cpp b/llvm/lib/Target/SPIRV/SPIRVInstructionSelector.cpp
index 1a752e44c35018..fe6ee417ea8ab6 100644
--- a/llvm/lib/Target/SPIRV/SPIRVInstructionSelector.cpp
+++ b/llvm/lib/Target/SPIRV/SPIRVInstructionSelector.cpp
@@ -274,6 +274,8 @@ class SPIRVInstructionSelector : public InstructionSelector {
void selectReadImageIntrinsic(Register &ResVReg, const SPIRVType *ResType,
MachineInstr &I) const;
+ void selectImageWriteIntrinsic(MachineInstr &I) const;
+
// Utilities
std::pair<Register, bool>
buildI32Constant(uint32_t Val, MachineInstr &I,
@@ -2878,6 +2880,10 @@ bool SPIRVInstructionSelector::selectIntrinsic(Register ResVReg,
case Intrinsic::spv_handle_fromBinding: {
return selectHandleFromBinding(ResVReg, ResType, I);
}
+ case Intrinsic::spv_typedBufferStore: {
+ selectImageWriteIntrinsic(I);
+ return true;
+ }
case Intrinsic::spv_typedBufferLoad: {
selectReadImageIntrinsic(ResVReg, ResType, I);
return true;
@@ -3000,6 +3006,27 @@ void SPIRVInstructionSelector::extractSubvector(
MIB.addUse(ComponentReg);
}
+void SPIRVInstructionSelector::selectImageWriteIntrinsic(
+ MachineInstr &I) const {
+ // If the load of the image is in a
diff erent basic block, then
+ // this will generate invalid code. A proper solution is to move
+ // the OpLoad from selectHandleFromBinding here. However, to do
+ // that we will need to change the return type of the intrinsic.
+ // We will do that when we can, but for now trying to move forward with other
+ // issues.
+ Register ImageReg = I.getOperand(1).getReg();
+ assert(MRI->getVRegDef(ImageReg)->getParent() == I.getParent() &&
+ "The image must be loaded in the same basic block as its use.");
+ Register CoordinateReg = I.getOperand(2).getReg();
+ Register DataReg = I.getOperand(3).getReg();
+ assert(GR.getResultType(DataReg)->getOpcode() == SPIRV::OpTypeVector);
+ assert(GR.getScalarOrVectorComponentCount(GR.getResultType(DataReg)) == 4);
+ BuildMI(*I.getParent(), I, I.getDebugLoc(), TII.get(SPIRV::OpImageWrite))
+ .addUse(ImageReg)
+ .addUse(CoordinateReg)
+ .addUse(DataReg);
+}
+
Register SPIRVInstructionSelector::buildPointerToResource(
const SPIRVType *ResType, uint32_t Set, uint32_t Binding,
uint32_t ArraySize, Register IndexReg, bool IsNonUniform,
diff --git a/llvm/lib/Target/SPIRV/SPIRVModuleAnalysis.cpp b/llvm/lib/Target/SPIRV/SPIRVModuleAnalysis.cpp
index 551c36daa609fe..056af08ebcb865 100644
--- a/llvm/lib/Target/SPIRV/SPIRVModuleAnalysis.cpp
+++ b/llvm/lib/Target/SPIRV/SPIRVModuleAnalysis.cpp
@@ -662,6 +662,7 @@ void RequirementHandler::initAvailableCapabilitiesForOpenCL(
addAvailableCaps({Capability::Addresses, Capability::Float16Buffer,
Capability::Kernel, Capability::Vector16,
Capability::Groups, Capability::GenericPointer,
+ Capability::StorageImageWriteWithoutFormat,
Capability::StorageImageReadWithoutFormat});
if (ST.hasOpenCLFullProfile())
addAvailableCaps({Capability::Int64, Capability::Int64Atomics});
@@ -724,7 +725,8 @@ void RequirementHandler::initAvailableCapabilitiesForVulkan(
// Became core in Vulkan 1.3
if (ST.isAtLeastSPIRVVer(VersionTuple(1, 6)))
- addAvailableCaps({Capability::StorageImageReadWithoutFormat});
+ addAvailableCaps({Capability::StorageImageWriteWithoutFormat,
+ Capability::StorageImageReadWithoutFormat});
}
} // namespace SPIRV
@@ -1444,6 +1446,13 @@ void addInstrRequirements(const MachineInstr &MI,
Reqs.addCapability(SPIRV::Capability::StorageImageReadWithoutFormat);
break;
}
+ case SPIRV::OpImageWrite: {
+ Register ImageReg = MI.getOperand(0).getReg();
+ SPIRVType *TypeDef = ST.getSPIRVGlobalRegistry()->getResultType(ImageReg);
+ if (isImageTypeWithUnknownFormat(TypeDef))
+ Reqs.addCapability(SPIRV::Capability::StorageImageWriteWithoutFormat);
+ break;
+ }
default:
break;
diff --git a/llvm/test/CodeGen/SPIRV/hlsl-resources/BufferStore.ll b/llvm/test/CodeGen/SPIRV/hlsl-resources/BufferStore.ll
new file mode 100644
index 00000000000000..afcc0ed0a455d8
--- /dev/null
+++ b/llvm/test/CodeGen/SPIRV/hlsl-resources/BufferStore.ll
@@ -0,0 +1,37 @@
+; RUN: llc -O3 -verify-machineinstrs -mtriple=spirv-vulkan-library %s -o - | FileCheck %s
+; RUN: %if spirv-tools %{ llc -O0 -mtriple=spirv-vulkan-library %s -o - -filetype=obj | spirv-val %}
+
+; CHECK-NOT: OpCapability StorageImageReadWithoutFormat
+
+; CHECK-DAG: OpDecorate [[IntBufferVar:%[0-9]+]] DescriptorSet 16
+; CHECK-DAG: OpDecorate [[IntBufferVar]] Binding 7
+
+; CHECK-DAG: [[int:%[0-9]+]] = OpTypeInt 32 0
+; CHECK-DAG: [[zero:%[0-9]+]] = OpConstant [[int]] 0
+; CHECK-DAG: [[v4_int:%[0-9]+]] = OpTypeVector [[int]] 4
+; CHECK-DAG: [[RWBufferTypeInt:%[0-9]+]] = OpTypeImage [[int]] Buffer 2 0 0 2 R32i {{$}}
+; CHECK-DAG: [[IntBufferPtrType:%[0-9]+]] = OpTypePointer UniformConstant [[RWBufferTypeInt]]
+; CHECK-DAG: [[IntBufferVar]] = OpVariable [[IntBufferPtrType]] UniformConstant
+
+
+; CHECK: {{%[0-9]+}} = OpFunction {{%[0-9]+}} DontInline {{%[0-9]+}}
+declare <4 x i32> @get_data() #1
+
+; CHECK: {{%[0-9]+}} = OpFunction {{%[0-9]+}} DontInline {{%[0-9]+}}
+; CHECK-NEXT: OpLabel
+define void @RWBufferStore_Vec4_I32() #0 {
+; CHECK: [[buffer:%[0-9]+]] = OpLoad [[RWBufferTypeInt]] [[IntBufferVar]]
+ %buffer0 = call target("spirv.Image", i32, 5, 2, 0, 0, 2, 24)
+ @llvm.spv.handle.fromBinding.tspirv.Image_i32_5_2_0_0_2_24(
+ i32 16, i32 7, i32 1, i32 0, i1 false)
+
+; CHECK: [[data:%[0-9]+]] = OpFunctionCall
+ %data = call <4 x i32> @get_data()
+; CHECK: OpImageWrite [[buffer]] [[zero]] [[data]]
+ call void @llvm.spv.typedBufferStore(target("spirv.Image", i32, 5, 2, 0, 0, 2, 24) %buffer0, i32 0, <4 x i32> %data)
+
+ ret void
+}
+
+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" }
+attributes #1 = { convergent noinline norecurse "frame-pointer"="all" "no-trapping-math"="true" "stack-protector-buffer-size"="8" }
\ No newline at end of file
diff --git a/llvm/test/CodeGen/SPIRV/hlsl-resources/UnknownBufferStore.ll b/llvm/test/CodeGen/SPIRV/hlsl-resources/UnknownBufferStore.ll
new file mode 100644
index 00000000000000..4d26f502568149
--- /dev/null
+++ b/llvm/test/CodeGen/SPIRV/hlsl-resources/UnknownBufferStore.ll
@@ -0,0 +1,36 @@
+; 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 %}
+
+; CHECK: OpCapability StorageImageWriteWithoutFormat
+; CHECK-DAG: OpDecorate [[IntBufferVar:%[0-9]+]] DescriptorSet 16
+; CHECK-DAG: OpDecorate [[IntBufferVar]] Binding 7
+
+; CHECK-DAG: [[int:%[0-9]+]] = OpTypeInt 32 0
+; CHECK-DAG: [[ten:%[0-9]+]] = OpConstant [[int]] 10
+; CHECK-DAG: [[v4_int:%[0-9]+]] = OpTypeVector [[int]] 4
+; CHECK-DAG: [[RWBufferTypeInt:%[0-9]+]] = OpTypeImage [[int]] Buffer 2 0 0 2 Unknown {{$}}
+; CHECK-DAG: [[IntBufferPtrType:%[0-9]+]] = OpTypePointer UniformConstant [[RWBufferTypeInt]]
+; CHECK-DAG: [[IntBufferVar]] = OpVariable [[IntBufferPtrType]] UniformConstant
+
+; CHECK: {{%[0-9]+}} = OpFunction {{%[0-9]+}} DontInline {{%[0-9]+}}
+declare <4 x i32> @get_data() #1
+
+; CHECK: {{%[0-9]+}} = OpFunction {{%[0-9]+}} DontInline {{%[0-9]+}}
+; CHECK-NEXT: OpLabel
+define void @RWBufferLoad_Vec4_I32() #0 {
+; CHECK: [[buffer:%[0-9]+]] = OpLoad [[RWBufferTypeInt]] [[IntBufferVar]]
+ %buffer0 = call target("spirv.Image", i32, 5, 2, 0, 0, 2, 0)
+ @llvm.spv.handle.fromBinding.tspirv.Image_f32_5_2_0_0_2_0(
+ i32 16, i32 7, i32 1, i32 0, i1 false)
+
+; CHECK: [[data:%[0-9]+]] = OpFunctionCall
+ %data = call <4 x i32> @get_data()
+; CHECK: OpImageWrite [[buffer]] [[ten]] [[data]]
+ call void @llvm.spv.typedBufferStore(
+ target("spirv.Image", i32, 5, 2, 0, 0, 2, 0) %buffer0, i32 10, <4 x i32> %data)
+
+ ret void
+}
+
+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" }
+attributes #1 = { convergent noinline norecurse "frame-pointer"="all" "no-trapping-math"="true" "stack-protector-buffer-size"="8" }
More information about the llvm-commits
mailing list