
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.
108 lines
3.5 KiB
LLVM
108 lines
3.5 KiB
LLVM
; NOTE: Assertions have been autogenerated by utils/update_llc_test_checks.py
|
|
; RUN: llc < %s -mtriple=x86_64-unknown-linux-gnu | FileCheck %s
|
|
|
|
; This test asserted in MachineBlockPlacement during asm-goto bring up.
|
|
|
|
%struct.wibble = type { %struct.pluto, i32, ptr }
|
|
%struct.pluto = type { i32, i32, i32 }
|
|
|
|
@global = external dso_local global [0 x %struct.wibble]
|
|
|
|
define i32 @foo(i32 %arg, ptr %arg3) nounwind {
|
|
; CHECK-LABEL: foo:
|
|
; CHECK: # %bb.0: # %bb
|
|
; CHECK-NEXT: pushq %rbp
|
|
; CHECK-NEXT: pushq %r15
|
|
; CHECK-NEXT: pushq %r14
|
|
; CHECK-NEXT: pushq %r13
|
|
; CHECK-NEXT: pushq %r12
|
|
; CHECK-NEXT: pushq %rbx
|
|
; CHECK-NEXT: pushq %rax
|
|
; CHECK-NEXT: movabsq $-2305847407260205056, %r14 # imm = 0xDFFFFC0000000000
|
|
; CHECK-NEXT: xorl %eax, %eax
|
|
; CHECK-NEXT: testb %al, %al
|
|
; CHECK-NEXT: jne .LBB0_5
|
|
; CHECK-NEXT: # %bb.1: # %bb5
|
|
; CHECK-NEXT: movq %rsi, %rbx
|
|
; CHECK-NEXT: movslq %edi, %rbp
|
|
; CHECK-NEXT: leaq (,%rbp,8), %rax
|
|
; CHECK-NEXT: leaq global(%rax,%rax,2), %r14
|
|
; CHECK-NEXT: leaq global+4(%rax,%rax,2), %r15
|
|
; CHECK-NEXT: xorl %r13d, %r13d
|
|
; CHECK-NEXT: .p2align 4
|
|
; CHECK-NEXT: .LBB0_2: # %bb8
|
|
; CHECK-NEXT: # =>This Inner Loop Header: Depth=1
|
|
; CHECK-NEXT: callq bar@PLT
|
|
; CHECK-NEXT: movq %rax, %r12
|
|
; CHECK-NEXT: movq %rax, %rdi
|
|
; CHECK-NEXT: callq *%rbx
|
|
; CHECK-NEXT: movq %r14, %rdi
|
|
; CHECK-NEXT: callq hoge@PLT
|
|
; CHECK-NEXT: movq %r15, %rdi
|
|
; CHECK-NEXT: callq hoge@PLT
|
|
; CHECK-NEXT: testb %r13b, %r13b
|
|
; CHECK-NEXT: jne .LBB0_2
|
|
; CHECK-NEXT: # %bb.3: # %bb15
|
|
; CHECK-NEXT: leaq (%rbp,%rbp,2), %rax
|
|
; CHECK-NEXT: movq %r12, global+16(,%rax,8)
|
|
; CHECK-NEXT: movabsq $-2305847407260205056, %r14 # imm = 0xDFFFFC0000000000
|
|
; CHECK-NEXT: #APP
|
|
; CHECK-NEXT: #NO_APP
|
|
; CHECK-NEXT: # %bb.4: # %bb17
|
|
; CHECK-NEXT: callq widget@PLT
|
|
; CHECK-NEXT: .LBB0_5: # Inline asm indirect target
|
|
; CHECK-NEXT: # %bb18
|
|
; CHECK-NEXT: # Label of block must be emitted
|
|
; CHECK-NEXT: movw $0, 14(%r14)
|
|
; CHECK-NEXT: addq $8, %rsp
|
|
; CHECK-NEXT: popq %rbx
|
|
; CHECK-NEXT: popq %r12
|
|
; CHECK-NEXT: popq %r13
|
|
; CHECK-NEXT: popq %r14
|
|
; CHECK-NEXT: popq %r15
|
|
; CHECK-NEXT: popq %rbp
|
|
; CHECK-NEXT: retq
|
|
bb:
|
|
%tmp = add i64 0, -2305847407260205056
|
|
%tmp4 = sext i32 %arg to i64
|
|
br i1 undef, label %bb18, label %bb5
|
|
|
|
bb5: ; preds = %bb
|
|
%tmp6 = getelementptr [0 x %struct.wibble], ptr @global, i64 0, i64 %tmp4, i32 0, i32 0
|
|
%tmp7 = getelementptr [0 x %struct.wibble], ptr @global, i64 0, i64 %tmp4, i32 0, i32 1
|
|
br label %bb8
|
|
|
|
bb8: ; preds = %bb8, %bb5
|
|
%tmp9 = call ptr @bar(i64 undef)
|
|
%tmp10 = call i32 %arg3(ptr nonnull %tmp9)
|
|
%tmp11 = ptrtoint ptr %tmp6 to i64
|
|
call void @hoge(i64 %tmp11)
|
|
%tmp12 = ptrtoint ptr %tmp7 to i64
|
|
%tmp13 = add i64 undef, -2305847407260205056
|
|
call void @hoge(i64 %tmp12)
|
|
%tmp14 = icmp eq i32 0, 0
|
|
br i1 %tmp14, label %bb15, label %bb8
|
|
|
|
bb15: ; preds = %bb8
|
|
%tmp16 = getelementptr [0 x %struct.wibble], ptr @global, i64 0, i64 %tmp4, i32 2
|
|
store ptr %tmp9, ptr %tmp16
|
|
callbr void asm sideeffect "", "!i"()
|
|
to label %bb17 [label %bb18]
|
|
|
|
bb17: ; preds = %bb15
|
|
call void @widget()
|
|
br label %bb18
|
|
|
|
bb18: ; preds = %bb17, %bb15, %bb
|
|
%tmp19 = add i64 %tmp, 14
|
|
%tmp20 = inttoptr i64 %tmp19 to ptr
|
|
store i16 0, ptr %tmp20
|
|
ret i32 undef
|
|
}
|
|
|
|
declare ptr @bar(i64)
|
|
|
|
declare void @widget()
|
|
|
|
declare void @hoge(i64)
|