1# RUN: llc -run-pass=aarch64-branch-targets %s -o - | FileCheck %s
2--- |
3  target datalayout = "e-m:e-i8:8:32-i16:16:32-i64:64-i128:128-n32:64-S128"
4  target triple = "aarch64-arm-none-eabi"
5
6  define hidden i32 @simple_external() "branch-target-enforcement" {
7  entry:
8    ret i32 0
9  }
10
11  define internal i32 @simple_internal() "branch-target-enforcement" {
12  entry:
13    ret i32 0
14  }
15
16  define hidden i32 @ptr_auth() "branch-target-enforcement" {
17  entry:
18    tail call void asm sideeffect "", "~{lr}"()
19    ret i32 0
20  }
21
22  define hidden i32 @ptr_auth_b() "branch-target-enforcement" {
23  entry:
24    tail call void asm sideeffect "", "~{lr}"()
25    ret i32 0
26  }
27
28  define hidden i32 @jump_table(i32 %a) "branch-target-enforcement" {
29  entry:
30    switch i32 %a, label %sw.epilog [
31      i32 1, label %sw.bb
32      i32 2, label %sw.bb1
33      i32 3, label %sw.bb2
34      i32 4, label %sw.bb3
35      i32 5, label %sw.bb4
36    ]
37
38  sw.bb:                                            ; preds = %entry
39    tail call void asm sideeffect "", ""()
40    br label %sw.epilog
41
42  sw.bb1:                                           ; preds = %entry
43    tail call void asm sideeffect "", ""()
44    br label %sw.epilog
45
46  sw.bb2:                                           ; preds = %entry
47    tail call void asm sideeffect "", ""()
48    br label %sw.epilog
49
50  sw.bb3:                                           ; preds = %entry
51    tail call void asm sideeffect "", ""()
52    br label %sw.epilog
53
54  sw.bb4:                                           ; preds = %entry
55    tail call void asm sideeffect "", ""()
56    br label %sw.epilog
57
58  sw.epilog:                                        ; preds = %entry, %sw.bb4, %sw.bb3, %sw.bb2, %sw.bb1, %sw.bb
59    ret i32 0
60  }
61
62  @label_address.addr = internal unnamed_addr global i8* blockaddress(@label_address, %return), align 8
63
64  define hidden i32 @label_address() "branch-target-enforcement" {
65  entry:
66    %0 = load i8*, i8** @label_address.addr, align 8
67    indirectbr i8* %0, [label %return, label %lab2]
68
69  lab2:                                             ; preds = %entry
70    br label %.split
71
72  return:                                           ; preds = %entry
73    br label %.split
74
75  .split:                                           ; preds = %lab2, %return
76    %merge = phi i8* [ blockaddress(@label_address, %lab2), %return ], [ blockaddress(@label_address, %return), %lab2 ]
77    %merge2 = phi i32 [ 1, %return ], [ 2, %lab2 ]
78    store i8* %merge, i8** @label_address.addr, align 8
79    ret i32 %merge2
80  }
81
82  define hidden i32 @label_address_entry() "branch-target-enforcement" {
83  entry:
84    %0 = load i8*, i8** @label_address.addr, align 8
85    indirectbr i8* %0, [label %return, label %lab2]
86
87  lab2:                                             ; preds = %entry
88    br label %.split
89
90  return:                                           ; preds = %entry
91    br label %.split
92
93  .split:                                           ; preds = %lab2, %return
94    %merge = phi i8* [ blockaddress(@label_address, %lab2), %return ], [ blockaddress(@label_address, %return), %lab2 ]
95    %merge2 = phi i32 [ 1, %return ], [ 2, %lab2 ]
96    store i8* %merge, i8** @label_address.addr, align 8
97    ret i32 %merge2
98  }
99
100...
101---
102# External function, could be addres-taken elsewhere so needs BTI JC.
103name:            simple_external
104body:             |
105  bb.0.entry:
106    ; CHECK-LABEL: name: simple_external
107    ; CHECK: HINT 34
108    ; CHECK: RET
109    $w0 = ORRWrs $wzr, $wzr, 0
110    RET undef $lr, implicit killed $w0
111
112---
113# Internal function, not address-taken in this module, so no BTI needed.
114name:            simple_internal
115body:             |
116  bb.0.entry:
117    ; CHECK-LABEL: name: simple_internal
118    ; CHECK-NOT: HINT
119    ; CHECK: RET
120    $w0 = ORRWrs $wzr, $wzr, 0
121    RET undef $lr, implicit killed $w0
122
123---
124# Function starts with PACIASP, which implicitly acts as BTI JC, so no change
125# needed.
126name:            ptr_auth
127stack:
128  - { id: 0, name: '', type: spill-slot, offset: -16, size: 8, alignment: 16,
129      stack-id: 0, callee-saved-register: '$lr', callee-saved-restored: true,
130      debug-info-variable: '', debug-info-expression: '', debug-info-location: '' }
131body:             |
132  bb.0.entry:
133    liveins: $lr
134
135    ; CHECK-LABEL: name: ptr_auth
136    ; CHECK-NOT: HINT
137    ; CHECK: frame-setup PACIASP
138    ; CHECK-NOT: HINT
139    ; CHECK: RETAA
140    frame-setup PACIASP implicit-def $lr, implicit killed $lr, implicit $sp
141    early-clobber $sp = frame-setup STRXpre killed $lr, $sp, -16 :: (store 8 into %stack.0)
142    INLINEASM &"", 1, 12, implicit-def dead early-clobber $lr
143    $w0 = ORRWrs $wzr, $wzr, 0
144    early-clobber $sp, $lr = frame-destroy LDRXpost $sp, 16 :: (load 8 from %stack.0)
145    RETAA implicit killed $w0
146
147---
148# Function starts with PACIBSP, which implicitly acts as BTI JC, so no change
149# needed.
150name:            ptr_auth_b
151stack:
152  - { id: 0, name: '', type: spill-slot, offset: -16, size: 8, alignment: 16,
153      stack-id: 0, callee-saved-register: '$lr', callee-saved-restored: true,
154      debug-info-variable: '', debug-info-expression: '', debug-info-location: '' }
155body:             |
156  bb.0.entry:
157    liveins: $lr
158
159    ; CHECK-LABEL: name: ptr_auth_b
160    ; CHECK-NOT: HINT
161    ; CHECK: frame-setup PACIBSP
162    ; CHECK-NOT: HINT
163    ; CHECK: RETAB
164    frame-setup PACIBSP implicit-def $lr, implicit killed $lr, implicit $sp
165    early-clobber $sp = frame-setup STRXpre killed $lr, $sp, -16 :: (store 8 into %stack.0)
166    INLINEASM &"", 1, 12, implicit-def dead early-clobber $lr
167    $w0 = ORRWrs $wzr, $wzr, 0
168    early-clobber $sp, $lr = frame-destroy LDRXpost $sp, 16 :: (load 8 from %stack.0)
169    RETAB implicit killed $w0
170
171---
172# Function contains a jump table, so every target of the jump table must start
173# with BTI J.
174name:            jump_table
175jumpTable:
176  kind:            block-address
177  entries:
178    - id:              0
179      blocks:          [ '%bb.2', '%bb.3', '%bb.4', '%bb.5', '%bb.6' ]
180body:             |
181  bb.0.entry:
182    ; CHECK-LABEL: name: jump_table
183    ; CHECK: HINT 34
184    successors: %bb.7(0x15555555), %bb.1(0x6aaaaaab)
185    liveins: $w0
186
187    renamable $w8 = SUBWri killed renamable $w0, 1, 0, implicit-def $x8
188    dead $wzr = SUBSWri renamable $w8, 4, 0, implicit-def $nzcv
189    Bcc 8, %bb.7, implicit $nzcv
190
191  bb.1.entry:
192    ; CHECK: bb.1.entry:
193    ; CHECK-NOT: HINT
194    ; CHECK: BR killed renamable $x8
195    successors: %bb.2(0x1999999a), %bb.3(0x1999999a), %bb.4(0x1999999a), %bb.5(0x1999999a), %bb.6(0x1999999a)
196    liveins: $x8
197
198    $x9 = ADRP target-flags(aarch64-page) %jump-table.0
199    renamable $x9 = ADDXri killed $x9, target-flags(aarch64-pageoff, aarch64-nc) %jump-table.0, 0
200    renamable $x8 = LDRXroX killed renamable $x9, killed renamable $x8, 0, 1 :: (load 8 from jump-table)
201    BR killed renamable $x8
202
203  bb.2.sw.bb:
204    ; CHECK: bb.2.sw.bb
205    ; CHECK-NEXT: HINT 36
206    $w0 = ORRWrs $wzr, $wzr, 0
207    INLINEASM &"", 1
208    RET undef $lr, implicit killed $w0
209
210  bb.3.sw.bb1:
211    ; CHECK: bb.3.sw.bb1
212    ; CHECK-NEXT: HINT 36
213    $w0 = ORRWrs $wzr, $wzr, 0
214    INLINEASM &"", 1
215    RET undef $lr, implicit killed $w0
216
217  bb.4.sw.bb2:
218    ; CHECK: bb.4.sw.bb2
219    ; CHECK-NEXT: HINT 36
220    $w0 = ORRWrs $wzr, $wzr, 0
221    INLINEASM &"", 1
222    RET undef $lr, implicit killed $w0
223
224  bb.5.sw.bb3:
225    ; CHECK: bb.5.sw.bb3
226    ; CHECK-NEXT: HINT 36
227    $w0 = ORRWrs $wzr, $wzr, 0
228    INLINEASM &"", 1
229    RET undef $lr, implicit killed $w0
230
231  bb.6.sw.bb4:
232    ; CHECK: bb.6.sw.bb4
233    ; CHECK-NEXT: successors: %bb.7(0x80000000)
234    ; CHECK-NEXT: {{  }}
235    ; CHECK-NEXT: HINT 36
236    successors: %bb.7(0x80000000)
237
238    INLINEASM &"", 1
239
240  bb.7.sw.epilog:
241    ; CHECK: bb.7.sw.epilog:
242    ; CHECK-NOT: HINT
243    ; CHECK: RET
244    $w0 = ORRWrs $wzr, $wzr, 0
245    RET undef $lr, implicit killed $w0
246
247---
248# Function takes address of basic blocks, so they must start with BTI J.
249name:            label_address
250body:             |
251  bb.0.entry:
252    ; CHECK-LABEL: label_address
253    ; CHECK: bb.0.entry:
254    ; CHECK-NEXT: successors: %bb.1(0x40000000), %bb.2(0x40000000)
255    ; CHECK-NEXT: {{  }}
256    ; CHECK-NEXT: HINT 34
257    ; CHECK: BR killed renamable $x9
258    successors: %bb.1(0x40000000), %bb.2(0x40000000)
259
260    renamable $x8 = ADRP target-flags(aarch64-page) @label_address.addr
261    renamable $x9 = LDRXui renamable $x8, target-flags(aarch64-pageoff, aarch64-nc) @label_address.addr :: (dereferenceable load 8 from @label_address.addr)
262    BR killed renamable $x9
263
264  bb.1.return (address-taken):
265    ; CHECK: bb.1.return (address-taken):
266    ; CHECK-NEXT: HINT 36
267    liveins: $x8
268
269    $x9 = ADRP target-flags(aarch64-page) blockaddress(@label_address, %ir-block.lab2)
270    renamable $w0 = ORRWri $wzr, 0
271    renamable $x9 = ADDXri killed $x9, target-flags(aarch64-pageoff, aarch64-nc) blockaddress(@label_address, %ir-block.lab2), 0
272    STRXui killed renamable $x9, killed renamable $x8, target-flags(aarch64-pageoff, aarch64-nc) @label_address.addr :: (store 8 into @label_address.addr)
273    RET undef $lr, implicit killed $w0
274
275  bb.2.lab2 (address-taken):
276    ; CHECK: bb.2.lab2 (address-taken):
277    ; CHECK-NEXT: HINT 36
278    liveins: $x8
279
280    $x9 = ADRP target-flags(aarch64-page) blockaddress(@label_address, %ir-block.return)
281    renamable $w0 = ORRWri $wzr, 1984
282    renamable $x9 = ADDXri killed $x9, target-flags(aarch64-pageoff, aarch64-nc) blockaddress(@label_address, %ir-block.return), 0
283    STRXui killed renamable $x9, killed renamable $x8, target-flags(aarch64-pageoff, aarch64-nc) @label_address.addr :: (store 8 into @label_address.addr)
284    RET undef $lr, implicit killed $w0
285
286---
287# Function takes address of the entry block, so the entry block needs a BTI JC.
288name:            label_address_entry
289body:             |
290  bb.0.entry (address-taken):
291    ; CHECK-LABEL: label_address_entry
292    ; CHECK: bb.0.entry (address-taken):
293    ; CHECK-NEXT: successors: %bb.1(0x40000000), %bb.2(0x40000000)
294    ; CHECK-NEXT: {{  }}
295    ; CHECK-NEXT: HINT 38
296    ; CHECK: BR killed renamable $x9
297    successors: %bb.1(0x40000000), %bb.2(0x40000000)
298
299    renamable $x8 = ADRP target-flags(aarch64-page) @label_address.addr
300    renamable $x9 = LDRXui renamable $x8, target-flags(aarch64-pageoff, aarch64-nc) @label_address.addr :: (dereferenceable load 8 from @label_address.addr)
301    BR killed renamable $x9
302
303  bb.1.return (address-taken):
304    ; CHECK: bb.1.return (address-taken):
305    ; CHECK-NEXT: HINT 36
306    liveins: $x8
307
308    $x9 = ADRP target-flags(aarch64-page) blockaddress(@label_address, %ir-block.entry)
309    renamable $w0 = ORRWri $wzr, 0
310    renamable $x9 = ADDXri killed $x9, target-flags(aarch64-pageoff, aarch64-nc) blockaddress(@label_address, %ir-block.entry), 0
311    STRXui killed renamable $x9, killed renamable $x8, target-flags(aarch64-pageoff, aarch64-nc) @label_address.addr :: (store 8 into @label_address.addr)
312    RET undef $lr, implicit killed $w0
313
314  bb.2.lab2:
315    ; CHECK: bb.2.lab2:
316    ; CHECK-NOT: HINT
317    liveins: $x8
318
319    $x9 = ADRP target-flags(aarch64-page) blockaddress(@label_address, %ir-block.return)
320    renamable $w0 = ORRWri $wzr, 1984
321    renamable $x9 = ADDXri killed $x9, target-flags(aarch64-pageoff, aarch64-nc) blockaddress(@label_address, %ir-block.return), 0
322    STRXui killed renamable $x9, killed renamable $x8, target-flags(aarch64-pageoff, aarch64-nc) @label_address.addr :: (store 8 into @label_address.addr)
323    RET undef $lr, implicit killed $w0
324
325...
326