1; NOTE: Assertions have been autogenerated by utils/update_llc_test_checks.py UTC_ARGS: --version 4 2; RUN: llc -mtriple=x86_64-pc-linux -stackrealign -verify-machineinstrs < %s | FileCheck %s 3 4; Calling convention ghccc uses ebp to pass parameter, so calling a function 5; using ghccc clobbers ebp. We should save and restore ebp around such a call 6; if ebp is used as frame pointer. 7 8declare ghccc i32 @external(i32) 9 10; Basic test with ghccc calling convention. 11define i32 @test1(i32 %0, i32 %1) { 12; CHECK-LABEL: test1: 13; CHECK: # %bb.0: 14; CHECK-NEXT: pushq %rbp 15; CHECK-NEXT: .cfi_def_cfa_offset 16 16; CHECK-NEXT: .cfi_offset %rbp, -16 17; CHECK-NEXT: movq %rsp, %rbp 18; CHECK-NEXT: .cfi_def_cfa_register %rbp 19; CHECK-NEXT: pushq %r15 20; CHECK-NEXT: pushq %r14 21; CHECK-NEXT: pushq %r13 22; CHECK-NEXT: pushq %r12 23; CHECK-NEXT: pushq %rbx 24; CHECK-NEXT: andq $-16, %rsp 25; CHECK-NEXT: subq $16, %rsp 26; CHECK-NEXT: .cfi_offset %rbx, -56 27; CHECK-NEXT: .cfi_offset %r12, -48 28; CHECK-NEXT: .cfi_offset %r13, -40 29; CHECK-NEXT: .cfi_offset %r14, -32 30; CHECK-NEXT: .cfi_offset %r15, -24 31; CHECK-NEXT: # kill: def $edi killed $edi def $rdi 32; CHECK-NEXT: pushq %rbp 33; CHECK-NEXT: pushq %rax 34; CHECK-NEXT: .cfi_remember_state 35; CHECK-NEXT: .cfi_escape 0x0f, 0x06, 0x77, 0x08, 0x06, 0x11, 0x10, 0x22 # 36; CHECK-NEXT: movl %esi, %ebp 37; CHECK-NEXT: movq %rdi, %r13 38; CHECK-NEXT: callq external@PLT 39; CHECK-NEXT: addq $8, %rsp 40; CHECK-NEXT: popq %rbp 41; CHECK-NEXT: .cfi_restore_state 42; CHECK-NEXT: leaq -40(%rbp), %rsp 43; CHECK-NEXT: popq %rbx 44; CHECK-NEXT: popq %r12 45; CHECK-NEXT: popq %r13 46; CHECK-NEXT: popq %r14 47; CHECK-NEXT: popq %r15 48; CHECK-NEXT: popq %rbp 49; CHECK-NEXT: .cfi_def_cfa %rsp, 8 50; CHECK-NEXT: retq 51 %x = call ghccc i32 @external(i32 %0, i32 %1) 52 ret i32 %x 53} 54 55; Calling convention hipe has similar behavior. It clobbers rbp but not rbx. 56 57declare cc 11 i64 @hipe1(i64) 58declare cc 11 i64 @hipe2(i64, i64, i64, i64, i64, i64, i64) 59 60; Basic test with hipe calling convention. 61define i64 @test2(i64 %a0, i64 %a1) { 62; CHECK-LABEL: test2: 63; CHECK: # %bb.0: 64; CHECK-NEXT: pushq %rbp 65; CHECK-NEXT: .cfi_def_cfa_offset 16 66; CHECK-NEXT: .cfi_offset %rbp, -16 67; CHECK-NEXT: movq %rsp, %rbp 68; CHECK-NEXT: .cfi_def_cfa_register %rbp 69; CHECK-NEXT: pushq %r15 70; CHECK-NEXT: pushq %r14 71; CHECK-NEXT: pushq %r13 72; CHECK-NEXT: pushq %r12 73; CHECK-NEXT: pushq %rbx 74; CHECK-NEXT: andq $-16, %rsp 75; CHECK-NEXT: subq $16, %rsp 76; CHECK-NEXT: .cfi_offset %rbx, -56 77; CHECK-NEXT: .cfi_offset %r12, -48 78; CHECK-NEXT: .cfi_offset %r13, -40 79; CHECK-NEXT: .cfi_offset %r14, -32 80; CHECK-NEXT: .cfi_offset %r15, -24 81; CHECK-NEXT: pushq %rbp 82; CHECK-NEXT: pushq %rax 83; CHECK-NEXT: .cfi_remember_state 84; CHECK-NEXT: .cfi_escape 0x0f, 0x06, 0x77, 0x08, 0x06, 0x11, 0x10, 0x22 # 85; CHECK-NEXT: movq %rsi, %rbp 86; CHECK-NEXT: movq %rdi, %r15 87; CHECK-NEXT: callq hipe1@PLT 88; CHECK-NEXT: addq $8, %rsp 89; CHECK-NEXT: popq %rbp 90; CHECK-NEXT: .cfi_restore_state 91; CHECK-NEXT: movq %r15, %rax 92; CHECK-NEXT: leaq -40(%rbp), %rsp 93; CHECK-NEXT: popq %rbx 94; CHECK-NEXT: popq %r12 95; CHECK-NEXT: popq %r13 96; CHECK-NEXT: popq %r14 97; CHECK-NEXT: popq %r15 98; CHECK-NEXT: popq %rbp 99; CHECK-NEXT: .cfi_def_cfa %rsp, 8 100; CHECK-NEXT: retq 101 %x = call cc 11 i64 @hipe1(i64 %a0, i64 %a1) 102 ret i64 %x 103} 104 105@buf = dso_local global [20 x ptr] zeroinitializer, align 16 106 107; longjmp modifies fp, it is expected behavior, wo should not save/restore fp 108; around it. 109define void @test4() { 110; CHECK-LABEL: test4: 111; CHECK: # %bb.0: # %entry 112; CHECK-NEXT: pushq %rbp 113; CHECK-NEXT: .cfi_def_cfa_offset 16 114; CHECK-NEXT: .cfi_offset %rbp, -16 115; CHECK-NEXT: movq %rsp, %rbp 116; CHECK-NEXT: .cfi_def_cfa_register %rbp 117; CHECK-NEXT: pushq %r15 118; CHECK-NEXT: pushq %r14 119; CHECK-NEXT: pushq %r13 120; CHECK-NEXT: pushq %r12 121; CHECK-NEXT: pushq %rbx 122; CHECK-NEXT: andq $-16, %rsp 123; CHECK-NEXT: subq $16, %rsp 124; CHECK-NEXT: .cfi_offset %rbx, -56 125; CHECK-NEXT: .cfi_offset %r12, -48 126; CHECK-NEXT: .cfi_offset %r13, -40 127; CHECK-NEXT: .cfi_offset %r14, -32 128; CHECK-NEXT: .cfi_offset %r15, -24 129; CHECK-NEXT: xorl %r13d, %r13d 130; CHECK-NEXT: pushq %rbp 131; CHECK-NEXT: pushq %rax 132; CHECK-NEXT: .cfi_remember_state 133; CHECK-NEXT: .cfi_escape 0x0f, 0x06, 0x77, 0x08, 0x06, 0x11, 0x10, 0x22 # 134; CHECK-NEXT: callq external@PLT 135; CHECK-NEXT: addq $8, %rsp 136; CHECK-NEXT: popq %rbp 137; CHECK-NEXT: .cfi_restore_state 138; CHECK-NEXT: movq buf(%rip), %rbp 139; CHECK-NEXT: movq buf+8(%rip), %rax 140; CHECK-NEXT: movq buf+16(%rip), %rsp 141; CHECK-NEXT: jmpq *%rax 142entry: 143 %x = call ghccc i32 @external(i32 0) 144 call void @llvm.eh.sjlj.longjmp(ptr @buf) 145 unreachable 146} 147 148declare ghccc void @tail() 149 150; We should not save/restore fp/bp around terminator. 151define ghccc void @test5() { 152; CHECK-LABEL: test5: 153; CHECK: # %bb.0: # %entry 154; CHECK-NEXT: pushq %rbp 155; CHECK-NEXT: .cfi_def_cfa_offset 16 156; CHECK-NEXT: .cfi_offset %rbp, -16 157; CHECK-NEXT: movq %rsp, %rbp 158; CHECK-NEXT: .cfi_def_cfa_register %rbp 159; CHECK-NEXT: andq $-8, %rsp 160; CHECK-NEXT: xorl %eax, %eax 161; CHECK-NEXT: testb %al, %al 162; CHECK-NEXT: jne .LBB3_2 163; CHECK-NEXT: # %bb.1: # %then 164; CHECK-NEXT: movq $0, (%rax) 165; CHECK-NEXT: movq %rbp, %rsp 166; CHECK-NEXT: popq %rbp 167; CHECK-NEXT: .cfi_def_cfa %rsp, 8 168; CHECK-NEXT: retq 169; CHECK-NEXT: .LBB3_2: # %else 170; CHECK-NEXT: .cfi_def_cfa %rbp, 16 171; CHECK-NEXT: movq %rbp, %rsp 172; CHECK-NEXT: popq %rbp 173; CHECK-NEXT: .cfi_def_cfa %rsp, 8 174; CHECK-NEXT: jmp tail@PLT # TAILCALL 175entry: 176 br i1 poison, label %then, label %else 177 178then: 179 store i64 0, ptr undef 180 br label %exit 181 182else: 183 musttail call ghccc void @tail() 184 ret void 185 186exit: 187 ret void 188} 189