Эх сурвалжийг харах

[Attributor] Deduce "nosync" function attribute.

Introduce and deduce "nosync" function attribute to indicate that a function
does not synchronize with another thread in a way that other thread might free memory.

Reviewers: jdoerfert, jfb, nhaehnle, arsenm

Subscribers: wdng, hfinkel, nhaenhle, mehdi_amini, steven_wu,
dexonsmith, arsenm, uenoku, hiraditya, jfb, llvm-commits

Differential Revision: https://reviews.llvm.org/D62766

git-svn-id: https://llvm.org/svn/llvm-project/llvm/trunk@365830 91177308-0d34-0410-b5e6-96231b3b80d8
Stefan Stipanovic 6 жил өмнө
parent
commit
52d4c048b3

+ 10 - 0
docs/LangRef.rst

@@ -1493,6 +1493,16 @@ example:
     Annotated functions may still raise an exception, i.a., ``nounwind`` is not implied.
     Annotated functions may still raise an exception, i.a., ``nounwind`` is not implied.
     If an invocation of an annotated function does not return control back
     If an invocation of an annotated function does not return control back
     to a point in the call stack, the behavior is undefined.
     to a point in the call stack, the behavior is undefined.
+``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 `convergent` function calls
+    non-memory communication, e.g., cross-lane operations, are possible and are also
+    considered synchronization. However `convergent` does not contradict `nosync`.
+    If an annotated function does ever synchronize with another thread,
+    the behavior is undefined.
 ``nounwind``
 ``nounwind``
     This function attribute indicates that the function never raises an
     This function attribute indicates that the function never raises an
     exception. If the function does raise an exception, its runtime
     exception. If the function does raise an exception, its runtime

+ 2 - 1
include/llvm/Bitcode/LLVMBitCodes.h

@@ -629,7 +629,8 @@ enum AttributeKindCodes {
   ATTR_KIND_SPECULATIVE_LOAD_HARDENING = 59,
   ATTR_KIND_SPECULATIVE_LOAD_HARDENING = 59,
   ATTR_KIND_IMMARG = 60,
   ATTR_KIND_IMMARG = 60,
   ATTR_KIND_WILLRETURN = 61,
   ATTR_KIND_WILLRETURN = 61,
-  ATTR_KIND_NOFREE = 62
+  ATTR_KIND_NOFREE = 62,
+  ATTR_KIND_NOSYNC = 63
 };
 };
 
 
 enum ComdatSelectionKindCodes {
 enum ComdatSelectionKindCodes {

+ 3 - 0
include/llvm/IR/Attributes.td

@@ -109,6 +109,9 @@ def NoRedZone : EnumAttr<"noredzone">;
 /// Mark the function as not returning.
 /// Mark the function as not returning.
 def NoReturn : EnumAttr<"noreturn">;
 def NoReturn : EnumAttr<"noreturn">;
 
 
+/// Function does not synchronize.
+def NoSync : EnumAttr<"nosync">;
+
 /// Disable Indirect Branch Tracking.
 /// Disable Indirect Branch Tracking.
 def NoCfCheck : EnumAttr<"nocf_check">;
 def NoCfCheck : EnumAttr<"nocf_check">;
 
 

+ 31 - 11
include/llvm/Transforms/IPO/Attributor.h

@@ -377,7 +377,7 @@ struct AbstractState {
 /// state will catch up with the assumed one, for a pessimistic fixpoint it is
 /// state will catch up with the assumed one, for a pessimistic fixpoint it is
 /// the other way around.
 /// the other way around.
 struct IntegerState : public AbstractState {
 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;
   using base_t = uint32_t;
 
 
   /// Initialize the (best) state.
   /// Initialize the (best) state.
@@ -664,20 +664,40 @@ struct AAReturnedValues : public AbstractAttribute {
 };
 };
 
 
 struct AANoUnwind : public AbstractAttribute {
 struct AANoUnwind : public AbstractAttribute {
-    /// An abstract interface for all nosync attributes.
-    AANoUnwind(Value &V, InformationCache &InfoCache)
-        : AbstractAttribute(V, InfoCache) {}
+  /// An abstract interface for all nosync attributes.
+  AANoUnwind(Value &V, InformationCache &InfoCache)
+      : AbstractAttribute(V, InfoCache) {}
 
 
-    /// See AbstractAttribute::getAttrKind()/
-    virtual Attribute::AttrKind getAttrKind() const override { return ID; }
+  /// See AbstractAttribute::getAttrKind()/
+  virtual Attribute::AttrKind getAttrKind() const override { return ID; }
+
+  static constexpr Attribute::AttrKind ID = Attribute::NoUnwind;
+
+  /// Returns true if nounwind is assumed.
+  virtual bool isAssumedNoUnwind() const = 0;
+
+  /// Returns true if nounwind is known.
+  virtual bool isKnownNoUnwind() const = 0;
+};
+
+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::NoUnwind;
+  static constexpr Attribute::AttrKind ID =
+      Attribute::AttrKind(Attribute::NoSync);
 
 
-    /// Returns true if nounwind is assumed.
-    virtual bool isAssumedNoUnwind() const = 0;
+  /// Returns true if "nosync" is assumed.
+  virtual bool isAssumedNoSync() const = 0;
 
 
-    /// Returns true if nounwind is known.
-    virtual bool isKnownNoUnwind() const = 0;
+  /// Returns true if "nosync" is known.
+  virtual bool isKnownNoSync() const = 0;
 };
 };
 
 
 } // end namespace llvm
 } // end namespace llvm

+ 1 - 0
lib/AsmParser/LLLexer.cpp

