Skip to content

Commit 18ef956

Browse files
committed
[SPIR-V] Add InferAddrspace pass to the backend
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 4e073a1 commit 18ef956

File tree

4 files changed

+136
-0
lines changed

4 files changed

+136
-0
lines changed

llvm/lib/Target/SPIRV/SPIRVTargetMachine.cpp

+9
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@
3232
#include "llvm/Passes/PassBuilder.h"
3333
#include "llvm/Target/TargetOptions.h"
3434
#include "llvm/Transforms/Scalar/Reg2Mem.h"
35+
#include "llvm/Transforms/Scalar.h"
3536
#include "llvm/Transforms/Utils.h"
3637
#include <optional>
3738

@@ -190,6 +191,14 @@ void SPIRVPassConfig::addIRPasses() {
190191
TargetPassConfig::addIRPasses();
191192

192193
if (TM.getSubtargetImpl()->isVulkanEnv()) {
194+
// The frontend has a tendency to quickly addrspacecast pointers to the
195+
// default address space, and relies on addrspacecast instructions at the
196+
// boundaries. Vulkan does not allow such things, and we must keep the
197+
// pointer address space stable.
198+
// This pass will determine real address space of a pointer, and patch
199+
// instructions removing Addrspacecasts.
200+
addPass(createInferAddressSpacesPass(/* AddressSpace= */ 0));
201+
193202
// 1. Simplify loop for subsequent transformations. After this steps, loops
194203
// have the following properties:
195204
// - loops have a single entry edge (pre-header to loop header).
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+
}
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)
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)