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 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188
|
; NOTE: Assertions have been autogenerated by utils/update_llc_test_checks.py UTC_ARGS: --version 4
; RUN: llc -mtriple=x86_64-pc-linux -stackrealign -verify-machineinstrs < %s | FileCheck %s
; Calling convention ghccc uses ebp to pass parameter, so calling a function
; using ghccc clobbers ebp. We should save and restore ebp around such a call
; if ebp is used as frame pointer.
declare ghccc i32 @external(i32)
; Basic test with ghccc calling convention.
define i32 @test1(i32 %0, i32 %1) {
; CHECK-LABEL: test1:
; CHECK: # %bb.0:
; CHECK-NEXT: pushq %rbp
; CHECK-NEXT: .cfi_def_cfa_offset 16
; CHECK-NEXT: .cfi_offset %rbp, -16
; CHECK-NEXT: movq %rsp, %rbp
; CHECK-NEXT: .cfi_def_cfa_register %rbp
; CHECK-NEXT: pushq %r15
; CHECK-NEXT: pushq %r14
; CHECK-NEXT: pushq %r13
; CHECK-NEXT: pushq %r12
; CHECK-NEXT: pushq %rbx
; CHECK-NEXT: andq $-16, %rsp
; CHECK-NEXT: subq $16, %rsp
; CHECK-NEXT: .cfi_offset %rbx, -56
; CHECK-NEXT: .cfi_offset %r12, -48
; CHECK-NEXT: .cfi_offset %r13, -40
; CHECK-NEXT: .cfi_offset %r14, -32
; CHECK-NEXT: .cfi_offset %r15, -24
; CHECK-NEXT: # kill: def $edi killed $edi def $rdi
; CHECK-NEXT: pushq %rbp
; CHECK-NEXT: pushq %rax
; CHECK-NEXT: .cfi_remember_state
; CHECK-NEXT: .cfi_escape 0x0f, 0x06, 0x77, 0x08, 0x06, 0x11, 0x10, 0x22 #
; CHECK-NEXT: movl %esi, %ebp
; CHECK-NEXT: movq %rdi, %r13
; CHECK-NEXT: callq external@PLT
; CHECK-NEXT: addq $8, %rsp
; CHECK-NEXT: popq %rbp
; CHECK-NEXT: .cfi_restore_state
; CHECK-NEXT: leaq -40(%rbp), %rsp
; CHECK-NEXT: popq %rbx
; CHECK-NEXT: popq %r12
; CHECK-NEXT: popq %r13
; CHECK-NEXT: popq %r14
; CHECK-NEXT: popq %r15
; CHECK-NEXT: popq %rbp
; CHECK-NEXT: .cfi_def_cfa %rsp, 8
; CHECK-NEXT: retq
%x = call ghccc i32 @external(i32 %0, i32 %1)
ret i32 %x
}
; Calling convention hipe has similar behavior. It clobbers rbp but not rbx.
declare cc 11 i64 @hipe1(i64)
declare cc 11 i64 @hipe2(i64, i64, i64, i64, i64, i64, i64)
; Basic test with hipe calling convention.
define i64 @test2(i64 %a0, i64 %a1) {
; CHECK-LABEL: test2:
; CHECK: # %bb.0:
; CHECK-NEXT: pushq %rbp
; CHECK-NEXT: .cfi_def_cfa_offset 16
; CHECK-NEXT: .cfi_offset %rbp, -16
; CHECK-NEXT: movq %rsp, %rbp
; CHECK-NEXT: .cfi_def_cfa_register %rbp
; CHECK-NEXT: pushq %r15
; CHECK-NEXT: pushq %r14
; CHECK-NEXT: pushq %r13
; CHECK-NEXT: pushq %r12
; CHECK-NEXT: pushq %rbx
; CHECK-NEXT: andq $-16, %rsp
; CHECK-NEXT: subq $16, %rsp
; CHECK-NEXT: .cfi_offset %rbx, -56
; CHECK-NEXT: .cfi_offset %r12, -48
; CHECK-NEXT: .cfi_offset %r13, -40
; CHECK-NEXT: .cfi_offset %r14, -32
; CHECK-NEXT: .cfi_offset %r15, -24
; CHECK-NEXT: pushq %rbp
; CHECK-NEXT: pushq %rax
; CHECK-NEXT: .cfi_remember_state
; CHECK-NEXT: .cfi_escape 0x0f, 0x06, 0x77, 0x08, 0x06, 0x11, 0x10, 0x22 #
; CHECK-NEXT: movq %rsi, %rbp
; CHECK-NEXT: movq %rdi, %r15
; CHECK-NEXT: callq hipe1@PLT
; CHECK-NEXT: addq $8, %rsp
; CHECK-NEXT: popq %rbp
; CHECK-NEXT: .cfi_restore_state
; CHECK-NEXT: movq %r15, %rax
; CHECK-NEXT: leaq -40(%rbp), %rsp
; CHECK-NEXT: popq %rbx
; CHECK-NEXT: popq %r12
; CHECK-NEXT: popq %r13
; CHECK-NEXT: popq %r14
; CHECK-NEXT: popq %r15
; CHECK-NEXT: popq %rbp
; CHECK-NEXT: .cfi_def_cfa %rsp, 8
; CHECK-NEXT: retq
%x = call cc 11 i64 @hipe1(i64 %a0, i64 %a1)
ret i64 %x
}
@buf = dso_local global [20 x ptr] zeroinitializer, align 16
; longjmp modifies fp, it is expected behavior, wo should not save/restore fp
; around it.
define void @test4() {
; CHECK-LABEL: test4:
; CHECK: # %bb.0: # %entry
; CHECK-NEXT: pushq %rbp
; CHECK-NEXT: .cfi_def_cfa_offset 16
; CHECK-NEXT: .cfi_offset %rbp, -16
; CHECK-NEXT: movq %rsp, %rbp
; CHECK-NEXT: .cfi_def_cfa_register %rbp
; CHECK-NEXT: pushq %r15
; CHECK-NEXT: pushq %r14
; CHECK-NEXT: pushq %r13
; CHECK-NEXT: pushq %r12
; CHECK-NEXT: pushq %rbx
; CHECK-NEXT: andq $-16, %rsp
; CHECK-NEXT: subq $16, %rsp
; CHECK-NEXT: .cfi_offset %rbx, -56
; CHECK-NEXT: .cfi_offset %r12, -48
; CHECK-NEXT: .cfi_offset %r13, -40
; CHECK-NEXT: .cfi_offset %r14, -32
; CHECK-NEXT: .cfi_offset %r15, -24
; CHECK-NEXT: xorl %r13d, %r13d
; CHECK-NEXT: pushq %rbp
; CHECK-NEXT: pushq %rax
; CHECK-NEXT: .cfi_remember_state
; CHECK-NEXT: .cfi_escape 0x0f, 0x06, 0x77, 0x08, 0x06, 0x11, 0x10, 0x22 #
; CHECK-NEXT: callq external@PLT
; CHECK-NEXT: addq $8, %rsp
; CHECK-NEXT: popq %rbp
; CHECK-NEXT: .cfi_restore_state
; CHECK-NEXT: movq buf(%rip), %rbp
; CHECK-NEXT: movq buf+8(%rip), %rax
; CHECK-NEXT: movq buf+16(%rip), %rsp
; CHECK-NEXT: jmpq *%rax
entry:
%x = call ghccc i32 @external(i32 0)
call void @llvm.eh.sjlj.longjmp(ptr @buf)
unreachable
}
declare ghccc void @tail()
; We should not save/restore fp/bp around terminator.
define ghccc void @test5() {
; CHECK-LABEL: test5:
; CHECK: # %bb.0: # %entry
; CHECK-NEXT: pushq %rbp
; CHECK-NEXT: .cfi_def_cfa_offset 16
; CHECK-NEXT: .cfi_offset %rbp, -16
; CHECK-NEXT: movq %rsp, %rbp
; CHECK-NEXT: .cfi_def_cfa_register %rbp
; CHECK-NEXT: andq $-8, %rsp
; CHECK-NEXT: xorl %eax, %eax
; CHECK-NEXT: testb %al, %al
; CHECK-NEXT: jne .LBB3_2
; CHECK-NEXT: # %bb.1: # %then
; CHECK-NEXT: movq $0, (%rax)
; CHECK-NEXT: movq %rbp, %rsp
; CHECK-NEXT: popq %rbp
; CHECK-NEXT: .cfi_def_cfa %rsp, 8
; CHECK-NEXT: retq
; CHECK-NEXT: .LBB3_2: # %else
; CHECK-NEXT: .cfi_def_cfa %rbp, 16
; CHECK-NEXT: movq %rbp, %rsp
; CHECK-NEXT: popq %rbp
; CHECK-NEXT: .cfi_def_cfa %rsp, 8
; CHECK-NEXT: jmp tail@PLT # TAILCALL
entry:
br i1 poison, label %then, label %else
then:
store i64 0, ptr undef
br label %exit
else:
musttail call ghccc void @tail()
ret void
exit:
ret void
}
|