434 lines
13 KiB
LLVM
434 lines
13 KiB
LLVM
|
; NOTE: Assertions have been autogenerated by utils/update_test_checks.py
|
||
|
; RUN: opt %s -callbrprepare -S -o - | FileCheck %s
|
||
|
; RUN: opt %s -passes=callbrprepare -S -o - | FileCheck %s
|
||
|
|
||
|
define i32 @test0() {
|
||
|
; CHECK-LABEL: @test0(
|
||
|
; CHECK-NEXT: entry:
|
||
|
; CHECK-NEXT: [[OUT:%.*]] = callbr i32 asm "# $0", "=r,!i"()
|
||
|
; CHECK-NEXT: to label [[DIRECT:%.*]] [label %entry.indirect_crit_edge]
|
||
|
; CHECK: entry.indirect_crit_edge:
|
||
|
; CHECK-NEXT: [[TMP0:%.*]] = call i32 @llvm.callbr.landingpad.i32(i32 [[OUT]])
|
||
|
; CHECK-NEXT: br label [[INDIRECT:%.*]]
|
||
|
; CHECK: direct:
|
||
|
; CHECK-NEXT: [[OUT2:%.*]] = callbr i32 asm "# $0", "=r,!i"()
|
||
|
; CHECK-NEXT: to label [[DIRECT2:%.*]] [label %direct.indirect_crit_edge]
|
||
|
; CHECK: direct.indirect_crit_edge:
|
||
|
; CHECK-NEXT: [[TMP1:%.*]] = call i32 @llvm.callbr.landingpad.i32(i32 [[OUT2]])
|
||
|
; CHECK-NEXT: br label [[INDIRECT]]
|
||
|
; CHECK: direct2:
|
||
|
; CHECK-NEXT: ret i32 0
|
||
|
; CHECK: indirect:
|
||
|
; CHECK-NEXT: [[OUT3:%.*]] = phi i32 [ [[TMP0]], [[ENTRY_INDIRECT_CRIT_EDGE:%.*]] ], [ [[TMP1]], [[DIRECT_INDIRECT_CRIT_EDGE:%.*]] ]
|
||
|
; CHECK-NEXT: ret i32 [[OUT3]]
|
||
|
;
|
||
|
entry:
|
||
|
%out = callbr i32 asm "# $0", "=r,!i"()
|
||
|
to label %direct [label %indirect]
|
||
|
direct:
|
||
|
%out2 = callbr i32 asm "# $0", "=r,!i"()
|
||
|
to label %direct2 [label %indirect]
|
||
|
direct2:
|
||
|
ret i32 0
|
||
|
indirect:
|
||
|
%out3 = phi i32 [%out, %entry], [%out2, %direct]
|
||
|
ret i32 %out3
|
||
|
}
|
||
|
|
||
|
; Don't split edges unless they are critical, and callbr produces output, and
|
||
|
; that output is used.
|
||
|
; Here we have none of the above.
|
||
|
define i32 @dont_split0() {
|
||
|
; CHECK-LABEL: @dont_split0(
|
||
|
; CHECK-NEXT: entry:
|
||
|
; CHECK-NEXT: callbr void asm "", "!i"()
|
||
|
; CHECK-NEXT: to label [[X:%.*]] [label %y]
|
||
|
; CHECK: x:
|
||
|
; CHECK-NEXT: ret i32 42
|
||
|
; CHECK: y:
|
||
|
; CHECK-NEXT: ret i32 0
|
||
|
;
|
||
|
entry:
|
||
|
callbr void asm "", "!i"()
|
||
|
to label %x [label %y]
|
||
|
|
||
|
x:
|
||
|
ret i32 42
|
||
|
|
||
|
y:
|
||
|
ret i32 0
|
||
|
}
|
||
|
|
||
|
; Don't split edges unless they are critical, and callbr produces output, and
|
||
|
; that output is used.
|
||
|
; Here we have output, but no critical edge.
|
||
|
; That said, we ought to insert a callbr landing pad intrinsic call and update
|
||
|
; to use the correct SSA value.
|
||
|
define i32 @dont_split1() {
|
||
|
; CHECK-LABEL: @dont_split1(
|
||
|
; CHECK-NEXT: entry:
|
||
|
; CHECK-NEXT: [[TMP0:%.*]] = callbr i32 asm "", "=r,!i"()
|
||
|
; CHECK-NEXT: to label [[X:%.*]] [label %y]
|
||
|
; CHECK: x:
|
||
|
; CHECK-NEXT: ret i32 42
|
||
|
; CHECK: y:
|
||
|
; CHECK-NEXT: [[TMP1:%.*]] = call i32 @llvm.callbr.landingpad.i32(i32 [[TMP0]])
|
||
|
; CHECK-NEXT: ret i32 [[TMP1]]
|
||
|
;
|
||
|
entry:
|
||
|
%0 = callbr i32 asm "", "=r,!i"()
|
||
|
to label %x [label %y]
|
||
|
|
||
|
x:
|
||
|
ret i32 42
|
||
|
|
||
|
y:
|
||
|
ret i32 %0
|
||
|
}
|
||
|
|
||
|
; Don't split edges unless they are critical, and callbr produces output, and
|
||
|
; that output is used.
|
||
|
; Here we have a critical edge along an indirect branch, but no output.
|
||
|
define i32 @dont_split2() {
|
||
|
; CHECK-LABEL: @dont_split2(
|
||
|
; CHECK-NEXT: entry:
|
||
|
; CHECK-NEXT: callbr void asm "", "!i"()
|
||
|
; CHECK-NEXT: to label [[X:%.*]] [label %y]
|
||
|
; CHECK: x:
|
||
|
; CHECK-NEXT: br label [[Y:%.*]]
|
||
|
; CHECK: y:
|
||
|
; CHECK-NEXT: [[TMP0:%.*]] = phi i32 [ 0, [[X]] ], [ 42, [[ENTRY:%.*]] ]
|
||
|
; CHECK-NEXT: ret i32 [[TMP0]]
|
||
|
;
|
||
|
entry:
|
||
|
callbr void asm "", "!i"()
|
||
|
to label %x [label %y]
|
||
|
|
||
|
x:
|
||
|
br label %y
|
||
|
|
||
|
y:
|
||
|
%0 = phi i32 [ 0, %x ], [ 42, %entry ]
|
||
|
ret i32 %0
|
||
|
}
|
||
|
|
||
|
; Don't split edges unless they are critical, and callbr produces output, and
|
||
|
; that output is used.
|
||
|
; Here we're missing a use.
|
||
|
define i32 @dont_split3() {
|
||
|
; CHECK-LABEL: @dont_split3(
|
||
|
; CHECK-NEXT: entry:
|
||
|
; CHECK-NEXT: [[TMP0:%.*]] = callbr i32 asm "", "=r,!i"()
|
||
|
; CHECK-NEXT: to label [[X:%.*]] [label %v]
|
||
|
; CHECK: x:
|
||
|
; CHECK-NEXT: br label [[V:%.*]]
|
||
|
; CHECK: v:
|
||
|
; CHECK-NEXT: ret i32 42
|
||
|
;
|
||
|
entry:
|
||
|
%0 = callbr i32 asm "", "=r,!i"() to label %x [label %v]
|
||
|
|
||
|
x:
|
||
|
br label %v
|
||
|
|
||
|
v:
|
||
|
ret i32 42
|
||
|
}
|
||
|
|
||
|
; Don't split edges unless they are critical, and callbr produces output, and
|
||
|
; that output is used.
|
||
|
; Here we have output and a critical edge along an indirect branch.
|
||
|
define i32 @split_me0() {
|
||
|
; CHECK-LABEL: @split_me0(
|
||
|
; CHECK-NEXT: entry:
|
||
|
; CHECK-NEXT: [[TMP0:%.*]] = callbr i32 asm "", "=r,!i"()
|
||
|
; CHECK-NEXT: to label [[X:%.*]] [label %entry.y_crit_edge]
|
||
|
; CHECK: entry.y_crit_edge:
|
||
|
; CHECK-NEXT: [[TMP1:%.*]] = call i32 @llvm.callbr.landingpad.i32(i32 [[TMP0]])
|
||
|
; CHECK-NEXT: br label [[Y:%.*]]
|
||
|
; CHECK: x:
|
||
|
; CHECK-NEXT: br label [[Y]]
|
||
|
; CHECK: y:
|
||
|
; CHECK-NEXT: [[TMP2:%.*]] = phi i32 [ [[TMP1]], [[ENTRY_Y_CRIT_EDGE:%.*]] ], [ 42, [[X]] ]
|
||
|
; CHECK-NEXT: ret i32 [[TMP2]]
|
||
|
;
|
||
|
entry:
|
||
|
%0 = callbr i32 asm "", "=r,!i"()
|
||
|
to label %x [label %y]
|
||
|
|
||
|
x:
|
||
|
br label %y
|
||
|
|
||
|
y:
|
||
|
%1 = phi i32 [ %0, %entry ], [ 42, %x ]
|
||
|
ret i32 %1
|
||
|
}
|
||
|
|
||
|
; Here we have output and a critical edge along an indirect branch.
|
||
|
; Ensure that if we repeat the indirect destination, that we only split it
|
||
|
; once.
|
||
|
define i32 @split_me1(i1 %z) {
|
||
|
; CHECK-LABEL: @split_me1(
|
||
|
; CHECK-NEXT: entry:
|
||
|
; CHECK-NEXT: br i1 [[Z:%.*]], label [[W:%.*]], label [[V:%.*]]
|
||
|
; CHECK: w:
|
||
|
; CHECK-NEXT: [[TMP0:%.*]] = callbr i32 asm "", "=r,!i,!i"()
|
||
|
; CHECK-NEXT: to label [[X:%.*]] [label [[W_V_CRIT_EDGE:%.*]], label %w.v_crit_edge]
|
||
|
; CHECK: w.v_crit_edge:
|
||
|
; CHECK-NEXT: [[TMP1:%.*]] = call i32 @llvm.callbr.landingpad.i32(i32 [[TMP0]])
|
||
|
; CHECK-NEXT: br label [[V]]
|
||
|
; CHECK: x:
|
||
|
; CHECK-NEXT: ret i32 42
|
||
|
; CHECK: v:
|
||
|
; CHECK-NEXT: [[TMP2:%.*]] = phi i32 [ [[TMP1]], [[W_V_CRIT_EDGE]] ], [ undef, [[ENTRY:%.*]] ]
|
||
|
; CHECK-NEXT: ret i32 [[TMP2]]
|
||
|
;
|
||
|
entry:
|
||
|
br i1 %z, label %w, label %v
|
||
|
|
||
|
w:
|
||
|
%0 = callbr i32 asm "", "=r,!i,!i"()
|
||
|
to label %x [label %v, label %v]
|
||
|
|
||
|
x:
|
||
|
ret i32 42
|
||
|
|
||
|
v:
|
||
|
%1 = phi i32 [%0, %w], [%0, %w], [undef, %entry]
|
||
|
ret i32 %1
|
||
|
}
|
||
|
|
||
|
; A more interessting case of @split_me1. Check that we still only split the
|
||
|
; critical edge from w to v once.
|
||
|
define i32 @split_me2(i1 %z) {
|
||
|
; CHECK-LABEL: @split_me2(
|
||
|
; CHECK-NEXT: entry:
|
||
|
; CHECK-NEXT: br i1 [[Z:%.*]], label [[W:%.*]], label [[V:%.*]]
|
||
|
; CHECK: w:
|
||
|
; CHECK-NEXT: [[TMP0:%.*]] = callbr i32 asm "", "=r,!i,!i"()
|
||
|
; CHECK-NEXT: to label [[X:%.*]] [label [[W_V_CRIT_EDGE:%.*]], label %w.v_crit_edge]
|
||
|
; CHECK: w.v_crit_edge:
|
||
|
; CHECK-NEXT: [[TMP1:%.*]] = call i32 @llvm.callbr.landingpad.i32(i32 [[TMP0]])
|
||
|
; CHECK-NEXT: br label [[V]]
|
||
|
; CHECK: x:
|
||
|
; CHECK-NEXT: ret i32 42
|
||
|
; CHECK: v:
|
||
|
; CHECK-NEXT: [[TMP2:%.*]] = phi i32 [ [[TMP1]], [[W_V_CRIT_EDGE]] ], [ 42, [[ENTRY:%.*]] ]
|
||
|
; CHECK-NEXT: ret i32 [[TMP2]]
|
||
|
;
|
||
|
entry:
|
||
|
br i1 %z, label %w, label %v
|
||
|
|
||
|
w:
|
||
|
%0 = callbr i32 asm "", "=r,!i,!i"()
|
||
|
to label %x [label %v, label %v]
|
||
|
|
||
|
x:
|
||
|
ret i32 42
|
||
|
|
||
|
v:
|
||
|
%1 = phi i32 [ %0, %w ], [ 42, %entry ], [ %0, %w ]
|
||
|
ret i32 %1
|
||
|
}
|
||
|
|
||
|
; Here we have a diamond with no phi.
|
||
|
define i32 @dont_split4() {
|
||
|
; CHECK-LABEL: @dont_split4(
|
||
|
; CHECK-NEXT: entry:
|
||
|
; CHECK-NEXT: [[TMP0:%.*]] = callbr i32 asm "", "=r,!i"()
|
||
|
; CHECK-NEXT: to label [[X:%.*]] [label %y]
|
||
|
; CHECK: x:
|
||
|
; CHECK-NEXT: br label [[OUT:%.*]]
|
||
|
; CHECK: y:
|
||
|
; CHECK-NEXT: [[TMP1:%.*]] = call i32 @llvm.callbr.landingpad.i32(i32 [[TMP0]])
|
||
|
; CHECK-NEXT: br label [[OUT]]
|
||
|
; CHECK: out:
|
||
|
; CHECK-NEXT: [[TMP2:%.*]] = phi i32 [ [[TMP1]], [[Y:%.*]] ], [ [[TMP0]], [[X]] ]
|
||
|
; CHECK-NEXT: ret i32 [[TMP2]]
|
||
|
;
|
||
|
entry:
|
||
|
%0 = callbr i32 asm "", "=r,!i"()
|
||
|
to label %x [label %y]
|
||
|
|
||
|
x:
|
||
|
br label %out
|
||
|
|
||
|
y:
|
||
|
br label %out
|
||
|
|
||
|
out:
|
||
|
ret i32 %0
|
||
|
}
|
||
|
|
||
|
; Triangle with no phi.
|
||
|
define i32 @dont_split5() {
|
||
|
; CHECK-LABEL: @dont_split5(
|
||
|
; CHECK-NEXT: entry:
|
||
|
; CHECK-NEXT: [[TMP0:%.*]] = callbr i32 asm "", "=r,!i"()
|
||
|
; CHECK-NEXT: to label [[OUT:%.*]] [label %y]
|
||
|
; CHECK: y:
|
||
|
; CHECK-NEXT: [[TMP1:%.*]] = call i32 @llvm.callbr.landingpad.i32(i32 [[TMP0]])
|
||
|
; CHECK-NEXT: br label [[OUT]]
|
||
|
; CHECK: out:
|
||
|
; CHECK-NEXT: [[TMP2:%.*]] = phi i32 [ [[TMP1]], [[Y:%.*]] ], [ [[TMP0]], [[ENTRY:%.*]] ]
|
||
|
; CHECK-NEXT: ret i32 [[TMP2]]
|
||
|
;
|
||
|
entry:
|
||
|
%0 = callbr i32 asm "", "=r,!i"()
|
||
|
to label %out [label %y]
|
||
|
|
||
|
y:
|
||
|
br label %out
|
||
|
|
||
|
out:
|
||
|
ret i32 %0
|
||
|
}
|
||
|
|
||
|
; Triangle the other way with no phi.
|
||
|
define i32 @split_me3() {
|
||
|
; CHECK-LABEL: @split_me3(
|
||
|
; CHECK-NEXT: entry:
|
||
|
; CHECK-NEXT: [[TMP0:%.*]] = callbr i32 asm "", "=r,!i"()
|
||
|
; CHECK-NEXT: to label [[Y:%.*]] [label %entry.out_crit_edge]
|
||
|
; CHECK: entry.out_crit_edge:
|
||
|
; CHECK-NEXT: [[TMP1:%.*]] = call i32 @llvm.callbr.landingpad.i32(i32 [[TMP0]])
|
||
|
; CHECK-NEXT: br label [[OUT:%.*]]
|
||
|
; CHECK: y:
|
||
|
; CHECK-NEXT: br label [[OUT]]
|
||
|
; CHECK: out:
|
||
|
; CHECK-NEXT: [[TMP2:%.*]] = phi i32 [ [[TMP1]], [[ENTRY_OUT_CRIT_EDGE:%.*]] ], [ [[TMP0]], [[Y]] ]
|
||
|
; CHECK-NEXT: ret i32 [[TMP2]]
|
||
|
;
|
||
|
entry:
|
||
|
%0 = callbr i32 asm "", "=r,!i"()
|
||
|
to label %y [label %out]
|
||
|
|
||
|
y:
|
||
|
br label %out
|
||
|
|
||
|
out:
|
||
|
ret i32 %0
|
||
|
}
|
||
|
|
||
|
; Test callbr looping back on itself.
|
||
|
define i32 @dont_split6(i32 %0) {
|
||
|
; CHECK-LABEL: @dont_split6(
|
||
|
; CHECK-NEXT: entry:
|
||
|
; CHECK-NEXT: br label [[LOOP:%.*]]
|
||
|
; CHECK: loop:
|
||
|
; CHECK-NEXT: [[TMP1:%.*]] = phi i32 [ [[TMP0:%.*]], [[ENTRY:%.*]] ], [ [[TMP3:%.*]], [[LOOP_LOOP_CRIT_EDGE:%.*]] ]
|
||
|
; CHECK-NEXT: [[TMP2:%.*]] = callbr i32 asm "", "=r,0,!i"(i32 [[TMP1]])
|
||
|
; CHECK-NEXT: to label [[EXIT:%.*]] [label %loop.loop_crit_edge]
|
||
|
; CHECK: loop.loop_crit_edge:
|
||
|
; CHECK-NEXT: [[TMP3]] = call i32 @llvm.callbr.landingpad.i32(i32 [[TMP2]])
|
||
|
; CHECK-NEXT: br label [[LOOP]]
|
||
|
; CHECK: exit:
|
||
|
; CHECK-NEXT: ret i32 0
|
||
|
;
|
||
|
entry:
|
||
|
br label %loop
|
||
|
loop:
|
||
|
%1 = phi i32 [%0, %entry], [%2, %loop]
|
||
|
%2 = callbr i32 asm "", "=r,0,!i"(i32 %1) to label %exit [label %loop]
|
||
|
exit:
|
||
|
ret i32 0
|
||
|
}
|
||
|
|
||
|
; Test same direct+indirect dest no phi.
|
||
|
define i32 @split_me4() {
|
||
|
; CHECK-LABEL: @split_me4(
|
||
|
; CHECK-NEXT: entry:
|
||
|
; CHECK-NEXT: [[TMP0:%.*]] = callbr i32 asm "", "=r,!i"()
|
||
|
; CHECK-NEXT: to label [[SAME:%.*]] [label %entry.same_crit_edge]
|
||
|
; CHECK: entry.same_crit_edge:
|
||
|
; CHECK-NEXT: [[TMP1:%.*]] = call i32 @llvm.callbr.landingpad.i32(i32 [[TMP0]])
|
||
|
; CHECK-NEXT: br label [[SAME]]
|
||
|
; CHECK: same:
|
||
|
; CHECK-NEXT: [[TMP2:%.*]] = phi i32 [ [[TMP1]], [[ENTRY_SAME_CRIT_EDGE:%.*]] ], [ [[TMP0]], [[ENTRY:%.*]] ]
|
||
|
; CHECK-NEXT: ret i32 [[TMP2]]
|
||
|
;
|
||
|
entry:
|
||
|
%0 = callbr i32 asm "", "=r,!i"() to label %same [label %same]
|
||
|
same:
|
||
|
ret i32 %0
|
||
|
}
|
||
|
|
||
|
; Test same direct+indirect dest w/ phi.
|
||
|
define i32 @split_me5() {
|
||
|
; CHECK-LABEL: @split_me5(
|
||
|
; CHECK-NEXT: entry:
|
||
|
; CHECK-NEXT: [[TMP0:%.*]] = callbr i32 asm "", "=r,!i"()
|
||
|
; CHECK-NEXT: to label [[SAME:%.*]] [label %entry.same_crit_edge]
|
||
|
; CHECK: entry.same_crit_edge:
|
||
|
; CHECK-NEXT: [[TMP1:%.*]] = call i32 @llvm.callbr.landingpad.i32(i32 [[TMP0]])
|
||
|
; CHECK-NEXT: br label [[SAME]]
|
||
|
; CHECK: same:
|
||
|
; CHECK-NEXT: [[TMP2:%.*]] = phi i32 [ [[TMP1]], [[ENTRY_SAME_CRIT_EDGE:%.*]] ], [ [[TMP0]], [[ENTRY:%.*]] ]
|
||
|
; CHECK-NEXT: ret i32 [[TMP2]]
|
||
|
;
|
||
|
entry:
|
||
|
%0 = callbr i32 asm "", "=r,!i"() to label %same [label %same]
|
||
|
same:
|
||
|
%1 = phi i32 [%0, %entry], [%0, %entry]
|
||
|
ret i32 %1
|
||
|
}
|
||
|
|
||
|
; "The Devil's cross" (i.e. two asm goto with conflicting physreg constraints
|
||
|
; going to the same destination).
|
||
|
define i64 @split_me6() {
|
||
|
; CHECK-LABEL: @split_me6(
|
||
|
; CHECK-NEXT: entry:
|
||
|
; CHECK-NEXT: [[TMP0:%.*]] = callbr i64 asm "# $0 $1", "={dx},!i"()
|
||
|
; CHECK-NEXT: to label [[ASM_FALLTHROUGH:%.*]] [label %entry.foo_crit_edge]
|
||
|
; CHECK: entry.foo_crit_edge:
|
||
|
; CHECK-NEXT: [[TMP1:%.*]] = call i64 @llvm.callbr.landingpad.i64(i64 [[TMP0]])
|
||
|
; CHECK-NEXT: br label [[FOO:%.*]]
|
||
|
; CHECK: asm.fallthrough:
|
||
|
; CHECK-NEXT: [[TMP2:%.*]] = callbr i64 asm "# $0 $1", "={bx},!i"()
|
||
|
; CHECK-NEXT: to label [[FOO]] [label %asm.fallthrough.foo_crit_edge]
|
||
|
; CHECK: asm.fallthrough.foo_crit_edge:
|
||
|
; CHECK-NEXT: [[TMP3:%.*]] = call i64 @llvm.callbr.landingpad.i64(i64 [[TMP2]])
|
||
|
; CHECK-NEXT: br label [[FOO]]
|
||
|
; CHECK: foo:
|
||
|
; CHECK-NEXT: [[X_0:%.*]] = phi i64 [ [[TMP1]], [[ENTRY_FOO_CRIT_EDGE:%.*]] ], [ [[TMP3]], [[ASM_FALLTHROUGH_FOO_CRIT_EDGE:%.*]] ], [ [[TMP2]], [[ASM_FALLTHROUGH]] ]
|
||
|
; CHECK-NEXT: ret i64 [[X_0]]
|
||
|
;
|
||
|
entry:
|
||
|
%0 = callbr i64 asm "# $0 $1", "={dx},!i"()
|
||
|
to label %asm.fallthrough [label %foo]
|
||
|
|
||
|
asm.fallthrough:
|
||
|
%1 = callbr i64 asm "# $0 $1", "={bx},!i"()
|
||
|
to label %foo [label %foo]
|
||
|
|
||
|
foo:
|
||
|
%x.0 = phi i64 [ %0, %entry ], [ %1, %asm.fallthrough ], [ %1, %asm.fallthrough ]
|
||
|
ret i64 %x.0
|
||
|
}
|
||
|
|
||
|
; Test the result of the callbr having multiple uses to avoid iterator
|
||
|
; invalidation bugs in CallBrPrepare::UpdateSSA.
|
||
|
define i32 @multiple_split() {
|
||
|
; CHECK-LABEL: @multiple_split(
|
||
|
; CHECK-NEXT: entry:
|
||
|
; CHECK-NEXT: [[TMP0:%.*]] = callbr i32 asm "", "=r,!i"()
|
||
|
; CHECK-NEXT: to label [[X:%.*]] [label %y]
|
||
|
; CHECK: x:
|
||
|
; CHECK-NEXT: ret i32 42
|
||
|
; CHECK: y:
|
||
|
; CHECK-NEXT: [[TMP1:%.*]] = call i32 @llvm.callbr.landingpad.i32(i32 [[TMP0]])
|
||
|
; CHECK-NEXT: [[TMP2:%.*]] = add nsw i32 [[TMP1]], [[TMP1]]
|
||
|
; CHECK-NEXT: ret i32 [[TMP2]]
|
||
|
;
|
||
|
entry:
|
||
|
%0 = callbr i32 asm "", "=r,!i"()
|
||
|
to label %x [label %y]
|
||
|
|
||
|
x:
|
||
|
ret i32 42
|
||
|
|
||
|
y:
|
||
|
%1 = add nsw i32 %0, %0
|
||
|
ret i32 %1
|
||
|
}
|