Home | History | Annotate | Download | only in X86
      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