Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 8 additions & 1 deletion ir/function.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1201,9 +1201,16 @@ void LoopAnalysis::run() {
// Construct the loop forest (0 or more loop trees)
for (unsigned i = 0; i < bb_count; ++i) {
auto h = header[i];
// Give every loop header a key of its own. A header only ever acquires one
// as a side effect of a body block naming it as parent, so a loop whose
// body is just the header itself (a self loop) would otherwise be absent
// from the forest and consumers walking it top-down would not see a loop
// there at all.
if (type[i] != nonheader)
(void)forest[node[i]];

if (h == 0 && type[i] != nonheader) {
roots.emplace_back(node[i]);
(void)forest[node[i]];
}
else if (h != 0 || type[i] != nonheader) {
parent.emplace(node[i], node[h]);
Expand Down
50 changes: 50 additions & 0 deletions tests/alive-tv/loops/nested-self-loop-fail.srctgt.ll
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
; TEST-ARGS: -src-unroll=8 -tgt-unroll=8
; ERROR: Value mismatch

; A loop whose body is a single basic block used not to be registered as a loop
; header in the loop forest, so the unroller skipped it when it was nested in
; another loop. Its backedge then survived into the unrolled function, where it
; is redirected to #sink, so the executions that run the inner loop more than
; once dropped out of the refinement query and this pair verified.
; tgt returns 3 * %n, not %n.

define i32 @src(i32 %n) {
entry:
%c = icmp slt i32 %n, 1
br i1 %c, label %zero, label %pos

zero:
ret i32 0

pos:
ret i32 %n
}

define i32 @tgt(i32 %n) {
entry:
%c = icmp slt i32 %n, 1
br i1 %c, label %done, label %outer

outer:
%i = phi i32 [ 0, %entry ], [ %i.next, %latch ]
%acc = phi i32 [ 0, %entry ], [ %acc.next, %latch ]
br label %inner

inner:
%j = phi i32 [ 0, %outer ], [ %j.next, %inner ]
%a = phi i32 [ %acc, %outer ], [ %a.next, %inner ]
%a.next = add i32 %a, 1
%j.next = add i32 %j, 1
%jc = icmp slt i32 %j.next, 3
br i1 %jc, label %inner, label %latch

latch:
%acc.next = phi i32 [ %a.next, %inner ]
%i.next = add i32 %i, 1
%ic = icmp slt i32 %i.next, %n
br i1 %ic, label %outer, label %done

done:
%r = phi i32 [ 0, %entry ], [ %acc.next, %latch ]
ret i32 %r
}
62 changes: 62 additions & 0 deletions tests/alive-tv/loops/nested-self-loop.srctgt.ll
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
; TEST-ARGS: -src-unroll=8 -tgt-unroll=8

; The counterpart of nested-self-loop-fail.srctgt.ll: unrolling a nested self
; loop must not make an equivalent pair unverifiable.

define i32 @src(i32 %n) {
entry:
%c = icmp slt i32 %n, 1
br i1 %c, label %done, label %outer

outer:
%i = phi i32 [ 0, %entry ], [ %i.next, %latch ]
%acc = phi i32 [ 0, %entry ], [ %acc.next, %latch ]
br label %inner

inner:
%j = phi i32 [ 0, %outer ], [ %j.next, %inner ]
%a = phi i32 [ %acc, %outer ], [ %a.next, %inner ]
%a.next = add i32 %a, 1
%j.next = add i32 %j, 1
%jc = icmp slt i32 %j.next, 3
br i1 %jc, label %inner, label %latch

latch:
%acc.next = phi i32 [ %a.next, %inner ]
%i.next = add i32 %i, 1
%ic = icmp slt i32 %i.next, %n
br i1 %ic, label %outer, label %done

done:
%r = phi i32 [ 0, %entry ], [ %acc.next, %latch ]
ret i32 %r
}

define i32 @tgt(i32 %n) {
entry:
%c = icmp slt i32 %n, 1
br i1 %c, label %done, label %outer

outer:
%i = phi i32 [ 0, %entry ], [ %i.next, %latch ]
%acc = phi i32 [ 0, %entry ], [ %acc.next, %latch ]
br label %inner

inner:
%j = phi i32 [ 0, %outer ], [ %j.next, %inner ]
%a = phi i32 [ %acc, %outer ], [ %a.next, %inner ]
%a.next = sub i32 %a, -1
%j.next = add i32 %j, 1
%jc = icmp slt i32 %j.next, 3
br i1 %jc, label %inner, label %latch

latch:
%acc.next = phi i32 [ %a.next, %inner ]
%i.next = add i32 %i, 1
%ic = icmp slt i32 %i.next, %n
br i1 %ic, label %outer, label %done

done:
%r = phi i32 [ 0, %entry ], [ %acc.next, %latch ]
ret i32 %r
}