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 13target datalayout = "e-i64:64-f80:128-n8:16:32:64-S128" 14target triple = "x86_64-pc-linux-gnu" 15 16; If not at a statepoint, we could forward known memory values 17; across this call. 18declare 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. 23define i1 @test_load_forward(i32 addrspace(1)* addrspace(1)* %p) gc "statepoint-example" { 24entry: 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 i32 (void ()*, i32, i32, ...) @llvm.experimental.gc.statepoint.p0f_isVoidf(void ()* @func, 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(i32 %safepoint_token, i32 4, i32 4) 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 41define i1 @test_store_forward(i32 addrspace(1)* addrspace(1)* %p, 42 i32 addrspace(1)* %v) gc "statepoint-example" { 43entry: 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 i32 (void ()*, i32, i32, ...) @llvm.experimental.gc.statepoint.p0f_isVoidf(void ()* @func, 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(i32 %safepoint_token, i32 4, i32 4) 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. 63declare 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. 70define i1 @test_load_forward_nongc_heap(i32 addrspace(1)** %p) gc "statepoint-example" { 71entry: 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 i32 (void ()*, i32, i32, ...) @llvm.experimental.gc.statepoint.p0f_isVoidf(void ()* @func, 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 87define i1 @test_store_forward_nongc_heap(i32 addrspace(1)** %p, 88 i32 addrspace(1)* %v) gc "statepoint-example" { 89entry: 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 i32 (void ()*, i32, i32, ...) @llvm.experimental.gc.statepoint.p0f_isVoidf(void ()* @func, 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 104declare void @llvm.assume(i1) 105declare i32 @llvm.experimental.gc.statepoint.p0f_isVoidf(void ()*, i32, i32, ...) 106declare i32 addrspace(1)* addrspace(1)* @llvm.experimental.gc.relocate.p1p1i32(i32, i32, i32) #3 107 108