@@ -658,6 +658,7 @@ lltok::Kind LLLexer::LexIdentifier() {
   KEYWORD(nonnull);
   KEYWORD(nonnull);
   KEYWORD(noredzone);
   KEYWORD(noredzone);
   KEYWORD(noreturn);
   KEYWORD(noreturn);
+  KEYWORD(nosync);
   KEYWORD(nocf_check);
   KEYWORD(nocf_check);
   KEYWORD(nounwind);
   KEYWORD(nounwind);
   KEYWORD(optforfuzzing);
   KEYWORD(optforfuzzing);

+ 1 - 0
lib/AsmParser/LLParser.cpp

@@ -1287,6 +1287,7 @@ bool LLParser::ParseFnAttributeValuePairs(AttrBuilder &B,
     case lltok::kw_nonlazybind: B.addAttribute(Attribute::NonLazyBind); break;
     case lltok::kw_nonlazybind: B.addAttribute(Attribute::NonLazyBind); break;
     case lltok::kw_noredzone: B.addAttribute(Attribute::NoRedZone); break;
     case lltok::kw_noredzone: B.addAttribute(Attribute::NoRedZone); break;
     case lltok::kw_noreturn: B.addAttribute(Attribute::NoReturn); 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_nocf_check: B.addAttribute(Attribute::NoCfCheck); break;
     case lltok::kw_norecurse: B.addAttribute(Attribute::NoRecurse); break;
     case lltok::kw_norecurse: B.addAttribute(Attribute::NoRecurse); break;
     case lltok::kw_nounwind: B.addAttribute(Attribute::NoUnwind); break;
     case lltok::kw_nounwind: B.addAttribute(Attribute::NoUnwind); break;

+ 1 - 0
lib/AsmParser/LLToken.h

@@ -203,6 +203,7 @@ enum Kind {
   kw_nonnull,
   kw_nonnull,
   kw_noredzone,
   kw_noredzone,
   kw_noreturn,
   kw_noreturn,
+  kw_nosync,
   kw_nocf_check,
   kw_nocf_check,
   kw_nounwind,
   kw_nounwind,
   kw_optforfuzzing,
   kw_optforfuzzing,

+ 7 - 1
lib/Bitcode/Reader/BitcodeReader.cpp

@@ -1280,6 +1280,9 @@ static uint64_t getRawAttributeMask(Attribute::AttrKind Val) {
     return 1ULL << 62;
     return 1ULL << 62;
   case Attribute::NoFree:
   case Attribute::NoFree:
     return 1ULL << 63;
     return 1ULL << 63;
+  case Attribute::NoSync:
+    llvm_unreachable("nosync attribute not supported in raw format");
+    break;
   case Attribute::Dereferenceable:
   case Attribute::Dereferenceable:
     llvm_unreachable("dereferenceable attribute not supported in raw format");
     llvm_unreachable("dereferenceable attribute not supported in raw format");
     break;
     break;
@@ -1305,7 +1308,8 @@ static void addRawAttributeValue(AttrBuilder &B, uint64_t Val) {
     if (I == Attribute::Dereferenceable ||
     if (I == Attribute::Dereferenceable ||
         I == Attribute::DereferenceableOrNull ||
         I == Attribute::DereferenceableOrNull ||
         I == Attribute::ArgMemOnly ||
         I == Attribute::ArgMemOnly ||
-        I == Attribute::AllocSize)
+        I == Attribute::AllocSize ||
+        I == Attribute::NoSync)
       continue;
       continue;
     if (uint64_t A = (Val & getRawAttributeMask(I))) {
     if (uint64_t A = (Val & getRawAttributeMask(I))) {
       if (I == Attribute::Alignment)
       if (I == Attribute::Alignment)
@@ -1466,6 +1470,8 @@ static Attribute::AttrKind getAttrFromCode(uint64_t Code) {
     return Attribute::NoRedZone;
     return Attribute::NoRedZone;
   case bitc::ATTR_KIND_NO_RETURN:
   case bitc::ATTR_KIND_NO_RETURN:
     return Attribute::NoReturn;
     return Attribute::NoReturn;
+  case bitc::ATTR_KIND_NOSYNC:
+    return Attribute::NoSync;
   case bitc::ATTR_KIND_NOCF_CHECK:
   case bitc::ATTR_KIND_NOCF_CHECK:
     return Attribute::NoCfCheck;
     return Attribute::NoCfCheck;
   case bitc::ATTR_KIND_NO_UNWIND:
   case bitc::ATTR_KIND_NO_UNWIND:

+ 2 - 0
lib/Bitcode/Writer/BitcodeWriter.cpp

@@ -659,6 +659,8 @@ static uint64_t getAttrKindEncoding(Attribute::AttrKind Kind) {
     return bitc::ATTR_KIND_NO_RED_ZONE;
     return bitc::ATTR_KIND_NO_RED_ZONE;
   case Attribute::NoReturn:
   case Attribute::NoReturn:
     return bitc::ATTR_KIND_NO_RETURN;
     return bitc::ATTR_KIND_NO_RETURN;
+  case Attribute::NoSync:
+    return bitc::ATTR_KIND_NOSYNC;
   case Attribute::NoCfCheck:
   case Attribute::NoCfCheck:
     return bitc::ATTR_KIND_NOCF_CHECK;
     return bitc::ATTR_KIND_NOCF_CHECK;
   case Attribute::NoUnwind:
   case Attribute::NoUnwind:

+ 2 - 0
lib/IR/Attributes.cpp

@@ -335,6 +335,8 @@ std::string Attribute::getAsString(bool InAttrGrp) const {
     return "noredzone";
     return "noredzone";
   if (hasAttribute(Attribute::NoReturn))
   if (hasAttribute(Attribute::NoReturn))
     return "noreturn";
     return "noreturn";
+  if (hasAttribute(Attribute::NoSync))
+    return "nosync";
   if (hasAttribute(Attribute::WillReturn))
   if (hasAttribute(Attribute::WillReturn))
     return "willreturn";
     return "willreturn";
   if (hasAttribute(Attribute::NoCfCheck))
   if (hasAttribute(Attribute::NoCfCheck))

+ 1 - 0
lib/IR/Verifier.cpp

@@ -1493,6 +1493,7 @@ void Verifier::visitModuleFlagCGProfileEntry(const MDOperand &MDO) {
 static bool isFuncOnlyAttr(Attribute::AttrKind Kind) {
 static bool isFuncOnlyAttr(Attribute::AttrKind Kind) {
   switch (Kind) {
   switch (Kind) {
   case Attribute::NoReturn:
   case Attribute::NoReturn:
+  case Attribute::NoSync:
   case Attribute::WillReturn:
   case Attribute::WillReturn:
   case Attribute::NoCfCheck:
   case Attribute::NoCfCheck:
   case Attribute::NoUnwind:
   case Attribute::NoUnwind:

+ 193 - 0
lib/Transforms/IPO/Attributor.cpp

@@ -23,6 +23,7 @@
 #include "llvm/IR/Argument.h"
 #include "llvm/IR/Argument.h"
 #include "llvm/IR/Attributes.h"
 #include "llvm/IR/Attributes.h"
 #include "llvm/IR/InstIterator.h"
 #include "llvm/IR/InstIterator.h"
+#include "llvm/IR/IntrinsicInst.h"
 #include "llvm/Support/CommandLine.h"
 #include "llvm/Support/CommandLine.h"
 #include "llvm/Support/Debug.h"
 #include "llvm/Support/Debug.h"
 #include "llvm/Support/raw_ostream.h"
 #include "llvm/Support/raw_ostream.h"
@@ -48,6 +49,7 @@ STATISTIC(NumFnUniqueReturned, "Number of function with unique return");
 STATISTIC(NumFnKnownReturns, "Number of function with known return values");
 STATISTIC(NumFnKnownReturns, "Number of function with known return values");
 STATISTIC(NumFnArgumentReturned,
 STATISTIC(NumFnArgumentReturned,
           "Number of function arguments marked returned");
           "Number of function arguments marked returned");
+STATISTIC(NumFnNoSync, "Number of functions marked nosync");
 
 
 // TODO: Determine a good default value.
 // TODO: Determine a good default value.
 //
 //
@@ -99,6 +101,9 @@ static void bookkeeping(AbstractAttribute::ManifestPosition MP,
   case Attribute::Returned:
   case Attribute::Returned:
     NumFnArgumentReturned++;
     NumFnArgumentReturned++;
     return;
     return;
+  case Attribute::NoSync:
+    NumFnNoSync++;
+    break;
   default:
   default:
     return;
     return;
   }
   }
@@ -719,6 +724,191 @@ ChangeStatus AAReturnedValuesImpl::updateImpl(Attributor &A) {
   return Changed;
   return Changed;
 }
 }
 
 
+/// ------------------------ 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 isNoSyncIntrinsic(Instruction *I);
+};
+
+bool AANoSyncFunction::isNonRelaxedAtomic(Instruction *I) {
+  if (!I->isAtomic())
+    return false;
+
+  AtomicOrdering Ordering;
+  switch (I->getOpcode()) {
+  case Instruction::AtomicRMW:
+    Ordering = cast<AtomicRMWInst>(I)->getOrdering();
+    break;
+  case Instruction::Store:
+    Ordering = cast<StoreInst>(I)->getOrdering();
+    break;
+  case Instruction::Load:
+    Ordering = cast<LoadInst>(I)->getOrdering();
+    break;
+  case Instruction::Fence: {
+    auto *FI = cast<FenceInst>(I);
+    if (FI->getSyncScopeID() == SyncScope::SingleThread)
+      return false;
+    Ordering = FI->getOrdering();
+    break;
+  }
+  case Instruction::AtomicCmpXchg: {
+    AtomicOrdering Success = cast<AtomicCmpXchgInst>(I)->getSuccessOrdering();
+    AtomicOrdering Failure = cast<AtomicCmpXchgInst>(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;
+}
+
+/// Checks if an intrinsic is nosync. Currently only checks mem* intrinsics.
+/// FIXME: We should ipmrove the handling of intrinsics.
+bool AANoSyncFunction::isNoSyncIntrinsic(Instruction *I) {
+  if (auto *II = dyn_cast<IntrinsicInst>(I)) {
+    switch (II->getIntrinsicID()) {
+    /// Element wise atomic memory intrinsics are can only be unordered,
+    /// therefore nosync.
+    case Intrinsic::memset_element_unordered_atomic:
+    case Intrinsic::memmove_element_unordered_atomic:
+    case Intrinsic::memcpy_element_unordered_atomic:
+      return true;
+    case Intrinsic::memset:
+    case Intrinsic::memmove:
+    case Intrinsic::memcpy:
+      if (!cast<MemIntrinsic>(II)->isVolatile())
+        return true;
+      return false;
+    default:
+      return false;
+    }
+  }
+  return false;
+}
+
+bool AANoSyncFunction::isVolatile(Instruction *I) {
+  assert(!ImmutableCallSite(I) && !isa<CallBase>(I) &&
+         "Calls should not be checked here");
+
+  switch (I->getOpcode()) {
+  case Instruction::AtomicRMW:
+    return cast<AtomicRMWInst>(I)->isVolatile();
+  case Instruction::Store:
+    return cast<StoreInst>(I)->isVolatile();
+  case Instruction::Load:
+    return cast<LoadInst>(I)->isVolatile();
+  case Instruction::AtomicCmpXchg:
+    return cast<AtomicCmpXchgInst>(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<AANoSyncFunction>(*this, *I);
+
+    if (isa<IntrinsicInst>(I) && isNoSyncIntrinsic(I))
+        continue;
+
+    if (ICS && (!NoSyncAA || !NoSyncAA->isAssumedNoSync()) &&
+        !ICS.hasFnAttr(Attribute::NoSync)) {
+      indicatePessimisticFixpoint();
+      return ChangeStatus::CHANGED;
+    }
+
+    if(ICS)
+      continue;
+
+    if (!isVolatile(I) && !isNonRelaxedAtomic(I))
+      continue;
+
+    indicatePessimisticFixpoint();
+    return ChangeStatus::CHANGED;
+  }
+
+  auto &OpcodeInstMap = InfoCache.getOpcodeInstMapForFunction(F);
+  auto Opcodes = {(unsigned)Instruction::Invoke, (unsigned)Instruction::CallBr,
+                  (unsigned)Instruction::Call};
+
+  for (unsigned Opcode : Opcodes) {
+    for (Instruction *I : OpcodeInstMap[Opcode]) {
+      // At this point we handled all read/write effects and they are all
+      // nosync, so they can be skipped.
+      if (I->mayReadOrWriteMemory())
+        continue;
+
+      ImmutableCallSite ICS(I);
+
+      // non-convergent and readnone imply nosync.
+      if (!ICS.isConvergent())
+        continue;
+
+      indicatePessimisticFixpoint();
+      return ChangeStatus::CHANGED;
+    }
+  }
+
+  return ChangeStatus::UNCHANGED;
+}
+
 /// ----------------------------------------------------------------------------
 /// ----------------------------------------------------------------------------
 ///                               Attributor
 ///                               Attributor
 /// ----------------------------------------------------------------------------
 /// ----------------------------------------------------------------------------
@@ -864,6 +1054,9 @@ void Attributor::identifyDefaultAbstractAttributes(
   // Every function can be nounwind.
   // Every function can be nounwind.
   registerAA(*new AANoUnwindFunction(F, InfoCache));
   registerAA(*new AANoUnwindFunction(F, InfoCache));
 
 
+  // Every function might be marked "nosync"
+  registerAA(*new AANoSyncFunction(F, InfoCache));
+
   // Return attributes are only appropriate if the return type is non void.
   // Return attributes are only appropriate if the return type is non void.
   Type *ReturnType = F.getReturnType();
   Type *ReturnType = F.getReturnType();
   if (!ReturnType->isVoidTy()) {
   if (!ReturnType->isVoidTy()) {

+ 1 - 0
lib/Transforms/Utils/CodeExtractor.cpp

@@ -809,6 +809,7 @@ Function *CodeExtractor::constructFunction(const ValueSet &inputs,
       case Attribute::NoBuiltin:
       case Attribute::NoBuiltin:
       case Attribute::NoCapture:
       case Attribute::NoCapture:
       case Attribute::NoReturn:
       case Attribute::NoReturn:
+      case Attribute::NoSync:
       case Attribute::None:
       case Attribute::None:
       case Attribute::NonNull:
       case Attribute::NonNull:
       case Attribute::ReadNone:
       case Attribute::ReadNone:

+ 10 - 3
test/Bitcode/attributes.ll

@@ -203,8 +203,8 @@ declare void @nobuiltin()
 define void @f34()
 define void @f34()
 ; CHECK: define void @f34()
 ; CHECK: define void @f34()
 {
 {
-        call void @nobuiltin() nobuiltin
-; CHECK: call void @nobuiltin() #38
+  call void @nobuiltin() nobuiltin
+; CHECK: call void @nobuiltin() #39
         ret void;
         ret void;
 }
 }
 
 
@@ -362,6 +362,12 @@ define void @f61() nofree {
   ret void
   ret void
 }
 }
 
 
+; CHECK: define void @f62() #38
+define void @f62() nosync
+{
+  ret void
+}
+
 ; CHECK: attributes #0 = { noreturn }
 ; CHECK: attributes #0 = { noreturn }
 ; CHECK: attributes #1 = { nounwind }
 ; CHECK: attributes #1 = { nounwind }
 ; CHECK: attributes #2 = { readnone }
 ; CHECK: attributes #2 = { readnone }
@@ -400,4 +406,5 @@ define void @f61() nofree {
 ; CHECK: attributes #35 = { shadowcallstack }
 ; CHECK: attributes #35 = { shadowcallstack }
 ; CHECK: attributes #36 = { willreturn }
 ; CHECK: attributes #36 = { willreturn }
 ; CHECK: attributes #37 = { nofree }
 ; CHECK: attributes #37 = { nofree }
-; CHECK: attributes #38 = { nobuiltin }
+; CHECK: attributes #38 = { nosync }
+; CHECK: attributes #39 = { nobuiltin }

+ 42 - 28
test/Transforms/FunctionAttrs/arg_returned.ll

@@ -8,13 +8,13 @@
 
 
 ; TEST SCC test returning an integer value argument
 ; TEST SCC test returning an integer value argument
 ;
 ;
-; BOTH: Function Attrs: noinline norecurse nounwind readnone uwtable
+; BOTH: Function Attrs: noinline norecurse nosync nounwind readnone uwtable
 ; BOTH-NEXT: define i32 @sink_r0(i32 returned %r)
 ; BOTH-NEXT: define i32 @sink_r0(i32 returned %r)
-; BOTH: Function Attrs: noinline nounwind readnone uwtable
+; BOTH: Function Attrs: noinline nosync nounwind readnone uwtable
 ; BOTH-NEXT: define i32 @scc_r1(i32 %a, i32 returned %r, i32 %b)
 ; BOTH-NEXT: define i32 @scc_r1(i32 %a, i32 returned %r, i32 %b)
-; BOTH: Function Attrs: noinline nounwind readnone uwtable
+; BOTH: Function Attrs: noinline nosync nounwind readnone uwtable
 ; BOTH-NEXT: define i32 @scc_r2(i32 %a, i32 %b, i32 returned %r)
 ; BOTH-NEXT: define i32 @scc_r2(i32 %a, i32 %b, i32 returned %r)
-; BOTH: Function Attrs: noinline nounwind readnone uwtable
+; BOTH: Function Attrs: noinline nosync nounwind readnone uwtable
 ; BOTH-NEXT: define i32 @scc_rX(i32 %a, i32 %b, i32 %r)
 ; BOTH-NEXT: define i32 @scc_rX(i32 %a, i32 %b, i32 %r)
 ;
 ;
 ; FNATTR: define i32 @sink_r0(i32 returned %r)
 ; FNATTR: define i32 @sink_r0(i32 returned %r)
@@ -159,20 +159,23 @@ return:                                           ; preds = %cond.end, %if.then3
 
 
 ; TEST SCC test returning a pointer value argument
 ; TEST SCC test returning a pointer value argument
 ;
 ;
-; BOTH: Function Attrs: noinline norecurse nounwind readnone uwtable
+; BOTH: Function Attrs: noinline norecurse nosync nounwind readnone uwtable
 ; BOTH-NEXT: define double* @ptr_sink_r0(double* readnone returned %r)
 ; BOTH-NEXT: define double* @ptr_sink_r0(double* readnone returned %r)
-; BOTH: Function Attrs: noinline nounwind readnone uwtable
+; BOTH: Function Attrs: noinline nosync nounwind readnone uwtable
 ; BOTH-NEXT: define double* @ptr_scc_r1(double* %a, double* readnone returned %r, double* nocapture readnone %b)
 ; BOTH-NEXT: define double* @ptr_scc_r1(double* %a, double* readnone returned %r, double* nocapture readnone %b)
-; BOTH: Function Attrs: noinline nounwind readnone uwtable
+; BOTH: Function Attrs: noinline nosync nounwind readnone uwtable
 ; BOTH-NEXT: define double* @ptr_scc_r2(double* readnone %a, double* readnone %b, double* readnone returned %r)
 ; BOTH-NEXT: define double* @ptr_scc_r2(double* readnone %a, double* readnone %b, double* readnone returned %r)
 ;
 ;
 ; FNATTR: define double* @ptr_sink_r0(double* readnone returned %r)
 ; FNATTR: define double* @ptr_sink_r0(double* readnone returned %r)
 ; FNATTR: define double* @ptr_scc_r1(double* %a, double* readnone %r, double* nocapture readnone %b)
 ; FNATTR: define double* @ptr_scc_r1(double* %a, double* readnone %r, double* nocapture readnone %b)
 ; FNATTR: define double* @ptr_scc_r2(double* readnone %a, double* readnone %b, double* readnone %r)
 ; FNATTR: define double* @ptr_scc_r2(double* readnone %a, double* readnone %b, double* readnone %r)
 ;
 ;
-; ATTRIBUTOR: define double* @ptr_sink_r0(double* returned %r)
-; ATTRIBUTOR: define double* @ptr_scc_r1(double* %a, double* returned %r, double* %b)
-; ATTRIBUTOR: define double* @ptr_scc_r2(double* %a, double* %b, double* returned %r)
+; ATTRIBUTOR: Function Attrs: noinline nosync nounwind uwtable
+; ATTRIBUTOR-NEXT: define double* @ptr_sink_r0(double* returned %r)
+; ATTRIBUTOR: Function Attrs: noinline nosync nounwind uwtable
+; ATTRIBUTOR-NEXT: define double* @ptr_scc_r1(double* %a, double* returned %r, double* %b)
+; ATTRIBUTOR: Function Attrs: noinline nosync nounwind uwtable
+; ATTRIBUTOR-NEXT: define double* @ptr_scc_r2(double* %a, double* %b, double* returned %r)
 ;
 ;
 ; double* ptr_scc_r1(double* a, double* b, double* r);
 ; double* ptr_scc_r1(double* a, double* b, double* r);
 ; double* ptr_scc_r2(double* a, double* b, double* r);
 ; double* ptr_scc_r2(double* a, double* b, double* r);
@@ -258,7 +261,7 @@ return:                                           ; preds = %cond.end, %if.then3
 ;
 ;
 ; FIXME: no-return missing
 ; FIXME: no-return missing
 ; FNATTR:  define i32* @rt0(i32* readonly %a)
 ; FNATTR:  define i32* @rt0(i32* readonly %a)
-; BOTH: Function Attrs: noinline nounwind readonly uwtable
+; BOTH: Function Attrs: noinline nosync nounwind readonly uwtable
 ; BOTH-NEXT:    define i32* @rt0(i32* readonly returned %a)
 ; BOTH-NEXT:    define i32* @rt0(i32* readonly returned %a)
 define i32* @rt0(i32* %a) #0 {
 define i32* @rt0(i32* %a) #0 {
 entry:
 entry:
@@ -277,7 +280,7 @@ entry:
 ;
 ;
 ; FIXME: no-return missing
 ; FIXME: no-return missing
 ; FNATTR:  define noalias i32* @rt1(i32* nocapture readonly %a)
 ; FNATTR:  define noalias i32* @rt1(i32* nocapture readonly %a)
-; BOTH: Function Attrs: noinline nounwind readonly uwtable
+; BOTH: Function Attrs: noinline nosync nounwind readonly uwtable
 ; BOTH-NEXT:    define noalias i32* @rt1(i32* nocapture readonly %a)
 ; BOTH-NEXT:    define noalias i32* @rt1(i32* nocapture readonly %a)
 define i32* @rt1(i32* %a) #0 {
 define i32* @rt1(i32* %a) #0 {
 entry:
 entry:
@@ -438,11 +441,12 @@ entry:
 ;   return b == 0? b : x;
 ;   return b == 0? b : x;
 ; }
 ; }
 ;
 ;
-; BOTH: Function Attrs: noinline norecurse nounwind readnone uwtable
+; BOTH: Function Attrs: noinline norecurse nosync nounwind readnone uwtable
 ; BOTH-NEXT: define double @select_and_phi(double returned %b)
 ; BOTH-NEXT: define double @select_and_phi(double returned %b)
 ;
 ;
 ; FNATTR:     define double @select_and_phi(double %b)
 ; FNATTR:     define double @select_and_phi(double %b)
-; ATTRIBUTOR: define double @select_and_phi(double returned %b)
+; ATTRIBUTOR: Function Attrs: noinline nosync nounwind uwtable
+; ATTRIBUTOR-NEXT: define double @select_and_phi(double returned %b)
 define double @select_and_phi(double %b) #0 {
 define double @select_and_phi(double %b) #0 {
 entry:
 entry:
   %cmp = fcmp ogt double %b, 0.000000e+00
   %cmp = fcmp ogt double %b, 0.000000e+00
@@ -468,11 +472,13 @@ if.end:                                           ; preds = %if.then, %entry
 ;   return b == 0? b : x;
 ;   return b == 0? b : x;
 ; }
 ; }
 ;
 ;
-; BOTH: Function Attrs: noinline nounwind readnone uwtable
+; BOTH: Function Attrs: noinline nosync nounwind readnone uwtable
 ; BOTH-NEXT: define double @recursion_select_and_phi(i32 %a, double returned %b)
 ; BOTH-NEXT: define double @recursion_select_and_phi(i32 %a, double returned %b)
 ;
 ;
 ; FNATTR:     define double @recursion_select_and_phi(i32 %a, double %b)
 ; FNATTR:     define double @recursion_select_and_phi(i32 %a, double %b)
-; ATTRIBUTOR: define double @recursion_select_and_phi(i32 %a, double returned %b)
+;
+; ATTRIBUTOR: Function Attrs: noinline nosync nounwind uwtable
+; ATTRIBUTOR-NEXT: define double @recursion_select_and_phi(i32 %a, double returned %b)
 define double @recursion_select_and_phi(i32 %a, double %b) #0 {
 define double @recursion_select_and_phi(i32 %a, double %b) #0 {
 entry:
 entry:
   %dec = add nsw i32 %a, -1
   %dec = add nsw i32 %a, -1
@@ -497,11 +503,13 @@ if.end:                                           ; preds = %if.then, %entry
 ;   return (double*)b;
 ;   return (double*)b;
 ; }
 ; }
 ;
 ;
-; BOTH: Function Attrs: noinline norecurse nounwind readnone uwtable
+; BOTH: Function Attrs: noinline norecurse nosync nounwind readnone uwtable
 ; BOTH-NEXT:  define double* @bitcast(i32* readnone returned %b)
 ; BOTH-NEXT:  define double* @bitcast(i32* readnone returned %b)
 ;
 ;
 ; FNATTR:     define double* @bitcast(i32* readnone %b)
 ; FNATTR:     define double* @bitcast(i32* readnone %b)
-; ATTRIBUTOR: define double* @bitcast(i32* returned %b)
+;
+; ATTRIBUTOR: Function Attrs: noinline nosync nounwind uwtable
+; ATTRIBUTOR-NEXT: define double* @bitcast(i32* returned %b)
 define double* @bitcast(i32* %b) #0 {
 define double* @bitcast(i32* %b) #0 {
 entry:
 entry:
   %bc0 = bitcast i32* %b to double*
   %bc0 = bitcast i32* %b to double*
@@ -518,11 +526,13 @@ entry:
 ;   return b != 0 ? b : x;
 ;   return b != 0 ? b : x;
 ; }
 ; }
 ;
 ;
-; BOTH: Function Attrs: noinline norecurse nounwind readnone uwtable
+; BOTH: Function Attrs: noinline norecurse nosync nounwind readnone uwtable
 ; BOTH-NEXT: define double* @bitcasts_select_and_phi(i32* readnone returned %b)
 ; BOTH-NEXT: define double* @bitcasts_select_and_phi(i32* readnone returned %b)
 ;
 ;
 ; FNATTR:     define double* @bitcasts_select_and_phi(i32* readnone %b)
 ; FNATTR:     define double* @bitcasts_select_and_phi(i32* readnone %b)
-; ATTRIBUTOR: define double* @bitcasts_select_and_phi(i32* returned %b)
+;
+; ATTRIBUTOR: Function Attrs: noinline nosync nounwind uwtable
+; ATTRIBUTOR-NEXT: define double* @bitcasts_select_and_phi(i32* returned %b)
 define double* @bitcasts_select_and_phi(i32* %b) #0 {
 define double* @bitcasts_select_and_phi(i32* %b) #0 {
 entry:
 entry:
   %bc0 = bitcast i32* %b to double*
   %bc0 = bitcast i32* %b to double*
@@ -554,11 +564,13 @@ if.end:                                           ; preds = %if.then, %entry
 ;   /* return undef */
 ;   /* return undef */
 ; }
 ; }
 ;
 ;
-; BOTH: Function Attrs: noinline norecurse nounwind readnone uwtable
+; BOTH: Function Attrs: noinline norecurse nosync nounwind readnone uwtable
 ; BOTH-NEXT:  define double* @ret_arg_arg_undef(i32* readnone returned %b)
 ; BOTH-NEXT:  define double* @ret_arg_arg_undef(i32* readnone returned %b)
 ;
 ;
 ; FNATTR:     define double* @ret_arg_arg_undef(i32* readnone %b)
 ; FNATTR:     define double* @ret_arg_arg_undef(i32* readnone %b)
-; ATTRIBUTOR: define double* @ret_arg_arg_undef(i32* returned %b)
+;
+; ATTRIBUTOR: Function Attrs: noinline nosync nounwind uwtable
+; ATTRIBUTOR-NEXT: define double* @ret_arg_arg_undef(i32* returned %b)
 define double* @ret_arg_arg_undef(i32* %b) #0 {
 define double* @ret_arg_arg_undef(i32* %b) #0 {
 entry:
 entry:
   %bc0 = bitcast i32* %b to double*
   %bc0 = bitcast i32* %b to double*
@@ -590,11 +602,13 @@ ret_undef:
 ;   /* return undef */
 ;   /* return undef */
 ; }
 ; }
 ;
 ;
-; BOTH: Function Attrs: noinline norecurse nounwind readnone uwtable
+; BOTH: Function Attrs: noinline norecurse nosync nounwind readnone uwtable
 ; BOTH-NEXT:  define double* @ret_undef_arg_arg(i32* readnone returned %b)
 ; BOTH-NEXT:  define double* @ret_undef_arg_arg(i32* readnone returned %b)
 ;
 ;
 ; FNATTR:     define double* @ret_undef_arg_arg(i32* readnone %b)
 ; FNATTR:     define double* @ret_undef_arg_arg(i32* readnone %b)
-; ATTRIBUTOR: define double* @ret_undef_arg_arg(i32* returned %b)
+;
+; ATTRIBUTOR: Function Attrs: noinline nosync nounwind uwtable
+; ATTRIBUTOR-NEXT: define double* @ret_undef_arg_arg(i32* returned %b)
 define double* @ret_undef_arg_arg(i32* %b) #0 {
 define double* @ret_undef_arg_arg(i32* %b) #0 {
 entry:
 entry:
   %bc0 = bitcast i32* %b to double*
   %bc0 = bitcast i32* %b to double*
@@ -626,7 +640,7 @@ ret_arg1:
 ;   /* return undef */
 ;   /* return undef */
 ; }
 ; }
 ;
 ;
-; BOTH: Function Attrs: noinline norecurse nounwind readnone uwtable
+; BOTH: Function Attrs: noinline norecurse nosync nounwind readnone uwtable
 ; BOTH-NEXT:  define double* @ret_undef_arg_undef(i32* readnone returned %b)
 ; BOTH-NEXT:  define double* @ret_undef_arg_undef(i32* readnone returned %b)
 ;
 ;
 ; FNATTR:     define double* @ret_undef_arg_undef(i32* readnone %b)
 ; FNATTR:     define double* @ret_undef_arg_undef(i32* readnone %b)
@@ -730,8 +744,8 @@ unreachableblock2:
 attributes #0 = { noinline nounwind uwtable }
 attributes #0 = { noinline nounwind uwtable }
 
 
 ; BOTH-NOT: attributes #
 ; BOTH-NOT: attributes #
-; BOTH-DAG: attributes #{{[0-9]*}} = { noinline norecurse nounwind readnone uwtable }
-; BOTH-DAG: attributes #{{[0-9]*}} = { noinline nounwind readnone uwtable }
-; BOTH-DAG: attributes #{{[0-9]*}} = { noinline nounwind readonly uwtable }
+; BOTH-DAG: attributes #{{[0-9]*}} = { noinline norecurse nosync nounwind readnone uwtable }
+; BOTH-DAG: attributes #{{[0-9]*}} = { noinline nosync nounwind readnone uwtable }
+; BOTH-DAG: attributes #{{[0-9]*}} = { noinline nosync nounwind readonly uwtable }
 ; BOTH-DAG: attributes #{{[0-9]*}} = { noinline nounwind uwtable }
 ; BOTH-DAG: attributes #{{[0-9]*}} = { noinline nounwind uwtable }
 ; BOTH-NOT: attributes #
 ; BOTH-NOT: attributes #

+ 5 - 5
test/Transforms/FunctionAttrs/fn_noreturn.ll

@@ -20,7 +20,7 @@ target datalayout = "e-m:e-i64:64-f80:128-n8:16:32:64-S128"
 ; }
 ; }
 ;
 ;
 ; FIXME: no-return missing
 ; FIXME: no-return missing
-; CHECK: Function Attrs: noinline nounwind readnone uwtable
+; CHECK: Function Attrs: noinline nosync nounwind readnone uwtable
 ; CHECK: define void @srec0()
 ; CHECK: define void @srec0()
 ;
 ;
 define void @srec0() #0 {
 define void @srec0() #0 {
@@ -37,7 +37,7 @@ entry:
 ; }
 ; }
 ;
 ;
 ; FIXME: no-return missing
 ; 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)
 ; CHECK: define i32 @srec16(i32 %a)
 ;
 ;
 define i32 @srec16(i32 %a) #0 {
 define i32 @srec16(i32 %a) #0 {
@@ -69,7 +69,7 @@ entry:
 ; }
 ; }
 ;
 ;
 ; FIXME: no-return missing
 ; 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)
 ; CHECK: define i32 @endless_loop(i32 %a)
 ;
 ;
 define i32 @endless_loop(i32 %a) #0 {
 define i32 @endless_loop(i32 %a) #0 {
