diff --git a/llvm/docs/LangRef.rst b/llvm/docs/LangRef.rst --- a/llvm/docs/LangRef.rst +++ b/llvm/docs/LangRef.rst @@ -1478,6 +1478,15 @@ This function attribute indicates that the function does not call itself either directly or indirectly down any possible call path. This produces 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 through memory or other well-defined means. + Synchronization is considered possible in the presence of `atomic` accesses + that enforce an order, thus not "unordered" and "monotonic", `volatile` accesses, + as well as `convergent` function calls. Note that through the latter non-memory + communication, e.g., cross-lane operations, is also considered synchronization. + If an annotated 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 @@ -1713,7 +1722,7 @@ on the stack such that they are adjacent to the stack protector guard. The specific layout rules are: - #. Large arrays and structures containing large arrays +#. Large arrays and structures containing large arrays (``>= ssp-buffer-size``) are closest to the stack protector. #. Small arrays and structures containing small arrays (``< ssp-buffer-size``) are 2nd closest to the protector. 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 @@ -106,6 +106,9 @@ /// Mark the function as not returning. def NoReturn : EnumAttr<"noreturn">; +/// Function does not synchronize. +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 @@ -377,7 +377,7 @@ /// state will catch up with the assumed one, for a pessimistic fixpoint it is /// the other way around. struct IntegerState : public AbstractState { - /// Undrlying integer type, we assume 32 bits to be enough. + /// Underlying integer type, we assume 32 bits to be enough. using base_t = uint32_t; /// Initialize the (best) state. @@ -642,6 +642,26 @@ /// Abstract Attribute Classes /// ---------------------------------------------------------------------------- +struct AANoSync : public AbstractAttribute { + /// An abstract interface for all nosync attributes. + AANoSync(Value &V, InformationCache &InfoCache) + : AbstractAttribute(V, InfoCache) {} + + /// See AbstractAttribute::getAttrKind(). + virtual Attribute::AttrKind getAttrKind() const override { + return ID; + } + + static constexpr Attribute::AttrKind ID = + Attribute::AttrKind(Attribute::NoSync); + + /// Returns true if "nosync" is assumed. + virtual bool isAssumedNoSync() const = 0; + + /// Returns true if "nosync" is known. + virtual bool isKnownNoSync() const = 0; +}; + } // end namespace llvm #endif // LLVM_TRANSFORMS_IPO_FUNCTIONATTRS_H 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 @@ -657,6 +657,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 @@ -1283,6 +1283,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 @@ -202,6 +202,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 @@ -1195,6 +1195,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; @@ -1373,6 +1375,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 @@ -333,6 +333,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 @@ -1521,6 +1521,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 @@ -23,6 +23,7 @@ #include "llvm/IR/Argument.h" #include "llvm/IR/Attributes.h" #include "llvm/IR/InstIterator.h" +#include "llvm/IR/IntrinsicInst.h" #include "llvm/Support/CommandLine.h" #include "llvm/Support/Debug.h" #include "llvm/Support/raw_ostream.h" @@ -42,7 +43,7 @@ "Number of abstract attributes in a valid fixpoint state"); STATISTIC(NumAttributesManifested, "Number of abstract attributes manifested in IR"); - +STATISTIC(NumFnNoSync, "Number of functions marked nosync"); // TODO: Determine a good default value. // // In the LLVM-TS and SPEC2006, 32 seems to not induce compile time overheads @@ -86,10 +87,12 @@ if (!Attr.isEnumAttribute()) return; - //switch (Attr.getKindAsEnum()) { - //default: - // return; - //} + switch (Attr.getKindAsEnum()) { + case Attribute::NoSync: + NumFnNoSync++; + default: + return; + } } /// Helper to identify the correct offset into an attribute list. @@ -241,6 +244,161 @@ return const_cast(this)->getAnchorScope(); } +/// ------------------------ NoSync Function Attribute ------------------------- + +struct AANoSyncFunction : AANoSync, BooleanState { + + AANoSyncFunction(Function &F, InformationCache &InfoCache) + : AANoSync(F, InfoCache) {} + + /// See AbstractAttribute::getState() + /// { + AbstractState &getState() override { return *this; } + const AbstractState &getState() const override { return *this; } + /// } + + /// See AbstractAttribute::getManifestPosition(). + virtual ManifestPosition getManifestPosition() const override { + return MP_FUNCTION; + } + + virtual const std::string getAsStr() const override { + return getAssumed() ? "nosync" : "may-sync"; + } + + /// See AbstractAttribute::updateImpl(...). + virtual ChangeStatus updateImpl(Attributor &A) override; + + /// 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 + /// or monotonic ordering + static bool isNonRelaxedAtomic(Instruction *I); + + /// Helper function used to determine whether an instruction is volatile. + static bool isVolatile(Instruction *I); + + /// Helper function uset to check if intrinsic is volatile (memcpy, memmove, memset). + static bool isVolatileIntrinsic(Instruction *I); +}; + +bool AANoSyncFunction::isNonRelaxedAtomic(Instruction *I) { + if (!I->isAtomic()) + return false; + + AtomicOrdering Ordering; + switch (I->getOpcode()) { + case Instruction::AtomicRMW: + Ordering = cast(I)->getOrdering(); + break; + case Instruction::Store: + Ordering = cast(I)->getOrdering(); + break; + case Instruction::Load: + Ordering = cast(I)->getOrdering(); + break; + case Instruction::Fence: { + auto *FI = cast(I); + if (FI->getSyncScopeID() == SyncScope::SingleThread) + return false; + Ordering = FI->getOrdering(); + break; + } + case Instruction::AtomicCmpXchg: { + AtomicOrdering Success = cast(I)->getSuccessOrdering(); + 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 true; + if (Failure != AtomicOrdering::Unordered && + Failure != AtomicOrdering::Monotonic) + return true; + return false; + } + default: + llvm_unreachable( + "New atomic operations need to be known in the attributor."); + } + + // Relaxed. + if (Ordering == AtomicOrdering::Unordered || + Ordering == AtomicOrdering::Monotonic) + return false; + return true; +} + +bool AANoSyncFunction::isVolatileIntrinsic(Instruction *I) { + if (auto *II = dyn_cast(I)) { + /// Element wise atomic memory intrinsics are skipped as they can't be + /// volatile and can only be unordered. + switch (II->getIntrinsicID()) { + case Intrinsic::memset: + case Intrinsic::memmove: + case Intrinsic::memcpy: { + /// isvolatile is 4th argument in these intrinsics. + Value *Arg = II->getOperand(3); + if (Arg->getType()->isIntegerTy(1) && + cast(Arg)->getValue() == 1) + return true; + break; + } + default: + return false; + } + } + return false; +} + +bool AANoSyncFunction::isVolatile(Instruction *I) { + switch (I->getOpcode()) { + case Instruction::AtomicRMW: + return cast(I)->isVolatile(); + case Instruction::Store: + return cast(I)->isVolatile(); + case Instruction::Load: + return cast(I)->isVolatile(); + case Instruction::AtomicCmpXchg: + return cast(I)->isVolatile(); + default: + return false; + } +} + +ChangeStatus AANoSyncFunction::updateImpl(Attributor &A) { + Function &F = getAnchorScope(); + + /// We are looking for volatile instructions or Non-Relaxed atomics. + /// FIXME: We should ipmrove the handling of intrinsics. + for (Instruction *I : InfoCache.getReadOrWriteInstsForFunction(F)) { + ImmutableCallSite ICS(I); + auto *NoSyncAA = A.getAAFor(*this, *I); + + if (isa(I) && !isVolatileIntrinsic(I)) + continue; + + if (ICS && (!NoSyncAA || !NoSyncAA->isAssumedNoSync()) && + !ICS.hasFnAttr(Attribute::NoSync)) { + indicatePessimisticFixpoint(); + return ChangeStatus::CHANGED; + } + + if (!isVolatile(I) && !isNonRelaxedAtomic(I)) + continue; + + indicatePessimisticFixpoint(); + return ChangeStatus::CHANGED; + } + + return ChangeStatus::UNCHANGED; +} + /// ---------------------------------------------------------------------------- /// Attributor /// ---------------------------------------------------------------------------- @@ -383,6 +541,9 @@ Function &F, InformationCache &InfoCache, DenseSet *Whitelist) { + // Every function might be marked "nosync" + registerAA(*new AANoSyncFunction(F, InfoCache)); + // Walk all instructions to find more attribute opportunities and also // interesting instructions that might be queried by abstract attributes // during their initialization or update. diff --git a/llvm/test/Bitcode/attributes.ll b/llvm/test/Bitcode/attributes.ll --- a/llvm/test/Bitcode/attributes.ll +++ b/llvm/test/Bitcode/attributes.ll @@ -351,6 +351,11 @@ ret void } +; CHECK: define void @f60() #37 +define void @f60() nosync { + ret void +} + ; CHECK: attributes #0 = { noreturn } ; CHECK: attributes #1 = { nounwind } ; CHECK: attributes #2 = { readnone } @@ -388,3 +393,4 @@ ; CHECK: attributes #34 = { sanitize_hwaddress } ; CHECK: attributes #35 = { shadowcallstack } ; CHECK: attributes #36 = { nobuiltin } +; CHECK: attributes #37 = { nosync } diff --git a/llvm/test/Transforms/FunctionAttrs/arg_returned.ll b/llvm/test/Transforms/FunctionAttrs/arg_returned.ll --- a/llvm/test/Transforms/FunctionAttrs/arg_returned.ll +++ b/llvm/test/Transforms/FunctionAttrs/arg_returned.ll @@ -7,15 +7,15 @@ ; TEST SCC test returning an integer value argument ; -; CHECK: Function Attrs: noinline norecurse nounwind readnone uwtable +; CHECK: Function Attrs: noinline norecurse nosync nounwind readnone uwtable ; CHECK: define i32 @sink_r0(i32 returned %r) ; ; FIXME: returned on %r missing: -; CHECK: Function Attrs: noinline nounwind readnone uwtable +; CHECK: Function Attrs: noinline nosync nounwind readnone uwtable ; CHECK: define i32 @scc_r1(i32 %a, i32 %r, i32 %b) ; ; FIXME: returned on %r missing: -; CHECK: Function Attrs: noinline nounwind readnone uwtable +; CHECK: Function Attrs: noinline nosync nounwind readnone uwtable ; CHECK: define i32 @scc_r2(i32 %a, i32 %b, i32 %r) ; ; int scc_r1(int a, int b, int r); @@ -150,15 +150,15 @@ ; TEST SCC test returning a pointer value argument ; -; CHECK: Function Attrs: noinline norecurse nounwind readnone uwtable +; CHECK: Function Attrs: noinline norecurse nosync nounwind readnone uwtable ; CHECK: define double* @ptr_sink_r0(double* readnone returned %r) ; ; FIXME: returned on %r missing: -; CHECK: Function Attrs: noinline nounwind readnone uwtable +; CHECK: Function Attrs: noinline nosync nounwind readnone uwtable ; CHECK: define double* @ptr_scc_r1(double* %a, double* readnone %r, double* nocapture readnone %b) ; ; FIXME: returned on %r missing: -; CHECK: Function Attrs: noinline nounwind readnone uwtable +; CHECK: Function Attrs: noinline nosync nounwind readnone uwtable ; CHECK: define double* @ptr_scc_r2(double* readnone %a, double* readnone %b, double* readnone %r) ; ; double* ptr_scc_r1(double* a, double* b, double* r); @@ -244,7 +244,7 @@ ; } ; ; FIXME: returned on %a missing: -; CHECK: Function Attrs: noinline nounwind readonly uwtable +; CHECK: Function Attrs: noinline nosync nounwind readonly uwtable ; CHECK: define i32* @ret0(i32* readonly %a) define i32* @ret0(i32* %a) #0 { entry: @@ -335,7 +335,7 @@ ; } ; ; FIXME: returned on %b missing: -; CHECK: Function Attrs: noinline norecurse nounwind readnone uwtable +; CHECK: Function Attrs: noinline norecurse nosync nounwind readnone uwtable ; CHECK: define double @select_and_phi(double %b) define double @select_and_phi(double %b) #0 { entry: @@ -363,7 +363,7 @@ ; } ; ; FIXME: returned on %b missing: -; CHECK: Function Attrs: noinline nounwind readnone uwtable +; CHECK: Function Attrs: noinline nosync nounwind readnone uwtable ; CHECK: define double @recursion_select_and_phi(i32 %a, double %b) define double @recursion_select_and_phi(i32 %a, double %b) #0 { entry: @@ -390,7 +390,7 @@ ; } ; ; FIXME: returned on %b missing: -; CHECK: Function Attrs: noinline norecurse nounwind readnone uwtable +; CHECK: Function Attrs: noinline norecurse nosync nounwind readnone uwtable ; CHECK: define double* @bitcast(i32* readnone %b) define double* @bitcast(i32* %b) #0 { entry: @@ -409,7 +409,7 @@ ; } ; ; FIXME: returned on %b missing: -; CHECK: Function Attrs: noinline norecurse nounwind readnone uwtable +; CHECK: Function Attrs: noinline norecurse nosync nounwind readnone uwtable ; CHECK: define double* @bitcasts_select_and_phi(i32* readnone %b) define double* @bitcasts_select_and_phi(i32* %b) #0 { entry: @@ -442,7 +442,7 @@ ; /* return undef */ ; } ; -; CHECK: Function Attrs: noinline norecurse nounwind readnone uwtable +; CHECK: Function Attrs: noinline norecurse nosync nounwind readnone uwtable ; CHECK: define double* @ret_arg_arg_undef(i32* readnone %b) define double* @ret_arg_arg_undef(i32* %b) #0 { entry: @@ -475,7 +475,7 @@ ; /* return undef */ ; } ; -; CHECK: Function Attrs: noinline norecurse nounwind readnone uwtable +; CHECK: Function Attrs: noinline norecurse nosync nounwind readnone uwtable ; CHECK: define double* @ret_undef_arg_arg(i32* readnone %b) define double* @ret_undef_arg_arg(i32* %b) #0 { entry: @@ -508,7 +508,7 @@ ; /* return undef */ ; } ; -; CHECK: Function Attrs: noinline norecurse nounwind readnone uwtable +; CHECK: Function Attrs: noinline norecurse nosync nounwind readnone uwtable ; CHECK: define double* @ret_undef_arg_undef(i32* readnone %b) define double* @ret_undef_arg_undef(i32* %b) #0 { entry: @@ -576,8 +576,8 @@ attributes #0 = { noinline nounwind uwtable } ; CHECK-NOT: attributes # -; CHECK-DAG: attributes #{{[0-9]*}} = { noinline norecurse nounwind readnone uwtable } -; CHECK-DAG: attributes #{{[0-9]*}} = { noinline nounwind readnone uwtable } -; CHECK-DAG: attributes #{{[0-9]*}} = { noinline nounwind readonly uwtable } +; CHECK-DAG: attributes #{{[0-9]*}} = { noinline norecurse nosync nounwind readnone uwtable } +; CHECK-DAG: attributes #{{[0-9]*}} = { noinline nosync nounwind readnone uwtable } +; CHECK-DAG: attributes #{{[0-9]*}} = { noinline nosync nounwind readonly uwtable } ; CHECK-DAG: attributes #{{[0-9]*}} = { noinline nounwind uwtable } ; CHECK-NOT: attributes # diff --git a/llvm/test/Transforms/FunctionAttrs/fn_noreturn.ll b/llvm/test/Transforms/FunctionAttrs/fn_noreturn.ll --- a/llvm/test/Transforms/FunctionAttrs/fn_noreturn.ll +++ b/llvm/test/Transforms/FunctionAttrs/fn_noreturn.ll @@ -20,7 +20,7 @@ ; } ; ; FIXME: no-return missing -; CHECK: Function Attrs: noinline nounwind readnone uwtable +; CHECK: Function Attrs: noinline nosync nounwind readnone uwtable ; CHECK: define void @srec0() ; define void @srec0() #0 { @@ -37,7 +37,7 @@ ; } ; ; FIXME: no-return missing -; CHECK: Function Attrs: noinline nounwind readnone uwtable +; CHECK: Function Attrs: noinline nosync nounwind readnone uwtable ; CHECK: define i32 @srec16(i32 %a) ; define i32 @srec16(i32 %a) #0 { @@ -69,7 +69,7 @@ ; } ; ; FIXME: no-return missing -; CHECK: Function Attrs: noinline norecurse nounwind readnone uwtable +; CHECK: Function Attrs: noinline norecurse nosync nounwind readnone uwtable ; CHECK: define i32 @endless_loop(i32 %a) ; define i32 @endless_loop(i32 %a) #0 { @@ -89,7 +89,7 @@ ; } ; ; FIXME: no-return missing -; CHECK: Function Attrs: noinline norecurse nounwind readnone uwtable +; CHECK: Function Attrs: noinline norecurse nosync nounwind readnone uwtable ; CHECK: define i32 @dead_return(i32 returned %a) ; define i32 @dead_return(i32 %a) #0 { @@ -111,7 +111,7 @@ ; } ; ; FIXME: no-return missing -; CHECK: Function Attrs: noinline nounwind readnone uwtable +; CHECK: Function Attrs: noinline nosync nounwind readnone uwtable ; CHECK: define i32 @multiple_noreturn_calls(i32 %a) ; define i32 @multiple_noreturn_calls(i32 %a) #0 { diff --git a/llvm/test/Transforms/FunctionAttrs/nosync.ll b/llvm/test/Transforms/FunctionAttrs/nosync.ll new file mode 100644 --- /dev/null +++ b/llvm/test/Transforms/FunctionAttrs/nosync.ll @@ -0,0 +1,320 @@ +; RUN: opt -functionattrs -S < %s | FileCheck %s --check-prefix=FNATTR +; RUN: opt -attributor -attributor-disable=false -S < %s | FileCheck %s --check-prefix=ATTRIBUTOR +target datalayout = "e-m:e-i64:64-f80:128-n8:16:32:64-S128" + +; Test cases designed for the nosync function attribute. +; FIXME's are used to indicate problems and missing attributes. + +; struct RT { +; char A; +; int B[10][20]; +; char C; +; }; +; struct ST { +; int X; +; double Y; +; struct RT Z; +; }; +; +; int *foo(struct ST *s) { +; return &s[1].Z.B[5][13]; +; } + +; TEST 1 +; attribute readnone implies nosync +%struct.RT = type { i8, [10 x [20 x i32]], i8 } +%struct.ST = type { i32, double, %struct.RT } + +; 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 +} + +; TEST 2 +; atomic load with monotonic ordering +; int load_monotonic(_Atomic int *num) { +; int n = atomic_load_explicit(num, memory_order_relaxed); +; return n; +; } + +; FNATTR: Function Attrs: norecurse nounwind uwtable +; FNATTR-NEXT: define i32 @load_monotonic(i32* nocapture readonly) +; 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 +} + + +; TEST 3 +; atomic store with monotonic ordering. +; void store_monotonic(_Atomic int *num) { +; atomic_load_explicit(num, memory_order_relaxed); +; } + +; FNATTR: Function Attrs: norecurse nounwind uwtable +; FNATTR-NEXT: define void @store_monotonic(i32* nocapture) +; 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 +} + +; 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); +; return n; +; } + +; 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 +} + +; 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); +; } + +; 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 volatile i32 10, i32* %0 release, align 4 + ret void +} + +; TEST 6 - negative volatile, relaxed atomic + +; FNATTR: Function Attrs: norecurse nounwind uwtable +; FNATTR-NEXT: define void @load_volatile_release(i32* nocapture) +; ATTRIBUTOR: Function Attrs: norecurse nounwind uwtable +; ATTRIBUTOR-NOT: nosync +; ATTRIBUTOR-NEXT: define void @load_volatile_release(i32* nocapture) +define void @load_volatile_release(i32* nocapture) norecurse nounwind uwtable { + store atomic volatile i32 10, i32* %0 release, align 4 + ret void +} + +; TEST 7 - negative, should not deduce nosync +; volatile store. +; void volatile_store(volatile int *num) { +; *num = 14; +; } + +; 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 + ret void +} + +; TEST 8 - negative, should not deduce nosync +; volatile load. +; int volatile_load(volatile int *num) { +; int n = *num; +; return n; +; } + +; 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 + ret i32 %2 +} + +; TEST 9 + +; 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-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 +} + +; TEST 10 - 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-NEXT: define void @call_might_sync() +; ATTRIBUTOR: Function Attrs: noinline nounwind uwtable +; 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 +} + +; TEST 11 - negative, should not deduce nosync +; volatile operation in same scc. Call volatile_load defined in TEST 8. + +; 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; +} + +; 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 12 - fences, negative +; +; void foo1(int *a, std::atomic flag){ +; *a = 100; +; atomic_thread_fence(std::memory_order_release); +; flag.store(true, std::memory_order_relaxed); +; } +; +; void bar(int *a, std::atomic flag){ +; 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 +; 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 + store atomic i8 1, i8* %3 monotonic, align 1 + ret void +} + +; 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 + +4: ; preds = %4, %2 + %5 = load atomic i8, i8* %3 monotonic, align 1 + %6 = and i8 %5, 1 + %7 = icmp eq i8 %6, 0 + br i1 %7, label %4, label %8 + +8: ; preds = %4 + fence acquire + ret void +} + +; TEST 13 - Fence syncscope("singlethread") seq_cst +; FNATTR: Function Attrs: norecurse nounwind +; FNATTR-NEXT: define void @foo1_singlethread(i32* nocapture, %"struct.std::atomic"* nocapture) +; ATTRIBUTOR: Function Attrs: nosync +; ATTRIBUTOR: define void @foo1_singlethread(i32*, %"struct.std::atomic"*) +define void @foo1_singlethread(i32*, %"struct.std::atomic"*) { + store i32 100, i32* %0, align 4 + fence syncscope("singlethread") release + %3 = getelementptr inbounds %"struct.std::atomic", %"struct.std::atomic"* %1, i64 0, i32 0, i32 0 + store atomic i8 1, i8* %3 monotonic, align 1 + ret void +} + +; FNATTR: Function Attrs: norecurse nounwind +; FNATTR-NEXT: define void @bar_singlethread(i32* nocapture readnone, %"struct.std::atomic"* nocapture readonly) +; ATTRIBUTOR: Function Attrs: nosync +; ATTRIBUTOR: define void @bar_singlethread(i32*, %"struct.std::atomic"*) +define void @bar_singlethread(i32 *, %"struct.std::atomic"*) { + %3 = getelementptr inbounds %"struct.std::atomic", %"struct.std::atomic"* %1, i64 0, i32 0, i32 0 + br label %4 + +4: ; preds = %4, %2 + %5 = load atomic i8, i8* %3 monotonic, align 1 + %6 = and i8 %5, 1 + %7 = icmp eq i8 %6, 0 + br i1 %7, label %4, label %8 + +8: ; preds = %4 + fence syncscope("singlethread") acquire + ret void +} + +declare void @llvm.memcpy(i8* %dest, i8* %src, i32 %len, i1 %isvolatile) +declare void @llvm.memset(i8* %dest, i8 %val, i32 %len, i1 %isvolatile) +declare float @llvm.cos(float %val) + +; TEST 14 - negative, checking volatile intrinsics. + +; ATTRIBUTOR: Function Attrs: nounwind +; ATTRIBUTOR-NOT: nosync +; ATTRIBUTOR-NEXT: define i32 @memcpy_volatile(i8* %ptr1, i8* %ptr2) +define i32 @memcpy_volatile(i8* %ptr1, i8* %ptr2) { + call void @llvm.memcpy(i8* %ptr1, i8* %ptr2, i32 8, i1 1) + ret i32 4 +} + +; TEST 15 - positive, non-volatile intrinsic. + +; ATTRIBUTOR: Function Attrs: nosync +; ATTRIBUTOR-NEXT: define i32 @memset_non_volatile(i8* %ptr1, i8 %val) +define i32 @memset_non_volatile(i8* %ptr1, i8 %val) { + call void @llvm.memset(i8* %ptr1, i8 %val, i32 8, i1 0) + ret i32 4 +} + +; TEST 16 - positive, non-volatile intrinsic. + +; ATTRIBUTOR: Function Attrs: nounwind +; ATTRIBUTOR-NOT: nosync +; ATTRIBUTOR-NEXT: define i32 @inline_asm_test(i32 %x) +define i32 @inline_asm_test(i32 %x) { + call i32 asm "bswap $0", "=r,r"(i32 %x) + ret i32 4 +} diff --git a/llvm/test/Transforms/FunctionAttrs/read_write_returned_arguments_scc.ll b/llvm/test/Transforms/FunctionAttrs/read_write_returned_arguments_scc.ll --- a/llvm/test/Transforms/FunctionAttrs/read_write_returned_arguments_scc.ll +++ b/llvm/test/Transforms/FunctionAttrs/read_write_returned_arguments_scc.ll @@ -30,7 +30,7 @@ ; target datalayout = "e-m:e-i64:64-f80:128-n8:16:32:64-S128" -; CHECK: Function Attrs: nounwind +; CHECK: Function Attrs: nosync nounwind ; CHECK-NEXT: define i32* @external_ret2_nrw(i32* %n0, i32* %r0, i32* %w0) define i32* @external_ret2_nrw(i32* %n0, i32* %r0, i32* %w0) { entry: @@ -41,7 +41,7 @@ ret i32* %call3 } -; CHECK: Function Attrs: nounwind +; CHECK: Function Attrs: nosync nounwind ; CHECK-NEXT: define internal i32* @internal_ret0_nw(i32* %n0, i32* %w0) define internal i32* @internal_ret0_nw(i32* %n0, i32* %w0) { entry: @@ -70,7 +70,7 @@ ret i32* %retval.0 } -; CHECK: Function Attrs: nounwind +; CHECK: Function Attrs: nosync nounwind ; CHECK-NEXT: define internal i32* @internal_ret1_rrw(i32* %r0, i32* %r1, i32* %w0) define internal i32* @internal_ret1_rrw(i32* %r0, i32* %r1, i32* %w0) { entry: @@ -102,7 +102,7 @@ ret i32* %retval.0 } -; CHECK: Function Attrs: norecurse nounwind +; CHECK: Function Attrs: norecurse nosync nounwind ; CHECK-NEXT: define i32* @external_sink_ret2_nrw(i32* readnone %n0, i32* nocapture readonly %r0, i32* returned %w0) define i32* @external_sink_ret2_nrw(i32* %n0, i32* %r0, i32* %w0) { entry: @@ -121,7 +121,7 @@ ret i32* %w0 } -; CHECK: Function Attrs: nounwind +; CHECK: Function Attrs: nosync nounwind ; CHECK-NEXT: define internal i32* @internal_ret1_rw(i32* %r0, i32* %w0) define internal i32* @internal_ret1_rw(i32* %r0, i32* %w0) { entry: @@ -147,7 +147,7 @@ ret i32* %retval.0 } -; CHECK: Function Attrs: nounwind +; CHECK: Function Attrs: nosync nounwind ; CHECK-NEXT: define i32* @external_source_ret2_nrw(i32* %n0, i32* %r0, i32* %w0) define i32* @external_source_ret2_nrw(i32* %n0, i32* %r0, i32* %w0) { entry: @@ -160,6 +160,6 @@ ; for a subset relation. ; ; CHECK-NOT: attributes # -; CHECK: attributes #{{.*}} = { nounwind } -; CHECK: attributes #{{.*}} = { norecurse nounwind } +; CHECK: attributes #{{.*}} = { nosync nounwind } +; CHECK: attributes #{{.*}} = { norecurse nosync nounwind } ; CHECK-NOT: attributes #