llvm-project/llvm/test/CodeGen/AArch64/callbr-asm-outputs-indirect-isel.ll
Simon Tatham 56acb06bc6
[ARM,AArch64] Don't put BTI at asm goto branch targets (#141562)
In 'asm goto' statements ('callbr' in LLVM IR), you can specify one or
more labels / basic blocks in the containing function which the assembly
code might jump to. If you're also compiling with branch target
enforcement via BTI, then previously listing a basic block as a possible
jump destination of an asm goto would cause a BTI instruction to be
placed at the start of the block, in case the assembly code used an
_indirect_ branch instruction (i.e. to a destination address read from a
register) to jump to that location. Now it doesn't do that any more:
branches to destination labels from the assembly code are assumed to be
direct branches (to a relative offset encoded in the instruction), which
don't require a BTI at their destination.

This change was proposed in https://discourse.llvm.org/t/85845 and there
seemed to be no disagreement. The rationale is:

1. it brings clang's handling of asm goto in Arm and AArch64 in line
with gcc's, which didn't generate BTIs at the target labels in the first
place.

2. it improves performance in the Linux kernel, which uses a lot of 'asm
goto' in which the assembly language just contains a NOP, and the
label's address is saved elsewhere to let the kernel self-modify at run
time to swap between the original NOP and a direct branch to the label.
This allows hot code paths to be instrumented for debugging, at only the
cost of a NOP when the instrumentation is turned off, instead of the
larger cost of an indirect branch. In this situation a BTI is
unnecessary (if the branch happens it's direct), and since the code
paths are hot, also a noticeable performance hit.

Implementation:

`SelectionDAGBuilder::visitCallBr` is the place where 'asm goto' target
labels are handled. It calls `setIsInlineAsmBrIndirectTarget()` on each
target `MachineBasicBlock`. Previously it also called
`setMachineBlockAddressTaken()`, which made `hasAddressTaken()` return
true, which caused a BTI to be added in the Arm backends.

Now `visitCallBr` doesn't call `setMachineBlockAddressTaken()` any more
on asm goto targets, but `hasAddressTaken()` also checks the flag set by
`setIsInlineAsmBrIndirectTarget()`. So call sites that were using
`hasAddressTaken()` don't need to be modified. But the Arm backends
don't call `hasAddressTaken()` any more: instead they test two more
specific query functions that cover all the reasons `hasAddressTaken()`
might have returned true _except_ being an asm goto target.

Testing:

The new test `AArch64/callbr-asm-label-bti.ll` is testing the actual
change, where it expects not to see a `bti` instruction after
`[[LABEL]]`. The rest of the test changes are all churn, due to the
flags on basic blocks changing. Actual output code hasn't changed in any
of the existing tests, only comments and diagnostics.

Further work:

`RISCVIndirectBranchTracking.cpp` and `X86IndirectBranchTracking.cpp`
also call `hasAddressTaken()` in a way that might benefit from using the
same more specific check I've put in `ARMBranchTargets.cpp` and
`AArch64BranchTargets.cpp`. But I'm not sure of that, so in this commit
I've only changed the Arm backends, and left those alone.
2025-06-03 08:44:13 +01:00

558 lines
20 KiB
LLVM

; NOTE: Assertions have been autogenerated by utils/update_mir_test_checks.py
; RUN: llc -mtriple=aarch64-linux-gnu %s -o - -verify-machineinstrs \
; RUN: -start-before=aarch64-isel -stop-after=finalize-isel \
; RUN: -global-isel=0 -fast-isel=0 | FileCheck %s
; This file was initially generated via:
; $ opt -S -callbrprepare llvm/test/CodeGen/AArch64/callbr-prepare.ll -o \
; llvm/test/CodeGen/AArch64/callbr-asm-outputs-indirect-isel.ll
; TODO: should we remove test cases that don't use landingpad intrinsic?
; They're not interesting IMO.
; Removed is the test case for x86 machine specific physreg constraints.
define i32 @test0() {
; CHECK-LABEL: name: test0
; CHECK: bb.0.entry:
; CHECK-NEXT: successors: %bb.2(0x80000000), %bb.1(0x00000000)
; CHECK-NEXT: {{ $}}
; CHECK-NEXT: INLINEASM_BR &"# $0", 0 /* attdialect */, {{[0-9]+}} /* regdef:GPR32common */, def %5, 13 /* imm */, %bb.1
; CHECK-NEXT: [[COPY:%[0-9]+]]:gpr32all = COPY %5
; CHECK-NEXT: B %bb.2
; CHECK-NEXT: {{ $}}
; CHECK-NEXT: bb.1.entry.indirect_crit_edge (inlineasm-br-indirect-target):
; CHECK-NEXT: successors: %bb.5(0x80000000)
; CHECK-NEXT: {{ $}}
; CHECK-NEXT: [[COPY1:%[0-9]+]]:gpr32all = COPY %5
; CHECK-NEXT: B %bb.5
; CHECK-NEXT: {{ $}}
; CHECK-NEXT: bb.2.direct:
; CHECK-NEXT: successors: %bb.4(0x80000000), %bb.3(0x00000000)
; CHECK-NEXT: {{ $}}
; CHECK-NEXT: INLINEASM_BR &"# $0", 0 /* attdialect */, {{[0-9]+}} /* regdef:GPR32common */, def %7, 13 /* imm */, %bb.3
; CHECK-NEXT: [[COPY2:%[0-9]+]]:gpr32all = COPY %7
; CHECK-NEXT: B %bb.4
; CHECK-NEXT: {{ $}}
; CHECK-NEXT: bb.3.direct.indirect_crit_edge (inlineasm-br-indirect-target):
; CHECK-NEXT: successors: %bb.5(0x80000000)
; CHECK-NEXT: {{ $}}
; CHECK-NEXT: [[COPY3:%[0-9]+]]:gpr32all = COPY %7
; CHECK-NEXT: B %bb.5
; CHECK-NEXT: {{ $}}
; CHECK-NEXT: bb.4.direct2:
; CHECK-NEXT: [[COPY4:%[0-9]+]]:gpr32all = COPY $wzr
; CHECK-NEXT: $w0 = COPY [[COPY4]]
; CHECK-NEXT: RET_ReallyLR implicit $w0
; CHECK-NEXT: {{ $}}
; CHECK-NEXT: bb.5.indirect:
; CHECK-NEXT: [[PHI:%[0-9]+]]:gpr32all = PHI [[COPY1]], %bb.1, [[COPY3]], %bb.3
; CHECK-NEXT: $w0 = COPY [[PHI]]
; CHECK-NEXT: RET_ReallyLR implicit $w0
entry:
%out = callbr i32 asm "# $0", "=r,!i"()
to label %direct [label %entry.indirect_crit_edge]
entry.indirect_crit_edge: ; preds = %entry
%0 = call i32 @llvm.callbr.landingpad.i32(i32 %out)
br label %indirect
direct: ; preds = %entry
%out2 = callbr i32 asm "# $0", "=r,!i"()
to label %direct2 [label %direct.indirect_crit_edge]
direct.indirect_crit_edge: ; preds = %direct
%1 = call i32 @llvm.callbr.landingpad.i32(i32 %out2)
br label %indirect
direct2: ; preds = %direct
ret i32 0
indirect: ; preds = %direct.indirect_crit_edge, %entry.indirect_crit_edge
%out3 = phi i32 [ %0, %entry.indirect_crit_edge ], [ %1, %direct.indirect_crit_edge ]
ret i32 %out3
}
define i32 @dont_split0() {
; CHECK-LABEL: name: dont_split0
; CHECK: bb.0.entry:
; CHECK-NEXT: successors: %bb.1(0x80000000), %bb.2(0x00000000)
; CHECK-NEXT: {{ $}}
; CHECK-NEXT: INLINEASM_BR &"", 0 /* attdialect */, 13 /* imm */, %bb.2
; CHECK-NEXT: B %bb.1
; CHECK-NEXT: {{ $}}
; CHECK-NEXT: bb.1.x:
; CHECK-NEXT: [[MOVi32imm:%[0-9]+]]:gpr32 = MOVi32imm 42
; CHECK-NEXT: $w0 = COPY [[MOVi32imm]]
; CHECK-NEXT: RET_ReallyLR implicit $w0
; CHECK-NEXT: {{ $}}
; CHECK-NEXT: bb.2.y (inlineasm-br-indirect-target):
; CHECK-NEXT: [[COPY:%[0-9]+]]:gpr32all = COPY $wzr
; CHECK-NEXT: $w0 = COPY [[COPY]]
; CHECK-NEXT: RET_ReallyLR implicit $w0
entry:
callbr void asm "", "!i"()
to label %x [label %y]
x: ; preds = %entry
ret i32 42
y: ; preds = %entry
ret i32 0
}
define i32 @dont_split1() {
; CHECK-LABEL: name: dont_split1
; CHECK: bb.0.entry:
; CHECK-NEXT: successors: %bb.1(0x80000000), %bb.2(0x00000000)
; CHECK-NEXT: {{ $}}
; CHECK-NEXT: INLINEASM_BR &"", 0 /* attdialect */, {{[0-9]+}} /* regdef:GPR32common */, def %1, 13 /* imm */, %bb.2
; CHECK-NEXT: [[COPY:%[0-9]+]]:gpr32all = COPY %1
; CHECK-NEXT: B %bb.1
; CHECK-NEXT: {{ $}}
; CHECK-NEXT: bb.1.x:
; CHECK-NEXT: [[MOVi32imm:%[0-9]+]]:gpr32 = MOVi32imm 42
; CHECK-NEXT: $w0 = COPY [[MOVi32imm]]
; CHECK-NEXT: RET_ReallyLR implicit $w0
; CHECK-NEXT: {{ $}}
; CHECK-NEXT: bb.2.y (inlineasm-br-indirect-target):
; CHECK-NEXT: $w0 = COPY %1
; CHECK-NEXT: RET_ReallyLR implicit $w0
entry:
%0 = callbr i32 asm "", "=r,!i"()
to label %x [label %y]
x: ; preds = %entry
ret i32 42
y: ; preds = %entry
%1 = call i32 @llvm.callbr.landingpad.i32(i32 %0)
ret i32 %1
}
define i32 @dont_split2() {
; CHECK-LABEL: name: dont_split2
; CHECK: bb.0.entry:
; CHECK-NEXT: successors: %bb.1(0x80000000), %bb.2(0x00000000)
; CHECK-NEXT: {{ $}}
; CHECK-NEXT: [[MOVi32imm:%[0-9]+]]:gpr32 = MOVi32imm 42
; CHECK-NEXT: [[COPY:%[0-9]+]]:gpr32all = COPY [[MOVi32imm]]
; CHECK-NEXT: INLINEASM_BR &"", 0 /* attdialect */, 13 /* imm */, %bb.2
; CHECK-NEXT: B %bb.1
; CHECK-NEXT: {{ $}}
; CHECK-NEXT: bb.1.x:
; CHECK-NEXT: successors: %bb.2(0x80000000)
; CHECK-NEXT: {{ $}}
; CHECK-NEXT: [[COPY1:%[0-9]+]]:gpr32all = COPY $wzr
; CHECK-NEXT: [[COPY2:%[0-9]+]]:gpr32all = COPY [[COPY1]]
; CHECK-NEXT: {{ $}}
; CHECK-NEXT: bb.2.y (inlineasm-br-indirect-target):
; CHECK-NEXT: [[PHI:%[0-9]+]]:gpr32all = PHI [[COPY]], %bb.0, [[COPY2]], %bb.1
; CHECK-NEXT: $w0 = COPY [[PHI]]
; CHECK-NEXT: RET_ReallyLR implicit $w0
entry:
callbr void asm "", "!i"()
to label %x [label %y]
x: ; preds = %entry
br label %y
y: ; preds = %x, %entry
%0 = phi i32 [ 0, %x ], [ 42, %entry ]
ret i32 %0
}
define i32 @dont_split3() {
; CHECK-LABEL: name: dont_split3
; CHECK: bb.0.entry:
; CHECK-NEXT: successors: %bb.1(0x80000000), %bb.2(0x00000000)
; CHECK-NEXT: {{ $}}
; CHECK-NEXT: INLINEASM_BR &"", 0 /* attdialect */, {{[0-9]+}} /* regdef:GPR32common */, def %0, 13 /* imm */, %bb.2
; CHECK-NEXT: B %bb.1
; CHECK-NEXT: {{ $}}
; CHECK-NEXT: bb.1.x:
; CHECK-NEXT: successors: %bb.2(0x80000000)
; CHECK-NEXT: {{ $}}
; CHECK-NEXT: bb.2.v (inlineasm-br-indirect-target):
; CHECK-NEXT: [[MOVi32imm:%[0-9]+]]:gpr32 = MOVi32imm 42
; CHECK-NEXT: $w0 = COPY [[MOVi32imm]]
; CHECK-NEXT: RET_ReallyLR implicit $w0
entry:
%0 = callbr i32 asm "", "=r,!i"()
to label %x [label %v]
x: ; preds = %entry
br label %v
v: ; preds = %x, %entry
ret i32 42
}
define i32 @split_me0() {
; CHECK-LABEL: name: split_me0
; CHECK: bb.0.entry:
; CHECK-NEXT: successors: %bb.2(0x80000000), %bb.1(0x00000000)
; CHECK-NEXT: {{ $}}
; CHECK-NEXT: INLINEASM_BR &"", 0 /* attdialect */, {{[0-9]+}} /* regdef:GPR32common */, def %3, 13 /* imm */, %bb.1
; CHECK-NEXT: [[COPY:%[0-9]+]]:gpr32all = COPY %3
; CHECK-NEXT: B %bb.2
; CHECK-NEXT: {{ $}}
; CHECK-NEXT: bb.1.entry.y_crit_edge (inlineasm-br-indirect-target):
; CHECK-NEXT: successors: %bb.3(0x80000000)
; CHECK-NEXT: {{ $}}
; CHECK-NEXT: [[COPY1:%[0-9]+]]:gpr32all = COPY %3
; CHECK-NEXT: B %bb.3
; CHECK-NEXT: {{ $}}
; CHECK-NEXT: bb.2.x:
; CHECK-NEXT: successors: %bb.3(0x80000000)
; CHECK-NEXT: {{ $}}
; CHECK-NEXT: [[MOVi32imm:%[0-9]+]]:gpr32 = MOVi32imm 42
; CHECK-NEXT: [[COPY2:%[0-9]+]]:gpr32all = COPY [[MOVi32imm]]
; CHECK-NEXT: {{ $}}
; CHECK-NEXT: bb.3.y:
; CHECK-NEXT: [[PHI:%[0-9]+]]:gpr32all = PHI [[COPY1]], %bb.1, [[COPY2]], %bb.2
; CHECK-NEXT: $w0 = COPY [[PHI]]
; CHECK-NEXT: RET_ReallyLR implicit $w0
entry:
%0 = callbr i32 asm "", "=r,!i"()
to label %x [label %entry.y_crit_edge]
entry.y_crit_edge: ; preds = %entry
%1 = call i32 @llvm.callbr.landingpad.i32(i32 %0)
br label %y
x: ; preds = %entry
br label %y
y: ; preds = %entry.y_crit_edge, %x
%2 = phi i32 [ %1, %entry.y_crit_edge ], [ 42, %x ]
ret i32 %2
}
define i32 @split_me1(i1 %z) {
; CHECK-LABEL: name: split_me1
; CHECK: bb.0.entry:
; CHECK-NEXT: successors: %bb.1(0x40000000), %bb.4(0x40000000)
; CHECK-NEXT: liveins: $w0
; CHECK-NEXT: {{ $}}
; CHECK-NEXT: [[COPY:%[0-9]+]]:gpr32 = COPY $w0
; CHECK-NEXT: [[DEF:%[0-9]+]]:gpr32all = IMPLICIT_DEF
; CHECK-NEXT: TBZW [[COPY]], 0, %bb.4
; CHECK-NEXT: B %bb.1
; CHECK-NEXT: {{ $}}
; CHECK-NEXT: bb.1.w:
; CHECK-NEXT: successors: %bb.3(0x80000000), %bb.2(0x00000000)
; CHECK-NEXT: {{ $}}
; CHECK-NEXT: INLINEASM_BR &"", 0 /* attdialect */, {{[0-9]+}} /* regdef:GPR32common */, def %5, 13 /* imm */, %bb.2, 13 /* imm */, %bb.2
; CHECK-NEXT: [[COPY1:%[0-9]+]]:gpr32all = COPY %5
; CHECK-NEXT: B %bb.3
; CHECK-NEXT: {{ $}}
; CHECK-NEXT: bb.2.w.v_crit_edge (inlineasm-br-indirect-target):
; CHECK-NEXT: successors: %bb.4(0x80000000)
; CHECK-NEXT: {{ $}}
; CHECK-NEXT: [[COPY2:%[0-9]+]]:gpr32all = COPY %5
; CHECK-NEXT: B %bb.4
; CHECK-NEXT: {{ $}}
; CHECK-NEXT: bb.3.x:
; CHECK-NEXT: [[MOVi32imm:%[0-9]+]]:gpr32 = MOVi32imm 42
; CHECK-NEXT: $w0 = COPY [[MOVi32imm]]
; CHECK-NEXT: RET_ReallyLR implicit $w0
; CHECK-NEXT: {{ $}}
; CHECK-NEXT: bb.4.v:
; CHECK-NEXT: [[PHI:%[0-9]+]]:gpr32all = PHI [[DEF]], %bb.0, [[COPY2]], %bb.2
; CHECK-NEXT: $w0 = COPY [[PHI]]
; CHECK-NEXT: RET_ReallyLR implicit $w0
entry:
br i1 %z, label %w, label %v
w: ; preds = %entry
%0 = callbr i32 asm "", "=r,!i,!i"()
to label %x [label %w.v_crit_edge, label %w.v_crit_edge]
w.v_crit_edge: ; preds = %w, %w
%1 = call i32 @llvm.callbr.landingpad.i32(i32 %0)
br label %v
x: ; preds = %w
ret i32 42
v: ; preds = %w.v_crit_edge, %entry
%2 = phi i32 [ %1, %w.v_crit_edge ], [ undef, %entry ]
ret i32 %2
}
define i32 @split_me2(i1 %z) {
; CHECK-LABEL: name: split_me2
; CHECK: bb.0.entry:
; CHECK-NEXT: successors: %bb.1(0x40000000), %bb.4(0x40000000)
; CHECK-NEXT: liveins: $w0
; CHECK-NEXT: {{ $}}
; CHECK-NEXT: [[COPY:%[0-9]+]]:gpr32 = COPY $w0
; CHECK-NEXT: [[MOVi32imm:%[0-9]+]]:gpr32 = MOVi32imm 42
; CHECK-NEXT: [[COPY1:%[0-9]+]]:gpr32all = COPY [[MOVi32imm]]
; CHECK-NEXT: TBZW [[COPY]], 0, %bb.4
; CHECK-NEXT: B %bb.1
; CHECK-NEXT: {{ $}}
; CHECK-NEXT: bb.1.w:
; CHECK-NEXT: successors: %bb.3(0x80000000), %bb.2(0x00000000)
; CHECK-NEXT: {{ $}}
; CHECK-NEXT: INLINEASM_BR &"", 0 /* attdialect */, {{[0-9]+}} /* regdef:GPR32common */, def %6, 13 /* imm */, %bb.2, 13 /* imm */, %bb.2
; CHECK-NEXT: [[COPY2:%[0-9]+]]:gpr32all = COPY %6
; CHECK-NEXT: B %bb.3
; CHECK-NEXT: {{ $}}
; CHECK-NEXT: bb.2.w.v_crit_edge (inlineasm-br-indirect-target):
; CHECK-NEXT: successors: %bb.4(0x80000000)
; CHECK-NEXT: {{ $}}
; CHECK-NEXT: [[COPY3:%[0-9]+]]:gpr32all = COPY %6
; CHECK-NEXT: B %bb.4
; CHECK-NEXT: {{ $}}
; CHECK-NEXT: bb.3.x:
; CHECK-NEXT: [[MOVi32imm1:%[0-9]+]]:gpr32 = MOVi32imm 42
; CHECK-NEXT: $w0 = COPY [[MOVi32imm1]]
; CHECK-NEXT: RET_ReallyLR implicit $w0
; CHECK-NEXT: {{ $}}
; CHECK-NEXT: bb.4.v:
; CHECK-NEXT: [[PHI:%[0-9]+]]:gpr32all = PHI [[COPY1]], %bb.0, [[COPY3]], %bb.2
; CHECK-NEXT: $w0 = COPY [[PHI]]
; CHECK-NEXT: RET_ReallyLR implicit $w0
entry:
br i1 %z, label %w, label %v
w: ; preds = %entry
%0 = callbr i32 asm "", "=r,!i,!i"()
to label %x [label %w.v_crit_edge, label %w.v_crit_edge]
w.v_crit_edge: ; preds = %w, %w
%1 = call i32 @llvm.callbr.landingpad.i32(i32 %0)
br label %v
x: ; preds = %w
ret i32 42
v: ; preds = %w.v_crit_edge, %entry
%2 = phi i32 [ %1, %w.v_crit_edge ], [ 42, %entry ]
ret i32 %2
}
define i32 @dont_split4() {
; CHECK-LABEL: name: dont_split4
; CHECK: bb.0.entry:
; CHECK-NEXT: successors: %bb.1(0x80000000), %bb.2(0x00000000)
; CHECK-NEXT: {{ $}}
; CHECK-NEXT: INLINEASM_BR &"", 0 /* attdialect */, {{[0-9]+}} /* regdef:GPR32common */, def %3, 13 /* imm */, %bb.2
; CHECK-NEXT: [[COPY:%[0-9]+]]:gpr32all = COPY %3
; CHECK-NEXT: B %bb.1
; CHECK-NEXT: {{ $}}
; CHECK-NEXT: bb.1.x:
; CHECK-NEXT: successors: %bb.3(0x80000000)
; CHECK-NEXT: {{ $}}
; CHECK-NEXT: B %bb.3
; CHECK-NEXT: {{ $}}
; CHECK-NEXT: bb.2.y (inlineasm-br-indirect-target):
; CHECK-NEXT: successors: %bb.3(0x80000000)
; CHECK-NEXT: {{ $}}
; CHECK-NEXT: [[COPY1:%[0-9]+]]:gpr32all = COPY %3
; CHECK-NEXT: {{ $}}
; CHECK-NEXT: bb.3.out:
; CHECK-NEXT: [[PHI:%[0-9]+]]:gpr32all = PHI [[COPY1]], %bb.2, [[COPY]], %bb.1
; CHECK-NEXT: $w0 = COPY [[PHI]]
; CHECK-NEXT: RET_ReallyLR implicit $w0
entry:
%0 = callbr i32 asm "", "=r,!i"()
to label %x [label %y]
x: ; preds = %entry
br label %out
y: ; preds = %entry
%1 = call i32 @llvm.callbr.landingpad.i32(i32 %0)
br label %out
out: ; preds = %y, %x
%2 = phi i32 [ %1, %y ], [ %0, %x ]
ret i32 %2
}
define i32 @dont_split5() {
; CHECK-LABEL: name: dont_split5
; CHECK: bb.0.entry:
; CHECK-NEXT: successors: %bb.2(0x80000000), %bb.1(0x00000000)
; CHECK-NEXT: {{ $}}
; CHECK-NEXT: INLINEASM_BR &"", 0 /* attdialect */, {{[0-9]+}} /* regdef:GPR32common */, def %3, 13 /* imm */, %bb.1
; CHECK-NEXT: [[COPY:%[0-9]+]]:gpr32all = COPY %3
; CHECK-NEXT: B %bb.2
; CHECK-NEXT: {{ $}}
; CHECK-NEXT: bb.1.y (inlineasm-br-indirect-target):
; CHECK-NEXT: successors: %bb.2(0x80000000)
; CHECK-NEXT: {{ $}}
; CHECK-NEXT: [[COPY1:%[0-9]+]]:gpr32all = COPY %3
; CHECK-NEXT: {{ $}}
; CHECK-NEXT: bb.2.out:
; CHECK-NEXT: [[PHI:%[0-9]+]]:gpr32all = PHI [[COPY]], %bb.0, [[COPY1]], %bb.1
; CHECK-NEXT: $w0 = COPY [[PHI]]
; CHECK-NEXT: RET_ReallyLR implicit $w0
entry:
%0 = callbr i32 asm "", "=r,!i"()
to label %out [label %y]
y: ; preds = %entry
%1 = call i32 @llvm.callbr.landingpad.i32(i32 %0)
br label %out
out: ; preds = %y, %entry
%2 = phi i32 [ %1, %y ], [ %0, %entry ]
ret i32 %2
}
define i32 @split_me3() {
; CHECK-LABEL: name: split_me3
; CHECK: bb.0.entry:
; CHECK-NEXT: successors: %bb.2(0x80000000), %bb.1(0x00000000)
; CHECK-NEXT: {{ $}}
; CHECK-NEXT: INLINEASM_BR &"", 0 /* attdialect */, {{[0-9]+}} /* regdef:GPR32common */, def %3, 13 /* imm */, %bb.1
; CHECK-NEXT: [[COPY:%[0-9]+]]:gpr32all = COPY %3
; CHECK-NEXT: B %bb.2
; CHECK-NEXT: {{ $}}
; CHECK-NEXT: bb.1.entry.out_crit_edge (inlineasm-br-indirect-target):
; CHECK-NEXT: successors: %bb.3(0x80000000)
; CHECK-NEXT: {{ $}}
; CHECK-NEXT: [[COPY1:%[0-9]+]]:gpr32all = COPY %3
; CHECK-NEXT: B %bb.3
; CHECK-NEXT: {{ $}}
; CHECK-NEXT: bb.2.y:
; CHECK-NEXT: successors: %bb.3(0x80000000)
; CHECK-NEXT: {{ $}}
; CHECK-NEXT: bb.3.out:
; CHECK-NEXT: [[PHI:%[0-9]+]]:gpr32all = PHI [[COPY1]], %bb.1, [[COPY]], %bb.2
; CHECK-NEXT: $w0 = COPY [[PHI]]
; CHECK-NEXT: RET_ReallyLR implicit $w0
entry:
%0 = callbr i32 asm "", "=r,!i"()
to label %y [label %entry.out_crit_edge]
entry.out_crit_edge: ; preds = %entry
%1 = call i32 @llvm.callbr.landingpad.i32(i32 %0)
br label %out
y: ; preds = %entry
br label %out
out: ; preds = %entry.out_crit_edge, %y
%2 = phi i32 [ %1, %entry.out_crit_edge ], [ %0, %y ]
ret i32 %2
}
define i32 @dont_split6(i32 %0) {
; CHECK-LABEL: name: dont_split6
; CHECK: bb.0.entry:
; CHECK-NEXT: successors: %bb.1(0x80000000)
; CHECK-NEXT: liveins: $w0
; CHECK-NEXT: {{ $}}
; CHECK-NEXT: [[COPY:%[0-9]+]]:gpr32 = COPY $w0
; CHECK-NEXT: {{ $}}
; CHECK-NEXT: bb.1.loop:
; CHECK-NEXT: successors: %bb.3(0x80000000), %bb.2(0x00000000)
; CHECK-NEXT: {{ $}}
; CHECK-NEXT: [[PHI:%[0-9]+]]:gpr32all = PHI [[COPY]], %bb.0, %2, %bb.2
; CHECK-NEXT: [[COPY1:%[0-9]+]]:gpr32common = COPY [[PHI]]
; CHECK-NEXT: INLINEASM_BR &"", 0 /* attdialect */, {{[0-9]+}} /* regdef:GPR32common */, def %4, 2147483657 /* reguse tiedto:$0 */, [[COPY1]](tied-def 3), 13 /* imm */, %bb.2
; CHECK-NEXT: [[COPY2:%[0-9]+]]:gpr32all = COPY %4
; CHECK-NEXT: B %bb.3
; CHECK-NEXT: {{ $}}
; CHECK-NEXT: bb.2.loop.loop_crit_edge (inlineasm-br-indirect-target):
; CHECK-NEXT: successors: %bb.1(0x80000000)
; CHECK-NEXT: {{ $}}
; CHECK-NEXT: [[COPY3:%[0-9]+]]:gpr32all = COPY %4
; CHECK-NEXT: B %bb.1
; CHECK-NEXT: {{ $}}
; CHECK-NEXT: bb.3.exit:
; CHECK-NEXT: [[COPY4:%[0-9]+]]:gpr32all = COPY $wzr
; CHECK-NEXT: $w0 = COPY [[COPY4]]
; CHECK-NEXT: RET_ReallyLR implicit $w0
entry:
br label %loop
loop: ; preds = %loop.loop_crit_edge, %entry
%1 = phi i32 [ %0, %entry ], [ %3, %loop.loop_crit_edge ]
%2 = callbr i32 asm "", "=r,0,!i"(i32 %1)
to label %exit [label %loop.loop_crit_edge]
loop.loop_crit_edge: ; preds = %loop
%3 = call i32 @llvm.callbr.landingpad.i32(i32 %2)
br label %loop
exit: ; preds = %loop
ret i32 0
}
define i32 @split_me4() {
; CHECK-LABEL: name: split_me4
; CHECK: bb.0.entry:
; CHECK-NEXT: successors: %bb.2(0x80000000), %bb.1(0x00000000)
; CHECK-NEXT: {{ $}}
; CHECK-NEXT: INLINEASM_BR &"", 0 /* attdialect */, {{[0-9]+}} /* regdef:GPR32common */, def %3, 13 /* imm */, %bb.1
; CHECK-NEXT: [[COPY:%[0-9]+]]:gpr32all = COPY %3
; CHECK-NEXT: B %bb.2
; CHECK-NEXT: {{ $}}
; CHECK-NEXT: bb.1.entry.same_crit_edge (inlineasm-br-indirect-target):
; CHECK-NEXT: successors: %bb.2(0x80000000)
; CHECK-NEXT: {{ $}}
; CHECK-NEXT: [[COPY1:%[0-9]+]]:gpr32all = COPY %3
; CHECK-NEXT: {{ $}}
; CHECK-NEXT: bb.2.same:
; CHECK-NEXT: [[PHI:%[0-9]+]]:gpr32all = PHI [[COPY]], %bb.0, [[COPY1]], %bb.1
; CHECK-NEXT: $w0 = COPY [[PHI]]
; CHECK-NEXT: RET_ReallyLR implicit $w0
entry:
%0 = callbr i32 asm "", "=r,!i"()
to label %same [label %entry.same_crit_edge]
entry.same_crit_edge: ; preds = %entry
%1 = call i32 @llvm.callbr.landingpad.i32(i32 %0)
br label %same
same: ; preds = %entry.same_crit_edge, %entry
%2 = phi i32 [ %1, %entry.same_crit_edge ], [ %0, %entry ]
ret i32 %2
}
define i32 @split_me5() {
; CHECK-LABEL: name: split_me5
; CHECK: bb.0.entry:
; CHECK-NEXT: successors: %bb.2(0x80000000), %bb.1(0x00000000)
; CHECK-NEXT: {{ $}}
; CHECK-NEXT: INLINEASM_BR &"", 0 /* attdialect */, {{[0-9]+}} /* regdef:GPR32common */, def %3, 13 /* imm */, %bb.1
; CHECK-NEXT: [[COPY:%[0-9]+]]:gpr32all = COPY %3
; CHECK-NEXT: B %bb.2
; CHECK-NEXT: {{ $}}
; CHECK-NEXT: bb.1.entry.same_crit_edge (inlineasm-br-indirect-target):
; CHECK-NEXT: successors: %bb.2(0x80000000)
; CHECK-NEXT: {{ $}}
; CHECK-NEXT: [[COPY1:%[0-9]+]]:gpr32all = COPY %3
; CHECK-NEXT: {{ $}}
; CHECK-NEXT: bb.2.same:
; CHECK-NEXT: [[PHI:%[0-9]+]]:gpr32all = PHI [[COPY]], %bb.0, [[COPY1]], %bb.1
; CHECK-NEXT: $w0 = COPY [[PHI]]
; CHECK-NEXT: RET_ReallyLR implicit $w0
entry:
%0 = callbr i32 asm "", "=r,!i"()
to label %same [label %entry.same_crit_edge]
entry.same_crit_edge: ; preds = %entry
%1 = call i32 @llvm.callbr.landingpad.i32(i32 %0)
br label %same
same: ; preds = %entry.same_crit_edge, %entry
%2 = phi i32 [ %1, %entry.same_crit_edge ], [ %0, %entry ]
ret i32 %2
}
; Function Attrs: nounwind
declare i32 @llvm.callbr.landingpad.i32(i32) #0
; Function Attrs: nounwind
declare i64 @llvm.callbr.landingpad.i64(i64) #0
attributes #0 = { nounwind }