| ; RUN: llc < %s -verify-machineinstrs -stack-symbol-ordering=0 -mtriple="x86_64-pc-linux-gnu" | FileCheck %s | |
| ; RUN: llc < %s -verify-machineinstrs -stack-symbol-ordering=0 -mtriple="x86_64-pc-unknown-elf" | FileCheck %s | |
| ; This test is a basic correctness check to ensure statepoints are generating | |
| ; StackMap sections correctly. This is not intended to be a rigorous test of | |
| ; the StackMap format (see the stackmap tests for that). | |
| target datalayout = "e-i64:64-f80:128-n8:16:32:64-S128" | |
| declare zeroext i1 @return_i1() | |
| define i1 @test(ptr addrspace(1) %ptr_base, i32 %arg) | |
| gc "statepoint-example" { | |
| ; CHECK-LABEL: test: | |
| ; Do we see two spills for the local values and the store to the | |
| ; alloca? | |
| ; CHECK: subq $40, %rsp | |
| ; CHECK: movq $0, 24(%rsp) | |
| ; CHECK: movq %rdi, 16(%rsp) | |
| ; CHECK: movq %rax, 8(%rsp) | |
| ; CHECK: callq return_i1 | |
| ; CHECK: addq $40, %rsp | |
| ; CHECK: retq | |
| entry: | |
| %metadata1 = alloca ptr addrspace(1), i32 2, align 8 | |
| store ptr addrspace(1) null, ptr %metadata1 | |
| %ptr_derived = getelementptr i32, ptr addrspace(1) %ptr_base, i32 %arg | |
| %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) %ptr_base, ptr addrspace(1) %ptr_derived, ptr addrspace(1) null), "deopt" (ptr addrspace(1) %ptr_base, ptr addrspace(1) null)] | |
| %call1 = call zeroext i1 @llvm.experimental.gc.result.i1(token %safepoint_token) | |
| %a = call ptr addrspace(1) @llvm.experimental.gc.relocate.p1(token %safepoint_token, i32 0, i32 0) | |
| %b = call ptr addrspace(1) @llvm.experimental.gc.relocate.p1(token %safepoint_token, i32 0, i32 1) | |
| %c = call ptr addrspace(1) @llvm.experimental.gc.relocate.p1(token %safepoint_token, i32 2, i32 2) | |
| ; | |
| ret i1 %call1 | |
| } | |
| ; This is similar to the previous test except that we have derived pointer as | |
| ; argument to the function. Despite that this can not happen after the | |
| ; RewriteSafepointForGC pass, lowering should be able to handle it anyway. | |
| define i1 @test_derived_arg(ptr addrspace(1) %ptr_base, | |
| ptr addrspace(1) %ptr_derived) | |
| gc "statepoint-example" { | |
| ; CHECK-LABEL: test_derived_arg | |
| ; Do we see two spills for the local values and the store to the | |
| ; alloca? | |
| ; CHECK: subq $40, %rsp | |
| ; CHECK: movq $0, 24(%rsp) | |
| ; CHECK: movq %rdi, 16(%rsp) | |
| ; CHECK: movq %rsi, 8(%rsp) | |
| ; CHECK: callq return_i1 | |
| ; CHECK: addq $40, %rsp | |
| ; CHECK: retq | |
| entry: | |
| %metadata1 = alloca ptr addrspace(1), i32 2, align 8 | |
| store ptr addrspace(1) null, ptr %metadata1 | |
| %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) %ptr_base, ptr addrspace(1) %ptr_derived, ptr addrspace(1) null), "deopt" (ptr addrspace(1) %ptr_base, ptr addrspace(1) null)] | |
| %call1 = call zeroext i1 @llvm.experimental.gc.result.i1(token %safepoint_token) | |
| %a = call ptr addrspace(1) @llvm.experimental.gc.relocate.p1(token %safepoint_token, i32 0, i32 0) | |
| %b = call ptr addrspace(1) @llvm.experimental.gc.relocate.p1(token %safepoint_token, i32 0, i32 1) | |
| %c = call ptr addrspace(1) @llvm.experimental.gc.relocate.p1(token %safepoint_token, i32 2, i32 2) | |
| ; | |
| ret i1 %call1 | |
| } | |
| ; Simple test case to check that we emit the ID field correctly | |
| define i1 @test_id() gc "statepoint-example" { | |
| ; CHECK-LABEL: test_id | |
| entry: | |
| %safepoint_token = tail call token (i64, i32, ptr, i32, i32, ...) @llvm.experimental.gc.statepoint.p0(i64 237, i32 0, ptr elementtype(i1 ()) @return_i1, i32 0, i32 0, i32 0, i32 0) | |
| %call1 = call zeroext i1 @llvm.experimental.gc.result.i1(token %safepoint_token) | |
| ret i1 %call1 | |
| } | |
| ; This test checks that when SP is changed in the function | |
| ; (e.g. passing arguments on stack), the stack map entry | |
| ; takes this adjustment into account. | |
| declare void @many_arg(i64, i64, i64, i64, i64, i64, i64, i64) | |
| define i32 @test_spadj(ptr addrspace(1) %p) gc "statepoint-example" { | |
| ; CHECK-LABEL: test_spadj | |
| ; CHECK: movq %rdi, (%rsp) | |
| ; CHECK: xorl %edi, %edi | |
| ; CHECK: xorl %esi, %esi | |
| ; CHECK: xorl %edx, %edx | |
| ; CHECK: xorl %ecx, %ecx | |
| ; CHECK: xorl %r8d, %r8d | |
| ; CHECK: xorl %r9d, %r9d | |
| ; CHECK: pushq $0 | |
| ; CHECK: pushq $0 | |
| ; CHECK: callq many_arg | |
| ; CHECK: addq $16, %rsp | |
| ; CHECK: movq (%rsp) | |
| %statepoint_token = call token (i64, i32, ptr, i32, i32, ...) @llvm.experimental.gc.statepoint.p0(i64 0, i32 0, ptr elementtype(void (i64, i64, i64, i64, i64, i64, i64, i64)) @many_arg, i32 8, i32 0, i64 0, i64 0, i64 0, i64 0, i64 0, i64 0, i64 0, i64 0, i32 0, i32 0) ["gc-live"(ptr addrspace(1) %p)] | |
| %p.relocated = call ptr addrspace(1) @llvm.experimental.gc.relocate.p1(token %statepoint_token, i32 0, i32 0) ; (%p, %p) | |
| %ld = load i32, ptr addrspace(1) %p.relocated | |
| ret i32 %ld | |
| } | |
| ; Test that function arguments at fixed stack offset | |
| ; can be directly encoded in the stack map, without | |
| ; spilling. | |
| %struct = type { i64, i64, i64 } | |
| declare void @use(ptr) | |
| define void @test_fixed_arg(ptr byval(%struct) %x) gc "statepoint-example" { | |
| ; CHECK-LABEL: test_fixed_arg | |
| ; CHECK: pushq %rax | |
| ; CHECK: leaq 16(%rsp), %rdi | |
| ; Should not spill fixed stack address. | |
| ; CHECK-NOT: movq %rdi, (%rsp) | |
| ; CHECK: callq use | |
| ; CHECK: popq %rax | |
| ; CHECK: retq | |
| entry: | |
| br label %bb | |
| bb: ; preds = %entry | |
| %statepoint_token = call token (i64, i32, ptr, i32, i32, ...) @llvm.experimental.gc.statepoint.p0(i64 0, i32 0, ptr elementtype(void (ptr)) @use, i32 1, i32 0, ptr %x, i32 0, i32 0) ["deopt" (ptr %x)] | |
| ret void | |
| } | |
| declare token @llvm.experimental.gc.statepoint.p0(i64, i32, ptr, i32, i32, ...) | |
| declare i1 @llvm.experimental.gc.result.i1(token) | |
| declare ptr addrspace(1) @llvm.experimental.gc.relocate.p1(token, i32, i32) #3 | |
| ; CHECK-LABEL: .section .llvm_stackmaps | |
| ; CHECK-NEXT: __LLVM_StackMaps: | |
| ; Header | |
| ; CHECK-NEXT: .byte 3 | |
| ; CHECK-NEXT: .byte 0 | |
| ; CHECK-NEXT: .short 0 | |
| ; Num Functions | |
| ; CHECK-NEXT: .long 5 | |
| ; Num LargeConstants | |
| ; CHECK-NEXT: .long 0 | |
| ; Num Callsites | |
| ; CHECK-NEXT: .long 5 | |
| ; Functions and stack size | |
| ; CHECK-NEXT: .quad test | |
| ; CHECK-NEXT: .quad 40 | |
| ; CHECK-NEXT: .quad 1 | |
| ; CHECK-NEXT: .quad test_derived_arg | |
| ; CHECK-NEXT: .quad 40 | |
| ; CHECK-NEXT: .quad 1 | |
| ; CHECK-NEXT: .quad test_id | |
| ; CHECK-NEXT: .quad 8 | |
| ; CHECK-NEXT: .quad 1 | |
| ; CHECK-NEXT: .quad test_spadj | |
| ; CHECK-NEXT: .quad 8 | |
| ; CHECK-NEXT: .quad 1 | |
| ; CHECK-NEXT: .quad test_fixed_arg | |
| ; CHECK-NEXT: .quad 8 | |
| ; CHECK-NEXT: .quad 1 | |
| ; | |
| ; test | |
| ; | |
| ; Statepoint ID | |
| ; CHECK-NEXT: .quad 0 | |
| ; Callsites | |
| ; Constant arguments | |
| ; CHECK-NEXT: .long .Ltmp0-test | |
| ; CHECK: .short 0 | |
| ; CHECK: .short 11 | |
| ; SmallConstant (0) | |
| ; CHECK: .byte 4 | |
| ; CHECK-NEXT: .byte 0 | |
| ; CHECK: .short 8 | |
| ; CHECK: .short 0 | |
| ; CHECK-NEXT: .short 0 | |
| ; CHECK: .long 0 | |
| ; SmallConstant (0) | |
| ; CHECK: .byte 4 | |
| ; CHECK-NEXT: .byte 0 | |
| ; CHECK: .short 8 | |
| ; CHECK: .short 0 | |
| ; CHECK-NEXT: .short 0 | |
| ; CHECK: .long 0 | |
| ; SmallConstant (2) | |
| ; CHECK: .byte 4 | |
| ; CHECK-NEXT: .byte 0 | |
| ; CHECK: .short 8 | |
| ; CHECK: .short 0 | |
| ; CHECK-NEXT: .short 0 | |
| ; CHECK: .long 2 | |
| ; Indirect Spill Slot [RSP+0] | |
| ; CHECK: .byte 3 | |
| ; CHECK-NEXT: .byte 0 | |
| ; CHECK: .short 8 | |
| ; CHECK: .short 7 | |
| ; CHECK-NEXT: .short 0 | |
| ; CHECK: .long 16 | |
| ; SmallConstant (0) | |
| ; CHECK: .byte 4 | |
| ; CHECK-NEXT: .byte 0 | |
| ; CHECK: .short 8 | |
| ; CHECK: .short 0 | |
| ; CHECK-NEXT: .short 0 | |
| ; CHECK: .long 0 | |
| ; SmallConstant (0) | |
| ; CHECK: .byte 4 | |
| ; CHECK-NEXT: .byte 0 | |
| ; CHECK: .short 8 | |
| ; CHECK: .short 0 | |
| ; CHECK-NEXT: .short 0 | |
| ; CHECK: .long 0 | |
| ; SmallConstant (0) | |
| ; CHECK: .byte 4 | |
| ; CHECK-NEXT: .byte 0 | |
| ; CHECK: .short 8 | |
| ; CHECK: .short 0 | |
| ; CHECK-NEXT: .short 0 | |
| ; CHECK: .long 0 | |
| ; Indirect Spill Slot [RSP+16] | |
| ; CHECK: .byte 3 | |
| ; CHECK-NEXT: .byte 0 | |
| ; CHECK: .short 8 | |
| ; CHECK: .short 7 | |
| ; CHECK-NEXT: .short 0 | |
| ; CHECK: .long 16 | |
| ; Indirect Spill Slot [RSP+8] | |
| ; CHECK: .byte 3 | |
| ; CHECK-NEXT: .byte 0 | |
| ; CHECK: .short 8 | |
| ; CHECK: .short 7 | |
| ; CHECK-NEXT: .short 0 | |
| ; CHECK: .long 8 | |
| ; Indirect Spill Slot [RSP+16] | |
| ; CHECK: .byte 3 | |
| ; CHECK-NEXT: .byte 0 | |
| ; CHECK: .short 8 | |
| ; CHECK: .short 7 | |
| ; CHECK-NEXT: .short 0 | |
| ; CHECK: .long 16 | |
| ; Indirect Spill Slot [RSP+16] | |
| ; CHECK: .byte 3 | |
| ; CHECK-NEXT: .byte 0 | |
| ; CHECK: .short 8 | |
| ; CHECK: .short 7 | |
| ; CHECK-NEXT: .short 0 | |
| ; CHECK: .long 16 | |
| ; No Padding or LiveOuts | |
| ; CHECK: .short 0 | |
| ; CHECK: .short 0 | |
| ; CHECK: .p2align 3 | |
| ; | |
| ; test_derived_arg | |
| ; Statepoint ID | |
| ; CHECK-NEXT: .quad 0 | |
| ; Callsites | |
| ; Constant arguments | |
| ; CHECK-NEXT: .long .Ltmp1-test_derived_arg | |
| ; CHECK: .short 0 | |
| ; CHECK: .short 11 | |
| ; SmallConstant (0) | |
| ; CHECK: .byte 4 | |
| ; CHECK-NEXT: .byte 0 | |
| ; CHECK: .short 8 | |
| ; CHECK: .short 0 | |
| ; CHECK-NEXT: .short 0 | |
| ; CHECK: .long 0 | |
| ; SmallConstant (2) | |
| ; CHECK: .byte 4 | |
| ; CHECK-NEXT: .byte 0 | |
| ; CHECK: .short 8 | |
| ; CHECK: .short 0 | |
| ; CHECK-NEXT: .short 0 | |
| ; CHECK: .long 2 | |
| ; Indirect Spill Slot [RSP+0] | |
| ; CHECK: .byte 3 | |
| ; CHECK-NEXT: .byte 0 | |
| ; CHECK: .short 8 | |
| ; CHECK: .short 7 | |
| ; CHECK-NEXT: .short 0 | |
| ; CHECK: .long 16 | |
| ; SmallConstant (0) | |
| ; CHECK: .byte 4 | |
| ; CHECK-NEXT: .byte 0 | |
| ; CHECK: .short 8 | |
| ; CHECK: .short 0 | |
| ; CHECK-NEXT: .short 0 | |
| ; CHECK: .long 0 | |
| ; SmallConstant (0) | |
| ; CHECK: .byte 4 | |
| ; CHECK-NEXT: .byte 0 | |
| ; CHECK: .short 8 | |
| ; CHECK: .short 0 | |
| ; CHECK-NEXT: .short 0 | |
| ; CHECK: .long 0 | |
| ; SmallConstant (0) | |
| ; CHECK: .byte 4 | |
| ; CHECK-NEXT: .byte 0 | |
| ; CHECK: .short 8 | |
| ; CHECK: .short 0 | |
| ; CHECK-NEXT: .short 0 | |
| ; CHECK: .long 0 | |
| ; Indirect Spill Slot [RSP+16] | |
| ; CHECK: .byte 3 | |
| ; CHECK-NEXT: .byte 0 | |
| ; CHECK: .short 8 | |
| ; CHECK: .short 7 | |
| ; CHECK-NEXT: .short 0 | |
| ; CHECK: .long 16 | |
| ; Indirect Spill Slot [RSP+8] | |
| ; CHECK: .byte 3 | |
| ; CHECK-NEXT: .byte 0 | |
| ; CHECK: .short 8 | |
| ; CHECK: .short 7 | |
| ; CHECK-NEXT: .short 0 | |
| ; CHECK: .long 8 | |
| ; Indirect Spill Slot [RSP+16] | |
| ; CHECK: .byte 3 | |
| ; CHECK-NEXT: .byte 0 | |
| ; CHECK: .short 8 | |
| ; CHECK: .short 7 | |
| ; CHECK-NEXT: .short 0 | |
| ; CHECK: .long 16 | |
| ; Indirect Spill Slot [RSP+16] | |
| ; CHECK: .byte 3 | |
| ; CHECK-NEXT: .byte 0 | |
| ; CHECK: .short 8 | |
| ; CHECK: .short 7 | |
| ; CHECK-NEXT: .short 0 | |
| ; CHECK: .long 16 | |
| ; No Padding or LiveOuts | |
| ; CHECK: .short 0 | |
| ; CHECK: .short 0 | |
| ; CHECK: .p2align 3 | |
| ; Records for the test_id function: | |
| ; The Statepoint ID: | |
| ; CHECK-NEXT: .quad 237 | |
| ; Instruction Offset | |
| ; CHECK-NEXT: .long .Ltmp2-test_id | |
| ; Reserved: | |
| ; CHECK: .short 0 | |
| ; NumLocations: | |
| ; CHECK: .short 3 | |
| ; StkMapRecord[0]: | |
| ; SmallConstant(0): | |
| ; CHECK: .byte 4 | |
| ; CHECK-NEXT: .byte 0 | |
| ; CHECK: .short 8 | |
| ; CHECK: .short 0 | |
| ; CHECK-NEXT: .short 0 | |
| ; CHECK: .long 0 | |
| ; StkMapRecord[1]: | |
| ; SmallConstant(0): | |
| ; CHECK: .byte 4 | |
| ; CHECK-NEXT: .byte 0 | |
| ; CHECK: .short 8 | |
| ; CHECK: .short 0 | |
| ; CHECK-NEXT: .short 0 | |
| ; CHECK: .long 0 | |
| ; StkMapRecord[2]: | |
| ; SmallConstant(0): | |
| ; CHECK: .byte 4 | |
| ; CHECK-NEXT: .byte 0 | |
| ; CHECK: .short 8 | |
| ; CHECK: .short 0 | |
| ; CHECK-NEXT: .short 0 | |
| ; CHECK: .long 0 | |
| ; No padding or LiveOuts | |
| ; CHECK: .short 0 | |
| ; CHECK: .short 0 | |
| ; CHECK: .p2align 3 | |
| ; | |
| ; test_spadj | |
| ; Statepoint ID | |
| ; CHECK-NEXT: .quad 0 | |
| ; Instruction Offset | |
| ; CHECK-NEXT: .long .Ltmp3-test_spadj | |
| ; Reserved: | |
| ; CHECK: .short 0 | |
| ; NumLocations: | |
| ; CHECK: .short 5 | |
| ; StkMapRecord[0]: | |
| ; SmallConstant(0): | |
| ; CHECK: .byte 4 | |
| ; CHECK-NEXT: .byte 0 | |
| ; CHECK: .short 8 | |
| ; CHECK: .short 0 | |
| ; CHECK-NEXT: .short 0 | |
| ; CHECK: .long 0 | |
| ; StkMapRecord[1]: | |
| ; SmallConstant(0): | |
| ; CHECK: .byte 4 | |
| ; CHECK-NEXT: .byte 0 | |
| ; CHECK: .short 8 | |
| ; CHECK: .short 0 | |
| ; CHECK-NEXT: .short 0 | |
| ; CHECK: .long 0 | |
| ; StkMapRecord[2]: | |
| ; SmallConstant(0): | |
| ; CHECK: .byte 4 | |
| ; CHECK-NEXT: .byte 0 | |
| ; CHECK: .short 8 | |
| ; CHECK: .short 0 | |
| ; CHECK-NEXT: .short 0 | |
| ; CHECK: .long 0 | |
| ; StkMapRecord[3]: | |
| ; Indirect Spill Slot [RSP+16] | |
| ; CHECK: .byte 3 | |
| ; CHECK-NEXT: .byte 0 | |
| ; CHECK: .short 8 | |
| ; CHECK: .short 7 | |
| ; CHECK-NEXT: .short 0 | |
| ; CHECK: .long 16 | |
| ; StkMapRecord[4]: | |
| ; Indirect Spill Slot [RSP+16] | |
| ; CHECK: .byte 3 | |
| ; CHECK-NEXT: .byte 0 | |
| ; CHECK: .short 8 | |
| ; CHECK: .short 7 | |
| ; CHECK-NEXT: .short 0 | |
| ; CHECK: .long 16 | |
| ; No padding or LiveOuts | |
| ; CHECK: .short 0 | |
| ; CHECK: .short 0 | |
| ; CHECK: .p2align 3 | |
| ; | |
| ; test_fixed_arg | |
| ; Statepoint ID | |
| ; CHECK-NEXT: .quad 0 | |
| ; Instruction Offset | |
| ; CHECK-NEXT: .long .Ltmp4-test_fixed_arg | |
| ; Reserved: | |
| ; CHECK: .short 0 | |
| ; NumLocations: | |
| ; CHECK: .short 4 | |
| ; StkMapRecord[0]: | |
| ; SmallConstant(0): | |
| ; CHECK: .byte 4 | |
| ; CHECK-NEXT: .byte 0 | |
| ; CHECK: .short 8 | |
| ; CHECK: .short 0 | |
| ; CHECK-NEXT: .short 0 | |
| ; CHECK: .long 0 | |
| ; StkMapRecord[1]: | |
| ; SmallConstant(0): | |
| ; CHECK: .byte 4 | |
| ; CHECK-NEXT: .byte 0 | |
| ; CHECK: .short 8 | |
| ; CHECK: .short 0 | |
| ; CHECK-NEXT: .short 0 | |
| ; CHECK: .long 0 | |
| ; StkMapRecord[2]: | |
| ; SmallConstant(1): | |
| ; CHECK: .byte 4 | |
| ; CHECK-NEXT: .byte 0 | |
| ; CHECK: .short 8 | |
| ; CHECK: .short 0 | |
| ; CHECK-NEXT: .short 0 | |
| ; CHECK: .long 1 | |
| ; StkMapRecord[3]: | |
| ; Direct RSP+16 | |
| ; CHECK: .byte 2 | |
| ; CHECK-NEXT: .byte 0 | |
| ; CHECK: .short 8 | |
| ; CHECK: .short 7 | |
| ; CHECK-NEXT: .short 0 | |
| ; CHECK: .long 16 | |
| ; No padding or LiveOuts | |
| ; CHECK: .short 0 | |
| ; CHECK: .short 0 | |
| ; CHECK: .p2align 3 |