[llvm] [LoopInterchange] Add test where loop has memory-affected calls (NFC) (PR #200827)

via llvm-commits llvm-commits at lists.llvm.org
Mon Jun 1 07:30:35 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>

This patch adds the following two test cases:

- There is a call to a function with `memory(write)` in the loop nest, which is taken from #<!-- -->200796.
- There is a call to a function with `memory(none)` in the loop nest.

For the former case, we are not allowed to interchange the loops because the function call may write to an unknown memory location, but the transformation is still applied, i.e., a miscompilation. As for the latter case, the call should not prevent the loops from being interchanged, which seems to be the original intention of the code.

---
Full diff: https://github.com/llvm/llvm-project/pull/200827.diff


1 Files Affected:

- (added) llvm/test/Transforms/LoopInterchange/memory-attr.ll (+140) 


``````````diff
diff --git a/llvm/test/Transforms/LoopInterchange/memory-attr.ll b/llvm/test/Transforms/LoopInterchange/memory-attr.ll
new file mode 100644
index 0000000000000..c32f48ef63694
--- /dev/null
+++ b/llvm/test/Transforms/LoopInterchange/memory-attr.ll
@@ -0,0 +1,140 @@
+; NOTE: Assertions have been autogenerated by utils/update_test_checks.py UTC_ARGS: --version 6
+; RUN: opt < %s -passes=loop-interchange -loop-interchange-profitabilities=ignore -S | FileCheck %s
+
+; for (i = 0; i < 100; i++)
+;   for (j = 0; j < 100; j++)
+;     writeonly(A);
+;
+; The writeonly call may write to `%A` at some unknown index, so we cannot
+; interchange the loops.
+;
+; FIXME: These loops are now interchanged.
+;
+define void @call_writeonly(ptr %A) {
+; CHECK-LABEL: define void @call_writeonly(
+; CHECK-SAME: ptr [[A:%.*]]) {
+; CHECK-NEXT:  [[INNER_PREHEADER:.*:]]
+; CHECK-NEXT:    br label %[[INNER:.*]]
+; CHECK:       [[OUTER_HEADER_PREHEADER1:.*]]:
+; CHECK-NEXT:    br label %[[OUTER_HEADER_PREHEADER:.*]]
+; CHECK:       [[OUTER_HEADER_PREHEADER]]:
+; CHECK-NEXT:    [[I:%.*]] = phi i64 [ [[I_NEXT:%.*]], %[[OUTER_LATCH:.*]] ], [ 0, %[[OUTER_HEADER_PREHEADER1]] ]
+; CHECK-NEXT:    br label %[[INNER_SPLIT1:.*]]
+; CHECK:       [[INNER]]:
+; CHECK-NEXT:    br label %[[INNER1:.*]]
+; CHECK:       [[INNER1]]:
+; CHECK-NEXT:    [[J:%.*]] = phi i64 [ [[TMP2:%.*]], %[[INNER_SPLIT:.*]] ], [ 0, %[[INNER]] ]
+; CHECK-NEXT:    br label %[[OUTER_HEADER_PREHEADER1]]
+; CHECK:       [[INNER_SPLIT1]]:
+; CHECK-NEXT:    call void @writeonly(ptr [[A]])
+; CHECK-NEXT:    [[TMP0:%.*]] = add i64 [[J]], 1
+; CHECK-NEXT:    [[TMP1:%.*]] = icmp eq i64 [[TMP0]], 100
+; CHECK-NEXT:    br label %[[OUTER_LATCH]]
+; CHECK:       [[INNER_SPLIT]]:
+; CHECK-NEXT:    [[TMP2]] = add i64 [[J]], 1
+; CHECK-NEXT:    [[TMP3:%.*]] = icmp eq i64 [[TMP2]], 100
+; CHECK-NEXT:    br i1 [[TMP3]], label %[[EXIT:.*]], label %[[INNER1]]
+; CHECK:       [[OUTER_LATCH]]:
+; CHECK-NEXT:    [[I_NEXT]] = add i64 [[I]], 1
+; CHECK-NEXT:    [[EC_OUTER:%.*]] = icmp eq i64 [[I_NEXT]], 100
+; CHECK-NEXT:    br i1 [[EC_OUTER]], label %[[INNER_SPLIT]], label %[[OUTER_HEADER_PREHEADER]]
+; CHECK:       [[EXIT]]:
+; CHECK-NEXT:    ret void
+;
+entry:
+  br label %outer.header
+
+outer.header:
+  %i = phi i64 [ 0, %entry ], [ %i.next, %outer.latch ]
+  br label %inner
+
+inner:
+  %j = phi i64 [ 0, %outer.header ], [ %j.next, %inner ]
+  call void @writeonly(ptr %A)
+  %j.next = add i64 %j, 1
+  %ec.inner = icmp eq i64 %j.next, 100
+  br i1 %ec.inner, label %outer.latch, label %inner
+
+outer.latch:
+  %i.next = add i64 %i, 1
+  %ec.outer = icmp eq i64 %i.next, 100
+  br i1 %ec.outer, label %exit, label %outer.header
+
+exit:
+  ret void
+}
+
+; for (i = 0; i < 100; i++)
+;   for (j = 0; j < 100; j++) {
+;     A[j][i] = 0;
+;     val = mem_none(A);
+;     B[j][i] = val;
+;   }
+;
+; The mem_none call doesn't read or write the memory, so we can interchange the
+; loops.
+;
+define void @call_mem_none(ptr noalias %A, ptr noalias %B) {
+; CHECK-LABEL: define void @call_mem_none(
+; CHECK-SAME: ptr noalias [[A:%.*]], ptr noalias [[B:%.*]]) {
+; CHECK-NEXT:  [[ENTRY:.*:]]
+; CHECK-NEXT:    br label %[[INNER_PREHEADER:.*]]
+; CHECK:       [[OUTER_HEADER_PREHEADER:.*]]:
+; CHECK-NEXT:    br label %[[OUTER_HEADER:.*]]
+; CHECK:       [[OUTER_HEADER]]:
+; CHECK-NEXT:    [[I:%.*]] = phi i64 [ [[I_NEXT:%.*]], %[[OUTER_LATCH:.*]] ], [ 0, %[[OUTER_HEADER_PREHEADER]] ]
+; CHECK-NEXT:    br label %[[INNER_SPLIT1:.*]]
+; CHECK:       [[INNER_PREHEADER]]:
+; CHECK-NEXT:    br label %[[INNER:.*]]
+; CHECK:       [[INNER]]:
+; CHECK-NEXT:    [[J:%.*]] = phi i64 [ [[TMP0:%.*]], %[[INNER_SPLIT:.*]] ], [ 0, %[[INNER_PREHEADER]] ]
+; CHECK-NEXT:    br label %[[OUTER_HEADER_PREHEADER]]
+; CHECK:       [[INNER_SPLIT1]]:
+; CHECK-NEXT:    [[GEP_A:%.*]] = getelementptr [100 x i32], ptr [[A]], i64 [[J]], i64 [[I]]
+; CHECK-NEXT:    store i32 0, ptr [[GEP_A]], align 4
+; CHECK-NEXT:    [[VAL:%.*]] = call i32 @mem_none(ptr [[A]])
+; CHECK-NEXT:    [[GEP_B:%.*]] = getelementptr [100 x i32], ptr [[B]], i64 [[J]], i64 [[I]]
+; CHECK-NEXT:    store i32 [[VAL]], ptr [[GEP_B]], align 4
+; CHECK-NEXT:    [[J_NEXT:%.*]] = add i64 [[J]], 1
+; CHECK-NEXT:    [[EC_INNER:%.*]] = icmp eq i64 [[J_NEXT]], 100
+; CHECK-NEXT:    br label %[[OUTER_LATCH]]
+; CHECK:       [[INNER_SPLIT]]:
+; CHECK-NEXT:    [[TMP0]] = add i64 [[J]], 1
+; CHECK-NEXT:    [[TMP1:%.*]] = icmp eq i64 [[TMP0]], 100
+; CHECK-NEXT:    br i1 [[TMP1]], label %[[EXIT:.*]], label %[[INNER]]
+; CHECK:       [[OUTER_LATCH]]:
+; CHECK-NEXT:    [[I_NEXT]] = add i64 [[I]], 1
+; CHECK-NEXT:    [[EC_OUTER:%.*]] = icmp eq i64 [[I_NEXT]], 100
+; CHECK-NEXT:    br i1 [[EC_OUTER]], label %[[INNER_SPLIT]], label %[[OUTER_HEADER]]
+; CHECK:       [[EXIT]]:
+; CHECK-NEXT:    ret void
+;
+entry:
+  br label %outer.header
+
+outer.header:
+  %i = phi i64 [ 0, %entry ], [ %i.next, %outer.latch ]
+  br label %inner
+
+inner:
+  %j = phi i64 [ 0, %outer.header ], [ %j.next, %inner ]
+  %gep.A = getelementptr [100 x i32], ptr %A, i64 %j, i64 %i
+  store i32 0, ptr %gep.A
+  %val = call i32 @mem_none(ptr %A)
+  %gep.B = getelementptr [100 x i32], ptr %B, i64 %j, i64 %i
+  store i32 %val, ptr %gep.B
+  %j.next = add i64 %j, 1
+  %ec.inner = icmp eq i64 %j.next, 100
+  br i1 %ec.inner, label %outer.latch, label %inner
+
+outer.latch:
+  %i.next = add i64 %i, 1
+  %ec.outer = icmp eq i64 %i.next, 100
+  br i1 %ec.outer, label %exit, label %outer.header
+
+exit:
+  ret void
+}
+
+declare void @writeonly(ptr %p) memory(write)
+declare void @mem_none(ptr %p) memory(none)

``````````

</details>


https://github.com/llvm/llvm-project/pull/200827


More information about the llvm-commits mailing list