Skip to content

Commit 7d98b66

Browse files
authored
[SPIR-V] Add InferAddrSpaces pass to the backend (#137766)
This commit enables a pass in the backend which propagates the addrspace of the pointers down to the last use, making sure the addrspace remains consistent, and thus stripping any addrspacecast. This is required to lower LLVM-IR to logical SPIR-V, which does not support generic pointers. This is now required as HLSL emits several address spaces, and thus addrspacecasts in some cases: Example 1: resource access ```llvm %handle = tail call target("spirv.VulkanBuffer", ...) %rptr = @llvm.spv.resource.getpointer(%handle, ...); %cptr = addrspacecast ptr addrspace(11) %rptr to ptr %fptr = load i32, ptr %cptr ``` Example 2: object methods ```llvm define void @objectmethod(ptr %this) { } define void @foo(ptr addrspace(11) %object) { call void @objectmethod(ptr addrspacecast(addrspace(11) %object to ptr)); } ```
1 parent 29e09af commit 7d98b66

File tree

5 files changed

+144
-0
lines changed

5 files changed

+144
-0
lines changed

llvm/lib/Target/SPIRV/SPIRVTargetMachine.cpp

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@
3131
#include "llvm/Pass.h"
3232
#include "llvm/Passes/PassBuilder.h"
3333
#include "llvm/Target/TargetOptions.h"
34+
#include "llvm/Transforms/Scalar.h"
3435
#include "llvm/Transforms/Scalar/Reg2Mem.h"
3536
#include "llvm/Transforms/Utils.h"
3637
#include <optional>
@@ -191,6 +192,12 @@ void SPIRVPassConfig::addIRPasses() {
191192
TargetPassConfig::addIRPasses();
192193

193194
if (TM.getSubtargetImpl()->isVulkanEnv()) {
195+
// Vulkan does not allow address space casts. This pass is run to remove
196+
// address space casts that can be removed.
197+
// If an address space cast is not removed while targeting Vulkan, lowering
198+
// will fail during MIR lowering.
199+
addPass(createInferAddressSpacesPass());
200+
194201
// 1. Simplify loop for subsequent transformations. After this steps, loops
195202
// have the following properties:
196203
// - loops have a single entry edge (pre-header to loop header).

llvm/lib/Target/SPIRV/SPIRVTargetTransformInfo.h

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,16 @@ class SPIRVTTIImpl : public BasicTTIImplBase<SPIRVTTIImpl> {
4848
return TTI::PSK_Software; // Arbitrary bit-width INT is not core SPIR-V.
4949
return TTI::PSK_FastHardware;
5050
}
51+
52+
unsigned getFlatAddressSpace() const override {
53+
if (ST->isVulkanEnv())
54+
return 0;
55+
// FIXME: Clang has 2 distinct address space maps. One where
56+
// default=4=Generic, and one with default=0=Function. This depends on the
57+
// environment. For OpenCL, we don't need to run the InferAddrSpace pass, so
58+
// we can return -1, but we might want to fix this.
59+
return -1;
60+
}
5161
};
5262

5363
} // namespace llvm
Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
; RUN: llc -verify-machineinstrs -O3 -mtriple=spirv-unknown-vulkan1.3-compute %s -o - | FileCheck %s
2+
; RUN: %if spirv-tools %{ llc -O3 -mtriple=spirv-unknown-vulkan1.3-compute %s -o - -filetype=obj | spirv-val %}
3+
4+
; CHECK-DAG: %[[#uint:]] = OpTypeInt 32 0
5+
; CHECK-DAG: %[[#uint_0:]] = OpConstant %[[#uint]] 0
6+
; CHECK-DAG: %[[#ptr_uint:]] = OpTypePointer Private %[[#uint]]
7+
; CHECK-DAG: %[[#var:]] = OpVariable %[[#ptr_uint]] Private %[[#uint_0]]
8+
9+
; CHECK-DAG: OpName %[[#func_simple:]] "simple"
10+
; CHECK-DAG: OpName %[[#func_chain:]] "chain"
11+
12+
@global = internal addrspace(10) global i32 zeroinitializer
13+
14+
define void @simple() {
15+
; CHECK: %[[#func_simple]] = OpFunction
16+
entry:
17+
%ptr = getelementptr i32, ptr addrspace(10) @global, i32 0
18+
%casted = addrspacecast ptr addrspace(10) %ptr to ptr
19+
%val = load i32, ptr %casted
20+
; CHECK: %{{.*}} = OpLoad %[[#uint]] %[[#var]] Aligned 4
21+
ret void
22+
}
23+
24+
define void @chain() {
25+
; CHECK: %[[#func_chain]] = OpFunction
26+
entry:
27+
%a = getelementptr i32, ptr addrspace(10) @global, i32 0
28+
%b = addrspacecast ptr addrspace(10) %a to ptr
29+
%c = getelementptr i32, ptr %b, i32 0
30+
%d = addrspacecast ptr %c to ptr addrspace(10)
31+
%e = addrspacecast ptr addrspace(10) %d to ptr
32+
33+
%val = load i32, ptr %e
34+
; CHECK: %{{.*}} = OpLoad %[[#uint]] %[[#var]] Aligned 4
35+
ret void
36+
}
Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
1+
; RUN: llc -verify-machineinstrs -O3 -mtriple=spirv-unknown-vulkan1.3-compute %s -o - | FileCheck %s --match-full-lines
2+
; RUN: %if spirv-tools %{ llc -O3 -mtriple=spirv-unknown-vulkan1.3-compute %s -o - -filetype=obj | spirv-val %}
3+
4+
; FIXME(134119): enable-this once Offset decoration are added.
5+
; XFAIL: spirv-tools
6+
7+
%S2 = type { { [10 x { i32, i32 } ] }, i32 }
8+
9+
; CHECK-DAG: %[[#uint:]] = OpTypeInt 32 0
10+
; CHECK-DAG: %[[#uint_0:]] = OpConstant %[[#uint]] 0
11+
; CHECK-DAG: %[[#uint_1:]] = OpConstant %[[#uint]] 1
12+
; CHECK-DAG: %[[#uint_3:]] = OpConstant %[[#uint]] 3
13+
; CHECK-DAG: %[[#uint_10:]] = OpConstant %[[#uint]] 10
14+
; CHECK-DAG: %[[#uint_11:]] = OpConstant %[[#uint]] 11
15+
; CHECK-DAG: %[[#ptr_StorageBuffer_uint:]] = OpTypePointer StorageBuffer %[[#uint]]
16+
17+
; CHECK-DAG: %[[#t_s2_s_a_s:]] = OpTypeStruct %[[#uint]] %[[#uint]]
18+
; CHECK-DAG: %[[#t_s2_s_a:]] = OpTypeArray %[[#t_s2_s_a_s]] %[[#uint_10]]
19+
; CHECK-DAG: %[[#t_s2_s:]] = OpTypeStruct %[[#t_s2_s_a]]
20+
; CHECK-DAG: %[[#t_s2:]] = OpTypeStruct %[[#t_s2_s]] %[[#uint]]
21+
22+
; CHECK-DAG: %[[#ptr_StorageBuffer_struct:]] = OpTypePointer StorageBuffer %[[#t_s2]]
23+
; CHECK-DAG: %[[#rarr:]] = OpTypeRuntimeArray %[[#t_s2]]
24+
; CHECK-DAG: %[[#rarr_struct:]] = OpTypeStruct %[[#rarr]]
25+
; CHECK-DAG: %[[#spirv_VulkanBuffer:]] = OpTypePointer StorageBuffer %[[#rarr_struct]]
26+
27+
declare target("spirv.VulkanBuffer", [0 x %S2], 12, 1) @llvm.spv.resource.handlefrombinding.tspirv.VulkanBuffer_a0s_Ss_12_1t(i32, i32, i32, i32, i1)
28+
29+
define void @main() "hlsl.numthreads"="1,1,1" "hlsl.shader"="compute" {
30+
entry:
31+
%handle = tail call target("spirv.VulkanBuffer", [0 x %S2], 12, 1) @llvm.spv.resource.handlefrombinding.tspirv.VulkanBuffer_a0s_Ss_12_1t(i32 0, i32 0, i32 1, i32 0, i1 false)
32+
; CHECK: %[[#resource:]] = OpVariable %[[#spirv_VulkanBuffer]] StorageBuffer
33+
34+
%ptr = tail call noundef align 4 dereferenceable(4) ptr addrspace(11) @llvm.spv.resource.getpointer.p11.tspirv.VulkanBuffer_a0s_Ss_12_1t(target("spirv.VulkanBuffer", [0 x %S2], 12, 1) %handle, i32 0)
35+
; CHECK: %[[#a:]] = OpCopyObject %[[#spirv_VulkanBuffer]] %[[#resource]]
36+
; CHECK: %[[#b:]] = OpAccessChain %[[#ptr_StorageBuffer_struct]] %[[#a:]] %[[#uint_0]] %[[#uint_0]]
37+
%casted = addrspacecast ptr addrspace(11) %ptr to ptr
38+
39+
; CHECK: %[[#ptr2:]] = OpInBoundsAccessChain %[[#ptr_StorageBuffer_uint]] %[[#b:]] %[[#uint_0]] %[[#uint_0]] %[[#uint_3]] %[[#uint_1]]
40+
%ptr2 = getelementptr inbounds %S2, ptr %casted, i64 0, i32 0, i32 0, i32 3, i32 1
41+
42+
; CHECK: OpStore %[[#ptr2]] %[[#uint_10]] Aligned 4
43+
store i32 10, ptr %ptr2, align 4
44+
45+
; Another store, but this time using LLVM's ability to load the first element
46+
; without an explicit GEP. The backend has to determine the ptr type and
47+
; generate the appropriate access chain.
48+
; CHECK: %[[#ptr3:]] = OpInBoundsAccessChain %[[#ptr_StorageBuffer_uint]] %[[#b:]] %[[#uint_0]] %[[#uint_0]] %[[#uint_0]] %[[#uint_0]]
49+
; CHECK: OpStore %[[#ptr3]] %[[#uint_11]] Aligned 4
50+
store i32 11, ptr %casted, align 4
51+
ret void
52+
}
53+
54+
declare ptr addrspace(11) @llvm.spv.resource.getpointer.p11.tspirv.VulkanBuffer_a0s_S2s_12_1t(target("spirv.VulkanBuffer", [0 x %S2], 12, 1), i32)
Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
; RUN: llc -verify-machineinstrs -O3 -mtriple=spirv-unknown-vulkan1.3-compute %s -o - | FileCheck %s
2+
; RUN: %if spirv-tools %{ llc -O3 -mtriple=spirv-unknown-vulkan1.3-compute %s -o - -filetype=obj | spirv-val %}
3+
4+
; FIXME(134119): enable-this once Offset decoration are added.
5+
; XFAIL: spirv-tools
6+
7+
%struct.S = type { i32 }
8+
9+
; CHECK-DAG: %[[#uint:]] = OpTypeInt 32 0
10+
; CHECK-DAG: %[[#uint_0:]] = OpConstant %[[#uint]] 0
11+
; CHECK-DAG: %[[#uint_10:]] = OpConstant %[[#uint]] 10
12+
; CHECK-DAG: %[[#ptr_StorageBuffer_uint:]] = OpTypePointer StorageBuffer %[[#uint]]
13+
; CHECK-DAG: %[[#struct:]] = OpTypeStruct %[[#uint]]
14+
; CHECK-DAG: %[[#ptr_StorageBuffer_struct:]] = OpTypePointer StorageBuffer %[[#struct]]
15+
; CHECK-DAG: %[[#rarr:]] = OpTypeRuntimeArray %[[#struct]]
16+
; CHECK-DAG: %[[#rarr_struct:]] = OpTypeStruct %[[#rarr]]
17+
; CHECK-DAG: %[[#spirv_VulkanBuffer:]] = OpTypePointer StorageBuffer %[[#rarr_struct]]
18+
19+
declare target("spirv.VulkanBuffer", [0 x %struct.S], 12, 1) @llvm.spv.resource.handlefrombinding.tspirv.VulkanBuffer_a0s_struct.Ss_12_1t(i32, i32, i32, i32, i1)
20+
21+
define void @main() "hlsl.numthreads"="1,1,1" "hlsl.shader"="compute" {
22+
entry:
23+
%handle = tail call target("spirv.VulkanBuffer", [0 x %struct.S], 12, 1) @llvm.spv.resource.handlefrombinding.tspirv.VulkanBuffer_a0s_struct.Ss_12_1t(i32 0, i32 0, i32 1, i32 0, i1 false)
24+
; CHECK: %[[#resource:]] = OpVariable %[[#spirv_VulkanBuffer]] StorageBuffer
25+
26+
%ptr = tail call noundef align 4 dereferenceable(4) ptr addrspace(11) @llvm.spv.resource.getpointer.p11.tspirv.VulkanBuffer_a0s_struct.Ss_12_1t(target("spirv.VulkanBuffer", [0 x %struct.S], 12, 1) %handle, i32 0)
27+
; CHECK: %[[#a:]] = OpCopyObject %[[#spirv_VulkanBuffer]] %[[#resource]]
28+
; CHECK: %[[#b:]] = OpAccessChain %[[#ptr_StorageBuffer_struct]] %[[#a:]] %[[#uint_0]] %[[#uint_0]]
29+
; CHECK: %[[#c:]] = OpInBoundsAccessChain %[[#ptr_StorageBuffer_uint]] %[[#b:]] %[[#uint_0]]
30+
%casted = addrspacecast ptr addrspace(11) %ptr to ptr
31+
32+
; CHECK: OpStore %[[#c]] %[[#uint_10]] Aligned 4
33+
store i32 10, ptr %casted, align 4
34+
ret void
35+
}
36+
37+
declare ptr addrspace(11) @llvm.spv.resource.getpointer.p11.tspirv.VulkanBuffer_a0s_struct.Ss_12_1t(target("spirv.VulkanBuffer", [0 x %struct.S], 12, 1), i32)

0 commit comments

Comments
 (0)