1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145
|
; 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
}
; 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
}
|