1; NOTE: Assertions have been autogenerated by utils/update_llc_test_checks.py 2; RUN: llc -verify-machineinstrs < %s | FileCheck %s 3; A collection of basic functionality tests for statepoint lowering - most 4; interesting cornercases are exercised through the x86 tests. 5 6target datalayout = "e-i64:64-f80:128-n8:16:32:64-S128" 7target triple = "aarch64-unknown-linux-gnu" 8 9%struct = type { i64, i64 } 10 11declare zeroext i1 @return_i1() 12declare zeroext i32 @return_i32() 13declare ptr @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: str x30, [sp, #-16]! // 8-byte Folded Spill 22; CHECK-NEXT: .cfi_def_cfa_offset 16 23; CHECK-NEXT: .cfi_offset w30, -16 24; CHECK-NEXT: bl return_i1 25; CHECK-NEXT: .Ltmp0: 26; CHECK-NEXT: ldr x30, [sp], #16 // 8-byte Folded Reload 27; CHECK-NEXT: ret 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, ptr, i32, i32, ...) @llvm.experimental.gc.statepoint.p0(i64 0, i32 0, ptr elementtype(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: str x30, [sp, #-16]! // 8-byte Folded Spill 40; CHECK-NEXT: .cfi_def_cfa_offset 16 41; CHECK-NEXT: .cfi_offset w30, -16 42; CHECK-NEXT: bl return_i32 43; CHECK-NEXT: .Ltmp1: 44; CHECK-NEXT: ldr x30, [sp], #16 // 8-byte Folded Reload 45; CHECK-NEXT: ret 46entry: 47 %safepoint_token = tail call token (i64, i32, ptr, i32, i32, ...) @llvm.experimental.gc.statepoint.p0(i64 0, i32 0, ptr elementtype(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 ptr @test_i32ptr_return() gc "statepoint-example" { 53; CHECK-LABEL: test_i32ptr_return: 54; CHECK: // %bb.0: // %entry 55; CHECK-NEXT: str x30, [sp, #-16]! // 8-byte Folded Spill 56; CHECK-NEXT: .cfi_def_cfa_offset 16 57; CHECK-NEXT: .cfi_offset w30, -16 58; CHECK-NEXT: bl return_i32ptr 59; CHECK-NEXT: .Ltmp2: 60; CHECK-NEXT: ldr x30, [sp], #16 // 8-byte Folded Reload 61; CHECK-NEXT: ret 62entry: 63 %safepoint_token = tail call token (i64, i32, ptr, i32, i32, ...) @llvm.experimental.gc.statepoint.p0(i64 0, i32 0, ptr elementtype(ptr ()) @return_i32ptr, i32 0, i32 0, i32 0, i32 0) 64 %call1 = call ptr @llvm.experimental.gc.result.p0(token %safepoint_token) 65 ret ptr %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: str x30, [sp, #-16]! // 8-byte Folded Spill 72; CHECK-NEXT: .cfi_def_cfa_offset 16 73; CHECK-NEXT: .cfi_offset w30, -16 74; CHECK-NEXT: bl return_float 75; CHECK-NEXT: .Ltmp3: 76; CHECK-NEXT: ldr x30, [sp], #16 // 8-byte Folded Reload 77; CHECK-NEXT: ret 78entry: 79 %safepoint_token = tail call token (i64, i32, ptr, i32, i32, ...) @llvm.experimental.gc.statepoint.p0(i64 0, i32 0, ptr elementtype(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: str x30, [sp, #-16]! // 8-byte Folded Spill 88; CHECK-NEXT: .cfi_def_cfa_offset 16 89; CHECK-NEXT: .cfi_offset w30, -16 90; CHECK-NEXT: bl return_struct 91; CHECK-NEXT: .Ltmp4: 92; CHECK-NEXT: ldr x30, [sp], #16 // 8-byte Folded Reload 93; CHECK-NEXT: ret 94entry: 95 %safepoint_token = tail call token (i64, i32, ptr, i32, i32, ...) @llvm.experimental.gc.statepoint.p0(i64 0, i32 0, ptr elementtype(%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(ptr addrspace(1) %a) gc "statepoint-example" { 101; CHECK-LABEL: test_relocate: 102; CHECK: // %bb.0: // %entry 103; CHECK-NEXT: stp x30, x0, [sp, #-16]! // 8-byte Folded Spill 104; CHECK-NEXT: .cfi_def_cfa_offset 16 105; CHECK-NEXT: .cfi_offset w30, -16 106; CHECK-NEXT: bl return_i1 107; CHECK-NEXT: .Ltmp5: 108; CHECK-NEXT: ldr x30, [sp], #16 // 8-byte Folded Reload 109; CHECK-NEXT: ret 110; Check that an ununsed relocate has no code-generation impact 111entry: 112 %safepoint_token = tail call token (i64, i32, ptr, i32, i32, ...) @llvm.experimental.gc.statepoint.p0(i64 0, i32 0, ptr elementtype(i1 ()) @return_i1, i32 0, i32 0, i32 0, i32 0) ["gc-live" (ptr addrspace(1) %a)] 113 %call1 = call ptr addrspace(1) @llvm.experimental.gc.relocate.p1(token %safepoint_token, i32 0, i32 0) 114 %call2 = call zeroext i1 @llvm.experimental.gc.result.i1(token %safepoint_token) 115 ret i1 %call2 116} 117 118define void @test_void_vararg() gc "statepoint-example" { 119; CHECK-LABEL: test_void_vararg: 120; CHECK: // %bb.0: // %entry 121; CHECK-NEXT: str x30, [sp, #-16]! // 8-byte Folded Spill 122; CHECK-NEXT: .cfi_def_cfa_offset 16 123; CHECK-NEXT: .cfi_offset w30, -16 124; CHECK-NEXT: mov w0, #42 // =0x2a 125; CHECK-NEXT: mov w1, #43 // =0x2b 126; CHECK-NEXT: bl varargf 127; CHECK-NEXT: .Ltmp6: 128; CHECK-NEXT: ldr x30, [sp], #16 // 8-byte Folded Reload 129; CHECK-NEXT: ret 130; Check a statepoint wrapping a *ptr returning vararg function works 131entry: 132 %safepoint_token = tail call token (i64, i32, ptr, i32, i32, ...) @llvm.experimental.gc.statepoint.p0(i64 0, i32 0, ptr elementtype(void (i32, ...)) @varargf, i32 2, i32 0, i32 42, i32 43, i32 0, i32 0) 133 ;; if we try to use the result from a statepoint wrapping a 134 ;; non-void-returning varargf, we will experience a crash. 135 ret void 136} 137 138define i1 @test_i1_return_patchable() gc "statepoint-example" { 139; CHECK-LABEL: test_i1_return_patchable: 140; CHECK: // %bb.0: // %entry 141; CHECK-NEXT: str x30, [sp, #-16]! // 8-byte Folded Spill 142; CHECK-NEXT: .cfi_def_cfa_offset 16 143; CHECK-NEXT: .cfi_offset w30, -16 144; CHECK-NEXT: nop 145; CHECK-NEXT: .Ltmp7: 146; CHECK-NEXT: ldr x30, [sp], #16 // 8-byte Folded Reload 147; CHECK-NEXT: ret 148; A patchable variant of test_i1_return 149entry: 150 %safepoint_token = tail call token (i64, i32, ptr, i32, i32, ...) @llvm.experimental.gc.statepoint.p0(i64 0, i32 4, ptr elementtype(i1 ()) null, i32 0, i32 0, i32 0, i32 0) 151 %call1 = call zeroext i1 @llvm.experimental.gc.result.i1(token %safepoint_token) 152 ret i1 %call1 153} 154 155declare void @consume(ptr addrspace(1) %obj) 156 157define i1 @test_cross_bb(ptr addrspace(1) %a, i1 %external_cond) gc "statepoint-example" { 158; CHECK-LABEL: test_cross_bb: 159; CHECK: // %bb.0: // %entry 160; CHECK-NEXT: str x30, [sp, #-32]! // 8-byte Folded Spill 161; CHECK-NEXT: stp x20, x19, [sp, #16] // 16-byte Folded Spill 162; CHECK-NEXT: .cfi_def_cfa_offset 32 163; CHECK-NEXT: .cfi_offset w19, -8 164; CHECK-NEXT: .cfi_offset w20, -16 165; CHECK-NEXT: .cfi_offset w30, -32 166; CHECK-NEXT: mov w20, w1 167; CHECK-NEXT: str x0, [sp, #8] 168; CHECK-NEXT: bl return_i1 169; CHECK-NEXT: .Ltmp8: 170; CHECK-NEXT: tbz w20, #0, .LBB8_2 171; CHECK-NEXT: // %bb.1: // %left 172; CHECK-NEXT: mov w19, w0 173; CHECK-NEXT: ldr x0, [sp, #8] 174; CHECK-NEXT: bl consume 175; CHECK-NEXT: b .LBB8_3 176; CHECK-NEXT: .LBB8_2: 177; CHECK-NEXT: mov w19, #1 // =0x1 178; CHECK-NEXT: .LBB8_3: // %common.ret 179; CHECK-NEXT: and w0, w19, #0x1 180; CHECK-NEXT: ldp x20, x19, [sp, #16] // 16-byte Folded Reload 181; CHECK-NEXT: ldr x30, [sp], #32 // 8-byte Folded Reload 182; CHECK-NEXT: ret 183entry: 184 %safepoint_token = tail call token (i64, i32, ptr, i32, i32, ...) @llvm.experimental.gc.statepoint.p0(i64 0, i32 0, ptr elementtype(i1 ()) @return_i1, i32 0, i32 0, i32 0, i32 0) ["gc-live" (ptr addrspace(1) %a)] 185 br i1 %external_cond, label %left, label %right 186 187left: 188 %call1 = call ptr addrspace(1) @llvm.experimental.gc.relocate.p1(token %safepoint_token, i32 0, i32 0) 189 %call2 = call zeroext i1 @llvm.experimental.gc.result.i1(token %safepoint_token) 190 call void @consume(ptr addrspace(1) %call1) 191 ret i1 %call2 192 193right: 194 ret i1 true 195} 196 197%struct2 = type { i64, i64, i64 } 198 199declare void @consume_attributes(i32, ptr nest, i32, ptr byval(%struct2)) 200 201define void @test_attributes(ptr byval(%struct2) %s) gc "statepoint-example" { 202; CHECK-LABEL: test_attributes: 203; CHECK: // %bb.0: // %entry 204; CHECK-NEXT: sub sp, sp, #48 205; CHECK-NEXT: str x30, [sp, #32] // 8-byte Folded Spill 206; CHECK-NEXT: .cfi_def_cfa_offset 48 207; CHECK-NEXT: .cfi_offset w30, -16 208; CHECK-NEXT: ldr x8, [sp, #64] 209; CHECK-NEXT: ldr q0, [sp, #48] 210; CHECK-NEXT: mov x18, xzr 211; CHECK-NEXT: mov w0, #42 // =0x2a 212; CHECK-NEXT: mov w1, #17 // =0x11 213; CHECK-NEXT: str x8, [sp, #16] 214; CHECK-NEXT: str q0, [sp] 215; CHECK-NEXT: bl consume_attributes 216; CHECK-NEXT: .Ltmp9: 217; CHECK-NEXT: ldr x30, [sp, #32] // 8-byte Folded Reload 218; CHECK-NEXT: add sp, sp, #48 219; CHECK-NEXT: ret 220entry: 221; Check that arguments with attributes are lowered correctly. 222; We call a function that has a nest argument and a byval argument. 223 %statepoint_token = call token (i64, i32, ptr, i32, i32, ...) @llvm.experimental.gc.statepoint.p0(i64 0, i32 0, ptr elementtype(void (i32, ptr, i32, ptr)) @consume_attributes, i32 4, i32 0, i32 42, ptr nest null, i32 17, ptr byval(%struct2) %s, i32 0, i32 0) 224 ret void 225} 226 227declare token @llvm.experimental.gc.statepoint.p0(i64, i32, ptr, i32, i32, ...) 228declare i1 @llvm.experimental.gc.result.i1(token) 229 230declare i32 @llvm.experimental.gc.result.i32(token) 231 232declare ptr @llvm.experimental.gc.result.p0(token) 233 234declare float @llvm.experimental.gc.result.f32(token) 235 236declare %struct @llvm.experimental.gc.result.struct(token) 237 238 239 240declare ptr addrspace(1) @llvm.experimental.gc.relocate.p1(token, i32, i32) 241