loops: fix multi-exit with phis not sharing a loop val
MODIFY
ir/function.cpp
ir/function.cpp
@@ -579,6 +579,18 @@ void Function::unroll(unsigned k) {
continue;
if (auto phi = dynamic_cast<Phi*>(user)) {
+ // Check if the phi uses this value through a path that is reachable
+ // from this exit, as it may only be reachable from another exit.
+ bool has_valid_pred = false;
+ for (auto &[entry, pred] : phi->getValues()) {
+ if (entry == val) {
+ if ((has_valid_pred = dom_tree.dominates(exit, &getBB(pred))))
+ break;
+ }
+ }
+ if (!has_valid_pred)
+ continue;
+
if (user_bb == dst) {
for (auto &[bb, val] : copies) {
phi->addValue(*val, string(bb->getName()));

ADD
tests/alive-tv/loops/multi-exit4-fail.srctgt.ll
+; TEST-ARGS: -src-unroll=3 -tgt-unroll=3
+; ERROR: Value mismatch
+
+define i32 @src() {
+entry:
+ br label %for.cond
+
+for.cond:
+ %i = phi i32 [ 0, %entry ], [ %inc1, %for.body ]
+ %r = phi i32 [ 0, %entry ], [ %inc, %for.body ]
+ %cmp = icmp uge i32 %i, 4
+ br i1 %cmp, label %for.end2, label %for.body
+
+for.body:
+ %inc = add i32 %r, 2
+ %inc1 = add i32 %i, 1
+ %cmp2 = icmp eq i32 %inc, 6
+ br i1 %cmp2, label %for.end1, label %for.cond
+
+for.end1:
+ br label %exit
+
+for.end2:
+ br label %exit
+
+exit:
+ %p = phi i32 [%inc, %for.end1], [%r, %for.end2]
+ ret i32 %p
+}
+
+define i32 @tgt() {
+ ret i32 4
+}

ADD
tests/alive-tv/loops/multi-exit4.srctgt.ll
+; TEST-ARGS: -src-unroll=3 -tgt-unroll=3
+
+define i32 @src() {
+entry:
+ br label %for.cond
+
+for.cond:
+ %i = phi i32 [ 0, %entry ], [ %inc1, %for.body ]
+ %r = phi i32 [ 0, %entry ], [ %inc, %for.body ]
+ %cmp = icmp uge i32 %i, 4
+ br i1 %cmp, label %for.end2, label %for.body
+
+for.body:
+ %inc = add i32 %r, 2
+ %inc1 = add i32 %i, 1
+ %cmp2 = icmp eq i32 %inc, 6
+ br i1 %cmp2, label %for.end1, label %for.cond
+
+for.end1:
+ br label %exit
+
+for.end2:
+ br label %exit
+
+exit:
+ %p = phi i32 [%inc, %for.end1], [%r, %for.end2]
+ ret i32 %p
+}
+
+define i32 @tgt() {
+ ret i32 6
+}

loops: fix multi-exit with phis not sharing a loop val