| # RUN: llc -run-pass=aarch64-branch-targets %s -o - | FileCheck %s |
| --- | |
| target datalayout = "e-m:e-i8:8:32-i16:16:32-i64:64-i128:128-n32:64-S128" |
| target triple = "aarch64-arm-none-eabi" |
| |
| define hidden i32 @simple_external() "branch-target-enforcement" { |
| entry: |
| ret i32 0 |
| } |
| |
| define internal i32 @simple_internal() "branch-target-enforcement" { |
| entry: |
| ret i32 0 |
| } |
| |
| define hidden i32 @ptr_auth() "branch-target-enforcement" { |
| entry: |
| tail call void asm sideeffect "", "~{lr}"() |
| ret i32 0 |
| } |
| |
| define hidden i32 @ptr_auth_b() "branch-target-enforcement" { |
| entry: |
| tail call void asm sideeffect "", "~{lr}"() |
| ret i32 0 |
| } |
| |
| define hidden i32 @jump_table(i32 %a) "branch-target-enforcement" { |
| entry: |
| switch i32 %a, label %sw.epilog [ |
| i32 1, label %sw.bb |
| i32 2, label %sw.bb1 |
| i32 3, label %sw.bb2 |
| i32 4, label %sw.bb3 |
| i32 5, label %sw.bb4 |
| ] |
| |
| sw.bb: ; preds = %entry |
| tail call void asm sideeffect "", ""() |
| br label %sw.epilog |
| |
| sw.bb1: ; preds = %entry |
| tail call void asm sideeffect "", ""() |
| br label %sw.epilog |
| |
| sw.bb2: ; preds = %entry |
| tail call void asm sideeffect "", ""() |
| br label %sw.epilog |
| |
| sw.bb3: ; preds = %entry |
| tail call void asm sideeffect "", ""() |
| br label %sw.epilog |
| |
| sw.bb4: ; preds = %entry |
| tail call void asm sideeffect "", ""() |
| br label %sw.epilog |
| |
| sw.epilog: ; preds = %entry, %sw.bb4, %sw.bb3, %sw.bb2, %sw.bb1, %sw.bb |
| ret i32 0 |
| } |
| |
| @label_address.addr = internal unnamed_addr global i8* blockaddress(@label_address, %return), align 8 |
| |
| define hidden i32 @label_address() "branch-target-enforcement" { |
| entry: |
| %0 = load i8*, i8** @label_address.addr, align 8 |
| indirectbr i8* %0, [label %return, label %lab2] |
| |
| lab2: ; preds = %entry |
| br label %.split |
| |
| return: ; preds = %entry |
| br label %.split |
| |
| .split: ; preds = %lab2, %return |
| %merge = phi i8* [ blockaddress(@label_address, %lab2), %return ], [ blockaddress(@label_address, %return), %lab2 ] |
| %merge2 = phi i32 [ 1, %return ], [ 2, %lab2 ] |
| store i8* %merge, i8** @label_address.addr, align 8 |
| ret i32 %merge2 |
| } |
| |
| define hidden i32 @label_address_entry() "branch-target-enforcement" { |
| entry: |
| %0 = load i8*, i8** @label_address.addr, align 8 |
| indirectbr i8* %0, [label %return, label %lab2] |
| |
| lab2: ; preds = %entry |
| br label %.split |
| |
| return: ; preds = %entry |
| br label %.split |
| |
| .split: ; preds = %lab2, %return |
| %merge = phi i8* [ blockaddress(@label_address, %lab2), %return ], [ blockaddress(@label_address, %return), %lab2 ] |
| %merge2 = phi i32 [ 1, %return ], [ 2, %lab2 ] |
| store i8* %merge, i8** @label_address.addr, align 8 |
| ret i32 %merge2 |
| } |
| |
| ... |
| --- |
| # External function, could be addres-taken elsewhere so needs BTI JC. |
| name: simple_external |
| body: | |
| bb.0.entry: |
| ; CHECK-LABEL: name: simple_external |
| ; CHECK: HINT 34 |
| ; CHECK: RET |
| $w0 = ORRWrs $wzr, $wzr, 0 |
| RET undef $lr, implicit killed $w0 |
| |
| --- |
| # Internal function, not address-taken in this module, so no BTI needed. |
| name: simple_internal |
| body: | |
| bb.0.entry: |
| ; CHECK-LABEL: name: simple_internal |
| ; CHECK-NOT: HINT |
| ; CHECK: RET |
| $w0 = ORRWrs $wzr, $wzr, 0 |
| RET undef $lr, implicit killed $w0 |
| |
| --- |
| # Function starts with PACIASP, which implicitly acts as BTI JC, so no change |
| # needed. |
| name: ptr_auth |
| stack: |
| - { id: 0, name: '', type: spill-slot, offset: -16, size: 8, alignment: 16, |
| stack-id: default, callee-saved-register: '$lr', callee-saved-restored: true, |
| debug-info-variable: '', debug-info-expression: '', debug-info-location: '' } |
| body: | |
| bb.0.entry: |
| liveins: $lr |
| |
| ; CHECK-LABEL: name: ptr_auth |
| ; CHECK-NOT: HINT |
| ; CHECK: frame-setup PACIASP |
| ; CHECK-NOT: HINT |
| ; CHECK: RETAA |
| frame-setup PACIASP implicit-def $lr, implicit killed $lr, implicit $sp |
| early-clobber $sp = frame-setup STRXpre killed $lr, $sp, -16 :: (store 8 into %stack.0) |
| INLINEASM &"", 1, 12, implicit-def dead early-clobber $lr |
| $w0 = ORRWrs $wzr, $wzr, 0 |
| early-clobber $sp, $lr = frame-destroy LDRXpost $sp, 16 :: (load 8 from %stack.0) |
| RETAA implicit killed $w0 |
| |
| --- |
| # Function starts with PACIBSP, which implicitly acts as BTI JC, so no change |
| # needed. |
| name: ptr_auth_b |
| stack: |
| - { id: 0, name: '', type: spill-slot, offset: -16, size: 8, alignment: 16, |
| stack-id: default, callee-saved-register: '$lr', callee-saved-restored: true, |
| debug-info-variable: '', debug-info-expression: '', debug-info-location: '' } |
| body: | |
| bb.0.entry: |
| liveins: $lr |
| |
| ; CHECK-LABEL: name: ptr_auth_b |
| ; CHECK-NOT: HINT |
| ; CHECK: frame-setup PACIBSP |
| ; CHECK-NOT: HINT |
| ; CHECK: RETAB |
| frame-setup PACIBSP implicit-def $lr, implicit killed $lr, implicit $sp |
| early-clobber $sp = frame-setup STRXpre killed $lr, $sp, -16 :: (store 8 into %stack.0) |
| INLINEASM &"", 1, 12, implicit-def dead early-clobber $lr |
| $w0 = ORRWrs $wzr, $wzr, 0 |
| early-clobber $sp, $lr = frame-destroy LDRXpost $sp, 16 :: (load 8 from %stack.0) |
| RETAB implicit killed $w0 |
| |
| --- |
| # Function contains a jump table, so every target of the jump table must start |
| # with BTI J. |
| name: jump_table |
| jumpTable: |
| kind: block-address |
| entries: |
| - id: 0 |
| blocks: [ '%bb.2', '%bb.3', '%bb.4', '%bb.5', '%bb.6' ] |
| body: | |
| bb.0.entry: |
| ; CHECK-LABEL: name: jump_table |
| ; CHECK: HINT 34 |
| successors: %bb.7(0x15555555), %bb.1(0x6aaaaaab) |
| liveins: $w0 |
| |
| renamable $w8 = SUBWri killed renamable $w0, 1, 0, implicit-def $x8 |
| dead $wzr = SUBSWri renamable $w8, 4, 0, implicit-def $nzcv |
| Bcc 8, %bb.7, implicit $nzcv |
| |
| bb.1.entry: |
| ; CHECK: bb.1.entry: |
| ; CHECK-NOT: HINT |
| ; CHECK: BR killed renamable $x8 |
| successors: %bb.2(0x1999999a), %bb.3(0x1999999a), %bb.4(0x1999999a), %bb.5(0x1999999a), %bb.6(0x1999999a) |
| liveins: $x8 |
| |
| $x9 = ADRP target-flags(aarch64-page) %jump-table.0 |
| renamable $x9 = ADDXri killed $x9, target-flags(aarch64-pageoff, aarch64-nc) %jump-table.0, 0 |
| renamable $x8 = LDRXroX killed renamable $x9, killed renamable $x8, 0, 1 :: (load 8 from jump-table) |
| BR killed renamable $x8 |
| |
| bb.2.sw.bb: |
| ; CHECK: bb.2.sw.bb |
| ; CHECK-NEXT: HINT 36 |
| $w0 = ORRWrs $wzr, $wzr, 0 |
| INLINEASM &"", 1 |
| RET undef $lr, implicit killed $w0 |
| |
| bb.3.sw.bb1: |
| ; CHECK: bb.3.sw.bb1 |
| ; CHECK-NEXT: HINT 36 |
| $w0 = ORRWrs $wzr, $wzr, 0 |
| INLINEASM &"", 1 |
| RET undef $lr, implicit killed $w0 |
| |
| bb.4.sw.bb2: |
| ; CHECK: bb.4.sw.bb2 |
| ; CHECK-NEXT: HINT 36 |
| $w0 = ORRWrs $wzr, $wzr, 0 |
| INLINEASM &"", 1 |
| RET undef $lr, implicit killed $w0 |
| |
| bb.5.sw.bb3: |
| ; CHECK: bb.5.sw.bb3 |
| ; CHECK-NEXT: HINT 36 |
| $w0 = ORRWrs $wzr, $wzr, 0 |
| INLINEASM &"", 1 |
| RET undef $lr, implicit killed $w0 |
| |
| bb.6.sw.bb4: |
| ; CHECK: bb.6.sw.bb4 |
| ; CHECK-NEXT: successors: %bb.7(0x80000000) |
| ; CHECK-NEXT: {{ }} |
| ; CHECK-NEXT: HINT 36 |
| successors: %bb.7(0x80000000) |
| |
| INLINEASM &"", 1 |
| |
| bb.7.sw.epilog: |
| ; CHECK: bb.7.sw.epilog: |
| ; CHECK-NOT: HINT |
| ; CHECK: RET |
| $w0 = ORRWrs $wzr, $wzr, 0 |
| RET undef $lr, implicit killed $w0 |
| |
| --- |
| # Function takes address of basic blocks, so they must start with BTI J. |
| name: label_address |
| body: | |
| bb.0.entry: |
| ; CHECK-LABEL: label_address |
| ; CHECK: bb.0.entry: |
| ; CHECK-NEXT: successors: %bb.1(0x40000000), %bb.2(0x40000000) |
| ; CHECK-NEXT: {{ }} |
| ; CHECK-NEXT: HINT 34 |
| ; CHECK: BR killed renamable $x9 |
| successors: %bb.1(0x40000000), %bb.2(0x40000000) |
| |
| renamable $x8 = ADRP target-flags(aarch64-page) @label_address.addr |
| renamable $x9 = LDRXui renamable $x8, target-flags(aarch64-pageoff, aarch64-nc) @label_address.addr :: (dereferenceable load 8 from @label_address.addr) |
| BR killed renamable $x9 |
| |
| bb.1.return (address-taken): |
| ; CHECK: bb.1.return (address-taken): |
| ; CHECK-NEXT: HINT 36 |
| liveins: $x8 |
| |
| $x9 = ADRP target-flags(aarch64-page) blockaddress(@label_address, %ir-block.lab2) |
| renamable $w0 = ORRWri $wzr, 0 |
| renamable $x9 = ADDXri killed $x9, target-flags(aarch64-pageoff, aarch64-nc) blockaddress(@label_address, %ir-block.lab2), 0 |
| STRXui killed renamable $x9, killed renamable $x8, target-flags(aarch64-pageoff, aarch64-nc) @label_address.addr :: (store 8 into @label_address.addr) |
| RET undef $lr, implicit killed $w0 |
| |
| bb.2.lab2 (address-taken): |
| ; CHECK: bb.2.lab2 (address-taken): |
| ; CHECK-NEXT: HINT 36 |
| liveins: $x8 |
| |
| $x9 = ADRP target-flags(aarch64-page) blockaddress(@label_address, %ir-block.return) |
| renamable $w0 = ORRWri $wzr, 1984 |
| renamable $x9 = ADDXri killed $x9, target-flags(aarch64-pageoff, aarch64-nc) blockaddress(@label_address, %ir-block.return), 0 |
| STRXui killed renamable $x9, killed renamable $x8, target-flags(aarch64-pageoff, aarch64-nc) @label_address.addr :: (store 8 into @label_address.addr) |
| RET undef $lr, implicit killed $w0 |
| |
| --- |
| # Function takes address of the entry block, so the entry block needs a BTI JC. |
| name: label_address_entry |
| body: | |
| bb.0.entry (address-taken): |
| ; CHECK-LABEL: label_address_entry |
| ; CHECK: bb.0.entry (address-taken): |
| ; CHECK-NEXT: successors: %bb.1(0x40000000), %bb.2(0x40000000) |
| ; CHECK-NEXT: {{ }} |
| ; CHECK-NEXT: HINT 38 |
| ; CHECK: BR killed renamable $x9 |
| successors: %bb.1(0x40000000), %bb.2(0x40000000) |
| |
| renamable $x8 = ADRP target-flags(aarch64-page) @label_address.addr |
| renamable $x9 = LDRXui renamable $x8, target-flags(aarch64-pageoff, aarch64-nc) @label_address.addr :: (dereferenceable load 8 from @label_address.addr) |
| BR killed renamable $x9 |
| |
| bb.1.return (address-taken): |
| ; CHECK: bb.1.return (address-taken): |
| ; CHECK-NEXT: HINT 36 |
| liveins: $x8 |
| |
| $x9 = ADRP target-flags(aarch64-page) blockaddress(@label_address, %ir-block.entry) |
| renamable $w0 = ORRWri $wzr, 0 |
| renamable $x9 = ADDXri killed $x9, target-flags(aarch64-pageoff, aarch64-nc) blockaddress(@label_address, %ir-block.entry), 0 |
| STRXui killed renamable $x9, killed renamable $x8, target-flags(aarch64-pageoff, aarch64-nc) @label_address.addr :: (store 8 into @label_address.addr) |
| RET undef $lr, implicit killed $w0 |
| |
| bb.2.lab2: |
| ; CHECK: bb.2.lab2: |
| ; CHECK-NOT: HINT |
| liveins: $x8 |
| |
| $x9 = ADRP target-flags(aarch64-page) blockaddress(@label_address, %ir-block.return) |
| renamable $w0 = ORRWri $wzr, 1984 |
| renamable $x9 = ADDXri killed $x9, target-flags(aarch64-pageoff, aarch64-nc) blockaddress(@label_address, %ir-block.return), 0 |
| STRXui killed renamable $x9, killed renamable $x8, target-flags(aarch64-pageoff, aarch64-nc) @label_address.addr :: (store 8 into @label_address.addr) |
| RET undef $lr, implicit killed $w0 |
| |
| ... |