1; NOTE: Assertions have been autogenerated by utils/update_llc_test_checks.py 2; RUN: llc -verify-machineinstrs < %s | FileCheck %s 3; This file contains a collection of basic tests to ensure we didn't 4; screw up normal call lowering when there are no deopt or gc arguments. 5 6target datalayout = "e-i64:64-f80:128-n8:16:32:64-S128" 7target triple = "x86_64-pc-linux-gnu" 8 9%struct = type { i64, i64 } 10 11declare zeroext i1 @return_i1() 12declare zeroext i32 @return_i32() 13declare i32* @return_i32ptr() 14declare float @return_float() 15declare %struct @return_struct() 16declare void @varargf(i32, ...) 17 18define i1 @test_i1_return() gc "statepoint-example" { 19; CHECK-LABEL: test_i1_return: 20; CHECK: # %bb.0: # %entry 21; CHECK-NEXT: pushq %rax 22; CHECK-NEXT: .cfi_def_cfa_offset 16 23; CHECK-NEXT: callq return_i1 24; CHECK-NEXT: .Ltmp0: 25; CHECK-NEXT: popq %rcx 26; CHECK-NEXT: .cfi_def_cfa_offset 8 27; CHECK-NEXT: retq 28; This is just checking that a i1 gets lowered normally when there's no extra 29; state arguments to the statepoint 30entry: 31 %safepoint_token = tail call token (i64, i32, i1 ()*, i32, i32, ...) @llvm.experimental.gc.statepoint.p0f_i1f(i64 0, i32 0, i1 ()* @return_i1, i32 0, i32 0, i32 0, i32 0) 32 %call1 = call zeroext i1 @llvm.experimental.gc.result.i1(token %safepoint_token) 33 ret i1 %call1 34} 35 36define i32 @test_i32_return() gc "statepoint-example" { 37; CHECK-LABEL: test_i32_return: 38; CHECK: # %bb.0: # %entry 39; CHECK-NEXT: pushq %rax 40; CHECK-NEXT: .cfi_def_cfa_offset 16 41; CHECK-NEXT: callq return_i32 42; CHECK-NEXT: .Ltmp1: 43; CHECK-NEXT: popq %rcx 44; CHECK-NEXT: .cfi_def_cfa_offset 8 45; CHECK-NEXT: retq 46entry: 47 %safepoint_token = tail call token (i64, i32, i32 ()*, i32, i32, ...) @llvm.experimental.gc.statepoint.p0f_i32f(i64 0, i32 0, i32 ()* @return_i32, i32 0, i32 0, i32 0, i32 0) 48 %call1 = call zeroext i32 @llvm.experimental.gc.result.i32(token %safepoint_token) 49 ret i32 %call1 50} 51 52define i32* @test_i32ptr_return() gc "statepoint-example" { 53; CHECK-LABEL: test_i32ptr_return: 54; CHECK: # %bb.0: # %entry 55; CHECK-NEXT: pushq %rax 56; CHECK-NEXT: .cfi_def_cfa_offset 16 57; CHECK-NEXT: callq return_i32ptr 58; CHECK-NEXT: .Ltmp2: 59; CHECK-NEXT: popq %rcx 60; CHECK-NEXT: .cfi_def_cfa_offset 8 61; CHECK-NEXT: retq 62entry: 63 %safepoint_token = tail call token (i64, i32, i32* ()*, i32, i32, ...) @llvm.experimental.gc.statepoint.p0f_p0i32f(i64 0, i32 0, i32* ()* @return_i32ptr, i32 0, i32 0, i32 0, i32 0) 64 %call1 = call i32* @llvm.experimental.gc.result.p0i32(token %safepoint_token) 65 ret i32* %call1 66} 67 68define float @test_float_return() gc "statepoint-example" { 69; CHECK-LABEL: test_float_return: 70; CHECK: # %bb.0: # %entry 71; CHECK-NEXT: pushq %rax 72; CHECK-NEXT: .cfi_def_cfa_offset 16 73; CHECK-NEXT: callq return_float 74; CHECK-NEXT: .Ltmp3: 75; CHECK-NEXT: popq %rax 76; CHECK-NEXT: .cfi_def_cfa_offset 8 77; CHECK-NEXT: retq 78entry: 79 %safepoint_token = tail call token (i64, i32, float ()*, i32, i32, ...) @llvm.experimental.gc.statepoint.p0f_f32f(i64 0, i32 0, float ()* @return_float, i32 0, i32 0, i32 0, i32 0) 80 %call1 = call float @llvm.experimental.gc.result.f32(token %safepoint_token) 81 ret float %call1 82} 83 84define %struct @test_struct_return() gc "statepoint-example" { 85; CHECK-LABEL: test_struct_return: 86; CHECK: # %bb.0: # %entry 87; CHECK-NEXT: pushq %rax 88; CHECK-NEXT: .cfi_def_cfa_offset 16 89; CHECK-NEXT: callq return_struct 90; CHECK-NEXT: .Ltmp4: 91; CHECK-NEXT: popq %rcx 92; CHECK-NEXT: .cfi_def_cfa_offset 8 93; CHECK-NEXT: retq 94entry: 95 %safepoint_token = tail call token (i64, i32, %struct ()*, i32, i32, ...) @llvm.experimental.gc.statepoint.p0f_structf(i64 0, i32 0, %struct ()* @return_struct, i32 0, i32 0, i32 0, i32 0) 96 %call1 = call %struct @llvm.experimental.gc.result.struct(token %safepoint_token) 97 ret %struct %call1 98} 99 100define i1 @test_relocate(i32 addrspace(1)* %a) gc "statepoint-example" { 101; CHECK-LABEL: test_relocate: 102; CHECK: # %bb.0: # %entry 103; CHECK-NEXT: pushq %rax 104; CHECK-NEXT: .cfi_def_cfa_offset 16 105; CHECK-NEXT: movq %rdi, (%rsp) 106; CHECK-NEXT: callq return_i1 107; CHECK-NEXT: .Ltmp5: 108; CHECK-NEXT: popq %rcx 109; CHECK-NEXT: .cfi_def_cfa_offset 8 110; CHECK-NEXT: retq 111; Check that an ununsed relocate has no code-generation impact 112entry: 113 %safepoint_token = tail call token (i64, i32, i1 ()*, i32, i32, ...) @llvm.experimental.gc.statepoint.p0f_i1f(i64 0, i32 0, i1 ()* @return_i1, i32 0, i32 0, i32 0, i32 0) ["gc-live" (i32 addrspace(1)* %a)] 114 %call1 = call i32 addrspace(1)* @llvm.experimental.gc.relocate.p1i32(token %safepoint_token, i32 0, i32 0) 115 %call2 = call zeroext i1 @llvm.experimental.gc.result.i1(token %safepoint_token) 116 ret i1 %call2 117} 118 119define void @test_void_vararg() gc "statepoint-example" { 120; CHECK-LABEL: test_void_vararg: 121; CHECK: # %bb.0: # %entry 122; CHECK-NEXT: pushq %rax 123; CHECK-NEXT: .cfi_def_cfa_offset 16 124; CHECK-NEXT: movl $42, %edi 125; CHECK-NEXT: movl $43, %esi 126; CHECK-NEXT: callq varargf 127; CHECK-NEXT: .Ltmp6: 128; CHECK-NEXT: popq %rax 129; CHECK-NEXT: .cfi_def_cfa_offset 8 130; CHECK-NEXT: retq 131; Check a statepoint wrapping a *void* returning vararg function works 132entry: 133 %safepoint_token = tail call token (i64, i32, void (i32, ...)*, i32, i32, ...) @llvm.experimental.gc.statepoint.p0f_isVoidi32varargf(i64 0, i32 0, void (i32, ...)* @varargf, i32 2, i32 0, i32 42, i32 43, i32 0, i32 0) 134 ;; if we try to use the result from a statepoint wrapping a 135 ;; non-void-returning varargf, we will experience a crash. 136 ret void 137} 138 139define i1 @test_i1_return_patchable() gc "statepoint-example" { 140; CHECK-LABEL: test_i1_return_patchable: 141; CHECK: # %bb.0: # %entry 142; CHECK-NEXT: pushq %rax 143; CHECK-NEXT: .cfi_def_cfa_offset 16 144; CHECK-NEXT: nopl (%rax) 145; CHECK-NEXT: .Ltmp7: 146; CHECK-NEXT: popq %rcx 147; CHECK-NEXT: .cfi_def_cfa_offset 8 148; CHECK-NEXT: retq 149; A patchable variant of test_i1_return 150entry: 151 %safepoint_token = tail call token (i64, i32, i1 ()*, i32, i32, ...) @llvm.experimental.gc.statepoint.p0f_i1f(i64 0, i32 3, i1 ()*null, i32 0, i32 0, i32 0, i32 0) 152 %call1 = call zeroext i1 @llvm.experimental.gc.result.i1(token %safepoint_token) 153 ret i1 %call1 154} 155 156declare void @consume(i32 addrspace(1)* %obj) 157 158define i1 @test_cross_bb(i32 addrspace(1)* %a, i1 %external_cond) gc "statepoint-example" { 159; CHECK-LABEL: test_cross_bb: 160; CHECK: # %bb.0: # %entry 161; CHECK-NEXT: pushq %rbp 162; CHECK-NEXT: .cfi_def_cfa_offset 16 163; CHECK-NEXT: pushq %rbx 164; CHECK-NEXT: .cfi_def_cfa_offset 24 165; CHECK-NEXT: pushq %rax 166; CHECK-NEXT: .cfi_def_cfa_offset 32 167; CHECK-NEXT: .cfi_offset %rbx, -24 168; CHECK-NEXT: .cfi_offset %rbp, -16 169; CHECK-NEXT: movl %esi, %ebp 170; CHECK-NEXT: movq %rdi, (%rsp) 171; CHECK-NEXT: callq return_i1 172; CHECK-NEXT: .Ltmp8: 173; CHECK-NEXT: testb $1, %bpl 174; CHECK-NEXT: je .LBB8_2 175; CHECK-NEXT: # %bb.1: # %left 176; CHECK-NEXT: movl %eax, %ebx 177; CHECK-NEXT: movq (%rsp), %rdi 178; CHECK-NEXT: callq consume 179; CHECK-NEXT: movl %ebx, %eax 180; CHECK-NEXT: jmp .LBB8_3 181; CHECK-NEXT: .LBB8_2: # %right 182; CHECK-NEXT: movb $1, %al 183; CHECK-NEXT: .LBB8_3: # %right 184; CHECK-NEXT: addq $8, %rsp 185; CHECK-NEXT: .cfi_def_cfa_offset 24 186; CHECK-NEXT: popq %rbx 187; CHECK-NEXT: .cfi_def_cfa_offset 16 188; CHECK-NEXT: popq %rbp 189; CHECK-NEXT: .cfi_def_cfa_offset 8 190; CHECK-NEXT: retq 191entry: 192 %safepoint_token = tail call token (i64, i32, i1 ()*, i32, i32, ...) @llvm.experimental.gc.statepoint.p0f_i1f(i64 0, i32 0, i1 ()* @return_i1, i32 0, i32 0, i32 0, i32 0) ["gc-live" (i32 addrspace(1)* %a)] 193 br i1 %external_cond, label %left, label %right 194 195left: 196 %call1 = call i32 addrspace(1)* @llvm.experimental.gc.relocate.p1i32(token %safepoint_token, i32 0, i32 0) 197 %call2 = call zeroext i1 @llvm.experimental.gc.result.i1(token %safepoint_token) 198 call void @consume(i32 addrspace(1)* %call1) 199 ret i1 %call2 200 201right: 202 ret i1 true 203} 204 205%struct2 = type { i64, i64, i64 } 206 207declare void @consume_attributes(i32, i8* nest, i32, %struct2* byval(%struct2)) 208 209define void @test_attributes(%struct2* byval(%struct2) %s) gc "statepoint-example" { 210; CHECK-LABEL: test_attributes: 211; CHECK: # %bb.0: # %entry 212; CHECK-NEXT: pushq %rax 213; CHECK-NEXT: .cfi_def_cfa_offset 16 214; CHECK-NEXT: subq $8, %rsp 215; CHECK-NEXT: .cfi_adjust_cfa_offset 8 216; CHECK-NEXT: movq {{[0-9]+}}(%rsp), %rax 217; CHECK-NEXT: movq {{[0-9]+}}(%rsp), %rcx 218; CHECK-NEXT: movq {{[0-9]+}}(%rsp), %rdx 219; CHECK-NEXT: movl $42, %edi 220; CHECK-NEXT: xorl %r10d, %r10d 221; CHECK-NEXT: movl $17, %esi 222; CHECK-NEXT: pushq %rax 223; CHECK-NEXT: .cfi_adjust_cfa_offset 8 224; CHECK-NEXT: pushq %rdx 225; CHECK-NEXT: .cfi_adjust_cfa_offset 8 226; CHECK-NEXT: pushq %rcx 227; CHECK-NEXT: .cfi_adjust_cfa_offset 8 228; CHECK-NEXT: callq consume_attributes 229; CHECK-NEXT: .Ltmp9: 230; CHECK-NEXT: addq $32, %rsp 231; CHECK-NEXT: .cfi_adjust_cfa_offset -32 232; CHECK-NEXT: popq %rax 233; CHECK-NEXT: .cfi_def_cfa_offset 8 234; CHECK-NEXT: retq 235entry: 236; Check that arguments with attributes are lowered correctly. 237; We call a function that has a nest argument and a byval argument. 238 %statepoint_token = call token (i64, i32, void (i32, i8*, i32, %struct2*)*, i32, i32, ...) @llvm.experimental.gc.statepoint.p0f_isVoidi32p0i8i32p0s_struct2sf(i64 0, i32 0, void (i32, i8*, i32, %struct2*)* @consume_attributes, i32 4, i32 0, i32 42, i8* nest null, i32 17, %struct2* byval(%struct2) %s, i32 0, i32 0) 239 ret void 240} 241 242declare token @llvm.experimental.gc.statepoint.p0f_i1f(i64, i32, i1 ()*, i32, i32, ...) 243declare i1 @llvm.experimental.gc.result.i1(token) 244 245declare token @llvm.experimental.gc.statepoint.p0f_i32f(i64, i32, i32 ()*, i32, i32, ...) 246declare i32 @llvm.experimental.gc.result.i32(token) 247 248declare token @llvm.experimental.gc.statepoint.p0f_p0i32f(i64, i32, i32* ()*, i32, i32, ...) 249declare i32* @llvm.experimental.gc.result.p0i32(token) 250 251declare token @llvm.experimental.gc.statepoint.p0f_f32f(i64, i32, float ()*, i32, i32, ...) 252declare float @llvm.experimental.gc.result.f32(token) 253 254declare token @llvm.experimental.gc.statepoint.p0f_structf(i64, i32, %struct ()*, i32, i32, ...) 255declare %struct @llvm.experimental.gc.result.struct(token) 256 257declare token @llvm.experimental.gc.statepoint.p0f_isVoidi32varargf(i64, i32, void (i32, ...)*, i32, i32, ...) 258 259declare token @llvm.experimental.gc.statepoint.p0f_isVoidi32p0i8i32p0s_struct2sf(i64, i32, void (i32, i8*, i32, %struct2*)*, i32, i32, ...) 260 261declare i32 addrspace(1)* @llvm.experimental.gc.relocate.p1i32(token, i32, i32) 262