[llvm] [LICM] Improve hoist-safety checks for dominated MemorySSA accesses (PR #169379)
via llvm-commits
llvm-commits at lists.llvm.org
Tue Apr 28 02:53:22 PDT 2026
https://github.com/CarolineConcatto updated https://github.com/llvm/llvm-project/pull/169379
>From 8c3dc41a3c07e379cf0c81c6c16fe7a6fcfe0138 Mon Sep 17 00:00:00 2001
From: CarolineConcatto <caroline.concatto at arm.com>
Date: Fri, 31 Oct 2025 16:26:46 +0000
Subject: [PATCH 1/5] [Draft][LICM] Modify noConflictingReadWrites behaviour
for calls
Change behaviour of noConflictingReadWrites for calls, it
allows to hoist calls from the loop when there is no conflict
with other memory Uses in the loop.
This patch implements two changes for the Memory access that are Use:
1) Alias-aware handling of call Uses:
Check if the memory Uses in the loop reads the same memory locations
as 'I' modifies(the call that can be hoisted). If not, 'I' can be hoisted.
There is a positive test called loop_alias tests for these changes.
2) Only consider Uses occurring before 'I'(the call that can be hoisted)
Only checks clobber for all memory uses in the loop that happens before 'I'.
This change is tested with hoist_until_I.
---
llvm/lib/Transforms/Scalar/LICM.cpp | 18 ++-
llvm/test/Transforms/LICM/call-hoisting.ll | 21 +--
.../LICM/hoist-inaccesiblemem-call.ll | 132 ++++++++++++++++++
3 files changed, 159 insertions(+), 12 deletions(-)
create mode 100644 llvm/test/Transforms/LICM/hoist-inaccesiblemem-call.ll
diff --git a/llvm/lib/Transforms/Scalar/LICM.cpp b/llvm/lib/Transforms/Scalar/LICM.cpp
index 03edf9e3b0c1e..772604ff66635 100644
--- a/llvm/lib/Transforms/Scalar/LICM.cpp
+++ b/llvm/lib/Transforms/Scalar/LICM.cpp
@@ -2315,6 +2315,15 @@ static bool noConflictingReadWrites(Instruction *I, MemorySSA *MSSA,
auto *IMD = MSSA->getMemoryAccess(I);
BatchAAResults BAA(*AA);
auto *Source = getClobberingMemoryAccess(*MSSA, BAA, Flags, IMD);
+ auto *CallI = dyn_cast<CallBase>(I);
+ auto doesItReadILoc = [&](Instruction *UserI) -> bool {
+ // Blocks if call reads the location I.
+ if (auto *OtherCB = dyn_cast_or_null<CallBase>(UserI)) {
+ ModRefInfo MRI = AA->getModRefInfo(CallI, OtherCB);
+ return isRefSet(MRI);
+ }
+ return true;
+ };
// Make sure there are no clobbers inside the loop.
if (!MSSA->isLiveOnEntryDef(Source) && CurLoop->contains(Source->getBlock()))
return false;
@@ -2328,11 +2337,14 @@ static bool noConflictingReadWrites(Instruction *I, MemorySSA *MSSA,
auto *Accesses = MSSA->getBlockAccesses(BB);
if (!Accesses)
continue;
+ bool FoundI = false;
for (const auto &MA : *Accesses)
if (const auto *MU = dyn_cast<MemoryUse>(&MA)) {
auto *MD = getClobberingMemoryAccess(*MSSA, BAA, Flags,
const_cast<MemoryUse *>(MU));
- if (!MSSA->isLiveOnEntryDef(MD) && CurLoop->contains(MD->getBlock()))
+ // Check clobbering only for Uses that happens before I
+ if (!MSSA->isLiveOnEntryDef(MD) && CurLoop->contains(MD->getBlock()) &&
+ !FoundI)
return false;
} else if (const auto *MD = dyn_cast<MemoryDef>(&MA)) {
if (auto *LI = dyn_cast<LoadInst>(MD->getMemoryInst())) {
@@ -2353,8 +2365,10 @@ static bool noConflictingReadWrites(Instruction *I, MemorySSA *MSSA,
auto *SCI = cast<CallInst>(I);
// If the instruction we are wanting to hoist is also a call
// instruction then we need not check mod/ref info with itself
- if (SCI == CI)
+ if (SCI == CI) {
+ FoundI = true;
continue;
+ }
ModRefInfo MRI = BAA.getModRefInfo(CI, SCI);
if (isModOrRefSet(MRI))
return false;
diff --git a/llvm/test/Transforms/LICM/call-hoisting.ll b/llvm/test/Transforms/LICM/call-hoisting.ll
index 1078c079dfa42..edc907d9e5a97 100644
--- a/llvm/test/Transforms/LICM/call-hoisting.ll
+++ b/llvm/test/Transforms/LICM/call-hoisting.ll
@@ -399,10 +399,10 @@ exit:
define void @neg_ref(ptr %loc) {
; CHECK-LABEL: define void @neg_ref(
; CHECK-SAME: ptr [[LOC:%.*]]) {
-; CHECK-NEXT: [[ENTRY:.*]]:
-; CHECK-NEXT: br label %[[LOOP:.*]]
-; CHECK: [[LOOP]]:
-; CHECK-NEXT: [[IV:%.*]] = phi i32 [ 0, %[[ENTRY]] ], [ [[IV_NEXT:%.*]], %[[BACKEDGE:.*]] ]
+; CHECK-NEXT: [[LOOP:.*]]:
+; CHECK-NEXT: br label %[[LOOP1:.*]]
+; CHECK: [[LOOP1]]:
+; CHECK-NEXT: [[IV:%.*]] = phi i32 [ 0, %[[LOOP]] ], [ [[IV_NEXT:%.*]], %[[BACKEDGE:.*]] ]
; CHECK-NEXT: call void @store(i32 0, ptr [[LOC]])
; CHECK-NEXT: [[V:%.*]] = load i32, ptr [[LOC]], align 4
; CHECK-NEXT: [[EARLYCND:%.*]] = icmp eq i32 [[V]], 198
@@ -410,7 +410,7 @@ define void @neg_ref(ptr %loc) {
; CHECK: [[BACKEDGE]]:
; CHECK-NEXT: [[IV_NEXT]] = add i32 [[IV]], 1
; CHECK-NEXT: [[CMP:%.*]] = icmp slt i32 [[IV]], 200
-; CHECK-NEXT: br i1 [[CMP]], label %[[LOOP]], label %[[EXIT2:.*]]
+; CHECK-NEXT: br i1 [[CMP]], label %[[LOOP1]], label %[[EXIT2:.*]]
; CHECK: [[EXIT1]]:
; CHECK-NEXT: ret void
; CHECK: [[EXIT2]]:
@@ -536,14 +536,14 @@ exit:
define i32 @neg_not_argmemonly(ptr %loc, ptr %loc2) {
; CHECK-LABEL: define i32 @neg_not_argmemonly(
; CHECK-SAME: ptr [[LOC:%.*]], ptr [[LOC2:%.*]]) {
-; CHECK-NEXT: [[ENTRY:.*]]:
+; CHECK-NEXT: [[LOOP:.*]]:
; CHECK-NEXT: call void @not_argmemonly(i32 0, ptr [[LOC]])
-; CHECK-NEXT: br label %[[LOOP:.*]]
-; CHECK: [[LOOP]]:
-; CHECK-NEXT: [[IV:%.*]] = phi i32 [ 0, %[[ENTRY]] ], [ [[IV_NEXT:%.*]], %[[LOOP]] ]
+; CHECK-NEXT: br label %[[LOOP1:.*]]
+; CHECK: [[LOOP1]]:
+; CHECK-NEXT: [[IV:%.*]] = phi i32 [ 0, %[[LOOP]] ], [ [[IV_NEXT:%.*]], %[[LOOP1]] ]
; CHECK-NEXT: [[IV_NEXT]] = add i32 [[IV]], 1
; CHECK-NEXT: [[CMP:%.*]] = icmp slt i32 [[IV]], 200
-; CHECK-NEXT: br i1 [[CMP]], label %[[LOOP]], label %[[EXIT:.*]]
+; CHECK-NEXT: br i1 [[CMP]], label %[[LOOP1]], label %[[EXIT:.*]]
; CHECK: [[EXIT]]:
; CHECK-NEXT: [[VAL_LE:%.*]] = call i32 @load(ptr [[LOC2]])
; CHECK-NEXT: ret i32 [[VAL_LE]]
@@ -620,3 +620,4 @@ exit:
ret void
}
+
diff --git a/llvm/test/Transforms/LICM/hoist-inaccesiblemem-call.ll b/llvm/test/Transforms/LICM/hoist-inaccesiblemem-call.ll
new file mode 100644
index 0000000000000..d3ff60ef6ba1d
--- /dev/null
+++ b/llvm/test/Transforms/LICM/hoist-inaccesiblemem-call.ll
@@ -0,0 +1,132 @@
+; NOTE: Assertions have been autogenerated by utils/update_test_checks.py UTC_ARGS: --version 6
+; RUN: opt -aa-pipeline=basic-aa -passes='require<aa>,require<target-ir>,loop-mssa(licm)' < %s -S | FileCheck %s
+
+
+define dso_local i32 @loop_alias(i32 %x, ptr %a, ptr %b) #0{
+; CHECK-LABEL: define dso_local i32 @loop_alias(
+; CHECK-SAME: i32 [[X:%.*]], ptr [[A:%.*]], ptr [[B:%.*]]) #[[ATTR0:[0-9]+]] {
+; CHECK-NEXT: [[ENTRY:.*]]:
+; CHECK-NEXT: [[VAL:%.*]] = getelementptr inbounds nuw i32, ptr [[A]], i64 1
+; CHECK-NEXT: call void @fn_write_inaccessible_mem()
+; CHECK-NEXT: call void @fn_read_inaccessible_mem()
+; CHECK-NEXT: br label %[[LOOP:.*]]
+; CHECK: [[LOOP]]:
+; CHECK-NEXT: [[PHI:%.*]] = phi ptr [ [[GEP:%.*]], %[[LOOP]] ], [ [[VAL]], %[[ENTRY]] ]
+; CHECK-NEXT: [[GEP2:%.*]] = getelementptr i8, ptr [[PHI]], i64 0
+; CHECK-NEXT: [[VAL2:%.*]] = call i32 @fn_args(ptr [[GEP2]])
+; CHECK-NEXT: [[GEP]] = getelementptr inbounds nuw i32, ptr [[PHI]], i64 0
+; CHECK-NEXT: [[ACC:%.*]] = add nuw nsw i32 [[VAL2]], 1
+; CHECK-NEXT: [[CMP:%.*]] = icmp ult i32 [[ACC]], 10
+; CHECK-NEXT: br i1 [[CMP]], label %[[LOOP]], label %[[AFTER_LOOP:.*]]
+; CHECK: [[AFTER_LOOP]]:
+; CHECK-NEXT: [[ACC_LCSSA:%.*]] = phi i32 [ [[ACC]], %[[LOOP]] ]
+; CHECK-NEXT: ret i32 [[ACC_LCSSA]]
+;
+entry:
+ %val = getelementptr inbounds nuw i32, ptr %a, i64 1
+ br label %loop
+loop:
+ %phi = phi ptr [ %gep, %loop ], [ %val, %entry ]
+ %44 = load i32, ptr %phi, align 16
+ %gep2 = getelementptr i8, ptr %phi, i64 0
+ %val2 = call i32 @fn_args(ptr %gep2)
+ call void @fn_write_inaccessible_mem()
+ call void @fn_read_inaccessible_mem()
+ %gep = getelementptr inbounds nuw i32, ptr %phi, i64 0
+ %acc = add nuw nsw i32 %val2, 1
+ %cmp = icmp ult i32 %acc, 10
+ br i1 %cmp, label %loop, label %after_loop
+after_loop:
+ ret i32 %acc
+}
+
+declare i32 @fn_args(ptr) nounwind willreturn
+memory(argmem: read)
+
+define void @hoist_untill_I(ptr noalias %loc, ptr noalias %loc2){
+; CHECK-LABEL: define void @hoist_untill_I(
+; CHECK-SAME: ptr noalias [[LOC:%.*]], ptr noalias [[LOC2:%.*]]) {
+; CHECK-NEXT: [[ENTRY:.*:]]
+; CHECK-NEXT: [[VAL:%.*]] = load i32, ptr [[LOC2]], align 4
+; CHECK-NEXT: call void @fn_write_inaccessible_mem()
+; CHECK-NEXT: call void @fn_read_inaccessible_mem()
+; CHECK-NEXT: br label %[[FOR_BODY:.*]]
+; CHECK: [[FOR_COND_CLEANUP:.*:]]
+; CHECK-NEXT: ret void
+; CHECK: [[FOR_BODY]]:
+; CHECK-NEXT: store i32 [[VAL]], ptr [[LOC]], align 4
+; CHECK-NEXT: br label %[[FOR_BODY]]
+;
+entry:
+ br label %for.body
+for.cond.cleanup: ; preds = %for.body
+ ret void
+for.body:
+ %val = load i32, ptr %loc2
+ store i32 %val, ptr %loc
+ call void @fn_write_inaccessible_mem()
+ call void @fn_read_inaccessible_mem()
+ br label %for.body
+}
+
+
+define void @neg_hoist_untill_I(ptr noalias %loc, ptr noalias %loc2){
+; CHECK-LABEL: define void @neg_hoist_untill_I(
+; CHECK-SAME: ptr noalias [[LOC:%.*]], ptr noalias [[LOC2:%.*]]) {
+; CHECK-NEXT: [[ENTRY:.*:]]
+; CHECK-NEXT: [[VAL:%.*]] = load i32, ptr [[LOC2]], align 4
+; CHECK-NEXT: br label %[[FOR_BODY:.*]]
+; CHECK: [[FOR_BODY]]:
+; CHECK-NEXT: store i32 [[VAL]], ptr [[LOC]], align 4
+; CHECK-NEXT: call void @fn_read_inaccessible_mem()
+; CHECK-NEXT: call void @fn_write_inaccessible_mem()
+; CHECK-NEXT: call void @fn_read_inaccessible_mem()
+; CHECK-NEXT: br label %[[FOR_BODY]]
+;
+entry:
+ br label %for.body
+for.body:
+ %val = load i32, ptr %loc2
+ store i32 %val, ptr %loc
+ call void @fn_read_inaccessible_mem()
+ call void @fn_write_inaccessible_mem()
+ call void @fn_read_inaccessible_mem()
+ br label %for.body
+}
+
+
+; Nothing should be hoisted from the loop because volatile
+; sets inaccessible memory to read write
+define void @neg_volatile(ptr %loc, ptr %loc2) {
+; CHECK-LABEL: define void @neg_volatile(
+; CHECK-SAME: ptr [[LOC:%.*]], ptr [[LOC2:%.*]]) {
+; CHECK-NEXT: [[ENTRY:.*:]]
+; CHECK-NEXT: br label %[[LOOP:.*]]
+; CHECK: [[LOOP]]:
+; CHECK-NEXT: store volatile i32 0, ptr [[LOC]], align 4
+; CHECK-NEXT: call void @fn_write_inaccessible_mem()
+; CHECK-NEXT: call void @fn_read_inaccessible_mem()
+; CHECK-NEXT: br label %[[LOOP]]
+;
+entry:
+ br label %loop
+
+loop:
+ %val = load i32, ptr %loc2
+ store volatile i32 0, ptr %loc
+ call void @fn_write_inaccessible_mem()
+ call void @fn_read_inaccessible_mem()
+ br label %loop
+}
+
+declare void @fn_write_inaccessible_mem()#0
+ memory(inaccessiblemem: write)
+
+declare void @fn_read_inaccessible_mem()#0
+ memory(inaccessiblemem: read)
+
+declare void @fn_readwrite_inaccessible_mem()#0
+ memory(inaccessiblemem: readwrite)
+
+; Needs to set nounwind because of doesNotThrow
+attributes #0 = { mustprogress nofree norecurse nosync nounwind}
>From bdd82d8cf1d9156255c9ede09e05702f1a15b27c Mon Sep 17 00:00:00 2001
From: CarolineConcatto <caroline.concatto at arm.com>
Date: Tue, 3 Mar 2026 18:21:33 +0000
Subject: [PATCH 2/5] Address review comments
---
llvm/lib/Transforms/Scalar/LICM.cpp | 27 ++--
llvm/test/Analysis/MemorySSA/pr43438.ll | 6 +-
llvm/test/Transforms/LICM/call-hoisting.ll | 10 +-
.../LICM/hoist-inaccesiblemem-call.ll | 118 +++++++++++++++---
llvm/test/Transforms/LICM/pr50367.ll | 4 +-
llvm/test/Transforms/LICM/store-hoisting.ll | 8 +-
.../invariant-store-vectorization.ll | 12 +-
7 files changed, 132 insertions(+), 53 deletions(-)
diff --git a/llvm/lib/Transforms/Scalar/LICM.cpp b/llvm/lib/Transforms/Scalar/LICM.cpp
index 772604ff66635..2688a89af352c 100644
--- a/llvm/lib/Transforms/Scalar/LICM.cpp
+++ b/llvm/lib/Transforms/Scalar/LICM.cpp
@@ -2315,15 +2315,6 @@ static bool noConflictingReadWrites(Instruction *I, MemorySSA *MSSA,
auto *IMD = MSSA->getMemoryAccess(I);
BatchAAResults BAA(*AA);
auto *Source = getClobberingMemoryAccess(*MSSA, BAA, Flags, IMD);
- auto *CallI = dyn_cast<CallBase>(I);
- auto doesItReadILoc = [&](Instruction *UserI) -> bool {
- // Blocks if call reads the location I.
- if (auto *OtherCB = dyn_cast_or_null<CallBase>(UserI)) {
- ModRefInfo MRI = AA->getModRefInfo(CallI, OtherCB);
- return isRefSet(MRI);
- }
- return true;
- };
// Make sure there are no clobbers inside the loop.
if (!MSSA->isLiveOnEntryDef(Source) && CurLoop->contains(Source->getBlock()))
return false;
@@ -2337,15 +2328,19 @@ static bool noConflictingReadWrites(Instruction *I, MemorySSA *MSSA,
auto *Accesses = MSSA->getBlockAccesses(BB);
if (!Accesses)
continue;
- bool FoundI = false;
for (const auto &MA : *Accesses)
if (const auto *MU = dyn_cast<MemoryUse>(&MA)) {
auto *MD = getClobberingMemoryAccess(*MSSA, BAA, Flags,
const_cast<MemoryUse *>(MU));
- // Check clobbering only for Uses that happens before I
- if (!MSSA->isLiveOnEntryDef(MD) && CurLoop->contains(MD->getBlock()) &&
- !FoundI)
- return false;
+ if (!MSSA->isLiveOnEntryDef(MD) && CurLoop->contains(MD->getBlock())) {
+ auto *MDI = dyn_cast_or_null<MemoryDef>(MD);
+ // It checks only if I is clobbering.
+ // If IMD is not the same as I (that it wants to hoist), assumes they
+ // clobber
+ // Sync store is not allowed, but store hoist is allowed
+ if (!Flags.getIsSink() && (!MDI || MDI->getMemoryInst() != I))
+ return false;
+ }
} else if (const auto *MD = dyn_cast<MemoryDef>(&MA)) {
if (auto *LI = dyn_cast<LoadInst>(MD->getMemoryInst())) {
(void)LI; // Silence warning.
@@ -2365,10 +2360,8 @@ static bool noConflictingReadWrites(Instruction *I, MemorySSA *MSSA,
auto *SCI = cast<CallInst>(I);
// If the instruction we are wanting to hoist is also a call
// instruction then we need not check mod/ref info with itself
- if (SCI == CI) {
- FoundI = true;
+ if (SCI == CI)
continue;
- }
ModRefInfo MRI = BAA.getModRefInfo(CI, SCI);
if (isModOrRefSet(MRI))
return false;
diff --git a/llvm/test/Analysis/MemorySSA/pr43438.ll b/llvm/test/Analysis/MemorySSA/pr43438.ll
index 0e09137c1cf9a..c2a8d68930e0d 100644
--- a/llvm/test/Analysis/MemorySSA/pr43438.ll
+++ b/llvm/test/Analysis/MemorySSA/pr43438.ll
@@ -46,11 +46,7 @@ if.end569: ; preds = %if.else568, %if.the
; CHECK-LABEL: @f(i1 %arg)
-; CHECK: 7 = MemoryPhi(
-; CHECK: 6 = MemoryPhi(
-; CHECK: 10 = MemoryPhi(
-; CHECK: 9 = MemoryPhi(
-; CHECK: 8 = MemoryPhi(
+; CHECK: 4 = MemoryPhi(
define void @f(i1 %arg) {
entry:
%e = alloca i16, align 1
diff --git a/llvm/test/Transforms/LICM/call-hoisting.ll b/llvm/test/Transforms/LICM/call-hoisting.ll
index edc907d9e5a97..fb8c7280dba23 100644
--- a/llvm/test/Transforms/LICM/call-hoisting.ll
+++ b/llvm/test/Transforms/LICM/call-hoisting.ll
@@ -399,18 +399,18 @@ exit:
define void @neg_ref(ptr %loc) {
; CHECK-LABEL: define void @neg_ref(
; CHECK-SAME: ptr [[LOC:%.*]]) {
-; CHECK-NEXT: [[LOOP:.*]]:
-; CHECK-NEXT: br label %[[LOOP1:.*]]
-; CHECK: [[LOOP1]]:
-; CHECK-NEXT: [[IV:%.*]] = phi i32 [ 0, %[[LOOP]] ], [ [[IV_NEXT:%.*]], %[[BACKEDGE:.*]] ]
+; CHECK-NEXT: [[LOOP1:.*]]:
; CHECK-NEXT: call void @store(i32 0, ptr [[LOC]])
; CHECK-NEXT: [[V:%.*]] = load i32, ptr [[LOC]], align 4
; CHECK-NEXT: [[EARLYCND:%.*]] = icmp eq i32 [[V]], 198
+; CHECK-NEXT: br label %[[LOOP:.*]]
+; CHECK: [[LOOP]]:
+; CHECK-NEXT: [[IV:%.*]] = phi i32 [ 0, %[[LOOP1]] ], [ [[IV_NEXT:%.*]], %[[BACKEDGE:.*]] ]
; CHECK-NEXT: br i1 [[EARLYCND]], label %[[EXIT1:.*]], label %[[BACKEDGE]]
; CHECK: [[BACKEDGE]]:
; CHECK-NEXT: [[IV_NEXT]] = add i32 [[IV]], 1
; CHECK-NEXT: [[CMP:%.*]] = icmp slt i32 [[IV]], 200
-; CHECK-NEXT: br i1 [[CMP]], label %[[LOOP1]], label %[[EXIT2:.*]]
+; CHECK-NEXT: br i1 [[CMP]], label %[[LOOP]], label %[[EXIT2:.*]]
; CHECK: [[EXIT1]]:
; CHECK-NEXT: ret void
; CHECK: [[EXIT2]]:
diff --git a/llvm/test/Transforms/LICM/hoist-inaccesiblemem-call.ll b/llvm/test/Transforms/LICM/hoist-inaccesiblemem-call.ll
index d3ff60ef6ba1d..114be4532f7c0 100644
--- a/llvm/test/Transforms/LICM/hoist-inaccesiblemem-call.ll
+++ b/llvm/test/Transforms/LICM/hoist-inaccesiblemem-call.ll
@@ -2,18 +2,21 @@
; RUN: opt -aa-pipeline=basic-aa -passes='require<aa>,require<target-ir>,loop-mssa(licm)' < %s -S | FileCheck %s
-define dso_local i32 @loop_alias(i32 %x, ptr %a, ptr %b) #0{
+;; It should hoist fn_write_inaccessible_mem
+;; because there is no conflict between inaccessible memory
+;; fn_read_inaccessible_mem is a nice side effect
+define dso_local i32 @loop_alias(i32 %x, ptr %a, ptr %b) #0 {
; CHECK-LABEL: define dso_local i32 @loop_alias(
; CHECK-SAME: i32 [[X:%.*]], ptr [[A:%.*]], ptr [[B:%.*]]) #[[ATTR0:[0-9]+]] {
; CHECK-NEXT: [[ENTRY:.*]]:
; CHECK-NEXT: [[VAL:%.*]] = getelementptr inbounds nuw i32, ptr [[A]], i64 1
; CHECK-NEXT: call void @fn_write_inaccessible_mem()
-; CHECK-NEXT: call void @fn_read_inaccessible_mem()
; CHECK-NEXT: br label %[[LOOP:.*]]
; CHECK: [[LOOP]]:
; CHECK-NEXT: [[PHI:%.*]] = phi ptr [ [[GEP:%.*]], %[[LOOP]] ], [ [[VAL]], %[[ENTRY]] ]
; CHECK-NEXT: [[GEP2:%.*]] = getelementptr i8, ptr [[PHI]], i64 0
; CHECK-NEXT: [[VAL2:%.*]] = call i32 @fn_args(ptr [[GEP2]])
+; CHECK-NEXT: call void @fn_read_inaccessible_mem(i32 [[VAL2]])
; CHECK-NEXT: [[GEP]] = getelementptr inbounds nuw i32, ptr [[PHI]], i64 0
; CHECK-NEXT: [[ACC:%.*]] = add nuw nsw i32 [[VAL2]], 1
; CHECK-NEXT: [[CMP:%.*]] = icmp ult i32 [[ACC]], 10
@@ -31,7 +34,7 @@ loop:
%gep2 = getelementptr i8, ptr %phi, i64 0
%val2 = call i32 @fn_args(ptr %gep2)
call void @fn_write_inaccessible_mem()
- call void @fn_read_inaccessible_mem()
+ call void @fn_read_inaccessible_mem(i32 %val2)
%gep = getelementptr inbounds nuw i32, ptr %phi, i64 0
%acc = add nuw nsw i32 %val2, 1
%cmp = icmp ult i32 %acc, 10
@@ -43,13 +46,96 @@ after_loop:
declare i32 @fn_args(ptr) nounwind willreturn
memory(argmem: read)
+;; It should be able to hoist fn_write_inaccessible_mem
+define dso_local i32 @loop_alias_store(i32 %x, ptr %a) #0 {
+; CHECK-LABEL: define dso_local i32 @loop_alias_store(
+; CHECK-SAME: i32 [[X:%.*]], ptr [[A:%.*]]) #[[ATTR0]] {
+; CHECK-NEXT: [[ENTRY:.*]]:
+; CHECK-NEXT: [[VAL:%.*]] = getelementptr inbounds nuw i32, ptr [[A]], i64 1
+; CHECK-NEXT: br label %[[LOOP:.*]]
+; CHECK: [[LOOP]]:
+; CHECK-NEXT: [[PHI:%.*]] = phi ptr [ [[GEP:%.*]], %[[LOOP]] ], [ [[VAL]], %[[ENTRY]] ]
+; CHECK-NEXT: [[TMP0:%.*]] = load i32, ptr [[PHI]], align 16
+; CHECK-NEXT: [[GEP]] = getelementptr i8, ptr [[PHI]], i64 0
+; CHECK-NEXT: call void @fn_write_inaccessible_mem()
+; CHECK-NEXT: call void @fn_read_inaccessible_mem(i32 [[TMP0]])
+; CHECK-NEXT: [[ACC:%.*]] = add nuw nsw i32 [[TMP0]], 1
+; CHECK-NEXT: store i32 [[ACC]], ptr [[A]], align 16
+; CHECK-NEXT: [[CMP:%.*]] = icmp ult i32 [[ACC]], 10
+; CHECK-NEXT: br i1 [[CMP]], label %[[LOOP]], label %[[AFTER_LOOP:.*]]
+; CHECK: [[AFTER_LOOP]]:
+; CHECK-NEXT: [[ACC_LCSSA:%.*]] = phi i32 [ [[ACC]], %[[LOOP]] ]
+; CHECK-NEXT: ret i32 [[ACC_LCSSA]]
+;
+entry:
+ %val = getelementptr inbounds nuw i32, ptr %a, i64 1
+ br label %loop
+loop:
+ %phi = phi ptr [ %gep, %loop ], [ %val, %entry ]
+ %44 = load i32, ptr %phi, align 16
+ %gep = getelementptr i8, ptr %phi, i64 0
+ %val2 = call i32 @fn_args(ptr %gep)
+ call void @fn_write_inaccessible_mem()
+ call void @fn_read_inaccessible_mem(i32 %44)
+ %acc = add nuw nsw i32 %44, 1
+ store i32 %acc, ptr %a, align 16
+ %cmp = icmp ult i32 %acc, 10
+ br i1 %cmp, label %loop, label %after_loop
+after_loop:
+ ret i32 %acc
+}
+
+;; It should NOT hoist fn_write_inaccessible_mem
+;; Because fn_args_2 reads same memory location
+define dso_local i32 @ne_loop_alias(i32 %x, ptr %a, ptr %b) #0 {
+; CHECK-LABEL: define dso_local i32 @ne_loop_alias(
+; CHECK-SAME: i32 [[X:%.*]], ptr [[A:%.*]], ptr [[B:%.*]]) #[[ATTR0]] {
+; CHECK-NEXT: [[ENTRY:.*]]:
+; CHECK-NEXT: [[VAL:%.*]] = getelementptr inbounds nuw i32, ptr [[A]], i64 1
+; CHECK-NEXT: br label %[[LOOP:.*]]
+; CHECK: [[LOOP]]:
+; CHECK-NEXT: [[PHI:%.*]] = phi ptr [ [[GEP:%.*]], %[[LOOP]] ], [ [[VAL]], %[[ENTRY]] ]
+; CHECK-NEXT: [[GEP2:%.*]] = getelementptr i8, ptr [[PHI]], i64 0
+; CHECK-NEXT: [[VAL2:%.*]] = call i32 @fn_args_2(ptr [[GEP2]])
+; CHECK-NEXT: call void @fn_write_inaccessible_mem()
+; CHECK-NEXT: call void @fn_read_inaccessible_mem(i32 [[VAL2]])
+; CHECK-NEXT: [[GEP]] = getelementptr inbounds nuw i32, ptr [[PHI]], i64 0
+; CHECK-NEXT: [[ACC:%.*]] = add nuw nsw i32 [[VAL2]], 1
+; CHECK-NEXT: [[CMP:%.*]] = icmp ult i32 [[ACC]], 10
+; CHECK-NEXT: br i1 [[CMP]], label %[[LOOP]], label %[[AFTER_LOOP:.*]]
+; CHECK: [[AFTER_LOOP]]:
+; CHECK-NEXT: [[ACC_LCSSA:%.*]] = phi i32 [ [[ACC]], %[[LOOP]] ]
+; CHECK-NEXT: ret i32 [[ACC_LCSSA]]
+;
+entry:
+ %val = getelementptr inbounds nuw i32, ptr %a, i64 1
+ br label %loop
+loop:
+ %phi = phi ptr [ %gep, %loop ], [ %val, %entry ]
+ %44 = load i32, ptr %phi, align 16
+ %gep2 = getelementptr i8, ptr %phi, i64 0
+ %val2 = call i32 @fn_args_2(ptr %gep2)
+ call void @fn_write_inaccessible_mem()
+ call void @fn_read_inaccessible_mem(i32 %val2)
+ %gep = getelementptr inbounds nuw i32, ptr %phi, i64 0
+ %acc = add nuw nsw i32 %val2, 1
+ %cmp = icmp ult i32 %acc, 10
+ br i1 %cmp, label %loop, label %after_loop
+after_loop:
+ ret i32 %acc
+}
+declare i32 @fn_args_2(ptr) nounwind willreturn
+memory(inaccessiblemem:read)
+
+;; Should hoist fn_write_inaccessible_mem
+;; It does not alias with store
define void @hoist_untill_I(ptr noalias %loc, ptr noalias %loc2){
; CHECK-LABEL: define void @hoist_untill_I(
; CHECK-SAME: ptr noalias [[LOC:%.*]], ptr noalias [[LOC2:%.*]]) {
; CHECK-NEXT: [[ENTRY:.*:]]
; CHECK-NEXT: [[VAL:%.*]] = load i32, ptr [[LOC2]], align 4
; CHECK-NEXT: call void @fn_write_inaccessible_mem()
-; CHECK-NEXT: call void @fn_read_inaccessible_mem()
+; CHECK-NEXT: call void @fn_read_inaccessible_mem(i32 [[VAL]])
; CHECK-NEXT: br label %[[FOR_BODY:.*]]
; CHECK: [[FOR_COND_CLEANUP:.*:]]
; CHECK-NEXT: ret void
@@ -65,11 +151,12 @@ for.body:
%val = load i32, ptr %loc2
store i32 %val, ptr %loc
call void @fn_write_inaccessible_mem()
- call void @fn_read_inaccessible_mem()
+ call void @fn_read_inaccessible_mem(i32 %val)
br label %for.body
}
-
+;; Should NOT hoist fn_write_inaccessible_mem
+;; because fn_read_inaccessible_mem reads the same memory location
define void @neg_hoist_untill_I(ptr noalias %loc, ptr noalias %loc2){
; CHECK-LABEL: define void @neg_hoist_untill_I(
; CHECK-SAME: ptr noalias [[LOC:%.*]], ptr noalias [[LOC2:%.*]]) {
@@ -78,9 +165,9 @@ define void @neg_hoist_untill_I(ptr noalias %loc, ptr noalias %loc2){
; CHECK-NEXT: br label %[[FOR_BODY:.*]]
; CHECK: [[FOR_BODY]]:
; CHECK-NEXT: store i32 [[VAL]], ptr [[LOC]], align 4
-; CHECK-NEXT: call void @fn_read_inaccessible_mem()
+; CHECK-NEXT: call void @fn_read_inaccessible_mem(i32 0)
; CHECK-NEXT: call void @fn_write_inaccessible_mem()
-; CHECK-NEXT: call void @fn_read_inaccessible_mem()
+; CHECK-NEXT: call void @fn_read_inaccessible_mem(i32 [[VAL]])
; CHECK-NEXT: br label %[[FOR_BODY]]
;
entry:
@@ -88,24 +175,25 @@ entry:
for.body:
%val = load i32, ptr %loc2
store i32 %val, ptr %loc
- call void @fn_read_inaccessible_mem()
+ call void @fn_read_inaccessible_mem(i32 0)
call void @fn_write_inaccessible_mem()
- call void @fn_read_inaccessible_mem()
+ call void @fn_read_inaccessible_mem(i32 %val)
br label %for.body
}
-; Nothing should be hoisted from the loop because volatile
-; sets inaccessible memory to read write
+;; Nothing should be hoisted from the loop because volatile
+;; sets inaccessible memory to read write
define void @neg_volatile(ptr %loc, ptr %loc2) {
; CHECK-LABEL: define void @neg_volatile(
; CHECK-SAME: ptr [[LOC:%.*]], ptr [[LOC2:%.*]]) {
; CHECK-NEXT: [[ENTRY:.*:]]
; CHECK-NEXT: br label %[[LOOP:.*]]
; CHECK: [[LOOP]]:
+; CHECK-NEXT: [[VAL:%.*]] = load i32, ptr [[LOC2]], align 4
; CHECK-NEXT: store volatile i32 0, ptr [[LOC]], align 4
; CHECK-NEXT: call void @fn_write_inaccessible_mem()
-; CHECK-NEXT: call void @fn_read_inaccessible_mem()
+; CHECK-NEXT: call void @fn_read_inaccessible_mem(i32 [[VAL]])
; CHECK-NEXT: br label %[[LOOP]]
;
entry:
@@ -115,14 +203,14 @@ loop:
%val = load i32, ptr %loc2
store volatile i32 0, ptr %loc
call void @fn_write_inaccessible_mem()
- call void @fn_read_inaccessible_mem()
+ call void @fn_read_inaccessible_mem(i32 %val)
br label %loop
}
declare void @fn_write_inaccessible_mem()#0
memory(inaccessiblemem: write)
-declare void @fn_read_inaccessible_mem()#0
+declare void @fn_read_inaccessible_mem(i32)#0
memory(inaccessiblemem: read)
declare void @fn_readwrite_inaccessible_mem()#0
diff --git a/llvm/test/Transforms/LICM/pr50367.ll b/llvm/test/Transforms/LICM/pr50367.ll
index 6aafff74f61d8..3e3e5c92cd481 100644
--- a/llvm/test/Transforms/LICM/pr50367.ll
+++ b/llvm/test/Transforms/LICM/pr50367.ll
@@ -44,6 +44,9 @@ define void @store_null(i1 %arg) {
; CHECK-LABEL: define void @store_null(
; CHECK-SAME: i1 [[ARG:%.*]]) {
; CHECK-NEXT: [[ENTRY:.*:]]
+; CHECK-NEXT: store ptr null, ptr @e, align 8, !tbaa [[ANYPTR_TBAA0]]
+; CHECK-NEXT: [[PTR:%.*]] = load ptr, ptr @e, align 8, !tbaa [[ANYPTR_TBAA0]]
+; CHECK-NEXT: store i32 0, ptr [[PTR]], align 4, !tbaa [[INT_TBAA4]]
; CHECK-NEXT: br label %[[LOOP1:.*]]
; CHECK: [[LOOP1]]:
; CHECK-NEXT: br label %[[LOOP2:.*]]
@@ -53,7 +56,6 @@ define void @store_null(i1 %arg) {
; CHECK-NEXT: store i32 0, ptr null, align 4
; CHECK-NEXT: br label %[[LOOP2]]
; CHECK: [[LOOP_LATCH]]:
-; CHECK-NEXT: store i32 0, ptr null, align 4, !tbaa [[INT_TBAA4]]
; CHECK-NEXT: br label %[[LOOP1]]
;
entry:
diff --git a/llvm/test/Transforms/LICM/store-hoisting.ll b/llvm/test/Transforms/LICM/store-hoisting.ll
index 016364fff9fbb..4ca7b4d48409a 100644
--- a/llvm/test/Transforms/LICM/store-hoisting.ll
+++ b/llvm/test/Transforms/LICM/store-hoisting.ll
@@ -188,20 +188,20 @@ define void @neg_ref(ptr %loc) {
; CHECK-LABEL: define void @neg_ref(
; CHECK-SAME: ptr [[LOC:%.*]]) {
; CHECK-NEXT: [[ENTRY:.*]]:
+; CHECK-NEXT: store i32 0, ptr [[LOC]], align 4
+; CHECK-NEXT: [[V:%.*]] = load i32, ptr [[LOC]], align 4
+; CHECK-NEXT: [[EARLYCND:%.*]] = icmp eq i32 [[V]], 198
; CHECK-NEXT: br label %[[LOOP:.*]]
; CHECK: [[LOOP]]:
; CHECK-NEXT: [[IV:%.*]] = phi i32 [ 0, %[[ENTRY]] ], [ [[IV_NEXT:%.*]], %[[BACKEDGE:.*]] ]
-; CHECK-NEXT: [[EARLYCND:%.*]] = icmp eq i32 0, 198
; CHECK-NEXT: br i1 [[EARLYCND]], label %[[EXIT1:.*]], label %[[BACKEDGE]]
; CHECK: [[BACKEDGE]]:
; CHECK-NEXT: [[IV_NEXT]] = add i32 [[IV]], 1
; CHECK-NEXT: [[CMP:%.*]] = icmp slt i32 [[IV]], 200
; CHECK-NEXT: br i1 [[CMP]], label %[[LOOP]], label %[[EXIT2:.*]]
; CHECK: [[EXIT1]]:
-; CHECK-NEXT: store i32 0, ptr [[LOC]], align 4
; CHECK-NEXT: ret void
; CHECK: [[EXIT2]]:
-; CHECK-NEXT: store i32 0, ptr [[LOC]], align 4
; CHECK-NEXT: ret void
;
entry:
@@ -588,10 +588,10 @@ define void @test_dominated_readonly(ptr %loc) {
; CHECK-LABEL: define void @test_dominated_readonly(
; CHECK-SAME: ptr [[LOC:%.*]]) {
; CHECK-NEXT: [[ENTRY:.*]]:
+; CHECK-NEXT: store i32 0, ptr [[LOC]], align 4
; CHECK-NEXT: br label %[[LOOP:.*]]
; CHECK: [[LOOP]]:
; CHECK-NEXT: [[IV:%.*]] = phi i32 [ 0, %[[ENTRY]] ], [ [[IV_NEXT:%.*]], %[[LOOP]] ]
-; CHECK-NEXT: store i32 0, ptr [[LOC]], align 4
; CHECK-NEXT: call void @readonly()
; CHECK-NEXT: [[IV_NEXT]] = add i32 [[IV]], 1
; CHECK-NEXT: [[CMP:%.*]] = icmp slt i32 [[IV]], 200
diff --git a/llvm/test/Transforms/LoopVectorize/invariant-store-vectorization.ll b/llvm/test/Transforms/LoopVectorize/invariant-store-vectorization.ll
index 7c4263617a6d9..944d0ac633418 100644
--- a/llvm/test/Transforms/LoopVectorize/invariant-store-vectorization.ll
+++ b/llvm/test/Transforms/LoopVectorize/invariant-store-vectorization.ll
@@ -22,7 +22,7 @@ define i32 @inv_val_store_to_inv_address_with_reduction(ptr %a, i64 %n, ptr %b)
; CHECK-NEXT: [[NTRUNC:%.*]] = trunc i64 [[N:%.*]] to i32
; CHECK-NEXT: [[SMAX2:%.*]] = call i64 @llvm.smax.i64(i64 [[N]], i64 1)
; CHECK-NEXT: [[MIN_ITERS_CHECK:%.*]] = icmp slt i64 [[N]], 4
-; CHECK-NEXT: br i1 [[MIN_ITERS_CHECK]], label [[SCALAR_PH:%.*]], label [[VECTOR_MEMCHECK:%.*]]
+; CHECK-NEXT: br i1 [[MIN_ITERS_CHECK]], label [[SCALAR_PH:%.*]], label [[VECTOR_PH:%.*]]
; CHECK: vector.memcheck:
; CHECK-NEXT: [[SCEVGEP:%.*]] = getelementptr i8, ptr [[A:%.*]], i64 4
; CHECK-NEXT: [[TMP0:%.*]] = shl i64 [[N]], 2
@@ -30,13 +30,13 @@ define i32 @inv_val_store_to_inv_address_with_reduction(ptr %a, i64 %n, ptr %b)
; CHECK-NEXT: [[BOUND0:%.*]] = icmp ult ptr [[A]], [[SCEVGEP1]]
; CHECK-NEXT: [[BOUND1:%.*]] = icmp ult ptr [[B]], [[SCEVGEP]]
; CHECK-NEXT: [[FOUND_CONFLICT:%.*]] = and i1 [[BOUND0]], [[BOUND1]]
-; CHECK-NEXT: br i1 [[FOUND_CONFLICT]], label [[SCALAR_PH]], label [[VECTOR_PH:%.*]]
+; CHECK-NEXT: br i1 [[FOUND_CONFLICT]], label [[SCALAR_PH]], label [[VECTOR_PH1:%.*]]
; CHECK: vector.ph:
; CHECK-NEXT: [[N_VEC:%.*]] = and i64 [[SMAX2]], 9223372036854775804
; CHECK-NEXT: br label [[VECTOR_BODY:%.*]]
; CHECK: vector.body:
-; CHECK-NEXT: [[INDEX:%.*]] = phi i64 [ 0, [[VECTOR_PH]] ], [ [[INDEX_NEXT:%.*]], [[VECTOR_BODY]] ]
-; CHECK-NEXT: [[VEC_PHI:%.*]] = phi <4 x i32> [ zeroinitializer, [[VECTOR_PH]] ], [ [[TMP2:%.*]], [[VECTOR_BODY]] ]
+; CHECK-NEXT: [[INDEX:%.*]] = phi i64 [ 0, [[VECTOR_PH1]] ], [ [[INDEX_NEXT:%.*]], [[VECTOR_BODY]] ]
+; CHECK-NEXT: [[VEC_PHI:%.*]] = phi <4 x i32> [ zeroinitializer, [[VECTOR_PH1]] ], [ [[TMP2:%.*]], [[VECTOR_BODY]] ]
; CHECK-NEXT: [[TMP1:%.*]] = getelementptr inbounds [4 x i8], ptr [[B]], i64 [[INDEX]]
; CHECK-NEXT: [[WIDE_LOAD:%.*]] = load <4 x i32>, ptr [[TMP1]], align 8, !alias.scope [[META0:![0-9]+]]
; CHECK-NEXT: [[TMP2]] = add <4 x i32> [[VEC_PHI]], [[WIDE_LOAD]]
@@ -50,8 +50,8 @@ define i32 @inv_val_store_to_inv_address_with_reduction(ptr %a, i64 %n, ptr %b)
; CHECK-NEXT: [[CMP_N:%.*]] = icmp eq i64 [[SMAX2]], [[N_VEC]]
; CHECK-NEXT: br i1 [[CMP_N]], label [[FOR_END:%.*]], label [[SCALAR_PH]]
; CHECK: scalar.ph:
-; CHECK-NEXT: [[BC_RESUME_VAL:%.*]] = phi i64 [ [[N_VEC]], [[MIDDLE_BLOCK]] ], [ 0, [[ENTRY:%.*]] ], [ 0, [[VECTOR_MEMCHECK]] ]
-; CHECK-NEXT: [[BC_MERGE_RDX:%.*]] = phi i32 [ [[TMP4]], [[MIDDLE_BLOCK]] ], [ 0, [[ENTRY]] ], [ 0, [[VECTOR_MEMCHECK]] ]
+; CHECK-NEXT: [[BC_RESUME_VAL:%.*]] = phi i64 [ [[N_VEC]], [[MIDDLE_BLOCK]] ], [ 0, [[ENTRY:%.*]] ], [ 0, [[VECTOR_PH]] ]
+; CHECK-NEXT: [[BC_MERGE_RDX:%.*]] = phi i32 [ [[TMP4]], [[MIDDLE_BLOCK]] ], [ 0, [[ENTRY]] ], [ 0, [[VECTOR_PH]] ]
; CHECK-NEXT: br label [[FOR_BODY:%.*]]
; CHECK: for.body:
; CHECK-NEXT: [[I:%.*]] = phi i64 [ [[I_NEXT:%.*]], [[FOR_BODY]] ], [ [[BC_RESUME_VAL]], [[SCALAR_PH]] ]
>From 7d584c6c24247cb8c9fb1f32dd4882527389196c Mon Sep 17 00:00:00 2001
From: CarolineConcatto <caroline.concatto at arm.com>
Date: Thu, 23 Apr 2026 15:36:46 +0000
Subject: [PATCH 3/5] Add domnitates as a check for hoist
---
llvm/lib/Transforms/Scalar/LICM.cpp | 7 +--
.../2011-04-06-PromoteResultOfPromotion.ll | 17 +++----
.../LICM/hoist-inaccesiblemem-call.ll | 45 ++-----------------
llvm/test/Transforms/LICM/pr59324.ll | 4 +-
4 files changed, 15 insertions(+), 58 deletions(-)
diff --git a/llvm/lib/Transforms/Scalar/LICM.cpp b/llvm/lib/Transforms/Scalar/LICM.cpp
index 2688a89af352c..68d5aa5fd2247 100644
--- a/llvm/lib/Transforms/Scalar/LICM.cpp
+++ b/llvm/lib/Transforms/Scalar/LICM.cpp
@@ -2333,12 +2333,7 @@ static bool noConflictingReadWrites(Instruction *I, MemorySSA *MSSA,
auto *MD = getClobberingMemoryAccess(*MSSA, BAA, Flags,
const_cast<MemoryUse *>(MU));
if (!MSSA->isLiveOnEntryDef(MD) && CurLoop->contains(MD->getBlock())) {
- auto *MDI = dyn_cast_or_null<MemoryDef>(MD);
- // It checks only if I is clobbering.
- // If IMD is not the same as I (that it wants to hoist), assumes they
- // clobber
- // Sync store is not allowed, but store hoist is allowed
- if (!Flags.getIsSink() && (!MDI || MDI->getMemoryInst() != I))
+ if (!Flags.getIsSink() && !MSSA->dominates(IMD, MU))
return false;
}
} else if (const auto *MD = dyn_cast<MemoryDef>(&MA)) {
diff --git a/llvm/test/Transforms/LICM/2011-04-06-PromoteResultOfPromotion.ll b/llvm/test/Transforms/LICM/2011-04-06-PromoteResultOfPromotion.ll
index 0d32e508edf5f..07352a299f76b 100644
--- a/llvm/test/Transforms/LICM/2011-04-06-PromoteResultOfPromotion.ll
+++ b/llvm/test/Transforms/LICM/2011-04-06-PromoteResultOfPromotion.ll
@@ -9,10 +9,12 @@ define void @f() {
; CHECK-LABEL: define void @f() {
; CHECK-NEXT: [[ENTRY:.*]]:
; CHECK-NEXT: [[L_87_I:%.*]] = alloca [9 x i16], align 16
-; CHECK-NEXT: [[G_58_PROMOTED:%.*]] = load i32, ptr @g_58, align 4, !tbaa [[INT_TBAA0:![0-9]+]]
+; CHECK-NEXT: store ptr @g_58, ptr @g_116, align 8, !tbaa [[ANYPTR_TBAA0:![0-9]+]]
+; CHECK-NEXT: [[TMP2:%.*]] = load ptr, ptr @g_116, align 8, !tbaa [[ANYPTR_TBAA0]]
+; CHECK-NEXT: [[TMP2_PROMOTED:%.*]] = load i32, ptr [[TMP2]], align 4, !tbaa [[INT_TBAA4:![0-9]+]]
; CHECK-NEXT: br label %[[FOR_BODY:.*]]
; CHECK: [[FOR_BODY]]:
-; CHECK-NEXT: [[TMP31:%.*]] = phi i32 [ [[G_58_PROMOTED]], %[[ENTRY]] ], [ [[OR:%.*]], %[[FOR_BODY]] ]
+; CHECK-NEXT: [[TMP31:%.*]] = phi i32 [ [[TMP2_PROMOTED]], %[[ENTRY]] ], [ [[OR:%.*]], %[[FOR_BODY]] ]
; CHECK-NEXT: [[INC12:%.*]] = phi i32 [ 0, %[[ENTRY]] ], [ [[INC:%.*]], %[[FOR_BODY]] ]
; CHECK-NEXT: [[OR]] = or i32 [[TMP31]], 10
; CHECK-NEXT: [[INC]] = add nsw i32 [[INC12]], 1
@@ -20,8 +22,7 @@ define void @f() {
; CHECK-NEXT: br i1 [[CMP]], label %[[FOR_BODY]], label %[[FOR_END:.*]]
; CHECK: [[FOR_END]]:
; CHECK-NEXT: [[OR_LCSSA:%.*]] = phi i32 [ [[OR]], %[[FOR_BODY]] ]
-; CHECK-NEXT: store ptr @g_58, ptr @g_116, align 8, !tbaa [[ANYPTR_TBAA4:![0-9]+]]
-; CHECK-NEXT: store i32 [[OR_LCSSA]], ptr @g_58, align 4, !tbaa [[INT_TBAA0]]
+; CHECK-NEXT: store i32 [[OR_LCSSA]], ptr [[TMP2]], align 4, !tbaa [[INT_TBAA4]]
; CHECK-NEXT: ret void
;
@@ -52,10 +53,10 @@ for.end: ; preds = %for.inc
!5 = !{!"any pointer", !1}
!6 = !{!"int", !1}
;.
-; CHECK: [[INT_TBAA0]] = !{[[META1:![0-9]+]], [[META1]], i64 0}
-; CHECK: [[META1]] = !{!"int", [[META2:![0-9]+]]}
+; CHECK: [[ANYPTR_TBAA0]] = !{[[META1:![0-9]+]], [[META1]], i64 0}
+; CHECK: [[META1]] = !{!"any pointer", [[META2:![0-9]+]]}
; CHECK: [[META2]] = !{!"omnipotent char", [[META3:![0-9]+]]}
; CHECK: [[META3]] = !{!"Simple C/C++ TBAA"}
-; CHECK: [[ANYPTR_TBAA4]] = !{[[META5:![0-9]+]], [[META5]], i64 0}
-; CHECK: [[META5]] = !{!"any pointer", [[META2]]}
+; CHECK: [[INT_TBAA4]] = !{[[META5:![0-9]+]], [[META5]], i64 0}
+; CHECK: [[META5]] = !{!"int", [[META2]]}
;.
diff --git a/llvm/test/Transforms/LICM/hoist-inaccesiblemem-call.ll b/llvm/test/Transforms/LICM/hoist-inaccesiblemem-call.ll
index 114be4532f7c0..24b843730e1fa 100644
--- a/llvm/test/Transforms/LICM/hoist-inaccesiblemem-call.ll
+++ b/llvm/test/Transforms/LICM/hoist-inaccesiblemem-call.ll
@@ -46,45 +46,6 @@ after_loop:
declare i32 @fn_args(ptr) nounwind willreturn
memory(argmem: read)
-;; It should be able to hoist fn_write_inaccessible_mem
-define dso_local i32 @loop_alias_store(i32 %x, ptr %a) #0 {
-; CHECK-LABEL: define dso_local i32 @loop_alias_store(
-; CHECK-SAME: i32 [[X:%.*]], ptr [[A:%.*]]) #[[ATTR0]] {
-; CHECK-NEXT: [[ENTRY:.*]]:
-; CHECK-NEXT: [[VAL:%.*]] = getelementptr inbounds nuw i32, ptr [[A]], i64 1
-; CHECK-NEXT: br label %[[LOOP:.*]]
-; CHECK: [[LOOP]]:
-; CHECK-NEXT: [[PHI:%.*]] = phi ptr [ [[GEP:%.*]], %[[LOOP]] ], [ [[VAL]], %[[ENTRY]] ]
-; CHECK-NEXT: [[TMP0:%.*]] = load i32, ptr [[PHI]], align 16
-; CHECK-NEXT: [[GEP]] = getelementptr i8, ptr [[PHI]], i64 0
-; CHECK-NEXT: call void @fn_write_inaccessible_mem()
-; CHECK-NEXT: call void @fn_read_inaccessible_mem(i32 [[TMP0]])
-; CHECK-NEXT: [[ACC:%.*]] = add nuw nsw i32 [[TMP0]], 1
-; CHECK-NEXT: store i32 [[ACC]], ptr [[A]], align 16
-; CHECK-NEXT: [[CMP:%.*]] = icmp ult i32 [[ACC]], 10
-; CHECK-NEXT: br i1 [[CMP]], label %[[LOOP]], label %[[AFTER_LOOP:.*]]
-; CHECK: [[AFTER_LOOP]]:
-; CHECK-NEXT: [[ACC_LCSSA:%.*]] = phi i32 [ [[ACC]], %[[LOOP]] ]
-; CHECK-NEXT: ret i32 [[ACC_LCSSA]]
-;
-entry:
- %val = getelementptr inbounds nuw i32, ptr %a, i64 1
- br label %loop
-loop:
- %phi = phi ptr [ %gep, %loop ], [ %val, %entry ]
- %44 = load i32, ptr %phi, align 16
- %gep = getelementptr i8, ptr %phi, i64 0
- %val2 = call i32 @fn_args(ptr %gep)
- call void @fn_write_inaccessible_mem()
- call void @fn_read_inaccessible_mem(i32 %44)
- %acc = add nuw nsw i32 %44, 1
- store i32 %acc, ptr %a, align 16
- %cmp = icmp ult i32 %acc, 10
- br i1 %cmp, label %loop, label %after_loop
-after_loop:
- ret i32 %acc
-}
-
;; It should NOT hoist fn_write_inaccessible_mem
;; Because fn_args_2 reads same memory location
define dso_local i32 @ne_loop_alias(i32 %x, ptr %a, ptr %b) #0 {
@@ -134,13 +95,13 @@ define void @hoist_untill_I(ptr noalias %loc, ptr noalias %loc2){
; CHECK-SAME: ptr noalias [[LOC:%.*]], ptr noalias [[LOC2:%.*]]) {
; CHECK-NEXT: [[ENTRY:.*:]]
; CHECK-NEXT: [[VAL:%.*]] = load i32, ptr [[LOC2]], align 4
+; CHECK-NEXT: store i32 [[VAL]], ptr [[LOC]], align 4
; CHECK-NEXT: call void @fn_write_inaccessible_mem()
; CHECK-NEXT: call void @fn_read_inaccessible_mem(i32 [[VAL]])
; CHECK-NEXT: br label %[[FOR_BODY:.*]]
; CHECK: [[FOR_COND_CLEANUP:.*:]]
; CHECK-NEXT: ret void
; CHECK: [[FOR_BODY]]:
-; CHECK-NEXT: store i32 [[VAL]], ptr [[LOC]], align 4
; CHECK-NEXT: br label %[[FOR_BODY]]
;
entry:
@@ -149,7 +110,7 @@ for.cond.cleanup: ; preds = %for.body
ret void
for.body:
%val = load i32, ptr %loc2
- store i32 %val, ptr %loc
+ store i32 %val, ptr %loc ;; This is blocking hoisting
call void @fn_write_inaccessible_mem()
call void @fn_read_inaccessible_mem(i32 %val)
br label %for.body
@@ -162,9 +123,9 @@ define void @neg_hoist_untill_I(ptr noalias %loc, ptr noalias %loc2){
; CHECK-SAME: ptr noalias [[LOC:%.*]], ptr noalias [[LOC2:%.*]]) {
; CHECK-NEXT: [[ENTRY:.*:]]
; CHECK-NEXT: [[VAL:%.*]] = load i32, ptr [[LOC2]], align 4
+; CHECK-NEXT: store i32 [[VAL]], ptr [[LOC]], align 4
; CHECK-NEXT: br label %[[FOR_BODY:.*]]
; CHECK: [[FOR_BODY]]:
-; CHECK-NEXT: store i32 [[VAL]], ptr [[LOC]], align 4
; CHECK-NEXT: call void @fn_read_inaccessible_mem(i32 0)
; CHECK-NEXT: call void @fn_write_inaccessible_mem()
; CHECK-NEXT: call void @fn_read_inaccessible_mem(i32 [[VAL]])
diff --git a/llvm/test/Transforms/LICM/pr59324.ll b/llvm/test/Transforms/LICM/pr59324.ll
index ec33a0f8ded0f..bb5570fec8b56 100644
--- a/llvm/test/Transforms/LICM/pr59324.ll
+++ b/llvm/test/Transforms/LICM/pr59324.ll
@@ -4,10 +4,10 @@
define void @test(ptr %a) {
; CHECK-LABEL: @test(
; CHECK-NEXT: entry:
-; CHECK-NEXT: br label [[LOOP:%.*]]
-; CHECK: loop:
; CHECK-NEXT: store ptr null, ptr null, align 8
; CHECK-NEXT: [[P:%.*]] = load ptr, ptr null, align 8
+; CHECK-NEXT: br label [[LOOP:%.*]]
+; CHECK: loop:
; CHECK-NEXT: [[V:%.*]] = load i32, ptr [[P]], align 4
; CHECK-NEXT: store i32 [[V]], ptr [[A:%.*]], align 4
; CHECK-NEXT: br label [[LOOP]]
>From 187d270912256233f977f69ae7c7aa48763045c2 Mon Sep 17 00:00:00 2001
From: CarolineConcatto <caroline.concatto at arm.com>
Date: Thu, 23 Apr 2026 16:28:30 +0000
Subject: [PATCH 4/5] Address review comments
---
llvm/lib/Transforms/Scalar/LICM.cpp | 11 ++-
llvm/test/Analysis/MemorySSA/pr43427.ll | 22 +++--
llvm/test/Transforms/LICM/call-hoisting.ll | 14 +--
llvm/test/Transforms/LICM/guards.ll | 6 +-
.../Transforms/LICM/hoist-debuginvariant.ll | 2 +-
.../LICM/hoist-inaccesiblemem-call.ll | 92 ++++++++-----------
llvm/test/Transforms/LICM/invariant.start.ll | 10 +-
llvm/test/Transforms/LICM/store-hoisting.ll | 5 +-
.../invariant-store-vectorization.ll | 12 +--
9 files changed, 80 insertions(+), 94 deletions(-)
diff --git a/llvm/lib/Transforms/Scalar/LICM.cpp b/llvm/lib/Transforms/Scalar/LICM.cpp
index 68d5aa5fd2247..330464fdb64ff 100644
--- a/llvm/lib/Transforms/Scalar/LICM.cpp
+++ b/llvm/lib/Transforms/Scalar/LICM.cpp
@@ -2328,14 +2328,14 @@ static bool noConflictingReadWrites(Instruction *I, MemorySSA *MSSA,
auto *Accesses = MSSA->getBlockAccesses(BB);
if (!Accesses)
continue;
- for (const auto &MA : *Accesses)
+ for (const auto &MA : *Accesses) {
+ if (!Flags.getIsSink() && MSSA->dominates(IMD, &MA))
+ continue;
if (const auto *MU = dyn_cast<MemoryUse>(&MA)) {
auto *MD = getClobberingMemoryAccess(*MSSA, BAA, Flags,
const_cast<MemoryUse *>(MU));
- if (!MSSA->isLiveOnEntryDef(MD) && CurLoop->contains(MD->getBlock())) {
- if (!Flags.getIsSink() && !MSSA->dominates(IMD, MU))
- return false;
- }
+ if (!MSSA->isLiveOnEntryDef(MD) && CurLoop->contains(MD->getBlock()))
+ return false;
} else if (const auto *MD = dyn_cast<MemoryDef>(&MA)) {
if (auto *LI = dyn_cast<LoadInst>(MD->getMemoryInst())) {
(void)LI; // Silence warning.
@@ -2363,6 +2363,7 @@ static bool noConflictingReadWrites(Instruction *I, MemorySSA *MSSA,
}
}
}
+ }
}
return true;
}
diff --git a/llvm/test/Analysis/MemorySSA/pr43427.ll b/llvm/test/Analysis/MemorySSA/pr43427.ll
index 254fb1104c590..074fe39ff0de7 100644
--- a/llvm/test/Analysis/MemorySSA/pr43427.ll
+++ b/llvm/test/Analysis/MemorySSA/pr43427.ll
@@ -2,9 +2,15 @@
; CHECK-LABEL: @f(i1 %arg)
+; CHECK: entry:
+; CHECK-NEXT: %e = alloca i16, align 1
+; CHECK-NEXT:; [[NO1:.*]] = MemoryDef(liveOnEntry)
+; CHECK-NEXT: store i16 undef, ptr %e, align 1
+; CHECK-NEXT: br label %lbl1
+
; CHECK: lbl1:
-; CHECK-NEXT: ; [[NO4:.*]] = MemoryPhi({entry,liveOnEntry},{lbl1.backedge,[[NO9:.*]]})
-; CHECK-NEXT: ; [[NO2:.*]] = MemoryDef([[NO4]])
+; CHECK-NEXT: ; [[NO4:.*]] = MemoryPhi({entry,1},{lbl1.backedge,[[NO2:.*]]})
+; CHECK-NEXT: ; [[NO2]] = MemoryDef([[NO4]])
; CHECK-NEXT: call void @g()
; CHECK-NEXT: br i1 %arg, label %for.end, label %if.else
@@ -12,26 +18,24 @@
; CHECK-NEXT: br i1 %arg, label %lbl3, label %lbl2
; CHECK: lbl2:
-; CHECK-NEXT: ; [[NO8:.*]] = MemoryPhi({lbl3,[[NO7:.*]]},{for.end,[[NO2]]})
; CHECK-NEXT: br label %lbl3
; CHECK: lbl3:
-; CHECK-NEXT: [[NO7]] = MemoryPhi({lbl2,[[NO8]]},{for.end,2})
+; CHECK-NEXT: br i1 %arg, label %lbl2, label %cleanup
; CHECK: cleanup:
; CHECK-NEXT: MemoryUse([[NO2]])
; CHECK-NEXT: %cleanup.dest = load i32, ptr undef, align 1
+; CHECK-NEXT: %switch = icmp ult i32 %cleanup.dest, 1
+; CHECK-NEXT: br i1 %switch, label %cleanup.cont, label %lbl1.backedge
; CHECK: lbl1.backedge:
-; CHECK-NEXT: [[NO9]] = MemoryPhi({cleanup,[[NO7]]},{if.else,2})
; CHECK-NEXT: br label %lbl1
; CHECK: cleanup.cont:
-; CHECK-NEXT: ; [[NO6:.*]] = MemoryDef([[NO7]])
-; CHECK-NEXT: store i16 undef, ptr %e, align 1
-; CHECK-NEXT: 3 = MemoryDef([[NO6]])
+; CHECK-NEXT: ; [[NO3:.*]] = MemoryDef([[NO2]])
; CHECK-NEXT: call void @g()
-
+; CHECK-NEXT: ret void
define void @f(i1 %arg) {
entry:
%e = alloca i16, align 1
diff --git a/llvm/test/Transforms/LICM/call-hoisting.ll b/llvm/test/Transforms/LICM/call-hoisting.ll
index fb8c7280dba23..02a90fb68040d 100644
--- a/llvm/test/Transforms/LICM/call-hoisting.ll
+++ b/llvm/test/Transforms/LICM/call-hoisting.ll
@@ -399,13 +399,13 @@ exit:
define void @neg_ref(ptr %loc) {
; CHECK-LABEL: define void @neg_ref(
; CHECK-SAME: ptr [[LOC:%.*]]) {
-; CHECK-NEXT: [[LOOP1:.*]]:
+; CHECK-NEXT: [[ENTRY:.*]]:
; CHECK-NEXT: call void @store(i32 0, ptr [[LOC]])
; CHECK-NEXT: [[V:%.*]] = load i32, ptr [[LOC]], align 4
; CHECK-NEXT: [[EARLYCND:%.*]] = icmp eq i32 [[V]], 198
; CHECK-NEXT: br label %[[LOOP:.*]]
; CHECK: [[LOOP]]:
-; CHECK-NEXT: [[IV:%.*]] = phi i32 [ 0, %[[LOOP1]] ], [ [[IV_NEXT:%.*]], %[[BACKEDGE:.*]] ]
+; CHECK-NEXT: [[IV:%.*]] = phi i32 [ 0, %[[ENTRY]] ], [ [[IV_NEXT:%.*]], %[[BACKEDGE:.*]] ]
; CHECK-NEXT: br i1 [[EARLYCND]], label %[[EXIT1:.*]], label %[[BACKEDGE]]
; CHECK: [[BACKEDGE]]:
; CHECK-NEXT: [[IV_NEXT]] = add i32 [[IV]], 1
@@ -536,14 +536,14 @@ exit:
define i32 @neg_not_argmemonly(ptr %loc, ptr %loc2) {
; CHECK-LABEL: define i32 @neg_not_argmemonly(
; CHECK-SAME: ptr [[LOC:%.*]], ptr [[LOC2:%.*]]) {
-; CHECK-NEXT: [[LOOP:.*]]:
+; CHECK-NEXT: [[ENTRY:.*]]:
; CHECK-NEXT: call void @not_argmemonly(i32 0, ptr [[LOC]])
-; CHECK-NEXT: br label %[[LOOP1:.*]]
-; CHECK: [[LOOP1]]:
-; CHECK-NEXT: [[IV:%.*]] = phi i32 [ 0, %[[LOOP]] ], [ [[IV_NEXT:%.*]], %[[LOOP1]] ]
+; CHECK-NEXT: br label %[[LOOP:.*]]
+; CHECK: [[LOOP]]:
+; CHECK-NEXT: [[IV:%.*]] = phi i32 [ 0, %[[ENTRY]] ], [ [[IV_NEXT:%.*]], %[[LOOP]] ]
; CHECK-NEXT: [[IV_NEXT]] = add i32 [[IV]], 1
; CHECK-NEXT: [[CMP:%.*]] = icmp slt i32 [[IV]], 200
-; CHECK-NEXT: br i1 [[CMP]], label %[[LOOP1]], label %[[EXIT:.*]]
+; CHECK-NEXT: br i1 [[CMP]], label %[[LOOP]], label %[[EXIT:.*]]
; CHECK: [[EXIT]]:
; CHECK-NEXT: [[VAL_LE:%.*]] = call i32 @load(ptr [[LOC2]])
; CHECK-NEXT: ret i32 [[VAL_LE]]
diff --git a/llvm/test/Transforms/LICM/guards.ll b/llvm/test/Transforms/LICM/guards.ll
index 361d938ce19b8..0f08e59f20bfa 100644
--- a/llvm/test/Transforms/LICM/guards.ll
+++ b/llvm/test/Transforms/LICM/guards.ll
@@ -34,9 +34,9 @@ define void @test2(i1 %cond, ptr %ptr) {
; CHECK-NEXT: br label [[LOOP:%.*]]
; CHECK: loop:
; CHECK-NEXT: [[X:%.*]] = phi i32 [ 0, [[ENTRY:%.*]] ], [ [[X_INC:%.*]], [[LOOP]] ]
-; CHECK-NEXT: store i32 0, ptr [[PTR:%.*]], align 4
+; CHECK-NEXT: store i32 [[X]], ptr [[PTR:%.*]], align 4
; CHECK-NEXT: call void (i1, ...) @llvm.experimental.guard(i1 [[COND:%.*]]) [ "deopt"(i32 0) ]
-; CHECK-NEXT: [[X_INC]] = add i32 [[X]], 0
+; CHECK-NEXT: [[X_INC]] = add i32 [[X]], [[X]]
; CHECK-NEXT: br label [[LOOP]]
;
@@ -45,7 +45,7 @@ entry:
loop:
%x = phi i32 [ 0, %entry ], [ %x.inc, %loop ]
- store i32 0, ptr %ptr
+ store i32 %x, ptr %ptr
call void (i1, ...) @llvm.experimental.guard(i1 %cond) ["deopt" (i32 0)]
%val = load i32, ptr %ptr
%x.inc = add i32 %x, %val
diff --git a/llvm/test/Transforms/LICM/hoist-debuginvariant.ll b/llvm/test/Transforms/LICM/hoist-debuginvariant.ll
index e560ac4c55378..98e7bd6a5c03c 100644
--- a/llvm/test/Transforms/LICM/hoist-debuginvariant.ll
+++ b/llvm/test/Transforms/LICM/hoist-debuginvariant.ll
@@ -16,8 +16,8 @@ define void @fn1() !dbg !6 {
; CHECK-NEXT: [[_TMP2:%.*]] = load i32, ptr @a, align 4
; CHECK-NEXT: [[_TMP3:%.*]] = load i32, ptr @b, align 4
; CHECK-NEXT: [[_TMP4:%.*]] = sdiv i32 [[_TMP2]], [[_TMP3]]
+; CHECK-NEXT: store i32 [[_TMP4]], ptr @c, align 4
; CHECK-NEXT: br label [[BB3:%.*]]
-
br label %bb3
bb3: ; preds = %bb3, %0
diff --git a/llvm/test/Transforms/LICM/hoist-inaccesiblemem-call.ll b/llvm/test/Transforms/LICM/hoist-inaccesiblemem-call.ll
index 24b843730e1fa..0e5f0b0034ff1 100644
--- a/llvm/test/Transforms/LICM/hoist-inaccesiblemem-call.ll
+++ b/llvm/test/Transforms/LICM/hoist-inaccesiblemem-call.ll
@@ -1,24 +1,22 @@
; NOTE: Assertions have been autogenerated by utils/update_test_checks.py UTC_ARGS: --version 6
-; RUN: opt -aa-pipeline=basic-aa -passes='require<aa>,require<target-ir>,loop-mssa(licm)' < %s -S | FileCheck %s
-
+; RUN: opt -passes='loop-mssa(licm)' < %s -S | FileCheck %s
;; It should hoist fn_write_inaccessible_mem
;; because there is no conflict between inaccessible memory
;; fn_read_inaccessible_mem is a nice side effect
-define dso_local i32 @loop_alias(i32 %x, ptr %a, ptr %b) #0 {
-; CHECK-LABEL: define dso_local i32 @loop_alias(
-; CHECK-SAME: i32 [[X:%.*]], ptr [[A:%.*]], ptr [[B:%.*]]) #[[ATTR0:[0-9]+]] {
+define i32 @loop_alias(i64 %x) {
+; CHECK-LABEL: define i32 @loop_alias(
+; CHECK-SAME: i64 [[X:%.*]]) {
; CHECK-NEXT: [[ENTRY:.*]]:
-; CHECK-NEXT: [[VAL:%.*]] = getelementptr inbounds nuw i32, ptr [[A]], i64 1
; CHECK-NEXT: call void @fn_write_inaccessible_mem()
; CHECK-NEXT: br label %[[LOOP:.*]]
; CHECK: [[LOOP]]:
-; CHECK-NEXT: [[PHI:%.*]] = phi ptr [ [[GEP:%.*]], %[[LOOP]] ], [ [[VAL]], %[[ENTRY]] ]
-; CHECK-NEXT: [[GEP2:%.*]] = getelementptr i8, ptr [[PHI]], i64 0
-; CHECK-NEXT: [[VAL2:%.*]] = call i32 @fn_args(ptr [[GEP2]])
-; CHECK-NEXT: call void @fn_read_inaccessible_mem(i32 [[VAL2]])
-; CHECK-NEXT: [[GEP]] = getelementptr inbounds nuw i32, ptr [[PHI]], i64 0
-; CHECK-NEXT: [[ACC:%.*]] = add nuw nsw i32 [[VAL2]], 1
+; CHECK-NEXT: [[PHI:%.*]] = phi ptr [ null, %[[ENTRY]] ], [ [[GEP:%.*]], %[[LOOP]] ]
+; CHECK-NEXT: [[LOAD:%.*]] = load i32, ptr [[PHI]], align 16
+; CHECK-NEXT: [[VAL:%.*]] = call i32 @fn_args(i32 [[LOAD]])
+; CHECK-NEXT: call void @fn_read_inaccessible_mem(i32 [[LOAD]])
+; CHECK-NEXT: [[GEP]] = getelementptr inbounds nuw i32, ptr [[PHI]], i64 [[X]]
+; CHECK-NEXT: [[ACC:%.*]] = add nuw nsw i32 [[VAL]], 1
; CHECK-NEXT: [[CMP:%.*]] = icmp ult i32 [[ACC]], 10
; CHECK-NEXT: br i1 [[CMP]], label %[[LOOP]], label %[[AFTER_LOOP:.*]]
; CHECK: [[AFTER_LOOP]]:
@@ -26,42 +24,36 @@ define dso_local i32 @loop_alias(i32 %x, ptr %a, ptr %b) #0 {
; CHECK-NEXT: ret i32 [[ACC_LCSSA]]
;
entry:
- %val = getelementptr inbounds nuw i32, ptr %a, i64 1
br label %loop
loop:
- %phi = phi ptr [ %gep, %loop ], [ %val, %entry ]
- %44 = load i32, ptr %phi, align 16
- %gep2 = getelementptr i8, ptr %phi, i64 0
- %val2 = call i32 @fn_args(ptr %gep2)
+ %phi = phi ptr [ null, %entry ], [ %gep, %loop ]
+ %load = load i32, ptr %phi, align 16
+ %val = call i32 @fn_args(i32 %load)
call void @fn_write_inaccessible_mem()
- call void @fn_read_inaccessible_mem(i32 %val2)
- %gep = getelementptr inbounds nuw i32, ptr %phi, i64 0
- %acc = add nuw nsw i32 %val2, 1
+ call void @fn_read_inaccessible_mem(i32 %load)
+ %gep = getelementptr inbounds nuw i32, ptr %phi, i64 %x
+ %acc = add nuw nsw i32 %val, 1
%cmp = icmp ult i32 %acc, 10
br i1 %cmp, label %loop, label %after_loop
after_loop:
ret i32 %acc
}
-declare i32 @fn_args(ptr) nounwind willreturn
-memory(argmem: read)
-
;; It should NOT hoist fn_write_inaccessible_mem
;; Because fn_args_2 reads same memory location
-define dso_local i32 @ne_loop_alias(i32 %x, ptr %a, ptr %b) #0 {
-; CHECK-LABEL: define dso_local i32 @ne_loop_alias(
-; CHECK-SAME: i32 [[X:%.*]], ptr [[A:%.*]], ptr [[B:%.*]]) #[[ATTR0]] {
+define i32 @ne_loop_alias(i64 %x) {
+; CHECK-LABEL: define i32 @ne_loop_alias(
+; CHECK-SAME: i64 [[X:%.*]]) {
; CHECK-NEXT: [[ENTRY:.*]]:
-; CHECK-NEXT: [[VAL:%.*]] = getelementptr inbounds nuw i32, ptr [[A]], i64 1
; CHECK-NEXT: br label %[[LOOP:.*]]
; CHECK: [[LOOP]]:
-; CHECK-NEXT: [[PHI:%.*]] = phi ptr [ [[GEP:%.*]], %[[LOOP]] ], [ [[VAL]], %[[ENTRY]] ]
-; CHECK-NEXT: [[GEP2:%.*]] = getelementptr i8, ptr [[PHI]], i64 0
-; CHECK-NEXT: [[VAL2:%.*]] = call i32 @fn_args_2(ptr [[GEP2]])
+; CHECK-NEXT: [[PHI:%.*]] = phi ptr [ null, %[[ENTRY]] ], [ [[GEP:%.*]], %[[LOOP]] ]
+; CHECK-NEXT: [[LOAD:%.*]] = load i32, ptr [[PHI]], align 16
+; CHECK-NEXT: [[VAL:%.*]] = call i32 @fn_args_2(i32 [[LOAD]])
; CHECK-NEXT: call void @fn_write_inaccessible_mem()
-; CHECK-NEXT: call void @fn_read_inaccessible_mem(i32 [[VAL2]])
-; CHECK-NEXT: [[GEP]] = getelementptr inbounds nuw i32, ptr [[PHI]], i64 0
-; CHECK-NEXT: [[ACC:%.*]] = add nuw nsw i32 [[VAL2]], 1
+; CHECK-NEXT: call void @fn_read_inaccessible_mem(i32 [[VAL]])
+; CHECK-NEXT: [[GEP]] = getelementptr inbounds nuw i32, ptr [[PHI]], i64 [[X]]
+; CHECK-NEXT: [[ACC:%.*]] = add nuw nsw i32 [[VAL]], 1
; CHECK-NEXT: [[CMP:%.*]] = icmp ult i32 [[ACC]], 10
; CHECK-NEXT: br i1 [[CMP]], label %[[LOOP]], label %[[AFTER_LOOP:.*]]
; CHECK: [[AFTER_LOOP]]:
@@ -69,24 +61,20 @@ define dso_local i32 @ne_loop_alias(i32 %x, ptr %a, ptr %b) #0 {
; CHECK-NEXT: ret i32 [[ACC_LCSSA]]
;
entry:
- %val = getelementptr inbounds nuw i32, ptr %a, i64 1
br label %loop
loop:
- %phi = phi ptr [ %gep, %loop ], [ %val, %entry ]
- %44 = load i32, ptr %phi, align 16
- %gep2 = getelementptr i8, ptr %phi, i64 0
- %val2 = call i32 @fn_args_2(ptr %gep2)
+ %phi = phi ptr [ null, %entry ], [ %gep, %loop ]
+ %load = load i32, ptr %phi, align 16
+ %val = call i32 @fn_args_2(i32 %load)
call void @fn_write_inaccessible_mem()
- call void @fn_read_inaccessible_mem(i32 %val2)
- %gep = getelementptr inbounds nuw i32, ptr %phi, i64 0
- %acc = add nuw nsw i32 %val2, 1
+ call void @fn_read_inaccessible_mem(i32 %val)
+ %gep = getelementptr inbounds nuw i32, ptr %phi, i64 %x
+ %acc = add nuw nsw i32 %val, 1
%cmp = icmp ult i32 %acc, 10
br i1 %cmp, label %loop, label %after_loop
after_loop:
ret i32 %acc
}
-declare i32 @fn_args_2(ptr) nounwind willreturn
-memory(inaccessiblemem:read)
;; Should hoist fn_write_inaccessible_mem
;; It does not alias with store
@@ -110,7 +98,7 @@ for.cond.cleanup: ; preds = %for.body
ret void
for.body:
%val = load i32, ptr %loc2
- store i32 %val, ptr %loc ;; This is blocking hoisting
+ store i32 %val, ptr %loc
call void @fn_write_inaccessible_mem()
call void @fn_read_inaccessible_mem(i32 %val)
br label %for.body
@@ -168,14 +156,8 @@ loop:
br label %loop
}
-declare void @fn_write_inaccessible_mem()#0
- memory(inaccessiblemem: write)
-
-declare void @fn_read_inaccessible_mem(i32)#0
- memory(inaccessiblemem: read)
-
-declare void @fn_readwrite_inaccessible_mem()#0
- memory(inaccessiblemem: readwrite)
-
-; Needs to set nounwind because of doesNotThrow
-attributes #0 = { mustprogress nofree norecurse nosync nounwind}
+declare i32 @fn_args(i32) nounwind willreturn memory(argmem: read)
+declare i32 @fn_args_2(i32) nounwind willreturn memory(inaccessiblemem: read)
+declare void @fn_write_inaccessible_mem() nounwind memory(inaccessiblemem: write)
+declare void @fn_read_inaccessible_mem(i32) nounwind memory(inaccessiblemem: read)
+declare void @fn_readwrite_inaccessible_mem() nounwind memory(inaccessiblemem: readwrite)
diff --git a/llvm/test/Transforms/LICM/invariant.start.ll b/llvm/test/Transforms/LICM/invariant.start.ll
index c9c16423e2637..9c957267d3db9 100644
--- a/llvm/test/Transforms/LICM/invariant.start.ll
+++ b/llvm/test/Transforms/LICM/invariant.start.ll
@@ -85,9 +85,9 @@ define void @test4(i1 %cond, ptr %ptr) {
; CHECK-NEXT: br label [[LOOP:%.*]]
; CHECK: loop:
; CHECK-NEXT: [[X:%.*]] = phi i32 [ 0, [[ENTRY:%.*]] ], [ [[X_INC:%.*]], [[LOOP]] ]
-; CHECK-NEXT: store i32 0, ptr [[PTR:%.*]], align 4
+; CHECK-NEXT: store i32 [[X]], ptr [[PTR:%.*]], align 4
; CHECK-NEXT: [[TMP0:%.*]] = call ptr @llvm.invariant.start.p0(i64 4, ptr [[PTR]])
-; CHECK-NEXT: [[X_INC]] = add i32 [[X]], 0
+; CHECK-NEXT: [[X_INC]] = add i32 [[X]], [[X]]
; CHECK-NEXT: br label [[LOOP]]
;
entry:
@@ -95,7 +95,7 @@ entry:
loop:
%x = phi i32 [ 0, %entry ], [ %x.inc, %loop ]
- store i32 0, ptr %ptr
+ store i32 %x, ptr %ptr
call ptr @llvm.invariant.start.p0(i64 4, ptr %ptr)
%val = load i32, ptr %ptr
%x.inc = add i32 %x, %val
@@ -106,12 +106,12 @@ loop:
define void @test5(i1 %cond, ptr %ptr) {
; CHECK-LABEL: @test5(
; CHECK-NEXT: entry:
+; CHECK-NEXT: store i32 0, ptr [[PTR:%.*]], align 4
+; CHECK-NEXT: [[VAL:%.*]] = load i32, ptr [[PTR]], align 4
; CHECK-NEXT: br label [[LOOP:%.*]]
; CHECK: loop:
; CHECK-NEXT: [[X:%.*]] = phi i32 [ 0, [[ENTRY:%.*]] ], [ [[X_INC:%.*]], [[LOOP]] ]
-; CHECK-NEXT: store i32 0, ptr [[PTR:%.*]], align 4
; CHECK-NEXT: [[SCOPE:%.*]] = call ptr @llvm.invariant.start.p0(i64 4, ptr [[PTR]])
-; CHECK-NEXT: [[VAL:%.*]] = load i32, ptr [[PTR]], align 4
; CHECK-NEXT: call void @llvm.invariant.end.p0(ptr [[SCOPE]], i64 4, ptr [[PTR]])
; CHECK-NEXT: [[X_INC]] = add i32 [[X]], [[VAL]]
; CHECK-NEXT: br label [[LOOP]]
diff --git a/llvm/test/Transforms/LICM/store-hoisting.ll b/llvm/test/Transforms/LICM/store-hoisting.ll
index 4ca7b4d48409a..a746caec18e54 100644
--- a/llvm/test/Transforms/LICM/store-hoisting.ll
+++ b/llvm/test/Transforms/LICM/store-hoisting.ll
@@ -184,8 +184,8 @@ exit:
}
; Hoisting the store is actually valid here, as it dominates the load.
-define void @neg_ref(ptr %loc) {
-; CHECK-LABEL: define void @neg_ref(
+define void @ref(ptr %loc) {
+; CHECK-LABEL: define void @ref(
; CHECK-SAME: ptr [[LOC:%.*]]) {
; CHECK-NEXT: [[ENTRY:.*]]:
; CHECK-NEXT: store i32 0, ptr [[LOC]], align 4
@@ -583,7 +583,6 @@ exit:
declare void @readonly() readonly
-; TODO: can legally hoist since value read by call is known
define void @test_dominated_readonly(ptr %loc) {
; CHECK-LABEL: define void @test_dominated_readonly(
; CHECK-SAME: ptr [[LOC:%.*]]) {
diff --git a/llvm/test/Transforms/LoopVectorize/invariant-store-vectorization.ll b/llvm/test/Transforms/LoopVectorize/invariant-store-vectorization.ll
index 944d0ac633418..7c4263617a6d9 100644
--- a/llvm/test/Transforms/LoopVectorize/invariant-store-vectorization.ll
+++ b/llvm/test/Transforms/LoopVectorize/invariant-store-vectorization.ll
@@ -22,7 +22,7 @@ define i32 @inv_val_store_to_inv_address_with_reduction(ptr %a, i64 %n, ptr %b)
; CHECK-NEXT: [[NTRUNC:%.*]] = trunc i64 [[N:%.*]] to i32
; CHECK-NEXT: [[SMAX2:%.*]] = call i64 @llvm.smax.i64(i64 [[N]], i64 1)
; CHECK-NEXT: [[MIN_ITERS_CHECK:%.*]] = icmp slt i64 [[N]], 4
-; CHECK-NEXT: br i1 [[MIN_ITERS_CHECK]], label [[SCALAR_PH:%.*]], label [[VECTOR_PH:%.*]]
+; CHECK-NEXT: br i1 [[MIN_ITERS_CHECK]], label [[SCALAR_PH:%.*]], label [[VECTOR_MEMCHECK:%.*]]
; CHECK: vector.memcheck:
; CHECK-NEXT: [[SCEVGEP:%.*]] = getelementptr i8, ptr [[A:%.*]], i64 4
; CHECK-NEXT: [[TMP0:%.*]] = shl i64 [[N]], 2
@@ -30,13 +30,13 @@ define i32 @inv_val_store_to_inv_address_with_reduction(ptr %a, i64 %n, ptr %b)
; CHECK-NEXT: [[BOUND0:%.*]] = icmp ult ptr [[A]], [[SCEVGEP1]]
; CHECK-NEXT: [[BOUND1:%.*]] = icmp ult ptr [[B]], [[SCEVGEP]]
; CHECK-NEXT: [[FOUND_CONFLICT:%.*]] = and i1 [[BOUND0]], [[BOUND1]]
-; CHECK-NEXT: br i1 [[FOUND_CONFLICT]], label [[SCALAR_PH]], label [[VECTOR_PH1:%.*]]
+; CHECK-NEXT: br i1 [[FOUND_CONFLICT]], label [[SCALAR_PH]], label [[VECTOR_PH:%.*]]
; CHECK: vector.ph:
; CHECK-NEXT: [[N_VEC:%.*]] = and i64 [[SMAX2]], 9223372036854775804
; CHECK-NEXT: br label [[VECTOR_BODY:%.*]]
; CHECK: vector.body:
-; CHECK-NEXT: [[INDEX:%.*]] = phi i64 [ 0, [[VECTOR_PH1]] ], [ [[INDEX_NEXT:%.*]], [[VECTOR_BODY]] ]
-; CHECK-NEXT: [[VEC_PHI:%.*]] = phi <4 x i32> [ zeroinitializer, [[VECTOR_PH1]] ], [ [[TMP2:%.*]], [[VECTOR_BODY]] ]
+; CHECK-NEXT: [[INDEX:%.*]] = phi i64 [ 0, [[VECTOR_PH]] ], [ [[INDEX_NEXT:%.*]], [[VECTOR_BODY]] ]
+; CHECK-NEXT: [[VEC_PHI:%.*]] = phi <4 x i32> [ zeroinitializer, [[VECTOR_PH]] ], [ [[TMP2:%.*]], [[VECTOR_BODY]] ]
; CHECK-NEXT: [[TMP1:%.*]] = getelementptr inbounds [4 x i8], ptr [[B]], i64 [[INDEX]]
; CHECK-NEXT: [[WIDE_LOAD:%.*]] = load <4 x i32>, ptr [[TMP1]], align 8, !alias.scope [[META0:![0-9]+]]
; CHECK-NEXT: [[TMP2]] = add <4 x i32> [[VEC_PHI]], [[WIDE_LOAD]]
@@ -50,8 +50,8 @@ define i32 @inv_val_store_to_inv_address_with_reduction(ptr %a, i64 %n, ptr %b)
; CHECK-NEXT: [[CMP_N:%.*]] = icmp eq i64 [[SMAX2]], [[N_VEC]]
; CHECK-NEXT: br i1 [[CMP_N]], label [[FOR_END:%.*]], label [[SCALAR_PH]]
; CHECK: scalar.ph:
-; CHECK-NEXT: [[BC_RESUME_VAL:%.*]] = phi i64 [ [[N_VEC]], [[MIDDLE_BLOCK]] ], [ 0, [[ENTRY:%.*]] ], [ 0, [[VECTOR_PH]] ]
-; CHECK-NEXT: [[BC_MERGE_RDX:%.*]] = phi i32 [ [[TMP4]], [[MIDDLE_BLOCK]] ], [ 0, [[ENTRY]] ], [ 0, [[VECTOR_PH]] ]
+; CHECK-NEXT: [[BC_RESUME_VAL:%.*]] = phi i64 [ [[N_VEC]], [[MIDDLE_BLOCK]] ], [ 0, [[ENTRY:%.*]] ], [ 0, [[VECTOR_MEMCHECK]] ]
+; CHECK-NEXT: [[BC_MERGE_RDX:%.*]] = phi i32 [ [[TMP4]], [[MIDDLE_BLOCK]] ], [ 0, [[ENTRY]] ], [ 0, [[VECTOR_MEMCHECK]] ]
; CHECK-NEXT: br label [[FOR_BODY:%.*]]
; CHECK: for.body:
; CHECK-NEXT: [[I:%.*]] = phi i64 [ [[I_NEXT:%.*]], [[FOR_BODY]] ], [ [[BC_RESUME_VAL]], [[SCALAR_PH]] ]
>From 0701a06c3452e603c9cf7a56c0ebbdaea69cd5f1 Mon Sep 17 00:00:00 2001
From: CarolineConcatto <caroline.concatto at arm.com>
Date: Tue, 28 Apr 2026 09:39:31 +0000
Subject: [PATCH 5/5] Update tests
---
llvm/test/Transforms/LICM/call-hoisting.ll | 6 ++--
.../LICM/hoist-inaccesiblemem-call.ll | 34 +++++++++----------
llvm/test/Transforms/LICM/invariant.start.ll | 6 ++--
3 files changed, 24 insertions(+), 22 deletions(-)
diff --git a/llvm/test/Transforms/LICM/call-hoisting.ll b/llvm/test/Transforms/LICM/call-hoisting.ll
index 02a90fb68040d..2520e2277cf56 100644
--- a/llvm/test/Transforms/LICM/call-hoisting.ll
+++ b/llvm/test/Transforms/LICM/call-hoisting.ll
@@ -396,8 +396,10 @@ exit:
ret void
}
-define void @neg_ref(ptr %loc) {
-; CHECK-LABEL: define void @neg_ref(
+;; The store can be hoisted because the its value is loop-invariant.
+;; Does not depend on values modified inside the loop.
+define void @ref(ptr %loc) {
+; CHECK-LABEL: define void @ref(
; CHECK-SAME: ptr [[LOC:%.*]]) {
; CHECK-NEXT: [[ENTRY:.*]]:
; CHECK-NEXT: call void @store(i32 0, ptr [[LOC]])
diff --git a/llvm/test/Transforms/LICM/hoist-inaccesiblemem-call.ll b/llvm/test/Transforms/LICM/hoist-inaccesiblemem-call.ll
index 0e5f0b0034ff1..03ab270de12e0 100644
--- a/llvm/test/Transforms/LICM/hoist-inaccesiblemem-call.ll
+++ b/llvm/test/Transforms/LICM/hoist-inaccesiblemem-call.ll
@@ -4,15 +4,15 @@
;; It should hoist fn_write_inaccessible_mem
;; because there is no conflict between inaccessible memory
;; fn_read_inaccessible_mem is a nice side effect
-define i32 @loop_alias(i64 %x) {
+define i32 @loop_alias(i64 %x, ptr %start) {
; CHECK-LABEL: define i32 @loop_alias(
-; CHECK-SAME: i64 [[X:%.*]]) {
+; CHECK-SAME: i64 [[X:%.*]], ptr [[START:%.*]]) {
; CHECK-NEXT: [[ENTRY:.*]]:
; CHECK-NEXT: call void @fn_write_inaccessible_mem()
; CHECK-NEXT: br label %[[LOOP:.*]]
; CHECK: [[LOOP]]:
-; CHECK-NEXT: [[PHI:%.*]] = phi ptr [ null, %[[ENTRY]] ], [ [[GEP:%.*]], %[[LOOP]] ]
-; CHECK-NEXT: [[LOAD:%.*]] = load i32, ptr [[PHI]], align 16
+; CHECK-NEXT: [[PHI:%.*]] = phi ptr [ [[START]], %[[ENTRY]] ], [ [[GEP:%.*]], %[[LOOP]] ]
+; CHECK-NEXT: [[LOAD:%.*]] = load i32, ptr [[PHI]]
; CHECK-NEXT: [[VAL:%.*]] = call i32 @fn_args(i32 [[LOAD]])
; CHECK-NEXT: call void @fn_read_inaccessible_mem(i32 [[LOAD]])
; CHECK-NEXT: [[GEP]] = getelementptr inbounds nuw i32, ptr [[PHI]], i64 [[X]]
@@ -26,9 +26,9 @@ define i32 @loop_alias(i64 %x) {
entry:
br label %loop
loop:
- %phi = phi ptr [ null, %entry ], [ %gep, %loop ]
- %load = load i32, ptr %phi, align 16
- %val = call i32 @fn_args(i32 %load)
+ %phi = phi ptr [ %start, %entry ], [ %gep, %loop ]
+ %load = load i32, ptr %phi
+ %val = call i32 @fn_args(i32 %load)
call void @fn_write_inaccessible_mem()
call void @fn_read_inaccessible_mem(i32 %load)
%gep = getelementptr inbounds nuw i32, ptr %phi, i64 %x
@@ -40,16 +40,16 @@ after_loop:
}
;; It should NOT hoist fn_write_inaccessible_mem
-;; Because fn_args_2 reads same memory location
-define i32 @ne_loop_alias(i64 %x) {
+;; Because fn_read_inaccessible_mem_2 reads the same inaccessible memory
+define i32 @ne_loop_alias(i64 %x, ptr %start) {
; CHECK-LABEL: define i32 @ne_loop_alias(
-; CHECK-SAME: i64 [[X:%.*]]) {
+; CHECK-SAME: i64 [[X:%.*]], ptr [[START:%.*]]) {
; CHECK-NEXT: [[ENTRY:.*]]:
; CHECK-NEXT: br label %[[LOOP:.*]]
; CHECK: [[LOOP]]:
-; CHECK-NEXT: [[PHI:%.*]] = phi ptr [ null, %[[ENTRY]] ], [ [[GEP:%.*]], %[[LOOP]] ]
-; CHECK-NEXT: [[LOAD:%.*]] = load i32, ptr [[PHI]], align 16
-; CHECK-NEXT: [[VAL:%.*]] = call i32 @fn_args_2(i32 [[LOAD]])
+; CHECK-NEXT: [[PHI:%.*]] = phi ptr [ [[START]], %[[ENTRY]] ], [ [[GEP:%.*]], %[[LOOP]] ]
+; CHECK-NEXT: [[LOAD:%.*]] = load i32, ptr [[PHI]]
+; CHECK-NEXT: [[VAL:%.*]] = call i32 @fn_read_inaccessible_mem_2(i32 [[LOAD]])
; CHECK-NEXT: call void @fn_write_inaccessible_mem()
; CHECK-NEXT: call void @fn_read_inaccessible_mem(i32 [[VAL]])
; CHECK-NEXT: [[GEP]] = getelementptr inbounds nuw i32, ptr [[PHI]], i64 [[X]]
@@ -63,9 +63,9 @@ define i32 @ne_loop_alias(i64 %x) {
entry:
br label %loop
loop:
- %phi = phi ptr [ null, %entry ], [ %gep, %loop ]
- %load = load i32, ptr %phi, align 16
- %val = call i32 @fn_args_2(i32 %load)
+ %phi = phi ptr [ %start, %entry ], [ %gep, %loop ]
+ %load = load i32, ptr %phi
+ %val = call i32 @fn_read_inaccessible_mem_2(i32 %load)
call void @fn_write_inaccessible_mem()
call void @fn_read_inaccessible_mem(i32 %val)
%gep = getelementptr inbounds nuw i32, ptr %phi, i64 %x
@@ -157,7 +157,7 @@ loop:
}
declare i32 @fn_args(i32) nounwind willreturn memory(argmem: read)
-declare i32 @fn_args_2(i32) nounwind willreturn memory(inaccessiblemem: read)
+declare i32 @fn_read_inaccessible_mem_2(i32) nounwind willreturn memory(inaccessiblemem: read)
declare void @fn_write_inaccessible_mem() nounwind memory(inaccessiblemem: write)
declare void @fn_read_inaccessible_mem(i32) nounwind memory(inaccessiblemem: read)
declare void @fn_readwrite_inaccessible_mem() nounwind memory(inaccessiblemem: readwrite)
diff --git a/llvm/test/Transforms/LICM/invariant.start.ll b/llvm/test/Transforms/LICM/invariant.start.ll
index 9c957267d3db9..02b5cb7d74213 100644
--- a/llvm/test/Transforms/LICM/invariant.start.ll
+++ b/llvm/test/Transforms/LICM/invariant.start.ll
@@ -106,12 +106,12 @@ loop:
define void @test5(i1 %cond, ptr %ptr) {
; CHECK-LABEL: @test5(
; CHECK-NEXT: entry:
-; CHECK-NEXT: store i32 0, ptr [[PTR:%.*]], align 4
-; CHECK-NEXT: [[VAL:%.*]] = load i32, ptr [[PTR]], align 4
; CHECK-NEXT: br label [[LOOP:%.*]]
; CHECK: loop:
; CHECK-NEXT: [[X:%.*]] = phi i32 [ 0, [[ENTRY:%.*]] ], [ [[X_INC:%.*]], [[LOOP]] ]
+; CHECK-NEXT: store i32 [[X]], ptr [[PTR:%.*]], align 4
; CHECK-NEXT: [[SCOPE:%.*]] = call ptr @llvm.invariant.start.p0(i64 4, ptr [[PTR]])
+; CHECK-NEXT: [[VAL:%.*]] = load i32, ptr [[PTR]], align 4
; CHECK-NEXT: call void @llvm.invariant.end.p0(ptr [[SCOPE]], i64 4, ptr [[PTR]])
; CHECK-NEXT: [[X_INC]] = add i32 [[X]], [[VAL]]
; CHECK-NEXT: br label [[LOOP]]
@@ -121,7 +121,7 @@ entry:
loop:
%x = phi i32 [ 0, %entry ], [ %x.inc, %loop ]
- store i32 0, ptr %ptr
+ store i32 %x, ptr %ptr
%scope = call ptr @llvm.invariant.start.p0(i64 4, ptr %ptr)
%val = load i32, ptr %ptr
call void @llvm.invariant.end.p0(ptr %scope, i64 4, ptr %ptr)
More information about the llvm-commits
mailing list