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 -attributor-annotate-decl-cs < %s | FileCheck %s --check-prefixes=ATTRIBUTOR,EITHER 4; RUN: opt -passes=attributor -attributor-manifest-internal -attributor-disable=false -S -attributor-annotate-decl-cs < %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* nofree 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* nofree 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* nofree 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* nofree 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 nofree 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* nofree 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; FNATTR: define i1 @c7(i32* readonly %q, i32 %bitno) 95; ATTRIBUTOR: define i1 @c7(i32* nofree readonly %q, i32 %bitno) 96define i1 @c7(i32* %q, i32 %bitno) { 97 %ptr = call i1* @lookup_bit(i32* %q, i32 %bitno) 98 %val = load i1, i1* %ptr 99 ret i1 %val 100} 101 102 103; FNATTR: define i32 @nc1(i32* %q, i32* nocapture %p, i1 %b) 104; ATTRIBUTOR: define i32 @nc1(i32* nofree %q, i32* nocapture nofree %p, i1 %b) 105define i32 @nc1(i32* %q, i32* %p, i1 %b) { 106e: 107 br label %l 108l: 109 %x = phi i32* [ %p, %e ] 110 %y = phi i32* [ %q, %e ] 111 %tmp = bitcast i32* %x to i32* ; <i32*> [#uses=2] 112 %tmp2 = select i1 %b, i32* %tmp, i32* %y 113 %val = load i32, i32* %tmp2 ; <i32> [#uses=1] 114 store i32 0, i32* %tmp 115 store i32* %y, i32** @g 116 ret i32 %val 117} 118 119; FNATTR: define i32 @nc1_addrspace(i32* %q, i32 addrspace(1)* nocapture %p, i1 %b) 120; ATTRIBUTOR: define i32 @nc1_addrspace(i32* nofree %q, i32 addrspace(1)* nocapture nofree %p, i1 %b) 121define i32 @nc1_addrspace(i32* %q, i32 addrspace(1)* %p, i1 %b) { 122e: 123 br label %l 124l: 125 %x = phi i32 addrspace(1)* [ %p, %e ] 126 %y = phi i32* [ %q, %e ] 127 %tmp = addrspacecast i32 addrspace(1)* %x to i32* ; <i32*> [#uses=2] 128 %tmp2 = select i1 %b, i32* %tmp, i32* %y 129 %val = load i32, i32* %tmp2 ; <i32> [#uses=1] 130 store i32 0, i32* %tmp 131 store i32* %y, i32** @g 132 ret i32 %val 133} 134 135; FNATTR: define void @nc2(i32* nocapture %p, i32* %q) 136; ATTRIBUTOR: define void @nc2(i32* nocapture nofree %p, i32* nofree %q) 137define void @nc2(i32* %p, i32* %q) { 138 %1 = call i32 @nc1(i32* %q, i32* %p, i1 0) ; <i32> [#uses=0] 139 ret void 140} 141 142 143; FNATTR: define void @nc3(void ()* nocapture %p) 144; ATTRIBUTOR: define void @nc3(void ()* nocapture nofree nonnull %p) 145define void @nc3(void ()* %p) { 146 call void %p() 147 ret void 148} 149 150declare void @external(i8*) readonly nounwind 151; EITHER: define void @nc4(i8* nocapture readonly %p) 152define void @nc4(i8* %p) { 153 call void @external(i8* %p) 154 ret void 155} 156 157; FNATTR: define void @nc5(void (i8*)* nocapture %f, i8* nocapture %p) 158; ATTRIBUTOR: define void @nc5(void (i8*)* nocapture nofree nonnull %f, i8* nocapture %p) 159define void @nc5(void (i8*)* %f, i8* %p) { 160 call void %f(i8* %p) readonly nounwind 161 call void %f(i8* nocapture %p) 162 ret void 163} 164 165; FNATTR: define void @test1_1(i8* nocapture readnone %x1_1, i8* %y1_1, i1 %c) 166; ATTRIBUTOR: define void @test1_1(i8* nocapture nofree readnone %x1_1, i8* nocapture nofree readnone %y1_1, i1 %c) 167; It would be acceptable to add readnone to %y1_1 and %y1_2. 168define void @test1_1(i8* %x1_1, i8* %y1_1, i1 %c) { 169 call i8* @test1_2(i8* %x1_1, i8* %y1_1, i1 %c) 170 store i32* null, i32** @g 171 ret void 172} 173 174; FNATTR: define i8* @test1_2(i8* nocapture readnone %x1_2, i8* returned %y1_2, i1 %c) 175; ATTRIBUTOR: define i8* @test1_2(i8* nocapture nofree readnone %x1_2, i8* nofree readnone returned "no-capture-maybe-returned" %y1_2, i1 %c) 176define i8* @test1_2(i8* %x1_2, i8* %y1_2, i1 %c) { 177 br i1 %c, label %t, label %f 178t: 179 call void @test1_1(i8* %x1_2, i8* %y1_2, i1 %c) 180 store i32* null, i32** @g 181 br label %f 182f: 183 ret i8* %y1_2 184} 185 186; FNATTR: define void @test2(i8* nocapture readnone %x2) 187; ATTRIBUTOR: define void @test2(i8* nocapture nofree readnone %x2) 188define void @test2(i8* %x2) { 189 call void @test2(i8* %x2) 190 store i32* null, i32** @g 191 ret void 192} 193 194; FNATTR: define void @test3(i8* nocapture readnone %x3, i8* nocapture readnone %y3, i8* nocapture readnone %z3) 195; ATTRIBUTOR: define void @test3(i8* nocapture nofree readnone %x3, i8* nocapture nofree readnone %y3, i8* nocapture nofree readnone %z3) 196define void @test3(i8* %x3, i8* %y3, i8* %z3) { 197 call void @test3(i8* %z3, i8* %y3, i8* %x3) 198 store i32* null, i32** @g 199 ret void 200} 201 202; FNATTR: define void @test4_1(i8* %x4_1, i1 %c) 203; ATTRIBUTOR: define void @test4_1(i8* nocapture nofree readnone %x4_1, i1 %c) 204define void @test4_1(i8* %x4_1, i1 %c) { 205 call i8* @test4_2(i8* %x4_1, i8* %x4_1, i8* %x4_1, i1 %c) 206 store i32* null, i32** @g 207 ret void 208} 209 210; FNATTR: define i8* @test4_2(i8* nocapture readnone %x4_2, i8* readnone returned %y4_2, i8* nocapture readnone %z4_2, i1 %c) 211; ATTRIBUTOR: define i8* @test4_2(i8* nocapture nofree readnone %x4_2, i8* nofree readnone returned "no-capture-maybe-returned" %y4_2, i8* nocapture nofree readnone %z4_2, i1 %c) 212define i8* @test4_2(i8* %x4_2, i8* %y4_2, i8* %z4_2, i1 %c) { 213 br i1 %c, label %t, label %f 214t: 215 call void @test4_1(i8* null, i1 %c) 216 store i32* null, i32** @g 217 br label %f 218f: 219 ret i8* %y4_2 220} 221 222declare i8* @test5_1(i8* %x5_1) 223 224; EITHER: define void @test5_2(i8* %x5_2) 225define void @test5_2(i8* %x5_2) { 226 call i8* @test5_1(i8* %x5_2) 227 store i32* null, i32** @g 228 ret void 229} 230 231declare void @test6_1(i8* %x6_1, i8* nocapture %y6_1, ...) 232 233; EITHER: define void @test6_2(i8* %x6_2, i8* nocapture %y6_2, i8* %z6_2) 234define void @test6_2(i8* %x6_2, i8* %y6_2, i8* %z6_2) { 235 call void (i8*, i8*, ...) @test6_1(i8* %x6_2, i8* %y6_2, i8* %z6_2) 236 store i32* null, i32** @g 237 ret void 238} 239 240; FNATTR: define void @test_cmpxchg(i32* nocapture %p) 241; ATTRIBUTOR: define void @test_cmpxchg(i32* nocapture nofree nonnull dereferenceable(4) %p) 242define void @test_cmpxchg(i32* %p) { 243 cmpxchg i32* %p, i32 0, i32 1 acquire monotonic 244 ret void 245} 246 247; FNATTR: define void @test_cmpxchg_ptr(i32** nocapture %p, i32* %q) 248; ATTRIBUTOR: define void @test_cmpxchg_ptr(i32** nocapture nofree nonnull dereferenceable(8) %p, i32* nofree %q) 249define void @test_cmpxchg_ptr(i32** %p, i32* %q) { 250 cmpxchg i32** %p, i32* null, i32* %q acquire monotonic 251 ret void 252} 253 254; FNATTR: define void @test_atomicrmw(i32* nocapture %p) 255; ATTRIBUTOR: define void @test_atomicrmw(i32* nocapture nofree nonnull dereferenceable(4) %p) 256define void @test_atomicrmw(i32* %p) { 257 atomicrmw add i32* %p, i32 1 seq_cst 258 ret void 259} 260 261; FNATTR: define void @test_volatile(i32* %x) 262; ATTRIBUTOR: define void @test_volatile(i32* nofree %x) 263define void @test_volatile(i32* %x) { 264entry: 265 %gep = getelementptr i32, i32* %x, i64 1 266 store volatile i32 0, i32* %gep, align 4 267 ret void 268} 269 270; EITHER: nocaptureLaunder(i8* nocapture %p) 271define void @nocaptureLaunder(i8* %p) { 272entry: 273 %b = call i8* @llvm.launder.invariant.group.p0i8(i8* %p) 274 store i8 42, i8* %b 275 ret void 276} 277 278@g2 = global i8* null 279; EITHER: define void @captureLaunder(i8* %p) 280define void @captureLaunder(i8* %p) { 281 %b = call i8* @llvm.launder.invariant.group.p0i8(i8* %p) 282 store i8* %b, i8** @g2 283 ret void 284} 285 286; FNATTR: @nocaptureStrip(i8* nocapture %p) 287; ATTRIBUTOR: @nocaptureStrip(i8* nocapture writeonly %p) 288define void @nocaptureStrip(i8* %p) { 289entry: 290 %b = call i8* @llvm.strip.invariant.group.p0i8(i8* %p) 291 store i8 42, i8* %b 292 ret void 293} 294 295@g3 = global i8* null 296; FNATTR: define void @captureStrip(i8* %p) 297; ATTRIBUTOR: define void @captureStrip(i8* writeonly %p) 298define void @captureStrip(i8* %p) { 299 %b = call i8* @llvm.strip.invariant.group.p0i8(i8* %p) 300 store i8* %b, i8** @g3 301 ret void 302} 303 304; FNATTR: define i1 @captureICmp(i32* readnone %x) 305; ATTRIBUTOR: define i1 @captureICmp(i32* nofree readnone %x) 306define i1 @captureICmp(i32* %x) { 307 %1 = icmp eq i32* %x, null 308 ret i1 %1 309} 310 311; FNATTR: define i1 @captureICmpRev(i32* readnone %x) 312; ATTRIBUTOR: define i1 @captureICmpRev(i32* nofree readnone %x) 313define i1 @captureICmpRev(i32* %x) { 314 %1 = icmp eq i32* null, %x 315 ret i1 %1 316} 317 318; FNATTR: define i1 @nocaptureInboundsGEPICmp(i32* nocapture readnone %x) 319; ATTRIBUTOR: define i1 @nocaptureInboundsGEPICmp(i32* nocapture nofree nonnull readnone %x) 320define i1 @nocaptureInboundsGEPICmp(i32* %x) { 321 %1 = getelementptr inbounds i32, i32* %x, i32 5 322 %2 = bitcast i32* %1 to i8* 323 %3 = icmp eq i8* %2, null 324 ret i1 %3 325} 326 327; FNATTR: define i1 @nocaptureInboundsGEPICmpRev(i32* nocapture readnone %x) 328; ATTRIBUTOR: define i1 @nocaptureInboundsGEPICmpRev(i32* nocapture nofree nonnull readnone %x) 329define i1 @nocaptureInboundsGEPICmpRev(i32* %x) { 330 %1 = getelementptr inbounds i32, i32* %x, i32 5 331 %2 = bitcast i32* %1 to i8* 332 %3 = icmp eq i8* null, %2 333 ret i1 %3 334} 335 336; FNATTR: define i1 @nocaptureDereferenceableOrNullICmp(i32* nocapture readnone dereferenceable_or_null(4) %x) 337; ATTRIBUTOR: define i1 @nocaptureDereferenceableOrNullICmp(i32* nocapture nofree readnone dereferenceable_or_null(4) %x) 338define i1 @nocaptureDereferenceableOrNullICmp(i32* dereferenceable_or_null(4) %x) { 339 %1 = bitcast i32* %x to i8* 340 %2 = icmp eq i8* %1, null 341 ret i1 %2 342} 343 344; FNATTR: define i1 @captureDereferenceableOrNullICmp(i32* readnone dereferenceable_or_null(4) %x) 345; ATTRIBUTOR: define i1 @captureDereferenceableOrNullICmp(i32* nofree readnone dereferenceable_or_null(4) %x) 346define i1 @captureDereferenceableOrNullICmp(i32* dereferenceable_or_null(4) %x) "null-pointer-is-valid"="true" { 347 %1 = bitcast i32* %x to i8* 348 %2 = icmp eq i8* %1, null 349 ret i1 %2 350} 351 352declare void @unknown(i8*) 353define void @test_callsite() { 354entry: 355; We know that 'null' in AS 0 does not alias anything and cannot be captured. Though the latter is not qurried -> derived atm. 356; ATTRIBUTOR: call void @unknown(i8* noalias null) 357 call void @unknown(i8* null) 358 ret void 359} 360 361declare i8* @unknownpi8pi8(i8*,i8* returned) 362define i8* @test_returned1(i8* %A, i8* returned %B) nounwind readonly { 363; ATTRIBUTOR: define i8* @test_returned1(i8* nocapture readonly %A, i8* readonly returned %B) 364entry: 365 %p = call i8* @unknownpi8pi8(i8* %A, i8* %B) 366 ret i8* %p 367} 368 369define i8* @test_returned2(i8* %A, i8* %B) { 370; ATTRIBUTOR: define i8* @test_returned2(i8* nocapture readonly %A, i8* readonly returned %B) 371entry: 372 %p = call i8* @unknownpi8pi8(i8* %A, i8* %B) nounwind readonly 373 ret i8* %p 374} 375 376declare i8* @llvm.launder.invariant.group.p0i8(i8*) 377declare i8* @llvm.strip.invariant.group.p0i8(i8*) 378