1; RUN: opt -functionattrs -S < %s | FileCheck %s --check-prefix=FNATTR 2; RUN: opt -attributor -attributor-disable=false -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* %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 readonly %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 %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 readonly %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 %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 %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* %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* %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 uwtable 189; ATTRIBUTOR-NEXT: define i32 @scc1(i32* %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 uwtable 199; ATTRIBUTOR-NEXT: define void @scc2(i32* %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* %0, %"struct.std::atomic"* %1) 228define void @foo1(i32* %0, %"struct.std::atomic"* %1) { 229 store i32 100, i32* %0, align 4 230 fence release 231 %3 = getelementptr inbounds %"struct.std::atomic", %"struct.std::atomic"* %1, i64 0, i32 0, i32 0 232 store atomic i8 1, i8* %3 monotonic, align 1 233 ret void 234} 235 236; FNATTR: Function Attrs: nofree norecurse nounwind 237; FNATTR-NEXT: define void @bar(i32* nocapture readnone %0, %"struct.std::atomic"* nocapture readonly %1) 238; ATTRIBUTOR-NOT: nosync 239; ATTRIBUTOR: define void @bar(i32* %0, %"struct.std::atomic"* %1) 240define void @bar(i32* %0, %"struct.std::atomic"* %1) { 241 %3 = getelementptr inbounds %"struct.std::atomic", %"struct.std::atomic"* %1, i64 0, i32 0, i32 0 242 br label %4 243 2444: ; preds = %4, %2 245 %5 = load atomic i8, i8* %3 monotonic, align 1 246 %6 = and i8 %5, 1 247 %7 = icmp eq i8 %6, 0 248 br i1 %7, label %4, label %8 249 2508: ; preds = %4 251 fence acquire 252 ret void 253} 254 255; TEST 13 - Fence syncscope("singlethread") seq_cst 256; FNATTR: Function Attrs: nofree norecurse nounwind 257; FNATTR-NEXT: define void @foo1_singlethread(i32* nocapture %0, %"struct.std::atomic"* nocapture %1) 258; ATTRIBUTOR: Function Attrs: nofree nosync 259; ATTRIBUTOR: define void @foo1_singlethread(i32* %0, %"struct.std::atomic"* %1) 260define void @foo1_singlethread(i32* %0, %"struct.std::atomic"* %1) { 261 store i32 100, i32* %0, align 4 262 fence syncscope("singlethread") release 263 %3 = getelementptr inbounds %"struct.std::atomic", %"struct.std::atomic"* %1, i64 0, i32 0, i32 0 264 store atomic i8 1, i8* %3 monotonic, align 1 265 ret void 266} 267 268; FNATTR: Function Attrs: nofree norecurse nounwind 269; FNATTR-NEXT: define void @bar_singlethread(i32* nocapture readnone %0, %"struct.std::atomic"* nocapture readonly %1) 270; ATTRIBUTOR: Function Attrs: nofree nosync 271; ATTRIBUTOR: define void @bar_singlethread(i32* %0, %"struct.std::atomic"* %1) 272define void @bar_singlethread(i32* %0, %"struct.std::atomic"* %1) { 273 %3 = getelementptr inbounds %"struct.std::atomic", %"struct.std::atomic"* %1, i64 0, i32 0, i32 0 274 br label %4 275 2764: ; preds = %4, %2 277 %5 = load atomic i8, i8* %3 monotonic, align 1 278 %6 = and i8 %5, 1 279 %7 = icmp eq i8 %6, 0 280 br i1 %7, label %4, label %8 281 2828: ; preds = %4 283 fence syncscope("singlethread") acquire 284 ret void 285} 286 287declare void @llvm.memcpy(i8* %dest, i8* %src, i32 %len, i1 %isvolatile) 288declare void @llvm.memset(i8* %dest, i8 %val, i32 %len, i1 %isvolatile) 289 290; TEST 14 - negative, checking volatile intrinsics. 291 292; ATTRIBUTOR: Function Attrs: nounwind 293; ATTRIBUTOR-NOT: nosync 294; ATTRIBUTOR-NEXT: define i32 @memcpy_volatile(i8* %ptr1, i8* %ptr2) 295define i32 @memcpy_volatile(i8* %ptr1, i8* %ptr2) { 296 call void @llvm.memcpy(i8* %ptr1, i8* %ptr2, i32 8, i1 1) 297 ret i32 4 298} 299 300; TEST 15 - positive, non-volatile intrinsic. 301 302; ATTRIBUTOR: Function Attrs: nosync 303; ATTRIBUTOR-NEXT: define i32 @memset_non_volatile(i8* %ptr1, i8 %val) 304define i32 @memset_non_volatile(i8* %ptr1, i8 %val) { 305 call void @llvm.memset(i8* %ptr1, i8 %val, i32 8, i1 0) 306 ret i32 4 307} 308 309; TEST 16 - negative, inline assembly. 310 311; ATTRIBUTOR: define i32 @inline_asm_test(i32 %x) 312define i32 @inline_asm_test(i32 %x) { 313 call i32 asm "bswap $0", "=r,r"(i32 %x) 314 ret i32 4 315} 316 317declare void @readnone_test() convergent readnone 318 319; ATTRIBUTOR: define void @convergent_readnone() 320; TEST 17 - negative. Convergent 321define void @convergent_readnone(){ 322 call void @readnone_test() 323 ret void 324} 325 326; ATTRIBUTOR: Function Attrs: nounwind 327; ATTRIBUTOR-NEXT: declare void @llvm.x86.sse2.clflush(i8*) 328declare void @llvm.x86.sse2.clflush(i8*) 329@a = common global i32 0, align 4 330 331; TEST 18 - negative. Synchronizing intrinsic 332 333; ATTRIBUTOR: Function Attrs: nounwind 334; ATTRIBUTOR-NOT: nosync 335; ATTRIBUTOR-NEXT: define void @i_totally_sync() 336define void @i_totally_sync() { 337 tail call void @llvm.x86.sse2.clflush(i8* bitcast (i32* @a to i8*)) 338 ret void 339} 340 341declare float @llvm.cos(float %val) readnone 342 343; TEST 19 - positive, readnone & non-convergent intrinsic. 344 345; ATTRIBUTOR: Function Attrs: nosync nounwind 346; ATTRIBUTOR-NEXT: define i32 @cos_test(float %x) 347define i32 @cos_test(float %x) { 348 call float @llvm.cos(float %x) 349 ret i32 4 350} 351