1 ; RUN: opt -O3 -S < %s | FileCheck --check-prefix=CHECK-OPT %s 2 ; RUN: llc < %s | FileCheck --check-prefix=CHECK-LLC %s 3 ; These tests are targetted at making sure we don't retain information 4 ; about memory which contains potential gc references across a statepoint. 5 ; They're carefully written to only outlaw forwarding of references. 6 ; Depending on the collector, forwarding non-reference fields or 7 ; constant null references may be perfectly legal. (If unimplemented.) 8 ; The general structure of these tests is: 9 ; - learn a fact about memory (via an assume) 10 ; - cross a statepoint 11 ; - check the same fact about memory (which we no longer know) 12 13 target datalayout = "e-i64:64-f80:128-n8:16:32:64-S128" 14 target triple = "x86_64-pc-linux-gnu" 15 16 ; If not at a statepoint, we could forward known memory values 17 ; across this call. 18 declare void @func() readonly 19 20 ;; Forwarding the value of a pointer load is invalid since it may have 21 ;; changed at the safepoint. Forwarding a non-gc pointer value would 22 ;; be valid, but is not currently implemented. 23 define i1 @test_load_forward(i32 addrspace(1)* addrspace(1)* %p) gc "statepoint-example" { 24 entry: 25 %before = load i32 addrspace(1)*, i32 addrspace(1)* addrspace(1)* %p 26 %cmp1 = call i1 @f(i32 addrspace(1)* %before) 27 call void @llvm.assume(i1 %cmp1) 28 %safepoint_token = tail call token (i64, i32, void ()*, i32, i32, ...) @llvm.experimental.gc.statepoint.p0f_isVoidf(i64 0, i32 0, void ()* @func, i32 0, i32 0, i32 0, i32 0, i32 addrspace(1)* addrspace(1)* %p) 29 %pnew = call i32 addrspace(1)* addrspace(1)* @llvm.experimental.gc.relocate.p1p1i32(token %safepoint_token, i32 7, i32 7) 30 %after = load i32 addrspace(1)*, i32 addrspace(1)* addrspace(1)* %pnew 31 %cmp2 = call i1 @f(i32 addrspace(1)* %after) 32 ret i1 %cmp2 33 34 ; CHECK-OPT-LABEL: test_load_forward 35 ; CHECK-OPT: ret i1 %cmp2 36 ; CHECK-LLC-LABEL: test_load_forward 37 ; CHECK-LLC: callq f 38 } 39 40 ;; Same as above, but forwarding from a store 41 define i1 @test_store_forward(i32 addrspace(1)* addrspace(1)* %p, 42 i32 addrspace(1)* %v) gc "statepoint-example" { 43 entry: 44 %cmp1 = call i1 @f(i32 addrspace(1)* %v) 45 call void @llvm.assume(i1 %cmp1) 46 store i32 addrspace(1)* %v, i32 addrspace(1)* addrspace(1)* %p 47 %safepoint_token = tail call token (i64, i32, void ()*, i32, i32, ...) @llvm.experimental.gc.statepoint.p0f_isVoidf(i64 0, i32 0, void ()* @func, i32 0, i32 0, i32 0, i32 0, i32 addrspace(1)* addrspace(1)* %p) 48 %pnew = call i32 addrspace(1)* addrspace(1)* @llvm.experimental.gc.relocate.p1p1i32(token %safepoint_token, i32 7, i32 7) 49 %after = load i32 addrspace(1)*, i32 addrspace(1)* addrspace(1)* %pnew 50 %cmp2 = call i1 @f(i32 addrspace(1)* %after) 51 ret i1 %cmp2 52 53 ; CHECK-OPT-LABEL: test_store_forward 54 ; CHECK-OPT: ret i1 %cmp2 55 ; CHECK-LLC-LABEL: test_store_forward 56 ; CHECK-LLC: callq f 57 } 58 59 ; A predicate on the pointer which is not simply null, but whose value 60 ; would be known unchanged if the pointer value could be forwarded. 61 ; The implementation of such a function could inspect the integral value 62 ; of the pointer and is thus not safe to reuse after a statepoint. 63 declare i1 @f(i32 addrspace(1)* %v) readnone 64 65 ; This is a variant of the test_load_forward test which is intended to 66 ; highlight the fact that a gc pointer can be stored in part of the heap 67 ; that is not itself GC managed. The GC may have an external mechanism 68 ; to know about and update that value at a safepoint. Note that the 69 ; statepoint does not provide the collector with this root. 70 define i1 @test_load_forward_nongc_heap(i32 addrspace(1)** %p) gc "statepoint-example" { 71 entry: 72 %before = load i32 addrspace(1)*, i32 addrspace(1)** %p 73 %cmp1 = call i1 @f(i32 addrspace(1)* %before) 74 call void @llvm.assume(i1 %cmp1) 75 call token (i64, i32, void ()*, i32, i32, ...) @llvm.experimental.gc.statepoint.p0f_isVoidf(i64 0, i32 0, void ()* @func, i32 0, i32 0, i32 0, i32 0) 76 %after = load i32 addrspace(1)*, i32 addrspace(1)** %p 77 %cmp2 = call i1 @f(i32 addrspace(1)* %after) 78 ret i1 %cmp2 79 80 ; CHECK-OPT-LABEL: test_load_forward_nongc_heap 81 ; CHECK-OPT: ret i1 %cmp2 82 ; CHECK-LLC-LABEL: test_load_forward_nongc_heap 83 ; CHECK-LLC: callq f 84 } 85 86 ;; Same as above, but forwarding from a store 87 define i1 @test_store_forward_nongc_heap(i32 addrspace(1)** %p, 88 i32 addrspace(1)* %v) gc "statepoint-example" { 89 entry: 90 %cmp1 = call i1 @f(i32 addrspace(1)* %v) 91 call void @llvm.assume(i1 %cmp1) 92 store i32 addrspace(1)* %v, i32 addrspace(1)** %p 93 call token (i64, i32, void ()*, i32, i32, ...) @llvm.experimental.gc.statepoint.p0f_isVoidf(i64 0, i32 0, void ()* @func, i32 0, i32 0, i32 0, i32 0) 94 %after = load i32 addrspace(1)*, i32 addrspace(1)** %p 95 %cmp2 = call i1 @f(i32 addrspace(1)* %after) 96 ret i1 %cmp2 97 98 ; CHECK-OPT-LABEL: test_store_forward_nongc_heap 99 ; CHECK-OPT: ret i1 %cmp2 100 ; CHECK-LLC-LABEL: test_store_forward_nongc_heap 101 ; CHECK-LLC: callq f 102 } 103 104 declare void @llvm.assume(i1) 105 declare token @llvm.experimental.gc.statepoint.p0f_isVoidf(i64, i32, void ()*, i32, i32, ...) 106 declare i32 addrspace(1)* addrspace(1)* @llvm.experimental.gc.relocate.p1p1i32(token, i32, i32) #3 107