| ; NOTE: Assertions have been autogenerated by utils/update_llc_test_checks.py UTC_ARGS: --version 6 | |
| ; RUN: llc < %s -mtriple=aarch64-windows -verify-machineinstrs | FileCheck %s --check-prefixes=DAGISEL | |
| ; RUN: llc < %s -mtriple=aarch64-unknown-uefi -verify-machineinstrs | FileCheck %s --check-prefixes=DAGISEL | |
| ; RUN: llc < %s -mtriple=aarch64-windows -verify-machineinstrs -O0 -global-isel | FileCheck %s --check-prefixes=GISEL | |
| ; RUN: llc < %s -mtriple=aarch64-linux-gnu -verify-machineinstrs | FileCheck %s --check-prefixes=LINUX | |
| ; Windows lays out the arguments of a variadic function as if on a stack whose | |
| ; first 64 bytes are loaded into x0-x7. A composite whose first eight bytes | |
| ; land in x7 is therefore split between x7 and the stack, for named and | |
| ; variadic arguments alike. AAPCS64 (Linux) keeps it in one block instead. | |
| declare void @sink(i32, ...) | |
| declare void @fixed_sink(i64, i64, i64, i64, i64, i64, i64, [2 x i64], ...) | |
| declare win64cc void @win64_sink(i32, ...) | |
| ; The fourth [2 x i64] starts at x7. | |
| define void @call_split([2 x i64] %a, [2 x i64] %b, [2 x i64] %c, [2 x i64] %d) nounwind { | |
| ; DAGISEL-LABEL: call_split: | |
| ; DAGISEL: // %bb.0: | |
| ; DAGISEL-NEXT: sub sp, sp, #32 | |
| ; DAGISEL-NEXT: mov x8, x7 | |
| ; DAGISEL-NEXT: mov x7, x6 | |
| ; DAGISEL-NEXT: mov x6, x5 | |
| ; DAGISEL-NEXT: mov x5, x4 | |
| ; DAGISEL-NEXT: mov x4, x3 | |
| ; DAGISEL-NEXT: mov x3, x2 | |
| ; DAGISEL-NEXT: mov x2, x1 | |
| ; DAGISEL-NEXT: mov x1, x0 | |
| ; DAGISEL-NEXT: mov w0, #4 // =0x4 | |
| ; DAGISEL-NEXT: str x30, [sp, #16] // 8-byte Spill | |
| ; DAGISEL-NEXT: str x8, [sp] | |
| ; DAGISEL-NEXT: bl sink | |
| ; DAGISEL-NEXT: ldr x30, [sp, #16] // 8-byte Reload | |
| ; DAGISEL-NEXT: add sp, sp, #32 | |
| ; DAGISEL-NEXT: ret | |
| ; | |
| ; GISEL-LABEL: call_split: | |
| ; GISEL: // %bb.0: | |
| ; GISEL-NEXT: sub sp, sp, #80 | |
| ; GISEL-NEXT: str x30, [sp, #64] // 8-byte Spill | |
| ; GISEL-NEXT: str x0, [sp, #8] // 8-byte Spill | |
| ; GISEL-NEXT: mov x0, x1 | |
| ; GISEL-NEXT: ldr x1, [sp, #8] // 8-byte Reload | |
| ; GISEL-NEXT: str x0, [sp, #16] // 8-byte Spill | |
| ; GISEL-NEXT: mov x0, x2 | |
| ; GISEL-NEXT: ldr x2, [sp, #16] // 8-byte Reload | |
| ; GISEL-NEXT: str x0, [sp, #24] // 8-byte Spill | |
| ; GISEL-NEXT: mov x0, x3 | |
| ; GISEL-NEXT: ldr x3, [sp, #24] // 8-byte Reload | |
| ; GISEL-NEXT: str x0, [sp, #32] // 8-byte Spill | |
| ; GISEL-NEXT: mov x0, x4 | |
| ; GISEL-NEXT: ldr x4, [sp, #32] // 8-byte Reload | |
| ; GISEL-NEXT: str x0, [sp, #40] // 8-byte Spill | |
| ; GISEL-NEXT: mov x0, x5 | |
| ; GISEL-NEXT: ldr x5, [sp, #40] // 8-byte Reload | |
| ; GISEL-NEXT: str x0, [sp, #48] // 8-byte Spill | |
| ; GISEL-NEXT: mov x0, x6 | |
| ; GISEL-NEXT: ldr x6, [sp, #48] // 8-byte Reload | |
| ; GISEL-NEXT: str x0, [sp, #56] // 8-byte Spill | |
| ; GISEL-NEXT: mov x8, x7 | |
| ; GISEL-NEXT: ldr x7, [sp, #56] // 8-byte Reload | |
| ; GISEL-NEXT: mov w0, #4 // =0x4 | |
| ; GISEL-NEXT: mov x9, sp | |
| ; GISEL-NEXT: str x8, [x9] | |
| ; GISEL-NEXT: bl sink | |
| ; GISEL-NEXT: ldr x30, [sp, #64] // 8-byte Reload | |
| ; GISEL-NEXT: add sp, sp, #80 | |
| ; GISEL-NEXT: ret | |
| ; | |
| ; LINUX-LABEL: call_split: | |
| ; LINUX: // %bb.0: | |
| ; LINUX-NEXT: sub sp, sp, #32 | |
| ; LINUX-NEXT: mov x8, x6 | |
| ; LINUX-NEXT: mov x6, x5 | |
| ; LINUX-NEXT: mov x5, x4 | |
| ; LINUX-NEXT: mov x4, x3 | |
| ; LINUX-NEXT: mov x3, x2 | |
| ; LINUX-NEXT: mov x2, x1 | |
| ; LINUX-NEXT: mov x1, x0 | |
| ; LINUX-NEXT: mov w0, #4 // =0x4 | |
| ; LINUX-NEXT: stp x7, x30, [sp, #8] // 8-byte Folded Spill | |
| ; LINUX-NEXT: str x8, [sp] | |
| ; LINUX-NEXT: bl sink | |
| ; LINUX-NEXT: ldr x30, [sp, #16] // 8-byte Reload | |
| ; LINUX-NEXT: add sp, sp, #32 | |
| ; LINUX-NEXT: ret | |
| call void (i32, ...) @sink(i32 4, [2 x i64] %a, [2 x i64] %b, [2 x i64] %c, [2 x i64] %d) | |
| ret void | |
| } | |
| ; Same, through win64cc, which uses the Windows variadic convention on any | |
| ; target. | |
| define void @call_split_win64cc([2 x i64] %a, [2 x i64] %b, [2 x i64] %c, [2 x i64] %d) nounwind { | |
| ; DAGISEL-LABEL: call_split_win64cc: | |
| ; DAGISEL: // %bb.0: | |
| ; DAGISEL-NEXT: sub sp, sp, #32 | |
| ; DAGISEL-NEXT: mov x8, x7 | |
| ; DAGISEL-NEXT: mov x7, x6 | |
| ; DAGISEL-NEXT: mov x6, x5 | |
| ; DAGISEL-NEXT: mov x5, x4 | |
| ; DAGISEL-NEXT: mov x4, x3 | |
| ; DAGISEL-NEXT: mov x3, x2 | |
| ; DAGISEL-NEXT: mov x2, x1 | |
| ; DAGISEL-NEXT: mov x1, x0 | |
| ; DAGISEL-NEXT: mov w0, #4 // =0x4 | |
| ; DAGISEL-NEXT: str x30, [sp, #16] // 8-byte Spill | |
| ; DAGISEL-NEXT: str x8, [sp] | |
| ; DAGISEL-NEXT: bl win64_sink | |
| ; DAGISEL-NEXT: ldr x30, [sp, #16] // 8-byte Reload | |
| ; DAGISEL-NEXT: add sp, sp, #32 | |
| ; DAGISEL-NEXT: ret | |
| ; | |
| ; GISEL-LABEL: call_split_win64cc: | |
| ; GISEL: // %bb.0: | |
| ; GISEL-NEXT: sub sp, sp, #80 | |
| ; GISEL-NEXT: str x30, [sp, #64] // 8-byte Spill | |
| ; GISEL-NEXT: str x0, [sp, #8] // 8-byte Spill | |
| ; GISEL-NEXT: mov x0, x1 | |
| ; GISEL-NEXT: ldr x1, [sp, #8] // 8-byte Reload | |
| ; GISEL-NEXT: str x0, [sp, #16] // 8-byte Spill | |
| ; GISEL-NEXT: mov x0, x2 | |
| ; GISEL-NEXT: ldr x2, [sp, #16] // 8-byte Reload | |
| ; GISEL-NEXT: str x0, [sp, #24] // 8-byte Spill | |
| ; GISEL-NEXT: mov x0, x3 | |
| ; GISEL-NEXT: ldr x3, [sp, #24] // 8-byte Reload | |
| ; GISEL-NEXT: str x0, [sp, #32] // 8-byte Spill | |
| ; GISEL-NEXT: mov x0, x4 | |
| ; GISEL-NEXT: ldr x4, [sp, #32] // 8-byte Reload | |
| ; GISEL-NEXT: str x0, [sp, #40] // 8-byte Spill | |
| ; GISEL-NEXT: mov x0, x5 | |
| ; GISEL-NEXT: ldr x5, [sp, #40] // 8-byte Reload | |
| ; GISEL-NEXT: str x0, [sp, #48] // 8-byte Spill | |
| ; GISEL-NEXT: mov x0, x6 | |
| ; GISEL-NEXT: ldr x6, [sp, #48] // 8-byte Reload | |
| ; GISEL-NEXT: str x0, [sp, #56] // 8-byte Spill | |
| ; GISEL-NEXT: mov x8, x7 | |
| ; GISEL-NEXT: ldr x7, [sp, #56] // 8-byte Reload | |
| ; GISEL-NEXT: mov w0, #4 // =0x4 | |
| ; GISEL-NEXT: mov x9, sp | |
| ; GISEL-NEXT: str x8, [x9] | |
| ; GISEL-NEXT: bl win64_sink | |
| ; GISEL-NEXT: ldr x30, [sp, #64] // 8-byte Reload | |
| ; GISEL-NEXT: add sp, sp, #80 | |
| ; GISEL-NEXT: ret | |
| ; | |
| ; LINUX-LABEL: call_split_win64cc: | |
| ; LINUX: // %bb.0: | |
| ; LINUX-NEXT: sub sp, sp, #32 | |
| ; LINUX-NEXT: mov x8, x7 | |
| ; LINUX-NEXT: mov x7, x6 | |
| ; LINUX-NEXT: mov x6, x5 | |
| ; LINUX-NEXT: mov x5, x4 | |
| ; LINUX-NEXT: mov x4, x3 | |
| ; LINUX-NEXT: mov x3, x2 | |
| ; LINUX-NEXT: mov x2, x1 | |
| ; LINUX-NEXT: mov x1, x0 | |
| ; LINUX-NEXT: mov w0, #4 // =0x4 | |
| ; LINUX-NEXT: str x30, [sp, #16] // 8-byte Spill | |
| ; LINUX-NEXT: str x8, [sp] | |
| ; LINUX-NEXT: bl win64_sink | |
| ; LINUX-NEXT: ldr x30, [sp, #16] // 8-byte Reload | |
| ; LINUX-NEXT: add sp, sp, #32 | |
| ; LINUX-NEXT: ret | |
| call win64cc void (i32, ...) @win64_sink(i32 4, [2 x i64] %a, [2 x i64] %b, [2 x i64] %c, [2 x i64] %d) | |
| ret void | |
| } | |
| ; Same for [2 x ptr], which clang uses for a struct of two pointers. | |
| define void @call_split_ptr([2 x ptr] %a, [2 x ptr] %b, [2 x ptr] %c, [2 x ptr] %d) nounwind { | |
| ; DAGISEL-LABEL: call_split_ptr: | |
| ; DAGISEL: // %bb.0: | |
| ; DAGISEL-NEXT: sub sp, sp, #32 | |
| ; DAGISEL-NEXT: mov x8, x7 | |
| ; DAGISEL-NEXT: mov x7, x6 | |
| ; DAGISEL-NEXT: mov x6, x5 | |
| ; DAGISEL-NEXT: mov x5, x4 | |
| ; DAGISEL-NEXT: mov x4, x3 | |
| ; DAGISEL-NEXT: mov x3, x2 | |
| ; DAGISEL-NEXT: mov x2, x1 | |
| ; DAGISEL-NEXT: mov x1, x0 | |
| ; DAGISEL-NEXT: mov w0, #4 // =0x4 | |
| ; DAGISEL-NEXT: str x30, [sp, #16] // 8-byte Spill | |
| ; DAGISEL-NEXT: str x8, [sp] | |
| ; DAGISEL-NEXT: bl sink | |
| ; DAGISEL-NEXT: ldr x30, [sp, #16] // 8-byte Reload | |
| ; DAGISEL-NEXT: add sp, sp, #32 | |
| ; DAGISEL-NEXT: ret | |
| ; | |
| ; GISEL-LABEL: call_split_ptr: | |
| ; GISEL: // %bb.0: | |
| ; GISEL-NEXT: sub sp, sp, #80 | |
| ; GISEL-NEXT: str x30, [sp, #64] // 8-byte Spill | |
| ; GISEL-NEXT: str x0, [sp, #8] // 8-byte Spill | |
| ; GISEL-NEXT: mov x0, x1 | |
| ; GISEL-NEXT: ldr x1, [sp, #8] // 8-byte Reload | |
| ; GISEL-NEXT: str x0, [sp, #16] // 8-byte Spill | |
| ; GISEL-NEXT: mov x0, x2 | |
| ; GISEL-NEXT: ldr x2, [sp, #16] // 8-byte Reload | |
| ; GISEL-NEXT: str x0, [sp, #24] // 8-byte Spill | |
| ; GISEL-NEXT: mov x0, x3 | |
| ; GISEL-NEXT: ldr x3, [sp, #24] // 8-byte Reload | |
| ; GISEL-NEXT: str x0, [sp, #32] // 8-byte Spill | |
| ; GISEL-NEXT: mov x0, x4 | |
| ; GISEL-NEXT: ldr x4, [sp, #32] // 8-byte Reload | |
| ; GISEL-NEXT: str x0, [sp, #40] // 8-byte Spill | |
| ; GISEL-NEXT: mov x0, x5 | |
| ; GISEL-NEXT: ldr x5, [sp, #40] // 8-byte Reload | |
| ; GISEL-NEXT: str x0, [sp, #48] // 8-byte Spill | |
| ; GISEL-NEXT: mov x0, x6 | |
| ; GISEL-NEXT: ldr x6, [sp, #48] // 8-byte Reload | |
| ; GISEL-NEXT: str x0, [sp, #56] // 8-byte Spill | |
| ; GISEL-NEXT: mov x8, x7 | |
| ; GISEL-NEXT: ldr x7, [sp, #56] // 8-byte Reload | |
| ; GISEL-NEXT: mov w0, #4 // =0x4 | |
| ; GISEL-NEXT: mov x9, sp | |
| ; GISEL-NEXT: str x8, [x9] | |
| ; GISEL-NEXT: bl sink | |
| ; GISEL-NEXT: ldr x30, [sp, #64] // 8-byte Reload | |
| ; GISEL-NEXT: add sp, sp, #80 | |
| ; GISEL-NEXT: ret | |
| ; | |
| ; LINUX-LABEL: call_split_ptr: | |
| ; LINUX: // %bb.0: | |
| ; LINUX-NEXT: sub sp, sp, #32 | |
| ; LINUX-NEXT: mov x8, x6 | |
| ; LINUX-NEXT: mov x6, x5 | |
| ; LINUX-NEXT: mov x5, x4 | |
| ; LINUX-NEXT: mov x4, x3 | |
| ; LINUX-NEXT: mov x3, x2 | |
| ; LINUX-NEXT: mov x2, x1 | |
| ; LINUX-NEXT: mov x1, x0 | |
| ; LINUX-NEXT: mov w0, #4 // =0x4 | |
| ; LINUX-NEXT: stp x7, x30, [sp, #8] // 8-byte Folded Spill | |
| ; LINUX-NEXT: str x8, [sp] | |
| ; LINUX-NEXT: bl sink | |
| ; LINUX-NEXT: ldr x30, [sp, #16] // 8-byte Reload | |
| ; LINUX-NEXT: add sp, sp, #32 | |
| ; LINUX-NEXT: ret | |
| call void (i32, ...) @sink(i32 4, [2 x ptr] %a, [2 x ptr] %b, [2 x ptr] %c, [2 x ptr] %d) | |
| ret void | |
| } | |
| ; A named [2 x i64] parameter of a variadic function starting at x7. | |
| define void @call_fixed_split([2 x i64] %p) nounwind { | |
| ; DAGISEL-LABEL: call_fixed_split: | |
| ; DAGISEL: // %bb.0: | |
| ; DAGISEL-NEXT: sub sp, sp, #32 | |
| ; DAGISEL-NEXT: mov x8, x1 | |
| ; DAGISEL-NEXT: mov x7, x0 | |
| ; DAGISEL-NEXT: mov w9, #42 // =0x2a | |
| ; DAGISEL-NEXT: mov w0, #1 // =0x1 | |
| ; DAGISEL-NEXT: mov w1, #2 // =0x2 | |
| ; DAGISEL-NEXT: mov w2, #3 // =0x3 | |
| ; DAGISEL-NEXT: mov w3, #4 // =0x4 | |
| ; DAGISEL-NEXT: mov w4, #5 // =0x5 | |
| ; DAGISEL-NEXT: mov w5, #6 // =0x6 | |
| ; DAGISEL-NEXT: mov w6, #7 // =0x7 | |
| ; DAGISEL-NEXT: str x30, [sp, #16] // 8-byte Spill | |
| ; DAGISEL-NEXT: str w9, [sp, #8] | |
| ; DAGISEL-NEXT: str x8, [sp] | |
| ; DAGISEL-NEXT: bl fixed_sink | |
| ; DAGISEL-NEXT: ldr x30, [sp, #16] // 8-byte Reload | |
| ; DAGISEL-NEXT: add sp, sp, #32 | |
| ; DAGISEL-NEXT: ret | |
| ; | |
| ; GISEL-LABEL: call_fixed_split: | |
| ; GISEL: // %bb.0: | |
| ; GISEL-NEXT: sub sp, sp, #32 | |
| ; GISEL-NEXT: str x30, [sp, #16] // 8-byte Spill | |
| ; GISEL-NEXT: mov x7, x0 | |
| ; GISEL-NEXT: mov x10, x1 | |
| ; GISEL-NEXT: mov w8, #1 // =0x1 | |
| ; GISEL-NEXT: mov w0, w8 | |
| ; GISEL-NEXT: mov w8, #2 // =0x2 | |
| ; GISEL-NEXT: mov w1, w8 | |
| ; GISEL-NEXT: mov w8, #3 // =0x3 | |
| ; GISEL-NEXT: mov w2, w8 | |
| ; GISEL-NEXT: mov w8, #4 // =0x4 | |
| ; GISEL-NEXT: mov w3, w8 | |
| ; GISEL-NEXT: mov w8, #5 // =0x5 | |
| ; GISEL-NEXT: mov w4, w8 | |
| ; GISEL-NEXT: mov w8, #6 // =0x6 | |
| ; GISEL-NEXT: mov w5, w8 | |
| ; GISEL-NEXT: mov w8, #7 // =0x7 | |
| ; GISEL-NEXT: mov w6, w8 | |
| ; GISEL-NEXT: mov w8, #42 // =0x2a | |
| ; GISEL-NEXT: mov x9, sp | |
| ; GISEL-NEXT: str x10, [x9] | |
| ; GISEL-NEXT: str w8, [x9, #8] | |
| ; GISEL-NEXT: bl fixed_sink | |
| ; GISEL-NEXT: ldr x30, [sp, #16] // 8-byte Reload | |
| ; GISEL-NEXT: add sp, sp, #32 | |
| ; GISEL-NEXT: ret | |
| ; | |
| ; LINUX-LABEL: call_fixed_split: | |
| ; LINUX: // %bb.0: | |
| ; LINUX-NEXT: sub sp, sp, #48 | |
| ; LINUX-NEXT: mov x8, x0 | |
| ; LINUX-NEXT: mov w9, #42 // =0x2a | |
| ; LINUX-NEXT: mov w0, #1 // =0x1 | |
| ; LINUX-NEXT: stp x8, x1, [sp] | |
| ; LINUX-NEXT: mov w1, #2 // =0x2 | |
| ; LINUX-NEXT: mov w2, #3 // =0x3 | |
| ; LINUX-NEXT: mov w3, #4 // =0x4 | |
| ; LINUX-NEXT: mov w4, #5 // =0x5 | |
| ; LINUX-NEXT: mov w5, #6 // =0x6 | |
| ; LINUX-NEXT: mov w6, #7 // =0x7 | |
| ; LINUX-NEXT: str x30, [sp, #32] // 8-byte Spill | |
| ; LINUX-NEXT: str w9, [sp, #16] | |
| ; LINUX-NEXT: bl fixed_sink | |
| ; LINUX-NEXT: ldr x30, [sp, #32] // 8-byte Reload | |
| ; LINUX-NEXT: add sp, sp, #48 | |
| ; LINUX-NEXT: ret | |
| call void (i64, i64, i64, i64, i64, i64, i64, [2 x i64], ...) @fixed_sink(i64 1, i64 2, i64 3, i64 4, i64 5, i64 6, i64 7, [2 x i64] %p, i32 42) | |
| ret void | |
| } | |
| ; The callee side of the above: the second half of %p is the first stack slot. | |
| define i64 @fixed_split_param(i64 %a0, i64 %a1, i64 %a2, i64 %a3, i64 %a4, i64 %a5, i64 %a6, [2 x i64] %p, ...) nounwind { | |
| ; DAGISEL-LABEL: fixed_split_param: | |
| ; DAGISEL: // %bb.0: | |
| ; DAGISEL-NEXT: ldr x0, [sp] | |
| ; DAGISEL-NEXT: ret | |
| ; | |
| ; GISEL-LABEL: fixed_split_param: | |
| ; GISEL: // %bb.0: | |
| ; GISEL-NEXT: ldr x0, [sp] | |
| ; GISEL-NEXT: ret | |
| ; | |
| ; LINUX-LABEL: fixed_split_param: | |
| ; LINUX: // %bb.0: | |
| ; LINUX-NEXT: ldr x0, [sp, #8] | |
| ; LINUX-NEXT: ret | |
| %hi = extractvalue [2 x i64] %p, 1 | |
| ret i64 %hi | |
| } | |
| ; ...and the variadic arguments start right after it, at the second stack slot. | |
| define ptr @fixed_split_va_start(i64 %a0, i64 %a1, i64 %a2, i64 %a3, i64 %a4, i64 %a5, i64 %a6, [2 x i64] %p, ...) nounwind { | |
| ; DAGISEL-LABEL: fixed_split_va_start: | |
| ; DAGISEL: // %bb.0: | |
| ; DAGISEL-NEXT: sub sp, sp, #16 | |
| ; DAGISEL-NEXT: add x8, sp, #24 | |
| ; DAGISEL-NEXT: add x0, sp, #24 | |
| ; DAGISEL-NEXT: str x8, [sp, #8] | |
| ; DAGISEL-NEXT: add sp, sp, #16 | |
| ; DAGISEL-NEXT: ret | |
| ; | |
| ; GISEL-LABEL: fixed_split_va_start: | |
| ; GISEL: // %bb.0: | |
| ; GISEL-NEXT: sub sp, sp, #16 | |
| ; GISEL-NEXT: add x8, sp, #24 | |
| ; GISEL-NEXT: str x8, [sp, #8] | |
| ; GISEL-NEXT: ldr x0, [sp, #8] | |
| ; GISEL-NEXT: add sp, sp, #16 | |
| ; GISEL-NEXT: ret | |
| ; | |
| ; LINUX-LABEL: fixed_split_va_start: | |
| ; LINUX: // %bb.0: | |
| ; LINUX-NEXT: sub sp, sp, #144 | |
| ; LINUX-NEXT: mov x8, sp | |
| ; LINUX-NEXT: mov x10, #-549755813888 // =0xffffff8000000000 | |
| ; LINUX-NEXT: add x9, sp, #160 | |
| ; LINUX-NEXT: add x8, x8, #128 | |
| ; LINUX-NEXT: stp q1, q2, [sp, #16] | |
| ; LINUX-NEXT: mov x0, x9 | |
| ; LINUX-NEXT: str q0, [sp] | |
| ; LINUX-NEXT: stp q3, q4, [sp, #48] | |
| ; LINUX-NEXT: str x9, [sp, #136] | |
| ; LINUX-NEXT: stp x8, x10, [sp, #152] | |
| ; LINUX-NEXT: stp q5, q6, [sp, #80] | |
| ; LINUX-NEXT: str q7, [sp, #112] | |
| ; LINUX-NEXT: add sp, sp, #144 | |
| ; LINUX-NEXT: ret | |
| %ap = alloca ptr, align 8 | |
| call void @llvm.va_start(ptr %ap) | |
| %list = load ptr, ptr %ap, align 8 | |
| call void @llvm.va_end(ptr %ap) | |
| ret ptr %list | |
| } | |
| ; Unchanged: two registers left, the block fits in x6 and x7. | |
| define void @call_fits([2 x i64] %a, [2 x i64] %b, [2 x i64] %c) nounwind { | |
| ; DAGISEL-LABEL: call_fits: | |
| ; DAGISEL: // %bb.0: | |
| ; DAGISEL-NEXT: str x30, [sp, #-16]! // 8-byte Folded Spill | |
| ; DAGISEL-NEXT: mov x7, x5 | |
| ; DAGISEL-NEXT: mov x6, x4 | |
| ; DAGISEL-NEXT: mov x5, x3 | |
| ; DAGISEL-NEXT: mov x4, x2 | |
| ; DAGISEL-NEXT: mov x3, x1 | |
| ; DAGISEL-NEXT: mov x2, x0 | |
| ; DAGISEL-NEXT: mov w0, #3 // =0x3 | |
| ; DAGISEL-NEXT: mov x1, xzr | |
| ; DAGISEL-NEXT: bl sink | |
| ; DAGISEL-NEXT: ldr x30, [sp], #16 // 8-byte Folded Reload | |
| ; DAGISEL-NEXT: ret | |
| ; | |
| ; GISEL-LABEL: call_fits: | |
| ; GISEL: // %bb.0: | |
| ; GISEL-NEXT: sub sp, sp, #48 | |
| ; GISEL-NEXT: str x30, [sp, #32] // 8-byte Spill | |
| ; GISEL-NEXT: str x0, [sp] // 8-byte Spill | |
| ; GISEL-NEXT: str x1, [sp, #8] // 8-byte Spill | |
| ; GISEL-NEXT: mov x0, x2 | |
| ; GISEL-NEXT: ldr x2, [sp] // 8-byte Reload | |
| ; GISEL-NEXT: str x0, [sp, #16] // 8-byte Spill | |
| ; GISEL-NEXT: mov x0, x3 | |
| ; GISEL-NEXT: ldr x3, [sp, #8] // 8-byte Reload | |
| ; GISEL-NEXT: str x0, [sp, #24] // 8-byte Spill | |
| ; GISEL-NEXT: mov x6, x4 | |
| ; GISEL-NEXT: ldr x4, [sp, #16] // 8-byte Reload | |
| ; GISEL-NEXT: mov x7, x5 | |
| ; GISEL-NEXT: ldr x5, [sp, #24] // 8-byte Reload | |
| ; GISEL-NEXT: mov w0, #3 // =0x3 | |
| ; GISEL-NEXT: mov x1, xzr | |
| ; GISEL-NEXT: bl sink | |
| ; GISEL-NEXT: ldr x30, [sp, #32] // 8-byte Reload | |
| ; GISEL-NEXT: add sp, sp, #48 | |
| ; GISEL-NEXT: ret | |
| ; | |
| ; LINUX-LABEL: call_fits: | |
| ; LINUX: // %bb.0: | |
| ; LINUX-NEXT: str x30, [sp, #-16]! // 8-byte Folded Spill | |
| ; LINUX-NEXT: mov x7, x5 | |
| ; LINUX-NEXT: mov x6, x4 | |
| ; LINUX-NEXT: mov x5, x3 | |
| ; LINUX-NEXT: mov x4, x2 | |
| ; LINUX-NEXT: mov x3, x1 | |
| ; LINUX-NEXT: mov x2, x0 | |
| ; LINUX-NEXT: mov w0, #3 // =0x3 | |
| ; LINUX-NEXT: mov x1, xzr | |
| ; LINUX-NEXT: bl sink | |
| ; LINUX-NEXT: ldr x30, [sp], #16 // 8-byte Folded Reload | |
| ; LINUX-NEXT: ret | |
| call void (i32, ...) @sink(i32 3, i64 0, [2 x i64] %a, [2 x i64] %b, [2 x i64] %c) | |
| ret void | |
| } | |
| ; Unchanged: no register left, the block goes entirely on the stack. | |
| define void @call_no_regs_left([2 x i64] %a, [2 x i64] %b, [2 x i64] %c, [2 x i64] %d) nounwind { | |
| ; DAGISEL-LABEL: call_no_regs_left: | |
| ; DAGISEL: // %bb.0: | |
| ; DAGISEL-NEXT: sub sp, sp, #32 | |
| ; DAGISEL-NEXT: mov x9, x5 | |
| ; DAGISEL-NEXT: stp x7, x30, [sp, #8] // 8-byte Folded Spill | |
| ; DAGISEL-NEXT: mov x8, x6 | |
| ; DAGISEL-NEXT: mov x6, x4 | |
| ; DAGISEL-NEXT: mov x5, x3 | |
| ; DAGISEL-NEXT: mov x4, x2 | |
| ; DAGISEL-NEXT: mov x3, x1 | |
| ; DAGISEL-NEXT: mov x2, x0 | |
| ; DAGISEL-NEXT: mov x1, xzr | |
| ; DAGISEL-NEXT: mov w0, #4 // =0x4 | |
| ; DAGISEL-NEXT: mov x7, x9 | |
| ; DAGISEL-NEXT: str x8, [sp] | |
| ; DAGISEL-NEXT: bl sink | |
| ; DAGISEL-NEXT: ldr x30, [sp, #16] // 8-byte Reload | |
| ; DAGISEL-NEXT: add sp, sp, #32 | |
| ; DAGISEL-NEXT: ret | |
| ; | |
| ; GISEL-LABEL: call_no_regs_left: | |
| ; GISEL: // %bb.0: | |
| ; GISEL-NEXT: sub sp, sp, #80 | |
| ; GISEL-NEXT: str x30, [sp, #64] // 8-byte Spill | |
| ; GISEL-NEXT: str x0, [sp, #16] // 8-byte Spill | |
| ; GISEL-NEXT: str x1, [sp, #24] // 8-byte Spill | |
| ; GISEL-NEXT: mov x0, x2 | |
| ; GISEL-NEXT: ldr x2, [sp, #16] // 8-byte Reload | |
| ; GISEL-NEXT: str x0, [sp, #32] // 8-byte Spill | |
| ; GISEL-NEXT: mov x0, x3 | |
| ; GISEL-NEXT: ldr x3, [sp, #24] // 8-byte Reload | |
| ; GISEL-NEXT: str x0, [sp, #40] // 8-byte Spill | |
| ; GISEL-NEXT: mov x0, x4 | |
| ; GISEL-NEXT: ldr x4, [sp, #32] // 8-byte Reload | |
| ; GISEL-NEXT: str x0, [sp, #48] // 8-byte Spill | |
| ; GISEL-NEXT: mov x0, x5 | |
| ; GISEL-NEXT: ldr x5, [sp, #40] // 8-byte Reload | |
| ; GISEL-NEXT: str x0, [sp, #56] // 8-byte Spill | |
| ; GISEL-NEXT: mov x10, x6 | |
| ; GISEL-NEXT: ldr x6, [sp, #48] // 8-byte Reload | |
| ; GISEL-NEXT: mov x8, x7 | |
| ; GISEL-NEXT: ldr x7, [sp, #56] // 8-byte Reload | |
| ; GISEL-NEXT: mov w0, #4 // =0x4 | |
| ; GISEL-NEXT: mov x1, xzr | |
| ; GISEL-NEXT: mov x9, sp | |
| ; GISEL-NEXT: str x10, [x9] | |
| ; GISEL-NEXT: str x8, [x9, #8] | |
| ; GISEL-NEXT: bl sink | |
| ; GISEL-NEXT: ldr x30, [sp, #64] // 8-byte Reload | |
| ; GISEL-NEXT: add sp, sp, #80 | |
| ; GISEL-NEXT: ret | |
| ; | |
| ; LINUX-LABEL: call_no_regs_left: | |
| ; LINUX: // %bb.0: | |
| ; LINUX-NEXT: sub sp, sp, #32 | |
| ; LINUX-NEXT: mov x9, x5 | |
| ; LINUX-NEXT: stp x7, x30, [sp, #8] // 8-byte Folded Spill | |
| ; LINUX-NEXT: mov x8, x6 | |
| ; LINUX-NEXT: mov x6, x4 | |
| ; LINUX-NEXT: mov x5, x3 | |
| ; LINUX-NEXT: mov x4, x2 | |
| ; LINUX-NEXT: mov x3, x1 | |
| ; LINUX-NEXT: mov x2, x0 | |
| ; LINUX-NEXT: mov x1, xzr | |
| ; LINUX-NEXT: mov w0, #4 // =0x4 | |
| ; LINUX-NEXT: mov x7, x9 | |
| ; LINUX-NEXT: str x8, [sp] | |
| ; LINUX-NEXT: bl sink | |
| ; LINUX-NEXT: ldr x30, [sp, #16] // 8-byte Reload | |
| ; LINUX-NEXT: add sp, sp, #32 | |
| ; LINUX-NEXT: ret | |
| call void (i32, ...) @sink(i32 4, i64 0, [2 x i64] %a, [2 x i64] %b, [2 x i64] %c, [2 x i64] %d) | |
| ret void | |
| } | |
| ; Unchanged: i128, which clang uses for a 16-byte aligned struct, starts at an | |
| ; even register, so with only x7 left it goes on the stack, 16-byte aligned, | |
| ; and x7 stays unused. | |
| define void @call_i128(i128 %a) nounwind { | |
| ; DAGISEL-LABEL: call_i128: | |
| ; DAGISEL: // %bb.0: | |
| ; DAGISEL-NEXT: sub sp, sp, #48 | |
| ; DAGISEL-NEXT: mov w9, #99 // =0x63 | |
| ; DAGISEL-NEXT: mov x8, x0 | |
| ; DAGISEL-NEXT: mov w0, wzr | |
| ; DAGISEL-NEXT: stp x1, x9, [sp, #8] | |
| ; DAGISEL-NEXT: mov w1, #1 // =0x1 | |
| ; DAGISEL-NEXT: mov w2, #2 // =0x2 | |
| ; DAGISEL-NEXT: mov w3, #3 // =0x3 | |
| ; DAGISEL-NEXT: mov w4, #4 // =0x4 | |
| ; DAGISEL-NEXT: mov w5, #5 // =0x5 | |
| ; DAGISEL-NEXT: mov w6, #6 // =0x6 | |
| ; DAGISEL-NEXT: str x30, [sp, #32] // 8-byte Spill | |
| ; DAGISEL-NEXT: str x8, [sp] | |
| ; DAGISEL-NEXT: bl sink | |
| ; DAGISEL-NEXT: ldr x30, [sp, #32] // 8-byte Reload | |
| ; DAGISEL-NEXT: add sp, sp, #48 | |
| ; DAGISEL-NEXT: ret | |
| ; | |
| ; GISEL-LABEL: call_i128: | |
| ; GISEL: // %bb.0: | |
| ; GISEL-NEXT: sub sp, sp, #48 | |
| ; GISEL-NEXT: str x30, [sp, #32] // 8-byte Spill | |
| ; GISEL-NEXT: mov x11, x0 | |
| ; GISEL-NEXT: mov x10, x1 | |
| ; GISEL-NEXT: mov w0, wzr | |
| ; GISEL-NEXT: mov w8, #1 // =0x1 | |
| ; GISEL-NEXT: mov w1, w8 | |
| ; GISEL-NEXT: mov w8, #2 // =0x2 | |
| ; GISEL-NEXT: mov w2, w8 | |
| ; GISEL-NEXT: mov w8, #3 // =0x3 | |
| ; GISEL-NEXT: mov w3, w8 | |
| ; GISEL-NEXT: mov w8, #4 // =0x4 | |
| ; GISEL-NEXT: mov w4, w8 | |
| ; GISEL-NEXT: mov w8, #5 // =0x5 | |
| ; GISEL-NEXT: mov w5, w8 | |
| ; GISEL-NEXT: mov w8, #6 // =0x6 | |
| ; GISEL-NEXT: mov w6, w8 | |
| ; GISEL-NEXT: mov w8, #99 // =0x63 | |
| ; GISEL-NEXT: // kill: def $x8 killed $w8 | |
| ; GISEL-NEXT: mov x9, sp | |
| ; GISEL-NEXT: str x11, [x9] | |
| ; GISEL-NEXT: str x10, [x9, #8] | |
| ; GISEL-NEXT: str x8, [x9, #16] | |
| ; GISEL-NEXT: bl sink | |
| ; GISEL-NEXT: ldr x30, [sp, #32] // 8-byte Reload | |
| ; GISEL-NEXT: add sp, sp, #48 | |
| ; GISEL-NEXT: ret | |
| ; | |
| ; LINUX-LABEL: call_i128: | |
| ; LINUX: // %bb.0: | |
| ; LINUX-NEXT: sub sp, sp, #48 | |
| ; LINUX-NEXT: mov w9, #99 // =0x63 | |
| ; LINUX-NEXT: mov x8, x0 | |
| ; LINUX-NEXT: mov w0, wzr | |
| ; LINUX-NEXT: stp x1, x9, [sp, #8] | |
| ; LINUX-NEXT: mov w1, #1 // =0x1 | |
| ; LINUX-NEXT: mov w2, #2 // =0x2 | |
| ; LINUX-NEXT: mov w3, #3 // =0x3 | |
| ; LINUX-NEXT: mov w4, #4 // =0x4 | |
| ; LINUX-NEXT: mov w5, #5 // =0x5 | |
| ; LINUX-NEXT: mov w6, #6 // =0x6 | |
| ; LINUX-NEXT: str x30, [sp, #32] // 8-byte Spill | |
| ; LINUX-NEXT: str x8, [sp] | |
| ; LINUX-NEXT: bl sink | |
| ; LINUX-NEXT: ldr x30, [sp, #32] // 8-byte Reload | |
| ; LINUX-NEXT: add sp, sp, #48 | |
| ; LINUX-NEXT: ret | |
| call void (i32, ...) @sink(i32 0, i64 1, i64 2, i64 3, i64 4, i64 5, i64 6, i128 %a, i64 99) | |
| ret void | |
| } | |
| ; Unchanged: byval is only lowered as a stack argument, so a byval array is | |
| ; still copied to the stack rather than split into registers. | |
| define void @call_byval(ptr %p) nounwind { | |
| ; DAGISEL-LABEL: call_byval: | |
| ; DAGISEL: // %bb.0: | |
| ; DAGISEL-NEXT: sub sp, sp, #32 | |
| ; DAGISEL-NEXT: str x30, [sp, #16] // 8-byte Spill | |
| ; DAGISEL-NEXT: mov w1, #1 // =0x1 | |
| ; DAGISEL-NEXT: mov w2, #2 // =0x2 | |
| ; DAGISEL-NEXT: ldr q0, [x0] | |
| ; DAGISEL-NEXT: mov w0, wzr | |
| ; DAGISEL-NEXT: mov w3, #3 // =0x3 | |
| ; DAGISEL-NEXT: mov w4, #4 // =0x4 | |
| ; DAGISEL-NEXT: mov w5, #5 // =0x5 | |
| ; DAGISEL-NEXT: mov w6, #6 // =0x6 | |
| ; DAGISEL-NEXT: str q0, [sp] | |
| ; DAGISEL-NEXT: bl sink | |
| ; DAGISEL-NEXT: ldr x30, [sp, #16] // 8-byte Reload | |
| ; DAGISEL-NEXT: add sp, sp, #32 | |
| ; DAGISEL-NEXT: ret | |
| ; | |
| ; GISEL-LABEL: call_byval: | |
| ; GISEL: // %bb.0: | |
| ; GISEL-NEXT: sub sp, sp, #32 | |
| ; GISEL-NEXT: str x30, [sp, #16] // 8-byte Spill | |
| ; GISEL-NEXT: mov x9, x0 | |
| ; GISEL-NEXT: mov w0, wzr | |
| ; GISEL-NEXT: mov w8, #1 // =0x1 | |
| ; GISEL-NEXT: mov w1, w8 | |
| ; GISEL-NEXT: mov w8, #2 // =0x2 | |
| ; GISEL-NEXT: mov w2, w8 | |
| ; GISEL-NEXT: mov w8, #3 // =0x3 | |
| ; GISEL-NEXT: mov w3, w8 | |
| ; GISEL-NEXT: mov w8, #4 // =0x4 | |
| ; GISEL-NEXT: mov w4, w8 | |
| ; GISEL-NEXT: mov w8, #5 // =0x5 | |
| ; GISEL-NEXT: mov w5, w8 | |
| ; GISEL-NEXT: mov w8, #6 // =0x6 | |
| ; GISEL-NEXT: mov w6, w8 | |
| ; GISEL-NEXT: mov x8, sp | |
| ; GISEL-NEXT: ldr q0, [x9] | |
| ; GISEL-NEXT: str q0, [x8] | |
| ; GISEL-NEXT: bl sink | |
| ; GISEL-NEXT: ldr x30, [sp, #16] // 8-byte Reload | |
| ; GISEL-NEXT: add sp, sp, #32 | |
| ; GISEL-NEXT: ret | |
| ; | |
| ; LINUX-LABEL: call_byval: | |
| ; LINUX: // %bb.0: | |
| ; LINUX-NEXT: sub sp, sp, #32 | |
| ; LINUX-NEXT: str x30, [sp, #16] // 8-byte Spill | |
| ; LINUX-NEXT: mov w1, #1 // =0x1 | |
| ; LINUX-NEXT: mov w2, #2 // =0x2 | |
| ; LINUX-NEXT: ldr q0, [x0] | |
| ; LINUX-NEXT: mov w0, wzr | |
| ; LINUX-NEXT: mov w3, #3 // =0x3 | |
| ; LINUX-NEXT: mov w4, #4 // =0x4 | |
| ; LINUX-NEXT: mov w5, #5 // =0x5 | |
| ; LINUX-NEXT: mov w6, #6 // =0x6 | |
| ; LINUX-NEXT: str q0, [sp] | |
| ; LINUX-NEXT: bl sink | |
| ; LINUX-NEXT: ldr x30, [sp, #16] // 8-byte Reload | |
| ; LINUX-NEXT: add sp, sp, #32 | |
| ; LINUX-NEXT: ret | |
| call void (i32, ...) @sink(i32 0, i64 1, i64 2, i64 3, i64 4, i64 5, i64 6, ptr byval([2 x i64]) align 8 %p) | |
| ret void | |
| } | |
| declare void @llvm.va_start(ptr) | |
| declare void @llvm.va_end(ptr) |