# RUN: llc -verify-machineinstrs -mtriple=aarch64-none-linux-gnu \
# RUN: -start-before aarch64-speculation-hardening -o - %s \
# RUN: | FileCheck %s
# Check that the speculation hardening pass generates code as expected for
# basic blocks ending with a variety of branch patterns:
# - (1) no branches (fallthrough)
# - (2) one unconditional branch
# - (3) one conditional branch + fall-through
# - (4) one conditional branch + one unconditional branch
# - other direct branches don't seem to be generated by the AArch64 codegen
--- |
define void @nobranch_fallthrough(i32 %a, i32 %b) speculative_load_hardening {
ret void
}
define void @uncondbranch(i32 %a, i32 %b) speculative_load_hardening {
ret void
}
define void @condbranch_fallthrough(i32 %a, i32 %b) speculative_load_hardening {
ret void
}
define void @condbranch_uncondbranch(i32 %a, i32 %b) speculative_load_hardening {
ret void
}
define void @indirectbranch(i32 %a, i32 %b) speculative_load_hardening {
ret void
}
; Also check that a non-default temporary register gets picked correctly to
; transfer the SP to to and it with the taint register when the default
; temporary isn't available.
define void @indirect_call_x17(i32 %a, i32 %b) speculative_load_hardening {
ret void
}
@g = common dso_local local_unnamed_addr global ptr null, align 8
define void @indirect_tailcall_x17(i32 %a, i32 %b) speculative_load_hardening {
ret void
}
define void @indirect_call_lr(i32 %a, i32 %b) speculative_load_hardening {
ret void
}
define void @RS_cannot_find_available_regs() speculative_load_hardening {
ret void
}
...
---
name: nobranch_fallthrough
tracksRegLiveness: true
body: |
; CHECK-LABEL: nobranch_fallthrough
bb.0:
successors: %bb.1
liveins: $w0, $w1
; CHECK-NOT: csel
bb.1:
liveins: $w0
RET undef $lr, implicit $w0
...
---
name: uncondbranch
tracksRegLiveness: true
body: |
; CHECK-LABEL: uncondbranch
bb.0:
successors: %bb.1
liveins: $w0, $w1
B %bb.1
; CHECK-NOT: csel
bb.1:
liveins: $w0
RET undef $lr, implicit $w0
...
---
name: condbranch_fallthrough
tracksRegLiveness: true
body: |
; CHECK-LABEL: condbranch_fallthrough
bb.0:
successors: %bb.1, %bb.2
liveins: $w0, $w1
$wzr = SUBSWrs renamable $w0, renamable $w1, 0, implicit-def $nzcv, implicit-def $nzcv
Bcc 11, %bb.2, implicit $nzcv
; CHECK: b.lt [[BB_LT_T:\.LBB[0-9_]+]]
bb.1:
liveins: $nzcv, $w0
; CHECK: csel x16, x16, xzr, ge
RET undef $lr, implicit $w0
bb.2:
liveins: $nzcv, $w0
; CHECK: csel x16, x16, xzr, lt
RET undef $lr, implicit $w0
...
---
name: condbranch_uncondbranch
tracksRegLiveness: true
body: |
; CHECK-LABEL: condbranch_uncondbranch
bb.0:
successors: %bb.1, %bb.2
liveins: $w0, $w1
$wzr = SUBSWrs renamable $w0, renamable $w1, 0, implicit-def $nzcv, implicit-def $nzcv
Bcc 11, %bb.2, implicit $nzcv
B %bb.1, implicit $nzcv
; CHECK: b.lt [[BB_LT_T:\.LBB[0-9_]+]]
bb.1:
liveins: $nzcv, $w0
; CHECK: csel x16, x16, xzr, ge
RET undef $lr, implicit $w0
bb.2:
liveins: $nzcv, $w0
; CHECK: csel x16, x16, xzr, lt
RET undef $lr, implicit $w0
...
---
name: indirectbranch
tracksRegLiveness: true
body: |
; Check that no instrumentation is done on indirect branches (for now).
; CHECK-LABEL: indirectbranch
bb.0:
successors: %bb.1, %bb.2
liveins: $x0
BR $x0
bb.1:
liveins: $x0
; CHECK-NOT: csel
RET undef $lr, implicit $x0
bb.2:
liveins: $x0
; CHECK-NOT: csel
RET undef $lr, implicit $x0
...
---
name: indirect_call_x17
tracksRegLiveness: true
body: |
bb.0:
liveins: $x17
; CHECK-LABEL: indirect_call_x17
; CHECK: mov x0, sp
; CHECK: and x0, x0, x16
; CHECK: mov sp, x0
; CHECK: blr x17
BLR killed renamable $x17, implicit-def dead $lr, implicit $sp
RET undef $lr, implicit undef $w0
...
---
name: indirect_tailcall_x17
tracksRegLiveness: true
body: |
bb.0:
liveins: $x0
; CHECK-LABEL: indirect_tailcall_x17
; CHECK: mov x1, sp
; CHECK: and x1, x1, x16
; CHECK: mov sp, x1
; CHECK: br x17
$x8 = ADRP target-flags(aarch64-page) @g
$x17 = LDRXui killed $x8, target-flags(aarch64-pageoff, aarch64-nc) @g
TCRETURNri killed $x17, 0, implicit $sp, implicit $x0
...
---
name: indirect_call_lr
tracksRegLiveness: true
body: |
bb.0:
; CHECK-LABEL: indirect_call_lr
; CHECK: mov x1, sp
; CHECK: and x1, x1, x16
; CHECK-NEXT: mov sp, x1
; CHECK-NEXT: blr x30
liveins: $x0, $lr
BLR killed renamable $lr, implicit-def dead $lr, implicit $sp, implicit-def $sp, implicit-def $w0, implicit $x0
$w0 = nsw ADDWri killed $w0, 1, 0
RET undef $lr, implicit $w0
...
---
name: RS_cannot_find_available_regs
tracksRegLiveness: true
body: |
bb.0:
; In the rare case when no free temporary register is available for the
; propagate taint-to-sp operation, just put in a full speculation barrier
; (isb+dsb sy) at the start of the basic block. And don't put masks on
; instructions for the rest of the basic block, since speculation in that
; basic block was already done, so no need to do masking.
; CHECK-LABEL: RS_cannot_find_available_regs
; CHECK: dsb sy
; CHECK-NEXT: isb
; CHECK-NEXT: ldr x0, [x0]
; The following 2 instructions come from propagating the taint encoded in
; sp at function entry to x16. It turns out the taint info in x16 is not
; used in this function, so those instructions could be optimized away. An
; optimization for later if it turns out this situation occurs often enough.
; CHECK-NEXT: cmp sp, #0
; CHECK-NEXT: csetm x16, ne
; CHECK-NEXT: ret
liveins: $x0, $x1, $x2, $x3, $x4, $x5, $x6, $x7, $x8, $x9, $x10, $x11, $x12, $x13, $x14, $x15, $x17, $x18, $x19, $x20, $x21, $x22, $x23, $x24, $x25, $x26, $x27, $x28, $fp, $lr
$x0 = LDRXui killed $x0, 0
RET $lr, implicit $x0, implicit $x1, implicit $x2, implicit $x3, implicit $x4, implicit $x5, implicit $x6, implicit $x7, implicit $x8, implicit $x9, implicit $x10, implicit $x11, implicit $x12, implicit $x13, implicit $x14, implicit $x15, implicit $x17, implicit $x18, implicit $x19, implicit $x20, implicit $x21, implicit $x22, implicit $x23, implicit $x24, implicit $x25, implicit $x26, implicit $x27, implicit $x28
...