1; RUN: opt -functionattrs -S < %s | FileCheck %s --check-prefixes=FNATTR,EITHER 2; RUN: opt -passes=function-attrs -S < %s | FileCheck %s --check-prefixes=FNATTR,EITHER 3; RUN: opt -attributor -attributor-manifest-internal -attributor-disable=false -S < %s | FileCheck %s --check-prefixes=ATTRIBUTOR,EITHER 4; RUN: opt -passes=attributor -attributor-manifest-internal -attributor-disable=false -S < %s | FileCheck %s --check-prefixes=ATTRIBUTOR,EITHER 5 6@g = global i32* null ; <i32**> [#uses=1] 7 8; FNATTR: define i32* @c1(i32* readnone returned %q) 9; ATTRIBUTOR: define i32* @c1(i32* readnone returned "no-capture-maybe-returned" %q) 10define i32* @c1(i32* %q) { 11 ret i32* %q 12} 13 14; FNATTR: define void @c2(i32* %q) 15; ATTRIBUTOR: define void @c2(i32* writeonly %q) 16; It would also be acceptable to mark %q as readnone. Update @c3 too. 17define void @c2(i32* %q) { 18 store i32* %q, i32** @g 19 ret void 20} 21 22; FNATTR: define void @c3(i32* %q) 23; ATTRIBUTOR: define void @c3(i32* writeonly %q) 24define void @c3(i32* %q) { 25 call void @c2(i32* %q) 26 ret void 27} 28 29; FNATTR: define i1 @c4(i32* %q, i32 %bitno) 30; ATTRIBUTOR: define i1 @c4(i32* readnone %q, i32 %bitno) 31define i1 @c4(i32* %q, i32 %bitno) { 32 %tmp = ptrtoint i32* %q to i32 33 %tmp2 = lshr i32 %tmp, %bitno 34 %bit = trunc i32 %tmp2 to i1 35 br i1 %bit, label %l1, label %l0 36l0: 37 ret i1 0 ; escaping value not caught by def-use chaining. 38l1: 39 ret i1 1 ; escaping value not caught by def-use chaining. 40} 41 42; c4b is c4 but without the escaping part 43; FNATTR: define i1 @c4b(i32* %q, i32 %bitno) 44; ATTRIBUTOR: define i1 @c4b(i32* nocapture readnone %q, i32 %bitno) 45define i1 @c4b(i32* %q, i32 %bitno) { 46 %tmp = ptrtoint i32* %q to i32 47 %tmp2 = lshr i32 %tmp, %bitno 48 %bit = trunc i32 %tmp2 to i1 49 br i1 %bit, label %l1, label %l0 50l0: 51 ret i1 0 ; not escaping! 52l1: 53 ret i1 0 ; not escaping! 54} 55 56@lookup_table = global [2 x i1] [ i1 0, i1 1 ] 57 58; FNATTR: define i1 @c5(i32* %q, i32 %bitno) 59; ATTRIBUTOR: define i1 @c5(i32* readonly %q, i32 %bitno) 60define i1 @c5(i32* %q, i32 %bitno) { 61 %tmp = ptrtoint i32* %q to i32 62 %tmp2 = lshr i32 %tmp, %bitno 63 %bit = and i32 %tmp2, 1 64 ; subtle escape mechanism follows 65 %lookup = getelementptr [2 x i1], [2 x i1]* @lookup_table, i32 0, i32 %bit 66 %val = load i1, i1* %lookup 67 ret i1 %val 68} 69 70declare void @throw_if_bit_set(i8*, i8) readonly 71 72; EITHER: define i1 @c6(i8* readonly %q, i8 %bit) 73define i1 @c6(i8* %q, i8 %bit) personality i32 (...)* @__gxx_personality_v0 { 74 invoke void @throw_if_bit_set(i8* %q, i8 %bit) 75 to label %ret0 unwind label %ret1 76ret0: 77 ret i1 0 78ret1: 79 %exn = landingpad {i8*, i32} 80 cleanup 81 ret i1 1 82} 83 84declare i32 @__gxx_personality_v0(...) 85 86define i1* @lookup_bit(i32* %q, i32 %bitno) readnone nounwind { 87 %tmp = ptrtoint i32* %q to i32 88 %tmp2 = lshr i32 %tmp, %bitno 89 %bit = and i32 %tmp2, 1 90 %lookup = getelementptr [2 x i1], [2 x i1]* @lookup_table, i32 0, i32 %bit 91 ret i1* %lookup 92} 93 94; EITHER: define i1 @c7(i32* readonly %q, i32 %bitno) 95define i1 @c7(i32* %q, i32 %bitno) { 96 %ptr = call i1* @lookup_bit(i32* %q, i32 %bitno) 97 %val = load i1, i1* %ptr 98 ret i1 %val 99} 100 101 102; EITHER: define i32 @nc1(i32* %q, i32* nocapture %p, i1 %b) 103define i32 @nc1(i32* %q, i32* %p, i1 %b) { 104e: 105 br label %l 106l: 107 %x = phi i32* [ %p, %e ] 108 %y = phi i32* [ %q, %e ] 109 %tmp = bitcast i32* %x to i32* ; <i32*> [#uses=2] 110 %tmp2 = select i1 %b, i32* %tmp, i32* %y 111 %val = load i32, i32* %tmp2 ; <i32> [#uses=1] 112 store i32 0, i32* %tmp 113 store i32* %y, i32** @g 114 ret i32 %val 115} 116 117; EITHER: define i32 @nc1_addrspace(i32* %q, i32 addrspace(1)* nocapture %p, i1 %b) 118define i32 @nc1_addrspace(i32* %q, i32 addrspace(1)* %p, i1 %b) { 119e: 120 br label %l 121l: 122 %x = phi i32 addrspace(1)* [ %p, %e ] 123 %y = phi i32* [ %q, %e ] 124 %tmp = addrspacecast i32 addrspace(1)* %x to i32* ; <i32*> [#uses=2] 125 %tmp2 = select i1 %b, i32* %tmp, i32* %y 126 %val = load i32, i32* %tmp2 ; <i32> [#uses=1] 127 store i32 0, i32* %tmp 128 store i32* %y, i32** @g 129 ret i32 %val 130} 131 132; EITHER: define void @nc2(i32* nocapture %p, i32* %q) 133define void @nc2(i32* %p, i32* %q) { 134 %1 = call i32 @nc1(i32* %q, i32* %p, i1 0) ; <i32> [#uses=0] 135 ret void 136} 137 138 139; FNATTR: define void @nc3(void ()* nocapture %p) 140; ATTRIBUTOR: define void @nc3(void ()* nocapture nonnull %p) 141define void @nc3(void ()* %p) { 142 call void %p() 143 ret void 144} 145 146declare void @external(i8*) readonly nounwind 147; EITHER: define void @nc4(i8* nocapture readonly %p) 148define void @nc4(i8* %p) { 149 call void @external(i8* %p) 150 ret void 151} 152 153; FNATTR: define void @nc5(void (i8*)* nocapture %f, i8* nocapture %p) 154; ATTRIBUTOR: define void @nc5(void (i8*)* nocapture nonnull %f, i8* nocapture %p) 155define void @nc5(void (i8*)* %f, i8* %p) { 156 call void %f(i8* %p) readonly nounwind 157 call void %f(i8* nocapture %p) 158 ret void 159} 160 161; FNATTR: define void @test1_1(i8* nocapture readnone %x1_1, i8* %y1_1, i1 %c) 162; ATTRIBUTOR: define void @test1_1(i8* nocapture readnone %x1_1, i8* nocapture readnone %y1_1, i1 %c) 163; It would be acceptable to add readnone to %y1_1 and %y1_2. 164define void @test1_1(i8* %x1_1, i8* %y1_1, i1 %c) { 165 call i8* @test1_2(i8* %x1_1, i8* %y1_1, i1 %c) 166 store i32* null, i32** @g 167 ret void 168} 169 170; FNATTR: define i8* @test1_2(i8* nocapture readnone %x1_2, i8* returned %y1_2, i1 %c) 171; ATTRIBUTOR: define i8* @test1_2(i8* nocapture readnone %x1_2, i8* readnone returned "no-capture-maybe-returned" %y1_2, i1 %c) 172define i8* @test1_2(i8* %x1_2, i8* %y1_2, i1 %c) { 173 br i1 %c, label %t, label %f 174t: 175 call void @test1_1(i8* %x1_2, i8* %y1_2, i1 %c) 176 store i32* null, i32** @g 177 br label %f 178f: 179 ret i8* %y1_2 180} 181 182; EITHER: define void @test2(i8* nocapture readnone %x2) 183define void @test2(i8* %x2) { 184 call void @test2(i8* %x2) 185 store i32* null, i32** @g 186 ret void 187} 188 189; EITHER: define void @test3(i8* nocapture readnone %x3, i8* nocapture readnone %y3, i8* nocapture readnone %z3) 190define void @test3(i8* %x3, i8* %y3, i8* %z3) { 191 call void @test3(i8* %z3, i8* %y3, i8* %x3) 192 store i32* null, i32** @g 193 ret void 194} 195 196; FNATTR: define void @test4_1(i8* %x4_1, i1 %c) 197; ATTRIBUTOR: define void @test4_1(i8* nocapture readnone %x4_1, i1 %c) 198define void @test4_1(i8* %x4_1, i1 %c) { 199 call i8* @test4_2(i8* %x4_1, i8* %x4_1, i8* %x4_1, i1 %c) 200 store i32* null, i32** @g 201 ret void 202} 203 204; FNATTR: define i8* @test4_2(i8* nocapture readnone %x4_2, i8* readnone returned %y4_2, i8* nocapture readnone %z4_2, i1 %c) 205; ATTRIBUTOR: define i8* @test4_2(i8* nocapture readnone %x4_2, i8* readnone returned "no-capture-maybe-returned" %y4_2, i8* nocapture readnone %z4_2, i1 %c) 206define i8* @test4_2(i8* %x4_2, i8* %y4_2, i8* %z4_2, i1 %c) { 207 br i1 %c, label %t, label %f 208t: 209 call void @test4_1(i8* null, i1 %c) 210 store i32* null, i32** @g 211 br label %f 212f: 213 ret i8* %y4_2 214} 215 216declare i8* @test5_1(i8* %x5_1) 217 218; EITHER: define void @test5_2(i8* %x5_2) 219define void @test5_2(i8* %x5_2) { 220 call i8* @test5_1(i8* %x5_2) 221 store i32* null, i32** @g 222 ret void 223} 224 225declare void @test6_1(i8* %x6_1, i8* nocapture %y6_1, ...) 226 227; EITHER: define void @test6_2(i8* %x6_2, i8* nocapture %y6_2, i8* %z6_2) 228define void @test6_2(i8* %x6_2, i8* %y6_2, i8* %z6_2) { 229 call void (i8*, i8*, ...) @test6_1(i8* %x6_2, i8* %y6_2, i8* %z6_2) 230 store i32* null, i32** @g 231 ret void 232} 233 234; FNATTR: define void @test_cmpxchg(i32* nocapture %p) 235; ATTRIBUTOR: define void @test_cmpxchg(i32* nocapture nonnull dereferenceable(4) %p) 236define void @test_cmpxchg(i32* %p) { 237 cmpxchg i32* %p, i32 0, i32 1 acquire monotonic 238 ret void 239} 240 241; FNATTR: define void @test_cmpxchg_ptr(i32** nocapture %p, i32* %q) 242; ATTRIBUTOR: define void @test_cmpxchg_ptr(i32** nocapture nonnull dereferenceable(8) %p, i32* %q) 243define void @test_cmpxchg_ptr(i32** %p, i32* %q) { 244 cmpxchg i32** %p, i32* null, i32* %q acquire monotonic 245 ret void 246} 247 248; FNATTR: define void @test_atomicrmw(i32* nocapture %p) 249; ATTRIBUTOR: define void @test_atomicrmw(i32* nocapture nonnull dereferenceable(4) %p) 250define void @test_atomicrmw(i32* %p) { 251 atomicrmw add i32* %p, i32 1 seq_cst 252 ret void 253} 254 255; EITHER: define void @test_volatile(i32* %x) 256define void @test_volatile(i32* %x) { 257entry: 258 %gep = getelementptr i32, i32* %x, i64 1 259 store volatile i32 0, i32* %gep, align 4 260 ret void 261} 262 263; EITHER: nocaptureLaunder(i8* nocapture %p) 264define void @nocaptureLaunder(i8* %p) { 265entry: 266 %b = call i8* @llvm.launder.invariant.group.p0i8(i8* %p) 267 store i8 42, i8* %b 268 ret void 269} 270 271@g2 = global i8* null 272; EITHER: define void @captureLaunder(i8* %p) 273define void @captureLaunder(i8* %p) { 274 %b = call i8* @llvm.launder.invariant.group.p0i8(i8* %p) 275 store i8* %b, i8** @g2 276 ret void 277} 278 279; FNATTR: @nocaptureStrip(i8* nocapture %p) 280; ATTRIBUTOR: @nocaptureStrip(i8* nocapture writeonly %p) 281define void @nocaptureStrip(i8* %p) { 282entry: 283 %b = call i8* @llvm.strip.invariant.group.p0i8(i8* %p) 284 store i8 42, i8* %b 285 ret void 286} 287 288@g3 = global i8* null 289; FNATTR: define void @captureStrip(i8* %p) 290; ATTRIBUTOR: define void @captureStrip(i8* writeonly %p) 291define void @captureStrip(i8* %p) { 292 %b = call i8* @llvm.strip.invariant.group.p0i8(i8* %p) 293 store i8* %b, i8** @g3 294 ret void 295} 296 297; EITHER: define i1 @captureICmp(i32* readnone %x) 298define i1 @captureICmp(i32* %x) { 299 %1 = icmp eq i32* %x, null 300 ret i1 %1 301} 302 303; EITHER: define i1 @captureICmpRev(i32* readnone %x) 304define i1 @captureICmpRev(i32* %x) { 305 %1 = icmp eq i32* null, %x 306 ret i1 %1 307} 308 309; FNATTR: define i1 @nocaptureInboundsGEPICmp(i32* nocapture readnone %x) 310; ATTRIBUTOR: define i1 @nocaptureInboundsGEPICmp(i32* nocapture nonnull readnone %x) 311define i1 @nocaptureInboundsGEPICmp(i32* %x) { 312 %1 = getelementptr inbounds i32, i32* %x, i32 5 313 %2 = bitcast i32* %1 to i8* 314 %3 = icmp eq i8* %2, null 315 ret i1 %3 316} 317 318; FNATTR: define i1 @nocaptureInboundsGEPICmpRev(i32* nocapture readnone %x) 319; ATTRIBUTOR: define i1 @nocaptureInboundsGEPICmpRev(i32* nocapture nonnull readnone %x) 320define i1 @nocaptureInboundsGEPICmpRev(i32* %x) { 321 %1 = getelementptr inbounds i32, i32* %x, i32 5 322 %2 = bitcast i32* %1 to i8* 323 %3 = icmp eq i8* null, %2 324 ret i1 %3 325} 326 327; EITHER: define i1 @nocaptureDereferenceableOrNullICmp(i32* nocapture readnone dereferenceable_or_null(4) %x) 328define i1 @nocaptureDereferenceableOrNullICmp(i32* dereferenceable_or_null(4) %x) { 329 %1 = bitcast i32* %x to i8* 330 %2 = icmp eq i8* %1, null 331 ret i1 %2 332} 333 334; EITHER: define i1 @captureDereferenceableOrNullICmp(i32* readnone dereferenceable_or_null(4) %x) 335define i1 @captureDereferenceableOrNullICmp(i32* dereferenceable_or_null(4) %x) "null-pointer-is-valid"="true" { 336 %1 = bitcast i32* %x to i8* 337 %2 = icmp eq i8* %1, null 338 ret i1 %2 339} 340 341declare void @unknown(i8*) 342define void @test_callsite() { 343entry: 344; We know that 'null' in AS 0 does not alias anything and cannot be captured. Though the latter is not qurried -> derived atm. 345; ATTRIBUTOR: call void @unknown(i8* noalias null) 346 call void @unknown(i8* null) 347 ret void 348} 349 350declare i8* @unknownpi8pi8(i8*,i8* returned) 351define i8* @test_returned1(i8* %A, i8* returned %B) nounwind readonly { 352; ATTRIBUTOR: define i8* @test_returned1(i8* nocapture readonly %A, i8* readonly returned %B) 353entry: 354 %p = call i8* @unknownpi8pi8(i8* %A, i8* %B) 355 ret i8* %p 356} 357 358define i8* @test_returned2(i8* %A, i8* %B) { 359; ATTRIBUTOR: define i8* @test_returned2(i8* nocapture readonly %A, i8* readonly returned %B) 360entry: 361 %p = call i8* @unknownpi8pi8(i8* %A, i8* %B) nounwind readonly 362 ret i8* %p 363} 364 365declare i8* @llvm.launder.invariant.group.p0i8(i8*) 366declare i8* @llvm.strip.invariant.group.p0i8(i8*) 367