xref: /llvm-project/llvm/test/CodeGen/AArch64/arm64_32.ll (revision 72901fe19eb1e55d0ee1c380ab7a9f57d2f187c5)
1; RUN: llc -mtriple=arm64_32-apple-ios7.0 %s -filetype=obj -o - -disable-post-ra -frame-pointer=non-leaf | \
2; RUN:     llvm-objdump --private-headers - | \
3; RUN:     FileCheck %s --check-prefix=CHECK-MACHO
4; RUN: llc -mtriple=arm64_32-apple-ios7.0 %s -o - -aarch64-enable-atomic-cfg-tidy=0 -disable-post-ra -frame-pointer=non-leaf | FileCheck %s --check-prefix=CHECK --check-prefix=CHECK-OPT
5; RUN: llc -mtriple=arm64_32-apple-ios7.0 %s -o - -fast-isel -aarch64-enable-atomic-cfg-tidy=0 -disable-post-ra -frame-pointer=non-leaf | FileCheck %s --check-prefix=CHECK --check-prefix=CHECK-FAST
6
7; CHECK-MACHO: Mach header
8; CHECK-MACHO: MH_MAGIC ARM64_32 V8
9
10@var64 = global i64 zeroinitializer, align 8
11@var32 = global i32 zeroinitializer, align 4
12
13@var_got = external global i8
14
15define ptr @test_global_addr() {
16; CHECK-LABEL: test_global_addr:
17; CHECK: adrp [[PAGE:x[0-9]+]], _var32@PAGE
18; CHECK-OPT: add x0, [[PAGE]], _var32@PAGEOFF
19; CHECK-FAST: add [[TMP:x[0-9]+]], [[PAGE]], _var32@PAGEOFF
20; CHECK-FAST: and x0, [[TMP]], #0xffffffff
21  ret ptr @var32
22}
23
24; ADRP is necessarily 64-bit. The important point to check is that, however that
25; gets truncated to 32-bits, it's free. No need to zero out higher bits of that
26; register.
27define i64 @test_global_addr_extension() {
28; CHECK-LABEL: test_global_addr_extension:
29; CHECK: adrp [[PAGE:x[0-9]+]], _var32@PAGE
30; CHECK: add x0, [[PAGE]], _var32@PAGEOFF
31; CHECK-NOT: and
32; CHECK: ret
33
34  ret i64 ptrtoint(ptr @var32 to i64)
35}
36
37define i32 @test_global_value() {
38; CHECK-LABEL: test_global_value:
39; CHECK: adrp x[[PAGE:[0-9]+]], _var32@PAGE
40; CHECK: ldr w0, [x[[PAGE]], _var32@PAGEOFF]
41  %val = load i32, ptr @var32, align 4
42  ret i32 %val
43}
44
45; Because the addition may wrap, it is not safe to use "ldr w0, [xN, #32]" here.
46define i32 @test_unsafe_indexed_add() {
47; CHECK-LABEL: test_unsafe_indexed_add:
48; CHECK: add x[[VAR32:[0-9]+]], {{x[0-9]+}}, _var32@PAGEOFF
49; CHECK: add w[[ADDR:[0-9]+]], w[[VAR32]], #32
50; CHECK: ldr w0, [x[[ADDR]]]
51  %addr_int = ptrtoint ptr @var32 to i32
52  %addr_plus_32 = add i32 %addr_int, 32
53  %addr = inttoptr i32 %addr_plus_32 to ptr
54  %val = load i32, ptr %addr, align 4
55  ret i32 %val
56}
57
58; Since we've promised there is no unsigned overflow, @var32 must be at least
59; 32-bytes below 2^32, and we can use the load this time.
60define i32 @test_safe_indexed_add() {
61; CHECK-LABEL: test_safe_indexed_add:
62; CHECK: add x[[VAR32:[0-9]+]], {{x[0-9]+}}, _var32@PAGEOFF
63; CHECK: add w[[ADDR:[0-9]+]], w[[VAR32]], #32
64; CHECK: ldr w0, [x[[ADDR]]]
65  %addr_int = ptrtoint ptr @var32 to i64
66  %addr_plus_32 = add nuw i64 %addr_int, 32
67  %addr = inttoptr i64 %addr_plus_32 to ptr
68  %val = load i32, ptr %addr, align 4
69  ret i32 %val
70}
71
72define i32 @test_safe_indexed_or(i32 %in) {
73; CHECK-LABEL: test_safe_indexed_or:
74; CHECK: and [[TMP:w[0-9]+]], {{w[0-9]+}}, #0xfffffff0
75; CHECK: orr w[[ADDR:[0-9]+]], [[TMP]], #0x4
76; CHECK: ldr w0, [x[[ADDR]]]
77  %addr_int = and i32 %in, -16
78  %addr_plus_4 = or i32 %addr_int, 4
79  %addr = inttoptr i32 %addr_plus_4 to ptr
80  %val = load i32, ptr %addr, align 4
81  ret i32 %val
82}
83
84
85; Promising nsw is not sufficient because the addressing mode basically
86; calculates "zext(base) + zext(offset)" and nsw only guarantees
87; "sext(base) + sext(offset) == base + offset".
88define i32 @test_unsafe_nsw_indexed_add() {
89; CHECK-LABEL: test_unsafe_nsw_indexed_add:
90; CHECK: add x[[VAR32:[0-9]+]], {{x[0-9]+}}, _var32@PAGEOFF
91; CHECK: add w[[ADDR:[0-9]+]], w[[VAR32]], #32
92; CHECK-NOT: ubfx
93; CHECK: ldr w0, [x[[ADDR]]]
94  %addr_int = ptrtoint ptr @var32 to i32
95  %addr_plus_32 = add nsw i32 %addr_int, 32
96  %addr = inttoptr i32 %addr_plus_32 to ptr
97  %val = load i32, ptr %addr, align 4
98  ret i32 %val
99}
100
101; Because the addition may wrap, it is not safe to use "ldr w0, [xN, #32]" here.
102define i32 @test_unsafe_unscaled_add() {
103; CHECK-LABEL: test_unsafe_unscaled_add:
104; CHECK: add x[[VAR32:[0-9]+]], {{x[0-9]+}}, _var32@PAGEOFF
105; CHECK: add w[[ADDR:[0-9]+]], w[[VAR32]], #3
106; CHECK: ldr w0, [x[[ADDR]]]
107  %addr_int = ptrtoint ptr @var32 to i32
108  %addr_plus_3 = add i32 %addr_int, 3
109  %addr = inttoptr i32 %addr_plus_3 to ptr
110  %val = load i32, ptr %addr, align 1
111  ret i32 %val
112}
113
114; Since we've promised there is no unsigned overflow, @var32 must be at least
115; 32-bytes below 2^32, and we can use the load this time.
116define i32 @test_safe_unscaled_add() {
117; CHECK-LABEL: test_safe_unscaled_add:
118; CHECK: add x[[VAR32:[0-9]+]], {{x[0-9]+}}, _var32@PAGEOFF
119; CHECK: add w[[ADDR:[0-9]+]], w[[VAR32]], #3
120; CHECK: ldr w0, [x[[ADDR]]]
121  %addr_int = ptrtoint ptr @var32 to i32
122  %addr_plus_3 = add nuw i32 %addr_int, 3
123  %addr = inttoptr i32 %addr_plus_3 to ptr
124  %val = load i32, ptr %addr, align 1
125  ret i32 %val
126}
127
128; Promising nsw is not sufficient because the addressing mode basically
129; calculates "zext(base) + zext(offset)" and nsw only guarantees
130; "sext(base) + sext(offset) == base + offset".
131define i32 @test_unsafe_nsw_unscaled_add() {
132; CHECK-LABEL: test_unsafe_nsw_unscaled_add:
133; CHECK: add x[[VAR32:[0-9]+]], {{x[0-9]+}}, _var32@PAGEOFF
134; CHECK: add w[[ADDR:[0-9]+]], w[[VAR32]], #3
135; CHECK-NOT: ubfx
136; CHECK: ldr w0, [x[[ADDR]]]
137  %addr_int = ptrtoint ptr @var32 to i32
138  %addr_plus_3 = add nsw i32 %addr_int, 3
139  %addr = inttoptr i32 %addr_plus_3 to ptr
140  %val = load i32, ptr %addr, align 1
141  ret i32 %val
142}
143
144; Because the addition may wrap, it is not safe to use "ldur w0, [xN, #-3]"
145; here.
146define i32 @test_unsafe_negative_unscaled_add() {
147; CHECK-LABEL: test_unsafe_negative_unscaled_add:
148; CHECK: add x[[VAR32:[0-9]+]], {{x[0-9]+}}, _var32@PAGEOFF
149; CHECK: sub w[[ADDR:[0-9]+]], w[[VAR32]], #3
150; CHECK: ldr w0, [x[[ADDR]]]
151  %addr_int = ptrtoint ptr @var32 to i32
152  %addr_minus_3 = add i32 %addr_int, -3
153  %addr = inttoptr i32 %addr_minus_3 to ptr
154  %val = load i32, ptr %addr, align 1
155  ret i32 %val
156}
157
158define ptr @test_got_addr() {
159; CHECK-LABEL: test_got_addr:
160; CHECK: adrp x[[PAGE:[0-9]+]], _var_got@GOTPAGE
161; CHECK-OPT: ldr w0, [x[[PAGE]], _var_got@GOTPAGEOFF]
162; CHECK-FAST: ldr w[[TMP:[0-9]+]], [x[[PAGE]], _var_got@GOTPAGEOFF]
163; CHECK-FAST: and x0, x[[TMP]], #0xffffffff
164  ret ptr @var_got
165}
166
167define float @test_va_arg_f32(ptr %list) {
168; CHECK-LABEL: test_va_arg_f32:
169
170; CHECK: ldr w[[START:[0-9]+]], [x0]
171; CHECK: add [[AFTER:w[0-9]+]], w[[START]], #8
172; CHECK: str [[AFTER]], [x0]
173
174  ; Floating point arguments get promoted to double as per C99.
175; CHECK: ldr [[DBL:d[0-9]+]], [x[[START]]]
176; CHECK: fcvt s0, [[DBL]]
177  %res = va_arg ptr %list, float
178  ret float %res
179}
180
181; Interesting point is that the slot is 4 bytes.
182define i8 @test_va_arg_i8(ptr %list) {
183; CHECK-LABEL: test_va_arg_i8:
184
185; CHECK: ldr w[[START:[0-9]+]], [x0]
186; CHECK: add [[AFTER:w[0-9]+]], w[[START]], #4
187; CHECK: str [[AFTER]], [x0]
188
189  ; i8 gets promoted to int (again, as per C99).
190; CHECK: ldr w0, [x[[START]]]
191
192  %res = va_arg ptr %list, i8
193  ret i8 %res
194}
195
196; Interesting point is that the slot needs aligning (again, min size is 4
197; bytes).
198define i64 @test_va_arg_i64(ptr %list) {
199; CHECK-LABEL: test_va_arg_i64:
200
201  ; Update the list for the next user (minimum slot size is 4, but the actual
202  ; argument is 8 which had better be reflected!)
203; CHECK: ldr w[[UNALIGNED_START:[0-9]+]], [x0]
204; CHECK: add [[ALIGN_TMP:x[0-9]+]], x[[UNALIGNED_START]], #7
205; CHECK: and x[[START:[0-9]+]], [[ALIGN_TMP]], #0x1fffffff8
206; CHECK: add w[[AFTER:[0-9]+]], w[[START]], #8
207; CHECK: str w[[AFTER]], [x0]
208
209; CHECK: ldr x0, [x[[START]]]
210
211  %res = va_arg ptr %list, i64
212  ret i64 %res
213}
214
215declare void @bar(...)
216define void @test_va_call(i8 %l, i8 %r, float %in, ptr %ptr) {
217; CHECK-LABEL: test_va_call:
218; CHECK: add [[SUM:w[0-9]+]], {{w[0-9]+}}, w1
219
220; CHECK-DAG: str w2, [sp, #32]
221; CHECK-DAG: str xzr, [sp, #24]
222; CHECK-DAG: str s0, [sp, #16]
223; CHECK-DAG: str xzr, [sp, #8]
224; CHECK-DAG: str [[SUM]], [sp]
225
226  ; Add them to ensure real promotion occurs.
227  %sum = add i8 %l, %r
228  call void(...) @bar(i8 %sum, i64 0, float %in, double 0.0, ptr %ptr)
229  ret void
230}
231
232declare ptr @llvm.frameaddress(i32)
233
234define ptr @test_frameaddr() {
235; CHECK-LABEL: test_frameaddr:
236; CHECK-OPT: ldr x0, [x29]
237; CHECK-FAST: ldr [[TMP:x[0-9]+]], [x29]
238; CHECK-FAST: and x0, [[TMP]], #0xffffffff
239  %val = call ptr @llvm.frameaddress(i32 1)
240  ret ptr %val
241}
242
243declare ptr @llvm.returnaddress(i32)
244
245define ptr @test_toplevel_returnaddr() {
246; CHECK-LABEL: test_toplevel_returnaddr:
247; CHECK-OPT: mov x0, x30
248; CHECK-FAST: and x0, x30, #0xffffffff
249  %val = call ptr @llvm.returnaddress(i32 0)
250  ret ptr %val
251}
252
253define ptr @test_deep_returnaddr() {
254; CHECK-LABEL: test_deep_returnaddr:
255; CHECK: ldr x[[FRAME_REC:[0-9]+]], [x29]
256; CHECK-OPT: ldr x30, [x[[FRAME_REC]], #8]
257; CHECK-OPT: hint #7
258; CHECK-OPT: mov x0, x30
259; CHECK-FAST: ldr [[TMP:x[0-9]+]], [x[[FRAME_REC]], #8]
260; CHECK-FAST: and x0, [[TMP]], #0xffffffff
261  %val = call ptr @llvm.returnaddress(i32 1)
262  ret ptr %val
263}
264
265define void @test_indirect_call(ptr %func) {
266; CHECK-LABEL: test_indirect_call:
267; CHECK: blr x0
268  call void() %func()
269  ret void
270}
271
272; Safe to use the unextended address here
273define void @test_indirect_safe_call(ptr %weird_funcs) {
274; CHECK-LABEL: test_indirect_safe_call:
275; CHECK: add w[[ADDR32:[0-9]+]], w0, #4
276; CHECK-OPT-NOT: ubfx
277; CHECK: blr x[[ADDR32]]
278  %addr = getelementptr i32, ptr %weird_funcs, i32 1
279  call void() %addr()
280  ret void
281}
282
283declare void @simple()
284define void @test_simple_tail_call() {
285; CHECK-LABEL: test_simple_tail_call:
286; CHECK: b _simple
287  tail call void @simple()
288  ret void
289}
290
291define void @test_indirect_tail_call(ptr %func) {
292; CHECK-LABEL: test_indirect_tail_call:
293; CHECK: br x0
294  tail call void() %func()
295  ret void
296}
297
298; Safe to use the unextended address here
299define void @test_indirect_safe_tail_call(ptr %weird_funcs) {
300; CHECK-LABEL: test_indirect_safe_tail_call:
301; CHECK: add w[[ADDR32:[0-9]+]], w0, #4
302; CHECK-OPT-NOT: ubfx
303; CHECK-OPT: br x[[ADDR32]]
304  %addr = getelementptr i32, ptr %weird_funcs, i32 1
305  tail call void() %addr()
306  ret void
307}
308
309; For the "armv7k" slice, Clang will be emitting some small structs as [N x
310; i32]. For ABI compatibility with arm64_32 these need to be passed in *X*
311; registers (e.g. [2 x i32] would be packed into a single register).
312
313define i32 @test_in_smallstruct_low([3 x i32] %in) {
314; CHECK-LABEL: test_in_smallstruct_low:
315; CHECK: mov x0, x1
316  %val = extractvalue [3 x i32] %in, 2
317  ret i32 %val
318}
319
320define i32 @test_in_smallstruct_high([3 x i32] %in) {
321; CHECK-LABEL: test_in_smallstruct_high:
322; CHECK: lsr x0, x0, #32
323  %val = extractvalue [3 x i32] %in, 1
324  ret i32 %val
325}
326
327; The 64-bit DarwinPCS ABI has the quirk that structs on the stack are always
328; 64-bit aligned. This must not happen for arm64_32 since othwerwise va_arg will
329; be incompatible with the armv7k ABI.
330define i32 @test_in_smallstruct_stack([8 x i64], i32, [3 x i32] %in) {
331; CHECK-LABEL: test_in_smallstruct_stack:
332; CHECK: ldr w0, [sp, #4]
333  %val = extractvalue [3 x i32] %in, 0
334  ret i32 %val
335}
336
337define [2 x i32] @test_ret_smallstruct([3 x i32] %in) {
338; CHECK-LABEL: test_ret_smallstruct:
339; CHECK: mov x0, #1
340; CHECK: movk x0, #2, lsl #32
341
342  ret [2 x i32] [i32 1, i32 2]
343}
344
345declare void @smallstruct_callee([4 x i32])
346define void @test_call_smallstruct() {
347; CHECK-LABEL: test_call_smallstruct:
348; CHECK: mov x0, #1
349; CHECK: movk x0, #2, lsl #32
350; CHECK: mov x1, #3
351; CHECK: movk x1, #4, lsl #32
352; CHECK: bl _smallstruct_callee
353
354  call void @smallstruct_callee([4 x i32] [i32 1, i32 2, i32 3, i32 4])
355  ret void
356}
357
358declare void @smallstruct_callee_stack([8 x i64], i32, [2 x i32])
359define void @test_call_smallstruct_stack() {
360; CHECK-LABEL: test_call_smallstruct_stack:
361; CHECK: mov [[VAL:x[0-9]+]], #1
362; CHECK: movk [[VAL]], #2, lsl #32
363; CHECK: stur [[VAL]], [sp, #4]
364
365  call void @smallstruct_callee_stack([8 x i64] undef, i32 undef, [2 x i32] [i32 1, i32 2])
366  ret void
367}
368
369declare [3 x i32] @returns_smallstruct()
370define i32 @test_use_smallstruct_low() {
371; CHECK-LABEL: test_use_smallstruct_low:
372; CHECK: bl _returns_smallstruct
373; CHECK: mov x0, x1
374
375  %struct = call [3 x i32] @returns_smallstruct()
376  %val = extractvalue [3 x i32] %struct, 2
377  ret i32 %val
378}
379
380define i32 @test_use_smallstruct_high() {
381; CHECK-LABEL: test_use_smallstruct_high:
382; CHECK: bl _returns_smallstruct
383; CHECK: lsr x0, x0, #32
384
385  %struct = call [3 x i32] @returns_smallstruct()
386  %val = extractvalue [3 x i32] %struct, 1
387  ret i32 %val
388}
389
390; If a small struct can't be allocated to x0-x7, the remaining registers should
391; be marked as unavailable and subsequent GPR arguments should also be on the
392; stack. Obviously the struct itself should be passed entirely on the stack.
393define i32 @test_smallstruct_padding([7 x i64], [4 x i32] %struct, i32 %in) {
394; CHECK-LABEL: test_smallstruct_padding:
395; CHECK-DAG: ldr [[IN:w[0-9]+]], [sp, #16]
396; CHECK-DAG: ldr [[LHS:w[0-9]+]], [sp]
397; CHECK: add w0, [[LHS]], [[IN]]
398  %lhs = extractvalue [4 x i32] %struct, 0
399  %sum = add i32 %lhs, %in
400  ret i32 %sum
401}
402
403declare void @take_small_smallstruct(i64, [1 x i32])
404define void @test_small_smallstruct() {
405; CHECK-LABEL: test_small_smallstruct:
406; CHECK-DAG: mov w0, #1
407; CHECK-DAG: mov w1, #2
408; CHECK: bl _take_small_smallstruct
409  call void @take_small_smallstruct(i64 1, [1 x i32] [i32 2])
410  ret void
411}
412
413define void @test_bare_frameaddr(ptr %addr) {
414; CHECK-LABEL: test_bare_frameaddr:
415; CHECK: add x[[LOCAL:[0-9]+]], sp, #{{[0-9]+}}
416; CHECK: str w[[LOCAL]],
417
418  %ptr = alloca i8
419  store ptr %ptr, ptr %addr, align 4
420  ret void
421}
422
423define void @test_sret_use(ptr sret([8 x i64]) %out) {
424; CHECK-LABEL: test_sret_use:
425; CHECK: str xzr, [x8]
426  store i64 0, ptr %out
427  ret void
428}
429
430define i64 @test_sret_call() {
431; CHECK-LABEL: test_sret_call:
432; CHECK: mov x8, sp
433; CHECK: bl _test_sret_use
434  %arr = alloca [8 x i64]
435  call void @test_sret_use(ptr sret([8 x i64]) %arr)
436
437  %val = load i64, ptr %arr
438  ret i64 %val
439}
440
441define double @test_constpool() {
442; CHECK-LABEL: test_constpool:
443; CHECK: adrp x[[PAGE:[0-9]+]], [[POOL:lCPI[0-9]+_[0-9]+]]@PAGE
444; CHECK: ldr d0, [x[[PAGE]], [[POOL]]@PAGEOFF]
445  ret double 1.0e-6
446}
447
448define ptr @test_blockaddress() {
449; CHECK-LABEL: test_blockaddress:
450; CHECK: [[BLOCK:Ltmp[0-9]+]]:
451; CHECK: adrp x[[PAGE:[0-9]+]], lCPI{{[0-9]+_[0-9]+}}@PAGE
452; CHECK: ldr x0, [x[[PAGE]], lCPI{{[0-9]+_[0-9]+}}@PAGEOFF]
453  br label %dest
454dest:
455  ret ptr blockaddress(@test_blockaddress, %dest)
456}
457
458define ptr @test_indirectbr(ptr %dest) {
459; CHECK-LABEL: test_indirectbr:
460; CHECK: br x0
461  indirectbr ptr %dest, [label %true, label %false]
462
463true:
464  ret ptr blockaddress(@test_indirectbr, %true)
465false:
466  ret ptr blockaddress(@test_indirectbr, %false)
467}
468
469; ISelDAGToDAG tries to fold an offset FI load (in this case var+4) into the
470; actual load instruction. This needs to be done slightly carefully since we
471; claim the FI in the process -- it doesn't need extending.
472define float @test_frameindex_offset_load() {
473; CHECK-LABEL: test_frameindex_offset_load:
474; CHECK: ldr s0, [sp, #4]
475  %arr = alloca float, i32 4, align 8
476  %addr = getelementptr inbounds float, ptr %arr, i32 1
477
478  %val = load float, ptr %addr, align 4
479  ret float %val
480}
481
482define void @test_unaligned_frameindex_offset_store() {
483; CHECK-LABEL: test_unaligned_frameindex_offset_store:
484; CHECK: mov x[[TMP:[0-9]+]], sp
485; CHECK: orr w[[ADDR:[0-9]+]], w[[TMP]], #0x2
486; CHECK: mov [[VAL:w[0-9]+]], #42
487; CHECK: str [[VAL]], [x[[ADDR]]]
488  %arr = alloca [4 x i32]
489
490  %addr.int = ptrtoint ptr %arr to i32
491  %addr.nextint = add nuw i32 %addr.int, 2
492  %addr.next = inttoptr i32 %addr.nextint to ptr
493  store i32 42, ptr %addr.next
494  ret void
495}
496
497
498define {i64, ptr} @test_pre_idx(ptr %addr) {
499; CHECK-LABEL: test_pre_idx:
500
501; CHECK: add w[[ADDR:[0-9]+]], w0, #8
502; CHECK: ldr x0, [x[[ADDR]]]
503  %addr.int = ptrtoint ptr %addr to i32
504  %addr.next.int = add nuw i32 %addr.int, 8
505  %addr.next = inttoptr i32 %addr.next.int to ptr
506  %val = load i64, ptr %addr.next
507
508  %tmp = insertvalue {i64, ptr} undef, i64 %val, 0
509  %res = insertvalue {i64, ptr} %tmp, ptr %addr.next, 1
510
511  ret {i64, ptr} %res
512}
513
514; Forming a post-indexed load is invalid here since the GEP needs to work when
515; %addr wraps round to 0.
516define {i64, ptr} @test_invalid_pre_idx(ptr %addr) {
517; CHECK-LABEL: test_invalid_pre_idx:
518; CHECK: add w1, w0, #8
519; CHECK: ldr x0, [x1]
520  %addr.next = getelementptr i64, ptr %addr, i32 1
521  %val = load i64, ptr %addr.next
522
523  %tmp = insertvalue {i64, ptr} undef, i64 %val, 0
524  %res = insertvalue {i64, ptr} %tmp, ptr %addr.next, 1
525
526  ret {i64, ptr} %res
527}
528
529declare void @callee(ptr)
530define void @test_stack_guard() ssp {
531; CHECK-LABEL: test_stack_guard:
532; CHECK: adrp x[[GUARD_GOTPAGE:[0-9]+]], ___stack_chk_guard@GOTPAGE
533; CHECK: ldr w[[GUARD_ADDR:[0-9]+]], [x[[GUARD_GOTPAGE]], ___stack_chk_guard@GOTPAGEOFF]
534; CHECK: ldr [[GUARD_VAL:w[0-9]+]], [x[[GUARD_ADDR]]]
535; CHECK: stur [[GUARD_VAL]], [x29, #[[GUARD_OFFSET:-[0-9]+]]]
536
537; CHECK: add x0, sp, #{{[0-9]+}}
538; CHECK: bl _callee
539
540; CHECK-OPT: adrp x[[GUARD_GOTPAGE:[0-9]+]], ___stack_chk_guard@GOTPAGE
541; CHECK-OPT: ldr w[[GUARD_ADDR:[0-9]+]], [x[[GUARD_GOTPAGE]], ___stack_chk_guard@GOTPAGEOFF]
542; CHECK-OPT: ldr [[GUARD_VAL:w[0-9]+]], [x[[GUARD_ADDR]]]
543; CHECK-OPT: ldur [[NEW_VAL:w[0-9]+]], [x29, #[[GUARD_OFFSET]]]
544; CHECK-OPT: cmp [[GUARD_VAL]], [[NEW_VAL]]
545; CHECK-OPT: b.ne [[FAIL:LBB[0-9]+_[0-9]+]]
546
547; CHECK-OPT: [[FAIL]]:
548; CHECK-OPT-NEXT: bl ___stack_chk_fail
549  %arr = alloca [8 x i32]
550  call void @callee(ptr %arr)
551  ret void
552}
553
554declare i32 @__gxx_personality_v0(...)
555declare void @eat_landingpad_args(i32, ptr, i32)
556@_ZTI8Whatever = external global i8
557define void @test_landingpad_marshalling() personality ptr @__gxx_personality_v0 {
558; CHECK-LABEL: test_landingpad_marshalling:
559; CHECK-OPT: mov x2, x1
560; CHECK-OPT: mov x1, x0
561; CHECK: bl _eat_landingpad_args
562  invoke void @callee(ptr undef) to label %done unwind label %lpad
563
564lpad:                                             ; preds = %entry
565  %exc = landingpad { ptr, i32 }
566          catch ptr @_ZTI8Whatever
567  %pointer = extractvalue { ptr, i32 } %exc, 0
568  %selector = extractvalue { ptr, i32 } %exc, 1
569  call void @eat_landingpad_args(i32 undef, ptr %pointer, i32 %selector)
570  ret void
571
572done:
573  ret void
574}
575
576define void @test_dynamic_stackalloc() {
577; CHECK-LABEL: test_dynamic_stackalloc:
578; CHECK: sub [[REG:x[0-9]+]], sp, #32
579; CHECK: mov sp, [[REG]]
580; CHECK-OPT-NOT: ubfx
581; CHECK: bl _callee
582  br label %next
583
584next:
585  %val = alloca [8 x i32]
586  call void @callee(ptr %val)
587  ret void
588}
589
590define void @test_asm_memory(ptr %base.addr) {
591; CHECK-LABEL: test_asm_memory:
592; CHECK: add w[[ADDR:[0-9]+]], w0, #4
593; CHECK: str wzr, [x[[ADDR]]
594  %addr = getelementptr i32, ptr %base.addr, i32 1
595  call void asm sideeffect "str wzr, $0", "*m"(ptr elementtype(i32) %addr)
596  ret void
597}
598
599define void @test_unsafe_asm_memory(i64 %val) {
600; CHECK-LABEL: test_unsafe_asm_memory:
601; CHECK: mov w[[ADDR:[0-9]+]], w0
602; CHECK: str wzr, [x[[ADDR]]]
603  %addr_int = trunc i64 %val to i32
604  %addr = inttoptr i32 %addr_int to ptr
605  call void asm sideeffect "str wzr, $0", "*m"(ptr elementtype(i32) %addr)
606  ret void
607}
608
609define [9 x ptr] @test_demoted_return(ptr %in) {
610; CHECK-LABEL: test_demoted_return:
611; CHECK: str w0, [x8, #32]
612  %res = insertvalue [9 x ptr] undef, ptr %in, 8
613  ret [9 x ptr] %res
614}
615
616define ptr @test_inttoptr(i64 %in) {
617; CHECK-LABEL: test_inttoptr:
618; CHECK-OPT: mov w0, w0
619; CHECK-FAST: and x0, x0, #0xffffffff
620  %res = inttoptr i64 %in to ptr
621  ret ptr %res
622}
623
624declare i32 @llvm.get.dynamic.area.offset.i32()
625define i32 @test_dynamic_area() {
626; CHECK-LABEL: test_dynamic_area:
627; CHECK: mov w0, wzr
628  %res = call i32 @llvm.get.dynamic.area.offset.i32()
629  ret i32 %res
630}
631
632define void @test_pointer_vec_store(ptr %addr) {
633; CHECK-LABEL: test_pointer_vec_store:
634; CHECK: str xzr, [x0]
635; CHECK-NOT: str
636; CHECK-NOT: stp
637
638  store <2 x ptr> zeroinitializer, ptr %addr, align 16
639  ret void
640}
641
642define <2 x ptr> @test_pointer_vec_load(ptr %addr) {
643; CHECK-LABEL: test_pointer_vec_load:
644; CHECK: ldr d[[TMP:[0-9]+]], [x0]
645; CHECK: ushll.2d v0, v[[TMP]], #0
646  %val = load <2 x ptr>, ptr %addr, align 16
647  ret <2 x ptr> %val
648}
649
650define void @test_inline_asm_mem_pointer(ptr %in) {
651; CHECK-LABEL: test_inline_asm_mem_pointer:
652; CHECK: str w0,
653  tail call void asm sideeffect "ldr x0, $0", "m"(ptr %in)
654  ret void
655}
656
657
658define void @test_struct_hi(i32 %hi) nounwind {
659; CHECK-LABEL: test_struct_hi:
660; CHECK: mov w[[IN:[0-9]+]], w0
661; CHECK: bl _get_int
662; CHECK-FAST-NEXT: mov w[[DST:[0-9]+]], w0
663; CHECK-FAST-NEXT: orr x0, x[[DST]], x[[IN]], lsl #32
664; CHECK-OPT-NEXT: bfi x0, x[[IN]], #32, #32
665; CHECK-NEXT: bl _take_pair
666  %val.64 = call i64 @get_int()
667  %val.32 = trunc i64 %val.64 to i32
668
669  %pair.0 = insertvalue [2 x i32] undef, i32 %val.32, 0
670  %pair.1 = insertvalue [2 x i32] %pair.0, i32 %hi, 1
671  call void @take_pair([2 x i32] %pair.1)
672
673  ret void
674}
675declare void @take_pair([2 x i32])
676declare i64 @get_int()
677
678define i1 @test_icmp_ptr(ptr %in) {
679; CHECK-LABEL: test_icmp_ptr
680; CHECK: lsr w0, w0, #31
681  %res = icmp slt ptr %in, null
682  ret i1 %res
683}
684
685define void @test_multiple_icmp_ptr(ptr %l, ptr %r) {
686; CHECK-LABEL: test_multiple_icmp_ptr:
687; CHECK: tbnz w0, #31, [[FALSEBB:LBB[0-9]+_[0-9]+]]
688; CHECK: tbnz w1, #31, [[FALSEBB]]
689  %tst1 = icmp sgt ptr %l, inttoptr (i32 -1 to ptr)
690  %tst2 = icmp sgt ptr %r, inttoptr (i32 -1 to ptr)
691  %tst = and i1 %tst1, %tst2
692  br i1 %tst, label %true, label %false
693
694true:
695  call void(...) @bar()
696  ret void
697
698false:
699  ret void
700}
701
702define void @test_multiple_icmp_ptr_select(ptr %l, ptr %r) {
703; CHECK-LABEL: test_multiple_icmp_ptr_select:
704; CHECK: tbnz w0, #31, [[FALSEBB:LBB[0-9]+_[0-9]+]]
705; CHECK: tbnz w1, #31, [[FALSEBB]]
706  %tst1 = icmp sgt ptr %l, inttoptr (i32 -1 to ptr)
707  %tst2 = icmp sgt ptr %r, inttoptr (i32 -1 to ptr)
708  %tst = select i1 %tst1, i1 %tst2, i1 false
709  br i1 %tst, label %true, label %false
710
711true:
712  call void(...) @bar()
713  ret void
714
715false:
716  ret void
717}
718
719define ptr @test_gep_nonpow2(ptr %a0, i32 %a1) {
720; CHECK-LABEL: test_gep_nonpow2:
721; CHECK-OPT:      mov w[[SIZE:[0-9]+]], #18
722; CHECK-OPT-NEXT: smaddl x0, w1, w[[SIZE]], x0
723; CHECK-OPT-NEXT: ret
724
725; CHECK-FAST:      mov w[[SIZE:[0-9]+]], #18
726; CHECK-FAST-NEXT: smaddl [[TMP:x[0-9]+]], w1, w[[SIZE]], x0
727; CHECK-FAST-NEXT: and x0, [[TMP]], #0xffffffff
728; CHECK-FAST-NEXT: ret
729  %tmp0 = getelementptr inbounds { [18 x i8] }, ptr %a0, i32 %a1
730  ret ptr %tmp0
731}
732
733define void @test_memset(i64 %in, i8 %value)  {
734; CHECK-LABEL: test_memset:
735; CHECK-DAG: lsr x2, x0, #32
736; CHECK-DAG: mov w0, w0
737; CHECK: b _memset
738
739  %ptr.i32 = trunc i64 %in to i32
740  %size.64 = lshr i64 %in, 32
741  %size = trunc i64 %size.64 to i32
742  %ptr = inttoptr i32 %ptr.i32 to ptr
743  tail call void @llvm.memset.p0.i32(ptr align 4 %ptr, i8 %value, i32 %size, i1 false)
744  ret void
745}
746
747define void @test_bzero(i64 %in)  {
748; CHECK-LABEL: test_bzero:
749; CHECK-DAG: lsr x1, x0, #32
750; CHECK-DAG: mov w0, w0
751; CHECK: b _bzero
752
753  %ptr.i32 = trunc i64 %in to i32
754  %size.64 = lshr i64 %in, 32
755  %size = trunc i64 %size.64 to i32
756  %ptr = inttoptr i32 %ptr.i32 to ptr
757  tail call void @llvm.memset.p0.i32(ptr align 4 %ptr, i8 0, i32 %size, i1 false)
758  ret void
759}
760
761declare void @llvm.memset.p0.i32(ptr nocapture writeonly, i8, i32, i1)
762
763define i1 @test_stackguard(ptr %p1) {
764; CHECK-LABEL: test_stackguard:
765; CHECK: adrp x[[TMP:[0-9]+]], ___stack_chk_guard@GOTPAGE
766; CHECK: ldr [[GUARD:w[0-9]+]], [x[[TMP]], ___stack_chk_guard@GOTPAGEOFF]
767; CHECK: cmp [[GUARD]], w
768
769  %p2 = call ptr @llvm.stackguard()
770  %res = icmp ne ptr %p2, %p1
771  ret i1 %res
772}
773declare ptr @llvm.stackguard()
774@__stack_chk_guard = external global i32
775
776
777!llvm.module.flags = !{!0}
778!0 = !{i32 7, !"PIC Level", i32 2}
779