diff --git a/ir/function.cpp b/ir/function.cpp index 81a97eb8e..b7ffa0f15 100644 --- a/ir/function.cpp +++ b/ir/function.cpp @@ -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]); diff --git a/tests/alive-tv/loops/nested-self-loop-fail.srctgt.ll b/tests/alive-tv/loops/nested-self-loop-fail.srctgt.ll new file mode 100644 index 000000000..5f0dbd670 --- /dev/null +++ b/tests/alive-tv/loops/nested-self-loop-fail.srctgt.ll @@ -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 +} diff --git a/tests/alive-tv/loops/nested-self-loop.srctgt.ll b/tests/alive-tv/loops/nested-self-loop.srctgt.ll new file mode 100644 index 000000000..22c2a087e --- /dev/null +++ b/tests/alive-tv/loops/nested-self-loop.srctgt.ll @@ -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 +}