1; RUN: opt -functionattrs -S < %s | FileCheck %s --check-prefix=FNATTR 2; RUN: opt -attributor -attributor-manifest-internal -attributor-disable=false -attributor-max-iterations-verify -attributor-annotate-decl-cs -attributor-max-iterations=2 -S < %s | FileCheck %s --check-prefix=ATTRIBUTOR 3target datalayout = "e-m:e-i64:64-f80:128-n8:16:32:64-S128" 4 5; Test cases designed for the nosync function attribute. 6; FIXME's are used to indicate problems and missing attributes. 7 8; struct RT { 9; char A; 10; int B[10][20]; 11; char C; 12; }; 13; struct ST { 14; int X; 15; double Y; 16; struct RT Z; 17; }; 18; 19; int *foo(struct ST *s) { 20; return &s[1].Z.B[5][13]; 21; } 22 23; TEST 1 24; non-convergent and readnone implies nosync 25%struct.RT = type { i8, [10 x [20 x i32]], i8 } 26%struct.ST = type { i32, double, %struct.RT } 27 28; FNATTR: Function Attrs: norecurse nounwind optsize readnone ssp uwtable 29; FNATTR-NEXT: define nonnull i32* @foo(%struct.ST* readnone %s) 30; ATTRIBUTOR: Function Attrs: nofree nosync nounwind optsize readnone ssp uwtable 31; ATTRIBUTOR-NEXT: define nonnull i32* @foo(%struct.ST* nofree nonnull readnone "no-capture-maybe-returned" %s) 32define i32* @foo(%struct.ST* %s) nounwind uwtable readnone optsize ssp { 33entry: 34 %arrayidx = getelementptr inbounds %struct.ST, %struct.ST* %s, i64 1, i32 2, i32 1, i64 5, i64 13 35 ret i32* %arrayidx 36} 37 38; TEST 2 39; atomic load with monotonic ordering 40; int load_monotonic(_Atomic int *num) { 41; int n = atomic_load_explicit(num, memory_order_relaxed); 42; return n; 43; } 44 45; FNATTR: Function Attrs: nofree norecurse nounwind uwtable 46; FNATTR-NEXT: define i32 @load_monotonic(i32* nocapture readonly %0) 47; ATTRIBUTOR: Function Attrs: nofree norecurse nosync nounwind uwtable 48; ATTRIBUTOR-NEXT: define i32 @load_monotonic(i32* nocapture nofree nonnull readonly dereferenceable(4) %0) 49define i32 @load_monotonic(i32* nocapture readonly %0) norecurse nounwind uwtable { 50 %2 = load atomic i32, i32* %0 monotonic, align 4 51 ret i32 %2 52} 53 54 55; TEST 3 56; atomic store with monotonic ordering. 57; void store_monotonic(_Atomic int *num) { 58; atomic_load_explicit(num, memory_order_relaxed); 59; } 60 61; FNATTR: Function Attrs: nofree norecurse nounwind uwtable 62; FNATTR-NEXT: define void @store_monotonic(i32* nocapture %0) 63; ATTRIBUTOR: Function Attrs: nofree norecurse nosync nounwind uwtable 64; ATTRIBUTOR-NEXT: define void @store_monotonic(i32* nocapture nofree nonnull writeonly dereferenceable(4) %0) 65define void @store_monotonic(i32* nocapture %0) norecurse nounwind uwtable { 66 store atomic i32 10, i32* %0 monotonic, align 4 67 ret void 68} 69 70; TEST 4 - negative, should not deduce nosync 71; atomic load with acquire ordering. 72; int load_acquire(_Atomic int *num) { 73; int n = atomic_load_explicit(num, memory_order_acquire); 74; return n; 75; } 76 77; FNATTR: Function Attrs: nofree norecurse nounwind uwtable 78; FNATTR-NEXT: define i32 @load_acquire(i32* nocapture readonly %0) 79; ATTRIBUTOR: Function Attrs: nofree norecurse nounwind uwtable 80; ATTRIBUTOR-NOT: nosync 81; ATTRIBUTOR-NEXT: define i32 @load_acquire(i32* nocapture nofree nonnull readonly dereferenceable(4) %0) 82define i32 @load_acquire(i32* nocapture readonly %0) norecurse nounwind uwtable { 83 %2 = load atomic i32, i32* %0 acquire, align 4 84 ret i32 %2 85} 86 87; TEST 5 - negative, should not deduce nosync 88; atomic load with release ordering 89; void load_release(_Atomic int *num) { 90; atomic_store_explicit(num, 10, memory_order_release); 91; } 92 93; FNATTR: Function Attrs: nofree norecurse nounwind uwtable 94; FNATTR-NEXT: define void @load_release(i32* nocapture %0) 95; ATTRIBUTOR: Function Attrs: nofree norecurse nounwind uwtable 96; ATTRIBUTOR-NOT: nosync 97; ATTRIBUTOR-NEXT: define void @load_release(i32* nocapture nofree writeonly %0) 98define void @load_release(i32* nocapture %0) norecurse nounwind uwtable { 99 store atomic volatile i32 10, i32* %0 release, align 4 100 ret void 101} 102 103; TEST 6 - negative volatile, relaxed atomic 104 105; FNATTR: Function Attrs: nofree norecurse nounwind uwtable 106; FNATTR-NEXT: define void @load_volatile_release(i32* nocapture %0) 107; ATTRIBUTOR: Function Attrs: nofree norecurse nounwind uwtable 108; ATTRIBUTOR-NOT: nosync 109; ATTRIBUTOR-NEXT: define void @load_volatile_release(i32* nocapture nofree writeonly %0) 110define void @load_volatile_release(i32* nocapture %0) norecurse nounwind uwtable { 111 store atomic volatile i32 10, i32* %0 release, align 4 112 ret void 113} 114 115; TEST 7 - negative, should not deduce nosync 116; volatile store. 117; void volatile_store(volatile int *num) { 118; *num = 14; 119; } 120 121; FNATTR: Function Attrs: nofree norecurse nounwind uwtable 122; FNATTR-NEXT: define void @volatile_store(i32* %0) 123; ATTRIBUTOR: Function Attrs: nofree norecurse nounwind uwtable 124; ATTRIBUTOR-NOT: nosync 125; ATTRIBUTOR-NEXT: define void @volatile_store(i32* nofree %0) 126define void @volatile_store(i32* %0) norecurse nounwind uwtable { 127 store volatile i32 14, i32* %0, align 4 128 ret void 129} 130 131; TEST 8 - negative, should not deduce nosync 132; volatile load. 133; int volatile_load(volatile int *num) { 134; int n = *num; 135; return n; 136; } 137 138; FNATTR: Function Attrs: nofree norecurse nounwind uwtable 139; FNATTR-NEXT: define i32 @volatile_load(i32* %0) 140; ATTRIBUTOR: Function Attrs: nofree norecurse nounwind uwtable 141; ATTRIBUTOR-NOT: nosync 142; ATTRIBUTOR-NEXT: define i32 @volatile_load(i32* nofree %0) 143define i32 @volatile_load(i32* %0) norecurse nounwind uwtable { 144 %2 = load volatile i32, i32* %0, align 4 145 ret i32 %2 146} 147 148; TEST 9 149 150; FNATTR: Function Attrs: noinline nosync nounwind uwtable 151; FNATTR-NEXT: declare void @nosync_function() 152; ATTRIBUTOR: Function Attrs: noinline nosync nounwind uwtable 153; ATTRIBUTOR-NEXT: declare void @nosync_function() 154declare void @nosync_function() noinline nounwind uwtable nosync 155 156; FNATTR: Function Attrs: noinline nounwind uwtable 157; FNATTR-NEXT: define void @call_nosync_function() 158; ATTRIBUTOR: Function Attrs: noinline nosync nounwind uwtable 159; ATTRIBUTOR-next: define void @call_nosync_function() 160define void @call_nosync_function() nounwind uwtable noinline { 161 tail call void @nosync_function() noinline nounwind uwtable 162 ret void 163} 164 165; TEST 10 - negative, should not deduce nosync 166 167; FNATTR: Function Attrs: noinline nounwind uwtable 168; FNATTR-NEXT: declare void @might_sync() 169; ATTRIBUTOR: Function Attrs: noinline nounwind uwtable 170; ATTRIBUTOR-NEXT: declare void @might_sync() 171declare void @might_sync() noinline nounwind uwtable 172 173; FNATTR: Function Attrs: noinline nounwind uwtable 174; FNATTR-NEXT: define void @call_might_sync() 175; ATTRIBUTOR: Function Attrs: noinline nounwind uwtable 176; ATTRIBUTOR-NOT: nosync 177; ATTRIBUTOR-NEXT: define void @call_might_sync() 178define void @call_might_sync() nounwind uwtable noinline { 179 tail call void @might_sync() noinline nounwind uwtable 180 ret void 181} 182 183; TEST 11 - positive, should deduce nosync 184; volatile operation in same scc but dead. Call volatile_load defined in TEST 8. 185 186; FNATTR: Function Attrs: nofree noinline nounwind uwtable 187; FNATTR-NEXT: define i32 @scc1(i32* %0) 188; ATTRIBUTOR: Function Attrs: nofree noinline noreturn nosync nounwind readnone uwtable 189; ATTRIBUTOR-NEXT: define i32 @scc1(i32* nocapture nofree readnone %0) 190define i32 @scc1(i32* %0) noinline nounwind uwtable { 191 tail call void @scc2(i32* %0); 192 %val = tail call i32 @volatile_load(i32* %0); 193 ret i32 %val; 194} 195 196; FNATTR: Function Attrs: nofree noinline nounwind uwtable 197; FNATTR-NEXT: define void @scc2(i32* %0) 198; ATTRIBUTOR: Function Attrs: nofree noinline noreturn nosync nounwind readnone uwtable 199; ATTRIBUTOR-NEXT: define void @scc2(i32* nocapture nofree readnone %0) 200define void @scc2(i32* %0) noinline nounwind uwtable { 201 tail call i32 @scc1(i32* %0); 202 ret void; 203} 204 205; TEST 12 - fences, negative 206; 207; void foo1(int *a, std::atomic<bool> flag){ 208; *a = 100; 209; atomic_thread_fence(std::memory_order_release); 210; flag.store(true, std::memory_order_relaxed); 211; } 212; 213; void bar(int *a, std::atomic<bool> flag){ 214; while(!flag.load(std::memory_order_relaxed)) 215; ; 216; 217; atomic_thread_fence(std::memory_order_acquire); 218; int b = *a; 219; } 220 221%"struct.std::atomic" = type { %"struct.std::__atomic_base" } 222%"struct.std::__atomic_base" = type { i8 } 223 224; FNATTR: Function Attrs: nofree norecurse nounwind 225; FNATTR-NEXT: define void @foo1(i32* nocapture %0, %"struct.std::atomic"* nocapture %1) 226; ATTRIBUTOR-NOT: nosync 227; ATTRIBUTOR: define void @foo1(i32* nocapture nofree nonnull writeonly dereferenceable(4) %0, %"struct.std::atomic"* nocapture nofree nonnull writeonly dereferenceable(1) %1) 228 229define void @foo1(i32* %0, %"struct.std::atomic"* %1) { 230 store i32 100, i32* %0, align 4 231 fence release 232 %3 = getelementptr inbounds %"struct.std::atomic", %"struct.std::atomic"* %1, i64 0, i32 0, i32 0 233 store atomic i8 1, i8* %3 monotonic, align 1 234 ret void 235} 236 237; FNATTR: Function Attrs: nofree norecurse nounwind 238; FNATTR-NEXT: define void @bar(i32* nocapture readnone %0, %"struct.std::atomic"* nocapture readonly %1) 239; ATTRIBUTOR-NOT: nosync 240; ATTRIBUTOR: define void @bar(i32* nocapture nofree readnone %0, %"struct.std::atomic"* nocapture nofree nonnull readonly dereferenceable(1) %1) 241define void @bar(i32* %0, %"struct.std::atomic"* %1) { 242 %3 = getelementptr inbounds %"struct.std::atomic", %"struct.std::atomic"* %1, i64 0, i32 0, i32 0 243 br label %4 244 2454: ; preds = %4, %2 246 %5 = load atomic i8, i8* %3 monotonic, align 1 247 %6 = and i8 %5, 1 248 %7 = icmp eq i8 %6, 0 249 br i1 %7, label %4, label %8 250 2518: ; preds = %4 252 fence acquire 253 ret void 254} 255 256; TEST 13 - Fence syncscope("singlethread") seq_cst 257; FNATTR: Function Attrs: nofree norecurse nounwind 258; FNATTR-NEXT: define void @foo1_singlethread(i32* nocapture %0, %"struct.std::atomic"* nocapture %1) 259; ATTRIBUTOR: Function Attrs: nofree nosync nounwind willreturn 260; ATTRIBUTOR: define void @foo1_singlethread(i32* nocapture nofree nonnull writeonly dereferenceable(4) %0, %"struct.std::atomic"* nocapture nofree nonnull writeonly dereferenceable(1) %1) 261 262define void @foo1_singlethread(i32* %0, %"struct.std::atomic"* %1) { 263 store i32 100, i32* %0, align 4 264 fence syncscope("singlethread") release 265 %3 = getelementptr inbounds %"struct.std::atomic", %"struct.std::atomic"* %1, i64 0, i32 0, i32 0 266 store atomic i8 1, i8* %3 monotonic, align 1 267 ret void 268} 269 270; FNATTR: Function Attrs: nofree norecurse nounwind 271; FNATTR-NEXT: define void @bar_singlethread(i32* nocapture readnone %0, %"struct.std::atomic"* nocapture readonly %1) 272; ATTRIBUTOR: Function Attrs: nofree nosync nounwind 273; ATTRIBUTOR: define void @bar_singlethread(i32* nocapture nofree readnone %0, %"struct.std::atomic"* nocapture nofree nonnull readonly dereferenceable(1) %1) 274define void @bar_singlethread(i32* %0, %"struct.std::atomic"* %1) { 275 %3 = getelementptr inbounds %"struct.std::atomic", %"struct.std::atomic"* %1, i64 0, i32 0, i32 0 276 br label %4 277 2784: ; preds = %4, %2 279 %5 = load atomic i8, i8* %3 monotonic, align 1 280 %6 = and i8 %5, 1 281 %7 = icmp eq i8 %6, 0 282 br i1 %7, label %4, label %8 283 2848: ; preds = %4 285 fence syncscope("singlethread") acquire 286 ret void 287} 288 289declare void @llvm.memcpy(i8* %dest, i8* %src, i32 %len, i1 %isvolatile) 290declare void @llvm.memset(i8* %dest, i8 %val, i32 %len, i1 %isvolatile) 291 292; TEST 14 - negative, checking volatile intrinsics. 293 294; It is odd to add nocapture but a result of the llvm.memcpy nocapture. 295; 296; ATTRIBUTOR: Function Attrs: nounwind 297; ATTRIBUTOR-NOT: nosync 298; ATTRIBUTOR-NEXT: define i32 @memcpy_volatile(i8* nocapture writeonly %ptr1, i8* nocapture readonly %ptr2) 299define i32 @memcpy_volatile(i8* %ptr1, i8* %ptr2) { 300 call void @llvm.memcpy(i8* %ptr1, i8* %ptr2, i32 8, i1 1) 301 ret i32 4 302} 303 304; TEST 15 - positive, non-volatile intrinsic. 305 306; It is odd to add nocapture but a result of the llvm.memset nocapture. 307; 308; ATTRIBUTOR: Function Attrs: nosync 309; ATTRIBUTOR-NEXT: define i32 @memset_non_volatile(i8* nocapture writeonly %ptr1, i8 %val) 310define i32 @memset_non_volatile(i8* %ptr1, i8 %val) { 311 call void @llvm.memset(i8* %ptr1, i8 %val, i32 8, i1 0) 312 ret i32 4 313} 314 315; TEST 16 - negative, inline assembly. 316 317; ATTRIBUTOR: define i32 @inline_asm_test(i32 %x) 318define i32 @inline_asm_test(i32 %x) { 319 call i32 asm "bswap $0", "=r,r"(i32 %x) 320 ret i32 4 321} 322 323declare void @readnone_test() convergent readnone 324 325; ATTRIBUTOR: define void @convergent_readnone() 326; TEST 17 - negative. Convergent 327define void @convergent_readnone(){ 328 call void @readnone_test() 329 ret void 330} 331 332; ATTRIBUTOR: Function Attrs: nounwind 333; ATTRIBUTOR-NEXT: declare void @llvm.x86.sse2.clflush(i8*) 334declare void @llvm.x86.sse2.clflush(i8*) 335@a = common global i32 0, align 4 336 337; TEST 18 - negative. Synchronizing intrinsic 338 339; ATTRIBUTOR: Function Attrs: nounwind 340; ATTRIBUTOR-NOT: nosync 341; ATTRIBUTOR-NEXT: define void @i_totally_sync() 342define void @i_totally_sync() { 343 tail call void @llvm.x86.sse2.clflush(i8* bitcast (i32* @a to i8*)) 344 ret void 345} 346 347declare float @llvm.cos(float %val) readnone 348 349; TEST 19 - positive, readnone & non-convergent intrinsic. 350 351; ATTRIBUTOR: Function Attrs: nosync nounwind 352; ATTRIBUTOR-NEXT: define i32 @cos_test(float %x) 353define i32 @cos_test(float %x) { 354 call float @llvm.cos(float %x) 355 ret i32 4 356} 357