diff --git a/llvm/docs/LangRef.rst b/llvm/docs/LangRef.rst --- a/llvm/docs/LangRef.rst +++ b/llvm/docs/LangRef.rst @@ -1474,7 +1474,8 @@ undefined behavior at runtime if the function ever does recurse. ``nosync`` This function attribute indicates that the function does not communicate - (synchronize) with another thread. + (synchronize) with another thread. If the function does ever synchronize + with another thread, the behavior is undefined. ``nounwind`` This function attribute indicates that the function never raises an exception. If the function does raise an exception, its runtime diff --git a/llvm/include/llvm/Bitcode/LLVMBitCodes.h b/llvm/include/llvm/Bitcode/LLVMBitCodes.h --- a/llvm/include/llvm/Bitcode/LLVMBitCodes.h +++ b/llvm/include/llvm/Bitcode/LLVMBitCodes.h @@ -606,7 +606,8 @@ ATTR_KIND_OPT_FOR_FUZZING = 57, ATTR_KIND_SHADOWCALLSTACK = 58, ATTR_KIND_SPECULATIVE_LOAD_HARDENING = 59, - ATTR_KIND_IMMARG = 60 + ATTR_KIND_IMMARG = 60, + ATTR_KIND_NOSYNC = 61 }; enum ComdatSelectionKindCodes { diff --git a/llvm/include/llvm/IR/Attributes.td b/llvm/include/llvm/IR/Attributes.td --- a/llvm/include/llvm/IR/Attributes.td +++ b/llvm/include/llvm/IR/Attributes.td @@ -107,7 +107,7 @@ def NoReturn : EnumAttr<"noreturn">; /// Function does not synchronize. -def NoSync : StrBoolAttr<"nosync">; +def NoSync : EnumAttr<"nosync">; /// Disable Indirect Branch Tracking. def NoCfCheck : EnumAttr<"nocf_check">; diff --git a/llvm/include/llvm/Transforms/IPO/Attributor.h b/llvm/include/llvm/Transforms/IPO/Attributor.h --- a/llvm/include/llvm/Transforms/IPO/Attributor.h +++ b/llvm/include/llvm/Transforms/IPO/Attributor.h @@ -644,27 +644,25 @@ /// Abstract Attribute Classes /// ---------------------------------------------------------------------------- -struct AANoSync : public AbstractAttribute, BooleanState { +struct AANoSync : public AbstractAttribute { /// An abstract interface for all nosync attributes. AANoSync(Value &V, InformationCache &InfoCache) : AbstractAttribute(V, InfoCache) {} - /// See AbstractAttribute::getAsStr(). - virtual const std::string getAsStr() const override { - return getAssumed() ? "nosync" : "may-sync"; - } - /// See AbstractAttribute::getAttrKind(). virtual Attribute::AttrKind getAttrKind() const override { - return Attribute::None; + return ID; } + static constexpr Attribute::AttrKind ID = + Attribute::AttrKind(Attribute::NoSync); + /// Returns true if "nosync" is assumed. - bool isAssumedNoSync() const { return getAssumed(); } + virtual bool isAssumedNoSync() const = 0; /// Returns true if "nosync" is known. - bool isKnownNoSync() const { return getKnown(); } -}; // namespace llvm + virtual bool isKnownNoSync() const = 0; +}; } // end namespace llvm diff --git a/llvm/lib/AsmParser/LLLexer.cpp b/llvm/lib/AsmParser/LLLexer.cpp --- a/llvm/lib/AsmParser/LLLexer.cpp +++ b/llvm/lib/AsmParser/LLLexer.cpp @@ -656,6 +656,7 @@ KEYWORD(nonnull); KEYWORD(noredzone); KEYWORD(noreturn); + KEYWORD(nosync); KEYWORD(nocf_check); KEYWORD(nounwind); KEYWORD(optforfuzzing); diff --git a/llvm/lib/AsmParser/LLParser.cpp b/llvm/lib/AsmParser/LLParser.cpp --- a/llvm/lib/AsmParser/LLParser.cpp +++ b/llvm/lib/AsmParser/LLParser.cpp @@ -1260,6 +1260,7 @@ case lltok::kw_nonlazybind: B.addAttribute(Attribute::NonLazyBind); break; case lltok::kw_noredzone: B.addAttribute(Attribute::NoRedZone); break; case lltok::kw_noreturn: B.addAttribute(Attribute::NoReturn); break; + case lltok::kw_nosync: B.addAttribute(Attribute::NoSync); break; case lltok::kw_nocf_check: B.addAttribute(Attribute::NoCfCheck); break; case lltok::kw_norecurse: B.addAttribute(Attribute::NoRecurse); break; case lltok::kw_nounwind: B.addAttribute(Attribute::NoUnwind); break; diff --git a/llvm/lib/AsmParser/LLToken.h b/llvm/lib/AsmParser/LLToken.h --- a/llvm/lib/AsmParser/LLToken.h +++ b/llvm/lib/AsmParser/LLToken.h @@ -200,6 +200,7 @@ kw_nonnull, kw_noredzone, kw_noreturn, + kw_nosync, kw_nocf_check, kw_nounwind, kw_optforfuzzing, diff --git a/llvm/lib/Bitcode/Reader/BitcodeReader.cpp b/llvm/lib/Bitcode/Reader/BitcodeReader.cpp --- a/llvm/lib/Bitcode/Reader/BitcodeReader.cpp +++ b/llvm/lib/Bitcode/Reader/BitcodeReader.cpp @@ -1191,6 +1191,8 @@ return 1ULL << 60; case Attribute::ImmArg: return 1ULL << 61; + case Attribute::NoSync: + return 1ULL << 62; case Attribute::Dereferenceable: llvm_unreachable("dereferenceable attribute not supported in raw format"); break; @@ -1369,6 +1371,8 @@ return Attribute::NoRedZone; case bitc::ATTR_KIND_NO_RETURN: return Attribute::NoReturn; + case bitc::ATTR_KIND_NOSYNC: + return Attribute::NoSync; case bitc::ATTR_KIND_NOCF_CHECK: return Attribute::NoCfCheck; case bitc::ATTR_KIND_NO_UNWIND: diff --git a/llvm/lib/Bitcode/Writer/BitcodeWriter.cpp b/llvm/lib/Bitcode/Writer/BitcodeWriter.cpp --- a/llvm/lib/Bitcode/Writer/BitcodeWriter.cpp +++ b/llvm/lib/Bitcode/Writer/BitcodeWriter.cpp @@ -656,6 +656,8 @@ return bitc::ATTR_KIND_NO_RED_ZONE; case Attribute::NoReturn: return bitc::ATTR_KIND_NO_RETURN; + case Attribute::NoSync: + return bitc::ATTR_KIND_NOSYNC; case Attribute::NoCfCheck: return bitc::ATTR_KIND_NOCF_CHECK; case Attribute::NoUnwind: diff --git a/llvm/lib/IR/Attributes.cpp b/llvm/lib/IR/Attributes.cpp --- a/llvm/lib/IR/Attributes.cpp +++ b/llvm/lib/IR/Attributes.cpp @@ -298,6 +298,8 @@ return "noredzone"; if (hasAttribute(Attribute::NoReturn)) return "noreturn"; + if(hasAttribute(Attribute::NoSync)) + return "nosync"; if (hasAttribute(Attribute::NoCfCheck)) return "nocf_check"; if (hasAttribute(Attribute::NoRecurse)) diff --git a/llvm/lib/IR/Verifier.cpp b/llvm/lib/IR/Verifier.cpp --- a/llvm/lib/IR/Verifier.cpp +++ b/llvm/lib/IR/Verifier.cpp @@ -1486,6 +1486,7 @@ static bool isFuncOnlyAttr(Attribute::AttrKind Kind) { switch (Kind) { case Attribute::NoReturn: + case Attribute::NoSync: case Attribute::NoCfCheck: case Attribute::NoUnwind: case Attribute::NoInline: 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 @@ -79,8 +79,6 @@ } ///} - - /// Helper to adjust the statistics. static void bookkeeping(AbstractAttribute::ManifestPosition MP, const Attribute &Attr) { @@ -252,7 +250,7 @@ /// ------------------------ NoSync Function Attribute ------------------------- -struct AANoSyncFunction : AANoSync { +struct AANoSyncFunction : AANoSync, BooleanState { AANoSyncFunction(Function &F, InformationCache &InfoCache) : AANoSync(F, InfoCache) {} @@ -268,18 +266,25 @@ return MP_FUNCTION; } + virtual const std::string getAsStr() const override { + return getAssumed() ? "nosync" : "may-sync"; + } + /// See AbstractAttribute::updateImpl(...). virtual ChangeStatus updateImpl(Attributor &A) override; /// Return deduced attributes in \p Attrs. - virtual void - getDeducedAttributes(SmallVectorImpl &Attrs) const override { - LLVMContext &Ctx = AnchoredVal.getContext(); - Attrs.emplace_back(Attribute::get(Ctx, "nosync")); - } + // virtual void + // getDeducedAttributes(SmallVectorImpl &Attrs) const override { + // LLVMContext &Ctx = AnchoredVal.getContext(); + // Attrs.emplace_back(Attribute::get(Ctx, "nosync")); + // } - static constexpr Attribute::AttrKind ID = - Attribute::AttrKind(Attribute::None + 1); + /// See AANoSync::isAssumedNoSync() + virtual bool isAssumedNoSync() const override { return getAssumed(); } + + /// See AANoSync::isKnownNoSync() + virtual bool isKnownNoSync() const override { return getKnown(); } /// Helper function used to determine whether an instruction is non-relaxed /// atomic. In other words, if an atomic instruction does not have unordered @@ -313,13 +318,13 @@ AtomicOrdering Failure = cast(I)->getFailureOrdering(); // Only if both are relaxed, than it can be treated as relaxed. // Otherwise it is non-relaxed. - if (Success == AtomicOrdering::Unordered || - Success == AtomicOrdering::Monotonic) - return false; - if (Failure == AtomicOrdering::Unordered || - Failure == AtomicOrdering::Monotonic) - return false; - return true; + if (Success != AtomicOrdering::Unordered && + Success != AtomicOrdering::Monotonic) + return true; + if (Failure != AtomicOrdering::Unordered && + Failure != AtomicOrdering::Monotonic) + return true; + return false; } default: // Unknown atomic, assume non-relaxed. @@ -357,8 +362,8 @@ ImmutableCallSite ICS(I); auto *NoSyncAA = A.getAAFor(*this, *I); - if (!ICS && (!NoSyncAA || !NoSyncAA->isAssumedNoSync()) && - !ICS.hasFnAttr("nosync")) { + if (ICS && (!NoSyncAA || !NoSyncAA->isAssumedNoSync()) && + !ICS.hasFnAttr(Attribute::NoSync)) { indicatePessimisticFixpoint(); return ChangeStatus::CHANGED; } 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 @@ -2,7 +2,7 @@ ; 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. +; Test cases designet for the nosync function attribute. ; FIXME's are used to indicate problems and missing attributes. ; struct RT { @@ -21,18 +21,18 @@ ; } ; TEST 1 -; attribute readnone implies "nosync" +; attribute readnone implies nosync %struct.RT = type { i8, [10 x [20 x i32]], i8 } %struct.ST = type { i32, double, %struct.RT } -; 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) +; FNATTR: Function Attrs: norecurse nounwind optsize readnone ssp uwtable +; FNATTR-NEXT: define nonnull i32* @foo(%struct.ST* readnone %s) +; ATTRIBUTOR: Function Attrs: nosync nounwind optsize readnone ssp uwtable +; 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 - ret i32* %arrayidx + %arrayidx = getelementptr inbounds %struct.ST, %struct.ST* %s, i64 1, i32 2, i32 1, i64 5, i64 13 + ret i32* %arrayidx } ; TEST 2 @@ -44,11 +44,11 @@ ; FNATTR: Function Attrs: norecurse nounwind uwtable ; FNATTR-NEXT: define i32 @load_monotonic(i32* nocapture readonly) -; ATTRIBUTOR: Function Attrs: norecurse nounwind uwtable "nosync" +; ATTRIBUTOR: Function Attrs: norecurse nosync nounwind uwtable ; 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 + %2 = load atomic i32, i32* %0 monotonic, align 4 + ret i32 %2 } @@ -60,14 +60,14 @@ ; FNATTR: Function Attrs: norecurse nounwind uwtable ; FNATTR-NEXT: define void @store_monotonic(i32* nocapture) -; ATTRIBUTOR: Function Attrs: norecurse nounwind uwtable "nosync" +; ATTRIBUTOR: Function Attrs: norecurse nosync nounwind uwtable ; 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 + store atomic i32 10, i32* %0 monotonic, align 4 + ret void } -; TEST 4 - negative, should not deduce "nosync" +; TEST 4 - negative, should not deduce nosync ; atomic load with acquire ordering. ; int load_acquire(_Atomic int *num) { ; int n = atomic_load_explicit(num, memory_order_acquire); @@ -77,14 +77,14 @@ ; 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-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 + %2 = load atomic i32, i32* %0 acquire, align 4 + ret i32 %2 } -; TEST 5 - negative, should not deduce "nosync" +; TEST 5 - negative, should not deduce nosync ; atomic load with release ordering ; void load_release(_Atomic int *num) { ; atomic_store_explicit(num, 10, memory_order_release); @@ -93,14 +93,14 @@ ; FNATTR: Function Attrs: norecurse nounwind uwtable ; FNATTR-NEXT: define void @load_release(i32* nocapture) ; ATTRIBUTOR: Function Attrs: norecurse nounwind uwtable -; ATTRIBUTOR-NOT: "nosync" +; 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 + store atomic i32 10, i32* %0 release, align 4 + ret void } -; TEST 6 - negative, should not deduce "nosync" +; TEST 6 - negative, should not deduce nosync ; volatile store. ; void volatile_store(volatile int *num) { ; *num = 14; @@ -109,15 +109,14 @@ ; FNATTR: Function Attrs: norecurse nounwind uwtable ; FNATTR-NEXT: define void @volatile_store(i32*) ; ATTRIBUTOR: Function Attrs: norecurse nounwind uwtable -; ATTRIBUTOR-NOT: "nosync" +; 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 - ret void + store volatile i32 14, i32* %0, align 4 + ret void } -; TEST 7 - negative, should not deduce "nosync" +; TEST 7 - negative, should not deduce nosync ; volatile load. ; int volatile_load(volatile int *num) { ; int n = *num; @@ -127,90 +126,96 @@ ; FNATTR: Function Attrs: norecurse nounwind uwtable ; FNATTR-NEXT: define i32 @volatile_load(i32*) ; ATTRIBUTOR: Function Attrs: norecurse nounwind uwtable -; ATTRIBUTOR-NOT: "nosync" +; 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 - ret i32 %2 + %2 = load volatile i32, i32* %0, align 4 + ret i32 %2 } -; TEST 8 -declare void @nosync_function() noinline nounwind uwtable +; TEST 8 + +; FNATTR: Function Attrs: noinline nosync nounwind uwtable +; FNATTR-NEXT: declare void @nosync_function() +; ATTRIBUTOR: Function Attrs: noinline nosync nounwind uwtable +; ATTRIBUTOR-NEXT: declare void @nosync_function() +declare void @nosync_function() noinline nounwind uwtable nosync ; 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() +; FNATTR-NEXT: define void @call_nosync_function() +; ATTRIBUTOR: Function Attrs: noinline nosync nounwind uwtable +; ATTRIBUTOR-next: define void @call_nosync_function() define void @call_nosync_function() nounwind uwtable noinline { - tail call void @nosync_function() noinline nounwind uwtable - ret void + tail call void @nosync_function() noinline nounwind uwtable + ret void } -; TEST 9 - negative, should not deduce "nosync" +; TEST 9 - negative, should not deduce nosync + +; FNATTR: Function Attrs: noinline nounwind uwtable +; FNATTR-NEXT: declare void @might_sync() +; ATTRIBUTOR: Function Attrs: noinline nounwind uwtable +; ATTRIBUTOR-NEXT: declare void @might_sync() declare void @might_sync() noinline nounwind uwtable ; FNATTR: Function Attrs: noinline nounwind uwtable -; FNATTR: define void @call_might_sync() +; FNATTR-NEXT: define void @call_might_sync() ; ATTRIBUTOR: Function Attrs: noinline nounwind uwtable -; ATTRIBUTOR-NOT: "nosync" -; ATTRIBUTOR: define void @call_might_sync() +; ATTRIBUTOR-NOT: nosync +; ATTRIBUTOR-NEXT: define void @call_might_sync() define void @call_might_sync() nounwind uwtable noinline { - tail call void @might_sync() noinline nounwind uwtable - ret void + tail call void @might_sync() noinline nounwind uwtable + ret void } -; TEST 10 - negative, should not deduce "nosync" +; TEST 10 - negative, should not deduce nosync ; volatile operation in same scc. Call volatile_load defined in TEST 7. ; FNATTR: Function Attrs: noinline nounwind uwtable ; FNATTR-NEXT: define i32 @scc1(i32*) ; ATTRIBUTOR: Function Attrs: noinline nounwind uwtable -; ATTRIBUTOR-NOT: "nosync" +; 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; + tail call void @scc2(i32* %0); + %val = tail call i32 @volatile_load(i32* %0); + ret i32 %val; } ; FNATTR: Function Attrs: noinline nounwind uwtable ; FNATTR-NEXT: define void @scc2(i32*) ; ATTRIBUTOR: Function Attrs: noinline nounwind uwtable -; ATTRIBUTOR-NOT: "nosync" +; ATTRIBUTOR-NOT: nosync ; ATTRIBUTOR-NEXT: define void @scc2(i32*) define void @scc2(i32*) noinline nounwind uwtable { - tail call i32 @scc1(i32* %0); - ret void; + tail call i32 @scc1(i32* %0); + ret void; } ; TEST 11 - fences, negative -; std::atomic flag(false); -; int a; ; -; void func1(){ - ; a = 100; +; void foo1(int *a, std::atomic flag){ +; *a = 100; ; atomic_thread_fence(std::memory_order_release); ; flag.store(true, std::memory_order_relaxed); ; } ; -; void foo(){ +; void bar(int *a, std::atomic flag){ ; while(!flag.load(std::memory_order_relaxed)) ; ; ; ; atomic_thread_fence(std::memory_order_acquire); -; int b = a; +; 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 @foo1() -; ATTRIBUTOR: Function Attrs: norecurse nounwind uwtable -; ATTRIBUTOR-NOT: "nosync" -; ATTRIBUTOR-NEXT: define void @foo1() -define void @foo1(i32 *, %"struct.std::atomic"*) { +; FNATTR: Function Attrs: norecurse nounwind +; FNATTR-NEXT: define void @foo1(i32* nocapture, %"struct.std::atomic"* nocapture) +; ATTRIBUTOR-NOT: nosync +; ATTRIBUTOR: define void @foo1(i32*, %"struct.std::atomic"*) +define void @foo1(i32*, %"struct.std::atomic"*) { store i32 100, i32* %0, align 4 fence release %3 = getelementptr inbounds %"struct.std::atomic", %"struct.std::atomic"* %1, i64 0, i32 0, i32 0 @@ -218,11 +223,10 @@ 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() +; FNATTR: Function Attrs: norecurse nounwind +; FNATTR-NEXT: define void @bar(i32* nocapture readnone, %"struct.std::atomic"* nocapture readonly) +; ATTRIBUTOR-NOT: nosync +; ATTRIBUTOR: define void @bar(i32*, %"struct.std::atomic"*) define void @bar(i32 *, %"struct.std::atomic"*) { %3 = getelementptr inbounds %"struct.std::atomic", %"struct.std::atomic"* %1, i64 0, i32 0, i32 0 br label %4