[llvm] 27862ae - [GVN] Use willNotFreeBetween in loop-load PRE (#228033)
via llvm-commits
llvm-commits at lists.llvm.org
Tue Oct 6 04:41:40 PDT 2026
Author: Anup Kumar
Date: 2026-10-06T11:41:31Z
New Revision: 27862ae3ef3117fd121af48529c11aea9347ec36
URL: https://github.com/llvm/llvm-project/commit/27862ae3ef3117fd121af48529c11aea9347ec36
DIFF: https://github.com/llvm/llvm-project/commit/27862ae3ef3117fd121af48529c11aea9347ec36.diff
LOG: [GVN] Use willNotFreeBetween in loop-load PRE (#228033)
`canBeFreed` used by GVN is very conservative. For an argument, one of
the cases where it returns false is when the argument has
`nofree`/`readonly` and `noalias`. A pointer that may alias a clobber in
the loop is never `noalias`, so PRE bails out even when nothing on the
path can free the object.
This uses `willNotFreeBetween` alongside `canBeFreed`. The header load
has already dereferenced `LoadPtr` on an iteration, so only a
deallocation between that load and the reload can make the same address
unsafe to read again.
The non-linear walk this relies on landed in #223580. The existing limit
of 32 instructions per query is left unchanged.
This is the first of two patches, split as suggested in review of my
earlier combined change in #227983. The follow-up will allow a blocker
inside an inner loop; that change is a no-op in many cases without this
one, since in the aliasing cases it targets, `canBeFreed` returns true
and PRE bails out before reaching it.
One phase-ordering test regresses: in `cse_two_guards` the load that is
now PRE'd becomes a loop-carried recurrence, so the second loop is no
longer vectorized.
RFC: https://discourse.llvm.org/t/rfc-extending-gvns-loop-load-pre/91740
Added:
Modified:
llvm/lib/Transforms/Scalar/GVN.cpp
llvm/test/Transforms/GVN/PRE/pre-aliasning-path.ll
llvm/test/Transforms/GVN/PRE/pre-loop-load.ll
llvm/test/Transforms/PhaseOrdering/X86/ptrtoaddr-ptrtoint.ll
Removed:
################################################################################
diff --git a/llvm/lib/Transforms/Scalar/GVN.cpp b/llvm/lib/Transforms/Scalar/GVN.cpp
index 274eb502e5ffe..1fc84886628c7 100644
--- a/llvm/lib/Transforms/Scalar/GVN.cpp
+++ b/llvm/lib/Transforms/Scalar/GVN.cpp
@@ -2338,7 +2338,13 @@ bool GVNPassImpl::performLoopLoadPRE(LoadInst *Load,
// Make sure the memory at this pointer cannot be freed, therefore we can
// safely reload from it after clobber.
- if (LoadPtr->canBeFreed())
+ //
+ // The header load has already dereferenced LoadPtr on this iteration, so
+ // only a deallocation between that load and the reload in LoopBlock can make
+ // the same address unsafe to read again. Check every path between these two
+ // points for an instruction that may deallocate the memory.
+ if (LoadPtr->canBeFreed() &&
+ !willNotFreeBetween(Load, LoopBlock->getTerminator(), DT))
return false;
// TODO: Support critical edge splitting if blocker has more than 1 successor.
diff --git a/llvm/test/Transforms/GVN/PRE/pre-aliasning-path.ll b/llvm/test/Transforms/GVN/PRE/pre-aliasning-path.ll
index 7dc884f87e360..3d4feb76aaa44 100644
--- a/llvm/test/Transforms/GVN/PRE/pre-aliasning-path.ll
+++ b/llvm/test/Transforms/GVN/PRE/pre-aliasning-path.ll
@@ -8,22 +8,26 @@ declare void @side_effect_1(i32 %x) nofree
declare void @no_side_effect() readonly
-; TODO: We can PRE the load into the cold path, removing it from the hot path.
+; We can PRE the load into the cold path, removing it from the hot path: the
+; callee is nofree, so %p cannot be deallocated between the loads.
define i32 @test_01(ptr %p) {
; CHECK-LABEL: @test_01(
; CHECK-NEXT: entry:
+; CHECK-NEXT: [[X_PRE1:%.*]] = load i32, ptr [[P:%.*]], align 4
; CHECK-NEXT: br label [[LOOP:%.*]]
; CHECK: loop:
-; CHECK-NEXT: [[IV:%.*]] = phi i32 [ 0, [[ENTRY:%.*]] ], [ [[IV_NEXT:%.*]], [[BACKEDGE:%.*]] ]
-; CHECK-NEXT: [[X:%.*]] = load i32, ptr [[P:%.*]], align 4
+; CHECK-NEXT: [[X:%.*]] = phi i32 [ [[X_PRE1]], [[ENTRY:%.*]] ], [ [[X2:%.*]], [[BACKEDGE:%.*]] ]
+; CHECK-NEXT: [[IV:%.*]] = phi i32 [ 0, [[ENTRY]] ], [ [[IV_NEXT:%.*]], [[BACKEDGE]] ]
; CHECK-NEXT: [[COND:%.*]] = icmp ult i32 [[X]], 100
; CHECK-NEXT: br i1 [[COND]], label [[HOT_PATH:%.*]], label [[COLD_PATH:%.*]]
; CHECK: hot_path:
; CHECK-NEXT: br label [[BACKEDGE]]
; CHECK: cold_path:
; CHECK-NEXT: call void @side_effect_0() #[[ATTR0:[0-9]+]]
+; CHECK-NEXT: [[X_PRE:%.*]] = load i32, ptr [[P]], align 4
; CHECK-NEXT: br label [[BACKEDGE]]
; CHECK: backedge:
+; CHECK-NEXT: [[X2]] = phi i32 [ [[X_PRE]], [[COLD_PATH]] ], [ [[X]], [[HOT_PATH]] ]
; CHECK-NEXT: [[IV_NEXT]] = add i32 [[IV]], [[X]]
; CHECK-NEXT: [[LOOP_COND:%.*]] = icmp ult i32 [[IV_NEXT]], 1000
; CHECK-NEXT: br i1 [[LOOP_COND]], label [[LOOP]], label [[EXIT:%.*]]
@@ -55,22 +59,26 @@ exit:
ret i32 %x
}
-; TODO: We can PRE the load into the cold path, removing it from the hot path.
+; We can PRE the load into the cold path, removing it from the hot path: the
+; callee is nofree, so %p cannot be deallocated between the loads.
define i32 @test_02(ptr %p) {
; CHECK-LABEL: @test_02(
; CHECK-NEXT: entry:
+; CHECK-NEXT: [[X_PRE1:%.*]] = load i32, ptr [[P:%.*]], align 4
; CHECK-NEXT: br label [[LOOP:%.*]]
; CHECK: loop:
-; CHECK-NEXT: [[IV:%.*]] = phi i32 [ 0, [[ENTRY:%.*]] ], [ [[IV_NEXT:%.*]], [[BACKEDGE:%.*]] ]
-; CHECK-NEXT: [[X:%.*]] = load i32, ptr [[P:%.*]], align 4
+; CHECK-NEXT: [[X:%.*]] = phi i32 [ [[X_PRE1]], [[ENTRY:%.*]] ], [ [[X2:%.*]], [[BACKEDGE:%.*]] ]
+; CHECK-NEXT: [[IV:%.*]] = phi i32 [ 0, [[ENTRY]] ], [ [[IV_NEXT:%.*]], [[BACKEDGE]] ]
; CHECK-NEXT: [[COND:%.*]] = icmp ult i32 [[X]], 100
; CHECK-NEXT: br i1 [[COND]], label [[HOT_PATH:%.*]], label [[COLD_PATH:%.*]]
; CHECK: hot_path:
; CHECK-NEXT: br label [[BACKEDGE]]
; CHECK: cold_path:
; CHECK-NEXT: call void @side_effect_1(i32 [[X]]) #[[ATTR0]]
+; CHECK-NEXT: [[X_PRE:%.*]] = load i32, ptr [[P]], align 4
; CHECK-NEXT: br label [[BACKEDGE]]
; CHECK: backedge:
+; CHECK-NEXT: [[X2]] = phi i32 [ [[X_PRE]], [[COLD_PATH]] ], [ [[X]], [[HOT_PATH]] ]
; CHECK-NEXT: [[IV_NEXT]] = add i32 [[IV]], [[X]]
; CHECK-NEXT: [[LOOP_COND:%.*]] = icmp ult i32 [[IV_NEXT]], 1000
; CHECK-NEXT: br i1 [[LOOP_COND]], label [[LOOP]], label [[EXIT:%.*]]
diff --git a/llvm/test/Transforms/GVN/PRE/pre-loop-load.ll b/llvm/test/Transforms/GVN/PRE/pre-loop-load.ll
index 8f1b73ad8383d..f1d3a813445ac 100644
--- a/llvm/test/Transforms/GVN/PRE/pre-loop-load.ll
+++ b/llvm/test/Transforms/GVN/PRE/pre-loop-load.ll
@@ -107,22 +107,26 @@ exit:
}
-; TODO: We can PRE the load away from the hot path.
+; We can PRE the load away from the hot path: @side_effect is nofree, so the
+; object under %p cannot be deallocated between the loads.
define i32 @test_load_on_cold_path(ptr %p) {
; CHECK-LABEL: @test_load_on_cold_path(
; CHECK-NEXT: entry:
+; CHECK-NEXT: [[X_PRE1:%.*]] = load i32, ptr [[P:%.*]], align 4
; CHECK-NEXT: br label [[LOOP:%.*]]
; CHECK: loop:
-; CHECK-NEXT: [[IV:%.*]] = phi i32 [ 0, [[ENTRY:%.*]] ], [ [[IV_NEXT:%.*]], [[BACKEDGE:%.*]] ]
-; CHECK-NEXT: [[X:%.*]] = load i32, ptr [[P:%.*]], align 4
+; CHECK-NEXT: [[X:%.*]] = phi i32 [ [[X_PRE1]], [[ENTRY:%.*]] ], [ [[X2:%.*]], [[BACKEDGE:%.*]] ]
+; CHECK-NEXT: [[IV:%.*]] = phi i32 [ 0, [[ENTRY]] ], [ [[IV_NEXT:%.*]], [[BACKEDGE]] ]
; CHECK-NEXT: [[COND:%.*]] = icmp ne i32 [[X]], 0
; CHECK-NEXT: br i1 [[COND]], label [[HOT_PATH:%.*]], label [[COLD_PATH:%.*]]
; CHECK: hot_path:
; CHECK-NEXT: br label [[BACKEDGE]]
; CHECK: cold_path:
; CHECK-NEXT: call void @side_effect() #[[ATTR0:[0-9]+]]
+; CHECK-NEXT: [[X_PRE:%.*]] = load i32, ptr [[P]], align 4
; CHECK-NEXT: br label [[BACKEDGE]]
; CHECK: backedge:
+; CHECK-NEXT: [[X2]] = phi i32 [ [[X_PRE]], [[COLD_PATH]] ], [ [[X]], [[HOT_PATH]] ]
; CHECK-NEXT: [[IV_NEXT]] = add i32 [[IV]], [[X]]
; CHECK-NEXT: [[LOOP_COND:%.*]] = icmp ult i32 [[IV_NEXT]], 1000
; CHECK-NEXT: br i1 [[LOOP_COND]], label [[LOOP]], label [[EXIT:%.*]]
@@ -355,22 +359,26 @@ exit:
ret i32 %x
}
-; TODO: We can PRE via splitting of the critical edge in the cold path.
+; We PRE into the cold path. TODO: split the critical edge so that the reload
+; does not also run on the path that leaves the loop.
define i32 @test_load_on_exiting_cold_path_01(ptr %p) {
; CHECK-LABEL: @test_load_on_exiting_cold_path_01(
; CHECK-NEXT: entry:
+; CHECK-NEXT: [[X_PRE2:%.*]] = load i32, ptr [[P:%.*]], align 4
; CHECK-NEXT: br label [[LOOP:%.*]]
; CHECK: loop:
-; CHECK-NEXT: [[IV:%.*]] = phi i32 [ 0, [[ENTRY:%.*]] ], [ [[IV_NEXT:%.*]], [[BACKEDGE:%.*]] ]
-; CHECK-NEXT: [[X:%.*]] = load i32, ptr [[P:%.*]], align 4
+; CHECK-NEXT: [[X:%.*]] = phi i32 [ [[X_PRE2]], [[ENTRY:%.*]] ], [ [[X3:%.*]], [[BACKEDGE:%.*]] ]
+; CHECK-NEXT: [[IV:%.*]] = phi i32 [ 0, [[ENTRY]] ], [ [[IV_NEXT:%.*]], [[BACKEDGE]] ]
; CHECK-NEXT: [[COND:%.*]] = icmp ne i32 [[X]], 0
; CHECK-NEXT: br i1 [[COND]], label [[HOT_PATH:%.*]], label [[COLD_PATH:%.*]]
; CHECK: hot_path:
; CHECK-NEXT: br label [[BACKEDGE]]
; CHECK: cold_path:
; CHECK-NEXT: [[SIDE_COND:%.*]] = call i1 @side_effect_cond() #[[ATTR0]]
+; CHECK-NEXT: [[X_PRE:%.*]] = load i32, ptr [[P]], align 4
; CHECK-NEXT: br i1 [[SIDE_COND]], label [[BACKEDGE]], label [[COLD_EXIT:%.*]]
; CHECK: backedge:
+; CHECK-NEXT: [[X3]] = phi i32 [ [[X_PRE]], [[COLD_PATH]] ], [ [[X]], [[HOT_PATH]] ]
; CHECK-NEXT: [[IV_NEXT]] = add i32 [[IV]], [[X]]
; CHECK-NEXT: [[LOOP_COND:%.*]] = icmp ult i32 [[IV_NEXT]], 1000
; CHECK-NEXT: br i1 [[LOOP_COND]], label [[LOOP]], label [[EXIT:%.*]]
@@ -570,14 +578,17 @@ exit:
ret i32 %x
}
-; TODO: We can PRE via splitting of the critical edge in the cold path. Make sure we only insert 1 load.
+; We PRE into the last cold block, inserting exactly 1 load. TODO: split the
+; critical edge so that the reload does not also run on the path that leaves the
+; loop.
define i32 @test_load_on_multi_exiting_cold_path(ptr %p) {
; CHECK-LABEL: @test_load_on_multi_exiting_cold_path(
; CHECK-NEXT: entry:
+; CHECK-NEXT: [[X_PRE2:%.*]] = load i32, ptr [[P:%.*]], align 4
; CHECK-NEXT: br label [[LOOP:%.*]]
; CHECK: loop:
-; CHECK-NEXT: [[IV:%.*]] = phi i32 [ 0, [[ENTRY:%.*]] ], [ [[IV_NEXT:%.*]], [[BACKEDGE:%.*]] ]
-; CHECK-NEXT: [[X:%.*]] = load i32, ptr [[P:%.*]], align 4
+; CHECK-NEXT: [[X:%.*]] = phi i32 [ [[X_PRE2]], [[ENTRY:%.*]] ], [ [[X3:%.*]], [[BACKEDGE:%.*]] ]
+; CHECK-NEXT: [[IV:%.*]] = phi i32 [ 0, [[ENTRY]] ], [ [[IV_NEXT:%.*]], [[BACKEDGE]] ]
; CHECK-NEXT: [[COND:%.*]] = icmp ne i32 [[X]], 0
; CHECK-NEXT: br i1 [[COND]], label [[HOT_PATH:%.*]], label [[COLD_PATH_1:%.*]]
; CHECK: hot_path:
@@ -590,8 +601,10 @@ define i32 @test_load_on_multi_exiting_cold_path(ptr %p) {
; CHECK-NEXT: br i1 [[SIDE_COND_2]], label [[COLD_PATH_3:%.*]], label [[COLD_EXIT]]
; CHECK: cold_path.3:
; CHECK-NEXT: [[SIDE_COND_3:%.*]] = call i1 @side_effect_cond() #[[ATTR0]]
+; CHECK-NEXT: [[X_PRE:%.*]] = load i32, ptr [[P]], align 4
; CHECK-NEXT: br i1 [[SIDE_COND_3]], label [[BACKEDGE]], label [[COLD_EXIT]]
; CHECK: backedge:
+; CHECK-NEXT: [[X3]] = phi i32 [ [[X_PRE]], [[COLD_PATH_3]] ], [ [[X]], [[HOT_PATH]] ]
; CHECK-NEXT: [[IV_NEXT]] = add i32 [[IV]], [[X]]
; CHECK-NEXT: [[LOOP_COND:%.*]] = icmp ult i32 [[IV_NEXT]], 1000
; CHECK-NEXT: br i1 [[LOOP_COND]], label [[LOOP]], label [[EXIT:%.*]]
@@ -773,14 +786,16 @@ exit:
ret i32 %x
}
-; TODO: We can PRE via split of critical edge.
+; We PRE into the call block. TODO: split the critical edge so that the reload
+; does not also run on the path that leaves the loop.
define i32 @test_side_exit_after_merge(ptr %p) {
; CHECK-LABEL: @test_side_exit_after_merge(
; CHECK-NEXT: entry:
+; CHECK-NEXT: [[X_PRE2:%.*]] = load i32, ptr [[P:%.*]], align 4
; CHECK-NEXT: br label [[LOOP:%.*]]
; CHECK: loop:
-; CHECK-NEXT: [[IV:%.*]] = phi i32 [ 0, [[ENTRY:%.*]] ], [ [[IV_NEXT:%.*]], [[BACKEDGE:%.*]] ]
-; CHECK-NEXT: [[X:%.*]] = load i32, ptr [[P:%.*]], align 4
+; CHECK-NEXT: [[X:%.*]] = phi i32 [ [[X_PRE2]], [[ENTRY:%.*]] ], [ [[X3:%.*]], [[BACKEDGE:%.*]] ]
+; CHECK-NEXT: [[IV:%.*]] = phi i32 [ 0, [[ENTRY]] ], [ [[IV_NEXT:%.*]], [[BACKEDGE]] ]
; CHECK-NEXT: [[COND:%.*]] = icmp ne i32 [[X]], 0
; CHECK-NEXT: br i1 [[COND]], label [[HOT_PATH:%.*]], label [[COLD_PATH:%.*]]
; CHECK: hot_path:
@@ -790,11 +805,14 @@ define i32 @test_side_exit_after_merge(ptr %p) {
; CHECK-NEXT: br i1 [[COND_1]], label [[DO_CALL:%.*]], label [[SIDE_EXITING:%.*]]
; CHECK: do_call:
; CHECK-NEXT: [[SIDE_COND:%.*]] = call i1 @side_effect_cond() #[[ATTR0]]
+; CHECK-NEXT: [[X_PRE:%.*]] = load i32, ptr [[P]], align 4
; CHECK-NEXT: br label [[SIDE_EXITING]]
; CHECK: side_exiting:
+; CHECK-NEXT: [[X4:%.*]] = phi i32 [ [[X_PRE]], [[DO_CALL]] ], [ 0, [[COLD_PATH]] ]
; CHECK-NEXT: [[SIDE_COND_PHI:%.*]] = phi i1 [ [[SIDE_COND]], [[DO_CALL]] ], [ true, [[COLD_PATH]] ]
; CHECK-NEXT: br i1 [[SIDE_COND_PHI]], label [[BACKEDGE]], label [[COLD_EXIT:%.*]]
; CHECK: backedge:
+; CHECK-NEXT: [[X3]] = phi i32 [ [[X4]], [[SIDE_EXITING]] ], [ [[X]], [[HOT_PATH]] ]
; CHECK-NEXT: [[IV_NEXT]] = add i32 [[IV]], [[X]]
; CHECK-NEXT: [[LOOP_COND:%.*]] = icmp ult i32 [[IV_NEXT]], 1000
; CHECK-NEXT: br i1 [[LOOP_COND]], label [[LOOP]], label [[EXIT:%.*]]
@@ -940,3 +958,71 @@ backedge:
exit:
ret i32 %x
}
+
+; We can PRE into the merge block. %merge has two predecessors, so proving the
+; absence of a deallocation requires walking both paths back to the header load.
+define i32 @test_merged_nofree_paths(ptr %p, i1 %take.a) {
+; CHECK-LABEL: @test_merged_nofree_paths(
+; CHECK-NEXT: entry:
+; CHECK-NEXT: [[X_PRE1:%.*]] = load i32, ptr [[P:%.*]], align 4
+; CHECK-NEXT: br label [[LOOP:%.*]]
+; CHECK: loop:
+; CHECK-NEXT: [[X:%.*]] = phi i32 [ [[X_PRE1]], [[ENTRY:%.*]] ], [ [[X2:%.*]], [[BACKEDGE:%.*]] ]
+; CHECK-NEXT: [[IV:%.*]] = phi i32 [ 0, [[ENTRY]] ], [ [[IV_NEXT:%.*]], [[BACKEDGE]] ]
+; CHECK-NEXT: [[COND:%.*]] = icmp ne i32 [[X]], 0
+; CHECK-NEXT: br i1 [[COND]], label [[HOT_PATH:%.*]], label [[COLD_ENTRY:%.*]]
+; CHECK: hot_path:
+; CHECK-NEXT: br label [[BACKEDGE]]
+; CHECK: cold_entry:
+; CHECK-NEXT: br i1 [[TAKE_A:%.*]], label [[PATH_A:%.*]], label [[PATH_B:%.*]]
+; CHECK: path_a:
+; CHECK-NEXT: call void @side_effect() #[[ATTR0]]
+; CHECK-NEXT: br label [[MERGE:%.*]]
+; CHECK: path_b:
+; CHECK-NEXT: br label [[MERGE]]
+; CHECK: merge:
+; CHECK-NEXT: call void @side_effect() #[[ATTR0]]
+; CHECK-NEXT: [[X_PRE:%.*]] = load i32, ptr [[P]], align 4
+; CHECK-NEXT: br label [[BACKEDGE]]
+; CHECK: backedge:
+; CHECK-NEXT: [[X2]] = phi i32 [ [[X_PRE]], [[MERGE]] ], [ [[X]], [[HOT_PATH]] ]
+; CHECK-NEXT: [[IV_NEXT]] = add i32 [[IV]], [[X]]
+; CHECK-NEXT: [[LOOP_COND:%.*]] = icmp ult i32 [[IV_NEXT]], 1000
+; CHECK-NEXT: br i1 [[LOOP_COND]], label [[LOOP]], label [[EXIT:%.*]]
+; CHECK: exit:
+; CHECK-NEXT: ret i32 [[X]]
+;
+entry:
+ br label %loop
+
+loop:
+ %iv = phi i32 [ 0, %entry ], [ %iv.next, %backedge ]
+ %x = load i32, ptr %p
+ %cond = icmp ne i32 %x, 0
+ br i1 %cond, label %hot_path, label %cold_entry
+
+hot_path:
+ br label %backedge
+
+cold_entry:
+ br i1 %take.a, label %path_a, label %path_b
+
+path_a:
+ call void @side_effect() nofree
+ br label %merge
+
+path_b:
+ br label %merge
+
+merge:
+ call void @side_effect() nofree
+ br label %backedge
+
+backedge:
+ %iv.next = add i32 %iv, %x
+ %loop.cond = icmp ult i32 %iv.next, 1000
+ br i1 %loop.cond, label %loop, label %exit
+
+exit:
+ ret i32 %x
+}
diff --git a/llvm/test/Transforms/PhaseOrdering/X86/ptrtoaddr-ptrtoint.ll b/llvm/test/Transforms/PhaseOrdering/X86/ptrtoaddr-ptrtoint.ll
index 1bb6b2fb5bd3a..06dd78217c4ae 100644
--- a/llvm/test/Transforms/PhaseOrdering/X86/ptrtoaddr-ptrtoint.ll
+++ b/llvm/test/Transforms/PhaseOrdering/X86/ptrtoaddr-ptrtoint.ll
@@ -1,5 +1,5 @@
; NOTE: Assertions have been autogenerated by utils/update_test_checks.py UTC_ARGS: --check-globals none --version 6
-; RUN: opt -passes="default<O3>" -S < %s | FileCheck %s
+; RUN: opt -passes="default<O3>" -enable-load-in-loop-pre=false -S < %s | FileCheck %s
target datalayout = "e-m:e-p270:32:32-p271:32:32-p272:64:64-i64:64-i128:128-f80:128-n8:16:32:64-S128"
target triple = "x86_64-pc-linux-gnu"
@@ -330,7 +330,7 @@ define i1 @lambda(ptr %lam, ptr %cmp) {
define ptr @gep_base_cancel(ptr %p, ptr %end, ptr noalias %a) {
; CHECK-LABEL: define noundef ptr @gep_base_cancel(
-; CHECK-SAME: ptr nofree readonly captures(address) [[P:%.*]], ptr nofree readnone captures(address) [[END:%.*]], ptr noalias nofree captures(address, ret: address, provenance) [[A:%.*]]) local_unnamed_addr #[[ATTR4:[0-9]+]] {
+; CHECK-SAME: ptr nofree readonly captures(address) [[P:%.*]], ptr nofree readnone captures(address) [[END:%.*]], ptr noalias nofree captures(address, ret: address, provenance) [[A:%.*]]) local_unnamed_addr #[[ATTR5:[0-9]+]] {
; CHECK-NEXT: [[ENTRY:.*]]:
; CHECK-NEXT: [[IN_BOUNDS6:%.*]] = icmp ult ptr [[P]], [[END]]
; CHECK-NEXT: br i1 [[IN_BOUNDS6]], label %[[BODY_PREHEADER:.*]], label %[[COMMON_RET:.*]]
More information about the llvm-commits
mailing list