blob: 52f0b24c2f269e4823127f13bfcef044e0944ec0 [file] [edit]
; NOTE: Assertions have been autogenerated by utils/update_test_checks.py UTC_ARGS: --check-globals all --version 6
; The state machine rewrite has to be on for this test to say anything; it is
; the default upstream but not everywhere, so ask for it explicitly.
; RUN: opt --mtriple=amdgpu-amd-amdhsa --data-layout=A5 -S -passes=openmp-opt \
; RUN: -openmp-opt-disable-state-machine-rewrite=false < %s | FileCheck %s
; The loop runtime functions take the loop body as a callback, so a parallel
; region inside the body is reached by the kernel through the runtime call.
; When the body is a definition we can see, the state machine should dispatch
; to that region directly; when it is opaque, the kernel has to keep the
; fallback that runs whatever work function it is handed.
;
; Both kernels stay in generic mode. Seeing the body does not make a kernel
; that distributes over teams SPMD-izable: __kmpc_distribute_static_loop_* is
; called by one thread per block by construction, so knowing what the body
; reaches says nothing about running the call itself on every thread.
%struct.ident_t = type { i32, i32, i32, i32, ptr }
%struct.KernelEnvironmentTy = type { %struct.ConfigurationEnvironmentTy, ptr, ptr }
%struct.ConfigurationEnvironmentTy = type { i8, i8, i8, i32, i32, i32, i32, i32, i32 }
@0 = private unnamed_addr constant [23 x i8] c";unknown;unknown;0;0;;\00", align 1
@1 = private unnamed_addr constant %struct.ident_t { i32 0, i32 2, i32 0, i32 0, ptr @0 }, align 8
; The two environments start out identical. What witnesses this change is
; ConfigurationEnvironmentTy's second field, MayUseNestedParallelism: the checks
; have it set for @visible_body, whose body the analysis can look into and find
; the parallel region in, and left clear for @opaque_body.
@visible_body_kernel_environment = local_unnamed_addr constant %struct.KernelEnvironmentTy { %struct.ConfigurationEnvironmentTy { i8 1, i8 0, i8 1, i32 0, i32 0, i32 0, i32 0, i32 0, i32 0 }, ptr @1, ptr null }
@opaque_body_kernel_environment = local_unnamed_addr constant %struct.KernelEnvironmentTy { %struct.ConfigurationEnvironmentTy { i8 1, i8 0, i8 1, i32 0, i32 0, i32 0, i32 0, i32 0, i32 0 }, ptr @1, ptr null }
;.
; CHECK: @[[GLOB0:[0-9]+]] = private unnamed_addr constant [23 x i8] c"
; CHECK: @[[GLOB1:[0-9]+]] = private unnamed_addr constant %struct.ident_t { i32 0, i32 2, i32 0, i32 0, ptr @[[GLOB0]] }, align 8
; CHECK: @visible_body_kernel_environment = local_unnamed_addr constant %struct.KernelEnvironmentTy { %struct.ConfigurationEnvironmentTy { i8 0, i8 1, i8 1, i32 0, i32 0, i32 0, i32 0, i32 0, i32 0 }, ptr @[[GLOB1]], ptr null }
; CHECK: @opaque_body_kernel_environment = local_unnamed_addr constant %struct.KernelEnvironmentTy { %struct.ConfigurationEnvironmentTy { i8 0, i8 0, i8 1, i32 0, i32 0, i32 0, i32 0, i32 0, i32 0 }, ptr @[[GLOB1]], ptr null }
;.
define weak amdgpu_kernel void @visible_body(ptr %dyn, ptr %payload) #0 {
; CHECK-LABEL: define weak amdgpu_kernel void @visible_body(
; CHECK-SAME: ptr [[DYN:%.*]], ptr [[PAYLOAD:%.*]]) #[[ATTR0:[0-9]+]] {
; CHECK-NEXT: [[ENTRY:.*:]]
; CHECK-NEXT: [[WORKER_WORK_FN_ADDR:%.*]] = alloca ptr, align 8, addrspace(5)
; CHECK-NEXT: [[TMP0:%.*]] = call i32 @__kmpc_target_init(ptr @visible_body_kernel_environment, ptr [[DYN]])
; CHECK-NEXT: [[THREAD_IS_WORKER:%.*]] = icmp ne i32 [[TMP0]], -1
; CHECK-NEXT: br i1 [[THREAD_IS_WORKER]], label %[[IS_WORKER_CHECK:.*]], label %[[THREAD_USER_CODE_CHECK:.*]]
; CHECK: [[IS_WORKER_CHECK]]:
; CHECK-NEXT: [[MAX_TEAM_THREADS:%.*]] = call i32 @__kmpc_get_max_team_threads(i32 0)
; CHECK-NEXT: [[THREAD_IS_MAIN_OR_WORKER:%.*]] = icmp slt i32 [[TMP0]], [[MAX_TEAM_THREADS]]
; CHECK-NEXT: br i1 [[THREAD_IS_MAIN_OR_WORKER]], label %[[WORKER_STATE_MACHINE_BEGIN:.*]], label %[[WORKER_STATE_MACHINE_FINISHED:.*]]
; CHECK: [[WORKER_STATE_MACHINE_BEGIN]]:
; CHECK-NEXT: call void @__kmpc_barrier_simple_generic(ptr @[[GLOB1]], i32 [[TMP0]])
; CHECK-NEXT: [[WORKER_WORK_FN_ADDR_GENERIC:%.*]] = addrspacecast ptr addrspace(5) [[WORKER_WORK_FN_ADDR]] to ptr
; CHECK-NEXT: [[WORKER_IS_ACTIVE:%.*]] = call i1 @__kmpc_kernel_parallel(ptr [[WORKER_WORK_FN_ADDR_GENERIC]])
; CHECK-NEXT: [[WORKER_WORK_FN:%.*]] = load ptr, ptr [[WORKER_WORK_FN_ADDR_GENERIC]], align 8
; CHECK-NEXT: [[WORKER_IS_DONE:%.*]] = icmp eq ptr [[WORKER_WORK_FN]], null
; CHECK-NEXT: br i1 [[WORKER_IS_DONE]], label %[[WORKER_STATE_MACHINE_FINISHED]], label %[[WORKER_STATE_MACHINE_IS_ACTIVE_CHECK:.*]]
; CHECK: [[WORKER_STATE_MACHINE_FINISHED]]:
; CHECK-NEXT: ret void
; CHECK: [[WORKER_STATE_MACHINE_IS_ACTIVE_CHECK]]:
; CHECK-NEXT: br i1 [[WORKER_IS_ACTIVE]], label %[[WORKER_STATE_MACHINE_PARALLEL_REGION_CHECK:.*]], label %[[WORKER_STATE_MACHINE_DONE_BARRIER:.*]]
; CHECK: [[WORKER_STATE_MACHINE_PARALLEL_REGION_CHECK]]:
; CHECK-NEXT: br i1 true, label %[[WORKER_STATE_MACHINE_PARALLEL_REGION_EXECUTE:.*]], label %[[WORKER_STATE_MACHINE_PARALLEL_REGION_CHECK1:.*]]
; CHECK: [[WORKER_STATE_MACHINE_PARALLEL_REGION_EXECUTE]]:
; CHECK-NEXT: call void @outlined_wrapper(i16 0, i32 [[TMP0]])
; CHECK-NEXT: br label %[[WORKER_STATE_MACHINE_PARALLEL_REGION_END:.*]]
; CHECK: [[WORKER_STATE_MACHINE_PARALLEL_REGION_CHECK1]]:
; CHECK-NEXT: br label %[[WORKER_STATE_MACHINE_PARALLEL_REGION_END]]
; CHECK: [[WORKER_STATE_MACHINE_PARALLEL_REGION_END]]:
; CHECK-NEXT: call void @__kmpc_kernel_end_parallel()
; CHECK-NEXT: br label %[[WORKER_STATE_MACHINE_DONE_BARRIER]]
; CHECK: [[WORKER_STATE_MACHINE_DONE_BARRIER]]:
; CHECK-NEXT: call void @__kmpc_barrier_simple_generic(ptr @[[GLOB1]], i32 [[TMP0]])
; CHECK-NEXT: br label %[[WORKER_STATE_MACHINE_BEGIN]]
; CHECK: [[THREAD_USER_CODE_CHECK]]:
; CHECK-NEXT: [[EXEC_USER_CODE:%.*]] = icmp eq i32 [[TMP0]], -1
; CHECK-NEXT: br i1 [[EXEC_USER_CODE]], label %[[USER_CODE_ENTRY:.*]], label %[[WORKER_EXIT:.*]]
; CHECK: [[USER_CODE_ENTRY]]:
; CHECK-NEXT: call void @__kmpc_distribute_static_loop_4u(ptr @[[GLOB1]], ptr @loop_body, ptr [[PAYLOAD]], i32 100, i32 0, i8 0)
; CHECK-NEXT: call void @__kmpc_target_deinit()
; CHECK-NEXT: ret void
; CHECK: [[WORKER_EXIT]]:
; CHECK-NEXT: ret void
;
entry:
%0 = call i32 @__kmpc_target_init(ptr @visible_body_kernel_environment, ptr %dyn)
%exec_user_code = icmp eq i32 %0, -1
br i1 %exec_user_code, label %user_code.entry, label %worker.exit
user_code.entry:
call void @__kmpc_distribute_static_loop_4u(ptr @1, ptr @loop_body, ptr %payload, i32 100, i32 0, i8 0)
call void @__kmpc_target_deinit()
ret void
worker.exit:
ret void
}
define weak amdgpu_kernel void @opaque_body(ptr %dyn, ptr %payload) #0 {
; CHECK-LABEL: define weak amdgpu_kernel void @opaque_body(
; CHECK-SAME: ptr [[DYN:%.*]], ptr [[PAYLOAD:%.*]]) #[[ATTR0]] {
; CHECK-NEXT: [[ENTRY:.*:]]
; CHECK-NEXT: [[WORKER_WORK_FN_ADDR:%.*]] = alloca ptr, align 8, addrspace(5)
; CHECK-NEXT: [[TMP0:%.*]] = call i32 @__kmpc_target_init(ptr @opaque_body_kernel_environment, ptr [[DYN]])
; CHECK-NEXT: [[THREAD_IS_WORKER:%.*]] = icmp ne i32 [[TMP0]], -1
; CHECK-NEXT: br i1 [[THREAD_IS_WORKER]], label %[[IS_WORKER_CHECK:.*]], label %[[THREAD_USER_CODE_CHECK:.*]]
; CHECK: [[IS_WORKER_CHECK]]:
; CHECK-NEXT: [[MAX_TEAM_THREADS:%.*]] = call i32 @__kmpc_get_max_team_threads(i32 0)
; CHECK-NEXT: [[THREAD_IS_MAIN_OR_WORKER:%.*]] = icmp slt i32 [[TMP0]], [[MAX_TEAM_THREADS]]
; CHECK-NEXT: br i1 [[THREAD_IS_MAIN_OR_WORKER]], label %[[WORKER_STATE_MACHINE_BEGIN:.*]], label %[[WORKER_STATE_MACHINE_FINISHED:.*]]
; CHECK: [[WORKER_STATE_MACHINE_BEGIN]]:
; CHECK-NEXT: call void @__kmpc_barrier_simple_generic(ptr @[[GLOB1]], i32 [[TMP0]])
; CHECK-NEXT: [[WORKER_WORK_FN_ADDR_GENERIC:%.*]] = addrspacecast ptr addrspace(5) [[WORKER_WORK_FN_ADDR]] to ptr
; CHECK-NEXT: [[WORKER_IS_ACTIVE:%.*]] = call i1 @__kmpc_kernel_parallel(ptr [[WORKER_WORK_FN_ADDR_GENERIC]])
; CHECK-NEXT: [[WORKER_WORK_FN:%.*]] = load ptr, ptr [[WORKER_WORK_FN_ADDR_GENERIC]], align 8
; CHECK-NEXT: [[WORKER_IS_DONE:%.*]] = icmp eq ptr [[WORKER_WORK_FN]], null
; CHECK-NEXT: br i1 [[WORKER_IS_DONE]], label %[[WORKER_STATE_MACHINE_FINISHED]], label %[[WORKER_STATE_MACHINE_IS_ACTIVE_CHECK:.*]]
; CHECK: [[WORKER_STATE_MACHINE_FINISHED]]:
; CHECK-NEXT: ret void
; CHECK: [[WORKER_STATE_MACHINE_IS_ACTIVE_CHECK]]:
; CHECK-NEXT: br i1 [[WORKER_IS_ACTIVE]], label %[[WORKER_STATE_MACHINE_PARALLEL_REGION_FALLBACK_EXECUTE:.*]], label %[[WORKER_STATE_MACHINE_DONE_BARRIER:.*]]
; CHECK: [[WORKER_STATE_MACHINE_PARALLEL_REGION_FALLBACK_EXECUTE]]:
; CHECK-NEXT: call void [[WORKER_WORK_FN]](i16 0, i32 [[TMP0]])
; CHECK-NEXT: br label %[[WORKER_STATE_MACHINE_PARALLEL_REGION_END:.*]]
; CHECK: [[WORKER_STATE_MACHINE_PARALLEL_REGION_END]]:
; CHECK-NEXT: call void @__kmpc_kernel_end_parallel()
; CHECK-NEXT: br label %[[WORKER_STATE_MACHINE_DONE_BARRIER]]
; CHECK: [[WORKER_STATE_MACHINE_DONE_BARRIER]]:
; CHECK-NEXT: call void @__kmpc_barrier_simple_generic(ptr @[[GLOB1]], i32 [[TMP0]])
; CHECK-NEXT: br label %[[WORKER_STATE_MACHINE_BEGIN]]
; CHECK: [[THREAD_USER_CODE_CHECK]]:
; CHECK-NEXT: [[EXEC_USER_CODE:%.*]] = icmp eq i32 [[TMP0]], -1
; CHECK-NEXT: br i1 [[EXEC_USER_CODE]], label %[[USER_CODE_ENTRY:.*]], label %[[WORKER_EXIT:.*]]
; CHECK: [[USER_CODE_ENTRY]]:
; CHECK-NEXT: call void @__kmpc_distribute_static_loop_4u(ptr @[[GLOB1]], ptr @opaque_loop_body, ptr [[PAYLOAD]], i32 100, i32 0, i8 0)
; CHECK-NEXT: call void @__kmpc_target_deinit()
; CHECK-NEXT: ret void
; CHECK: [[WORKER_EXIT]]:
; CHECK-NEXT: ret void
;
entry:
%0 = call i32 @__kmpc_target_init(ptr @opaque_body_kernel_environment, ptr %dyn)
%exec_user_code = icmp eq i32 %0, -1
br i1 %exec_user_code, label %user_code.entry, label %worker.exit
user_code.entry:
call void @__kmpc_distribute_static_loop_4u(ptr @1, ptr @opaque_loop_body, ptr %payload, i32 100, i32 0, i8 0)
call void @__kmpc_target_deinit()
ret void
worker.exit:
ret void
}
define internal void @loop_body(i32 %iv, ptr %payload) #1 {
; CHECK-LABEL: define internal void @loop_body(
; CHECK-SAME: i32 [[IV:%.*]], ptr [[PAYLOAD:%.*]]) #[[ATTR1:[0-9]+]] {
; CHECK-NEXT: [[ENTRY:.*:]]
; CHECK-NEXT: [[TID:%.*]] = call i32 @__kmpc_global_thread_num(ptr @[[GLOB1]]) #[[ATTR1]]
; CHECK-NEXT: call void @__kmpc_parallel_60(ptr @[[GLOB1]], i32 [[TID]], i32 1, i32 -1, i32 -1, ptr @outlined, ptr @outlined_wrapper, ptr [[PAYLOAD]], i64 1, i32 0)
; CHECK-NEXT: ret void
;
entry:
%tid = call i32 @__kmpc_global_thread_num(ptr @1)
call void @__kmpc_parallel_60(ptr @1, i32 %tid, i32 1, i32 -1, i32 -1, ptr @outlined, ptr @outlined_wrapper, ptr %payload, i64 1, i32 0)
ret void
}
define internal void @outlined(ptr noalias %tid.addr, ptr noalias %zero.addr) #1 {
; CHECK-LABEL: define internal void @outlined(
; CHECK-SAME: ptr noalias [[TID_ADDR:%.*]], ptr noalias [[ZERO_ADDR:%.*]]) #[[ATTR2:[0-9]+]] {
; CHECK-NEXT: [[ENTRY:.*:]]
; CHECK-NEXT: [[TID:%.*]] = load i32, ptr [[TID_ADDR]], align 4
; CHECK-NEXT: [[ZERO:%.*]] = load i32, ptr [[ZERO_ADDR]], align 4
; CHECK-NEXT: call void @p0(i32 [[TID]], i32 [[ZERO]]) #[[ATTR1]]
; CHECK-NEXT: ret void
;
entry:
%tid = load i32, ptr %tid.addr, align 4
%zero = load i32, ptr %zero.addr, align 4
call void @p0(i32 %tid, i32 %zero)
ret void
}
define internal void @outlined_wrapper(i16 zeroext %0, i32 %1) #1 {
; CHECK-LABEL: define internal void @outlined_wrapper(
; CHECK-SAME: i16 zeroext [[TMP0:%.*]], i32 [[TMP1:%.*]]) #[[ATTR2]] {
; CHECK-NEXT: [[ENTRY:.*:]]
; CHECK-NEXT: [[TID_ADDR:%.*]] = alloca i32, align 4, addrspace(5)
; CHECK-NEXT: [[TID_ADDR_CAST:%.*]] = addrspacecast ptr addrspace(5) [[TID_ADDR]] to ptr
; CHECK-NEXT: [[ZERO_ADDR:%.*]] = alloca i32, align 4, addrspace(5)
; CHECK-NEXT: [[ZERO_ADDR_CAST:%.*]] = addrspacecast ptr addrspace(5) [[ZERO_ADDR]] to ptr
; CHECK-NEXT: store i32 [[TMP1]], ptr addrspace(5) [[TID_ADDR]], align 4
; CHECK-NEXT: store i32 0, ptr addrspace(5) [[ZERO_ADDR]], align 4
; CHECK-NEXT: call void @outlined(ptr [[TID_ADDR_CAST]], ptr [[ZERO_ADDR_CAST]]) #[[ATTR1]]
; CHECK-NEXT: ret void
;
entry:
%tid.addr = alloca i32, align 4, addrspace(5)
%tid.addr.cast = addrspacecast ptr addrspace(5) %tid.addr to ptr
%zero.addr = alloca i32, align 4, addrspace(5)
%zero.addr.cast = addrspacecast ptr addrspace(5) %zero.addr to ptr
store i32 %1, ptr %tid.addr.cast, align 4
store i32 0, ptr %zero.addr.cast, align 4
call void @outlined(ptr %tid.addr.cast, ptr %zero.addr.cast)
ret void
}
define weak i32 @__kmpc_target_init(ptr %0, ptr %1) {
; CHECK-LABEL: define weak i32 @__kmpc_target_init(
; CHECK-SAME: ptr [[TMP0:%.*]], ptr [[TMP1:%.*]]) {
; CHECK-NEXT: ret i32 0
;
ret i32 0
}
declare void @opaque_loop_body(i32, ptr)
declare void @p0(i32, i32) #1
declare void @__kmpc_target_deinit()
declare i32 @__kmpc_global_thread_num(ptr)
declare void @__kmpc_parallel_60(ptr, i32, i32, i32, i32, ptr, ptr, ptr, i64, i32)
declare void @__kmpc_distribute_static_loop_4u(ptr, ptr, ptr, i32, i32, i8)
attributes #0 = { "kernel" }
attributes #1 = { convergent nounwind }
!llvm.module.flags = !{!0, !1}
!0 = !{i32 7, !"openmp", i32 50}
!1 = !{i32 7, !"openmp-device", i32 50}
;.
; CHECK: attributes #[[ATTR0]] = { "kernel" }
; CHECK: attributes #[[ATTR1]] = { nounwind }
; CHECK: attributes #[[ATTR2]] = { convergent nounwind }
; CHECK: attributes #[[ATTR3:[0-9]+]] = { alwaysinline }
;.
; CHECK: [[META0:![0-9]+]] = !{i32 7, !"openmp", i32 50}
; CHECK: [[META1:![0-9]+]] = !{i32 7, !"openmp-device", i32 50}
; CHECK: [[META2:![0-9]+]] = !{[[META3:![0-9]+]]}
; CHECK: [[META3]] = !{i64 1, i64 -1, i64 -1, i1 false}
;.