1 ; A collection of liveness test cases to ensure we're reporting the 2 ; correct live values at statepoints 3 ; RUN: opt -rewrite-statepoints-for-gc -rs4gc-use-deopt-bundles -spp-rematerialization-threshold=0 -S < %s | FileCheck %s 4 5 ; Tests to make sure we consider %obj live in both the taken and untaken 6 ; predeccessor of merge. 7 8 define i64 addrspace(1)* @test1(i1 %cmp, i64 addrspace(1)* %obj) gc "statepoint-example" { 9 ; CHECK-LABEL: @test1 10 entry: 11 br i1 %cmp, label %taken, label %untaken 12 13 taken: ; preds = %entry 14 ; CHECK-LABEL: taken: 15 ; CHECK-NEXT: gc.statepoint 16 ; CHECK-NEXT: %obj.relocated = call coldcc i8 addrspace(1)* 17 ; CHECK-NEXT: bitcast 18 ; CHECK-NEXT: br label %merge 19 call void @foo() [ "deopt"() ] 20 br label %merge 21 22 untaken: ; preds = %entry 23 ; CHECK-LABEL: untaken: 24 ; CHECK-NEXT: gc.statepoint 25 ; CHECK-NEXT: %obj.relocated2 = call coldcc i8 addrspace(1)* 26 ; CHECK-NEXT: bitcast 27 ; CHECK-NEXT: br label %merge 28 call void @foo() [ "deopt"() ] 29 br label %merge 30 31 merge: ; preds = %untaken, %taken 32 ; CHECK-LABEL: merge: 33 ; CHECK-NEXT: %.0 = phi i64 addrspace(1)* [ %obj.relocated.casted, %taken ], [ %obj.relocated2.casted, %untaken ] 34 ; CHECK-NEXT: ret i64 addrspace(1)* %.0 35 ; A local kill should not effect liveness in predecessor block 36 ret i64 addrspace(1)* %obj 37 } 38 39 define i64 addrspace(1)* @test2(i1 %cmp, i64 addrspace(1)** %loc) gc "statepoint-example" { 40 ; CHECK-LABEL: @test2 41 entry: 42 ; CHECK-LABEL: entry: 43 ; CHECK-NEXT: gc.statepoint 44 ; CHECK-NEXT: br 45 call void @foo() [ "deopt"() ] 46 br i1 %cmp, label %taken, label %untaken 47 48 taken: ; preds = %entry 49 ; CHECK-LABEL: taken: 50 ; CHECK-NEXT: %obj = load 51 ; CHECK-NEXT: gc.statepoint 52 ; CHECK-NEXT: gc.relocate 53 ; CHECK-NEXT: bitcast 54 ; CHECK-NEXT: ret i64 addrspace(1)* %obj.relocated.casted 55 ; A local kill should effect values live from a successor phi. Also, we 56 ; should only propagate liveness from a phi to the appropriate predecessors. 57 %obj = load i64 addrspace(1)*, i64 addrspace(1)** %loc 58 call void @foo() [ "deopt"() ] 59 ret i64 addrspace(1)* %obj 60 61 untaken: ; preds = %entry 62 ret i64 addrspace(1)* null 63 } 64 65 define i64 addrspace(1)* @test3(i1 %cmp, i64 addrspace(1)** %loc) gc "statepoint-example" { 66 ; CHECK-LABEL: @test3 67 entry: 68 br i1 %cmp, label %taken, label %untaken 69 70 taken: ; preds = %entry 71 ; CHECK-LABEL: taken: 72 ; CHECK-NEXT: gc.statepoint 73 ; CHECK-NEXT: %obj = load 74 ; CHECK-NEXT: gc.statepoint 75 ; CHECK-NEXT: %obj.relocated = call coldcc i8 addrspace(1)* 76 ; CHECK-NEXT: bitcast 77 ; CHECK-NEXT: br label %merge 78 call void @foo() [ "deopt"() ] 79 %obj = load i64 addrspace(1)*, i64 addrspace(1)** %loc 80 call void @foo() [ "deopt"() ] 81 br label %merge 82 83 untaken: ; preds = %entry 84 ; CHECK-LABEL: taken: 85 ; CHECK-NEXT: gc.statepoint 86 ; CHECK-NEXT: br label %merge 87 ; A base pointer must be live if it is needed at a later statepoint, 88 ; even if the base pointer is otherwise unused. 89 call void @foo() [ "deopt"() ] 90 br label %merge 91 92 merge: ; preds = %untaken, %taken 93 %phi = phi i64 addrspace(1)* [ %obj, %taken ], [ null, %untaken ] 94 ret i64 addrspace(1)* %phi 95 } 96 97 define i64 addrspace(1)* @test4(i1 %cmp, i64 addrspace(1)* %obj) gc "statepoint-example" { 98 ; CHECK-LABEL: @test4 99 entry: 100 ; CHECK-LABEL: entry: 101 ; CHECK-NEXT: %derived = getelementptr 102 ; CHECK-NEXT: gc.statepoint 103 ; CHECK-NEXT: %derived.relocated = 104 ; CHECK-NEXT: bitcast 105 ; CHECK-NEXT: %obj.relocated = 106 ; CHECK-NEXT: bitcast 107 ; CHECK-NEXT: gc.statepoint 108 ; CHECK-NEXT: %derived.relocated2 = 109 ; CHECK-NEXT: bitcast 110 111 ; Note: It's legal to relocate obj again, but not strictly needed 112 ; CHECK-NEXT: %obj.relocated3 = 113 ; CHECK-NEXT: bitcast 114 ; CHECK-NEXT: ret i64 addrspace(1)* %derived.relocated2.casted 115 ; 116 ; Make sure that a phi def visited during iteration is considered a kill. 117 ; Also, liveness after base pointer analysis can change based on new uses, 118 ; not just new defs. 119 %derived = getelementptr i64, i64 addrspace(1)* %obj, i64 8 120 call void @foo() [ "deopt"() ] 121 call void @foo() [ "deopt"() ] 122 ret i64 addrspace(1)* %derived 123 } 124 125 declare void @consume(...) readonly "gc-leaf-function" 126 127 define i64 addrspace(1)* @test5(i1 %cmp, i64 addrspace(1)* %obj) gc "statepoint-example" { 128 ; CHECK-LABEL: @test5 129 entry: 130 br i1 %cmp, label %taken, label %untaken 131 132 taken: ; preds = %entry 133 ; CHECK-LABEL: taken: 134 ; CHECK-NEXT: gc.statepoint 135 ; CHECK-NEXT: %obj.relocated = call coldcc i8 addrspace(1)* 136 ; CHECK-NEXT: bitcast 137 ; CHECK-NEXT: br label %merge 138 call void @foo() [ "deopt"() ] 139 br label %merge 140 141 untaken: ; preds = %entry 142 ; CHECK-LABEL: untaken: 143 ; CHECK-NEXT: br label %merge 144 br label %merge 145 146 merge: ; preds = %untaken, %taken 147 ; CHECK-LABEL: merge: 148 ; CHECK-NEXT: %.0 = phi i64 addrspace(1)* 149 ; CHECK-NEXT: %obj2a = phi 150 ; CHECK-NEXT: @consume 151 ; CHECK-NEXT: br label %final 152 %obj2a = phi i64 addrspace(1)* [ %obj, %taken ], [ null, %untaken ] 153 call void (...) @consume(i64 addrspace(1)* %obj2a) 154 br label %final 155 156 final: ; preds = %merge 157 ; CHECK-LABEL: final: 158 ; CHECK-NEXT: @consume 159 ; CHECK-NEXT: ret i64 addrspace(1)* %.0 160 call void (...) @consume(i64 addrspace(1)* %obj2a) 161 ret i64 addrspace(1)* %obj 162 } 163 164 declare void @foo() 165 166