blob: d5b88659994bdd64404148fda61c4aec1be388cd [file] [edit]
; 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)