llvm-project/llvm/test/Transforms/SpeculateAroundPHIs/basic-x86.ll

596 lines
16 KiB
LLVM
Raw Normal View History

Add a new pass to speculate around PHI nodes with constant (integer) operands when profitable. The core idea is to (re-)introduce some redundancies where their cost is hidden by the cost of materializing immediates for constant operands of PHI nodes. When the cost of the redundancies is covered by this, avoiding materializing the immediate has numerous benefits: 1) Less register pressure 2) Potential for further folding / combining 3) Potential for more efficient instructions due to immediate operand As a motivating example, consider the remarkably different cost on x86 of a SHL instruction with an immediate operand versus a register operand. This pattern turns up surprisingly frequently, but is somewhat rarely obvious as a significant performance problem. The pass is entirely target independent, but it does rely on the target cost model in TTI to decide when to speculate things around the PHI node. I've included x86-focused tests, but any target that sets up its immediate cost model should benefit from this pass. There is probably more that can be done in this space, but the pass as-is is enough to get some important performance on our internal benchmarks, and should be generally performance neutral, but help with more extensive benchmarking is always welcome. One awkward part is that this pass has to be scheduled after *everything* that can eliminate these kinds of redundancies. This includes SimplifyCFG, GVN, etc. I'm open to suggestions about better places to put this. We could in theory make it part of the codegen pass pipeline, but there doesn't really seem to be a good reason for that -- it isn't "lowering" in any sense and only relies on pretty standard cost model based TTI queries, so it seems to fit well with the "optimization" pipeline model. Still, further thoughts on the pipeline position are welcome. I've also only implemented this in the new pass manager. If folks are very interested, I can try to add it to the old PM as well, but I didn't really see much point (my use case is already switched over to the new PM). I've tested this pretty heavily without issue. A wide range of benchmarks internally show no change outside the noise, and I don't see any significant changes in SPEC either. However, the size class computation in tcmalloc is substantially improved by this, which turns into a 2% to 4% win on the hottest path through tcmalloc for us, so there are definitely important cases where this is going to make a substantial difference. Differential revision: https://reviews.llvm.org/D37467 llvm-svn: 319164
2017-11-28 19:32:31 +08:00
; Test the basic functionality of speculating around PHI nodes based on reduced
; cost of the constant operands to the PHI nodes using the x86 cost model.
;
; REQUIRES: x86-registered-target
; RUN: opt -S -passes=spec-phis < %s | FileCheck %s
target triple = "x86_64-unknown-unknown"
define i32 @test_basic(i1 %flag, i32 %arg) {
; CHECK-LABEL: define i32 @test_basic(
entry:
br i1 %flag, label %a, label %b
; CHECK: br i1 %flag, label %a, label %b
a:
br label %exit
; CHECK: a:
; CHECK-NEXT: %[[SUM_A:.*]] = add i32 %arg, 7
; CHECK-NEXT: br label %exit
b:
br label %exit
; CHECK: b:
; CHECK-NEXT: %[[SUM_B:.*]] = add i32 %arg, 11
; CHECK-NEXT: br label %exit
exit:
%p = phi i32 [ 7, %a ], [ 11, %b ]
%sum = add i32 %arg, %p
ret i32 %sum
; CHECK: exit:
; CHECK-NEXT: %[[PHI:.*]] = phi i32 [ %[[SUM_A]], %a ], [ %[[SUM_B]], %b ]
; CHECK-NEXT: ret i32 %[[PHI]]
}
; Check that we handle commuted operands and get the constant onto the RHS.
define i32 @test_commuted(i1 %flag, i32 %arg) {
; CHECK-LABEL: define i32 @test_commuted(
entry:
br i1 %flag, label %a, label %b
; CHECK: br i1 %flag, label %a, label %b
a:
br label %exit
; CHECK: a:
; CHECK-NEXT: %[[SUM_A:.*]] = add i32 %arg, 7
; CHECK-NEXT: br label %exit
b:
br label %exit
; CHECK: b:
; CHECK-NEXT: %[[SUM_B:.*]] = add i32 %arg, 11
; CHECK-NEXT: br label %exit
exit:
%p = phi i32 [ 7, %a ], [ 11, %b ]
%sum = add i32 %p, %arg
ret i32 %sum
; CHECK: exit:
; CHECK-NEXT: %[[PHI:.*]] = phi i32 [ %[[SUM_A]], %a ], [ %[[SUM_B]], %b ]
; CHECK-NEXT: ret i32 %[[PHI]]
}
define i32 @test_split_crit_edge(i1 %flag, i32 %arg) {
; CHECK-LABEL: define i32 @test_split_crit_edge(
entry:
br i1 %flag, label %exit, label %a
; CHECK: entry:
; CHECK-NEXT: br i1 %flag, label %[[ENTRY_SPLIT:.*]], label %a
;
; CHECK: [[ENTRY_SPLIT]]:
; CHECK-NEXT: %[[SUM_ENTRY_SPLIT:.*]] = add i32 %arg, 7
; CHECK-NEXT: br label %exit
a:
br label %exit
; CHECK: a:
; CHECK-NEXT: %[[SUM_A:.*]] = add i32 %arg, 11
; CHECK-NEXT: br label %exit
exit:
%p = phi i32 [ 7, %entry ], [ 11, %a ]
%sum = add i32 %arg, %p
ret i32 %sum
; CHECK: exit:
; CHECK-NEXT: %[[PHI:.*]] = phi i32 [ %[[SUM_ENTRY_SPLIT]], %[[ENTRY_SPLIT]] ], [ %[[SUM_A]], %a ]
; CHECK-NEXT: ret i32 %[[PHI]]
}
define i32 @test_no_spec_dominating_inst(i1 %flag, i32* %ptr) {
; CHECK-LABEL: define i32 @test_no_spec_dominating_inst(
entry:
%load = load i32, i32* %ptr
br i1 %flag, label %a, label %b
; CHECK: %[[LOAD:.*]] = load i32, i32* %ptr
; CHECK-NEXT: br i1 %flag, label %a, label %b
a:
br label %exit
; CHECK: a:
; CHECK-NEXT: %[[SUM_A:.*]] = add i32 %[[LOAD]], 7
; CHECK-NEXT: br label %exit
b:
br label %exit
; CHECK: b:
; CHECK-NEXT: %[[SUM_B:.*]] = add i32 %[[LOAD]], 11
; CHECK-NEXT: br label %exit
exit:
%p = phi i32 [ 7, %a ], [ 11, %b ]
%sum = add i32 %load, %p
ret i32 %sum
; CHECK: exit:
; CHECK-NEXT: %[[PHI:.*]] = phi i32 [ %[[SUM_A]], %a ], [ %[[SUM_B]], %b ]
; CHECK-NEXT: ret i32 %[[PHI]]
}
; We have special logic handling PHI nodes, make sure it doesn't get confused
; by a dominating PHI.
define i32 @test_no_spec_dominating_phi(i1 %flag1, i1 %flag2, i32 %x, i32 %y) {
; CHECK-LABEL: define i32 @test_no_spec_dominating_phi(
entry:
br i1 %flag1, label %x.block, label %y.block
; CHECK: entry:
; CHECK-NEXT: br i1 %flag1, label %x.block, label %y.block
x.block:
br label %merge
; CHECK: x.block:
; CHECK-NEXT: br label %merge
y.block:
br label %merge
; CHECK: y.block:
; CHECK-NEXT: br label %merge
merge:
%xy.phi = phi i32 [ %x, %x.block ], [ %y, %y.block ]
br i1 %flag2, label %a, label %b
; CHECK: merge:
; CHECK-NEXT: %[[XY_PHI:.*]] = phi i32 [ %x, %x.block ], [ %y, %y.block ]
; CHECK-NEXT: br i1 %flag2, label %a, label %b
a:
br label %exit
; CHECK: a:
; CHECK-NEXT: %[[SUM_A:.*]] = add i32 %[[XY_PHI]], 7
; CHECK-NEXT: br label %exit
b:
br label %exit
; CHECK: b:
; CHECK-NEXT: %[[SUM_B:.*]] = add i32 %[[XY_PHI]], 11
; CHECK-NEXT: br label %exit
exit:
%p = phi i32 [ 7, %a ], [ 11, %b ]
%sum = add i32 %xy.phi, %p
ret i32 %sum
; CHECK: exit:
; CHECK-NEXT: %[[SUM_PHI:.*]] = phi i32 [ %[[SUM_A]], %a ], [ %[[SUM_B]], %b ]
; CHECK-NEXT: ret i32 %[[SUM_PHI]]
}
; Ensure that we will speculate some number of "free" instructions on the given
; architecture even though they are unrelated to the PHI itself.
define i32 @test_speculate_free_insts(i1 %flag, i64 %arg) {
; CHECK-LABEL: define i32 @test_speculate_free_insts(
entry:
br i1 %flag, label %a, label %b
; CHECK: br i1 %flag, label %a, label %b
a:
br label %exit
; CHECK: a:
; CHECK-NEXT: %[[T1_A:.*]] = trunc i64 %arg to i48
; CHECK-NEXT: %[[T2_A:.*]] = trunc i48 %[[T1_A]] to i32
; CHECK-NEXT: %[[SUM_A:.*]] = add i32 %[[T2_A]], 7
; CHECK-NEXT: br label %exit
b:
br label %exit
; CHECK: b:
; CHECK-NEXT: %[[T1_B:.*]] = trunc i64 %arg to i48
; CHECK-NEXT: %[[T2_B:.*]] = trunc i48 %[[T1_B]] to i32
; CHECK-NEXT: %[[SUM_B:.*]] = add i32 %[[T2_B]], 11
; CHECK-NEXT: br label %exit
exit:
%p = phi i32 [ 7, %a ], [ 11, %b ]
%t1 = trunc i64 %arg to i48
%t2 = trunc i48 %t1 to i32
%sum = add i32 %t2, %p
ret i32 %sum
; CHECK: exit:
; CHECK-NEXT: %[[PHI:.*]] = phi i32 [ %[[SUM_A]], %a ], [ %[[SUM_B]], %b ]
; CHECK-NEXT: ret i32 %[[PHI]]
}
define i32 @test_speculate_free_phis(i1 %flag, i32 %arg1, i32 %arg2) {
; CHECK-LABEL: define i32 @test_speculate_free_phis(
entry:
br i1 %flag, label %a, label %b
; CHECK: br i1 %flag, label %a, label %b
a:
br label %exit
; CHECK: a:
; CHECK-NEXT: %[[SUM_A:.*]] = add i32 %arg1, 7
; CHECK-NEXT: br label %exit
b:
br label %exit
; CHECK: b:
; CHECK-NEXT: %[[SUM_B:.*]] = add i32 %arg2, 11
; CHECK-NEXT: br label %exit
exit:
%p1 = phi i32 [ 7, %a ], [ 11, %b ]
%p2 = phi i32 [ %arg1, %a ], [ %arg2, %b ]
%sum = add i32 %p2, %p1
ret i32 %sum
; CHECK: exit:
; CHECK-NEXT: %[[PHI:.*]] = phi i32 [ %[[SUM_A]], %a ], [ %[[SUM_B]], %b ]
; We don't DCE the now unused PHI node...
; CHECK-NEXT: %{{.*}} = phi i32 [ %arg1, %a ], [ %arg2, %b ]
; CHECK-NEXT: ret i32 %[[PHI]]
}
; We shouldn't speculate multiple uses even if each individually looks
; profitable because of the total cost.
define i32 @test_no_spec_multi_uses(i1 %flag, i32 %arg1, i32 %arg2, i32 %arg3) {
; CHECK-LABEL: define i32 @test_no_spec_multi_uses(
entry:
br i1 %flag, label %a, label %b
; CHECK: br i1 %flag, label %a, label %b
a:
br label %exit
; CHECK: a:
; CHECK-NEXT: br label %exit
b:
br label %exit
; CHECK: b:
; CHECK-NEXT: br label %exit
exit:
%p = phi i32 [ 7, %a ], [ 11, %b ]
%add1 = add i32 %arg1, %p
%add2 = add i32 %arg2, %p
%add3 = add i32 %arg3, %p
%sum1 = add i32 %add1, %add2
%sum2 = add i32 %sum1, %add3
ret i32 %sum2
; CHECK: exit:
; CHECK-NEXT: %[[PHI:.*]] = phi i32 [ 7, %a ], [ 11, %b ]
; CHECK-NEXT: %[[ADD1:.*]] = add i32 %arg1, %[[PHI]]
; CHECK-NEXT: %[[ADD2:.*]] = add i32 %arg2, %[[PHI]]
; CHECK-NEXT: %[[ADD3:.*]] = add i32 %arg3, %[[PHI]]
; CHECK-NEXT: %[[SUM1:.*]] = add i32 %[[ADD1]], %[[ADD2]]
; CHECK-NEXT: %[[SUM2:.*]] = add i32 %[[SUM1]], %[[ADD3]]
; CHECK-NEXT: ret i32 %[[SUM2]]
}
define i32 @test_multi_phis1(i1 %flag, i32 %arg) {
; CHECK-LABEL: define i32 @test_multi_phis1(
entry:
br i1 %flag, label %a, label %b
; CHECK: br i1 %flag, label %a, label %b
a:
br label %exit
; CHECK: a:
; CHECK-NEXT: %[[SUM_A1:.*]] = add i32 %arg, 1
; CHECK-NEXT: %[[SUM_A2:.*]] = add i32 %[[SUM_A1]], 3
; CHECK-NEXT: %[[SUM_A3:.*]] = add i32 %[[SUM_A2]], 5
; CHECK-NEXT: br label %exit
b:
br label %exit
; CHECK: b:
; CHECK-NEXT: %[[SUM_B1:.*]] = add i32 %arg, 2
; CHECK-NEXT: %[[SUM_B2:.*]] = add i32 %[[SUM_B1]], 4
; CHECK-NEXT: %[[SUM_B3:.*]] = add i32 %[[SUM_B2]], 6
; CHECK-NEXT: br label %exit
exit:
%p1 = phi i32 [ 1, %a ], [ 2, %b ]
%p2 = phi i32 [ 3, %a ], [ 4, %b ]
%p3 = phi i32 [ 5, %a ], [ 6, %b ]
%sum1 = add i32 %arg, %p1
%sum2 = add i32 %sum1, %p2
%sum3 = add i32 %sum2, %p3
ret i32 %sum3
; CHECK: exit:
; CHECK-NEXT: %[[PHI:.*]] = phi i32 [ %[[SUM_A3]], %a ], [ %[[SUM_B3]], %b ]
; CHECK-NEXT: ret i32 %[[PHI]]
}
; Check that the order of the PHIs doesn't impact the behavior.
define i32 @test_multi_phis2(i1 %flag, i32 %arg) {
; CHECK-LABEL: define i32 @test_multi_phis2(
entry:
br i1 %flag, label %a, label %b
; CHECK: br i1 %flag, label %a, label %b
a:
br label %exit
; CHECK: a:
; CHECK-NEXT: %[[SUM_A1:.*]] = add i32 %arg, 1
; CHECK-NEXT: %[[SUM_A2:.*]] = add i32 %[[SUM_A1]], 3
; CHECK-NEXT: %[[SUM_A3:.*]] = add i32 %[[SUM_A2]], 5
; CHECK-NEXT: br label %exit
b:
br label %exit
; CHECK: b:
; CHECK-NEXT: %[[SUM_B1:.*]] = add i32 %arg, 2
; CHECK-NEXT: %[[SUM_B2:.*]] = add i32 %[[SUM_B1]], 4
; CHECK-NEXT: %[[SUM_B3:.*]] = add i32 %[[SUM_B2]], 6
; CHECK-NEXT: br label %exit
exit:
%p3 = phi i32 [ 5, %a ], [ 6, %b ]
%p2 = phi i32 [ 3, %a ], [ 4, %b ]
%p1 = phi i32 [ 1, %a ], [ 2, %b ]
%sum1 = add i32 %arg, %p1
%sum2 = add i32 %sum1, %p2
%sum3 = add i32 %sum2, %p3
ret i32 %sum3
; CHECK: exit:
; CHECK-NEXT: %[[PHI:.*]] = phi i32 [ %[[SUM_A3]], %a ], [ %[[SUM_B3]], %b ]
; CHECK-NEXT: ret i32 %[[PHI]]
}
define i32 @test_no_spec_indirectbr(i1 %flag, i32 %arg) {
; CHECK-LABEL: define i32 @test_no_spec_indirectbr(
entry:
br i1 %flag, label %a, label %b
; CHECK: entry:
; CHECK-NEXT: br i1 %flag, label %a, label %b
a:
indirectbr i8* undef, [label %exit]
; CHECK: a:
; CHECK-NEXT: indirectbr i8* undef, [label %exit]
b:
indirectbr i8* undef, [label %exit]
; CHECK: b:
; CHECK-NEXT: indirectbr i8* undef, [label %exit]
exit:
%p = phi i32 [ 7, %a ], [ 11, %b ]
%sum = add i32 %arg, %p
ret i32 %sum
; CHECK: exit:
; CHECK-NEXT: %[[PHI:.*]] = phi i32 [ 7, %a ], [ 11, %b ]
; CHECK-NEXT: %[[SUM:.*]] = add i32 %arg, %[[PHI]]
; CHECK-NEXT: ret i32 %[[SUM]]
}
declare void @g()
declare i32 @__gxx_personality_v0(...)
; FIXME: We should be able to handle this case -- only the exceptional edge is
; impossible to split.
define i32 @test_no_spec_invoke_continue(i1 %flag, i32 %arg) personality i8* bitcast (i32 (...)* @__gxx_personality_v0 to i8*) {
; CHECK-LABEL: define i32 @test_no_spec_invoke_continue(
entry:
br i1 %flag, label %a, label %b
; CHECK: entry:
; CHECK-NEXT: br i1 %flag, label %a, label %b
a:
invoke void @g()
to label %exit unwind label %lpad
; CHECK: a:
; CHECK-NEXT: invoke void @g()
; CHECK-NEXT: to label %exit unwind label %lpad
b:
invoke void @g()
to label %exit unwind label %lpad
; CHECK: b:
; CHECK-NEXT: invoke void @g()
; CHECK-NEXT: to label %exit unwind label %lpad
exit:
%p = phi i32 [ 7, %a ], [ 11, %b ]
%sum = add i32 %arg, %p
ret i32 %sum
; CHECK: exit:
; CHECK-NEXT: %[[PHI:.*]] = phi i32 [ 7, %a ], [ 11, %b ]
; CHECK-NEXT: %[[SUM:.*]] = add i32 %arg, %[[PHI]]
; CHECK-NEXT: ret i32 %[[SUM]]
lpad:
%lp = landingpad { i8*, i32 }
cleanup
resume { i8*, i32 } undef
}
define i32 @test_no_spec_landingpad(i32 %arg, i32* %ptr) personality i8* bitcast (i32 (...)* @__gxx_personality_v0 to i8*) {
; CHECK-LABEL: define i32 @test_no_spec_landingpad(
entry:
invoke void @g()
to label %invoke.cont unwind label %lpad
; CHECK: entry:
; CHECK-NEXT: invoke void @g()
; CHECK-NEXT: to label %invoke.cont unwind label %lpad
invoke.cont:
invoke void @g()
to label %exit unwind label %lpad
; CHECK: invoke.cont:
; CHECK-NEXT: invoke void @g()
; CHECK-NEXT: to label %exit unwind label %lpad
lpad:
%p = phi i32 [ 7, %entry ], [ 11, %invoke.cont ]
%lp = landingpad { i8*, i32 }
cleanup
%sum = add i32 %arg, %p
store i32 %sum, i32* %ptr
resume { i8*, i32 } undef
; CHECK: lpad:
; CHECK-NEXT: %[[PHI:.*]] = phi i32 [ 7, %entry ], [ 11, %invoke.cont ]
exit:
ret i32 0
}
declare i32 @__CxxFrameHandler3(...)
define i32 @test_no_spec_cleanuppad(i32 %arg, i32* %ptr) personality i32 (...)* @__CxxFrameHandler3 {
; CHECK-LABEL: define i32 @test_no_spec_cleanuppad(
entry:
invoke void @g()
to label %invoke.cont unwind label %lpad
; CHECK: entry:
; CHECK-NEXT: invoke void @g()
; CHECK-NEXT: to label %invoke.cont unwind label %lpad
invoke.cont:
invoke void @g()
to label %exit unwind label %lpad
; CHECK: invoke.cont:
; CHECK-NEXT: invoke void @g()
; CHECK-NEXT: to label %exit unwind label %lpad
lpad:
%p = phi i32 [ 7, %entry ], [ 11, %invoke.cont ]
%cp = cleanuppad within none []
%sum = add i32 %arg, %p
store i32 %sum, i32* %ptr
cleanupret from %cp unwind to caller
; CHECK: lpad:
; CHECK-NEXT: %[[PHI:.*]] = phi i32 [ 7, %entry ], [ 11, %invoke.cont ]
exit:
ret i32 0
}
; Check that we don't fall over when confronted with seemingly reasonable code
; for us to handle but in an unreachable region and with non-PHI use-def
; cycles.
define i32 @test_unreachable_non_phi_cycles(i1 %flag, i32 %arg) {
; CHECK-LABEL: define i32 @test_unreachable_non_phi_cycles(
entry:
ret i32 42
; CHECK: entry:
; CHECK-NEXT: ret i32 42
a:
br label %exit
; CHECK: a:
; CHECK-NEXT: br label %exit
b:
br label %exit
; CHECK: b:
; CHECK-NEXT: br label %exit
exit:
%p = phi i32 [ 7, %a ], [ 11, %b ]
%zext = zext i32 %sum to i64
%trunc = trunc i64 %zext to i32
%sum = add i32 %trunc, %p
br i1 %flag, label %a, label %b
; CHECK: exit:
; CHECK-NEXT: %[[PHI:.*]] = phi i32 [ 7, %a ], [ 11, %b ]
; CHECK-NEXT: %[[ZEXT:.*]] = zext i32 %[[SUM:.*]] to i64
; CHECK-NEXT: %[[TRUNC:.*]] = trunc i64 %[[ZEXT]] to i32
; CHECK-NEXT: %[[SUM]] = add i32 %[[TRUNC]], %[[PHI]]
; CHECK-NEXT: br i1 %flag, label %a, label %b
}
; Check that we don't speculate in the face of an expensive immediate. There
; are two reasons this should never speculate. First, even a local analysis
; should fail because it makes some paths (%a) potentially more expensive due
; to multiple uses of the immediate. Additionally, when we go to speculate the
; instructions, their cost will also be too high.
; FIXME: The goal is really to test the first property, but there doesn't
; happen to be any way to use free-to-speculate instructions here so that it
; would be the only interesting property.
define i64 @test_expensive_imm(i32 %flag, i64 %arg) {
; CHECK-LABEL: define i64 @test_expensive_imm(
entry:
switch i32 %flag, label %a [
i32 1, label %b
i32 2, label %c
i32 3, label %d
]
; CHECK: switch i32 %flag, label %a [
; CHECK-NEXT: i32 1, label %b
; CHECK-NEXT: i32 2, label %c
; CHECK-NEXT: i32 3, label %d
; CHECK-NEXT: ]
a:
br label %exit
; CHECK: a:
; CHECK-NEXT: br label %exit
b:
br label %exit
; CHECK: b:
; CHECK-NEXT: br label %exit
c:
br label %exit
; CHECK: c:
; CHECK-NEXT: br label %exit
d:
br label %exit
; CHECK: d:
; CHECK-NEXT: br label %exit
exit:
%p = phi i64 [ 4294967296, %a ], [ 1, %b ], [ 1, %c ], [ 1, %d ]
%sum1 = add i64 %arg, %p
%sum2 = add i64 %sum1, %p
ret i64 %sum2
; CHECK: exit:
; CHECK-NEXT: %[[PHI:.*]] = phi i64 [ {{[0-9]+}}, %a ], [ 1, %b ], [ 1, %c ], [ 1, %d ]
; CHECK-NEXT: %[[SUM1:.*]] = add i64 %arg, %[[PHI]]
; CHECK-NEXT: %[[SUM2:.*]] = add i64 %[[SUM1]], %[[PHI]]
; CHECK-NEXT: ret i64 %[[SUM2]]
}
define i32 @test_no_spec_non_postdominating_uses(i1 %flag1, i1 %flag2, i32 %arg) {
; CHECK-LABEL: define i32 @test_no_spec_non_postdominating_uses(
entry:
br i1 %flag1, label %a, label %b
; CHECK: br i1 %flag1, label %a, label %b
a:
br label %merge
; CHECK: a:
; CHECK-NEXT: %[[SUM_A:.*]] = add i32 %arg, 7
; CHECK-NEXT: br label %merge
b:
br label %merge
; CHECK: b:
; CHECK-NEXT: %[[SUM_B:.*]] = add i32 %arg, 11
; CHECK-NEXT: br label %merge
merge:
%p1 = phi i32 [ 7, %a ], [ 11, %b ]
%p2 = phi i32 [ 13, %a ], [ 42, %b ]
%sum1 = add i32 %arg, %p1
br i1 %flag2, label %exit1, label %exit2
; CHECK: merge:
; CHECK-NEXT: %[[PHI1:.*]] = phi i32 [ %[[SUM_A]], %a ], [ %[[SUM_B]], %b ]
; CHECK-NEXT: %[[PHI2:.*]] = phi i32 [ 13, %a ], [ 42, %b ]
; CHECK-NEXT: br i1 %flag2, label %exit1, label %exit2
exit1:
ret i32 %sum1
; CHECK: exit1:
; CHECK-NEXT: ret i32 %[[PHI1]]
exit2:
%sum2 = add i32 %arg, %p2
ret i32 %sum2
; CHECK: exit2:
; CHECK-NEXT: %[[SUM2:.*]] = add i32 %arg, %[[PHI2]]
; CHECK-NEXT: ret i32 %[[SUM2]]
}