; NOTE: Assertions have been autogenerated by utils/update_llc_test_checks.py UTC_ARGS: --version 4 ; RUN: llc -mtriple=x86_64-unknown-unknown -mcpu=corei7 < %s | FileCheck %s ; This test checks various function call behaviors between preserve_none and ; normal calling conventions. declare preserve_nonecc void @callee(ptr) ; Normal caller calls preserve_none callee. Will not generated tail call because ; of incompatible calling convention. Callee saved registers are saved/restored ; around the call. define void @caller1(ptr %a) { ; CHECK-LABEL: caller1: ; CHECK: # %bb.0: ; CHECK-NEXT: pushq %r15 ; CHECK-NEXT: .cfi_def_cfa_offset 16 ; CHECK-NEXT: pushq %r14 ; CHECK-NEXT: .cfi_def_cfa_offset 24 ; CHECK-NEXT: pushq %r13 ; CHECK-NEXT: .cfi_def_cfa_offset 32 ; CHECK-NEXT: pushq %r12 ; CHECK-NEXT: .cfi_def_cfa_offset 40 ; CHECK-NEXT: pushq %rbx ; CHECK-NEXT: .cfi_def_cfa_offset 48 ; CHECK-NEXT: .cfi_offset %rbx, -48 ; CHECK-NEXT: .cfi_offset %r12, -40 ; CHECK-NEXT: .cfi_offset %r13, -32 ; CHECK-NEXT: .cfi_offset %r14, -24 ; CHECK-NEXT: .cfi_offset %r15, -16 ; CHECK-NEXT: movq %rdi, %r12 ; CHECK-NEXT: callq callee@PLT ; CHECK-NEXT: popq %rbx ; CHECK-NEXT: .cfi_def_cfa_offset 40 ; CHECK-NEXT: popq %r12 ; CHECK-NEXT: .cfi_def_cfa_offset 32 ; CHECK-NEXT: popq %r13 ; CHECK-NEXT: .cfi_def_cfa_offset 24 ; CHECK-NEXT: popq %r14 ; CHECK-NEXT: .cfi_def_cfa_offset 16 ; CHECK-NEXT: popq %r15 ; CHECK-NEXT: .cfi_def_cfa_offset 8 ; CHECK-NEXT: retq tail call preserve_nonecc void @callee(ptr %a) ret void } ; Preserve_none caller calls preserve_none callee. Same function body. ; The tail call is preserved. No registers are saved/restored around the call. ; Actually a simple jmp instruction is generated. define preserve_nonecc void @caller2(ptr %a) { ; CHECK-LABEL: caller2: ; CHECK: # %bb.0: ; CHECK-NEXT: jmp callee@PLT # TAILCALL tail call preserve_nonecc void @callee(ptr %a) ret void } %S = type { [3 x i64] } declare preserve_nonecc void @callee_byval(ptr byval(%S) align 8) ; Preserve_none caller with a byval argument from a local alloca should not ; be lowered as a sibling tail call, as this would corrupt the stack frame ; containing the byval copy. define preserve_nonecc void @caller_byval_alloca() { ; CHECK-LABEL: caller_byval_alloca: ; CHECK: # %bb.0: ; CHECK-NEXT: subq $56, %rsp ; CHECK-NEXT: .cfi_def_cfa_offset 64 ; CHECK-NEXT: xorps %xmm0, %xmm0 ; CHECK-NEXT: movups %xmm0, {{[0-9]+}}(%rsp) ; CHECK-NEXT: movq $0, {{[0-9]+}}(%rsp) ; CHECK-NEXT: movups %xmm0, (%rsp) ; CHECK-NEXT: movq $0, {{[0-9]+}}(%rsp) ; CHECK-NEXT: callq callee_byval@PLT ; CHECK-NEXT: addq $56, %rsp ; CHECK-NEXT: .cfi_def_cfa_offset 8 ; CHECK-NEXT: retq %s = alloca %S, align 8 %a0 = getelementptr inbounds %S, ptr %s, i32 0, i32 0, i32 0 store i64 0, ptr %a0, align 8 %a1 = getelementptr inbounds %S, ptr %s, i32 0, i32 0, i32 1 store i64 0, ptr %a1, align 8 %a2 = getelementptr inbounds %S, ptr %s, i32 0, i32 0, i32 2 store i64 0, ptr %a2, align 8 tail call preserve_nonecc void @callee_byval(ptr byval(%S) align 8 %s) ret void } ; Test that byval arguments from an incoming pointer are correctly copied. define preserve_nonecc void @caller_byval_ptr(ptr %a) { ; CHECK-LABEL: caller_byval_ptr: ; CHECK: # %bb.0: ; CHECK-NEXT: subq $24, %rsp ; CHECK-NEXT: .cfi_def_cfa_offset 32 ; CHECK-NEXT: movq 16(%r12), %rax ; CHECK-NEXT: movq %rax, {{[0-9]+}}(%rsp) ; CHECK-NEXT: movups (%r12), %xmm0 ; CHECK-NEXT: movups %xmm0, (%rsp) ; CHECK-NEXT: callq callee_byval@PLT ; CHECK-NEXT: addq $24, %rsp ; CHECK-NEXT: .cfi_def_cfa_offset 8 ; CHECK-NEXT: retq call preserve_nonecc void @callee_byval(ptr byval(%S) align 8 %a) ret void } ; Preserve_none function can use more registers to pass parameters. declare preserve_nonecc i64 @callee_with_many_param2(i64 %a1, i64 %a2, i64 %a3, i64 %a4, i64 %a5, i64 %a6, i64 %a7, i64 %a8, i64 %a9, i64 %a10, i64 %a11) define preserve_nonecc i64 @callee_with_many_param(i64 %a1, i64 %a2, i64 %a3, i64 %a4, i64 %a5, i64 %a6, i64 %a7, i64 %a8, i64 %a9, i64 %a10, i64 %a11, i64 %a12) { ; CHECK-LABEL: callee_with_many_param: ; CHECK: # %bb.0: ; CHECK-NEXT: pushq %rax ; CHECK-NEXT: .cfi_def_cfa_offset 16 ; CHECK-NEXT: movq %r13, %r12 ; CHECK-NEXT: movq %r14, %r13 ; CHECK-NEXT: movq %r15, %r14 ; CHECK-NEXT: movq %rdi, %r15 ; CHECK-NEXT: movq %rsi, %rdi ; CHECK-NEXT: movq %rdx, %rsi ; CHECK-NEXT: movq %rcx, %rdx ; CHECK-NEXT: movq %r8, %rcx ; CHECK-NEXT: movq %r9, %r8 ; CHECK-NEXT: movq %r11, %r9 ; CHECK-NEXT: movq %rax, %r11 ; CHECK-NEXT: callq callee_with_many_param2@PLT ; CHECK-NEXT: popq %rcx ; CHECK-NEXT: .cfi_def_cfa_offset 8 ; CHECK-NEXT: retq %ret = call preserve_nonecc i64 @callee_with_many_param2(i64 %a2, i64 %a3, i64 %a4, i64 %a5, i64 %a6, i64 %a7, i64 %a8, i64 %a9, i64 %a10, i64 %a11, i64 %a12) ret i64 %ret } define i64 @caller3() { ; CHECK-LABEL: caller3: ; CHECK: # %bb.0: ; CHECK-NEXT: pushq %r15 ; CHECK-NEXT: .cfi_def_cfa_offset 16 ; CHECK-NEXT: pushq %r14 ; CHECK-NEXT: .cfi_def_cfa_offset 24 ; CHECK-NEXT: pushq %r13 ; CHECK-NEXT: .cfi_def_cfa_offset 32 ; CHECK-NEXT: pushq %r12 ; CHECK-NEXT: .cfi_def_cfa_offset 40 ; CHECK-NEXT: pushq %rbx ; CHECK-NEXT: .cfi_def_cfa_offset 48 ; CHECK-NEXT: .cfi_offset %rbx, -48 ; CHECK-NEXT: .cfi_offset %r12, -40 ; CHECK-NEXT: .cfi_offset %r13, -32 ; CHECK-NEXT: .cfi_offset %r14, -24 ; CHECK-NEXT: .cfi_offset %r15, -16 ; CHECK-NEXT: movl $1, %r12d ; CHECK-NEXT: movl $2, %r13d ; CHECK-NEXT: movl $3, %r14d ; CHECK-NEXT: movl $4, %r15d ; CHECK-NEXT: movl $5, %edi ; CHECK-NEXT: movl $6, %esi ; CHECK-NEXT: movl $7, %edx ; CHECK-NEXT: movl $8, %ecx ; CHECK-NEXT: movl $9, %r8d ; CHECK-NEXT: movl $10, %r9d ; CHECK-NEXT: movl $11, %r11d ; CHECK-NEXT: movl $12, %eax ; CHECK-NEXT: callq callee_with_many_param@PLT ; CHECK-NEXT: popq %rbx ; CHECK-NEXT: .cfi_def_cfa_offset 40 ; CHECK-NEXT: popq %r12 ; CHECK-NEXT: .cfi_def_cfa_offset 32 ; CHECK-NEXT: popq %r13 ; CHECK-NEXT: .cfi_def_cfa_offset 24 ; CHECK-NEXT: popq %r14 ; CHECK-NEXT: .cfi_def_cfa_offset 16 ; CHECK-NEXT: popq %r15 ; CHECK-NEXT: .cfi_def_cfa_offset 8 ; CHECK-NEXT: retq %ret = call preserve_nonecc i64 @callee_with_many_param(i64 1, i64 2, i64 3, i64 4, i64 5, i64 6, i64 7, i64 8, i64 9, i64 10, i64 11, i64 12) ret i64 %ret } ; Non-volatile registers are used to pass the first few parameters. declare void @boring() declare preserve_nonecc void @continuation(ptr, ptr, ptr, ptr) define preserve_nonecc void @entry(ptr %r12, ptr %r13, ptr %r14, ptr %r15) { ; CHECK-LABEL: entry: ; CHECK: # %bb.0: ; CHECK-NEXT: pushq %rax ; CHECK-NEXT: .cfi_def_cfa_offset 16 ; CHECK-NEXT: callq boring@PLT ; CHECK-NEXT: popq %rax ; CHECK-NEXT: .cfi_def_cfa_offset 8 ; CHECK-NEXT: jmp continuation@PLT # TAILCALL call void @boring() musttail call preserve_nonecc void @continuation(ptr %r12, ptr %r13, ptr %r14, ptr %r15) ret void }