| ; NOTE: Assertions have been autogenerated by utils/update_test_checks.py |
| ; RUN: opt < %s -unify-loop-exits -S | FileCheck %s |
| |
| ; Loop consists of A and B: |
| ; - A is the header |
| ; - A and B are exiting blocks |
| ; - C and return are exit blocks. |
| ; Pattern: Value (%mytmp42) defined in exiting block (A) and used in |
| ; exit block (return). |
| ; The relevant code uses DT::dominates(Value, |
| ; BasicBlock). This is misnamed because it actually checks |
| ; strict dominance, causing the pattern to be miscompiled |
| ; (the use receives an undef value). |
| define i32 @exiting-used-in-exit(i32* %arg1, i32* %arg2) local_unnamed_addr align 2 { |
| ; CHECK-LABEL: @exiting-used-in-exit( |
| ; CHECK-NEXT: entry: |
| ; CHECK-NEXT: br label [[A:%.*]] |
| ; CHECK: A: |
| ; CHECK-NEXT: [[MYTMP42:%.*]] = load i32, i32* [[ARG1:%.*]], align 4 |
| ; CHECK-NEXT: [[CMP1:%.*]] = icmp slt i32 [[MYTMP42]], 0 |
| ; CHECK-NEXT: br i1 [[CMP1]], label [[B:%.*]], label [[LOOP_EXIT_GUARD:%.*]] |
| ; CHECK: B: |
| ; CHECK-NEXT: [[MYTMP41:%.*]] = load i32, i32* [[ARG2:%.*]], align 4 |
| ; CHECK-NEXT: [[CMP:%.*]] = icmp slt i32 [[MYTMP41]], 0 |
| ; CHECK-NEXT: br i1 [[CMP]], label [[A]], label [[LOOP_EXIT_GUARD]] |
| ; CHECK: C: |
| ; CHECK-NEXT: [[INC:%.*]] = add i32 [[MYTMP41_MOVED:%.*]], 1 |
| ; CHECK-NEXT: br label [[RETURN:%.*]] |
| ; CHECK: return: |
| ; CHECK-NEXT: [[PHI:%.*]] = phi i32 [ [[INC]], [[C:%.*]] ], [ [[PHI_MOVED:%.*]], [[LOOP_EXIT_GUARD]] ] |
| ; CHECK-NEXT: ret i32 [[PHI]] |
| ; CHECK: loop.exit.guard: |
| ; CHECK-NEXT: [[GUARD_RETURN:%.*]] = phi i1 [ true, [[A]] ], [ false, [[B]] ] |
| ; CHECK-NEXT: [[PHI_MOVED]] = phi i32 [ [[MYTMP42]], [[A]] ], [ undef, [[B]] ] |
| ; CHECK-NEXT: [[MYTMP41_MOVED]] = phi i32 [ undef, [[A]] ], [ [[MYTMP41]], [[B]] ] |
| ; CHECK-NEXT: br i1 [[GUARD_RETURN]], label [[RETURN]], label [[C]] |
| ; |
| entry: |
| br label %A |
| |
| A: |
| %mytmp42 = load i32, i32* %arg1, align 4 |
| %cmp1 = icmp slt i32 %mytmp42, 0 |
| br i1 %cmp1, label %B, label %return |
| |
| B: |
| %mytmp41 = load i32, i32* %arg2, align 4 |
| %cmp = icmp slt i32 %mytmp41, 0 |
| br i1 %cmp, label %A, label %C |
| |
| C: |
| %inc = add i32 %mytmp41, 1 |
| br label %return |
| |
| return: |
| %phi = phi i32 [ %inc, %C ], [ %mytmp42, %A ] |
| ret i32 %phi |
| } |
| |
| ; Loop consists of A, B and C: |
| ; - A is the header |
| ; - A and C are exiting blocks |
| ; - B is an "internal" block that dominates exiting block C |
| ; - D and return are exit blocks. |
| ; Pattern: Value (%mytmp41) defined in internal block (B) and used in an |
| ; exit block (D). |
| define i32 @internal-used-in-exit(i32* %arg1, i32* %arg2) local_unnamed_addr align 2 { |
| ; CHECK-LABEL: @internal-used-in-exit( |
| ; CHECK-NEXT: entry: |
| ; CHECK-NEXT: [[MYTMP42:%.*]] = load i32, i32* [[ARG1:%.*]], align 4 |
| ; CHECK-NEXT: br label [[A:%.*]] |
| ; CHECK: A: |
| ; CHECK-NEXT: [[CMP1:%.*]] = icmp slt i32 [[MYTMP42]], 0 |
| ; CHECK-NEXT: br i1 [[CMP1]], label [[B:%.*]], label [[LOOP_EXIT_GUARD:%.*]] |
| ; CHECK: B: |
| ; CHECK-NEXT: [[MYTMP41:%.*]] = load i32, i32* [[ARG2:%.*]], align 4 |
| ; CHECK-NEXT: br label [[C:%.*]] |
| ; CHECK: C: |
| ; CHECK-NEXT: [[CMP:%.*]] = icmp slt i32 [[MYTMP42]], 0 |
| ; CHECK-NEXT: br i1 [[CMP]], label [[A]], label [[LOOP_EXIT_GUARD]] |
| ; CHECK: D: |
| ; CHECK-NEXT: [[INC:%.*]] = add i32 [[MYTMP41_MOVED:%.*]], 1 |
| ; CHECK-NEXT: br label [[RETURN:%.*]] |
| ; CHECK: return: |
| ; CHECK-NEXT: ret i32 0 |
| ; CHECK: loop.exit.guard: |
| ; CHECK-NEXT: [[GUARD_RETURN:%.*]] = phi i1 [ true, [[A]] ], [ false, [[C]] ] |
| ; CHECK-NEXT: [[MYTMP41_MOVED]] = phi i32 [ undef, [[A]] ], [ [[MYTMP41]], [[C]] ] |
| ; CHECK-NEXT: br i1 [[GUARD_RETURN]], label [[RETURN]], label [[D:%.*]] |
| ; |
| entry: |
| %mytmp42 = load i32, i32* %arg1, align 4 |
| br label %A |
| |
| A: |
| %cmp1 = icmp slt i32 %mytmp42, 0 |
| br i1 %cmp1, label %B, label %return |
| |
| B: |
| %mytmp41 = load i32, i32* %arg2, align 4 |
| br label %C |
| |
| C: |
| %cmp = icmp slt i32 %mytmp42, 0 |
| br i1 %cmp, label %A, label %D |
| |
| D: |
| %inc = add i32 %mytmp41, 1 |
| br label %return |
| |
| return: |
| ret i32 0 |
| } |
| |
| ; Loop consists of A, B and C: |
| ; - A is the header |
| ; - A and C are exiting blocks |
| ; - B is an "internal" block that dominates exiting block C |
| ; - D and return are exit blocks. |
| ; Pattern: %return contains a phi node that receives values from |
| ; %entry, %A and %D. This mixes all the special cases in a single phi. |
| define i32 @mixed-use-in-exit(i32* %arg1, i32* %arg2) local_unnamed_addr align 2 { |
| ; CHECK-LABEL: @mixed-use-in-exit( |
| ; CHECK-NEXT: entry: |
| ; CHECK-NEXT: [[MYTMP42:%.*]] = load i32, i32* [[ARG1:%.*]], align 4 |
| ; CHECK-NEXT: [[CMP2:%.*]] = icmp slt i32 [[MYTMP42]], 0 |
| ; CHECK-NEXT: br i1 [[CMP2]], label [[A:%.*]], label [[RETURN:%.*]] |
| ; CHECK: A: |
| ; CHECK-NEXT: [[MYTMP43:%.*]] = add i32 [[MYTMP42]], 1 |
| ; CHECK-NEXT: [[CMP1:%.*]] = icmp slt i32 [[MYTMP42]], 0 |
| ; CHECK-NEXT: br i1 [[CMP1]], label [[B:%.*]], label [[LOOP_EXIT_GUARD:%.*]] |
| ; CHECK: B: |
| ; CHECK-NEXT: [[MYTMP41:%.*]] = load i32, i32* [[ARG2:%.*]], align 4 |
| ; CHECK-NEXT: br label [[C:%.*]] |
| ; CHECK: C: |
| ; CHECK-NEXT: [[CMP:%.*]] = icmp slt i32 [[MYTMP42]], 0 |
| ; CHECK-NEXT: br i1 [[CMP]], label [[A]], label [[LOOP_EXIT_GUARD]] |
| ; CHECK: D: |
| ; CHECK-NEXT: br label [[RETURN]] |
| ; CHECK: return: |
| ; CHECK-NEXT: [[PHI:%.*]] = phi i32 [ [[MYTMP41_MOVED:%.*]], [[D:%.*]] ], [ [[MYTMP42]], [[ENTRY:%.*]] ], [ [[PHI_MOVED:%.*]], [[LOOP_EXIT_GUARD]] ] |
| ; CHECK-NEXT: ret i32 [[PHI]] |
| ; CHECK: loop.exit.guard: |
| ; CHECK-NEXT: [[GUARD_RETURN:%.*]] = phi i1 [ true, [[A]] ], [ false, [[C]] ] |
| ; CHECK-NEXT: [[PHI_MOVED]] = phi i32 [ [[MYTMP43]], [[A]] ], [ undef, [[C]] ] |
| ; CHECK-NEXT: [[MYTMP41_MOVED]] = phi i32 [ undef, [[A]] ], [ [[MYTMP41]], [[C]] ] |
| ; CHECK-NEXT: br i1 [[GUARD_RETURN]], label [[RETURN]], label [[D]] |
| ; |
| entry: |
| %mytmp42 = load i32, i32* %arg1, align 4 |
| %cmp2 = icmp slt i32 %mytmp42, 0 |
| br i1 %cmp2, label %A, label %return |
| |
| A: |
| %mytmp43 = add i32 %mytmp42, 1 |
| %cmp1 = icmp slt i32 %mytmp42, 0 |
| br i1 %cmp1, label %B, label %return |
| |
| B: |
| %mytmp41 = load i32, i32* %arg2, align 4 |
| br label %C |
| |
| C: |
| %cmp = icmp slt i32 %mytmp42, 0 |
| br i1 %cmp, label %A, label %D |
| |
| D: |
| br label %return |
| |
| return: |
| %phi = phi i32 [ %mytmp41, %D ], [ %mytmp43, %A ], [%mytmp42, %entry] |
| ret i32 %phi |
| } |
| |
| ; Loop consists of A, B and C: |
| ; - A is the header |
| ; - A and C are exiting blocks |
| ; - B is an "internal" block that dominates exiting block C |
| ; - D and E are exit blocks. |
| ; Pattern: Value (%mytmp41) defined in internal block (B) and used in a |
| ; downstream block not related to the loop (return). The use |
| ; is a phi where the incoming block for %mytmp41 is not related |
| ; to the loop (D). |
| ; This pattern does not involve either the exiting blocks or |
| ; the exit blocks, which catches any such assumptions built |
| ; into the SSA reconstruction phase. |
| define i32 @phi-via-external-block(i32* %arg1, i32* %arg2) local_unnamed_addr align 2 { |
| ; CHECK-LABEL: @phi-via-external-block( |
| ; CHECK-NEXT: entry: |
| ; CHECK-NEXT: [[MYTMP42:%.*]] = load i32, i32* [[ARG1:%.*]], align 4 |
| ; CHECK-NEXT: br label [[A:%.*]] |
| ; CHECK: A: |
| ; CHECK-NEXT: [[CMP1:%.*]] = icmp slt i32 [[MYTMP42]], 0 |
| ; CHECK-NEXT: br i1 [[CMP1]], label [[B:%.*]], label [[LOOP_EXIT_GUARD:%.*]] |
| ; CHECK: B: |
| ; CHECK-NEXT: [[MYTMP41:%.*]] = load i32, i32* [[ARG2:%.*]], align 4 |
| ; CHECK-NEXT: br label [[C:%.*]] |
| ; CHECK: C: |
| ; CHECK-NEXT: [[CMP:%.*]] = icmp slt i32 [[MYTMP42]], 0 |
| ; CHECK-NEXT: br i1 [[CMP]], label [[A]], label [[LOOP_EXIT_GUARD]] |
| ; CHECK: D: |
| ; CHECK-NEXT: br label [[RETURN:%.*]] |
| ; CHECK: E: |
| ; CHECK-NEXT: br label [[RETURN]] |
| ; CHECK: return: |
| ; CHECK-NEXT: [[PHI:%.*]] = phi i32 [ [[MYTMP41_MOVED:%.*]], [[D:%.*]] ], [ [[MYTMP42]], [[E:%.*]] ] |
| ; CHECK-NEXT: ret i32 [[PHI]] |
| ; CHECK: loop.exit.guard: |
| ; CHECK-NEXT: [[GUARD_E:%.*]] = phi i1 [ true, [[A]] ], [ false, [[C]] ] |
| ; CHECK-NEXT: [[MYTMP41_MOVED]] = phi i32 [ undef, [[A]] ], [ [[MYTMP41]], [[C]] ] |
| ; CHECK-NEXT: br i1 [[GUARD_E]], label [[E]], label [[D]] |
| ; |
| entry: |
| %mytmp42 = load i32, i32* %arg1, align 4 |
| br label %A |
| |
| A: |
| %cmp1 = icmp slt i32 %mytmp42, 0 |
| br i1 %cmp1, label %B, label %E |
| |
| B: |
| %mytmp41 = load i32, i32* %arg2, align 4 |
| br label %C |
| |
| C: |
| %cmp = icmp slt i32 %mytmp42, 0 |
| br i1 %cmp, label %A, label %D |
| |
| D: |
| br label %return |
| |
| E: |
| br label %return |
| |
| return: |
| %phi = phi i32 [ %mytmp41, %D ], [ %mytmp42, %E ] |
| ret i32 %phi |
| } |