; 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: 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 }