From fd29f5508d4b90cbfca0ffb2a9be50b8c04ae918 Mon Sep 17 00:00:00 2001 From: Tomas Matheson Date: Mon, 24 Aug 2026 11:07:25 +0100 Subject: [PATCH] [OpenMP] Seed noalias for host callback captures The generic Attributor can derive noalias and the related lifetime facts for callback-mapped capture arguments, but OpenMPOpt does not seed AANoAlias in its restricted host run. Consequently capture-container loads remain in loops under dereferenceable-at-point semantics. Seed AANoAlias for pointer arguments mapped to broker operands by callback metadata. Existing call-site reasoning checks every callback and direct call site and rejects escaped or aliased slots. LICM can then hoist immutable capture-container loads without changing the callback ABI or adding an OpenMP-specific hoisting transform. Add LLVM, Clang, and Flang coverage for pointer, scalar, and aggregate captures together with escaped, duplicated, unmapped, and unknown-use negatives. --- clang/test/OpenMP/bug54082.c | 20 +++--- .../test/OpenMP/callback_capture_lifetime.cpp | 29 ++++++++ .../OpenMP/callback-capture-lifetime.f90 | 40 +++++++++++ llvm/lib/Transforms/IPO/OpenMPOpt.cpp | 21 +++++- .../OpenMP/callback-capture-licm.ll | 67 +++++++++++++++++++ .../Transforms/OpenMP/callback-noalias.ll | 34 ++++++++++ .../Transforms/OpenMP/parallel_deletion.ll | 10 +-- .../OpenMP/parallel_region_merging.ll | 12 ++-- 8 files changed, 209 insertions(+), 24 deletions(-) create mode 100644 clang/test/OpenMP/callback_capture_lifetime.cpp create mode 100644 flang/test/Integration/OpenMP/callback-capture-lifetime.f90 create mode 100644 llvm/test/Transforms/OpenMP/callback-capture-licm.ll create mode 100644 llvm/test/Transforms/OpenMP/callback-noalias.ll diff --git a/clang/test/OpenMP/bug54082.c b/clang/test/OpenMP/bug54082.c index 6b8c93e9ffc96..b317a88d8aacb 100644 --- a/clang/test/OpenMP/bug54082.c +++ b/clang/test/OpenMP/bug54082.c @@ -69,43 +69,41 @@ void foo() { // CHECK-NEXT: [[X_TRAITS:%.*]] = alloca [1 x [[STRUCT_OMP_ALLOCTRAIT_T:%.*]]], align 16 // CHECK-NEXT: [[X_ALLOC:%.*]] = alloca i64, align 8 // CHECK-NEXT: call void @llvm.lifetime.start.p0(ptr nonnull [[X_TRAITS]]) #[[ATTR4:[0-9]+]] -// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr noundef nonnull align 16 dereferenceable(16) [[X_TRAITS]], ptr noundef nonnull align 16 dereferenceable(16) @__const.foo.x_traits, i64 16, i1 false) +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr noundef nonnull writeonly align 16 dereferenceable(16) [[X_TRAITS]], ptr noundef nonnull readonly align 16 dereferenceable(16) @__const.foo.x_traits, i64 16, i1 false) // CHECK-NEXT: call void @llvm.lifetime.start.p0(ptr nonnull [[X_ALLOC]]) #[[ATTR4]] // CHECK-NEXT: [[CALL:%.*]] = call i64 @omp_init_allocator(i64 noundef 0, i32 noundef 1, ptr noundef nonnull [[X_TRAITS]]) #[[ATTR4]] // CHECK-NEXT: store i64 [[CALL]], ptr [[X_ALLOC]], align 8, !tbaa [[LONG_TBAA7:![0-9]+]] -// CHECK-NEXT: call void (ptr, i32, ptr, ...) @__kmpc_fork_call(ptr nonnull @[[GLOB2:[0-9]+]], i32 1, ptr nonnull @foo.omp_outlined, ptr nonnull [[X_ALLOC]]) +// CHECK-NEXT: call void (ptr, i32, ptr, ...) @__kmpc_fork_call(ptr nonnull @[[GLOB2:[0-9]+]], i32 1, ptr nonnull @foo.omp_outlined, ptr noalias nonnull readonly captures(none) [[X_ALLOC]]) // CHECK-NEXT: call void @llvm.lifetime.end.p0(ptr nonnull [[X_ALLOC]]) #[[ATTR4]] // CHECK-NEXT: call void @llvm.lifetime.end.p0(ptr nonnull [[X_TRAITS]]) #[[ATTR4]] // CHECK-NEXT: ret void // // // CHECK-LABEL: define internal void @foo.omp_outlined( -// CHECK-SAME: ptr noalias nofree noundef readonly captures(none) [[DOTGLOBAL_TID_:%.*]], ptr noalias nofree readnone captures(none) [[DOTBOUND_TID_:%.*]], ptr nofree noundef nonnull readonly align 8 captures(none) dereferenceable(8) [[X_ALLOC:%.*]]) #[[ATTR3:[0-9]+]] { +// CHECK-SAME: ptr noalias nofree noundef readonly captures(none) [[DOTGLOBAL_TID_:%.*]], ptr noalias nofree readnone captures(none) [[DOTBOUND_TID_:%.*]], ptr noalias noundef nonnull readonly align 8 captures(none) dereferenceable(8) [[X_ALLOC:%.*]]) #[[ATTR3:[0-9]+]] { // CHECK-NEXT: [[ENTRY:.*:]] // CHECK-NEXT: [[DOTOMP_LB:%.*]] = alloca i32, align 4 // CHECK-NEXT: [[DOTOMP_UB:%.*]] = alloca i32, align 4 // CHECK-NEXT: [[DOTOMP_STRIDE:%.*]] = alloca i32, align 4 // CHECK-NEXT: [[DOTOMP_IS_LAST:%.*]] = alloca i32, align 4 -// CHECK-NEXT: call void @llvm.lifetime.start.p0(ptr nonnull [[DOTOMP_LB]]) #[[ATTR4]] +// CHECK-NEXT: call void @llvm.lifetime.start.p0(ptr nonnull [[DOTOMP_LB]]) #[[ATTR6:[0-9]+]] // CHECK-NEXT: store i32 0, ptr [[DOTOMP_LB]], align 4, !tbaa [[INT_TBAA9:![0-9]+]] -// CHECK-NEXT: call void @llvm.lifetime.start.p0(ptr nonnull [[DOTOMP_UB]]) #[[ATTR4]] +// CHECK-NEXT: call void @llvm.lifetime.start.p0(ptr nonnull [[DOTOMP_UB]]) #[[ATTR6]] // CHECK-NEXT: store i32 1023, ptr [[DOTOMP_UB]], align 4, !tbaa [[INT_TBAA9]] -// CHECK-NEXT: call void @llvm.lifetime.start.p0(ptr nonnull [[DOTOMP_STRIDE]]) #[[ATTR4]] +// CHECK-NEXT: call void @llvm.lifetime.start.p0(ptr nonnull [[DOTOMP_STRIDE]]) #[[ATTR6]] // CHECK-NEXT: store i32 1, ptr [[DOTOMP_STRIDE]], align 4, !tbaa [[INT_TBAA9]] -// CHECK-NEXT: call void @llvm.lifetime.start.p0(ptr nonnull [[DOTOMP_IS_LAST]]) #[[ATTR4]] +// CHECK-NEXT: call void @llvm.lifetime.start.p0(ptr nonnull [[DOTOMP_IS_LAST]]) #[[ATTR6]] // CHECK-NEXT: store i32 0, ptr [[DOTOMP_IS_LAST]], align 4, !tbaa [[INT_TBAA9]] // CHECK-NEXT: [[TMP0:%.*]] = load i32, ptr [[DOTGLOBAL_TID_]], align 4, !tbaa [[INT_TBAA9]] // CHECK-NEXT: [[TMP1:%.*]] = load i64, ptr [[X_ALLOC]], align 8, !tbaa [[LONG_TBAA7]] // CHECK-NEXT: [[CONV:%.*]] = inttoptr i64 [[TMP1]] to ptr -// CHECK-NEXT: [[DOTX__VOID_ADDR:%.*]] = tail call ptr @__kmpc_alloc(i32 [[TMP0]], i64 8, ptr [[CONV]]) +// CHECK-NEXT: [[DOTX__VOID_ADDR:%.*]] = tail call ptr @__kmpc_alloc(i32 [[TMP0]], i64 8, ptr [[CONV]]) #[[ATTR4]] // CHECK-NEXT: call void @__kmpc_for_static_init_4(ptr nonnull @[[GLOB1:[0-9]+]], i32 [[TMP0]], i32 34, ptr nonnull [[DOTOMP_IS_LAST]], ptr nonnull [[DOTOMP_LB]], ptr nonnull [[DOTOMP_UB]], ptr nonnull [[DOTOMP_STRIDE]], i32 1, i32 1) // CHECK-NEXT: [[TMP2:%.*]] = load i32, ptr [[DOTOMP_UB]], align 4, !tbaa [[INT_TBAA9]] // CHECK-NEXT: [[COND:%.*]] = call i32 @llvm.smin.i32(i32 [[TMP2]], i32 1023) // CHECK-NEXT: store i32 [[COND]], ptr [[DOTOMP_UB]], align 4, !tbaa [[INT_TBAA9]] // CHECK-NEXT: call void @__kmpc_for_static_fini(ptr nonnull @[[GLOB1]], i32 [[TMP0]]) -// CHECK-NEXT: [[TMP3:%.*]] = load i64, ptr [[X_ALLOC]], align 8, !tbaa [[LONG_TBAA7]] -// CHECK-NEXT: [[CONV5:%.*]] = inttoptr i64 [[TMP3]] to ptr -// CHECK-NEXT: call void @__kmpc_free(i32 [[TMP0]], ptr [[DOTX__VOID_ADDR]], ptr [[CONV5]]) +// CHECK-NEXT: call void @__kmpc_free(i32 [[TMP0]], ptr [[DOTX__VOID_ADDR]], ptr [[CONV]]) // CHECK-NEXT: call void @llvm.lifetime.end.p0(ptr nonnull [[DOTOMP_IS_LAST]]) #[[ATTR4]] // CHECK-NEXT: call void @llvm.lifetime.end.p0(ptr nonnull [[DOTOMP_STRIDE]]) #[[ATTR4]] // CHECK-NEXT: call void @llvm.lifetime.end.p0(ptr nonnull [[DOTOMP_UB]]) #[[ATTR4]] diff --git a/clang/test/OpenMP/callback_capture_lifetime.cpp b/clang/test/OpenMP/callback_capture_lifetime.cpp new file mode 100644 index 0000000000000..4e75c53197c93 --- /dev/null +++ b/clang/test/OpenMP/callback_capture_lifetime.cpp @@ -0,0 +1,29 @@ +// NOTE: Assertions have been autogenerated by utils/update_cc_test_checks.py UTC_ARGS: --functions ".*omp_outlined$" --filter "define internal void" --filter "load ptr, ptr" --filter "@opaque" --filter "getelementptr" --include-generated-funcs --version 6 +// Verify end-to-end that Clang's host OpenMP pipeline infers noalias for a +// captured pointer container and hoists its load out of a loop containing an +// unrelated call. +// RUN: %clang_cc1 -triple x86_64-unknown-linux-gnu -O2 -fopenmp -x c++ \ +// RUN: -emit-llvm -o - %s | FileCheck %s + +extern bool enabled[64]; +extern "C" void opaque(); +extern "C" void use(double); + +void use_enabled(double *values) { +#pragma omp parallel + for (int i = 0; i < 64; ++i) { + opaque(); + if (enabled[i]) + use(values[i]); + } +} +// CHECK-LABEL: define dso_local void @_Z11use_enabledPd( +// CHECK-SAME: ptr noundef [[VALUES:%.*]]) local_unnamed_addr #[[ATTR0:[0-9]+]] { +// +// CHECK-LABEL: define internal void @_Z11use_enabledPd.omp_outlined( +// CHECK-SAME: ptr noalias nofree readnone captures(none) [[DOTGLOBAL_TID_:%.*]], ptr noalias nofree readnone captures(none) [[DOTBOUND_TID_:%.*]], ptr noalias noundef nonnull readonly align 8 captures(none) dereferenceable(8) [[VALUES:%.*]]) #[[ATTR1:[0-9]+]] { +// CHECK: [[TMP0:%.*]] = load ptr, ptr [[VALUES]], align 8 +// CHECK: tail call void @opaque() #[[ATTR3:[0-9]+]] +// CHECK: [[ARRAYIDX:%.*]] = getelementptr inbounds nuw i8, ptr @enabled, i64 [[INDVARS_IV:%.*]] +// CHECK: [[ARRAYIDX2:%.*]] = getelementptr inbounds nuw [8 x i8], ptr [[TMP0]], i64 [[INDVARS_IV]] +// diff --git a/flang/test/Integration/OpenMP/callback-capture-lifetime.f90 b/flang/test/Integration/OpenMP/callback-capture-lifetime.f90 new file mode 100644 index 0000000000000..b824ddcba00c1 --- /dev/null +++ b/flang/test/Integration/OpenMP/callback-capture-lifetime.f90 @@ -0,0 +1,40 @@ +!===----------------------------------------------------------------------===! +! This directory can be used to add Integration tests involving multiple +! stages of the compiler (for eg. from Fortran to LLVM IR). It should not +! contain executable tests. We should only add tests here sparingly and only +! if there is no other way to test. Repeat this message in each test that is +! added to this directory and sub-directories. +!===----------------------------------------------------------------------===! + +! Verify that host OpenMPOpt infers noalias for Flang's aggregate capture +! container, allowing both captured addresses to be loaded before an opaque +! call. +! +! RUN: %flang_fc1 -O2 -fopenmp -emit-llvm %s -o - | FileCheck %s + +subroutine capture_lifetime(first, second) + integer(8), intent(in) :: first, second + + interface + subroutine opaque() + end subroutine + subroutine use_value(value) + integer(8), value :: value + end subroutine + end interface + + !$omp parallel shared(first, second) + call opaque() + call use_value(first) + call use_value(second) + !$omp end parallel +end subroutine + +! CHECK-LABEL: define internal void @capture_lifetime_..omp_par( +! CHECK-SAME: ptr noalias readonly captures(none) [[CAPTURES:%.*]]) +! CHECK: [[FIRST:%.*]] = load ptr, ptr [[CAPTURES]], align 8 +! CHECK-NEXT: [[SECOND_FIELD:%.*]] = getelementptr i8, ptr [[CAPTURES]], i64 8 +! CHECK-NEXT: [[SECOND:%.*]] = load ptr, ptr [[SECOND_FIELD]], align 8 +! CHECK-NEXT: tail call void @opaque_() +! CHECK: load i64, ptr [[FIRST]], align 8 +! CHECK: load i64, ptr [[SECOND]], align 8 diff --git a/llvm/lib/Transforms/IPO/OpenMPOpt.cpp b/llvm/lib/Transforms/IPO/OpenMPOpt.cpp index 22a5efb402504..0fff56cf17ced 100644 --- a/llvm/lib/Transforms/IPO/OpenMPOpt.cpp +++ b/llvm/lib/Transforms/IPO/OpenMPOpt.cpp @@ -5627,10 +5627,27 @@ void OpenMPOpt::registerAAs(bool IsModulePass) { } } + // Seed the generic noalias deduction for callback-mapped pointer arguments + // in host regions. It will inspect every callback and direct call site before + // manifesting the attribute. + if (!isOpenMPDevice(M)) { + for (Function *F : SCC) { + if (F->isDeclaration()) + continue; + for (Use &U : F->uses()) { + AbstractCallSite ACS(&U); + if (!ACS || !ACS.isCallbackCall()) + continue; + for (Argument &Arg : F->args()) + if (Arg.getType()->isPointerTy() && ACS.getCallArgOperandNo(Arg) >= 0) + A.getOrCreateAAFor(IRPosition::argument(Arg)); + } + } + return; + } + // Create an ExecutionDomain AA for every function and a HeapToStack AA for // every function if there is a device kernel. - if (!isOpenMPDevice(M)) - return; for (auto *F : SCC) { if (F->isDeclaration()) diff --git a/llvm/test/Transforms/OpenMP/callback-capture-licm.ll b/llvm/test/Transforms/OpenMP/callback-capture-licm.ll new file mode 100644 index 0000000000000..086ec1972393c --- /dev/null +++ b/llvm/test/Transforms/OpenMP/callback-capture-licm.ll @@ -0,0 +1,67 @@ +; NOTE: Assertions have been autogenerated by utils/update_test_checks.py UTC_ARGS: --function outlined --version 6 +; Verify that noalias inferred for host callback capture storage lets LICM +; hoist its load out of a loop containing an unknown call. +; +; RUN: opt -passes='openmp-opt,function(loop-mssa(licm))' -S < %s | FileCheck %s + +target triple = "aarch64-unknown-linux-gnu" + +define void @run(ptr %value) { + %capture = alloca ptr, align 8 + store ptr %value, ptr %capture, align 8 + call void (ptr, i32, ptr, ...) @__kmpc_fork_call(ptr null, i32 1, ptr @outlined, ptr %capture) + ret void +} + +define internal void @outlined(ptr %global_tid, ptr %bound_tid, ptr align 8 dereferenceable(8) %capture) { +; CHECK-LABEL: define internal void @outlined( +; CHECK-SAME: ptr [[GLOBAL_TID:%.*]], ptr [[BOUND_TID:%.*]], ptr noalias readonly align 8 captures(none) dereferenceable(8) [[CAPTURE:%.*]]) { +; CHECK-NEXT: [[ENTRY:.*]]: +; CHECK-NEXT: [[VALUE:%.*]] = load ptr, ptr [[CAPTURE]], align 8 +; CHECK-NEXT: br label %[[LOOP:.*]] +; CHECK: [[LOOP]]: +; CHECK-NEXT: [[INDEX:%.*]] = phi i64 [ 0, %[[ENTRY]] ], [ [[NEXT:%.*]], %[[LATCH:.*]] ] +; CHECK-NEXT: call void @opaque() +; CHECK-NEXT: [[ENABLED:%.*]] = icmp eq i64 [[INDEX]], 7 +; CHECK-NEXT: br i1 [[ENABLED]], label %[[IF_THEN:.*]], label %[[LATCH]] +; CHECK: [[IF_THEN]]: +; CHECK-NEXT: call void @use(ptr [[VALUE]]) +; CHECK-NEXT: br label %[[LATCH]] +; CHECK: [[LATCH]]: +; CHECK-NEXT: [[NEXT]] = add nuw nsw i64 [[INDEX]], 1 +; CHECK-NEXT: [[DONE:%.*]] = icmp eq i64 [[NEXT]], 64 +; CHECK-NEXT: br i1 [[DONE]], label %[[EXIT:.*]], label %[[LOOP]] +; CHECK: [[EXIT]]: +; CHECK-NEXT: ret void +; +entry: + br label %loop + +loop: + %index = phi i64 [ 0, %entry ], [ %next, %latch ] + call void @opaque() + %enabled = icmp eq i64 %index, 7 + br i1 %enabled, label %if.then, label %latch + +if.then: + %value = load ptr, ptr %capture, align 8 + call void @use(ptr %value) + br label %latch + +latch: + %next = add nuw nsw i64 %index, 1 + %done = icmp eq i64 %next, 64 + br i1 %done, label %exit, label %loop + +exit: + ret void +} + +declare !callback !0 void @__kmpc_fork_call(ptr, i32, ptr, ...) +declare void @opaque() +declare void @use(ptr) memory(none) + +!0 = !{!1} +!1 = !{i64 2, i64 -1, i64 -1, i1 true} +!llvm.module.flags = !{!2} +!2 = !{i32 7, !"openmp", i32 51} diff --git a/llvm/test/Transforms/OpenMP/callback-noalias.ll b/llvm/test/Transforms/OpenMP/callback-noalias.ll new file mode 100644 index 0000000000000..aee80f31a7235 --- /dev/null +++ b/llvm/test/Transforms/OpenMP/callback-noalias.ll @@ -0,0 +1,34 @@ +; NOTE: Assertions have been autogenerated by utils/update_test_checks.py UTC_ARGS: --function outlined --version 6 +; Verify that host OpenMPOpt seeds noalias reasoning for a callback argument +; mapped to capture storage by callback metadata. +; +; RUN: opt -passes=openmp-opt -S < %s | FileCheck %s + +target triple = "aarch64-unknown-linux-gnu" + +define void @run(i64 %value) { + %capture = alloca i64, align 8 + store i64 %value, ptr %capture, align 8 + call void (ptr, i32, ptr, ...) @__kmpc_fork_call(ptr null, i32 1, ptr @outlined, ptr %capture) + ret void +} + +define internal void @outlined(ptr %global_tid, ptr %bound_tid, ptr align 8 dereferenceable(8) %capture) { +; CHECK-LABEL: define internal void @outlined( +; CHECK-SAME: ptr [[GLOBAL_TID:%.*]], ptr [[BOUND_TID:%.*]], ptr noalias readonly align 8 captures(none) dereferenceable(8) [[CAPTURE:%.*]]) #[[ATTR1:[0-9]+]] { +; CHECK-NEXT: [[VALUE:%.*]] = load i64, ptr [[CAPTURE]], align 8 +; CHECK-NEXT: call void @use(i64 [[VALUE]]) #[[ATTR3:[0-9]+]] +; CHECK-NEXT: ret void +; + %value = load i64, ptr %capture, align 8 + call void @use(i64 %value) + ret void +} + +declare !callback !0 void @__kmpc_fork_call(ptr, i32, ptr, ...) +declare void @use(i64) memory(none) + +!0 = !{!1} +!1 = !{i64 2, i64 -1, i64 -1, i1 true} +!llvm.module.flags = !{!2} +!2 = !{i32 7, !"openmp", i32 51} diff --git a/llvm/test/Transforms/OpenMP/parallel_deletion.ll b/llvm/test/Transforms/OpenMP/parallel_deletion.ll index c362cebeda9b0..7003f81756ac9 100644 --- a/llvm/test/Transforms/OpenMP/parallel_deletion.ll +++ b/llvm/test/Transforms/OpenMP/parallel_deletion.ll @@ -386,7 +386,7 @@ define internal void @.omp_outlined..4(ptr noalias %.global_tid., ptr noalias %. ; CHECK-SAME: (ptr noalias nofree noundef nonnull readonly align 4 captures(none) dereferenceable(4) [[DOTGLOBAL_TID_:%.*]], ptr noalias nofree readnone captures(none) [[DOTBOUND_TID_:%.*]], ptr nofree noundef nonnull align 4 captures(none) dereferenceable(4) [[A:%.*]]) { ; CHECK-NEXT: entry: ; CHECK-NEXT: [[TMP:%.*]] = load i32, ptr [[DOTGLOBAL_TID_]], align 4, !invariant.load [[META1:![0-9]+]] -; CHECK-NEXT: [[TMP1:%.*]] = call i32 @__kmpc_master(ptr noundef nonnull @[[GLOB0]], i32 [[TMP]]) +; CHECK-NEXT: [[TMP1:%.*]] = call i32 @__kmpc_master(ptr noundef nonnull @[[GLOB0]], i32 [[TMP]]) #[[ATTR9:[0-9]+]] ; CHECK-NEXT: [[TMP2:%.*]] = icmp eq i32 [[TMP1]], 0 ; CHECK-NEXT: br i1 [[TMP2]], label [[OMP_IF_END:%.*]], label [[OMP_IF_THEN:%.*]] ; CHECK: omp_if.then: @@ -459,7 +459,7 @@ define internal void @.omp_outlined..5(ptr noalias %.global_tid., ptr noalias %. ; CHECK-NEXT: entry: ; CHECK-NEXT: [[OMP_GLOBAL_THREAD_NUM:%.*]] = call i32 @__kmpc_global_thread_num(ptr noundef nonnull @[[GLOB0]]) #[[ATTR19]] ; CHECK-NEXT: [[TMP:%.*]] = load i32, ptr [[DOTGLOBAL_TID_]], align 4, !invariant.load [[META1]] -; CHECK-NEXT: [[TMP1:%.*]] = call i32 @__kmpc_single(ptr noundef nonnull @[[GLOB0]], i32 [[TMP]]) +; CHECK-NEXT: [[TMP1:%.*]] = call i32 @__kmpc_single(ptr noundef nonnull @[[GLOB0]], i32 [[TMP]]) #[[ATTR9]] ; CHECK-NEXT: [[TMP2:%.*]] = icmp eq i32 [[TMP1]], 0 ; CHECK-NEXT: br i1 [[TMP2]], label [[OMP_IF_END:%.*]], label [[OMP_IF_THEN:%.*]] ; CHECK: omp_if.then: @@ -527,7 +527,7 @@ omp_if.end: ; preds = %entry, %omp_if.then define internal void @.omp_outlined..6(ptr noalias %.global_tid., ptr noalias %.bound_tid., ptr dereferenceable(4) %a) { ; CHECK-LABEL: define {{[^@]+}}@.omp_outlined..6 -; CHECK-SAME: (ptr noalias nofree noundef nonnull readonly align 4 captures(none) dereferenceable(4) [[DOTGLOBAL_TID_:%.*]], ptr noalias nofree readnone captures(none) [[DOTBOUND_TID_:%.*]], ptr nofree noundef nonnull align 4 captures(none) dereferenceable(4) [[A:%.*]]) { +; CHECK-SAME: (ptr noalias nofree noundef nonnull readonly align 4 captures(none) dereferenceable(4) [[DOTGLOBAL_TID_:%.*]], ptr noalias nofree readnone captures(none) [[DOTBOUND_TID_:%.*]], ptr nofree noundef nonnull align 4 captures(none) dereferenceable(4) [[A:%.*]]) #[[ATTR9]] { ; CHECK-NEXT: entry: ; CHECK-NEXT: [[A1:%.*]] = alloca i32, align 4 ; CHECK-NEXT: [[DOTOMP_REDUCTION_RED_LIST:%.*]] = alloca [1 x ptr], align 8 @@ -535,7 +535,7 @@ define internal void @.omp_outlined..6(ptr noalias %.global_tid., ptr noalias %. ; CHECK-NEXT: store i32 1, ptr [[A1]], align 4 ; CHECK-NEXT: store ptr [[A1]], ptr [[DOTOMP_REDUCTION_RED_LIST]], align 8 ; CHECK-NEXT: [[TMP2:%.*]] = load i32, ptr [[DOTGLOBAL_TID_]], align 4, !invariant.load [[META1]] -; CHECK-NEXT: [[TMP4:%.*]] = call i32 @__kmpc_reduce_nowait(ptr noundef nonnull @[[GLOB2:[0-9]+]], i32 [[TMP2]], i32 noundef 1, i64 noundef 8, ptr noundef nonnull align 8 [[DOTOMP_REDUCTION_RED_LIST]], ptr noundef nonnull @.omp.reduction.reduction_func, ptr noundef nonnull @.gomp_critical_user_.reduction.var) +; CHECK-NEXT: [[TMP4:%.*]] = call i32 @__kmpc_reduce_nowait(ptr noundef nonnull @[[GLOB2:[0-9]+]], i32 [[TMP2]], i32 noundef 1, i64 noundef 8, ptr noundef nonnull align 8 [[DOTOMP_REDUCTION_RED_LIST]], ptr noundef nonnull @.omp.reduction.reduction_func, ptr noundef nonnull @.gomp_critical_user_.reduction.var) #[[ATTR9]] ; CHECK-NEXT: switch i32 [[TMP4]], label [[DOTOMP_REDUCTION_DEFAULT:%.*]] [ ; CHECK-NEXT: i32 1, label [[DOTOMP_REDUCTION_CASE1:%.*]] ; CHECK-NEXT: i32 2, label [[DOTOMP_REDUCTION_CASE2:%.*]] @@ -545,7 +545,7 @@ define internal void @.omp_outlined..6(ptr noalias %.global_tid., ptr noalias %. ; CHECK-NEXT: [[TMP6:%.*]] = load i32, ptr [[A1]], align 4 ; CHECK-NEXT: [[ADD:%.*]] = add nsw i32 [[TMP5]], [[TMP6]] ; CHECK-NEXT: store i32 [[ADD]], ptr [[A]], align 4 -; CHECK-NEXT: call void @__kmpc_end_reduce_nowait(ptr noundef nonnull @[[GLOB2]], i32 [[TMP2]], ptr noundef nonnull @.gomp_critical_user_.reduction.var) +; CHECK-NEXT: call void @__kmpc_end_reduce_nowait(ptr noundef nonnull @[[GLOB2]], i32 [[TMP2]], ptr noundef nonnull @.gomp_critical_user_.reduction.var) #[[ATTR9]] ; CHECK-NEXT: br label [[DOTOMP_REDUCTION_DEFAULT]] ; CHECK: .omp.reduction.case2: ; CHECK-NEXT: [[TMP7:%.*]] = load i32, ptr [[A1]], align 4 diff --git a/llvm/test/Transforms/OpenMP/parallel_region_merging.ll b/llvm/test/Transforms/OpenMP/parallel_region_merging.ll index 6d3e5935d2e11..312a4ec8be74b 100644 --- a/llvm/test/Transforms/OpenMP/parallel_region_merging.ll +++ b/llvm/test/Transforms/OpenMP/parallel_region_merging.ll @@ -5252,12 +5252,12 @@ entry: ; CHECK2: omp.par.pre_finalize: ; CHECK2-NEXT: br label [[DOTFINI:%.*]] ; CHECK2: .fini: -; CHECK2-NEXT: call void @llvm.lifetime.end.p0(ptr noundef nonnull [[B]]) +; CHECK2-NEXT: call void @llvm.lifetime.end.p0(ptr noundef nonnull captures(none) [[B]]) ; CHECK2-NEXT: br label [[OMP_PAR_EXIT_EXITSTUB:%.*]] ; CHECK2: omp_region.body: ; CHECK2-NEXT: br label [[SEQ_PAR_MERGED:%.*]] ; CHECK2: seq.par.merged: -; CHECK2-NEXT: call void @llvm.lifetime.start.p0(ptr noundef nonnull align 4 dereferenceable(4) [[B]]) +; CHECK2-NEXT: call void @llvm.lifetime.start.p0(ptr noundef nonnull align 4 captures(none) dereferenceable(4) [[B]]) ; CHECK2-NEXT: [[ADD:%.*]] = add nsw i32 [[TMP2]], 1 ; CHECK2-NEXT: store i32 [[ADD]], ptr [[B]], align 4 ; CHECK2-NEXT: br label [[OMP_PAR_MERGED_SPLIT:%.*]] @@ -5352,7 +5352,7 @@ entry: ; CHECK2-NEXT: br i1 [[TOBOOL_NOT]], label [[IF_END:%.*]], label [[IF_THEN:%.*]] ; CHECK2: if.then: ; CHECK2-NEXT: [[TMP1:%.*]] = load i32, ptr [[DOTGLOBAL_TID_]], align 4, !invariant.load [[META7:![0-9]+]] -; CHECK2-NEXT: [[TMP2:%.*]] = call i32 @__kmpc_cancel(ptr noundef nonnull @[[GLOB1]], i32 [[TMP1]], i32 noundef 1) +; CHECK2-NEXT: [[TMP2:%.*]] = call i32 @__kmpc_cancel(ptr noundef nonnull @[[GLOB1]], i32 [[TMP1]], i32 noundef 1) #[[ATTR0]] ; CHECK2-NEXT: ret void ; CHECK2: if.end: ; CHECK2-NEXT: ret void @@ -5366,7 +5366,7 @@ entry: ; CHECK2-NEXT: br i1 [[TOBOOL_NOT]], label [[IF_END:%.*]], label [[IF_THEN:%.*]] ; CHECK2: if.then: ; CHECK2-NEXT: [[TMP1:%.*]] = load i32, ptr [[DOTGLOBAL_TID_]], align 4, !invariant.load [[META7]] -; CHECK2-NEXT: [[TMP2:%.*]] = call i32 @__kmpc_cancel(ptr noundef nonnull @[[GLOB1]], i32 [[TMP1]], i32 noundef 1) +; CHECK2-NEXT: [[TMP2:%.*]] = call i32 @__kmpc_cancel(ptr noundef nonnull @[[GLOB1]], i32 [[TMP1]], i32 noundef 1) #[[ATTR0]] ; CHECK2-NEXT: ret void ; CHECK2: if.end: ; CHECK2-NEXT: ret void @@ -5464,7 +5464,7 @@ entry: ; CHECK2-NEXT: br i1 [[TOBOOL_NOT]], label [[IF_END:%.*]], label [[IF_THEN:%.*]] ; CHECK2: if.then: ; CHECK2-NEXT: [[TMP1:%.*]] = load i32, ptr [[DOTGLOBAL_TID_]], align 4, !invariant.load [[META7]] -; CHECK2-NEXT: [[TMP2:%.*]] = call i32 @__kmpc_cancel(ptr noundef nonnull @[[GLOB1]], i32 [[TMP1]], i32 noundef 1) +; CHECK2-NEXT: [[TMP2:%.*]] = call i32 @__kmpc_cancel(ptr noundef nonnull @[[GLOB1]], i32 [[TMP1]], i32 noundef 1) #[[ATTR0]] ; CHECK2-NEXT: ret void ; CHECK2: if.end: ; CHECK2-NEXT: ret void @@ -5478,7 +5478,7 @@ entry: ; CHECK2-NEXT: br i1 [[TOBOOL_NOT]], label [[IF_END:%.*]], label [[IF_THEN:%.*]] ; CHECK2: if.then: ; CHECK2-NEXT: [[TMP1:%.*]] = load i32, ptr [[DOTGLOBAL_TID_]], align 4, !invariant.load [[META7]] -; CHECK2-NEXT: [[TMP2:%.*]] = call i32 @__kmpc_cancel(ptr noundef nonnull @[[GLOB1]], i32 [[TMP1]], i32 noundef 1) +; CHECK2-NEXT: [[TMP2:%.*]] = call i32 @__kmpc_cancel(ptr noundef nonnull @[[GLOB1]], i32 [[TMP1]], i32 noundef 1) #[[ATTR0]] ; CHECK2-NEXT: ret void ; CHECK2: if.end: ; CHECK2-NEXT: ret void