1; RUN: opt -S -passes=rewrite-statepoints-for-gc < %s | FileCheck %s 2 3target datalayout = "e-ni:1:6" 4 5; constants don't get relocated. 6@G = addrspace(1) global i8 5 7 8declare void @foo() 9 10define i8 @test() gc "statepoint-example" { 11; CHECK-LABEL: @test 12; CHECK: gc.statepoint 13; CHECK-NEXT: load i8, ptr addrspace(1) inttoptr (i64 15 to ptr addrspace(1)) 14; Mostly just here to show reasonable code test can come from. 15entry: 16 call void @foo() [ "deopt"() ] 17 %res = load i8, ptr addrspace(1) inttoptr (i64 15 to ptr addrspace(1)) 18 ret i8 %res 19} 20 21define i8 @test2(ptr addrspace(1) %p) gc "statepoint-example" { 22; CHECK-LABEL: @test2 23; CHECK: gc.statepoint 24; CHECK-NEXT: gc.relocate 25; CHECK-NEXT: icmp 26; Globals don't move and thus don't get relocated 27entry: 28 call void @foo() [ "deopt"() ] 29 %cmp = icmp eq ptr addrspace(1) %p, null 30 br i1 %cmp, label %taken, label %not_taken 31 32taken: ; preds = %not_taken, %entry 33 ret i8 0 34 35not_taken: ; preds = %entry 36 %cmp2 = icmp ne ptr addrspace(1) %p, null 37 br i1 %cmp2, label %taken, label %dead 38 39dead: ; preds = %not_taken 40 %addr = getelementptr i8, ptr addrspace(1) %p, i32 15 41 %res = load i8, ptr addrspace(1) %addr 42 ret i8 %res 43} 44 45define i8 @test3(i1 %always_true) gc "statepoint-example" { 46; CHECK-LABEL: @test3 47; CHECK: gc.statepoint 48; CHECK-NEXT: load i8, ptr addrspace(1) @G 49entry: 50 call void @foo() [ "deopt"() ] 51 %res = load i8, ptr addrspace(1) @G, align 1 52 ret i8 %res 53} 54 55; Even for source languages without constant references, we can 56; see constants can show up along paths where the value is dead. 57; This is particular relevant when computing bases of PHIs. 58define ptr addrspace(1) @test4(ptr addrspace(1) %p) gc "statepoint-example" { 59; CHECK-LABEL: @test4 60entry: 61 %is_null = icmp eq ptr addrspace(1) %p, null 62 br i1 %is_null, label %split, label %join 63 64split: 65 call void @foo() 66 %arg_value_addr.i = getelementptr inbounds i8, ptr addrspace(1) %p, i64 8 67 br label %join 68 69join: 70; CHECK-LABEL: join 71; CHECK: %addr2.base = 72 %addr2 = phi ptr addrspace(1) [ %arg_value_addr.i, %split ], [ inttoptr (i64 8 to ptr addrspace(1)), %entry ] 73 ;; NOTE: This particular example can be jump-threaded, but in general, 74 ;; we can't, and have to deal with the resulting IR. 75 br i1 %is_null, label %early-exit, label %use 76 77early-exit: 78 ret ptr addrspace(1) null 79 80use: 81; CHECK-LABEL: use: 82; CHECK: gc.statepoint 83; CHECK: gc.relocate 84 call void @foo() 85 %res = load ptr addrspace(1), ptr addrspace(1) %addr2, align 1 86 ret ptr addrspace(1) %res 87} 88 89; Globals don't move and thus don't get relocated 90define ptr addrspace(1) @test5(i1 %always_true) gc "statepoint-example" { 91; CHECK-LABEL: @test5 92; CHECK: gc.statepoint 93; CHECK-NEXT: %res = extractelement <2 x ptr addrspace(1)> <ptr addrspace(1) @G, ptr addrspace(1) @G>, i32 0 94entry: 95 call void @foo() 96 %res = extractelement <2 x ptr addrspace(1)> <ptr addrspace(1) @G, ptr addrspace(1) @G>, i32 0 97 ret ptr addrspace(1) %res 98} 99 100define ptr addrspace(1) @test6(i64 %arg) gc "statepoint-example" { 101entry: 102 ; Don't fail any assertions and don't record null as a live value 103 ; CHECK-LABEL: test6 104 ; CHECK: gc.statepoint 105 ; CHECK-NOT: call {{.*}}gc.relocate 106 %load_addr = getelementptr i8, ptr addrspace(1) null, i64 %arg 107 call void @foo() [ "deopt"() ] 108 ret ptr addrspace(1) %load_addr 109} 110 111define ptr addrspace(1) @test7(i64 %arg) gc "statepoint-example" { 112entry: 113 ; Same as test7 but use regular constant instead of a null 114 ; CHECK-LABEL: test7 115 ; CHECK: gc.statepoint 116 ; CHECK-NOT: call {{.*}}gc.relocate 117 %load_addr = getelementptr i8, ptr addrspace(1) inttoptr (i64 15 to ptr addrspace(1)), i64 %arg 118 call void @foo() [ "deopt"() ] 119 ret ptr addrspace(1) %load_addr 120} 121 122define i8 @test8(ptr addrspace(1) %p) gc "statepoint-example" { 123; Checks that base( phi(gep null, oop) ) = phi(null, base(oop)) and that we 124; correctly relocate this value 125; CHECK-LABEL: @test8 126entry: 127 %is_null = icmp eq ptr addrspace(1) %p, null 128 br i1 %is_null, label %null.crit-edge, label %not-null 129 130not-null: 131 %load_addr = getelementptr inbounds i8, ptr addrspace(1) %p, i64 8 132 br label %join 133 134null.crit-edge: 135 %load_addr.const = getelementptr inbounds i8, ptr addrspace(1) null, i64 8 136 br label %join 137 138join: 139 %addr = phi ptr addrspace(1) [ %load_addr, %not-null ], [%load_addr.const, %null.crit-edge] 140 ; CHECK: %addr.base = phi ptr addrspace(1) 141 ; CHECK-DAG: [ %p, %not-null ] 142 ; CHECK-DAG: [ null, %null.crit-edge ] 143 ; CHECK: gc.statepoint 144 call void @foo() [ "deopt"() ] 145 ; CHECK-DAG: call {{.*}}gc.relocate{{.*}}(%addr.base, %addr.base) 146 ; CHECK-DAG: call {{.*}}gc.relocate{{.*}}(%addr.base, %addr) 147 br i1 %is_null, label %early-exit, label %use 148 149early-exit: 150 ret i8 0 151 152use: 153 %res = load i8, ptr addrspace(1) %addr, align 1 154 ret i8 %res 155} 156 157define i8 @test9(ptr addrspace(1) %p) gc "statepoint-example" { 158; Checks that base( phi(inttoptr, oop) ) = phi(null, base(oop)) and that we 159; correctly relocate this value 160; CHECK-LABEL: @test9 161entry: 162 %is_null = icmp eq ptr addrspace(1) %p, null 163 br i1 %is_null, label %null.crit-edge, label %not-null 164 165not-null: 166 %load_addr = getelementptr inbounds i8, ptr addrspace(1) %p, i64 8 167 br label %join 168 169null.crit-edge: 170 br label %join 171 172join: 173 %addr = phi ptr addrspace(1) [ %load_addr, %not-null ], [inttoptr (i64 8 to ptr addrspace(1)), %null.crit-edge] 174 ; CHECK: %addr.base = phi ptr addrspace(1) 175 ; CHECK-DAG: [ %p, %not-null ] 176 ; CHECK-DAG: [ null, %null.crit-edge ] 177 ; CHECK: gc.statepoint 178 call void @foo() [ "deopt"() ] 179 ; CHECK-DAG: call {{.*}}gc.relocate{{.*}}(%addr.base, %addr.base) 180 ; CHECK-DAG: call {{.*}}gc.relocate{{.*}}(%addr.base, %addr) 181 br i1 %is_null, label %early-exit, label %use 182 183early-exit: 184 ret i8 0 185 186use: 187 %res = load i8, ptr addrspace(1) %addr, align 1 188 ret i8 %res 189} 190 191define i8 @test10(ptr addrspace(1) %p) gc "statepoint-example" { 192; Checks that base( phi(const gep, oop) ) = phi(null, base(oop)) and that we 193; correctly relocate this value 194; CHECK-LABEL: @test10 195entry: 196 %is_null = icmp eq ptr addrspace(1) %p, null 197 br i1 %is_null, label %null.crit-edge, label %not-null 198 199not-null: 200 %load_addr = getelementptr inbounds i8, ptr addrspace(1) %p, i64 8 201 br label %join 202 203null.crit-edge: 204 br label %join 205 206join: 207 %addr = phi ptr addrspace(1) [ %load_addr, %not-null ], [getelementptr (i8, ptr addrspace(1) null, i64 8), %null.crit-edge] 208 ; CHECK: %addr.base = phi ptr addrspace(1) 209 ; CHECK-DAG: [ %p, %not-null ] 210 ; CHECK-DAG: [ null, %null.crit-edge ] 211 ; CHECK: gc.statepoint 212 call void @foo() [ "deopt"() ] 213 ; CHECK-DAG: call {{.*}}gc.relocate{{.*}}(%addr.base, %addr.base) 214 ; CHECK-DAG: call {{.*}}gc.relocate{{.*}}(%addr.base, %addr) 215 br i1 %is_null, label %early-exit, label %use 216 217early-exit: 218 ret i8 0 219 220use: 221 %res = load i8, ptr addrspace(1) %addr, align 1 222 ret i8 %res 223} 224 225define ptr addrspace(1) @test11(i1 %c) gc "statepoint-example" { 226; CHECK-LABEL: @test11 227; Checks that base( select(const1, const2) ) == null and that we don't record 228; such value in the oop map 229entry: 230 %val = select i1 %c, ptr addrspace(1) inttoptr (i64 8 to ptr addrspace(1)), ptr addrspace(1) inttoptr (i64 15 to ptr addrspace(1)) 231 ; CHECK: gc.statepoint 232 ; CHECK-NOT: call {{.*}}gc.relocate 233 call void @foo() [ "deopt"() ] 234 ret ptr addrspace(1) %val 235} 236 237 238define <2 x ptr addrspace(1)> @test12(i1 %c) gc "statepoint-example" { 239; CHECK-LABEL: @test12 240; Same as test11 but with vectors 241entry: 242 %val = select i1 %c, <2 x ptr addrspace(1)> <ptr addrspace(1) inttoptr (i64 5 to ptr addrspace(1)), 243 ptr addrspace(1) inttoptr (i64 15 to ptr addrspace(1))>, 244 <2 x ptr addrspace(1)> <ptr addrspace(1) inttoptr (i64 30 to ptr addrspace(1)), 245 ptr addrspace(1) inttoptr (i64 60 to ptr addrspace(1))> 246 ; CHECK: gc.statepoint 247 ; CHECK-NOT: call {{.*}}gc.relocate 248 call void @foo() [ "deopt"() ] 249 ret <2 x ptr addrspace(1)> %val 250} 251 252define <2 x ptr addrspace(1)> @test13(i1 %c, <2 x ptr addrspace(1)> %ptr) gc "statepoint-example" { 253; CHECK-LABEL: @test13 254; Similar to test8, test9 and test10 but with vectors 255entry: 256 %val = select i1 %c, <2 x ptr addrspace(1)> %ptr, 257 <2 x ptr addrspace(1)> <ptr addrspace(1) inttoptr (i64 30 to ptr addrspace(1)), ptr addrspace(1) inttoptr (i64 60 to ptr addrspace(1))> 258 ; CHECK: %val.base = select i1 %c, <2 x ptr addrspace(1)> %ptr, <2 x ptr addrspace(1)> zeroinitializer, !is_base_value !0 259 ; CHECK: gc.statepoint 260 call void @foo() [ "deopt"() ] 261 ; CHECK-DAG: call {{.*}}gc.relocate{{.*}}(%val.base, %val.base) 262 ; CHECK-DAG: call {{.*}}gc.relocate{{.*}}(%val.base, %val) 263 ret <2 x ptr addrspace(1)> %val 264} 265 266define ptr addrspace(1) @test14() gc "statepoint-example" { 267; CHECK-LABEL: @test14 268; FIXME: this is a extract element of constant vector and as such should not be relocated, but our current code relocates it since the extractelement will conservatively be marked as conflict during the BDV insertion algorithm. In many cases the extra instruction will be identical to the one already present and will be removed by later phases. 269entry: 270 %val = extractelement <2 x ptr addrspace(1)> <ptr addrspace(1) inttoptr (i64 5 to ptr addrspace(1)), 271 ptr addrspace(1) inttoptr (i64 15 to ptr addrspace(1))>, i32 0 272 ; CHECK: gc.statepoint 273 call void @foo() [ "deopt"() ] 274 ; CHECK-DAG: call {{.*}}gc.relocate{{.*}}(%val.base, %val.base) 275 ; CHECK-DAG: call {{.*}}gc.relocate{{.*}}(%val.base, %val) 276 ret ptr addrspace(1) %val 277}