[llvm-branch-commits] [llvm] [LoopInterchange] Identify unsafe instructions for interchange (PR #201402)
via llvm-branch-commits
llvm-branch-commits at lists.llvm.org
Wed Jun 3 09:49:29 PDT 2026
llvmorg-github-actions[bot] wrote:
<!--LLVM PR SUMMARY COMMENT-->
@llvm/pr-subscribers-llvm-transforms
Author: Ryotaro Kasuga (kasuga-fj)
<details>
<summary>Changes</summary>
In LoopInterchange, all instructions in the loops are traversed and checked during the legality phase, and the pass bails out if it finds an instruction that is unsafe to interchange. However, previously it only handled call instructions and ignored all others, leaving several kinds of instruction that should have been detected but were not.
This patch fixes the issue by restructuring the legality check, in particular replacing instruction‑specific handling with calling general Instruction APIs.
Fixes #<!-- -->200913.
---
Full diff: https://github.com/llvm/llvm-project/pull/201402.diff
3 Files Affected:
- (modified) llvm/lib/Transforms/Scalar/LoopInterchange.cpp (+21-18)
- (modified) llvm/test/Transforms/LoopInterchange/atomic-memory-ordering.ll (+14-36)
- (modified) llvm/test/Transforms/LoopInterchange/invoke.ll (+5-15)
``````````diff
diff --git a/llvm/lib/Transforms/Scalar/LoopInterchange.cpp b/llvm/lib/Transforms/Scalar/LoopInterchange.cpp
index 6b608eef15b5c..1037690b1af5f 100644
--- a/llvm/lib/Transforms/Scalar/LoopInterchange.cpp
+++ b/llvm/lib/Transforms/Scalar/LoopInterchange.cpp
@@ -1472,25 +1472,28 @@ bool LoopInterchangeLegality::canInterchangeLoops(unsigned InnerLoopId,
}
// Check if outer and inner loop contain legal instructions only.
for (auto *BB : OuterLoop->blocks())
- for (Instruction &I : *BB)
- if (CallInst *CI = dyn_cast<CallInst>(&I)) {
- // Functions which don't access memory and don't diverge do not prevent
- // interchanging.
- if ((CI->doesNotAccessMemory() && !CI->mayHaveSideEffects()) ||
- isa<PseudoProbeInst>(CI))
- continue;
- LLVM_DEBUG(
- dbgs() << "Loops with call instructions cannot be interchanged "
- << "safely.");
- ORE->emit([&]() {
- return OptimizationRemarkMissed(DEBUG_TYPE, "CallInst",
- CI->getDebugLoc(),
- CI->getParent())
- << "Cannot interchange loops due to call instruction.";
- });
+ for (Instruction &I : *BB) {
+ // Loads and stores are checked separately, so we can skip them here.
+ if (isa<LoadInst, StoreInst, PseudoProbeInst>(&I))
+ continue;
- return false;
- }
+ // We cannot ignore potential memory reads, e.g., loads inside the called
+ // function.
+ if (!I.mayHaveSideEffects() && !I.mayReadFromMemory() && !I.isAtomic())
+ continue;
+
+ LLVM_DEBUG(
+ dbgs()
+ << "Loops contain instructions that cannot be safely interchanged\n");
+ ORE->emit([&]() {
+ return OptimizationRemarkMissed(DEBUG_TYPE, "UnsafeInst",
+ I.getDebugLoc(), I.getParent())
+ << "Cannot interchange loops due to instruction that is "
+ "potentially unsafe to interchange.";
+ });
+
+ return false;
+ }
if (!findInductions(InnerLoop, InnerLoopInductions)) {
LLVM_DEBUG(dbgs() << "Could not find inner loop induction variables.\n");
diff --git a/llvm/test/Transforms/LoopInterchange/atomic-memory-ordering.ll b/llvm/test/Transforms/LoopInterchange/atomic-memory-ordering.ll
index 8c6fc32e16a96..53c5da3ffa92c 100644
--- a/llvm/test/Transforms/LoopInterchange/atomic-memory-ordering.ll
+++ b/llvm/test/Transforms/LoopInterchange/atomic-memory-ordering.ll
@@ -5,36 +5,24 @@
; Test cases that contain atomic instructions inside the loops thus interchange
; must not be applied.
-;
-; FIXME: Currently some of them are interchanged.
define void @atomicrmw() {
; CHECK-LABEL: define void @atomicrmw() {
-; CHECK-NEXT: [[ENTRY:.*:]]
-; CHECK-NEXT: br label %[[FOR_J_PREHEADER:.*]]
-; CHECK: [[FOR_I_HEADER_PREHEADER:.*]]:
-; CHECK-NEXT: br label %[[FOR_I_HEADER:.*]]
-; CHECK: [[FOR_I_HEADER]]:
-; CHECK-NEXT: [[I:%.*]] = phi i64 [ [[I_NEXT:%.*]], %[[FOR_I_LATCH:.*]] ], [ 0, %[[FOR_I_HEADER_PREHEADER]] ]
-; CHECK-NEXT: br label %[[FOR_J_SPLIT1:.*]]
-; CHECK: [[FOR_J_PREHEADER]]:
+; CHECK-NEXT: [[FOR_J_PREHEADER:.*]]:
; CHECK-NEXT: br label %[[FOR_J:.*]]
; CHECK: [[FOR_J]]:
-; CHECK-NEXT: [[J:%.*]] = phi i64 [ [[TMP0:%.*]], %[[FOR_J_SPLIT:.*]] ], [ 0, %[[FOR_J_PREHEADER]] ]
-; CHECK-NEXT: br label %[[FOR_I_HEADER_PREHEADER]]
-; CHECK: [[FOR_J_SPLIT1]]:
+; CHECK-NEXT: [[I:%.*]] = phi i64 [ 0, %[[FOR_J_PREHEADER]] ], [ [[I_NEXT:%.*]], %[[FOR_I_LATCH:.*]] ]
+; CHECK-NEXT: br label %[[FOR_I_HEADER_PREHEADER:.*]]
+; CHECK: [[FOR_I_HEADER_PREHEADER]]:
+; CHECK-NEXT: [[J:%.*]] = phi i64 [ 0, %[[FOR_J]] ], [ [[TMP0:%.*]], %[[FOR_I_HEADER_PREHEADER]] ]
; CHECK-NEXT: [[OLD:%.*]] = atomicrmw xchg ptr @g, i64 [[J]] monotonic, align 8
-; CHECK-NEXT: [[J_NEXT:%.*]] = add i64 [[J]], 1
-; CHECK-NEXT: [[EC_J:%.*]] = icmp eq i64 [[J_NEXT]], 8
-; CHECK-NEXT: br label %[[FOR_I_LATCH]]
-; CHECK: [[FOR_J_SPLIT]]:
; CHECK-NEXT: [[TMP0]] = add i64 [[J]], 1
; CHECK-NEXT: [[TMP1:%.*]] = icmp eq i64 [[TMP0]], 8
-; CHECK-NEXT: br i1 [[TMP1]], label %[[EXIT:.*]], label %[[FOR_J]]
+; CHECK-NEXT: br i1 [[TMP1]], label %[[FOR_I_LATCH]], label %[[FOR_I_HEADER_PREHEADER]]
; CHECK: [[FOR_I_LATCH]]:
; CHECK-NEXT: [[I_NEXT]] = add i64 [[I]], 1
; CHECK-NEXT: [[EC_I:%.*]] = icmp eq i64 [[I_NEXT]], 8
-; CHECK-NEXT: br i1 [[EC_I]], label %[[FOR_J_SPLIT]], label %[[FOR_I_HEADER]]
+; CHECK-NEXT: br i1 [[EC_I]], label %[[EXIT:.*]], label %[[FOR_J]]
; CHECK: [[EXIT]]:
; CHECK-NEXT: ret void
;
@@ -64,31 +52,21 @@ exit:
define void @cmpxchg(i64 %cmp) {
; CHECK-LABEL: define void @cmpxchg(
; CHECK-SAME: i64 [[CMP:%.*]]) {
-; CHECK-NEXT: [[ENTRY:.*:]]
-; CHECK-NEXT: br label %[[FOR_J_PREHEADER:.*]]
-; CHECK: [[FOR_I_HEADER_PREHEADER:.*]]:
-; CHECK-NEXT: br label %[[FOR_I_HEADER:.*]]
-; CHECK: [[FOR_I_HEADER]]:
-; CHECK-NEXT: [[I:%.*]] = phi i64 [ [[I_NEXT:%.*]], %[[FOR_I_LATCH:.*]] ], [ 0, %[[FOR_I_HEADER_PREHEADER]] ]
-; CHECK-NEXT: br label %[[FOR_J_SPLIT1:.*]]
-; CHECK: [[FOR_J_PREHEADER]]:
+; CHECK-NEXT: [[FOR_J_PREHEADER:.*]]:
; CHECK-NEXT: br label %[[FOR_J:.*]]
; CHECK: [[FOR_J]]:
-; CHECK-NEXT: [[J:%.*]] = phi i64 [ [[TMP0:%.*]], %[[FOR_J_SPLIT:.*]] ], [ 0, %[[FOR_J_PREHEADER]] ]
-; CHECK-NEXT: br label %[[FOR_I_HEADER_PREHEADER]]
-; CHECK: [[FOR_J_SPLIT1]]:
+; CHECK-NEXT: [[I:%.*]] = phi i64 [ 0, %[[FOR_J_PREHEADER]] ], [ [[I_NEXT:%.*]], %[[FOR_I_LATCH:.*]] ]
+; CHECK-NEXT: br label %[[FOR_I_HEADER_PREHEADER:.*]]
+; CHECK: [[FOR_I_HEADER_PREHEADER]]:
+; CHECK-NEXT: [[J:%.*]] = phi i64 [ 0, %[[FOR_J]] ], [ [[TMP0:%.*]], %[[FOR_I_HEADER_PREHEADER]] ]
; CHECK-NEXT: [[OLD:%.*]] = cmpxchg ptr @g, i64 [[CMP]], i64 [[J]] monotonic monotonic, align 8
-; CHECK-NEXT: [[J_NEXT:%.*]] = add i64 [[J]], 1
-; CHECK-NEXT: [[EC_J:%.*]] = icmp eq i64 [[J_NEXT]], 8
-; CHECK-NEXT: br label %[[FOR_I_LATCH]]
-; CHECK: [[FOR_J_SPLIT]]:
; CHECK-NEXT: [[TMP0]] = add i64 [[J]], 1
; CHECK-NEXT: [[TMP1:%.*]] = icmp eq i64 [[TMP0]], 8
-; CHECK-NEXT: br i1 [[TMP1]], label %[[EXIT:.*]], label %[[FOR_J]]
+; CHECK-NEXT: br i1 [[TMP1]], label %[[FOR_I_LATCH]], label %[[FOR_I_HEADER_PREHEADER]]
; CHECK: [[FOR_I_LATCH]]:
; CHECK-NEXT: [[I_NEXT]] = add i64 [[I]], 1
; CHECK-NEXT: [[EC_I:%.*]] = icmp eq i64 [[I_NEXT]], 8
-; CHECK-NEXT: br i1 [[EC_I]], label %[[FOR_J_SPLIT]], label %[[FOR_I_HEADER]]
+; CHECK-NEXT: br i1 [[EC_I]], label %[[EXIT:.*]], label %[[FOR_J]]
; CHECK: [[EXIT]]:
; CHECK-NEXT: ret void
;
diff --git a/llvm/test/Transforms/LoopInterchange/invoke.ll b/llvm/test/Transforms/LoopInterchange/invoke.ll
index 8365eda175b82..f91051666afaa 100644
--- a/llvm/test/Transforms/LoopInterchange/invoke.ll
+++ b/llvm/test/Transforms/LoopInterchange/invoke.ll
@@ -8,39 +8,29 @@ declare i32 @__gxx_personality_v0(...)
; The following loops must not be interchanged because the invocation of @rd
; may read or write the memory.
;
-; FIXME: Currently the loops pass the legality check.
-;
define void @invoke_may_rw() personality ptr @__gxx_personality_v0 {
; CHECK-LABEL: define void @invoke_may_rw() personality ptr @__gxx_personality_v0 {
-; CHECK-NEXT: [[ENTRY:.*:]]
-; CHECK-NEXT: br label %[[I_HEADER_PREHEADER:.*]]
-; CHECK: [[I_HEADER_PREHEADER]]:
-; CHECK-NEXT: br label %[[I_HEADER:.*]]
-; CHECK: [[I_HEADER]]:
-; CHECK-NEXT: [[I:%.*]] = phi i64 [ [[I_NEXT:%.*]], %[[I_LATCH:.*]] ], [ 0, %[[I_HEADER_PREHEADER]] ]
+; CHECK-NEXT: [[I_HEADER:.*]]:
; CHECK-NEXT: br label %[[J_HEADER_PREHEADER:.*]]
; CHECK: [[J_HEADER_PREHEADER]]:
+; CHECK-NEXT: [[I:%.*]] = phi i64 [ 0, %[[I_HEADER]] ], [ [[I_NEXT:%.*]], %[[I_LATCH:.*]] ]
; CHECK-NEXT: br label %[[J_HEADER:.*]]
; CHECK: [[J_HEADER]]:
-; CHECK-NEXT: [[J:%.*]] = phi i64 [ [[TMP0:%.*]], %[[J_LATCH_SPLIT:.*]] ], [ 0, %[[J_HEADER_PREHEADER]] ]
+; CHECK-NEXT: [[J:%.*]] = phi i64 [ 0, %[[J_HEADER_PREHEADER]] ], [ [[TMP0:%.*]], %[[J_LATCH:.*]] ]
; CHECK-NEXT: invoke void @rd(ptr @g)
-; CHECK-NEXT: to label %[[J_LATCH:.*]] unwind label %[[LP:.*]]
+; CHECK-NEXT: to label %[[J_LATCH]] unwind label %[[LP:.*]]
; CHECK: [[LP]]:
; CHECK-NEXT: [[L:%.*]] = landingpad { ptr, i32 }
; CHECK-NEXT: cleanup
; CHECK-NEXT: br label %[[J_LATCH]]
; CHECK: [[J_LATCH]]:
-; CHECK-NEXT: [[J_NEXT:%.*]] = add i64 [[J]], 1
-; CHECK-NEXT: [[EC_J:%.*]] = icmp eq i64 [[J_NEXT]], 8
-; CHECK-NEXT: br label %[[J_LATCH_SPLIT]]
-; CHECK: [[J_LATCH_SPLIT]]:
; CHECK-NEXT: [[TMP0]] = add i64 [[J]], 1
; CHECK-NEXT: [[TMP1:%.*]] = icmp eq i64 [[TMP0]], 8
; CHECK-NEXT: br i1 [[TMP1]], label %[[I_LATCH]], label %[[J_HEADER]]
; CHECK: [[I_LATCH]]:
; CHECK-NEXT: [[I_NEXT]] = add i64 [[I]], 1
; CHECK-NEXT: [[EC_I:%.*]] = icmp eq i64 [[I_NEXT]], 8
-; CHECK-NEXT: br i1 [[EC_I]], label %[[EXIT:.*]], label %[[I_HEADER]]
+; CHECK-NEXT: br i1 [[EC_I]], label %[[EXIT:.*]], label %[[J_HEADER_PREHEADER]]
; CHECK: [[EXIT]]:
; CHECK-NEXT: ret void
;
``````````
</details>
https://github.com/llvm/llvm-project/pull/201402
More information about the llvm-branch-commits
mailing list