@@ -89,7 +89,7 @@ while.body:                                       ; preds = %entry, %while.body
 ; }
 ; }
 ;
 ;
 ; FIXME: no-return missing
 ; 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)
 ; CHECK: define i32 @dead_return(i32 returned %a)
 ;
 ;
 define i32 @dead_return(i32 %a) #0 {
 define i32 @dead_return(i32 %a) #0 {
@@ -111,7 +111,7 @@ return:                                           ; No predecessors!
 ; }
 ; }
 ;
 ;
 ; FIXME: no-return missing
 ; 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)
 ; CHECK: define i32 @multiple_noreturn_calls(i32 %a)
 ;
 ;
 define i32 @multiple_noreturn_calls(i32 %a) #0 {
 define i32 @multiple_noreturn_calls(i32 %a) #0 {

+ 352 - 0
test/Transforms/FunctionAttrs/nosync.ll

@@ -0,0 +1,352 @@
+; 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
+; non-convergent and 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: nofree 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: nofree 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: nofree 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: nofree 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: nofree 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: nofree 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: nofree 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: nofree 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: nofree 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<bool> flag){
+;   *a = 100;
+;   atomic_thread_fence(std::memory_order_release);
+;   flag.store(true, std::memory_order_relaxed);
+; }
+;
+; void bar(int *a, std::atomic<bool> 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: nofree 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: nofree 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: nofree 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: nofree 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)
+
+; 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 - negative, inline assembly.
+
+; ATTRIBUTOR: 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
+}
+
+declare void @readnone_test() convergent readnone
+
+; ATTRIBUTOR: define void @convergent_readnone()
+; TEST 17 - negative. Convergent
+define void @convergent_readnone(){
+    call void @readnone_test()
+    ret void
+}
+
+; ATTRIBUTOR: Function Attrs: nounwind
+; ATTRIBUTOR-NEXT: declare void @llvm.x86.sse2.clflush(i8*)
+declare void @llvm.x86.sse2.clflush(i8*)
+@a = common global i32 0, align 4
+
+; TEST 18 - negative. Synchronizing intrinsic
+
+; ATTRIBUTOR: Function Attrs: nounwind
+; ATTRIBUTOR-NOT: nosync
+; ATTRIBUTOR-NEXT: define void @i_totally_sync()
+define void @i_totally_sync() {
+  tail call void @llvm.x86.sse2.clflush(i8* bitcast (i32* @a to i8*))
+  ret void
+}
+
+declare float @llvm.cos(float %val) readnone
+
+; TEST 19 - positive, readnone & non-convergent intrinsic.
+
+; ATTRIBUTOR: Function Attrs: nosync nounwind
+; ATTRIBUTOR-NEXT: define i32 @cos_test(float %x)
+define i32 @cos_test(float %x) {
+  call float @llvm.cos(float %x)
+  ret i32 4
+}

