| Issue |
202401
|
| Summary |
[LoopInterchange] Miscompilation when a value-carrying inner induction has an outer-IV-dependent step
|
| Labels |
miscompilation,
loopoptim
|
| Assignees |
kasuga-fj
|
| Reporter |
kasuga-fj
|
```llvm
; for (i = 1; i < 6; i++)
; for (j = 0, k = 0; j < 30; j++, k += i) // latch tests the CLEAN IV %j (constant step)
; A[i][j] = k; // store the value IV %k, which equals j*i
@A = global [6 x [40 x i64]] zeroinitializer
define void @f(ptr %A) {
entry:
br label %oh
oh: ; outer (i)
%i = phi i64 [ 1, %entry ], [ %i.next, %ol ]
br label %ih
ih: ; inner
%j = phi i64 [ 0, %oh ], [ %j.next, %il ] ; clean trip IV, constant step 1
%k = phi i64 [ 0, %oh ], [ %k.next, %il ] ; value IV, step = OUTER %i
%gep = getelementptr inbounds [6 x [40 x i64]], ptr %A, i64 0, i64 %i, i64 %j
store i64 %k, ptr %gep ; A[i][j] = k (= j*i)
%j.next = add nuw nsw i64 %j, 1
%k.next = add nuw nsw i64 %k, %i ; outer-IV-dependent step
br label %il
il:
%cond = icmp slt i64 %j, 30 ; latch on the CLEAN IV -> rectangular
br i1 %cond, label %ih, label %ie
ie:
br label %ol
ol:
%i.next = add nuw nsw i64 %i, 1
%ci = icmp slt i64 %i.next, 6
br i1 %ci, label %oh, label %ex
ex:
ret void
}
define i32 @main() {
entry:
call void @f(ptr @A)
br label %loop
loop:
%m = phi i64 [ 0, %entry ], [ %m.next, %loop ]
%acc = phi i64 [ 0, %entry ], [ %acc.next, %loop ]
%g = getelementptr inbounds i64, ptr @A, i64 %m
%v = load i64, ptr %g
%acc3 = mul i64 %acc, 31
%acc.next = add i64 %acc3, %v
%m.next = add i64 %m, 1
%c = icmp slt i64 %m.next, 240
br i1 %c, label %loop, label %done
done:
%r = trunc i64 %acc.next to i32
%r2 = and i32 %r, 65535
ret i32 %r2
}
```
```console
$ lli repro.ll
$ echo $?
31
$ opt --passes=loop-interchange --loop-interchange-profitabilities=ignore repro.ll -S -o tmp.ll
$ lli tmp.ll
$ echo $?
137
```
Note that this is similar to the case of #202383, but the root cause may be
different.
Analysis by Claude Code (not checked carefully, but looks plausible):
> The inner IV `%k` has a step (`%k.next = %k + %i`) that is the outer IV, so it
> is not really an induction of the inner loop, but `isInductionPHI` accepts it
> because only the inner-loop-invariance of the step is checked. Since the latch
> tests the clean IV `%j`, the nest is rectangular and the iteration set is not
> changed by interchange. Instead, when the loops are swapped the moved increment
> `%k.next = %k + %i` is cloned into the new inner latch, where `%i` is now the
> inner loop and only its final (LCSSA) value is available; the clone reads
> `%i.lcssa`, so `%k` effectively steps by the constant final `%i` and the stored
> values become `j * final_i` instead of `j * i`. A fix that only rejects nests
> whose inner trip count is not outer-invariant (the trapezoidal case) does not
> cover this; rejecting an inner induction whose step is not invariant in the
> outer loop in `findInductions` would address both.
Disclosure: The test case is generated by AI
_______________________________________________
llvm-bugs mailing list
[email protected]
https://lists.llvm.org/cgi-bin/mailman/listinfo/llvm-bugs