diff --git a/llvm/lib/Transforms/IPO/Attributor.cpp b/llvm/lib/Transforms/IPO/Attributor.cpp --- a/llvm/lib/Transforms/IPO/Attributor.cpp +++ b/llvm/lib/Transforms/IPO/Attributor.cpp @@ -374,42 +374,58 @@ /// Returns true if "nosync" is assumed. bool isAssumedNoSync() const { return getAssumed(); } - /// Returns true of "nosync" is known. + /// Returns true if "nosync" is known. bool isKnownNoSync() const { return getKnown(); } static constexpr Attribute::AttrKind ID = Attribute::AttrKind(Attribute::None - 2); - AtomicOrdering getOrdering(Instruction *I) const; + /// Helper function used to get ordering of an atomic instruction. + bool isNonRelaxedAtomic(Instruction *I) const; + /// Helper function used to determine whether an instruction is volatile. bool isVolatile(Instruction *I) const; }; -/// Helper function used to get ordering of an atomic instruction. -AtomicOrdering AANoSyncFunction::getOrdering(Instruction *I) const { - AtomicOrdering Success, Failure; +bool AANoSyncFunction::isNonRelaxedAtomic(Instruction *I) const { + if (!I->isAtomic()) + return false; + + AtomicOrdering Success, Failure, Ordering; switch (I->getOpcode()) { case Instruction::AtomicRMW: - return cast(I)->getOrdering(); + Ordering = cast(I)->getOrdering(); + break; case Instruction::Store: - return cast(I)->getOrdering(); + Ordering = cast(I)->getOrdering(); + break; case Instruction::Load: - return cast(I)->getOrdering(); + Ordering = cast(I)->getOrdering(); + break; case Instruction::Fence: - return cast(I)->getOrdering(); + Ordering = cast(I)->getOrdering(); + break; case Instruction::AtomicCmpXchg: Success = cast(I)->getSuccessOrdering(); Failure = cast(I)->getFailureOrdering(); if (Success == AtomicOrdering::Unordered || Success == AtomicOrdering::Monotonic) - return Failure; - return Success; + return false; + if (Failure == AtomicOrdering::Unordered || + Failure == AtomicOrdering::Monotonic) + return false; default: - return AtomicOrdering::NotAtomic; + Ordering = AtomicOrdering::NotAtomic; + + break; + // Relaxed. + if (Ordering == AtomicOrdering::Unordered || + Ordering == AtomicOrdering::Monotonic) + return false; + return true; } } -/// Helper function used to determine whether an instruction is volatile. bool AANoSyncFunction::isVolatile(Instruction *I) const { switch (I->getOpcode()) { case Instruction::AtomicRMW: @@ -432,22 +448,18 @@ /// FIXME: We should ipmrove the handling of intrinsics. for (Instruction *I : InfoCache.getReadOrWriteInstsForFunction(F)) { ImmutableCallSite ICS(I); + if(!ICS) + continue; + auto *NoSyncAA = A.getAAFor(*this, *I); if ((!NoSyncAA || !NoSyncAA->isAssumedNoSync()) && - !ICS.hasFnAttr("nosync") && !ICS.hasFnAttr("readnone")) { + !ICS.hasFnAttr("nosync")) { indicatePessimisticFixpoint(); return ChangeStatus::CHANGED; } - if (!isVolatile(I) && !I->isAtomic()) - continue; - - AtomicOrdering Ordering = getOrdering(I); - - if ((Ordering == AtomicOrdering::Unordered || - Ordering == AtomicOrdering::Monotonic) && - !isVolatile(I)) + if (!isVolatile(I) && !isNonRelaxedAtomic(I)) continue; indicatePessimisticFixpoint(); @@ -594,11 +606,6 @@ switch (I.getOpcode()) { default: break; - case Instruction::Load: - case Instruction::Store: - case Instruction::AtomicRMW: - case Instruction::Fence: - IsInterestingOpcode = true; } if (IsInterestingOpcode) InstOpcodeMap[I.getOpcode()].push_back(&I); diff --git a/llvm/test/Transforms/FunctionAttrs/nosync.ll b/llvm/test/Transforms/FunctionAttrs/nosync.ll --- a/llvm/test/Transforms/FunctionAttrs/nosync.ll +++ b/llvm/test/Transforms/FunctionAttrs/nosync.ll @@ -1,4 +1,5 @@ -; RUN: opt -functionattrs -S < %s | FileCheck %s +; RUN: opt -functionattrs -S < %s | FileCheck %s --check-prefic=FNATTR +; RUN: opt -attributor -S < %s | FileCheck %s --check-prefix=ATTRIBUTOR target datalayout = "e-m:e-i64:64-f80:128-n8:16:32:64-S128" ; Test cases designet for the "nosync" function attribute. @@ -24,8 +25,10 @@ %struct.RT = type { i8, [10 x [20 x i32]], i8 } %struct.ST = type { i32, double, %struct.RT } -; FIXME: missing "nosync" -; CHECK Function Attrs: nounwind uwtable readnone optsize ssp +; FNATTR: Function Attrs: nounwind uwtable readnone optsize ssp +; FNATTR-NEXT: define i32 *@foo(%struct.ST* %s) +; ATTRIBUTOR: Function Attrs: nounwind uwtable readnone optsize ssp "nosync" +; ATTRIBUTOR-NEXT: define i32 *@foo(%struct.ST* %s) define i32* @foo(%struct.ST* %s) nounwind uwtable readnone optsize ssp { entry: %arrayidx = getelementptr inbounds %struct.ST, %struct.ST* %s, i64 1, i32 2, i32 1, i64 5, i64 13 @@ -39,9 +42,10 @@ ; return n; ; } -; FIXME: missing "nosync" -; CHECK: Function Attrs: norecurse nounwind uwtable -; CHECK-NEXT: define i32 @load_monotonic(i32* nocapture readonly) +; FNATTR: Function Attrs: norecurse nounwind uwtable +; FNATTR-NEXT: define i32 @load_monotonic(i32* nocapture readonly) +; ATTRIBUTOR: Function Attrs: norecurse nounwind uwtable "nosync" +; ATTRIBUTOR-NEXT: define i32 @load_monotonic(i32* nocapture readonly) define i32 @load_monotonic(i32* nocapture readonly) norecurse nounwind uwtable { %2 = load atomic i32, i32* %0 monotonic, align 4 ret i32 %2 @@ -54,9 +58,10 @@ ; atomic_load_explicit(num, memory_order_relaxed); ; } -; FIXME: missing "nosync" -; CHECK: Function Attrs: norecurse nounwind uwtable -; CHECK-NEXT: define void @store_monotonic(i32* nocapture) +; FNATTR: Function Attrs: norecurse nounwind uwtable +; FNATTR-NEXT: define void @store_monotonic(i32* nocapture) +; ATTRIBUTOR: Function Attrs: norecurse nounwind uwtable "nosync" +; ATTRIBUTOR-NEXT: define void @store_monotonic(i32* nocapture) define void @store_monotonic(i32* nocapture) norecurse nounwind uwtable { store atomic i32 10, i32* %0 monotonic, align 4 ret void @@ -69,9 +74,11 @@ ; return n; ; } -; CHECK: Function Attrs: norecurse nounwind uwtable -; CHECK-NOT: nosync -; CHECK-NEXT: define i32 @load_acquire(i32* nocapture readonly) +; FNATTR: Function Attrs: norecurse nounwind uwtable +; FNATTR-NEXT: define i32 @load_acquire(i32* nocapture readonly) +; ATTRIBUTOR: Function Attrs: norecurse nounwind uwtable +; ATTRIBUTOR-NOT: "nosync" +; ATTRIBUTOR-NEXT: define i32 @load_acquire(i32* nocapture readonly) define i32 @load_acquire(i32* nocapture readonly) norecurse nounwind uwtable { %2 = load atomic i32, i32* %0 acquire, align 4 ret i32 %2 @@ -83,9 +90,11 @@ ; atomic_store_explicit(num, 10, memory_order_release); ; } -; CHECK: Function Attrs: norecurse nounwind uwtable -; CHECK-NOT: nosync -; CHECK-NEXT: define void @load_release(i32* nocapture) +; FNATTR: Function Attrs: norecurse nounwind uwtable +; FNATTR-NEXT: define void @load_release(i32* nocapture) +; ATTRIBUTOR: Function Attrs: norecurse nounwind uwtable +; ATTRIBUTOR-NOT: "nosync" +; ATTRIBUTOR-NEXT: define void @load_release(i32* nocapture) define void @load_release(i32* nocapture) norecurse nounwind uwtable { store atomic i32 10, i32* %0 release, align 4 ret void @@ -97,9 +106,11 @@ ; *num = 14; ; } -; CHECK: Function Attrs: norecurse nounwind uwtable -; CHECK-NOT: nosync -; CHECK-NEXT: define void @volatile_store(i32*) +; FNATTR: Function Attrs: norecurse nounwind uwtable +; FNATTR-NEXT: define void @volatile_store(i32*) +; ATTRIBUTOR: Function Attrs: norecurse nounwind uwtable +; ATTRIBUTOR-NOT: "nosync" +; ATTRIBUTOR-NEXT: define void @volatile_store(i32*) define void @volatile_store(i32*) norecurse nounwind uwtable { ;store volatile i32 14, i32* %0, align 4, !tbaa !2 store volatile i32 14, i32* %0, align 4 @@ -113,9 +124,11 @@ ; return n; ; } -; CHECK: Function Attrs: norecurse nounwind uwtable -; CHECK-NOT: nosync -; CHECK-NEXT: define i32 @volatile_load(i32*) +; FNATTR: Function Attrs: norecurse nounwind uwtable +; FNATTR-NEXT: define i32 @volatile_load(i32*) +; ATTRIBUTOR: Function Attrs: norecurse nounwind uwtable +; ATTRIBUTOR-NOT: "nosync" +; ATTRIBUTOR-NEXT: define i32 @volatile_load(i32*) define i32 @volatile_load(i32*) norecurse nounwind uwtable { ;%2 = load volatile i32, i32* %0, align 4, !tbaa !2 %2 = load volatile i32, i32* %0, align 4 @@ -125,9 +138,10 @@ ; TEST 8 declare void @nosync_function() noinline nounwind uwtable -; FIXME: missing "nosync" -; CHECK: Function Attrs: noinline nounwind uwtable -; CHECK: define void @call_nosync_function() +; FNATTR: Function Attrs: noinline nounwind uwtable +; FNATTR: define void @call_nosync_function() +; ATTRIBUTOR: Function Attrs: noinline nounwind uwtable "nosync" +; ATTRIBUTOR: define void @call_nosync_function() define void @call_nosync_function() nounwind uwtable noinline { tail call void @nosync_function() noinline nounwind uwtable ret void @@ -136,9 +150,11 @@ ; TEST 9 - negative, should not deduce "nosync" declare void @might_sync() noinline nounwind uwtable -; CHECK: Function Attrs: noinline nounwind uwtable -; CHECK-NOT: nosync -; CHECK: define void @call_might_sync() +; FNATTR: Function Attrs: noinline nounwind uwtable +; FNATTR: define void @call_might_sync() +; ATTRIBUTOR: Function Attrs: noinline nounwind uwtable +; ATTRIBUTOR-NOT: "nosync" +; ATTRIBUTOR: define void @call_might_sync() define void @call_might_sync() nounwind uwtable noinline { tail call void @might_sync() noinline nounwind uwtable ret void @@ -147,19 +163,75 @@ ; TEST 10 - negative, should not deduce "nosync" ; volatile operation in same scc. Call volatile_load defined in TEST 7. -; CHECK: Function Attrs: noinline nounwind uwtable -; CHECK-NOT: nosync -; CHECK-NEXT: define i32 @scc1(i32*) +; FNATTR: Function Attrs: noinline nounwind uwtable +; FNATTR-NEXT: define i32 @scc1(i32*) +; ATTRIBUTOR: Function Attrs: noinline nounwind uwtable +; ATTRIBUTOR-NOT: "nosync" +; ATTRIBUTOR-NEXT: define i32 @scc1(i32*) define i32 @scc1(i32*) noinline nounwind uwtable { tail call void @scc2(i32* %0); %val = tail call i32 @volatile_load(i32* %0); ret i32 %val; } -; CHECK: Function Attrs: noinline nounwind uwtable -; CHECK-NOT: nosync -; CHECK-NEXT: define void @scc2(i32*) +; FNATTR: Function Attrs: noinline nounwind uwtable +; FNATTR-NEXT: define void @scc2(i32*) +; ATTRIBUTOR: Function Attrs: noinline nounwind uwtable +; ATTRIBUTOR-NOT: "nosync" +; ATTRIBUTOR-NEXT: define void @scc2(i32*) define void @scc2(i32*) noinline nounwind uwtable { tail call i32 @scc1(i32* %0); ret void; } + +; TEST 11 - fences, negative +; std::atomic flag(false); +; int a; +; +; void func1(){ +; a = 100; +; atomic_thread_fence(std::memory_order_release); +; flag.store(true, std::memory_order_relaxed); +; } +; +; void foo(){ +; while(!flag.load(std::memory_order_relaxed)) +; ; +; +; atomic_thread_fence(std::memory_order_acquire); +; int b = a; +; } + + %"struct.std::atomic" = type { %"struct.std::__atomic_base" } + %"struct.std::__atomic_base" = type { i8 } + +; FNATTR: Function Attrs: norecurse nounwind uwtable +; FNATTR-NEXT: define void @foo() +; ATTRIBUTOR: Function Attrs: norecurse nounwind uwtable +; ATTRIBUTOR-NOT: "nosync" +; ATTRIBUTOR-NEXT: define void @foo() + define void @foo() { + store i32 100, i32* @a, align 4 + fence release + store atomic i8 1, i8* getelementptr inbounds (%"struct.std::atomic", %"struct.std::atomic"* @flag, i64 0, i32 0, i32 0) monotonic, align 1 + ret void + } + +; FNATTR: Function Attrs: norecurse nounwind uwtable +; FNATTR-NEXT: define void @bar() +; ATTRIBUTOR: Function Attrs: norecurse nounwind uwtable +; ATTRIBUTOR-NOT: "nosync" +; ATTRIBUTOR-NEXT: define void @bar() +define void @bar() { + br label %1 + + 1: ; preds = %1, %0 + %2 = load atomic i8, i8* getelementptr inbounds (%"struct.std::atomic", %"struct.std::atomic"* @flag, i64 0, i32 0, i32 0) monotonic, align 1 + %3 = and i8 %2, 1 + %4 = icmp eq i8 %3, 0 + br i1 %4, label %1, label %5 + +5: ; preds = %1 + fence acquire + ret void + }