+ 3 - 3
test/Transforms/FunctionAttrs/nounwind.ll

@@ -4,7 +4,7 @@
 ; TEST 1
 ; TEST 1
 ; CHECK: Function Attrs: norecurse nounwind readnone
 ; CHECK: Function Attrs: norecurse nounwind readnone
 ; CHECK-NEXT: define i32 @foo1()
 ; CHECK-NEXT: define i32 @foo1()
-; ATTRIBUTOR: Function Attrs: nounwind
+; ATTRIBUTOR: Function Attrs: nosync nounwind
 ; ATTRIBUTOR-NEXT: define i32 @foo1()
 ; ATTRIBUTOR-NEXT: define i32 @foo1()
 define i32 @foo1() {
 define i32 @foo1() {
   ret i32 1
   ret i32 1
@@ -13,7 +13,7 @@ define i32 @foo1() {
 ; TEST 2
 ; TEST 2
 ; CHECK: Function Attrs: nounwind readnone
 ; CHECK: Function Attrs: nounwind readnone
 ; CHECK-NEXT: define i32 @scc1_foo()
 ; CHECK-NEXT: define i32 @scc1_foo()
-; ATTRIBUTOR: Function Attrs: nounwind
+; ATTRIBUTOR: Function Attrs: nosync nounwind
 ; ATTRIBUTOR-NEXT: define i32 @scc1_foo()
 ; ATTRIBUTOR-NEXT: define i32 @scc1_foo()
 define i32 @scc1_foo() {
 define i32 @scc1_foo() {
   %1 = call i32 @scc1_bar()
   %1 = call i32 @scc1_bar()
@@ -24,7 +24,7 @@ define i32 @scc1_foo() {
 ; TEST 3
 ; TEST 3
 ; CHECK: Function Attrs: nounwind readnone
 ; CHECK: Function Attrs: nounwind readnone
 ; CHECK-NEXT: define i32 @scc1_bar()
 ; CHECK-NEXT: define i32 @scc1_bar()
-; ATTRIBUTOR: Function Attrs: nounwind
+; ATTRIBUTOR: Function Attrs: nosync nounwind
 ; ATTRIBUTOR-NEXT: define i32 @scc1_bar()
 ; ATTRIBUTOR-NEXT: define i32 @scc1_bar()
 define i32 @scc1_bar() {
 define i32 @scc1_bar() {
   %1 = call i32 @scc1_foo()
   %1 = call i32 @scc1_foo()

+ 8 - 8
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"
 target datalayout = "e-m:e-i64:64-f80:128-n8:16:32:64-S128"
 
 
-; CHECK: Function Attrs: nofree nounwind
+; CHECK: Function Attrs: nofree nosync nounwind
 ; CHECK-NEXT: define i32* @external_ret2_nrw(i32* %n0, i32* %r0, i32* returned %w0)
 ; CHECK-NEXT: define i32* @external_ret2_nrw(i32* %n0, i32* %r0, i32* returned %w0)
 define i32* @external_ret2_nrw(i32* %n0, i32* %r0, i32* %w0) {
 define i32* @external_ret2_nrw(i32* %n0, i32* %r0, i32* %w0) {
 entry:
 entry:
@@ -41,7 +41,7 @@ entry:
   ret i32* %call3
   ret i32* %call3
 }
 }
 
 
-; CHECK: Function Attrs: nofree nounwind
+; CHECK: Function Attrs: nofree nosync nounwind
 ; CHECK-NEXT: define internal i32* @internal_ret0_nw(i32* returned %n0, i32* %w0)
 ; CHECK-NEXT: define internal i32* @internal_ret0_nw(i32* returned %n0, i32* %w0)
 define internal i32* @internal_ret0_nw(i32* %n0, i32* %w0) {
 define internal i32* @internal_ret0_nw(i32* %n0, i32* %w0) {
 entry:
 entry:
@@ -70,7 +70,7 @@ return:                                           ; preds = %if.end, %if.then
   ret i32* %retval.0
   ret i32* %retval.0
 }
 }
 
 
-; CHECK: Function Attrs: nofree nounwind
+; CHECK: Function Attrs: nofree nosync nounwind
 ; CHECK-NEXT: define internal i32* @internal_ret1_rrw(i32* %r0, i32* returned %r1, i32* %w0)
 ; CHECK-NEXT: define internal i32* @internal_ret1_rrw(i32* %r0, i32* returned %r1, i32* %w0)
 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:
 entry:
@@ -102,7 +102,7 @@ return:                                           ; preds = %if.end, %if.then
   ret i32* %retval.0
   ret i32* %retval.0
 }
 }
 
 
-; CHECK: Function Attrs: nofree norecurse nounwind
+; CHECK: Function Attrs: nofree norecurse nosync nounwind
 ; CHECK-NEXT: define i32* @external_sink_ret2_nrw(i32* readnone %n0, i32* nocapture readonly %r0, i32* returned %w0)
 ; 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) {
 define i32* @external_sink_ret2_nrw(i32* %n0, i32* %r0, i32* %w0) {
 entry:
 entry:
@@ -121,7 +121,7 @@ return:                                           ; preds = %if.end, %if.then
   ret i32* %w0
   ret i32* %w0
 }
 }
 
 
-; CHECK: Function Attrs: nofree nounwind
+; CHECK: Function Attrs: nofree nosync nounwind
 ; CHECK-NEXT: define internal i32* @internal_ret1_rw(i32* %r0, i32* returned %w0)
 ; CHECK-NEXT: define internal i32* @internal_ret1_rw(i32* %r0, i32* returned %w0)
 define internal i32* @internal_ret1_rw(i32* %r0, i32* %w0) {
 define internal i32* @internal_ret1_rw(i32* %r0, i32* %w0) {
 entry:
 entry:
@@ -147,7 +147,7 @@ return:                                           ; preds = %if.end, %if.then
   ret i32* %retval.0
   ret i32* %retval.0
 }
 }
 
 
-; CHECK: Function Attrs: nofree nounwind
+; CHECK: Function Attrs: nofree nosync nounwind
 ; CHECK-NEXT: define i32* @external_source_ret2_nrw(i32* %n0, i32* %r0, i32* returned %w0)
 ; CHECK-NEXT: define i32* @external_source_ret2_nrw(i32* %n0, i32* %r0, i32* returned %w0)
 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:
 entry:
@@ -160,6 +160,6 @@ entry:
 ; for a subset relation.
 ; for a subset relation.
 ;
 ;
 ; CHECK-NOT: attributes #
 ; CHECK-NOT: attributes #
-; CHECK: attributes #{{.*}} = { nofree nounwind }
-; CHECK: attributes #{{.*}} = { nofree norecurse nounwind }
+; CHECK: attributes #{{.*}} = { nofree nosync nounwind }
+; CHECK: attributes #{{.*}} = { nofree norecurse nosync nounwind }
 ; CHECK-NOT: attributes #
 ; CHECK-NOT: attributes #