[amdgpu] Implement lower function LDS pass Local variables are allocated at kernel launch. This pass collects global variables that are used from non-kernel functions, moves them into a new struct type, and allocates an instance of that type in every kernel. Uses are then replaced with a constantexpr offset. Prior to this pass, accesses from a function are compiled to trap. With this pass, most such accesses are removed before reaching codegen. The trap logic is left unchanged by this pass. It is still reachable for the cases this pass misses, notably the extern shared construct from hip and variables marked constant which survive the optimizer. This is of interest to the openmp project because the deviceRTL runtime library uses cuda shared variables from functions that cannot be inlined. Trunk llvm therefore cannot compile some openmp kernels for amdgpu. In addition to the unit tests attached, this patch applied to ROCm llvm with fixed-abi enabled and the function pointer hashing scheme deleted passes the openmp suite. This lowering will use more LDS than strictly necessary. It is intended to be a functionally correct fallback for cases that are difficult to target from future optimisation passes. Reviewed By: arsenm Differential Revision: https://reviews.llvm.org/D94648
69 lines
3.5 KiB
LLVM
69 lines
3.5 KiB
LLVM
; RUN: opt -S -mtriple=amdgcn-- -amdgpu-lower-module-lds < %s | FileCheck %s
|
|
; RUN: opt -S -mtriple=amdgcn-- -passes=amdgpu-lower-module-lds < %s | FileCheck %s
|
|
|
|
; Variables that are not lowered by this pass are left unchanged
|
|
; CHECK-NOT: asm
|
|
; CHECK-NOT: llvm.amdgcn.module.lds
|
|
; CHECK-NOT: llvm.amdgcn.module.lds.t
|
|
|
|
; var1, var2 would be transformed were they used from a non-kernel function
|
|
; CHECK: @var1 = addrspace(3) global i32 undef
|
|
; CHECK: @var2 = addrspace(3) global float undef
|
|
@var1 = addrspace(3) global i32 undef
|
|
@var2 = addrspace(3) global float undef
|
|
|
|
; constant variables are left to the optimizer / error diagnostics
|
|
; CHECK: @const_undef = addrspace(3) constant i32 undef
|
|
; CHECK: @const_with_init = addrspace(3) constant i64 8
|
|
@const_undef = addrspace(3) constant i32 undef
|
|
@const_with_init = addrspace(3) constant i64 8
|
|
|
|
; External and constant are both left to the optimizer / error diagnostics
|
|
; CHECK: @extern = external addrspace(3) global i32
|
|
@extern = external addrspace(3) global i32
|
|
|
|
; Use of an addrspace(3) variable with an initializer is skipped,
|
|
; so as to preserve the unimplemented error from llc
|
|
; CHECK: @with_init = addrspace(3) global i64 0
|
|
@with_init = addrspace(3) global i64 0
|
|
|
|
; Only local addrspace variables are transformed
|
|
; CHECK: @addr4 = addrspace(4) global i64 undef
|
|
@addr4 = addrspace(4) global i64 undef
|
|
|
|
; Assign to self is treated as any other initializer, i.e. ignored by this pass
|
|
; CHECK: @toself = addrspace(3) global float addrspace(3)* bitcast (float addrspace(3)* addrspace(3)* @toself to float addrspace(3)*), align 8
|
|
@toself = addrspace(3) global float addrspace(3)* bitcast (float addrspace(3)* addrspace(3)* @toself to float addrspace(3)*), align 8
|
|
|
|
; Use by .used lists doesn't trigger lowering
|
|
; CHECK: @llvm.used = appending global [1 x i8*] [i8* addrspacecast (i8 addrspace(3)* bitcast (i32 addrspace(3)* @var1 to i8 addrspace(3)*) to i8*)], section "llvm.metadata"
|
|
@llvm.used = appending global [1 x i8*] [i8* addrspacecast (i8 addrspace(3)* bitcast (i32 addrspace(3)* @var1 to i8 addrspace(3)*) to i8*)], section "llvm.metadata"
|
|
|
|
; CHECK: @llvm.compiler.used = appending global [1 x i8*] [i8* addrspacecast (i8 addrspace(3)* bitcast (float addrspace(3)* @var2 to i8 addrspace(3)*) to i8*)], section "llvm.metadata"
|
|
@llvm.compiler.used = appending global [1 x i8*] [i8* addrspacecast (i8 addrspace(3)* bitcast (float addrspace(3)* @var2 to i8 addrspace(3)*) to i8*)], section "llvm.metadata"
|
|
|
|
; Access from a function would cause lowering for non-excluded cases
|
|
; CHECK-LABEL: @use_variables()
|
|
; CHECK: %c0 = load i32, i32 addrspace(3)* @const_undef, align 4
|
|
; CHECK: %c1 = load i64, i64 addrspace(3)* @const_with_init, align 4
|
|
; CHECK: %v0 = atomicrmw add i64 addrspace(3)* @with_init, i64 1 seq_cst
|
|
; CHECK: %v1 = cmpxchg i32 addrspace(3)* @extern, i32 4, i32 %c0 acq_rel monotonic
|
|
; CHECK: %v2 = atomicrmw add i64 addrspace(4)* @addr4, i64 %c1 monotonic
|
|
define void @use_variables() {
|
|
%c0 = load i32, i32 addrspace(3)* @const_undef, align 4
|
|
%c1 = load i64, i64 addrspace(3)* @const_with_init, align 4
|
|
%v0 = atomicrmw add i64 addrspace(3)* @with_init, i64 1 seq_cst
|
|
%v1 = cmpxchg i32 addrspace(3)* @extern, i32 4, i32 %c0 acq_rel monotonic
|
|
%v2 = atomicrmw add i64 addrspace(4)* @addr4, i64 %c1 monotonic
|
|
ret void
|
|
}
|
|
|
|
; Use by kernel doesn't trigger lowering
|
|
; CHECK-LABEL: @kern_use()
|
|
; CHECK: %inc = atomicrmw add i32 addrspace(3)* @var1, i32 1 monotonic
|
|
define amdgpu_kernel void @kern_use() {
|
|
%inc = atomicrmw add i32 addrspace(3)* @var1, i32 1 monotonic
|
|
call void @use_variables()
|
|
ret void
|
|
}
|