[llvm] 400fde9 - [Attributor] Add lightweight version for attribute deduction only.

Florian Hahn via llvm-commits llvm-commits at lists.llvm.org
Sat Aug 5 03:51:59 PDT 2023


Author: Florian Hahn
Date: 2023-08-05T11:47:28+01:00
New Revision: 400fde92963588ae2b618626cb1ac73fd91c7d4d

URL: https://github.com/llvm/llvm-project/commit/400fde92963588ae2b618626cb1ac73fd91c7d4d
DIFF: https://github.com/llvm/llvm-project/commit/400fde92963588ae2b618626cb1ac73fd91c7d4d.diff

LOG: [Attributor] Add lightweight version for attribute deduction only.

This patch adds a lightweight instance of Attributor that only deduces
attributes.

This is just an initial version with the goal to have a version that
only focuses on attributes to replace the function-attrs pass.

The initial version has a few open issues pending until default
enablement, the main one probably being compile time. The main
additional functionality this will provide in general is propagating
attributes to call sites.

Open issues:

* compile time
  The current version increase O3 +2.67% and ThinLTO +6.18% when replacing FunctionAttr
   https://llvm-compile-time-tracker.com/compare.php?from=c4bb3e073548cf436d5fa0406e3ae75e94684dec&to=d992630a69c79a2587d736e6a88f448850413bd1&stat=instructions%3Au
   Both are with an additional change to preserve more analysis, like FunctionAttrs CGSCC run.

* some missed attribute inference

Reviewed By: jdoerfert

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

Added: 
    

Modified: 
    llvm/include/llvm/Transforms/IPO/Attributor.h
    llvm/lib/Passes/PassRegistry.def
    llvm/lib/Transforms/IPO/Attributor.cpp
    llvm/test/Transforms/Attributor/nofpclass-round.ll
    llvm/test/Transforms/FunctionAttrs/argmemonly.ll
    llvm/test/Transforms/FunctionAttrs/nocapture.ll
    llvm/test/Transforms/FunctionAttrs/nonnull.ll
    llvm/test/Transforms/FunctionAttrs/norecurse.ll
    llvm/test/Transforms/FunctionAttrs/nounwind.ll
    llvm/test/Transforms/FunctionAttrs/readattrs.ll
    llvm/test/Transforms/FunctionAttrs/readnone.ll
    llvm/test/Transforms/FunctionAttrs/willreturn.ll
    llvm/test/Transforms/FunctionAttrs/writeonly.ll

Removed: 
    


################################################################################
diff  --git a/llvm/include/llvm/Transforms/IPO/Attributor.h b/llvm/include/llvm/Transforms/IPO/Attributor.h
index 675221576fb680..b311f7b663524c 100644
--- a/llvm/include/llvm/Transforms/IPO/Attributor.h
+++ b/llvm/include/llvm/Transforms/IPO/Attributor.h
@@ -2387,6 +2387,10 @@ struct Attributor {
   /// The allocator used to allocate memory, e.g. for `AbstractAttribute`s.
   BumpPtrAllocator &Allocator;
 
+  const SmallSetVector<Function *, 8> &getModifiedFunctions() {
+    return CGModifiedFunctions;
+  }
+
 private:
   /// This method will do fixpoint iteration until fixpoint or the
   /// maximum iteration count is reached.
@@ -3375,6 +3379,20 @@ struct AttributorCGSCCPass : public PassInfoMixin<AttributorCGSCCPass> {
                         LazyCallGraph &CG, CGSCCUpdateResult &UR);
 };
 
+/// A more lightweight version of the Attributor which only runs attribute
+/// inference but no simplifications.
+struct AttributorLightPass : public PassInfoMixin<AttributorLightPass> {
+  PreservedAnalyses run(Module &M, ModuleAnalysisManager &AM);
+};
+
+/// A more lightweight version of the Attributor which only runs attribute
+/// inference but no simplifications.
+struct AttributorLightCGSCCPass
+    : public PassInfoMixin<AttributorLightCGSCCPass> {
+  PreservedAnalyses run(LazyCallGraph::SCC &C, CGSCCAnalysisManager &AM,
+                        LazyCallGraph &CG, CGSCCUpdateResult &UR);
+};
+
 /// Helper function to clamp a state \p S of type \p StateType with the
 /// information in \p R and indicate/return if \p S did change (as-in update is
 /// required to be run again).

diff  --git a/llvm/lib/Passes/PassRegistry.def b/llvm/lib/Passes/PassRegistry.def
index 3907a5fdee2596..fe24e9da9125ff 100644
--- a/llvm/lib/Passes/PassRegistry.def
+++ b/llvm/lib/Passes/PassRegistry.def
@@ -42,6 +42,7 @@ MODULE_ALIAS_ANALYSIS("globals-aa", GlobalsAA())
 #endif
 MODULE_PASS("always-inline", AlwaysInlinerPass())
 MODULE_PASS("attributor", AttributorPass())
+MODULE_PASS("attributor-light", AttributorLightPass())
 MODULE_PASS("annotation2metadata", Annotation2MetadataPass())
 MODULE_PASS("openmp-opt", OpenMPOptPass())
 MODULE_PASS("openmp-opt-postlink", OpenMPOptPass(ThinOrFullLTOPhase::FullLTOPostLink))
@@ -206,6 +207,7 @@ CGSCC_ANALYSIS("pass-instrumentation", PassInstrumentationAnalysis(PIC))
 CGSCC_PASS("argpromotion", ArgumentPromotionPass())
 CGSCC_PASS("invalidate<all>", InvalidateAllAnalysesPass())
 CGSCC_PASS("attributor-cgscc", AttributorCGSCCPass())
+CGSCC_PASS("attributor-light-cgscc", AttributorLightCGSCCPass())
 CGSCC_PASS("openmp-opt-cgscc", OpenMPOptCGSCCPass())
 CGSCC_PASS("no-op-cgscc", NoOpCGSCCPass())
 #undef CGSCC_PASS

diff  --git a/llvm/lib/Transforms/IPO/Attributor.cpp b/llvm/lib/Transforms/IPO/Attributor.cpp
index 3b6ca60f0f9970..773ae8444d4e9a 100644
--- a/llvm/lib/Transforms/IPO/Attributor.cpp
+++ b/llvm/lib/Transforms/IPO/Attributor.cpp
@@ -3275,9 +3275,10 @@ void Attributor::checkAndQueryIRAttr(const IRPosition &IRP,
                                      AttributeSet Attrs) {
   bool IsKnown;
   if (!Attrs.hasAttribute(AK))
-    if (!AA::hasAssumedIRAttr<AK>(*this, nullptr, IRP, DepClassTy::NONE,
-                                  IsKnown))
-      getOrCreateAAFor<AAType>(IRP);
+    if (!Configuration.Allowed || Configuration.Allowed->count(&AAType::ID))
+      if (!AA::hasAssumedIRAttr<AK>(*this, nullptr, IRP, DepClassTy::NONE,
+                                    IsKnown))
+        getOrCreateAAFor<AAType>(IRP);
 }
 
 void Attributor::identifyDefaultAbstractAttributes(Function &F) {
@@ -3798,6 +3799,88 @@ static bool runAttributorOnFunctions(InformationCache &InfoCache,
   return Changed == ChangeStatus::CHANGED;
 }
 
+static bool runAttributorLightOnFunctions(InformationCache &InfoCache,
+                                          SetVector<Function *> &Functions,
+                                          AnalysisGetter &AG,
+                                          CallGraphUpdater &CGUpdater,
+                                          FunctionAnalysisManager &FAM,
+                                          bool IsModulePass) {
+  if (Functions.empty())
+    return false;
+
+  LLVM_DEBUG({
+    dbgs() << "[AttributorLight] Run on module with " << Functions.size()
+           << " functions:\n";
+    for (Function *Fn : Functions)
+      dbgs() << "  - " << Fn->getName() << "\n";
+  });
+
+  // Create an Attributor and initially empty information cache that is filled
+  // while we identify default attribute opportunities.
+  AttributorConfig AC(CGUpdater);
+  AC.IsModulePass = IsModulePass;
+  AC.DeleteFns = false;
+  DenseSet<const char *> Allowed(
+      {&AAWillReturn::ID, &AANoUnwind::ID, &AANoRecurse::ID, &AANoSync::ID,
+       &AANoFree::ID, &AANoReturn::ID, &AAMemoryLocation::ID,
+       &AAMemoryBehavior::ID, &AAUnderlyingObjects::ID, &AANoCapture::ID,
+       &AAInterFnReachability::ID, &AAIntraFnReachability::ID, &AACallEdges::ID,
+       &AANoFPClass::ID, &AAMustProgress::ID, &AANonNull::ID});
+  AC.Allowed = &Allowed;
+  AC.UseLiveness = false;
+
+  Attributor A(Functions, InfoCache, AC);
+
+  for (Function *F : Functions) {
+    if (F->hasExactDefinition())
+      NumFnWithExactDefinition++;
+    else
+      NumFnWithoutExactDefinition++;
+
+    // We look at internal functions only on-demand but if any use is not a
+    // direct call or outside the current set of analyzed functions, we have
+    // to do it eagerly.
+    if (F->hasLocalLinkage()) {
+      if (llvm::all_of(F->uses(), [&Functions](const Use &U) {
+            const auto *CB = dyn_cast<CallBase>(U.getUser());
+            return CB && CB->isCallee(&U) &&
+                   Functions.count(const_cast<Function *>(CB->getCaller()));
+          }))
+        continue;
+    }
+
+    // Populate the Attributor with abstract attribute opportunities in the
+    // function and the information cache with IR information.
+    A.identifyDefaultAbstractAttributes(*F);
+  }
+
+  ChangeStatus Changed = A.run();
+
+  if (Changed == ChangeStatus::CHANGED) {
+    // Invalidate analyses for modified functions so that we don't have to
+    // invalidate all analyses for all functions in this SCC.
+    PreservedAnalyses FuncPA;
+    // We haven't changed the CFG for modified functions.
+    FuncPA.preserveSet<CFGAnalyses>();
+    for (Function *Changed : A.getModifiedFunctions()) {
+      FAM.invalidate(*Changed, FuncPA);
+      // Also invalidate any direct callers of changed functions since analyses
+      // may care about attributes of direct callees. For example, MemorySSA
+      // cares about whether or not a call's callee modifies memory and queries
+      // that through function attributes.
+      for (auto *U : Changed->users()) {
+        if (auto *Call = dyn_cast<CallBase>(U)) {
+          if (Call->getCalledFunction() == Changed)
+            FAM.invalidate(*Call->getFunction(), FuncPA);
+        }
+      }
+    }
+  }
+  LLVM_DEBUG(dbgs() << "[Attributor] Done with " << Functions.size()
+                    << " functions, result: " << Changed << ".\n");
+  return Changed == ChangeStatus::CHANGED;
+}
+
 void AADepGraph::viewGraph() { llvm::ViewGraph(this, "Dependency Graph"); }
 
 void AADepGraph::dumpGraph() {
@@ -3878,6 +3961,62 @@ PreservedAnalyses AttributorCGSCCPass::run(LazyCallGraph::SCC &C,
   return PreservedAnalyses::all();
 }
 
+PreservedAnalyses AttributorLightPass::run(Module &M,
+                                           ModuleAnalysisManager &AM) {
+  FunctionAnalysisManager &FAM =
+      AM.getResult<FunctionAnalysisManagerModuleProxy>(M).getManager();
+  AnalysisGetter AG(FAM, /* CachedOnly */ true);
+
+  SetVector<Function *> Functions;
+  for (Function &F : M)
+    Functions.insert(&F);
+
+  CallGraphUpdater CGUpdater;
+  BumpPtrAllocator Allocator;
+  InformationCache InfoCache(M, AG, Allocator, /* CGSCC */ nullptr);
+  if (runAttributorLightOnFunctions(InfoCache, Functions, AG, CGUpdater, FAM,
+                                    /* IsModulePass */ true)) {
+    PreservedAnalyses PA;
+    // We have not added or removed functions.
+    PA.preserve<FunctionAnalysisManagerCGSCCProxy>();
+    // We already invalidated all relevant function analyses above.
+    PA.preserveSet<AllAnalysesOn<Function>>();
+    return PA;
+  }
+  return PreservedAnalyses::all();
+}
+
+PreservedAnalyses AttributorLightCGSCCPass::run(LazyCallGraph::SCC &C,
+                                                CGSCCAnalysisManager &AM,
+                                                LazyCallGraph &CG,
+                                                CGSCCUpdateResult &UR) {
+  FunctionAnalysisManager &FAM =
+      AM.getResult<FunctionAnalysisManagerCGSCCProxy>(C, CG).getManager();
+  AnalysisGetter AG(FAM);
+
+  SetVector<Function *> Functions;
+  for (LazyCallGraph::Node &N : C)
+    Functions.insert(&N.getFunction());
+
+  if (Functions.empty())
+    return PreservedAnalyses::all();
+
+  Module &M = *Functions.back()->getParent();
+  CallGraphUpdater CGUpdater;
+  CGUpdater.initialize(CG, C, AM, UR);
+  BumpPtrAllocator Allocator;
+  InformationCache InfoCache(M, AG, Allocator, /* CGSCC */ &Functions);
+  if (runAttributorLightOnFunctions(InfoCache, Functions, AG, CGUpdater, FAM,
+                                    /* IsModulePass */ false)) {
+    PreservedAnalyses PA;
+    // We have not added or removed functions.
+    PA.preserve<FunctionAnalysisManagerCGSCCProxy>();
+    // We already invalidated all relevant function analyses above.
+    PA.preserveSet<AllAnalysesOn<Function>>();
+    return PA;
+  }
+  return PreservedAnalyses::all();
+}
 namespace llvm {
 
 template <> struct GraphTraits<AADepGraphNode *> {

diff  --git a/llvm/test/Transforms/Attributor/nofpclass-round.ll b/llvm/test/Transforms/Attributor/nofpclass-round.ll
index 089bd0e3ac695b..cadfeb331b3bef 100644
--- a/llvm/test/Transforms/Attributor/nofpclass-round.ll
+++ b/llvm/test/Transforms/Attributor/nofpclass-round.ll
@@ -1,5 +1,6 @@
 ; NOTE: Assertions have been autogenerated by utils/update_test_checks.py UTC_ARGS: --version 2
-; RUN: opt -aa-pipeline=basic-aa -passes=attributor -attributor-manifest-internal -S < %s | FileCheck %s --check-prefixes=CHECK,TUNIT
+; RUN: opt -aa-pipeline=basic-aa -passes=attributor -attributor-manifest-internal -S < %s | FileCheck %s --check-prefixes=CHECK
+; RUN: opt -aa-pipeline=basic-aa -passes=attributor-light -attributor-manifest-internal -S < %s | FileCheck %s --check-prefixes=LIGHT
 
 declare float @llvm.round.f32(float)
 declare ppc_fp128 @llvm.round.ppcf128(ppc_fp128)
@@ -9,6 +10,11 @@ define float @ret_round(float %arg0) {
 ; CHECK-SAME: (float [[ARG0:%.*]]) #[[ATTR1:[0-9]+]] {
 ; CHECK-NEXT:    [[CALL:%.*]] = call nofpclass(sub) float @llvm.round.f32(float [[ARG0]]) #[[ATTR2:[0-9]+]]
 ; CHECK-NEXT:    ret float [[CALL]]
+;
+; LIGHT-LABEL: define float @ret_round
+; LIGHT-SAME: (float [[ARG0:%.*]]) #[[ATTR1:[0-9]+]] {
+; LIGHT-NEXT:    [[CALL:%.*]] = call float @llvm.round.f32(float [[ARG0]]) #[[ATTR2:[0-9]+]]
+; LIGHT-NEXT:    ret float [[CALL]]
 ;
   %call = call float @llvm.round.f32(float %arg0)
   ret float %call
@@ -19,6 +25,11 @@ define float @ret_round_noinf(float nofpclass(inf) %arg0) {
 ; CHECK-SAME: (float nofpclass(inf) [[ARG0:%.*]]) #[[ATTR1]] {
 ; CHECK-NEXT:    [[CALL:%.*]] = call nofpclass(inf sub) float @llvm.round.f32(float [[ARG0]]) #[[ATTR2]]
 ; CHECK-NEXT:    ret float [[CALL]]
+;
+; LIGHT-LABEL: define float @ret_round_noinf
+; LIGHT-SAME: (float nofpclass(inf) [[ARG0:%.*]]) #[[ATTR1]] {
+; LIGHT-NEXT:    [[CALL:%.*]] = call float @llvm.round.f32(float [[ARG0]]) #[[ATTR2]]
+; LIGHT-NEXT:    ret float [[CALL]]
 ;
   %call = call float @llvm.round.f32(float %arg0)
   ret float %call
@@ -29,6 +40,11 @@ define float @ret_round_nopinf(float nofpclass(pinf) %arg0) {
 ; CHECK-SAME: (float nofpclass(pinf) [[ARG0:%.*]]) #[[ATTR1]] {
 ; CHECK-NEXT:    [[CALL:%.*]] = call nofpclass(pinf sub) float @llvm.round.f32(float [[ARG0]]) #[[ATTR2]]
 ; CHECK-NEXT:    ret float [[CALL]]
+;
+; LIGHT-LABEL: define float @ret_round_nopinf
+; LIGHT-SAME: (float nofpclass(pinf) [[ARG0:%.*]]) #[[ATTR1]] {
+; LIGHT-NEXT:    [[CALL:%.*]] = call float @llvm.round.f32(float [[ARG0]]) #[[ATTR2]]
+; LIGHT-NEXT:    ret float [[CALL]]
 ;
   %call = call float @llvm.round.f32(float %arg0)
   ret float %call
@@ -39,6 +55,11 @@ define float @ret_round_noninf(float nofpclass(ninf) %arg0) {
 ; CHECK-SAME: (float nofpclass(ninf) [[ARG0:%.*]]) #[[ATTR1]] {
 ; CHECK-NEXT:    [[CALL:%.*]] = call nofpclass(ninf sub) float @llvm.round.f32(float [[ARG0]]) #[[ATTR2]]
 ; CHECK-NEXT:    ret float [[CALL]]
+;
+; LIGHT-LABEL: define float @ret_round_noninf
+; LIGHT-SAME: (float nofpclass(ninf) [[ARG0:%.*]]) #[[ATTR1]] {
+; LIGHT-NEXT:    [[CALL:%.*]] = call float @llvm.round.f32(float [[ARG0]]) #[[ATTR2]]
+; LIGHT-NEXT:    ret float [[CALL]]
 ;
   %call = call float @llvm.round.f32(float %arg0)
   ret float %call
@@ -49,6 +70,11 @@ define float @ret_round_nonan(float nofpclass(nan) %arg0) {
 ; CHECK-SAME: (float nofpclass(nan) [[ARG0:%.*]]) #[[ATTR1]] {
 ; CHECK-NEXT:    [[CALL:%.*]] = call nofpclass(nan sub) float @llvm.round.f32(float [[ARG0]]) #[[ATTR2]]
 ; CHECK-NEXT:    ret float [[CALL]]
+;
+; LIGHT-LABEL: define float @ret_round_nonan
+; LIGHT-SAME: (float nofpclass(nan) [[ARG0:%.*]]) #[[ATTR1]] {
+; LIGHT-NEXT:    [[CALL:%.*]] = call float @llvm.round.f32(float [[ARG0]]) #[[ATTR2]]
+; LIGHT-NEXT:    ret float [[CALL]]
 ;
   %call = call float @llvm.round.f32(float %arg0)
   ret float %call
@@ -59,6 +85,11 @@ define float @ret_round_noqnan(float nofpclass(qnan) %arg0) {
 ; CHECK-SAME: (float nofpclass(qnan) [[ARG0:%.*]]) #[[ATTR1]] {
 ; CHECK-NEXT:    [[CALL:%.*]] = call nofpclass(sub) float @llvm.round.f32(float [[ARG0]]) #[[ATTR2]]
 ; CHECK-NEXT:    ret float [[CALL]]
+;
+; LIGHT-LABEL: define float @ret_round_noqnan
+; LIGHT-SAME: (float nofpclass(qnan) [[ARG0:%.*]]) #[[ATTR1]] {
+; LIGHT-NEXT:    [[CALL:%.*]] = call float @llvm.round.f32(float [[ARG0]]) #[[ATTR2]]
+; LIGHT-NEXT:    ret float [[CALL]]
 ;
   %call = call float @llvm.round.f32(float %arg0)
   ret float %call
@@ -69,6 +100,11 @@ define float @ret_round_nosnan(float nofpclass(snan) %arg0) {
 ; CHECK-SAME: (float nofpclass(snan) [[ARG0:%.*]]) #[[ATTR1]] {
 ; CHECK-NEXT:    [[CALL:%.*]] = call nofpclass(snan sub) float @llvm.round.f32(float [[ARG0]]) #[[ATTR2]]
 ; CHECK-NEXT:    ret float [[CALL]]
+;
+; LIGHT-LABEL: define float @ret_round_nosnan
+; LIGHT-SAME: (float nofpclass(snan) [[ARG0:%.*]]) #[[ATTR1]] {
+; LIGHT-NEXT:    [[CALL:%.*]] = call float @llvm.round.f32(float [[ARG0]]) #[[ATTR2]]
+; LIGHT-NEXT:    ret float [[CALL]]
 ;
   %call = call float @llvm.round.f32(float %arg0)
   ret float %call
@@ -79,6 +115,11 @@ define float @ret_round_nozero(float nofpclass(zero) %arg0) {
 ; CHECK-SAME: (float nofpclass(zero) [[ARG0:%.*]]) #[[ATTR1]] {
 ; CHECK-NEXT:    [[CALL:%.*]] = call nofpclass(sub) float @llvm.round.f32(float [[ARG0]]) #[[ATTR2]]
 ; CHECK-NEXT:    ret float [[CALL]]
+;
+; LIGHT-LABEL: define float @ret_round_nozero
+; LIGHT-SAME: (float nofpclass(zero) [[ARG0:%.*]]) #[[ATTR1]] {
+; LIGHT-NEXT:    [[CALL:%.*]] = call float @llvm.round.f32(float [[ARG0]]) #[[ATTR2]]
+; LIGHT-NEXT:    ret float [[CALL]]
 ;
   %call = call float @llvm.round.f32(float %arg0)
   ret float %call
@@ -89,6 +130,11 @@ define float @ret_round_nopzero(float nofpclass(pzero) %arg0) {
 ; CHECK-SAME: (float nofpclass(pzero) [[ARG0:%.*]]) #[[ATTR1]] {
 ; CHECK-NEXT:    [[CALL:%.*]] = call nofpclass(sub) float @llvm.round.f32(float [[ARG0]]) #[[ATTR2]]
 ; CHECK-NEXT:    ret float [[CALL]]
+;
+; LIGHT-LABEL: define float @ret_round_nopzero
+; LIGHT-SAME: (float nofpclass(pzero) [[ARG0:%.*]]) #[[ATTR1]] {
+; LIGHT-NEXT:    [[CALL:%.*]] = call float @llvm.round.f32(float [[ARG0]]) #[[ATTR2]]
+; LIGHT-NEXT:    ret float [[CALL]]
 ;
   %call = call float @llvm.round.f32(float %arg0)
   ret float %call
@@ -99,6 +145,11 @@ define float @ret_round_nonzero(float nofpclass(nzero) %arg0) {
 ; CHECK-SAME: (float nofpclass(nzero) [[ARG0:%.*]]) #[[ATTR1]] {
 ; CHECK-NEXT:    [[CALL:%.*]] = call nofpclass(sub) float @llvm.round.f32(float [[ARG0]]) #[[ATTR2]]
 ; CHECK-NEXT:    ret float [[CALL]]
+;
+; LIGHT-LABEL: define float @ret_round_nonzero
+; LIGHT-SAME: (float nofpclass(nzero) [[ARG0:%.*]]) #[[ATTR1]] {
+; LIGHT-NEXT:    [[CALL:%.*]] = call float @llvm.round.f32(float [[ARG0]]) #[[ATTR2]]
+; LIGHT-NEXT:    ret float [[CALL]]
 ;
   %call = call float @llvm.round.f32(float %arg0)
   ret float %call
@@ -109,6 +160,11 @@ define float @ret_round_nonorm(float nofpclass(norm) %arg0) {
 ; CHECK-SAME: (float nofpclass(norm) [[ARG0:%.*]]) #[[ATTR1]] {
 ; CHECK-NEXT:    [[CALL:%.*]] = call nofpclass(sub) float @llvm.round.f32(float [[ARG0]]) #[[ATTR2]]
 ; CHECK-NEXT:    ret float [[CALL]]
+;
+; LIGHT-LABEL: define float @ret_round_nonorm
+; LIGHT-SAME: (float nofpclass(norm) [[ARG0:%.*]]) #[[ATTR1]] {
+; LIGHT-NEXT:    [[CALL:%.*]] = call float @llvm.round.f32(float [[ARG0]]) #[[ATTR2]]
+; LIGHT-NEXT:    ret float [[CALL]]
 ;
   %call = call float @llvm.round.f32(float %arg0)
   ret float %call
@@ -119,6 +175,11 @@ define float @ret_round_nonnorm(float nofpclass(nnorm) %arg0) {
 ; CHECK-SAME: (float nofpclass(nnorm) [[ARG0:%.*]]) #[[ATTR1]] {
 ; CHECK-NEXT:    [[CALL:%.*]] = call nofpclass(sub) float @llvm.round.f32(float [[ARG0]]) #[[ATTR2]]
 ; CHECK-NEXT:    ret float [[CALL]]
+;
+; LIGHT-LABEL: define float @ret_round_nonnorm
+; LIGHT-SAME: (float nofpclass(nnorm) [[ARG0:%.*]]) #[[ATTR1]] {
+; LIGHT-NEXT:    [[CALL:%.*]] = call float @llvm.round.f32(float [[ARG0]]) #[[ATTR2]]
+; LIGHT-NEXT:    ret float [[CALL]]
 ;
   %call = call float @llvm.round.f32(float %arg0)
   ret float %call
@@ -129,6 +190,11 @@ define float @ret_round_nopnorm(float nofpclass(pnorm) %arg0) {
 ; CHECK-SAME: (float nofpclass(pnorm) [[ARG0:%.*]]) #[[ATTR1]] {
 ; CHECK-NEXT:    [[CALL:%.*]] = call nofpclass(sub) float @llvm.round.f32(float [[ARG0]]) #[[ATTR2]]
 ; CHECK-NEXT:    ret float [[CALL]]
+;
+; LIGHT-LABEL: define float @ret_round_nopnorm
+; LIGHT-SAME: (float nofpclass(pnorm) [[ARG0:%.*]]) #[[ATTR1]] {
+; LIGHT-NEXT:    [[CALL:%.*]] = call float @llvm.round.f32(float [[ARG0]]) #[[ATTR2]]
+; LIGHT-NEXT:    ret float [[CALL]]
 ;
   %call = call float @llvm.round.f32(float %arg0)
   ret float %call
@@ -139,6 +205,11 @@ define float @ret_round_nonsub(float nofpclass(nsub) %arg0) {
 ; CHECK-SAME: (float nofpclass(nsub) [[ARG0:%.*]]) #[[ATTR1]] {
 ; CHECK-NEXT:    [[CALL:%.*]] = call nofpclass(sub) float @llvm.round.f32(float [[ARG0]]) #[[ATTR2]]
 ; CHECK-NEXT:    ret float [[CALL]]
+;
+; LIGHT-LABEL: define float @ret_round_nonsub
+; LIGHT-SAME: (float nofpclass(nsub) [[ARG0:%.*]]) #[[ATTR1]] {
+; LIGHT-NEXT:    [[CALL:%.*]] = call float @llvm.round.f32(float [[ARG0]]) #[[ATTR2]]
+; LIGHT-NEXT:    ret float [[CALL]]
 ;
   %call = call float @llvm.round.f32(float %arg0)
   ret float %call
@@ -149,6 +220,11 @@ define float @ret_round_nopsub(float nofpclass(psub) %arg0) {
 ; CHECK-SAME: (float nofpclass(psub) [[ARG0:%.*]]) #[[ATTR1]] {
 ; CHECK-NEXT:    [[CALL:%.*]] = call nofpclass(sub) float @llvm.round.f32(float [[ARG0]]) #[[ATTR2]]
 ; CHECK-NEXT:    ret float [[CALL]]
+;
+; LIGHT-LABEL: define float @ret_round_nopsub
+; LIGHT-SAME: (float nofpclass(psub) [[ARG0:%.*]]) #[[ATTR1]] {
+; LIGHT-NEXT:    [[CALL:%.*]] = call float @llvm.round.f32(float [[ARG0]]) #[[ATTR2]]
+; LIGHT-NEXT:    ret float [[CALL]]
 ;
   %call = call float @llvm.round.f32(float %arg0)
   ret float %call
@@ -159,6 +235,11 @@ define float @ret_round_nonorm_nosub(float nofpclass(norm sub) %arg0) {
 ; CHECK-SAME: (float nofpclass(sub norm) [[ARG0:%.*]]) #[[ATTR1]] {
 ; CHECK-NEXT:    [[CALL:%.*]] = call nofpclass(sub) float @llvm.round.f32(float [[ARG0]]) #[[ATTR2]]
 ; CHECK-NEXT:    ret float [[CALL]]
+;
+; LIGHT-LABEL: define float @ret_round_nonorm_nosub
+; LIGHT-SAME: (float nofpclass(sub norm) [[ARG0:%.*]]) #[[ATTR1]] {
+; LIGHT-NEXT:    [[CALL:%.*]] = call float @llvm.round.f32(float [[ARG0]]) #[[ATTR2]]
+; LIGHT-NEXT:    ret float [[CALL]]
 ;
   %call = call float @llvm.round.f32(float %arg0)
   ret float %call
@@ -169,6 +250,11 @@ define float @ret_round_nopnorm_nopsub(float nofpclass(pnorm psub) %arg0) {
 ; CHECK-SAME: (float nofpclass(psub pnorm) [[ARG0:%.*]]) #[[ATTR1]] {
 ; CHECK-NEXT:    [[CALL:%.*]] = call nofpclass(sub) float @llvm.round.f32(float [[ARG0]]) #[[ATTR2]]
 ; CHECK-NEXT:    ret float [[CALL]]
+;
+; LIGHT-LABEL: define float @ret_round_nopnorm_nopsub
+; LIGHT-SAME: (float nofpclass(psub pnorm) [[ARG0:%.*]]) #[[ATTR1]] {
+; LIGHT-NEXT:    [[CALL:%.*]] = call float @llvm.round.f32(float [[ARG0]]) #[[ATTR2]]
+; LIGHT-NEXT:    ret float [[CALL]]
 ;
   %call = call float @llvm.round.f32(float %arg0)
   ret float %call
@@ -179,6 +265,11 @@ define float @ret_round_nonnorm_nonsub(float nofpclass(nnorm nsub) %arg0) {
 ; CHECK-SAME: (float nofpclass(nsub nnorm) [[ARG0:%.*]]) #[[ATTR1]] {
 ; CHECK-NEXT:    [[CALL:%.*]] = call nofpclass(sub) float @llvm.round.f32(float [[ARG0]]) #[[ATTR2]]
 ; CHECK-NEXT:    ret float [[CALL]]
+;
+; LIGHT-LABEL: define float @ret_round_nonnorm_nonsub
+; LIGHT-SAME: (float nofpclass(nsub nnorm) [[ARG0:%.*]]) #[[ATTR1]] {
+; LIGHT-NEXT:    [[CALL:%.*]] = call float @llvm.round.f32(float [[ARG0]]) #[[ATTR2]]
+; LIGHT-NEXT:    ret float [[CALL]]
 ;
   %call = call float @llvm.round.f32(float %arg0)
   ret float %call
@@ -189,6 +280,11 @@ define float @ret_round_nopnorm_nonsub(float nofpclass(pnorm nsub) %arg0) {
 ; CHECK-SAME: (float nofpclass(nsub pnorm) [[ARG0:%.*]]) #[[ATTR1]] {
 ; CHECK-NEXT:    [[CALL:%.*]] = call nofpclass(sub) float @llvm.round.f32(float [[ARG0]]) #[[ATTR2]]
 ; CHECK-NEXT:    ret float [[CALL]]
+;
+; LIGHT-LABEL: define float @ret_round_nopnorm_nonsub
+; LIGHT-SAME: (float nofpclass(nsub pnorm) [[ARG0:%.*]]) #[[ATTR1]] {
+; LIGHT-NEXT:    [[CALL:%.*]] = call float @llvm.round.f32(float [[ARG0]]) #[[ATTR2]]
+; LIGHT-NEXT:    ret float [[CALL]]
 ;
   %call = call float @llvm.round.f32(float %arg0)
   ret float %call
@@ -199,6 +295,11 @@ define ppc_fp128 @ret_round_ppcf128(ppc_fp128 %arg0) {
 ; CHECK-SAME: (ppc_fp128 [[ARG0:%.*]]) #[[ATTR1]] {
 ; CHECK-NEXT:    [[CALL:%.*]] = call nofpclass(sub) ppc_fp128 @llvm.round.ppcf128(ppc_fp128 [[ARG0]]) #[[ATTR2]]
 ; CHECK-NEXT:    ret ppc_fp128 [[CALL]]
+;
+; LIGHT-LABEL: define ppc_fp128 @ret_round_ppcf128
+; LIGHT-SAME: (ppc_fp128 [[ARG0:%.*]]) #[[ATTR1]] {
+; LIGHT-NEXT:    [[CALL:%.*]] = call ppc_fp128 @llvm.round.ppcf128(ppc_fp128 [[ARG0]]) #[[ATTR2]]
+; LIGHT-NEXT:    ret ppc_fp128 [[CALL]]
 ;
   %call = call ppc_fp128 @llvm.round.ppcf128(ppc_fp128 %arg0)
   ret ppc_fp128 %call
@@ -209,6 +310,11 @@ define ppc_fp128 @ret_round_noinf_ppcf128(ppc_fp128 nofpclass(inf) %arg0) {
 ; CHECK-SAME: (ppc_fp128 nofpclass(inf) [[ARG0:%.*]]) #[[ATTR1]] {
 ; CHECK-NEXT:    [[CALL:%.*]] = call nofpclass(sub) ppc_fp128 @llvm.round.ppcf128(ppc_fp128 [[ARG0]]) #[[ATTR2]]
 ; CHECK-NEXT:    ret ppc_fp128 [[CALL]]
+;
+; LIGHT-LABEL: define ppc_fp128 @ret_round_noinf_ppcf128
+; LIGHT-SAME: (ppc_fp128 nofpclass(inf) [[ARG0:%.*]]) #[[ATTR1]] {
+; LIGHT-NEXT:    [[CALL:%.*]] = call ppc_fp128 @llvm.round.ppcf128(ppc_fp128 [[ARG0]]) #[[ATTR2]]
+; LIGHT-NEXT:    ret ppc_fp128 [[CALL]]
 ;
   %call = call ppc_fp128 @llvm.round.ppcf128(ppc_fp128 %arg0)
   ret ppc_fp128 %call
@@ -219,6 +325,11 @@ define ppc_fp128 @ret_round_nopinf_ppcf128(ppc_fp128 nofpclass(pinf) %arg0) {
 ; CHECK-SAME: (ppc_fp128 nofpclass(pinf) [[ARG0:%.*]]) #[[ATTR1]] {
 ; CHECK-NEXT:    [[CALL:%.*]] = call nofpclass(sub) ppc_fp128 @llvm.round.ppcf128(ppc_fp128 [[ARG0]]) #[[ATTR2]]
 ; CHECK-NEXT:    ret ppc_fp128 [[CALL]]
+;
+; LIGHT-LABEL: define ppc_fp128 @ret_round_nopinf_ppcf128
+; LIGHT-SAME: (ppc_fp128 nofpclass(pinf) [[ARG0:%.*]]) #[[ATTR1]] {
+; LIGHT-NEXT:    [[CALL:%.*]] = call ppc_fp128 @llvm.round.ppcf128(ppc_fp128 [[ARG0]]) #[[ATTR2]]
+; LIGHT-NEXT:    ret ppc_fp128 [[CALL]]
 ;
   %call = call ppc_fp128 @llvm.round.ppcf128(ppc_fp128 %arg0)
   ret ppc_fp128 %call
@@ -229,6 +340,11 @@ define ppc_fp128 @ret_round_noninf_ppcf128(ppc_fp128 nofpclass(ninf) %arg0) {
 ; CHECK-SAME: (ppc_fp128 nofpclass(ninf) [[ARG0:%.*]]) #[[ATTR1]] {
 ; CHECK-NEXT:    [[CALL:%.*]] = call nofpclass(sub) ppc_fp128 @llvm.round.ppcf128(ppc_fp128 [[ARG0]]) #[[ATTR2]]
 ; CHECK-NEXT:    ret ppc_fp128 [[CALL]]
+;
+; LIGHT-LABEL: define ppc_fp128 @ret_round_noninf_ppcf128
+; LIGHT-SAME: (ppc_fp128 nofpclass(ninf) [[ARG0:%.*]]) #[[ATTR1]] {
+; LIGHT-NEXT:    [[CALL:%.*]] = call ppc_fp128 @llvm.round.ppcf128(ppc_fp128 [[ARG0]]) #[[ATTR2]]
+; LIGHT-NEXT:    ret ppc_fp128 [[CALL]]
 ;
   %call = call ppc_fp128 @llvm.round.ppcf128(ppc_fp128 %arg0)
   ret ppc_fp128 %call
@@ -239,6 +355,11 @@ define ppc_fp128 @ret_round_nonan_ppcf128(ppc_fp128 nofpclass(nan) %arg0) {
 ; CHECK-SAME: (ppc_fp128 nofpclass(nan) [[ARG0:%.*]]) #[[ATTR1]] {
 ; CHECK-NEXT:    [[CALL:%.*]] = call nofpclass(nan sub) ppc_fp128 @llvm.round.ppcf128(ppc_fp128 [[ARG0]]) #[[ATTR2]]
 ; CHECK-NEXT:    ret ppc_fp128 [[CALL]]
+;
+; LIGHT-LABEL: define ppc_fp128 @ret_round_nonan_ppcf128
+; LIGHT-SAME: (ppc_fp128 nofpclass(nan) [[ARG0:%.*]]) #[[ATTR1]] {
+; LIGHT-NEXT:    [[CALL:%.*]] = call ppc_fp128 @llvm.round.ppcf128(ppc_fp128 [[ARG0]]) #[[ATTR2]]
+; LIGHT-NEXT:    ret ppc_fp128 [[CALL]]
 ;
   %call = call ppc_fp128 @llvm.round.ppcf128(ppc_fp128 %arg0)
   ret ppc_fp128 %call
@@ -249,6 +370,11 @@ define float @ret_round_noneg(float nofpclass(ninf nsub nnorm) %arg0) {
 ; CHECK-SAME: (float nofpclass(ninf nsub nnorm) [[ARG0:%.*]]) #[[ATTR1]] {
 ; CHECK-NEXT:    [[CALL:%.*]] = call nofpclass(ninf sub) float @llvm.round.f32(float [[ARG0]]) #[[ATTR2]]
 ; CHECK-NEXT:    ret float [[CALL]]
+;
+; LIGHT-LABEL: define float @ret_round_noneg
+; LIGHT-SAME: (float nofpclass(ninf nsub nnorm) [[ARG0:%.*]]) #[[ATTR1]] {
+; LIGHT-NEXT:    [[CALL:%.*]] = call float @llvm.round.f32(float [[ARG0]]) #[[ATTR2]]
+; LIGHT-NEXT:    ret float [[CALL]]
 ;
   %call = call float @llvm.round.f32(float %arg0)
   ret float %call
@@ -259,6 +385,11 @@ define float @ret_round_noneg_nonegzero(float nofpclass(ninf nsub nnorm nzero) %
 ; CHECK-SAME: (float nofpclass(ninf nzero nsub nnorm) [[ARG0:%.*]]) #[[ATTR1]] {
 ; CHECK-NEXT:    [[CALL:%.*]] = call nofpclass(ninf nzero sub nnorm) float @llvm.round.f32(float [[ARG0]]) #[[ATTR2]]
 ; CHECK-NEXT:    ret float [[CALL]]
+;
+; LIGHT-LABEL: define float @ret_round_noneg_nonegzero
+; LIGHT-SAME: (float nofpclass(ninf nzero nsub nnorm) [[ARG0:%.*]]) #[[ATTR1]] {
+; LIGHT-NEXT:    [[CALL:%.*]] = call float @llvm.round.f32(float [[ARG0]]) #[[ATTR2]]
+; LIGHT-NEXT:    ret float [[CALL]]
 ;
   %call = call float @llvm.round.f32(float %arg0)
   ret float %call
@@ -269,6 +400,11 @@ define float @ret_round_noneg_nonegzero_nonan(float nofpclass(ninf nsub nnorm nz
 ; CHECK-SAME: (float nofpclass(nan ninf nzero nsub nnorm) [[ARG0:%.*]]) #[[ATTR1]] {
 ; CHECK-NEXT:    [[CALL:%.*]] = call nofpclass(nan ninf nzero sub nnorm) float @llvm.round.f32(float [[ARG0]]) #[[ATTR2]]
 ; CHECK-NEXT:    ret float [[CALL]]
+;
+; LIGHT-LABEL: define float @ret_round_noneg_nonegzero_nonan
+; LIGHT-SAME: (float nofpclass(nan ninf nzero nsub nnorm) [[ARG0:%.*]]) #[[ATTR1]] {
+; LIGHT-NEXT:    [[CALL:%.*]] = call float @llvm.round.f32(float [[ARG0]]) #[[ATTR2]]
+; LIGHT-NEXT:    ret float [[CALL]]
 ;
   %call = call float @llvm.round.f32(float %arg0)
   ret float %call
@@ -279,6 +415,11 @@ define float @ret_round_noneg_nozero(float nofpclass(ninf nsub nnorm zero) %arg0
 ; CHECK-SAME: (float nofpclass(ninf zero nsub nnorm) [[ARG0:%.*]]) #[[ATTR1]] {
 ; CHECK-NEXT:    [[CALL:%.*]] = call nofpclass(ninf nzero sub nnorm) float @llvm.round.f32(float [[ARG0]]) #[[ATTR2]]
 ; CHECK-NEXT:    ret float [[CALL]]
+;
+; LIGHT-LABEL: define float @ret_round_noneg_nozero
+; LIGHT-SAME: (float nofpclass(ninf zero nsub nnorm) [[ARG0:%.*]]) #[[ATTR1]] {
+; LIGHT-NEXT:    [[CALL:%.*]] = call float @llvm.round.f32(float [[ARG0]]) #[[ATTR2]]
+; LIGHT-NEXT:    ret float [[CALL]]
 ;
   %call = call float @llvm.round.f32(float %arg0)
   ret float %call
@@ -289,6 +430,11 @@ define float @ret_round_noneg_nozero_nonan(float nofpclass(ninf nsub nnorm zero
 ; CHECK-SAME: (float nofpclass(nan ninf zero nsub nnorm) [[ARG0:%.*]]) #[[ATTR1]] {
 ; CHECK-NEXT:    [[CALL:%.*]] = call nofpclass(nan ninf nzero sub nnorm) float @llvm.round.f32(float [[ARG0]]) #[[ATTR2]]
 ; CHECK-NEXT:    ret float [[CALL]]
+;
+; LIGHT-LABEL: define float @ret_round_noneg_nozero_nonan
+; LIGHT-SAME: (float nofpclass(nan ninf zero nsub nnorm) [[ARG0:%.*]]) #[[ATTR1]] {
+; LIGHT-NEXT:    [[CALL:%.*]] = call float @llvm.round.f32(float [[ARG0]]) #[[ATTR2]]
+; LIGHT-NEXT:    ret float [[CALL]]
 ;
   %call = call float @llvm.round.f32(float %arg0)
   ret float %call
@@ -299,6 +445,11 @@ define float @ret_round_nopos(float nofpclass(pinf psub pnorm) %arg0) {
 ; CHECK-SAME: (float nofpclass(pinf psub pnorm) [[ARG0:%.*]]) #[[ATTR1]] {
 ; CHECK-NEXT:    [[CALL:%.*]] = call nofpclass(pinf sub) float @llvm.round.f32(float [[ARG0]]) #[[ATTR2]]
 ; CHECK-NEXT:    ret float [[CALL]]
+;
+; LIGHT-LABEL: define float @ret_round_nopos
+; LIGHT-SAME: (float nofpclass(pinf psub pnorm) [[ARG0:%.*]]) #[[ATTR1]] {
+; LIGHT-NEXT:    [[CALL:%.*]] = call float @llvm.round.f32(float [[ARG0]]) #[[ATTR2]]
+; LIGHT-NEXT:    ret float [[CALL]]
 ;
   %call = call float @llvm.round.f32(float %arg0)
   ret float %call
@@ -309,6 +460,11 @@ define float @ret_round_nopos_nopzero(float nofpclass(pinf psub pnorm pzero) %ar
 ; CHECK-SAME: (float nofpclass(pinf pzero psub pnorm) [[ARG0:%.*]]) #[[ATTR1]] {
 ; CHECK-NEXT:    [[CALL:%.*]] = call nofpclass(pinf pzero sub pnorm) float @llvm.round.f32(float [[ARG0]]) #[[ATTR2]]
 ; CHECK-NEXT:    ret float [[CALL]]
+;
+; LIGHT-LABEL: define float @ret_round_nopos_nopzero
+; LIGHT-SAME: (float nofpclass(pinf pzero psub pnorm) [[ARG0:%.*]]) #[[ATTR1]] {
+; LIGHT-NEXT:    [[CALL:%.*]] = call float @llvm.round.f32(float [[ARG0]]) #[[ATTR2]]
+; LIGHT-NEXT:    ret float [[CALL]]
 ;
   %call = call float @llvm.round.f32(float %arg0)
   ret float %call
@@ -319,6 +475,11 @@ define float @ret_round_nopos_nopzero_nonan(float nofpclass(pinf psub pnorm pzer
 ; CHECK-SAME: (float nofpclass(nan pinf pzero psub pnorm) [[ARG0:%.*]]) #[[ATTR1]] {
 ; CHECK-NEXT:    [[CALL:%.*]] = call nofpclass(nan pinf pzero sub pnorm) float @llvm.round.f32(float [[ARG0]]) #[[ATTR2]]
 ; CHECK-NEXT:    ret float [[CALL]]
+;
+; LIGHT-LABEL: define float @ret_round_nopos_nopzero_nonan
+; LIGHT-SAME: (float nofpclass(nan pinf pzero psub pnorm) [[ARG0:%.*]]) #[[ATTR1]] {
+; LIGHT-NEXT:    [[CALL:%.*]] = call float @llvm.round.f32(float [[ARG0]]) #[[ATTR2]]
+; LIGHT-NEXT:    ret float [[CALL]]
 ;
   %call = call float @llvm.round.f32(float %arg0)
   ret float %call
@@ -329,6 +490,11 @@ define float @ret_round_nopos_nozero(float nofpclass(pinf psub pnorm zero) %arg0
 ; CHECK-SAME: (float nofpclass(pinf zero psub pnorm) [[ARG0:%.*]]) #[[ATTR1]] {
 ; CHECK-NEXT:    [[CALL:%.*]] = call nofpclass(pinf pzero sub pnorm) float @llvm.round.f32(float [[ARG0]]) #[[ATTR2]]
 ; CHECK-NEXT:    ret float [[CALL]]
+;
+; LIGHT-LABEL: define float @ret_round_nopos_nozero
+; LIGHT-SAME: (float nofpclass(pinf zero psub pnorm) [[ARG0:%.*]]) #[[ATTR1]] {
+; LIGHT-NEXT:    [[CALL:%.*]] = call float @llvm.round.f32(float [[ARG0]]) #[[ATTR2]]
+; LIGHT-NEXT:    ret float [[CALL]]
 ;
   %call = call float @llvm.round.f32(float %arg0)
   ret float %call
@@ -339,6 +505,11 @@ define float @ret_round_nopos_nozero_nonan(float nofpclass(pinf psub pnorm zero
 ; CHECK-SAME: (float nofpclass(nan pinf zero psub pnorm) [[ARG0:%.*]]) #[[ATTR1]] {
 ; CHECK-NEXT:    [[CALL:%.*]] = call nofpclass(nan pinf pzero sub pnorm) float @llvm.round.f32(float [[ARG0]]) #[[ATTR2]]
 ; CHECK-NEXT:    ret float [[CALL]]
+;
+; LIGHT-LABEL: define float @ret_round_nopos_nozero_nonan
+; LIGHT-SAME: (float nofpclass(nan pinf zero psub pnorm) [[ARG0:%.*]]) #[[ATTR1]] {
+; LIGHT-NEXT:    [[CALL:%.*]] = call float @llvm.round.f32(float [[ARG0]]) #[[ATTR2]]
+; LIGHT-NEXT:    ret float [[CALL]]
 ;
   %call = call float @llvm.round.f32(float %arg0)
   ret float %call
@@ -349,6 +520,11 @@ define float @ret_round_nopzero_nopnorm(float nofpclass(pzero pnorm) %arg0) {
 ; CHECK-SAME: (float nofpclass(pzero pnorm) [[ARG0:%.*]]) #[[ATTR1]] {
 ; CHECK-NEXT:    [[CALL:%.*]] = call nofpclass(sub) float @llvm.round.f32(float [[ARG0]]) #[[ATTR2]]
 ; CHECK-NEXT:    ret float [[CALL]]
+;
+; LIGHT-LABEL: define float @ret_round_nopzero_nopnorm
+; LIGHT-SAME: (float nofpclass(pzero pnorm) [[ARG0:%.*]]) #[[ATTR1]] {
+; LIGHT-NEXT:    [[CALL:%.*]] = call float @llvm.round.f32(float [[ARG0]]) #[[ATTR2]]
+; LIGHT-NEXT:    ret float [[CALL]]
 ;
   %call = call float @llvm.round.f32(float %arg0)
   ret float %call
@@ -359,6 +535,11 @@ define float @ret_round_nonzero_nonnorm(float nofpclass(nzero nnorm) %arg0) {
 ; CHECK-SAME: (float nofpclass(nzero nnorm) [[ARG0:%.*]]) #[[ATTR1]] {
 ; CHECK-NEXT:    [[CALL:%.*]] = call nofpclass(sub) float @llvm.round.f32(float [[ARG0]]) #[[ATTR2]]
 ; CHECK-NEXT:    ret float [[CALL]]
+;
+; LIGHT-LABEL: define float @ret_round_nonzero_nonnorm
+; LIGHT-SAME: (float nofpclass(nzero nnorm) [[ARG0:%.*]]) #[[ATTR1]] {
+; LIGHT-NEXT:    [[CALL:%.*]] = call float @llvm.round.f32(float [[ARG0]]) #[[ATTR2]]
+; LIGHT-NEXT:    ret float [[CALL]]
 ;
   %call = call float @llvm.round.f32(float %arg0)
   ret float %call
@@ -369,10 +550,12 @@ define float @ret_round_nozero_nonorm(float nofpclass(zero norm) %arg0) {
 ; CHECK-SAME: (float nofpclass(zero norm) [[ARG0:%.*]]) #[[ATTR1]] {
 ; CHECK-NEXT:    [[CALL:%.*]] = call nofpclass(sub) float @llvm.round.f32(float [[ARG0]]) #[[ATTR2]]
 ; CHECK-NEXT:    ret float [[CALL]]
+;
+; LIGHT-LABEL: define float @ret_round_nozero_nonorm
+; LIGHT-SAME: (float nofpclass(zero norm) [[ARG0:%.*]]) #[[ATTR1]] {
+; LIGHT-NEXT:    [[CALL:%.*]] = call float @llvm.round.f32(float [[ARG0]]) #[[ATTR2]]
+; LIGHT-NEXT:    ret float [[CALL]]
 ;
   %call = call float @llvm.round.f32(float %arg0)
   ret float %call
 }
-
-;; NOTE: These prefixes are unused and the list is autogenerated. Do not add tests below this line:
-; TUNIT: {{.*}}

diff  --git a/llvm/test/Transforms/FunctionAttrs/argmemonly.ll b/llvm/test/Transforms/FunctionAttrs/argmemonly.ll
index 8c2073ef4ac7ae..c8a2a5fcfb7bc5 100644
--- a/llvm/test/Transforms/FunctionAttrs/argmemonly.ll
+++ b/llvm/test/Transforms/FunctionAttrs/argmemonly.ll
@@ -1,26 +1,34 @@
 ; NOTE: Assertions have been autogenerated by utils/update_test_checks.py UTC_ARGS: --check-attributes --version 2
-; RUN: opt -passes=function-attrs -S %s | FileCheck %s
+; RUN: opt -passes=function-attrs -S < %s | FileCheck --check-prefixes=COMMON,FNATTRS %s
+; RUN: opt -passes=attributor-light -S < %s | FileCheck --check-prefixes=COMMON,ATTRIBUTOR %s
 
 @g = global i32 20
 
 define void @test_no_read_or_write() {
-; CHECK: Function Attrs: mustprogress nofree norecurse nosync nounwind willreturn memory(none)
-; CHECK-LABEL: define void @test_no_read_or_write
-; CHECK-SAME: () #[[ATTR0:[0-9]+]] {
-; CHECK-NEXT:  entry:
-; CHECK-NEXT:    ret void
+; COMMON: Function Attrs: mustprogress nofree norecurse nosync nounwind willreturn memory(none)
+; COMMON-LABEL: define void @test_no_read_or_write
+; COMMON-SAME: () #[[ATTR0:[0-9]+]] {
+; COMMON-NEXT:  entry:
+; COMMON-NEXT:    ret void
 ;
 entry:
   ret void
 }
 
 define i32 @test_only_read_arg(ptr %ptr) {
-; CHECK: Function Attrs: mustprogress nofree norecurse nosync nounwind willreturn memory(argmem: read)
-; CHECK-LABEL: define i32 @test_only_read_arg
-; CHECK-SAME: (ptr nocapture readonly [[PTR:%.*]]) #[[ATTR1:[0-9]+]] {
-; CHECK-NEXT:  entry:
-; CHECK-NEXT:    [[L:%.*]] = load i32, ptr [[PTR]], align 4
-; CHECK-NEXT:    ret i32 [[L]]
+; FNATTRS: Function Attrs: mustprogress nofree norecurse nosync nounwind willreturn memory(argmem: read)
+; FNATTRS-LABEL: define i32 @test_only_read_arg
+; FNATTRS-SAME: (ptr nocapture readonly [[PTR:%.*]]) #[[ATTR1:[0-9]+]] {
+; FNATTRS-NEXT:  entry:
+; FNATTRS-NEXT:    [[L:%.*]] = load i32, ptr [[PTR]], align 4
+; FNATTRS-NEXT:    ret i32 [[L]]
+;
+; ATTRIBUTOR: Function Attrs: mustprogress nofree norecurse nosync nounwind willreturn memory(argmem: read)
+; ATTRIBUTOR-LABEL: define i32 @test_only_read_arg
+; ATTRIBUTOR-SAME: (ptr nocapture nofree nonnull readonly [[PTR:%.*]]) #[[ATTR1:[0-9]+]] {
+; ATTRIBUTOR-NEXT:  entry:
+; ATTRIBUTOR-NEXT:    [[L:%.*]] = load i32, ptr [[PTR]], align 4
+; ATTRIBUTOR-NEXT:    ret i32 [[L]]
 ;
 entry:
   %l = load i32, ptr %ptr
@@ -28,12 +36,19 @@ entry:
 }
 
 define i32 @test_only_read_arg_already_has_argmemonly(ptr %ptr) argmemonly {
-; CHECK: Function Attrs: mustprogress nofree norecurse nosync nounwind willreturn memory(argmem: read)
-; CHECK-LABEL: define i32 @test_only_read_arg_already_has_argmemonly
-; CHECK-SAME: (ptr nocapture readonly [[PTR:%.*]]) #[[ATTR1]] {
-; CHECK-NEXT:  entry:
-; CHECK-NEXT:    [[L:%.*]] = load i32, ptr [[PTR]], align 4
-; CHECK-NEXT:    ret i32 [[L]]
+; FNATTRS: Function Attrs: mustprogress nofree norecurse nosync nounwind willreturn memory(argmem: read)
+; FNATTRS-LABEL: define i32 @test_only_read_arg_already_has_argmemonly
+; FNATTRS-SAME: (ptr nocapture readonly [[PTR:%.*]]) #[[ATTR1]] {
+; FNATTRS-NEXT:  entry:
+; FNATTRS-NEXT:    [[L:%.*]] = load i32, ptr [[PTR]], align 4
+; FNATTRS-NEXT:    ret i32 [[L]]
+;
+; ATTRIBUTOR: Function Attrs: mustprogress nofree norecurse nosync nounwind willreturn memory(argmem: read)
+; ATTRIBUTOR-LABEL: define i32 @test_only_read_arg_already_has_argmemonly
+; ATTRIBUTOR-SAME: (ptr nocapture nofree nonnull readonly [[PTR:%.*]]) #[[ATTR1]] {
+; ATTRIBUTOR-NEXT:  entry:
+; ATTRIBUTOR-NEXT:    [[L:%.*]] = load i32, ptr [[PTR]], align 4
+; ATTRIBUTOR-NEXT:    ret i32 [[L]]
 ;
 entry:
   %l = load i32, ptr %ptr
@@ -41,12 +56,19 @@ entry:
 }
 
 define i32 @test_read_global() {
-; CHECK: Function Attrs: mustprogress nofree norecurse nosync nounwind willreturn memory(read, argmem: none, inaccessiblemem: none)
-; CHECK-LABEL: define i32 @test_read_global
-; CHECK-SAME: () #[[ATTR2:[0-9]+]] {
-; CHECK-NEXT:  entry:
-; CHECK-NEXT:    [[L:%.*]] = load i32, ptr @g, align 4
-; CHECK-NEXT:    ret i32 [[L]]
+; FNATTRS: Function Attrs: mustprogress nofree norecurse nosync nounwind willreturn memory(read, argmem: none, inaccessiblemem: none)
+; FNATTRS-LABEL: define i32 @test_read_global
+; FNATTRS-SAME: () #[[ATTR2:[0-9]+]] {
+; FNATTRS-NEXT:  entry:
+; FNATTRS-NEXT:    [[L:%.*]] = load i32, ptr @g, align 4
+; FNATTRS-NEXT:    ret i32 [[L]]
+;
+; ATTRIBUTOR: Function Attrs: mustprogress nofree norecurse nosync nounwind willreturn memory(read)
+; ATTRIBUTOR-LABEL: define i32 @test_read_global
+; ATTRIBUTOR-SAME: () #[[ATTR2:[0-9]+]] {
+; ATTRIBUTOR-NEXT:  entry:
+; ATTRIBUTOR-NEXT:    [[L:%.*]] = load i32, ptr @g, align 4
+; ATTRIBUTOR-NEXT:    ret i32 [[L]]
 ;
 entry:
   %l = load i32, ptr @g
@@ -54,13 +76,21 @@ entry:
 }
 
 define i32 @test_read_loaded_ptr(ptr %ptr) {
-; CHECK: Function Attrs: mustprogress nofree norecurse nosync nounwind willreturn memory(read, inaccessiblemem: none)
-; CHECK-LABEL: define i32 @test_read_loaded_ptr
-; CHECK-SAME: (ptr nocapture readonly [[PTR:%.*]]) #[[ATTR3:[0-9]+]] {
-; CHECK-NEXT:  entry:
-; CHECK-NEXT:    [[L:%.*]] = load ptr, ptr [[PTR]], align 8
-; CHECK-NEXT:    [[L_2:%.*]] = load i32, ptr [[L]], align 4
-; CHECK-NEXT:    ret i32 [[L_2]]
+; FNATTRS: Function Attrs: mustprogress nofree norecurse nosync nounwind willreturn memory(read, inaccessiblemem: none)
+; FNATTRS-LABEL: define i32 @test_read_loaded_ptr
+; FNATTRS-SAME: (ptr nocapture readonly [[PTR:%.*]]) #[[ATTR3:[0-9]+]] {
+; FNATTRS-NEXT:  entry:
+; FNATTRS-NEXT:    [[L:%.*]] = load ptr, ptr [[PTR]], align 8
+; FNATTRS-NEXT:    [[L_2:%.*]] = load i32, ptr [[L]], align 4
+; FNATTRS-NEXT:    ret i32 [[L_2]]
+;
+; ATTRIBUTOR: Function Attrs: mustprogress nofree norecurse nosync nounwind willreturn memory(read)
+; ATTRIBUTOR-LABEL: define i32 @test_read_loaded_ptr
+; ATTRIBUTOR-SAME: (ptr nocapture nofree nonnull readonly [[PTR:%.*]]) #[[ATTR2]] {
+; ATTRIBUTOR-NEXT:  entry:
+; ATTRIBUTOR-NEXT:    [[L:%.*]] = load ptr, ptr [[PTR]], align 8
+; ATTRIBUTOR-NEXT:    [[L_2:%.*]] = load i32, ptr [[L]], align 4
+; ATTRIBUTOR-NEXT:    ret i32 [[L_2]]
 ;
 entry:
   %l = load ptr, ptr %ptr
@@ -69,12 +99,19 @@ entry:
 }
 
 define void @test_only_write_arg(ptr %ptr) {
-; CHECK: Function Attrs: mustprogress nofree norecurse nosync nounwind willreturn memory(argmem: write)
-; CHECK-LABEL: define void @test_only_write_arg
-; CHECK-SAME: (ptr nocapture writeonly [[PTR:%.*]]) #[[ATTR4:[0-9]+]] {
-; CHECK-NEXT:  entry:
-; CHECK-NEXT:    store i32 0, ptr [[PTR]], align 4
-; CHECK-NEXT:    ret void
+; FNATTRS: Function Attrs: mustprogress nofree norecurse nosync nounwind willreturn memory(argmem: write)
+; FNATTRS-LABEL: define void @test_only_write_arg
+; FNATTRS-SAME: (ptr nocapture writeonly [[PTR:%.*]]) #[[ATTR4:[0-9]+]] {
+; FNATTRS-NEXT:  entry:
+; FNATTRS-NEXT:    store i32 0, ptr [[PTR]], align 4
+; FNATTRS-NEXT:    ret void
+;
+; ATTRIBUTOR: Function Attrs: mustprogress nofree norecurse nosync nounwind willreturn memory(argmem: write)
+; ATTRIBUTOR-LABEL: define void @test_only_write_arg
+; ATTRIBUTOR-SAME: (ptr nocapture nofree nonnull writeonly [[PTR:%.*]]) #[[ATTR3:[0-9]+]] {
+; ATTRIBUTOR-NEXT:  entry:
+; ATTRIBUTOR-NEXT:    store i32 0, ptr [[PTR]], align 4
+; ATTRIBUTOR-NEXT:    ret void
 ;
 entry:
   store i32 0, ptr %ptr
@@ -82,12 +119,19 @@ entry:
 }
 
 define void @test_write_global() {
-; CHECK: Function Attrs: mustprogress nofree norecurse nosync nounwind willreturn memory(write, argmem: none, inaccessiblemem: none)
-; CHECK-LABEL: define void @test_write_global
-; CHECK-SAME: () #[[ATTR5:[0-9]+]] {
-; CHECK-NEXT:  entry:
-; CHECK-NEXT:    store i32 0, ptr @g, align 4
-; CHECK-NEXT:    ret void
+; FNATTRS: Function Attrs: mustprogress nofree norecurse nosync nounwind willreturn memory(write, argmem: none, inaccessiblemem: none)
+; FNATTRS-LABEL: define void @test_write_global
+; FNATTRS-SAME: () #[[ATTR5:[0-9]+]] {
+; FNATTRS-NEXT:  entry:
+; FNATTRS-NEXT:    store i32 0, ptr @g, align 4
+; FNATTRS-NEXT:    ret void
+;
+; ATTRIBUTOR: Function Attrs: mustprogress nofree norecurse nosync nounwind willreturn memory(write)
+; ATTRIBUTOR-LABEL: define void @test_write_global
+; ATTRIBUTOR-SAME: () #[[ATTR4:[0-9]+]] {
+; ATTRIBUTOR-NEXT:  entry:
+; ATTRIBUTOR-NEXT:    store i32 0, ptr @g, align 4
+; ATTRIBUTOR-NEXT:    ret void
 ;
 entry:
   store i32 0, ptr @g
@@ -97,10 +141,10 @@ entry:
 declare void @fn_may_access_memory()
 
 define void @test_call_may_access_memory() {
-; CHECK-LABEL: define void @test_call_may_access_memory() {
-; CHECK-NEXT:  entry:
-; CHECK-NEXT:    call void @fn_may_access_memory()
-; CHECK-NEXT:    ret void
+; COMMON-LABEL: define void @test_call_may_access_memory() {
+; COMMON-NEXT:  entry:
+; COMMON-NEXT:    call void @fn_may_access_memory()
+; COMMON-NEXT:    ret void
 ;
 entry:
   call void @fn_may_access_memory()
@@ -110,13 +154,21 @@ entry:
 declare i32 @fn_readnone() readnone
 
 define void @test_call_readnone(ptr %ptr) {
-; CHECK: Function Attrs: memory(argmem: write)
-; CHECK-LABEL: define void @test_call_readnone
-; CHECK-SAME: (ptr nocapture writeonly [[PTR:%.*]]) #[[ATTR7:[0-9]+]] {
-; CHECK-NEXT:  entry:
-; CHECK-NEXT:    [[C:%.*]] = call i32 @fn_readnone()
-; CHECK-NEXT:    store i32 [[C]], ptr [[PTR]], align 4
-; CHECK-NEXT:    ret void
+; FNATTRS: Function Attrs: memory(argmem: write)
+; FNATTRS-LABEL: define void @test_call_readnone
+; FNATTRS-SAME: (ptr nocapture writeonly [[PTR:%.*]]) #[[ATTR7:[0-9]+]] {
+; FNATTRS-NEXT:  entry:
+; FNATTRS-NEXT:    [[C:%.*]] = call i32 @fn_readnone()
+; FNATTRS-NEXT:    store i32 [[C]], ptr [[PTR]], align 4
+; FNATTRS-NEXT:    ret void
+;
+; ATTRIBUTOR: Function Attrs: nosync memory(argmem: write)
+; ATTRIBUTOR-LABEL: define void @test_call_readnone
+; ATTRIBUTOR-SAME: (ptr nocapture nofree writeonly [[PTR:%.*]]) #[[ATTR6:[0-9]+]] {
+; ATTRIBUTOR-NEXT:  entry:
+; ATTRIBUTOR-NEXT:    [[C:%.*]] = call i32 @fn_readnone() #[[ATTR19:[0-9]+]]
+; ATTRIBUTOR-NEXT:    store i32 [[C]], ptr [[PTR]], align 4
+; ATTRIBUTOR-NEXT:    ret void
 ;
 entry:
   %c = call i32 @fn_readnone()
@@ -127,12 +179,19 @@ entry:
 declare i32 @fn_argmemonly(ptr) argmemonly
 
 define i32 @test_call_argmemonly(ptr %ptr) {
-; CHECK: Function Attrs: memory(argmem: readwrite)
-; CHECK-LABEL: define i32 @test_call_argmemonly
-; CHECK-SAME: (ptr [[PTR:%.*]]) #[[ATTR8:[0-9]+]] {
-; CHECK-NEXT:  entry:
-; CHECK-NEXT:    [[C:%.*]] = call i32 @fn_argmemonly(ptr [[PTR]])
-; CHECK-NEXT:    ret i32 [[C]]
+; FNATTRS: Function Attrs: memory(argmem: readwrite)
+; FNATTRS-LABEL: define i32 @test_call_argmemonly
+; FNATTRS-SAME: (ptr [[PTR:%.*]]) #[[ATTR8:[0-9]+]] {
+; FNATTRS-NEXT:  entry:
+; FNATTRS-NEXT:    [[C:%.*]] = call i32 @fn_argmemonly(ptr [[PTR]])
+; FNATTRS-NEXT:    ret i32 [[C]]
+;
+; ATTRIBUTOR: Function Attrs: memory(argmem: readwrite)
+; ATTRIBUTOR-LABEL: define i32 @test_call_argmemonly
+; ATTRIBUTOR-SAME: (ptr [[PTR:%.*]]) #[[ATTR7:[0-9]+]] {
+; ATTRIBUTOR-NEXT:  entry:
+; ATTRIBUTOR-NEXT:    [[C:%.*]] = call i32 @fn_argmemonly(ptr [[PTR]])
+; ATTRIBUTOR-NEXT:    ret i32 [[C]]
 ;
 entry:
   %c = call i32 @fn_argmemonly(ptr %ptr)
@@ -140,12 +199,19 @@ entry:
 }
 
 define i32 @test_call_fn_where_argmemonly_can_be_inferred(ptr %ptr) {
-; CHECK: Function Attrs: mustprogress nofree norecurse nosync nounwind willreturn memory(argmem: read)
-; CHECK-LABEL: define i32 @test_call_fn_where_argmemonly_can_be_inferred
-; CHECK-SAME: (ptr nocapture readonly [[PTR:%.*]]) #[[ATTR1]] {
-; CHECK-NEXT:  entry:
-; CHECK-NEXT:    [[C:%.*]] = call i32 @test_only_read_arg(ptr [[PTR]])
-; CHECK-NEXT:    ret i32 [[C]]
+; FNATTRS: Function Attrs: mustprogress nofree norecurse nosync nounwind willreturn memory(argmem: read)
+; FNATTRS-LABEL: define i32 @test_call_fn_where_argmemonly_can_be_inferred
+; FNATTRS-SAME: (ptr nocapture readonly [[PTR:%.*]]) #[[ATTR1]] {
+; FNATTRS-NEXT:  entry:
+; FNATTRS-NEXT:    [[C:%.*]] = call i32 @test_only_read_arg(ptr [[PTR]])
+; FNATTRS-NEXT:    ret i32 [[C]]
+;
+; ATTRIBUTOR: Function Attrs: mustprogress nofree norecurse nosync nounwind willreturn memory(argmem: read)
+; ATTRIBUTOR-LABEL: define i32 @test_call_fn_where_argmemonly_can_be_inferred
+; ATTRIBUTOR-SAME: (ptr nocapture nofree readonly [[PTR:%.*]]) #[[ATTR1]] {
+; ATTRIBUTOR-NEXT:  entry:
+; ATTRIBUTOR-NEXT:    [[C:%.*]] = call i32 @test_only_read_arg(ptr nocapture nofree readonly [[PTR]]) #[[ATTR20:[0-9]+]]
+; ATTRIBUTOR-NEXT:    ret i32 [[C]]
 ;
 entry:
   %c = call i32 @test_only_read_arg(ptr %ptr)
@@ -153,12 +219,19 @@ entry:
 }
 
 define void @test_memcpy_argonly(ptr %dst, ptr %src) {
-; CHECK: Function Attrs: mustprogress nofree nosync nounwind willreturn memory(argmem: readwrite)
-; CHECK-LABEL: define void @test_memcpy_argonly
-; CHECK-SAME: (ptr nocapture writeonly [[DST:%.*]], ptr nocapture readonly [[SRC:%.*]]) #[[ATTR9:[0-9]+]] {
-; CHECK-NEXT:  entry:
-; CHECK-NEXT:    call void @llvm.memcpy.p0.p0.i64(ptr [[DST]], ptr [[SRC]], i64 32, i1 false)
-; CHECK-NEXT:    ret void
+; FNATTRS: Function Attrs: mustprogress nofree nosync nounwind willreturn memory(argmem: readwrite)
+; FNATTRS-LABEL: define void @test_memcpy_argonly
+; FNATTRS-SAME: (ptr nocapture writeonly [[DST:%.*]], ptr nocapture readonly [[SRC:%.*]]) #[[ATTR9:[0-9]+]] {
+; FNATTRS-NEXT:  entry:
+; FNATTRS-NEXT:    call void @llvm.memcpy.p0.p0.i64(ptr [[DST]], ptr [[SRC]], i64 32, i1 false)
+; FNATTRS-NEXT:    ret void
+;
+; ATTRIBUTOR: Function Attrs: mustprogress nofree norecurse nosync nounwind willreturn memory(argmem: readwrite)
+; ATTRIBUTOR-LABEL: define void @test_memcpy_argonly
+; ATTRIBUTOR-SAME: (ptr nocapture nofree writeonly [[DST:%.*]], ptr nocapture nofree readonly [[SRC:%.*]]) #[[ATTR8:[0-9]+]] {
+; ATTRIBUTOR-NEXT:  entry:
+; ATTRIBUTOR-NEXT:    call void @llvm.memcpy.p0.p0.i64(ptr nocapture writeonly [[DST]], ptr nocapture readonly [[SRC]], i64 32, i1 false) #[[ATTR21:[0-9]+]]
+; ATTRIBUTOR-NEXT:    ret void
 ;
 entry:
   call void @llvm.memcpy.p0.p0.i64(ptr %dst, ptr %src, i64 32, i1 false)
@@ -170,12 +243,19 @@ declare void @llvm.memcpy.p0.p0.i64(ptr, ptr, i64, i1)
 @arr = global [32 x i8] zeroinitializer
 
 define void @test_memcpy_src_global(ptr %dst) {
-; CHECK: Function Attrs: mustprogress nofree nosync nounwind willreturn memory(readwrite, inaccessiblemem: none)
-; CHECK-LABEL: define void @test_memcpy_src_global
-; CHECK-SAME: (ptr nocapture writeonly [[DST:%.*]]) #[[ATTR11:[0-9]+]] {
-; CHECK-NEXT:  entry:
-; CHECK-NEXT:    call void @llvm.memcpy.p0.p0.i64(ptr [[DST]], ptr @arr, i64 32, i1 false)
-; CHECK-NEXT:    ret void
+; FNATTRS: Function Attrs: mustprogress nofree nosync nounwind willreturn memory(readwrite, inaccessiblemem: none)
+; FNATTRS-LABEL: define void @test_memcpy_src_global
+; FNATTRS-SAME: (ptr nocapture writeonly [[DST:%.*]]) #[[ATTR11:[0-9]+]] {
+; FNATTRS-NEXT:  entry:
+; FNATTRS-NEXT:    call void @llvm.memcpy.p0.p0.i64(ptr [[DST]], ptr @arr, i64 32, i1 false)
+; FNATTRS-NEXT:    ret void
+;
+; ATTRIBUTOR: Function Attrs: mustprogress nofree norecurse nosync nounwind willreturn
+; ATTRIBUTOR-LABEL: define void @test_memcpy_src_global
+; ATTRIBUTOR-SAME: (ptr nocapture nofree writeonly [[DST:%.*]]) #[[ATTR10:[0-9]+]] {
+; ATTRIBUTOR-NEXT:  entry:
+; ATTRIBUTOR-NEXT:    call void @llvm.memcpy.p0.p0.i64(ptr nocapture writeonly [[DST]], ptr readonly @arr, i64 32, i1 false) #[[ATTR21]]
+; ATTRIBUTOR-NEXT:    ret void
 ;
 entry:
   call void @llvm.memcpy.p0.p0.i64(ptr %dst, ptr @arr, i64 32, i1 false)
@@ -183,12 +263,19 @@ entry:
 }
 
 define void @test_memcpy_dst_global(ptr %src) {
-; CHECK: Function Attrs: mustprogress nofree nosync nounwind willreturn memory(readwrite, inaccessiblemem: none)
-; CHECK-LABEL: define void @test_memcpy_dst_global
-; CHECK-SAME: (ptr nocapture readonly [[SRC:%.*]]) #[[ATTR11]] {
-; CHECK-NEXT:  entry:
-; CHECK-NEXT:    call void @llvm.memcpy.p0.p0.i64(ptr @arr, ptr [[SRC]], i64 32, i1 false)
-; CHECK-NEXT:    ret void
+; FNATTRS: Function Attrs: mustprogress nofree nosync nounwind willreturn memory(readwrite, inaccessiblemem: none)
+; FNATTRS-LABEL: define void @test_memcpy_dst_global
+; FNATTRS-SAME: (ptr nocapture readonly [[SRC:%.*]]) #[[ATTR11]] {
+; FNATTRS-NEXT:  entry:
+; FNATTRS-NEXT:    call void @llvm.memcpy.p0.p0.i64(ptr @arr, ptr [[SRC]], i64 32, i1 false)
+; FNATTRS-NEXT:    ret void
+;
+; ATTRIBUTOR: Function Attrs: mustprogress nofree norecurse nosync nounwind willreturn
+; ATTRIBUTOR-LABEL: define void @test_memcpy_dst_global
+; ATTRIBUTOR-SAME: (ptr nocapture nofree readonly [[SRC:%.*]]) #[[ATTR10]] {
+; ATTRIBUTOR-NEXT:  entry:
+; ATTRIBUTOR-NEXT:    call void @llvm.memcpy.p0.p0.i64(ptr writeonly @arr, ptr nocapture readonly [[SRC]], i64 32, i1 false) #[[ATTR21]]
+; ATTRIBUTOR-NEXT:    ret void
 ;
 entry:
   call void @llvm.memcpy.p0.p0.i64(ptr @arr, ptr %src, i64 32, i1 false)
@@ -196,15 +283,25 @@ entry:
 }
 
 define i32 @test_read_arg_access_alloca(ptr %ptr) {
-; CHECK: Function Attrs: mustprogress nofree norecurse nosync nounwind willreturn memory(argmem: read)
-; CHECK-LABEL: define i32 @test_read_arg_access_alloca
-; CHECK-SAME: (ptr nocapture readonly [[PTR:%.*]]) #[[ATTR1]] {
-; CHECK-NEXT:  entry:
-; CHECK-NEXT:    [[A:%.*]] = alloca i32, align 4
-; CHECK-NEXT:    [[L:%.*]] = load i32, ptr [[PTR]], align 4
-; CHECK-NEXT:    store i32 [[L]], ptr [[A]], align 4
-; CHECK-NEXT:    [[L_2:%.*]] = load i32, ptr [[A]], align 4
-; CHECK-NEXT:    ret i32 [[L_2]]
+; FNATTRS: Function Attrs: mustprogress nofree norecurse nosync nounwind willreturn memory(argmem: read)
+; FNATTRS-LABEL: define i32 @test_read_arg_access_alloca
+; FNATTRS-SAME: (ptr nocapture readonly [[PTR:%.*]]) #[[ATTR1]] {
+; FNATTRS-NEXT:  entry:
+; FNATTRS-NEXT:    [[A:%.*]] = alloca i32, align 4
+; FNATTRS-NEXT:    [[L:%.*]] = load i32, ptr [[PTR]], align 4
+; FNATTRS-NEXT:    store i32 [[L]], ptr [[A]], align 4
+; FNATTRS-NEXT:    [[L_2:%.*]] = load i32, ptr [[A]], align 4
+; FNATTRS-NEXT:    ret i32 [[L_2]]
+;
+; ATTRIBUTOR: Function Attrs: mustprogress nofree norecurse nosync nounwind willreturn memory(argmem: readwrite)
+; ATTRIBUTOR-LABEL: define i32 @test_read_arg_access_alloca
+; ATTRIBUTOR-SAME: (ptr nocapture nofree nonnull readonly [[PTR:%.*]]) #[[ATTR8]] {
+; ATTRIBUTOR-NEXT:  entry:
+; ATTRIBUTOR-NEXT:    [[A:%.*]] = alloca i32, align 4
+; ATTRIBUTOR-NEXT:    [[L:%.*]] = load i32, ptr [[PTR]], align 4
+; ATTRIBUTOR-NEXT:    store i32 [[L]], ptr [[A]], align 4
+; ATTRIBUTOR-NEXT:    [[L_2:%.*]] = load i32, ptr [[A]], align 4
+; ATTRIBUTOR-NEXT:    ret i32 [[L_2]]
 ;
 entry:
   %a = alloca i32
@@ -217,34 +314,53 @@ entry:
 declare void @fn_inaccessiblememonly() inaccessiblememonly
 
 define void @test_inaccessiblememonly() {
-; CHECK: Function Attrs: memory(inaccessiblemem: readwrite)
-; CHECK-LABEL: define void @test_inaccessiblememonly
-; CHECK-SAME: () #[[ATTR12:[0-9]+]] {
-; CHECK-NEXT:    call void @fn_inaccessiblememonly()
-; CHECK-NEXT:    ret void
+; FNATTRS: Function Attrs: memory(inaccessiblemem: readwrite)
+; FNATTRS-LABEL: define void @test_inaccessiblememonly
+; FNATTRS-SAME: () #[[ATTR12:[0-9]+]] {
+; FNATTRS-NEXT:    call void @fn_inaccessiblememonly()
+; FNATTRS-NEXT:    ret void
+;
+; ATTRIBUTOR: Function Attrs: memory(inaccessiblemem: readwrite)
+; ATTRIBUTOR-LABEL: define void @test_inaccessiblememonly
+; ATTRIBUTOR-SAME: () #[[ATTR11:[0-9]+]] {
+; ATTRIBUTOR-NEXT:    call void @fn_inaccessiblememonly()
+; ATTRIBUTOR-NEXT:    ret void
 ;
   call void @fn_inaccessiblememonly()
   ret void
 }
 
 define void @test_inaccessiblememonly_readonly() {
-; CHECK: Function Attrs: nofree memory(inaccessiblemem: read)
-; CHECK-LABEL: define void @test_inaccessiblememonly_readonly
-; CHECK-SAME: () #[[ATTR13:[0-9]+]] {
-; CHECK-NEXT:    call void @fn_inaccessiblememonly() #[[ATTR18:[0-9]+]]
-; CHECK-NEXT:    ret void
+; FNATTRS: Function Attrs: nofree memory(inaccessiblemem: read)
+; FNATTRS-LABEL: define void @test_inaccessiblememonly_readonly
+; FNATTRS-SAME: () #[[ATTR13:[0-9]+]] {
+; FNATTRS-NEXT:    call void @fn_inaccessiblememonly() #[[ATTR19:[0-9]+]]
+; FNATTRS-NEXT:    ret void
+;
+; ATTRIBUTOR: Function Attrs: nosync memory(inaccessiblemem: read)
+; ATTRIBUTOR-LABEL: define void @test_inaccessiblememonly_readonly
+; ATTRIBUTOR-SAME: () #[[ATTR12:[0-9]+]] {
+; ATTRIBUTOR-NEXT:    call void @fn_inaccessiblememonly() #[[ATTR22:[0-9]+]]
+; ATTRIBUTOR-NEXT:    ret void
 ;
   call void @fn_inaccessiblememonly() readonly
   ret void
 }
 
 define void @test_inaccessibleorargmemonly_readonly(ptr %arg) {
-; CHECK: Function Attrs: nofree memory(argmem: read, inaccessiblemem: read)
-; CHECK-LABEL: define void @test_inaccessibleorargmemonly_readonly
-; CHECK-SAME: (ptr nocapture readonly [[ARG:%.*]]) #[[ATTR14:[0-9]+]] {
-; CHECK-NEXT:    [[TMP1:%.*]] = load i32, ptr [[ARG]], align 4
-; CHECK-NEXT:    call void @fn_inaccessiblememonly() #[[ATTR18]]
-; CHECK-NEXT:    ret void
+; FNATTRS: Function Attrs: nofree memory(argmem: read, inaccessiblemem: read)
+; FNATTRS-LABEL: define void @test_inaccessibleorargmemonly_readonly
+; FNATTRS-SAME: (ptr nocapture readonly [[ARG:%.*]]) #[[ATTR14:[0-9]+]] {
+; FNATTRS-NEXT:    [[TMP1:%.*]] = load i32, ptr [[ARG]], align 4
+; FNATTRS-NEXT:    call void @fn_inaccessiblememonly() #[[ATTR19]]
+; FNATTRS-NEXT:    ret void
+;
+; ATTRIBUTOR: Function Attrs: nosync memory(argmem: read, inaccessiblemem: read)
+; ATTRIBUTOR-LABEL: define void @test_inaccessibleorargmemonly_readonly
+; ATTRIBUTOR-SAME: (ptr nocapture nofree nonnull readonly [[ARG:%.*]]) #[[ATTR13:[0-9]+]] {
+; ATTRIBUTOR-NEXT:    [[TMP1:%.*]] = load i32, ptr [[ARG]], align 4
+; ATTRIBUTOR-NEXT:    call void @fn_inaccessiblememonly() #[[ATTR22]]
+; ATTRIBUTOR-NEXT:    ret void
 ;
   load i32, ptr %arg
   call void @fn_inaccessiblememonly() readonly
@@ -252,12 +368,19 @@ define void @test_inaccessibleorargmemonly_readonly(ptr %arg) {
 }
 
 define void @test_inaccessibleorargmemonly_readwrite(ptr %arg) {
-; CHECK: Function Attrs: memory(argmem: write, inaccessiblemem: read)
-; CHECK-LABEL: define void @test_inaccessibleorargmemonly_readwrite
-; CHECK-SAME: (ptr nocapture writeonly [[ARG:%.*]]) #[[ATTR15:[0-9]+]] {
-; CHECK-NEXT:    store i32 0, ptr [[ARG]], align 4
-; CHECK-NEXT:    call void @fn_inaccessiblememonly() #[[ATTR18]]
-; CHECK-NEXT:    ret void
+; FNATTRS: Function Attrs: memory(argmem: write, inaccessiblemem: read)
+; FNATTRS-LABEL: define void @test_inaccessibleorargmemonly_readwrite
+; FNATTRS-SAME: (ptr nocapture writeonly [[ARG:%.*]]) #[[ATTR15:[0-9]+]] {
+; FNATTRS-NEXT:    store i32 0, ptr [[ARG]], align 4
+; FNATTRS-NEXT:    call void @fn_inaccessiblememonly() #[[ATTR19]]
+; FNATTRS-NEXT:    ret void
+;
+; ATTRIBUTOR: Function Attrs: nosync memory(argmem: readwrite, inaccessiblemem: readwrite)
+; ATTRIBUTOR-LABEL: define void @test_inaccessibleorargmemonly_readwrite
+; ATTRIBUTOR-SAME: (ptr nocapture nofree nonnull writeonly [[ARG:%.*]]) #[[ATTR14:[0-9]+]] {
+; ATTRIBUTOR-NEXT:    store i32 0, ptr [[ARG]], align 4
+; ATTRIBUTOR-NEXT:    call void @fn_inaccessiblememonly() #[[ATTR22]]
+; ATTRIBUTOR-NEXT:    ret void
 ;
   store i32 0, ptr %arg
   call void @fn_inaccessiblememonly() readonly
@@ -265,12 +388,19 @@ define void @test_inaccessibleorargmemonly_readwrite(ptr %arg) {
 }
 
 define void @test_recursive_argmem_read(ptr %p) {
-; CHECK: Function Attrs: nofree nosync nounwind memory(argmem: read)
-; CHECK-LABEL: define void @test_recursive_argmem_read
-; CHECK-SAME: (ptr nocapture readonly [[P:%.*]]) #[[ATTR16:[0-9]+]] {
-; CHECK-NEXT:    [[PVAL:%.*]] = load ptr, ptr [[P]], align 8
-; CHECK-NEXT:    call void @test_recursive_argmem_read(ptr [[PVAL]])
-; CHECK-NEXT:    ret void
+; FNATTRS: Function Attrs: nofree nosync nounwind memory(argmem: read)
+; FNATTRS-LABEL: define void @test_recursive_argmem_read
+; FNATTRS-SAME: (ptr nocapture readonly [[P:%.*]]) #[[ATTR16:[0-9]+]] {
+; FNATTRS-NEXT:    [[PVAL:%.*]] = load ptr, ptr [[P]], align 8
+; FNATTRS-NEXT:    call void @test_recursive_argmem_read(ptr [[PVAL]])
+; FNATTRS-NEXT:    ret void
+;
+; ATTRIBUTOR: Function Attrs: nofree nosync nounwind memory(read)
+; ATTRIBUTOR-LABEL: define void @test_recursive_argmem_read
+; ATTRIBUTOR-SAME: (ptr nocapture nofree nonnull readonly [[P:%.*]]) #[[ATTR15:[0-9]+]] {
+; ATTRIBUTOR-NEXT:    [[PVAL:%.*]] = load ptr, ptr [[P]], align 8
+; ATTRIBUTOR-NEXT:    call void @test_recursive_argmem_read(ptr nocapture nofree readonly [[PVAL]]) #[[ATTR15]]
+; ATTRIBUTOR-NEXT:    ret void
 ;
   %pval = load ptr, ptr %p
   call void @test_recursive_argmem_read(ptr %pval)
@@ -278,13 +408,21 @@ define void @test_recursive_argmem_read(ptr %p) {
 }
 
 define void @test_recursive_argmem_readwrite(ptr %p) {
-; CHECK: Function Attrs: nofree nosync nounwind memory(argmem: readwrite)
-; CHECK-LABEL: define void @test_recursive_argmem_readwrite
-; CHECK-SAME: (ptr nocapture [[P:%.*]]) #[[ATTR17:[0-9]+]] {
-; CHECK-NEXT:    [[PVAL:%.*]] = load ptr, ptr [[P]], align 8
-; CHECK-NEXT:    store i32 0, ptr [[P]], align 4
-; CHECK-NEXT:    call void @test_recursive_argmem_readwrite(ptr [[PVAL]])
-; CHECK-NEXT:    ret void
+; FNATTRS: Function Attrs: nofree nosync nounwind memory(argmem: readwrite)
+; FNATTRS-LABEL: define void @test_recursive_argmem_readwrite
+; FNATTRS-SAME: (ptr nocapture [[P:%.*]]) #[[ATTR17:[0-9]+]] {
+; FNATTRS-NEXT:    [[PVAL:%.*]] = load ptr, ptr [[P]], align 8
+; FNATTRS-NEXT:    store i32 0, ptr [[P]], align 4
+; FNATTRS-NEXT:    call void @test_recursive_argmem_readwrite(ptr [[PVAL]])
+; FNATTRS-NEXT:    ret void
+;
+; ATTRIBUTOR: Function Attrs: nofree nosync nounwind
+; ATTRIBUTOR-LABEL: define void @test_recursive_argmem_readwrite
+; ATTRIBUTOR-SAME: (ptr nocapture nofree nonnull [[P:%.*]]) #[[ATTR16:[0-9]+]] {
+; ATTRIBUTOR-NEXT:    [[PVAL:%.*]] = load ptr, ptr [[P]], align 8
+; ATTRIBUTOR-NEXT:    store i32 0, ptr [[P]], align 4
+; ATTRIBUTOR-NEXT:    call void @test_recursive_argmem_readwrite(ptr nocapture nofree [[PVAL]]) #[[ATTR16]]
+; ATTRIBUTOR-NEXT:    ret void
 ;
   %pval = load ptr, ptr %p
   store i32 0, ptr %p
@@ -293,13 +431,21 @@ define void @test_recursive_argmem_readwrite(ptr %p) {
 }
 
 define void @test_recursive_argmem_read_alloca(ptr %p) {
-; CHECK: Function Attrs: nofree nosync nounwind memory(argmem: read)
-; CHECK-LABEL: define void @test_recursive_argmem_read_alloca
-; CHECK-SAME: (ptr nocapture readonly [[P:%.*]]) #[[ATTR16]] {
-; CHECK-NEXT:    [[A:%.*]] = alloca ptr, align 8
-; CHECK-NEXT:    [[TMP1:%.*]] = load i32, ptr [[P]], align 4
-; CHECK-NEXT:    call void @test_recursive_argmem_read_alloca(ptr [[A]])
-; CHECK-NEXT:    ret void
+; FNATTRS: Function Attrs: nofree nosync nounwind memory(argmem: read)
+; FNATTRS-LABEL: define void @test_recursive_argmem_read_alloca
+; FNATTRS-SAME: (ptr nocapture readonly [[P:%.*]]) #[[ATTR16]] {
+; FNATTRS-NEXT:    [[A:%.*]] = alloca ptr, align 8
+; FNATTRS-NEXT:    [[TMP1:%.*]] = load i32, ptr [[P]], align 4
+; FNATTRS-NEXT:    call void @test_recursive_argmem_read_alloca(ptr [[A]])
+; FNATTRS-NEXT:    ret void
+;
+; ATTRIBUTOR: Function Attrs: nofree nosync nounwind memory(argmem: read)
+; ATTRIBUTOR-LABEL: define void @test_recursive_argmem_read_alloca
+; ATTRIBUTOR-SAME: (ptr nocapture nofree nonnull readonly [[P:%.*]]) #[[ATTR17:[0-9]+]] {
+; ATTRIBUTOR-NEXT:    [[A:%.*]] = alloca ptr, align 8
+; ATTRIBUTOR-NEXT:    [[TMP1:%.*]] = load i32, ptr [[P]], align 4
+; ATTRIBUTOR-NEXT:    call void @test_recursive_argmem_read_alloca(ptr nocapture nofree nonnull readonly [[A]]) #[[ATTR15]]
+; ATTRIBUTOR-NEXT:    ret void
 ;
   %a = alloca ptr
   load i32, ptr %p
@@ -308,24 +454,17 @@ define void @test_recursive_argmem_read_alloca(ptr %p) {
 }
 
 define void @test_scc_argmem_read_1(ptr %p) {
-; CHECK: Function Attrs: nofree nosync nounwind memory(argmem: read)
-; CHECK-LABEL: define void @test_scc_argmem_read_1
-; CHECK-SAME: (ptr nocapture readonly [[P:%.*]]) #[[ATTR16]] {
-; CHECK-NEXT:    [[PVAL:%.*]] = load ptr, ptr [[P]], align 8
-; CHECK-NEXT:    call void @test_scc_argmem_read_2(ptr [[PVAL]])
-; CHECK-NEXT:    ret void
+; FNATTRS: Function Attrs: nofree nosync nounwind memory(none)
+; FNATTRS-LABEL: define void @test_scc_argmem_read_1
+; FNATTRS-SAME: (ptr nocapture readnone [[P:%.*]]) #[[ATTR18:[0-9]+]] {
+; FNATTRS-NEXT:    call void @test_scc_argmem_read_1(ptr [[P]])
+; FNATTRS-NEXT:    ret void
 ;
-  %pval = load ptr, ptr %p
-  call void @test_scc_argmem_read_2(ptr %pval)
-  ret void
-}
-
-define void @test_scc_argmem_read_2(ptr %p) {
-; CHECK: Function Attrs: nofree nosync nounwind memory(argmem: read)
-; CHECK-LABEL: define void @test_scc_argmem_read_2
-; CHECK-SAME: (ptr nocapture readonly [[P:%.*]]) #[[ATTR16]] {
-; CHECK-NEXT:    call void @test_scc_argmem_read_1(ptr [[P]])
-; CHECK-NEXT:    ret void
+; ATTRIBUTOR: Function Attrs: nofree nosync nounwind memory(none)
+; ATTRIBUTOR-LABEL: define void @test_scc_argmem_read_1
+; ATTRIBUTOR-SAME: (ptr nocapture nofree readnone [[P:%.*]]) #[[ATTR18:[0-9]+]] {
+; ATTRIBUTOR-NEXT:    call void @test_scc_argmem_read_1(ptr nocapture nofree readnone [[P]]) #[[ATTR18]]
+; ATTRIBUTOR-NEXT:    ret void
 ;
   call void @test_scc_argmem_read_1(ptr %p)
   ret void

diff  --git a/llvm/test/Transforms/FunctionAttrs/nocapture.ll b/llvm/test/Transforms/FunctionAttrs/nocapture.ll
index 6a8372451659d5..854590ed04f489 100644
--- a/llvm/test/Transforms/FunctionAttrs/nocapture.ll
+++ b/llvm/test/Transforms/FunctionAttrs/nocapture.ll
@@ -1,48 +1,82 @@
-; NOTE: Assertions have been autogenerated by utils/update_test_checks.py UTC_ARGS: --version 2
-; RUN: opt -passes=function-attrs -S < %s | FileCheck %s
+; NOTE: Assertions have been autogenerated by utils/update_test_checks.py UTC_ARGS: --check-attributes --version 2
+; RUN: opt -passes=function-attrs -S < %s | FileCheck --check-prefixes=COMMON,FNATTRS %s
+; RUN: opt -passes=attributor-light -S < %s | FileCheck --check-prefixes=COMMON,ATTRIBUTOR %s
 
 @g = global ptr null		; <ptr> [#uses=1]
 
 define ptr @c1(ptr %q) {
-; CHECK-LABEL: define ptr @c1
-; CHECK-SAME: (ptr readnone returned [[Q:%.*]]) #[[ATTR0:[0-9]+]] {
-; CHECK-NEXT:    ret ptr [[Q]]
+; FNATTRS: Function Attrs: mustprogress nofree norecurse nosync nounwind willreturn memory(none)
+; FNATTRS-LABEL: define ptr @c1
+; FNATTRS-SAME: (ptr readnone returned [[Q:%.*]]) #[[ATTR0:[0-9]+]] {
+; FNATTRS-NEXT:    ret ptr [[Q]]
+;
+; ATTRIBUTOR: Function Attrs: mustprogress nofree norecurse nosync nounwind willreturn memory(none)
+; ATTRIBUTOR-LABEL: define ptr @c1
+; ATTRIBUTOR-SAME: (ptr nofree readnone [[Q:%.*]]) #[[ATTR0:[0-9]+]] {
+; ATTRIBUTOR-NEXT:    ret ptr [[Q]]
 ;
   ret ptr %q
 }
 
 ; It would also be acceptable to mark %q as readnone. Update @c3 too.
 define void @c2(ptr %q) {
-; CHECK-LABEL: define void @c2
-; CHECK-SAME: (ptr [[Q:%.*]]) #[[ATTR1:[0-9]+]] {
-; CHECK-NEXT:    store ptr [[Q]], ptr @g, align 8
-; CHECK-NEXT:    ret void
+; FNATTRS: Function Attrs: mustprogress nofree norecurse nosync nounwind willreturn memory(write, argmem: none, inaccessiblemem: none)
+; FNATTRS-LABEL: define void @c2
+; FNATTRS-SAME: (ptr [[Q:%.*]]) #[[ATTR1:[0-9]+]] {
+; FNATTRS-NEXT:    store ptr [[Q]], ptr @g, align 8
+; FNATTRS-NEXT:    ret void
+;
+; ATTRIBUTOR: Function Attrs: mustprogress nofree norecurse nosync nounwind willreturn memory(write)
+; ATTRIBUTOR-LABEL: define void @c2
+; ATTRIBUTOR-SAME: (ptr nofree writeonly [[Q:%.*]]) #[[ATTR1:[0-9]+]] {
+; ATTRIBUTOR-NEXT:    store ptr [[Q]], ptr @g, align 8
+; ATTRIBUTOR-NEXT:    ret void
 ;
   store ptr %q, ptr @g
   ret void
 }
 
 define void @c3(ptr %q) {
-; CHECK-LABEL: define void @c3
-; CHECK-SAME: (ptr [[Q:%.*]]) #[[ATTR2:[0-9]+]] {
-; CHECK-NEXT:    call void @c2(ptr [[Q]])
-; CHECK-NEXT:    ret void
+; FNATTRS: Function Attrs: mustprogress nofree norecurse nosync nounwind willreturn memory(write, inaccessiblemem: none)
+; FNATTRS-LABEL: define void @c3
+; FNATTRS-SAME: (ptr [[Q:%.*]]) #[[ATTR2:[0-9]+]] {
+; FNATTRS-NEXT:    call void @c2(ptr [[Q]])
+; FNATTRS-NEXT:    ret void
+;
+; ATTRIBUTOR: Function Attrs: mustprogress nofree norecurse nosync nounwind willreturn memory(write)
+; ATTRIBUTOR-LABEL: define void @c3
+; ATTRIBUTOR-SAME: (ptr nofree writeonly [[Q:%.*]]) #[[ATTR1]] {
+; ATTRIBUTOR-NEXT:    call void @c2(ptr nofree writeonly [[Q]]) #[[ATTR16:[0-9]+]]
+; ATTRIBUTOR-NEXT:    ret void
 ;
   call void @c2(ptr %q)
   ret void
 }
 
 define i1 @c4(ptr %q, i32 %bitno) {
-; CHECK-LABEL: define i1 @c4
-; CHECK-SAME: (ptr [[Q:%.*]], i32 [[BITNO:%.*]]) #[[ATTR0]] {
-; CHECK-NEXT:    [[TMP:%.*]] = ptrtoint ptr [[Q]] to i32
-; CHECK-NEXT:    [[TMP2:%.*]] = lshr i32 [[TMP]], [[BITNO]]
-; CHECK-NEXT:    [[BIT:%.*]] = trunc i32 [[TMP2]] to i1
-; CHECK-NEXT:    br i1 [[BIT]], label [[L1:%.*]], label [[L0:%.*]]
-; CHECK:       l0:
-; CHECK-NEXT:    ret i1 false
-; CHECK:       l1:
-; CHECK-NEXT:    ret i1 true
+; FNATTRS: Function Attrs: mustprogress nofree norecurse nosync nounwind willreturn memory(none)
+; FNATTRS-LABEL: define i1 @c4
+; FNATTRS-SAME: (ptr [[Q:%.*]], i32 [[BITNO:%.*]]) #[[ATTR0]] {
+; FNATTRS-NEXT:    [[TMP:%.*]] = ptrtoint ptr [[Q]] to i32
+; FNATTRS-NEXT:    [[TMP2:%.*]] = lshr i32 [[TMP]], [[BITNO]]
+; FNATTRS-NEXT:    [[BIT:%.*]] = trunc i32 [[TMP2]] to i1
+; FNATTRS-NEXT:    br i1 [[BIT]], label [[L1:%.*]], label [[L0:%.*]]
+; FNATTRS:       l0:
+; FNATTRS-NEXT:    ret i1 false
+; FNATTRS:       l1:
+; FNATTRS-NEXT:    ret i1 true
+;
+; ATTRIBUTOR: Function Attrs: mustprogress nofree norecurse nosync nounwind willreturn memory(none)
+; ATTRIBUTOR-LABEL: define i1 @c4
+; ATTRIBUTOR-SAME: (ptr nofree readnone [[Q:%.*]], i32 [[BITNO:%.*]]) #[[ATTR0]] {
+; ATTRIBUTOR-NEXT:    [[TMP:%.*]] = ptrtoint ptr [[Q]] to i32
+; ATTRIBUTOR-NEXT:    [[TMP2:%.*]] = lshr i32 [[TMP]], [[BITNO]]
+; ATTRIBUTOR-NEXT:    [[BIT:%.*]] = trunc i32 [[TMP2]] to i1
+; ATTRIBUTOR-NEXT:    br i1 [[BIT]], label [[L1:%.*]], label [[L0:%.*]]
+; ATTRIBUTOR:       l0:
+; ATTRIBUTOR-NEXT:    ret i1 false
+; ATTRIBUTOR:       l1:
+; ATTRIBUTOR-NEXT:    ret i1 true
 ;
   %tmp = ptrtoint ptr %q to i32
   %tmp2 = lshr i32 %tmp, %bitno
@@ -56,16 +90,29 @@ l1:
 
 ; c4b is c4 but without the escaping part
 define i1 @c4b(ptr %q, i32 %bitno) {
-; CHECK-LABEL: define i1 @c4b
-; CHECK-SAME: (ptr [[Q:%.*]], i32 [[BITNO:%.*]]) #[[ATTR0]] {
-; CHECK-NEXT:    [[TMP:%.*]] = ptrtoint ptr [[Q]] to i32
-; CHECK-NEXT:    [[TMP2:%.*]] = lshr i32 [[TMP]], [[BITNO]]
-; CHECK-NEXT:    [[BIT:%.*]] = trunc i32 [[TMP2]] to i1
-; CHECK-NEXT:    br i1 [[BIT]], label [[L1:%.*]], label [[L0:%.*]]
-; CHECK:       l0:
-; CHECK-NEXT:    ret i1 false
-; CHECK:       l1:
-; CHECK-NEXT:    ret i1 false
+; FNATTRS: Function Attrs: mustprogress nofree norecurse nosync nounwind willreturn memory(none)
+; FNATTRS-LABEL: define i1 @c4b
+; FNATTRS-SAME: (ptr [[Q:%.*]], i32 [[BITNO:%.*]]) #[[ATTR0]] {
+; FNATTRS-NEXT:    [[TMP:%.*]] = ptrtoint ptr [[Q]] to i32
+; FNATTRS-NEXT:    [[TMP2:%.*]] = lshr i32 [[TMP]], [[BITNO]]
+; FNATTRS-NEXT:    [[BIT:%.*]] = trunc i32 [[TMP2]] to i1
+; FNATTRS-NEXT:    br i1 [[BIT]], label [[L1:%.*]], label [[L0:%.*]]
+; FNATTRS:       l0:
+; FNATTRS-NEXT:    ret i1 false
+; FNATTRS:       l1:
+; FNATTRS-NEXT:    ret i1 false
+;
+; ATTRIBUTOR: Function Attrs: mustprogress nofree norecurse nosync nounwind willreturn memory(none)
+; ATTRIBUTOR-LABEL: define i1 @c4b
+; ATTRIBUTOR-SAME: (ptr nofree readnone [[Q:%.*]], i32 [[BITNO:%.*]]) #[[ATTR0]] {
+; ATTRIBUTOR-NEXT:    [[TMP:%.*]] = ptrtoint ptr [[Q]] to i32
+; ATTRIBUTOR-NEXT:    [[TMP2:%.*]] = lshr i32 [[TMP]], [[BITNO]]
+; ATTRIBUTOR-NEXT:    [[BIT:%.*]] = trunc i32 [[TMP2]] to i1
+; ATTRIBUTOR-NEXT:    br i1 [[BIT]], label [[L1:%.*]], label [[L0:%.*]]
+; ATTRIBUTOR:       l0:
+; ATTRIBUTOR-NEXT:    ret i1 false
+; ATTRIBUTOR:       l1:
+; ATTRIBUTOR-NEXT:    ret i1 false
 ;
   %tmp = ptrtoint ptr %q to i32
   %tmp2 = lshr i32 %tmp, %bitno
@@ -80,14 +127,25 @@ l1:
 @lookup_table = global [2 x i1] [ i1 0, i1 1 ]
 
 define i1 @c5(ptr %q, i32 %bitno) {
-; CHECK-LABEL: define i1 @c5
-; CHECK-SAME: (ptr [[Q:%.*]], i32 [[BITNO:%.*]]) #[[ATTR3:[0-9]+]] {
-; CHECK-NEXT:    [[TMP:%.*]] = ptrtoint ptr [[Q]] to i32
-; CHECK-NEXT:    [[TMP2:%.*]] = lshr i32 [[TMP]], [[BITNO]]
-; CHECK-NEXT:    [[BIT:%.*]] = and i32 [[TMP2]], 1
-; CHECK-NEXT:    [[LOOKUP:%.*]] = getelementptr [2 x i1], ptr @lookup_table, i32 0, i32 [[BIT]]
-; CHECK-NEXT:    [[VAL:%.*]] = load i1, ptr [[LOOKUP]], align 1
-; CHECK-NEXT:    ret i1 [[VAL]]
+; FNATTRS: Function Attrs: mustprogress nofree norecurse nosync nounwind willreturn memory(read, argmem: none, inaccessiblemem: none)
+; FNATTRS-LABEL: define i1 @c5
+; FNATTRS-SAME: (ptr [[Q:%.*]], i32 [[BITNO:%.*]]) #[[ATTR3:[0-9]+]] {
+; FNATTRS-NEXT:    [[TMP:%.*]] = ptrtoint ptr [[Q]] to i32
+; FNATTRS-NEXT:    [[TMP2:%.*]] = lshr i32 [[TMP]], [[BITNO]]
+; FNATTRS-NEXT:    [[BIT:%.*]] = and i32 [[TMP2]], 1
+; FNATTRS-NEXT:    [[LOOKUP:%.*]] = getelementptr [2 x i1], ptr @lookup_table, i32 0, i32 [[BIT]]
+; FNATTRS-NEXT:    [[VAL:%.*]] = load i1, ptr [[LOOKUP]], align 1
+; FNATTRS-NEXT:    ret i1 [[VAL]]
+;
+; ATTRIBUTOR: Function Attrs: mustprogress nofree norecurse nosync nounwind willreturn memory(read)
+; ATTRIBUTOR-LABEL: define i1 @c5
+; ATTRIBUTOR-SAME: (ptr nofree readonly [[Q:%.*]], i32 [[BITNO:%.*]]) #[[ATTR2:[0-9]+]] {
+; ATTRIBUTOR-NEXT:    [[TMP:%.*]] = ptrtoint ptr [[Q]] to i32
+; ATTRIBUTOR-NEXT:    [[TMP2:%.*]] = lshr i32 [[TMP]], [[BITNO]]
+; ATTRIBUTOR-NEXT:    [[BIT:%.*]] = and i32 [[TMP2]], 1
+; ATTRIBUTOR-NEXT:    [[LOOKUP:%.*]] = getelementptr [2 x i1], ptr @lookup_table, i32 0, i32 [[BIT]]
+; ATTRIBUTOR-NEXT:    [[VAL:%.*]] = load i1, ptr [[LOOKUP]], align 1
+; ATTRIBUTOR-NEXT:    ret i1 [[VAL]]
 ;
   %tmp = ptrtoint ptr %q to i32
   %tmp2 = lshr i32 %tmp, %bitno
@@ -101,16 +159,29 @@ define i1 @c5(ptr %q, i32 %bitno) {
 declare void @throw_if_bit_set(ptr, i8) readonly
 
 define i1 @c6(ptr %q, i8 %bit) personality ptr @__gxx_personality_v0 {
-; CHECK-LABEL: define i1 @c6
-; CHECK-SAME: (ptr readonly [[Q:%.*]], i8 [[BIT:%.*]]) #[[ATTR5:[0-9]+]] personality ptr @__gxx_personality_v0 {
-; CHECK-NEXT:    invoke void @throw_if_bit_set(ptr [[Q]], i8 [[BIT]])
-; CHECK-NEXT:    to label [[RET0:%.*]] unwind label [[RET1:%.*]]
-; CHECK:       ret0:
-; CHECK-NEXT:    ret i1 false
-; CHECK:       ret1:
-; CHECK-NEXT:    [[EXN:%.*]] = landingpad { ptr, i32 }
-; CHECK-NEXT:    cleanup
-; CHECK-NEXT:    ret i1 true
+; FNATTRS: Function Attrs: nofree memory(read)
+; FNATTRS-LABEL: define i1 @c6
+; FNATTRS-SAME: (ptr readonly [[Q:%.*]], i8 [[BIT:%.*]]) #[[ATTR5:[0-9]+]] personality ptr @__gxx_personality_v0 {
+; FNATTRS-NEXT:    invoke void @throw_if_bit_set(ptr [[Q]], i8 [[BIT]])
+; FNATTRS-NEXT:    to label [[RET0:%.*]] unwind label [[RET1:%.*]]
+; FNATTRS:       ret0:
+; FNATTRS-NEXT:    ret i1 false
+; FNATTRS:       ret1:
+; FNATTRS-NEXT:    [[EXN:%.*]] = landingpad { ptr, i32 }
+; FNATTRS-NEXT:    cleanup
+; FNATTRS-NEXT:    ret i1 true
+;
+; ATTRIBUTOR: Function Attrs: nosync memory(read)
+; ATTRIBUTOR-LABEL: define i1 @c6
+; ATTRIBUTOR-SAME: (ptr readonly [[Q:%.*]], i8 [[BIT:%.*]]) #[[ATTR4:[0-9]+]] personality ptr @__gxx_personality_v0 {
+; ATTRIBUTOR-NEXT:    invoke void @throw_if_bit_set(ptr [[Q]], i8 [[BIT]]) #[[ATTR4]]
+; ATTRIBUTOR-NEXT:    to label [[RET0:%.*]] unwind label [[RET1:%.*]]
+; ATTRIBUTOR:       ret0:
+; ATTRIBUTOR-NEXT:    ret i1 false
+; ATTRIBUTOR:       ret1:
+; ATTRIBUTOR-NEXT:    [[EXN:%.*]] = landingpad { ptr, i32 }
+; ATTRIBUTOR-NEXT:    cleanup
+; ATTRIBUTOR-NEXT:    ret i1 true
 ;
   invoke void @throw_if_bit_set(ptr %q, i8 %bit)
   to label %ret0 unwind label %ret1
@@ -125,13 +196,23 @@ ret1:
 declare i32 @__gxx_personality_v0(...)
 
 define ptr @lookup_bit(ptr %q, i32 %bitno) readnone nounwind {
-; CHECK-LABEL: define nonnull ptr @lookup_bit
-; CHECK-SAME: (ptr [[Q:%.*]], i32 [[BITNO:%.*]]) #[[ATTR0]] {
-; CHECK-NEXT:    [[TMP:%.*]] = ptrtoint ptr [[Q]] to i32
-; CHECK-NEXT:    [[TMP2:%.*]] = lshr i32 [[TMP]], [[BITNO]]
-; CHECK-NEXT:    [[BIT:%.*]] = and i32 [[TMP2]], 1
-; CHECK-NEXT:    [[LOOKUP:%.*]] = getelementptr [2 x i1], ptr @lookup_table, i32 0, i32 [[BIT]]
-; CHECK-NEXT:    ret ptr [[LOOKUP]]
+; FNATTRS: Function Attrs: mustprogress nofree norecurse nosync nounwind willreturn memory(none)
+; FNATTRS-LABEL: define nonnull ptr @lookup_bit
+; FNATTRS-SAME: (ptr [[Q:%.*]], i32 [[BITNO:%.*]]) #[[ATTR0]] {
+; FNATTRS-NEXT:    [[TMP:%.*]] = ptrtoint ptr [[Q]] to i32
+; FNATTRS-NEXT:    [[TMP2:%.*]] = lshr i32 [[TMP]], [[BITNO]]
+; FNATTRS-NEXT:    [[BIT:%.*]] = and i32 [[TMP2]], 1
+; FNATTRS-NEXT:    [[LOOKUP:%.*]] = getelementptr [2 x i1], ptr @lookup_table, i32 0, i32 [[BIT]]
+; FNATTRS-NEXT:    ret ptr [[LOOKUP]]
+;
+; ATTRIBUTOR: Function Attrs: mustprogress nofree norecurse nosync nounwind willreturn memory(none)
+; ATTRIBUTOR-LABEL: define ptr @lookup_bit
+; ATTRIBUTOR-SAME: (ptr nofree readnone [[Q:%.*]], i32 [[BITNO:%.*]]) #[[ATTR0]] {
+; ATTRIBUTOR-NEXT:    [[TMP:%.*]] = ptrtoint ptr [[Q]] to i32
+; ATTRIBUTOR-NEXT:    [[TMP2:%.*]] = lshr i32 [[TMP]], [[BITNO]]
+; ATTRIBUTOR-NEXT:    [[BIT:%.*]] = and i32 [[TMP2]], 1
+; ATTRIBUTOR-NEXT:    [[LOOKUP:%.*]] = getelementptr [2 x i1], ptr @lookup_table, i32 0, i32 [[BIT]]
+; ATTRIBUTOR-NEXT:    ret ptr [[LOOKUP]]
 ;
   %tmp = ptrtoint ptr %q to i32
   %tmp2 = lshr i32 %tmp, %bitno
@@ -141,11 +222,19 @@ define ptr @lookup_bit(ptr %q, i32 %bitno) readnone nounwind {
 }
 
 define i1 @c7(ptr %q, i32 %bitno) {
-; CHECK-LABEL: define i1 @c7
-; CHECK-SAME: (ptr readonly [[Q:%.*]], i32 [[BITNO:%.*]]) #[[ATTR6:[0-9]+]] {
-; CHECK-NEXT:    [[PTR:%.*]] = call ptr @lookup_bit(ptr [[Q]], i32 [[BITNO]])
-; CHECK-NEXT:    [[VAL:%.*]] = load i1, ptr [[PTR]], align 1
-; CHECK-NEXT:    ret i1 [[VAL]]
+; FNATTRS: Function Attrs: mustprogress nofree norecurse nosync nounwind willreturn memory(read, inaccessiblemem: none)
+; FNATTRS-LABEL: define i1 @c7
+; FNATTRS-SAME: (ptr readonly [[Q:%.*]], i32 [[BITNO:%.*]]) #[[ATTR6:[0-9]+]] {
+; FNATTRS-NEXT:    [[PTR:%.*]] = call ptr @lookup_bit(ptr [[Q]], i32 [[BITNO]])
+; FNATTRS-NEXT:    [[VAL:%.*]] = load i1, ptr [[PTR]], align 1
+; FNATTRS-NEXT:    ret i1 [[VAL]]
+;
+; ATTRIBUTOR: Function Attrs: mustprogress nofree norecurse nosync nounwind willreturn memory(read)
+; ATTRIBUTOR-LABEL: define i1 @c7
+; ATTRIBUTOR-SAME: (ptr nofree readonly [[Q:%.*]], i32 [[BITNO:%.*]]) #[[ATTR2]] {
+; ATTRIBUTOR-NEXT:    [[PTR:%.*]] = call ptr @lookup_bit(ptr nofree readnone [[Q]], i32 [[BITNO]]) #[[ATTR17:[0-9]+]]
+; ATTRIBUTOR-NEXT:    [[VAL:%.*]] = load i1, ptr [[PTR]], align 1
+; ATTRIBUTOR-NEXT:    ret i1 [[VAL]]
 ;
   %ptr = call ptr @lookup_bit(ptr %q, i32 %bitno)
   %val = load i1, ptr %ptr
@@ -154,18 +243,33 @@ define i1 @c7(ptr %q, i32 %bitno) {
 
 
 define i32 @nc1(ptr %q, ptr %p, i1 %b) {
-; CHECK-LABEL: define i32 @nc1
-; CHECK-SAME: (ptr [[Q:%.*]], ptr nocapture [[P:%.*]], i1 [[B:%.*]]) #[[ATTR7:[0-9]+]] {
-; CHECK-NEXT:  e:
-; CHECK-NEXT:    br label [[L:%.*]]
-; CHECK:       l:
-; CHECK-NEXT:    [[X:%.*]] = phi ptr [ [[P]], [[E:%.*]] ]
-; CHECK-NEXT:    [[Y:%.*]] = phi ptr [ [[Q]], [[E]] ]
-; CHECK-NEXT:    [[TMP2:%.*]] = select i1 [[B]], ptr [[X]], ptr [[Y]]
-; CHECK-NEXT:    [[VAL:%.*]] = load i32, ptr [[TMP2]], align 4
-; CHECK-NEXT:    store i32 0, ptr [[X]], align 4
-; CHECK-NEXT:    store ptr [[Y]], ptr @g, align 8
-; CHECK-NEXT:    ret i32 [[VAL]]
+; FNATTRS: Function Attrs: mustprogress nofree norecurse nosync nounwind willreturn memory(readwrite, inaccessiblemem: none)
+; FNATTRS-LABEL: define i32 @nc1
+; FNATTRS-SAME: (ptr [[Q:%.*]], ptr nocapture [[P:%.*]], i1 [[B:%.*]]) #[[ATTR7:[0-9]+]] {
+; FNATTRS-NEXT:  e:
+; FNATTRS-NEXT:    br label [[L:%.*]]
+; FNATTRS:       l:
+; FNATTRS-NEXT:    [[X:%.*]] = phi ptr [ [[P]], [[E:%.*]] ]
+; FNATTRS-NEXT:    [[Y:%.*]] = phi ptr [ [[Q]], [[E]] ]
+; FNATTRS-NEXT:    [[TMP2:%.*]] = select i1 [[B]], ptr [[X]], ptr [[Y]]
+; FNATTRS-NEXT:    [[VAL:%.*]] = load i32, ptr [[TMP2]], align 4
+; FNATTRS-NEXT:    store i32 0, ptr [[X]], align 4
+; FNATTRS-NEXT:    store ptr [[Y]], ptr @g, align 8
+; FNATTRS-NEXT:    ret i32 [[VAL]]
+;
+; ATTRIBUTOR: Function Attrs: mustprogress nofree norecurse nosync nounwind willreturn
+; ATTRIBUTOR-LABEL: define i32 @nc1
+; ATTRIBUTOR-SAME: (ptr nofree [[Q:%.*]], ptr nocapture nofree [[P:%.*]], i1 [[B:%.*]]) #[[ATTR5:[0-9]+]] {
+; ATTRIBUTOR-NEXT:  e:
+; ATTRIBUTOR-NEXT:    br label [[L:%.*]]
+; ATTRIBUTOR:       l:
+; ATTRIBUTOR-NEXT:    [[X:%.*]] = phi ptr [ [[P]], [[E:%.*]] ]
+; ATTRIBUTOR-NEXT:    [[Y:%.*]] = phi ptr [ [[Q]], [[E]] ]
+; ATTRIBUTOR-NEXT:    [[TMP2:%.*]] = select i1 [[B]], ptr [[X]], ptr [[Y]]
+; ATTRIBUTOR-NEXT:    [[VAL:%.*]] = load i32, ptr [[TMP2]], align 4
+; ATTRIBUTOR-NEXT:    store i32 0, ptr [[X]], align 4
+; ATTRIBUTOR-NEXT:    store ptr [[Y]], ptr @g, align 8
+; ATTRIBUTOR-NEXT:    ret i32 [[VAL]]
 ;
 e:
   br label %l
@@ -180,19 +284,35 @@ l:
 }
 
 define i32 @nc1_addrspace(ptr %q, ptr addrspace(1) %p, i1 %b) {
-; CHECK-LABEL: define i32 @nc1_addrspace
-; CHECK-SAME: (ptr [[Q:%.*]], ptr addrspace(1) nocapture [[P:%.*]], i1 [[B:%.*]]) #[[ATTR7]] {
-; CHECK-NEXT:  e:
-; CHECK-NEXT:    br label [[L:%.*]]
-; CHECK:       l:
-; CHECK-NEXT:    [[X:%.*]] = phi ptr addrspace(1) [ [[P]], [[E:%.*]] ]
-; CHECK-NEXT:    [[Y:%.*]] = phi ptr [ [[Q]], [[E]] ]
-; CHECK-NEXT:    [[TMP:%.*]] = addrspacecast ptr addrspace(1) [[X]] to ptr
-; CHECK-NEXT:    [[TMP2:%.*]] = select i1 [[B]], ptr [[TMP]], ptr [[Y]]
-; CHECK-NEXT:    [[VAL:%.*]] = load i32, ptr [[TMP2]], align 4
-; CHECK-NEXT:    store i32 0, ptr [[TMP]], align 4
-; CHECK-NEXT:    store ptr [[Y]], ptr @g, align 8
-; CHECK-NEXT:    ret i32 [[VAL]]
+; FNATTRS: Function Attrs: mustprogress nofree norecurse nosync nounwind willreturn memory(readwrite, inaccessiblemem: none)
+; FNATTRS-LABEL: define i32 @nc1_addrspace
+; FNATTRS-SAME: (ptr [[Q:%.*]], ptr addrspace(1) nocapture [[P:%.*]], i1 [[B:%.*]]) #[[ATTR7]] {
+; FNATTRS-NEXT:  e:
+; FNATTRS-NEXT:    br label [[L:%.*]]
+; FNATTRS:       l:
+; FNATTRS-NEXT:    [[X:%.*]] = phi ptr addrspace(1) [ [[P]], [[E:%.*]] ]
+; FNATTRS-NEXT:    [[Y:%.*]] = phi ptr [ [[Q]], [[E]] ]
+; FNATTRS-NEXT:    [[TMP:%.*]] = addrspacecast ptr addrspace(1) [[X]] to ptr
+; FNATTRS-NEXT:    [[TMP2:%.*]] = select i1 [[B]], ptr [[TMP]], ptr [[Y]]
+; FNATTRS-NEXT:    [[VAL:%.*]] = load i32, ptr [[TMP2]], align 4
+; FNATTRS-NEXT:    store i32 0, ptr [[TMP]], align 4
+; FNATTRS-NEXT:    store ptr [[Y]], ptr @g, align 8
+; FNATTRS-NEXT:    ret i32 [[VAL]]
+;
+; ATTRIBUTOR: Function Attrs: mustprogress nofree norecurse nosync nounwind willreturn
+; ATTRIBUTOR-LABEL: define i32 @nc1_addrspace
+; ATTRIBUTOR-SAME: (ptr nofree [[Q:%.*]], ptr addrspace(1) nocapture nofree [[P:%.*]], i1 [[B:%.*]]) #[[ATTR5]] {
+; ATTRIBUTOR-NEXT:  e:
+; ATTRIBUTOR-NEXT:    br label [[L:%.*]]
+; ATTRIBUTOR:       l:
+; ATTRIBUTOR-NEXT:    [[X:%.*]] = phi ptr addrspace(1) [ [[P]], [[E:%.*]] ]
+; ATTRIBUTOR-NEXT:    [[Y:%.*]] = phi ptr [ [[Q]], [[E]] ]
+; ATTRIBUTOR-NEXT:    [[TMP:%.*]] = addrspacecast ptr addrspace(1) [[X]] to ptr
+; ATTRIBUTOR-NEXT:    [[TMP2:%.*]] = select i1 [[B]], ptr [[TMP]], ptr [[Y]]
+; ATTRIBUTOR-NEXT:    [[VAL:%.*]] = load i32, ptr [[TMP2]], align 4
+; ATTRIBUTOR-NEXT:    store i32 0, ptr [[TMP]], align 4
+; ATTRIBUTOR-NEXT:    store ptr [[Y]], ptr @g, align 8
+; ATTRIBUTOR-NEXT:    ret i32 [[VAL]]
 ;
 e:
   br label %l
@@ -208,10 +328,17 @@ l:
 }
 
 define void @nc2(ptr %p, ptr %q) {
-; CHECK-LABEL: define void @nc2
-; CHECK-SAME: (ptr nocapture [[P:%.*]], ptr [[Q:%.*]]) #[[ATTR7]] {
-; CHECK-NEXT:    [[TMP1:%.*]] = call i32 @nc1(ptr [[Q]], ptr [[P]], i1 false)
-; CHECK-NEXT:    ret void
+; FNATTRS: Function Attrs: mustprogress nofree norecurse nosync nounwind willreturn memory(readwrite, inaccessiblemem: none)
+; FNATTRS-LABEL: define void @nc2
+; FNATTRS-SAME: (ptr nocapture [[P:%.*]], ptr [[Q:%.*]]) #[[ATTR7]] {
+; FNATTRS-NEXT:    [[TMP1:%.*]] = call i32 @nc1(ptr [[Q]], ptr [[P]], i1 false)
+; FNATTRS-NEXT:    ret void
+;
+; ATTRIBUTOR: Function Attrs: mustprogress nofree norecurse nosync nounwind willreturn
+; ATTRIBUTOR-LABEL: define void @nc2
+; ATTRIBUTOR-SAME: (ptr nocapture nofree [[P:%.*]], ptr nofree [[Q:%.*]]) #[[ATTR5]] {
+; ATTRIBUTOR-NEXT:    [[TMP1:%.*]] = call i32 @nc1(ptr nofree [[Q]], ptr nocapture nofree [[P]], i1 false) #[[ATTR18:[0-9]+]]
+; ATTRIBUTOR-NEXT:    ret void
 ;
   %1 = call i32 @nc1(ptr %q, ptr %p, i1 0)		; <i32> [#uses=0]
   ret void
@@ -219,10 +346,15 @@ define void @nc2(ptr %p, ptr %q) {
 
 
 define void @nc3(ptr %p) {
-; CHECK-LABEL: define void @nc3
-; CHECK-SAME: (ptr nocapture readonly [[P:%.*]]) {
-; CHECK-NEXT:    call void [[P]]()
-; CHECK-NEXT:    ret void
+; FNATTRS-LABEL: define void @nc3
+; FNATTRS-SAME: (ptr nocapture readonly [[P:%.*]]) {
+; FNATTRS-NEXT:    call void [[P]]()
+; FNATTRS-NEXT:    ret void
+;
+; ATTRIBUTOR-LABEL: define void @nc3
+; ATTRIBUTOR-SAME: (ptr nocapture nofree nonnull [[P:%.*]]) {
+; ATTRIBUTOR-NEXT:    call void [[P]]()
+; ATTRIBUTOR-NEXT:    ret void
 ;
   call void %p()
   ret void
@@ -230,21 +362,34 @@ define void @nc3(ptr %p) {
 
 declare void @external(ptr) readonly nounwind
 define void @nc4(ptr %p) {
-; CHECK-LABEL: define void @nc4
-; CHECK-SAME: (ptr nocapture readonly [[P:%.*]]) #[[ATTR9:[0-9]+]] {
-; CHECK-NEXT:    call void @external(ptr [[P]])
-; CHECK-NEXT:    ret void
+; FNATTRS: Function Attrs: nofree nounwind memory(read)
+; FNATTRS-LABEL: define void @nc4
+; FNATTRS-SAME: (ptr nocapture readonly [[P:%.*]]) #[[ATTR9:[0-9]+]] {
+; FNATTRS-NEXT:    call void @external(ptr [[P]])
+; FNATTRS-NEXT:    ret void
+;
+; ATTRIBUTOR: Function Attrs: nosync nounwind memory(read)
+; ATTRIBUTOR-LABEL: define void @nc4
+; ATTRIBUTOR-SAME: (ptr nocapture readonly [[P:%.*]]) #[[ATTR7:[0-9]+]] {
+; ATTRIBUTOR-NEXT:    call void @external(ptr nocapture readonly [[P]]) #[[ATTR4]]
+; ATTRIBUTOR-NEXT:    ret void
 ;
   call void @external(ptr %p)
   ret void
 }
 
 define void @nc5(ptr %f, ptr %p) {
-; CHECK-LABEL: define void @nc5
-; CHECK-SAME: (ptr nocapture readonly [[F:%.*]], ptr nocapture [[P:%.*]]) {
-; CHECK-NEXT:    call void [[F]](ptr [[P]]) #[[ATTR8:[0-9]+]]
-; CHECK-NEXT:    call void [[F]](ptr nocapture [[P]])
-; CHECK-NEXT:    ret void
+; FNATTRS-LABEL: define void @nc5
+; FNATTRS-SAME: (ptr nocapture readonly [[F:%.*]], ptr nocapture [[P:%.*]]) {
+; FNATTRS-NEXT:    call void [[F]](ptr [[P]]) #[[ATTR8:[0-9]+]]
+; FNATTRS-NEXT:    call void [[F]](ptr nocapture [[P]])
+; FNATTRS-NEXT:    ret void
+;
+; ATTRIBUTOR-LABEL: define void @nc5
+; ATTRIBUTOR-SAME: (ptr nocapture nofree nonnull [[F:%.*]], ptr nocapture [[P:%.*]]) {
+; ATTRIBUTOR-NEXT:    call void [[F]](ptr [[P]]) #[[ATTR6:[0-9]+]]
+; ATTRIBUTOR-NEXT:    call void [[F]](ptr nocapture [[P]])
+; ATTRIBUTOR-NEXT:    ret void
 ;
   call void %f(ptr %p) readonly nounwind
   call void %f(ptr nocapture %p)
@@ -253,11 +398,19 @@ define void @nc5(ptr %f, ptr %p) {
 
 ; It would be acceptable to add readnone to %y1_1 and %y1_2.
 define void @test1_1(ptr %x1_1, ptr %y1_1, i1 %c) {
-; CHECK-LABEL: define void @test1_1
-; CHECK-SAME: (ptr nocapture readnone [[X1_1:%.*]], ptr [[Y1_1:%.*]], i1 [[C:%.*]]) #[[ATTR10:[0-9]+]] {
-; CHECK-NEXT:    [[TMP1:%.*]] = call ptr @test1_2(ptr [[X1_1]], ptr [[Y1_1]], i1 [[C]])
-; CHECK-NEXT:    store ptr null, ptr @g, align 8
-; CHECK-NEXT:    ret void
+; FNATTRS: Function Attrs: nofree nosync nounwind memory(write, argmem: none, inaccessiblemem: none)
+; FNATTRS-LABEL: define void @test1_1
+; FNATTRS-SAME: (ptr nocapture readnone [[X1_1:%.*]], ptr [[Y1_1:%.*]], i1 [[C:%.*]]) #[[ATTR10:[0-9]+]] {
+; FNATTRS-NEXT:    [[TMP1:%.*]] = call ptr @test1_2(ptr [[X1_1]], ptr [[Y1_1]], i1 [[C]])
+; FNATTRS-NEXT:    store ptr null, ptr @g, align 8
+; FNATTRS-NEXT:    ret void
+;
+; ATTRIBUTOR: Function Attrs: nofree nosync nounwind memory(write)
+; ATTRIBUTOR-LABEL: define void @test1_1
+; ATTRIBUTOR-SAME: (ptr nocapture nofree readnone [[X1_1:%.*]], ptr nocapture nofree readnone [[Y1_1:%.*]], i1 [[C:%.*]]) #[[ATTR8:[0-9]+]] {
+; ATTRIBUTOR-NEXT:    [[TMP1:%.*]] = call ptr @test1_2(ptr nocapture nofree readnone [[X1_1]], ptr nofree readnone [[Y1_1]], i1 [[C]]) #[[ATTR8]]
+; ATTRIBUTOR-NEXT:    store ptr null, ptr @g, align 8
+; ATTRIBUTOR-NEXT:    ret void
 ;
   call ptr @test1_2(ptr %x1_1, ptr %y1_1, i1 %c)
   store ptr null, ptr @g
@@ -265,15 +418,27 @@ define void @test1_1(ptr %x1_1, ptr %y1_1, i1 %c) {
 }
 
 define ptr @test1_2(ptr %x1_2, ptr %y1_2, i1 %c) {
-; CHECK-LABEL: define ptr @test1_2
-; CHECK-SAME: (ptr nocapture readnone [[X1_2:%.*]], ptr returned [[Y1_2:%.*]], i1 [[C:%.*]]) #[[ATTR10]] {
-; CHECK-NEXT:    br i1 [[C]], label [[T:%.*]], label [[F:%.*]]
-; CHECK:       t:
-; CHECK-NEXT:    call void @test1_1(ptr [[X1_2]], ptr [[Y1_2]], i1 [[C]])
-; CHECK-NEXT:    store ptr null, ptr @g, align 8
-; CHECK-NEXT:    br label [[F]]
-; CHECK:       f:
-; CHECK-NEXT:    ret ptr [[Y1_2]]
+; FNATTRS: Function Attrs: nofree nosync nounwind memory(write, argmem: none, inaccessiblemem: none)
+; FNATTRS-LABEL: define ptr @test1_2
+; FNATTRS-SAME: (ptr nocapture readnone [[X1_2:%.*]], ptr returned [[Y1_2:%.*]], i1 [[C:%.*]]) #[[ATTR10]] {
+; FNATTRS-NEXT:    br i1 [[C]], label [[T:%.*]], label [[F:%.*]]
+; FNATTRS:       t:
+; FNATTRS-NEXT:    call void @test1_1(ptr [[X1_2]], ptr [[Y1_2]], i1 [[C]])
+; FNATTRS-NEXT:    store ptr null, ptr @g, align 8
+; FNATTRS-NEXT:    br label [[F]]
+; FNATTRS:       f:
+; FNATTRS-NEXT:    ret ptr [[Y1_2]]
+;
+; ATTRIBUTOR: Function Attrs: nofree nosync nounwind memory(write)
+; ATTRIBUTOR-LABEL: define ptr @test1_2
+; ATTRIBUTOR-SAME: (ptr nocapture nofree readnone [[X1_2:%.*]], ptr nofree readnone [[Y1_2:%.*]], i1 [[C:%.*]]) #[[ATTR8]] {
+; ATTRIBUTOR-NEXT:    br i1 [[C]], label [[T:%.*]], label [[F:%.*]]
+; ATTRIBUTOR:       t:
+; ATTRIBUTOR-NEXT:    call void @test1_1(ptr nocapture nofree readnone [[X1_2]], ptr nocapture nofree readnone [[Y1_2]], i1 [[C]]) #[[ATTR8]]
+; ATTRIBUTOR-NEXT:    store ptr null, ptr @g, align 8
+; ATTRIBUTOR-NEXT:    br label [[F]]
+; ATTRIBUTOR:       f:
+; ATTRIBUTOR-NEXT:    ret ptr [[Y1_2]]
 ;
   br i1 %c, label %t, label %f
 t:
@@ -285,11 +450,19 @@ f:
 }
 
 define void @test2(ptr %x2) {
-; CHECK-LABEL: define void @test2
-; CHECK-SAME: (ptr nocapture readnone [[X2:%.*]]) #[[ATTR10]] {
-; CHECK-NEXT:    call void @test2(ptr [[X2]])
-; CHECK-NEXT:    store ptr null, ptr @g, align 8
-; CHECK-NEXT:    ret void
+; FNATTRS: Function Attrs: nofree nosync nounwind memory(write, argmem: none, inaccessiblemem: none)
+; FNATTRS-LABEL: define void @test2
+; FNATTRS-SAME: (ptr nocapture readnone [[X2:%.*]]) #[[ATTR10]] {
+; FNATTRS-NEXT:    call void @test2(ptr [[X2]])
+; FNATTRS-NEXT:    store ptr null, ptr @g, align 8
+; FNATTRS-NEXT:    ret void
+;
+; ATTRIBUTOR: Function Attrs: nofree nosync nounwind memory(write)
+; ATTRIBUTOR-LABEL: define void @test2
+; ATTRIBUTOR-SAME: (ptr nocapture nofree readnone [[X2:%.*]]) #[[ATTR8]] {
+; ATTRIBUTOR-NEXT:    call void @test2(ptr nocapture nofree readnone [[X2]]) #[[ATTR8]]
+; ATTRIBUTOR-NEXT:    store ptr null, ptr @g, align 8
+; ATTRIBUTOR-NEXT:    ret void
 ;
   call void @test2(ptr %x2)
   store ptr null, ptr @g
@@ -297,11 +470,19 @@ define void @test2(ptr %x2) {
 }
 
 define void @test3(ptr %x3, ptr %y3, ptr %z3) {
-; CHECK-LABEL: define void @test3
-; CHECK-SAME: (ptr nocapture readnone [[X3:%.*]], ptr nocapture readnone [[Y3:%.*]], ptr nocapture readnone [[Z3:%.*]]) #[[ATTR10]] {
-; CHECK-NEXT:    call void @test3(ptr [[Z3]], ptr [[Y3]], ptr [[X3]])
-; CHECK-NEXT:    store ptr null, ptr @g, align 8
-; CHECK-NEXT:    ret void
+; FNATTRS: Function Attrs: nofree nosync nounwind memory(write, argmem: none, inaccessiblemem: none)
+; FNATTRS-LABEL: define void @test3
+; FNATTRS-SAME: (ptr nocapture readnone [[X3:%.*]], ptr nocapture readnone [[Y3:%.*]], ptr nocapture readnone [[Z3:%.*]]) #[[ATTR10]] {
+; FNATTRS-NEXT:    call void @test3(ptr [[Z3]], ptr [[Y3]], ptr [[X3]])
+; FNATTRS-NEXT:    store ptr null, ptr @g, align 8
+; FNATTRS-NEXT:    ret void
+;
+; ATTRIBUTOR: Function Attrs: nofree nosync nounwind memory(write)
+; ATTRIBUTOR-LABEL: define void @test3
+; ATTRIBUTOR-SAME: (ptr nocapture nofree readnone [[X3:%.*]], ptr nocapture nofree readnone [[Y3:%.*]], ptr nocapture nofree readnone [[Z3:%.*]]) #[[ATTR8]] {
+; ATTRIBUTOR-NEXT:    call void @test3(ptr nocapture nofree readnone [[Z3]], ptr nocapture nofree readnone [[Y3]], ptr nocapture nofree readnone [[X3]]) #[[ATTR8]]
+; ATTRIBUTOR-NEXT:    store ptr null, ptr @g, align 8
+; ATTRIBUTOR-NEXT:    ret void
 ;
   call void @test3(ptr %z3, ptr %y3, ptr %x3)
   store ptr null, ptr @g
@@ -309,11 +490,19 @@ define void @test3(ptr %x3, ptr %y3, ptr %z3) {
 }
 
 define void @test4_1(ptr %x4_1, i1 %c) {
-; CHECK-LABEL: define void @test4_1
-; CHECK-SAME: (ptr [[X4_1:%.*]], i1 [[C:%.*]]) #[[ATTR10]] {
-; CHECK-NEXT:    [[TMP1:%.*]] = call ptr @test4_2(ptr [[X4_1]], ptr [[X4_1]], ptr [[X4_1]], i1 [[C]])
-; CHECK-NEXT:    store ptr null, ptr @g, align 8
-; CHECK-NEXT:    ret void
+; FNATTRS: Function Attrs: nofree nosync nounwind memory(write, argmem: none, inaccessiblemem: none)
+; FNATTRS-LABEL: define void @test4_1
+; FNATTRS-SAME: (ptr [[X4_1:%.*]], i1 [[C:%.*]]) #[[ATTR10]] {
+; FNATTRS-NEXT:    [[TMP1:%.*]] = call ptr @test4_2(ptr [[X4_1]], ptr [[X4_1]], ptr [[X4_1]], i1 [[C]])
+; FNATTRS-NEXT:    store ptr null, ptr @g, align 8
+; FNATTRS-NEXT:    ret void
+;
+; ATTRIBUTOR: Function Attrs: nofree nosync nounwind memory(write)
+; ATTRIBUTOR-LABEL: define void @test4_1
+; ATTRIBUTOR-SAME: (ptr nocapture nofree readnone [[X4_1:%.*]], i1 [[C:%.*]]) #[[ATTR8]] {
+; ATTRIBUTOR-NEXT:    [[TMP1:%.*]] = call ptr @test4_2(ptr nocapture nofree readnone [[X4_1]], ptr nofree readnone [[X4_1]], ptr nocapture nofree readnone [[X4_1]], i1 [[C]]) #[[ATTR8]]
+; ATTRIBUTOR-NEXT:    store ptr null, ptr @g, align 8
+; ATTRIBUTOR-NEXT:    ret void
 ;
   call ptr @test4_2(ptr %x4_1, ptr %x4_1, ptr %x4_1, i1 %c)
   store ptr null, ptr @g
@@ -321,15 +510,27 @@ define void @test4_1(ptr %x4_1, i1 %c) {
 }
 
 define ptr @test4_2(ptr %x4_2, ptr %y4_2, ptr %z4_2, i1 %c) {
-; CHECK-LABEL: define ptr @test4_2
-; CHECK-SAME: (ptr nocapture readnone [[X4_2:%.*]], ptr readnone returned [[Y4_2:%.*]], ptr nocapture readnone [[Z4_2:%.*]], i1 [[C:%.*]]) #[[ATTR10]] {
-; CHECK-NEXT:    br i1 [[C]], label [[T:%.*]], label [[F:%.*]]
-; CHECK:       t:
-; CHECK-NEXT:    call void @test4_1(ptr null, i1 [[C]])
-; CHECK-NEXT:    store ptr null, ptr @g, align 8
-; CHECK-NEXT:    br label [[F]]
-; CHECK:       f:
-; CHECK-NEXT:    ret ptr [[Y4_2]]
+; FNATTRS: Function Attrs: nofree nosync nounwind memory(write, argmem: none, inaccessiblemem: none)
+; FNATTRS-LABEL: define ptr @test4_2
+; FNATTRS-SAME: (ptr nocapture readnone [[X4_2:%.*]], ptr readnone returned [[Y4_2:%.*]], ptr nocapture readnone [[Z4_2:%.*]], i1 [[C:%.*]]) #[[ATTR10]] {
+; FNATTRS-NEXT:    br i1 [[C]], label [[T:%.*]], label [[F:%.*]]
+; FNATTRS:       t:
+; FNATTRS-NEXT:    call void @test4_1(ptr null, i1 [[C]])
+; FNATTRS-NEXT:    store ptr null, ptr @g, align 8
+; FNATTRS-NEXT:    br label [[F]]
+; FNATTRS:       f:
+; FNATTRS-NEXT:    ret ptr [[Y4_2]]
+;
+; ATTRIBUTOR: Function Attrs: nofree nosync nounwind memory(write)
+; ATTRIBUTOR-LABEL: define ptr @test4_2
+; ATTRIBUTOR-SAME: (ptr nocapture nofree readnone [[X4_2:%.*]], ptr nofree readnone [[Y4_2:%.*]], ptr nocapture nofree readnone [[Z4_2:%.*]], i1 [[C:%.*]]) #[[ATTR8]] {
+; ATTRIBUTOR-NEXT:    br i1 [[C]], label [[T:%.*]], label [[F:%.*]]
+; ATTRIBUTOR:       t:
+; ATTRIBUTOR-NEXT:    call void @test4_1(ptr nofree readnone null, i1 [[C]]) #[[ATTR8]]
+; ATTRIBUTOR-NEXT:    store ptr null, ptr @g, align 8
+; ATTRIBUTOR-NEXT:    br label [[F]]
+; ATTRIBUTOR:       f:
+; ATTRIBUTOR-NEXT:    ret ptr [[Y4_2]]
 ;
   br i1 %c, label %t, label %f
 t:
@@ -343,11 +544,11 @@ f:
 declare ptr @test5_1(ptr %x5_1)
 
 define void @test5_2(ptr %x5_2) {
-; CHECK-LABEL: define void @test5_2
-; CHECK-SAME: (ptr [[X5_2:%.*]]) {
-; CHECK-NEXT:    [[TMP1:%.*]] = call ptr @test5_1(ptr [[X5_2]])
-; CHECK-NEXT:    store ptr null, ptr @g, align 8
-; CHECK-NEXT:    ret void
+; COMMON-LABEL: define void @test5_2
+; COMMON-SAME: (ptr [[X5_2:%.*]]) {
+; COMMON-NEXT:    [[TMP1:%.*]] = call ptr @test5_1(ptr [[X5_2]])
+; COMMON-NEXT:    store ptr null, ptr @g, align 8
+; COMMON-NEXT:    ret void
 ;
   call ptr @test5_1(ptr %x5_2)
   store ptr null, ptr @g
@@ -357,11 +558,17 @@ define void @test5_2(ptr %x5_2) {
 declare void @test6_1(ptr %x6_1, ptr nocapture %y6_1, ...)
 
 define void @test6_2(ptr %x6_2, ptr %y6_2, ptr %z6_2) {
-; CHECK-LABEL: define void @test6_2
-; CHECK-SAME: (ptr [[X6_2:%.*]], ptr nocapture [[Y6_2:%.*]], ptr [[Z6_2:%.*]]) {
-; CHECK-NEXT:    call void (ptr, ptr, ...) @test6_1(ptr [[X6_2]], ptr [[Y6_2]], ptr [[Z6_2]])
-; CHECK-NEXT:    store ptr null, ptr @g, align 8
-; CHECK-NEXT:    ret void
+; FNATTRS-LABEL: define void @test6_2
+; FNATTRS-SAME: (ptr [[X6_2:%.*]], ptr nocapture [[Y6_2:%.*]], ptr [[Z6_2:%.*]]) {
+; FNATTRS-NEXT:    call void (ptr, ptr, ...) @test6_1(ptr [[X6_2]], ptr [[Y6_2]], ptr [[Z6_2]])
+; FNATTRS-NEXT:    store ptr null, ptr @g, align 8
+; FNATTRS-NEXT:    ret void
+;
+; ATTRIBUTOR-LABEL: define void @test6_2
+; ATTRIBUTOR-SAME: (ptr [[X6_2:%.*]], ptr nocapture [[Y6_2:%.*]], ptr [[Z6_2:%.*]]) {
+; ATTRIBUTOR-NEXT:    call void (ptr, ptr, ...) @test6_1(ptr [[X6_2]], ptr nocapture [[Y6_2]], ptr [[Z6_2]])
+; ATTRIBUTOR-NEXT:    store ptr null, ptr @g, align 8
+; ATTRIBUTOR-NEXT:    ret void
 ;
   call void (ptr, ptr, ...) @test6_1(ptr %x6_2, ptr %y6_2, ptr %z6_2)
   store ptr null, ptr @g
@@ -369,42 +576,72 @@ define void @test6_2(ptr %x6_2, ptr %y6_2, ptr %z6_2) {
 }
 
 define void @test_cmpxchg(ptr %p) {
-; CHECK-LABEL: define void @test_cmpxchg
-; CHECK-SAME: (ptr nocapture [[P:%.*]]) #[[ATTR11:[0-9]+]] {
-; CHECK-NEXT:    [[TMP1:%.*]] = cmpxchg ptr [[P]], i32 0, i32 1 acquire monotonic, align 4
-; CHECK-NEXT:    ret void
+; FNATTRS: Function Attrs: mustprogress nofree norecurse nounwind willreturn memory(argmem: readwrite)
+; FNATTRS-LABEL: define void @test_cmpxchg
+; FNATTRS-SAME: (ptr nocapture [[P:%.*]]) #[[ATTR11:[0-9]+]] {
+; FNATTRS-NEXT:    [[TMP1:%.*]] = cmpxchg ptr [[P]], i32 0, i32 1 acquire monotonic, align 4
+; FNATTRS-NEXT:    ret void
+;
+; ATTRIBUTOR: Function Attrs: mustprogress nofree norecurse nounwind willreturn memory(argmem: readwrite)
+; ATTRIBUTOR-LABEL: define void @test_cmpxchg
+; ATTRIBUTOR-SAME: (ptr nocapture nofree nonnull [[P:%.*]]) #[[ATTR9:[0-9]+]] {
+; ATTRIBUTOR-NEXT:    [[TMP1:%.*]] = cmpxchg ptr [[P]], i32 0, i32 1 acquire monotonic, align 4
+; ATTRIBUTOR-NEXT:    ret void
 ;
   cmpxchg ptr %p, i32 0, i32 1 acquire monotonic
   ret void
 }
 
 define void @test_cmpxchg_ptr(ptr %p, ptr %q) {
-; CHECK-LABEL: define void @test_cmpxchg_ptr
-; CHECK-SAME: (ptr nocapture [[P:%.*]], ptr [[Q:%.*]]) #[[ATTR11]] {
-; CHECK-NEXT:    [[TMP1:%.*]] = cmpxchg ptr [[P]], ptr null, ptr [[Q]] acquire monotonic, align 8
-; CHECK-NEXT:    ret void
+; FNATTRS: Function Attrs: mustprogress nofree norecurse nounwind willreturn memory(argmem: readwrite)
+; FNATTRS-LABEL: define void @test_cmpxchg_ptr
+; FNATTRS-SAME: (ptr nocapture [[P:%.*]], ptr [[Q:%.*]]) #[[ATTR11]] {
+; FNATTRS-NEXT:    [[TMP1:%.*]] = cmpxchg ptr [[P]], ptr null, ptr [[Q]] acquire monotonic, align 8
+; FNATTRS-NEXT:    ret void
+;
+; ATTRIBUTOR: Function Attrs: mustprogress nofree norecurse nounwind willreturn memory(argmem: readwrite)
+; ATTRIBUTOR-LABEL: define void @test_cmpxchg_ptr
+; ATTRIBUTOR-SAME: (ptr nocapture nofree nonnull [[P:%.*]], ptr nofree [[Q:%.*]]) #[[ATTR9]] {
+; ATTRIBUTOR-NEXT:    [[TMP1:%.*]] = cmpxchg ptr [[P]], ptr null, ptr [[Q]] acquire monotonic, align 8
+; ATTRIBUTOR-NEXT:    ret void
 ;
   cmpxchg ptr %p, ptr null, ptr %q acquire monotonic
   ret void
 }
 
 define void @test_atomicrmw(ptr %p) {
-; CHECK-LABEL: define void @test_atomicrmw
-; CHECK-SAME: (ptr nocapture [[P:%.*]]) #[[ATTR11]] {
-; CHECK-NEXT:    [[TMP1:%.*]] = atomicrmw add ptr [[P]], i32 1 seq_cst, align 4
-; CHECK-NEXT:    ret void
+; FNATTRS: Function Attrs: mustprogress nofree norecurse nounwind willreturn memory(argmem: readwrite)
+; FNATTRS-LABEL: define void @test_atomicrmw
+; FNATTRS-SAME: (ptr nocapture [[P:%.*]]) #[[ATTR11]] {
+; FNATTRS-NEXT:    [[TMP1:%.*]] = atomicrmw add ptr [[P]], i32 1 seq_cst, align 4
+; FNATTRS-NEXT:    ret void
+;
+; ATTRIBUTOR: Function Attrs: mustprogress nofree norecurse nounwind willreturn memory(argmem: readwrite)
+; ATTRIBUTOR-LABEL: define void @test_atomicrmw
+; ATTRIBUTOR-SAME: (ptr nocapture nofree nonnull [[P:%.*]]) #[[ATTR9]] {
+; ATTRIBUTOR-NEXT:    [[TMP1:%.*]] = atomicrmw add ptr [[P]], i32 1 seq_cst, align 4
+; ATTRIBUTOR-NEXT:    ret void
 ;
   atomicrmw add ptr %p, i32 1 seq_cst
   ret void
 }
 
 define void @test_volatile(ptr %x) {
-; CHECK-LABEL: define void @test_volatile
-; CHECK-SAME: (ptr [[X:%.*]]) #[[ATTR12:[0-9]+]] {
-; CHECK-NEXT:  entry:
-; CHECK-NEXT:    [[GEP:%.*]] = getelementptr i32, ptr [[X]], i64 1
-; CHECK-NEXT:    store volatile i32 0, ptr [[GEP]], align 4
-; CHECK-NEXT:    ret void
+; FNATTRS: Function Attrs: nofree norecurse nounwind memory(argmem: readwrite, inaccessiblemem: readwrite)
+; FNATTRS-LABEL: define void @test_volatile
+; FNATTRS-SAME: (ptr [[X:%.*]]) #[[ATTR12:[0-9]+]] {
+; FNATTRS-NEXT:  entry:
+; FNATTRS-NEXT:    [[GEP:%.*]] = getelementptr i32, ptr [[X]], i64 1
+; FNATTRS-NEXT:    store volatile i32 0, ptr [[GEP]], align 4
+; FNATTRS-NEXT:    ret void
+;
+; ATTRIBUTOR: Function Attrs: mustprogress nofree norecurse nounwind willreturn memory(argmem: readwrite)
+; ATTRIBUTOR-LABEL: define void @test_volatile
+; ATTRIBUTOR-SAME: (ptr nofree [[X:%.*]]) #[[ATTR9]] {
+; ATTRIBUTOR-NEXT:  entry:
+; ATTRIBUTOR-NEXT:    [[GEP:%.*]] = getelementptr i32, ptr [[X]], i64 1
+; ATTRIBUTOR-NEXT:    store volatile i32 0, ptr [[GEP]], align 4
+; ATTRIBUTOR-NEXT:    ret void
 ;
 entry:
   %gep = getelementptr i32, ptr %x, i64 1
@@ -413,12 +650,21 @@ entry:
 }
 
 define void @nocaptureLaunder(ptr %p) {
-; CHECK-LABEL: define void @nocaptureLaunder
-; CHECK-SAME: (ptr nocapture [[P:%.*]]) #[[ATTR13:[0-9]+]] {
-; CHECK-NEXT:  entry:
-; CHECK-NEXT:    [[B:%.*]] = call ptr @llvm.launder.invariant.group.p0(ptr [[P]])
-; CHECK-NEXT:    store i8 42, ptr [[B]], align 1
-; CHECK-NEXT:    ret void
+; FNATTRS: Function Attrs: mustprogress nofree nosync nounwind willreturn memory(argmem: write, inaccessiblemem: readwrite)
+; FNATTRS-LABEL: define void @nocaptureLaunder
+; FNATTRS-SAME: (ptr nocapture [[P:%.*]]) #[[ATTR13:[0-9]+]] {
+; FNATTRS-NEXT:  entry:
+; FNATTRS-NEXT:    [[B:%.*]] = call ptr @llvm.launder.invariant.group.p0(ptr [[P]])
+; FNATTRS-NEXT:    store i8 42, ptr [[B]], align 1
+; FNATTRS-NEXT:    ret void
+;
+; ATTRIBUTOR: Function Attrs: mustprogress nofree norecurse nosync nounwind willreturn memory(argmem: readwrite, inaccessiblemem: readwrite)
+; ATTRIBUTOR-LABEL: define void @nocaptureLaunder
+; ATTRIBUTOR-SAME: (ptr nocapture nofree [[P:%.*]]) #[[ATTR10:[0-9]+]] {
+; ATTRIBUTOR-NEXT:  entry:
+; ATTRIBUTOR-NEXT:    [[B:%.*]] = call ptr @llvm.launder.invariant.group.p0(ptr [[P]]) #[[ATTR19:[0-9]+]]
+; ATTRIBUTOR-NEXT:    store i8 42, ptr [[B]], align 1
+; ATTRIBUTOR-NEXT:    ret void
 ;
 entry:
   %b = call ptr @llvm.launder.invariant.group.p0(ptr %p)
@@ -428,11 +674,19 @@ entry:
 
 @g2 = global ptr null
 define void @captureLaunder(ptr %p) {
-; CHECK-LABEL: define void @captureLaunder
-; CHECK-SAME: (ptr [[P:%.*]]) #[[ATTR14:[0-9]+]] {
-; CHECK-NEXT:    [[B:%.*]] = call ptr @llvm.launder.invariant.group.p0(ptr [[P]])
-; CHECK-NEXT:    store ptr [[B]], ptr @g2, align 8
-; CHECK-NEXT:    ret void
+; FNATTRS: Function Attrs: mustprogress nofree nosync nounwind willreturn memory(write, argmem: none, inaccessiblemem: readwrite)
+; FNATTRS-LABEL: define void @captureLaunder
+; FNATTRS-SAME: (ptr [[P:%.*]]) #[[ATTR14:[0-9]+]] {
+; FNATTRS-NEXT:    [[B:%.*]] = call ptr @llvm.launder.invariant.group.p0(ptr [[P]])
+; FNATTRS-NEXT:    store ptr [[B]], ptr @g2, align 8
+; FNATTRS-NEXT:    ret void
+;
+; ATTRIBUTOR: Function Attrs: mustprogress nofree norecurse nosync nounwind willreturn
+; ATTRIBUTOR-LABEL: define void @captureLaunder
+; ATTRIBUTOR-SAME: (ptr nofree [[P:%.*]]) #[[ATTR5]] {
+; ATTRIBUTOR-NEXT:    [[B:%.*]] = call ptr @llvm.launder.invariant.group.p0(ptr [[P]]) #[[ATTR19]]
+; ATTRIBUTOR-NEXT:    store ptr [[B]], ptr @g2, align 8
+; ATTRIBUTOR-NEXT:    ret void
 ;
   %b = call ptr @llvm.launder.invariant.group.p0(ptr %p)
   store ptr %b, ptr @g2
@@ -440,12 +694,21 @@ define void @captureLaunder(ptr %p) {
 }
 
 define void @nocaptureStrip(ptr %p) {
-; CHECK-LABEL: define void @nocaptureStrip
-; CHECK-SAME: (ptr nocapture writeonly [[P:%.*]]) #[[ATTR15:[0-9]+]] {
-; CHECK-NEXT:  entry:
-; CHECK-NEXT:    [[B:%.*]] = call ptr @llvm.strip.invariant.group.p0(ptr [[P]])
-; CHECK-NEXT:    store i8 42, ptr [[B]], align 1
-; CHECK-NEXT:    ret void
+; FNATTRS: Function Attrs: mustprogress nofree nosync nounwind willreturn memory(argmem: write)
+; FNATTRS-LABEL: define void @nocaptureStrip
+; FNATTRS-SAME: (ptr nocapture writeonly [[P:%.*]]) #[[ATTR15:[0-9]+]] {
+; FNATTRS-NEXT:  entry:
+; FNATTRS-NEXT:    [[B:%.*]] = call ptr @llvm.strip.invariant.group.p0(ptr [[P]])
+; FNATTRS-NEXT:    store i8 42, ptr [[B]], align 1
+; FNATTRS-NEXT:    ret void
+;
+; ATTRIBUTOR: Function Attrs: mustprogress nofree norecurse nosync nounwind willreturn memory(argmem: write)
+; ATTRIBUTOR-LABEL: define void @nocaptureStrip
+; ATTRIBUTOR-SAME: (ptr nocapture nofree writeonly [[P:%.*]]) #[[ATTR11:[0-9]+]] {
+; ATTRIBUTOR-NEXT:  entry:
+; ATTRIBUTOR-NEXT:    [[B:%.*]] = call ptr @llvm.strip.invariant.group.p0(ptr [[P]]) #[[ATTR17]]
+; ATTRIBUTOR-NEXT:    store i8 42, ptr [[B]], align 1
+; ATTRIBUTOR-NEXT:    ret void
 ;
 entry:
   %b = call ptr @llvm.strip.invariant.group.p0(ptr %p)
@@ -455,11 +718,19 @@ entry:
 
 @g3 = global ptr null
 define void @captureStrip(ptr %p) {
-; CHECK-LABEL: define void @captureStrip
-; CHECK-SAME: (ptr [[P:%.*]]) #[[ATTR16:[0-9]+]] {
-; CHECK-NEXT:    [[B:%.*]] = call ptr @llvm.strip.invariant.group.p0(ptr [[P]])
-; CHECK-NEXT:    store ptr [[B]], ptr @g3, align 8
-; CHECK-NEXT:    ret void
+; FNATTRS: Function Attrs: mustprogress nofree nosync nounwind willreturn memory(write, argmem: none, inaccessiblemem: none)
+; FNATTRS-LABEL: define void @captureStrip
+; FNATTRS-SAME: (ptr [[P:%.*]]) #[[ATTR16:[0-9]+]] {
+; FNATTRS-NEXT:    [[B:%.*]] = call ptr @llvm.strip.invariant.group.p0(ptr [[P]])
+; FNATTRS-NEXT:    store ptr [[B]], ptr @g3, align 8
+; FNATTRS-NEXT:    ret void
+;
+; ATTRIBUTOR: Function Attrs: mustprogress nofree norecurse nosync nounwind willreturn memory(write)
+; ATTRIBUTOR-LABEL: define void @captureStrip
+; ATTRIBUTOR-SAME: (ptr nofree writeonly [[P:%.*]]) #[[ATTR1]] {
+; ATTRIBUTOR-NEXT:    [[B:%.*]] = call ptr @llvm.strip.invariant.group.p0(ptr [[P]]) #[[ATTR17]]
+; ATTRIBUTOR-NEXT:    store ptr [[B]], ptr @g3, align 8
+; ATTRIBUTOR-NEXT:    ret void
 ;
   %b = call ptr @llvm.strip.invariant.group.p0(ptr %p)
   store ptr %b, ptr @g3
@@ -467,31 +738,53 @@ define void @captureStrip(ptr %p) {
 }
 
 define i1 @captureICmp(ptr %x) {
-; CHECK-LABEL: define i1 @captureICmp
-; CHECK-SAME: (ptr readnone [[X:%.*]]) #[[ATTR0]] {
-; CHECK-NEXT:    [[TMP1:%.*]] = icmp eq ptr [[X]], null
-; CHECK-NEXT:    ret i1 [[TMP1]]
+; FNATTRS: Function Attrs: mustprogress nofree norecurse nosync nounwind willreturn memory(none)
+; FNATTRS-LABEL: define i1 @captureICmp
+; FNATTRS-SAME: (ptr readnone [[X:%.*]]) #[[ATTR0]] {
+; FNATTRS-NEXT:    [[TMP1:%.*]] = icmp eq ptr [[X]], null
+; FNATTRS-NEXT:    ret i1 [[TMP1]]
+;
+; ATTRIBUTOR: Function Attrs: mustprogress nofree norecurse nosync nounwind willreturn memory(none)
+; ATTRIBUTOR-LABEL: define i1 @captureICmp
+; ATTRIBUTOR-SAME: (ptr nofree readnone [[X:%.*]]) #[[ATTR0]] {
+; ATTRIBUTOR-NEXT:    [[TMP1:%.*]] = icmp eq ptr [[X]], null
+; ATTRIBUTOR-NEXT:    ret i1 [[TMP1]]
 ;
   %1 = icmp eq ptr %x, null
   ret i1 %1
 }
 
 define i1 @captureICmpRev(ptr %x) {
-; CHECK-LABEL: define i1 @captureICmpRev
-; CHECK-SAME: (ptr readnone [[X:%.*]]) #[[ATTR0]] {
-; CHECK-NEXT:    [[TMP1:%.*]] = icmp eq ptr null, [[X]]
-; CHECK-NEXT:    ret i1 [[TMP1]]
+; FNATTRS: Function Attrs: mustprogress nofree norecurse nosync nounwind willreturn memory(none)
+; FNATTRS-LABEL: define i1 @captureICmpRev
+; FNATTRS-SAME: (ptr readnone [[X:%.*]]) #[[ATTR0]] {
+; FNATTRS-NEXT:    [[TMP1:%.*]] = icmp eq ptr null, [[X]]
+; FNATTRS-NEXT:    ret i1 [[TMP1]]
+;
+; ATTRIBUTOR: Function Attrs: mustprogress nofree norecurse nosync nounwind willreturn memory(none)
+; ATTRIBUTOR-LABEL: define i1 @captureICmpRev
+; ATTRIBUTOR-SAME: (ptr nofree readnone [[X:%.*]]) #[[ATTR0]] {
+; ATTRIBUTOR-NEXT:    [[TMP1:%.*]] = icmp eq ptr null, [[X]]
+; ATTRIBUTOR-NEXT:    ret i1 [[TMP1]]
 ;
   %1 = icmp eq ptr null, %x
   ret i1 %1
 }
 
 define i1 @nocaptureInboundsGEPICmp(ptr %x) {
-; CHECK-LABEL: define i1 @nocaptureInboundsGEPICmp
-; CHECK-SAME: (ptr readnone [[X:%.*]]) #[[ATTR0]] {
-; CHECK-NEXT:    [[TMP1:%.*]] = getelementptr inbounds i32, ptr [[X]], i32 5
-; CHECK-NEXT:    [[TMP2:%.*]] = icmp eq ptr [[TMP1]], null
-; CHECK-NEXT:    ret i1 [[TMP2]]
+; FNATTRS: Function Attrs: mustprogress nofree norecurse nosync nounwind willreturn memory(none)
+; FNATTRS-LABEL: define i1 @nocaptureInboundsGEPICmp
+; FNATTRS-SAME: (ptr readnone [[X:%.*]]) #[[ATTR0]] {
+; FNATTRS-NEXT:    [[TMP1:%.*]] = getelementptr inbounds i32, ptr [[X]], i32 5
+; FNATTRS-NEXT:    [[TMP2:%.*]] = icmp eq ptr [[TMP1]], null
+; FNATTRS-NEXT:    ret i1 [[TMP2]]
+;
+; ATTRIBUTOR: Function Attrs: mustprogress nofree norecurse nosync nounwind willreturn memory(none)
+; ATTRIBUTOR-LABEL: define i1 @nocaptureInboundsGEPICmp
+; ATTRIBUTOR-SAME: (ptr nofree readnone [[X:%.*]]) #[[ATTR0]] {
+; ATTRIBUTOR-NEXT:    [[TMP1:%.*]] = getelementptr inbounds i32, ptr [[X]], i32 5
+; ATTRIBUTOR-NEXT:    [[TMP2:%.*]] = icmp eq ptr [[TMP1]], null
+; ATTRIBUTOR-NEXT:    ret i1 [[TMP2]]
 ;
   %1 = getelementptr inbounds i32, ptr %x, i32 5
   %2 = icmp eq ptr %1, null
@@ -499,11 +792,19 @@ define i1 @nocaptureInboundsGEPICmp(ptr %x) {
 }
 
 define i1 @nocaptureInboundsGEPICmpRev(ptr %x) {
-; CHECK-LABEL: define i1 @nocaptureInboundsGEPICmpRev
-; CHECK-SAME: (ptr readnone [[X:%.*]]) #[[ATTR0]] {
-; CHECK-NEXT:    [[TMP1:%.*]] = getelementptr inbounds i32, ptr [[X]], i32 5
-; CHECK-NEXT:    [[TMP2:%.*]] = icmp eq ptr null, [[TMP1]]
-; CHECK-NEXT:    ret i1 [[TMP2]]
+; FNATTRS: Function Attrs: mustprogress nofree norecurse nosync nounwind willreturn memory(none)
+; FNATTRS-LABEL: define i1 @nocaptureInboundsGEPICmpRev
+; FNATTRS-SAME: (ptr readnone [[X:%.*]]) #[[ATTR0]] {
+; FNATTRS-NEXT:    [[TMP1:%.*]] = getelementptr inbounds i32, ptr [[X]], i32 5
+; FNATTRS-NEXT:    [[TMP2:%.*]] = icmp eq ptr null, [[TMP1]]
+; FNATTRS-NEXT:    ret i1 [[TMP2]]
+;
+; ATTRIBUTOR: Function Attrs: mustprogress nofree norecurse nosync nounwind willreturn memory(none)
+; ATTRIBUTOR-LABEL: define i1 @nocaptureInboundsGEPICmpRev
+; ATTRIBUTOR-SAME: (ptr nofree readnone [[X:%.*]]) #[[ATTR0]] {
+; ATTRIBUTOR-NEXT:    [[TMP1:%.*]] = getelementptr inbounds i32, ptr [[X]], i32 5
+; ATTRIBUTOR-NEXT:    [[TMP2:%.*]] = icmp eq ptr null, [[TMP1]]
+; ATTRIBUTOR-NEXT:    ret i1 [[TMP2]]
 ;
   %1 = getelementptr inbounds i32, ptr %x, i32 5
   %2 = icmp eq ptr null, %1
@@ -511,20 +812,34 @@ define i1 @nocaptureInboundsGEPICmpRev(ptr %x) {
 }
 
 define i1 @nocaptureDereferenceableOrNullICmp(ptr dereferenceable_or_null(4) %x) {
-; CHECK-LABEL: define i1 @nocaptureDereferenceableOrNullICmp
-; CHECK-SAME: (ptr nocapture readnone dereferenceable_or_null(4) [[X:%.*]]) #[[ATTR0]] {
-; CHECK-NEXT:    [[TMP1:%.*]] = icmp eq ptr [[X]], null
-; CHECK-NEXT:    ret i1 [[TMP1]]
+; FNATTRS: Function Attrs: mustprogress nofree norecurse nosync nounwind willreturn memory(none)
+; FNATTRS-LABEL: define i1 @nocaptureDereferenceableOrNullICmp
+; FNATTRS-SAME: (ptr nocapture readnone dereferenceable_or_null(4) [[X:%.*]]) #[[ATTR0]] {
+; FNATTRS-NEXT:    [[TMP1:%.*]] = icmp eq ptr [[X]], null
+; FNATTRS-NEXT:    ret i1 [[TMP1]]
+;
+; ATTRIBUTOR: Function Attrs: mustprogress nofree norecurse nosync nounwind willreturn memory(none)
+; ATTRIBUTOR-LABEL: define i1 @nocaptureDereferenceableOrNullICmp
+; ATTRIBUTOR-SAME: (ptr nofree readnone dereferenceable_or_null(4) [[X:%.*]]) #[[ATTR0]] {
+; ATTRIBUTOR-NEXT:    [[TMP1:%.*]] = icmp eq ptr [[X]], null
+; ATTRIBUTOR-NEXT:    ret i1 [[TMP1]]
 ;
   %1 = icmp eq ptr %x, null
   ret i1 %1
 }
 
 define i1 @captureDereferenceableOrNullICmp(ptr dereferenceable_or_null(4) %x) null_pointer_is_valid {
-; CHECK-LABEL: define i1 @captureDereferenceableOrNullICmp
-; CHECK-SAME: (ptr readnone dereferenceable_or_null(4) [[X:%.*]]) #[[ATTR17:[0-9]+]] {
-; CHECK-NEXT:    [[TMP1:%.*]] = icmp eq ptr [[X]], null
-; CHECK-NEXT:    ret i1 [[TMP1]]
+; FNATTRS: Function Attrs: mustprogress nofree norecurse nosync nounwind null_pointer_is_valid willreturn memory(none)
+; FNATTRS-LABEL: define i1 @captureDereferenceableOrNullICmp
+; FNATTRS-SAME: (ptr readnone dereferenceable_or_null(4) [[X:%.*]]) #[[ATTR17:[0-9]+]] {
+; FNATTRS-NEXT:    [[TMP1:%.*]] = icmp eq ptr [[X]], null
+; FNATTRS-NEXT:    ret i1 [[TMP1]]
+;
+; ATTRIBUTOR: Function Attrs: mustprogress nofree norecurse nosync nounwind null_pointer_is_valid willreturn memory(none)
+; ATTRIBUTOR-LABEL: define i1 @captureDereferenceableOrNullICmp
+; ATTRIBUTOR-SAME: (ptr nofree readnone dereferenceable_or_null(4) [[X:%.*]]) #[[ATTR12:[0-9]+]] {
+; ATTRIBUTOR-NEXT:    [[TMP1:%.*]] = icmp eq ptr [[X]], null
+; ATTRIBUTOR-NEXT:    ret i1 [[TMP1]]
 ;
   %1 = icmp eq ptr %x, null
   ret i1 %1
@@ -533,11 +848,17 @@ define i1 @captureDereferenceableOrNullICmp(ptr dereferenceable_or_null(4) %x) n
 declare void @capture(ptr)
 
 define void @nocapture_fptr(ptr %f, ptr %p) {
-; CHECK-LABEL: define void @nocapture_fptr
-; CHECK-SAME: (ptr nocapture readonly [[F:%.*]], ptr [[P:%.*]]) {
-; CHECK-NEXT:    [[RES:%.*]] = call ptr [[F]](ptr [[P]])
-; CHECK-NEXT:    call void @capture(ptr [[RES]])
-; CHECK-NEXT:    ret void
+; FNATTRS-LABEL: define void @nocapture_fptr
+; FNATTRS-SAME: (ptr nocapture readonly [[F:%.*]], ptr [[P:%.*]]) {
+; FNATTRS-NEXT:    [[RES:%.*]] = call ptr [[F]](ptr [[P]])
+; FNATTRS-NEXT:    call void @capture(ptr [[RES]])
+; FNATTRS-NEXT:    ret void
+;
+; ATTRIBUTOR-LABEL: define void @nocapture_fptr
+; ATTRIBUTOR-SAME: (ptr nocapture nofree nonnull [[F:%.*]], ptr [[P:%.*]]) {
+; ATTRIBUTOR-NEXT:    [[RES:%.*]] = call ptr [[F]](ptr [[P]])
+; ATTRIBUTOR-NEXT:    call void @capture(ptr [[RES]])
+; ATTRIBUTOR-NEXT:    ret void
 ;
   %res = call ptr %f(ptr %p)
   call void @capture(ptr %res)
@@ -545,11 +866,17 @@ define void @nocapture_fptr(ptr %f, ptr %p) {
 }
 
 define void @recurse_fptr(ptr %f, ptr %p) {
-; CHECK-LABEL: define void @recurse_fptr
-; CHECK-SAME: (ptr nocapture readonly [[F:%.*]], ptr [[P:%.*]]) {
-; CHECK-NEXT:    [[RES:%.*]] = call ptr [[F]](ptr [[P]])
-; CHECK-NEXT:    store i8 0, ptr [[RES]], align 1
-; CHECK-NEXT:    ret void
+; FNATTRS-LABEL: define void @recurse_fptr
+; FNATTRS-SAME: (ptr nocapture readonly [[F:%.*]], ptr [[P:%.*]]) {
+; FNATTRS-NEXT:    [[RES:%.*]] = call ptr [[F]](ptr [[P]])
+; FNATTRS-NEXT:    store i8 0, ptr [[RES]], align 1
+; FNATTRS-NEXT:    ret void
+;
+; ATTRIBUTOR-LABEL: define void @recurse_fptr
+; ATTRIBUTOR-SAME: (ptr nocapture nofree nonnull [[F:%.*]], ptr [[P:%.*]]) {
+; ATTRIBUTOR-NEXT:    [[RES:%.*]] = call ptr [[F]](ptr [[P]])
+; ATTRIBUTOR-NEXT:    store i8 0, ptr [[RES]], align 1
+; ATTRIBUTOR-NEXT:    ret void
 ;
   %res = call ptr %f(ptr %p)
   store i8 0, ptr %res
@@ -557,10 +884,17 @@ define void @recurse_fptr(ptr %f, ptr %p) {
 }
 
 define void @readnone_indirec(ptr %f, ptr %p) {
-; CHECK-LABEL: define void @readnone_indirec
-; CHECK-SAME: (ptr nocapture readonly [[F:%.*]], ptr readnone [[P:%.*]]) #[[ATTR18:[0-9]+]] {
-; CHECK-NEXT:    call void [[F]](ptr [[P]]) #[[ATTR21:[0-9]+]]
-; CHECK-NEXT:    ret void
+; FNATTRS: Function Attrs: nofree nosync memory(none)
+; FNATTRS-LABEL: define void @readnone_indirec
+; FNATTRS-SAME: (ptr nocapture readonly [[F:%.*]], ptr readnone [[P:%.*]]) #[[ATTR18:[0-9]+]] {
+; FNATTRS-NEXT:    call void [[F]](ptr [[P]]) #[[ATTR21:[0-9]+]]
+; FNATTRS-NEXT:    ret void
+;
+; ATTRIBUTOR: Function Attrs: nosync memory(none)
+; ATTRIBUTOR-LABEL: define void @readnone_indirec
+; ATTRIBUTOR-SAME: (ptr nocapture nofree nonnull readnone [[F:%.*]], ptr readnone [[P:%.*]]) #[[ATTR13:[0-9]+]] {
+; ATTRIBUTOR-NEXT:    call void [[F]](ptr [[P]]) #[[ATTR20:[0-9]+]]
+; ATTRIBUTOR-NEXT:    ret void
 ;
   call void %f(ptr %p) readnone
   ret void

diff  --git a/llvm/test/Transforms/FunctionAttrs/nonnull.ll b/llvm/test/Transforms/FunctionAttrs/nonnull.ll
index 17a4155319b606..3e41b8a07bc720 100644
--- a/llvm/test/Transforms/FunctionAttrs/nonnull.ll
+++ b/llvm/test/Transforms/FunctionAttrs/nonnull.ll
@@ -1,5 +1,6 @@
-; NOTE: Assertions have been autogenerated by utils/update_test_checks.py
-; RUN: opt -S -passes=function-attrs -enable-nonnull-arg-prop %s | FileCheck %s --check-prefixes=FNATTR
+; NOTE: Assertions have been autogenerated by utils/update_test_checks.py UTC_ARGS: --version 3
+; RUN: opt -S -passes=function-attrs -enable-nonnull-arg-prop %s | FileCheck %s --check-prefixes=COMMON,FNATTRS
+; RUN: opt -S -passes=attributor-light %s | FileCheck %s --check-prefixes=COMMON,ATTRIBUTOR
 
 target datalayout = "e-m:e-i64:64-f80:128-n8:16:32:64-S128"
 
@@ -7,21 +8,39 @@ declare nonnull ptr @ret_nonnull()
 
 ; Return a pointer trivially nonnull (call return attribute)
 define ptr @test1() {
-; FNATTR: define nonnull ptr @test1
+; COMMON-LABEL: define nonnull ptr @test1() {
+; COMMON-NEXT:    [[RET:%.*]] = call ptr @ret_nonnull()
+; COMMON-NEXT:    ret ptr [[RET]]
+;
   %ret = call ptr @ret_nonnull()
   ret ptr %ret
 }
 
 ; Return a pointer trivially nonnull (argument attribute)
 define ptr @test2(ptr nonnull %p) {
-; FNATTR: define nonnull ptr @test2
+; FNATTRS-LABEL: define nonnull ptr @test2
+; FNATTRS-SAME: (ptr nonnull readnone returned [[P:%.*]]) #[[ATTR0:[0-9]+]] {
+; FNATTRS-NEXT:    ret ptr [[P]]
+;
+; ATTRIBUTOR-LABEL: define nonnull ptr @test2
+; ATTRIBUTOR-SAME: (ptr nofree nonnull readnone [[P:%.*]]) #[[ATTR0:[0-9]+]] {
+; ATTRIBUTOR-NEXT:    ret ptr [[P]]
+;
   ret ptr %p
 }
 
 ; Given an SCC where one of the functions can not be marked nonnull,
 ; can we still mark the other one which is trivially nonnull
 define ptr @scc_binder(i1 %c) {
-; FNATTR: define ptr @scc_binder
+; COMMON-LABEL: define ptr @scc_binder
+; COMMON-SAME: (i1 [[C:%.*]]) {
+; COMMON-NEXT:    br i1 [[C]], label [[REC:%.*]], label [[END:%.*]]
+; COMMON:       rec:
+; COMMON-NEXT:    [[TMP1:%.*]] = call ptr @test3(i1 [[C]])
+; COMMON-NEXT:    br label [[END]]
+; COMMON:       end:
+; COMMON-NEXT:    ret ptr null
+;
   br i1 %c, label %rec, label %end
 rec:
   call ptr @test3(i1 %c)
@@ -31,7 +50,12 @@ end:
 }
 
 define ptr @test3(i1 %c) {
-; FNATTR: define nonnull ptr @test3
+; COMMON-LABEL: define nonnull ptr @test3
+; COMMON-SAME: (i1 [[C:%.*]]) {
+; COMMON-NEXT:    [[TMP1:%.*]] = call ptr @scc_binder(i1 [[C]])
+; COMMON-NEXT:    [[RET:%.*]] = call ptr @ret_nonnull()
+; COMMON-NEXT:    ret ptr [[RET]]
+;
   call ptr @scc_binder(i1 %c)
   %ret = call ptr @ret_nonnull()
   ret ptr %ret
@@ -41,13 +65,31 @@ define ptr @test3(i1 %c) {
 ; nonnull if neither can ever return null.  (In this case, they
 ; just never return period.)
 define ptr @test4_helper() {
-; FNATTR: define noalias nonnull ptr @test4_helper
+; FNATTRS-LABEL: define noalias nonnull ptr @test4_helper
+; FNATTRS-SAME: () #[[ATTR1:[0-9]+]] {
+; FNATTRS-NEXT:    [[RET:%.*]] = call ptr @test4()
+; FNATTRS-NEXT:    ret ptr [[RET]]
+;
+; ATTRIBUTOR-LABEL: define ptr @test4_helper
+; ATTRIBUTOR-SAME: () #[[ATTR1:[0-9]+]] {
+; ATTRIBUTOR-NEXT:    [[RET:%.*]] = call ptr @test4() #[[ATTR1]]
+; ATTRIBUTOR-NEXT:    ret ptr [[RET]]
+;
   %ret = call ptr @test4()
   ret ptr %ret
 }
 
 define ptr @test4() {
-; FNATTR: define noalias nonnull ptr @test4
+; FNATTRS-LABEL: define noalias nonnull ptr @test4
+; FNATTRS-SAME: () #[[ATTR1]] {
+; FNATTRS-NEXT:    [[RET:%.*]] = call ptr @test4_helper()
+; FNATTRS-NEXT:    ret ptr [[RET]]
+;
+; ATTRIBUTOR-LABEL: define ptr @test4
+; ATTRIBUTOR-SAME: () #[[ATTR1]] {
+; ATTRIBUTOR-NEXT:    [[RET:%.*]] = call ptr @test4_helper() #[[ATTR1]]
+; ATTRIBUTOR-NEXT:    ret ptr [[RET]]
+;
   %ret = call ptr @test4_helper()
   ret ptr %ret
 }
@@ -55,7 +97,24 @@ define ptr @test4() {
 ; Given a mutual recursive set of functions which *can* return null
 ; make sure we haven't marked them as nonnull.
 define ptr @test5_helper(i1 %c) {
-; FNATTR: define noalias ptr @test5_helper
+; FNATTRS-LABEL: define noalias ptr @test5_helper
+; FNATTRS-SAME: (i1 [[C:%.*]]) #[[ATTR1]] {
+; FNATTRS-NEXT:    br i1 [[C]], label [[REC:%.*]], label [[END:%.*]]
+; FNATTRS:       rec:
+; FNATTRS-NEXT:    [[RET:%.*]] = call ptr @test5(i1 [[C]])
+; FNATTRS-NEXT:    br label [[END]]
+; FNATTRS:       end:
+; FNATTRS-NEXT:    ret ptr null
+;
+; ATTRIBUTOR-LABEL: define ptr @test5_helper
+; ATTRIBUTOR-SAME: (i1 [[C:%.*]]) #[[ATTR1]] {
+; ATTRIBUTOR-NEXT:    br i1 [[C]], label [[REC:%.*]], label [[END:%.*]]
+; ATTRIBUTOR:       rec:
+; ATTRIBUTOR-NEXT:    [[RET:%.*]] = call ptr @test5(i1 [[C]]) #[[ATTR1]]
+; ATTRIBUTOR-NEXT:    br label [[END]]
+; ATTRIBUTOR:       end:
+; ATTRIBUTOR-NEXT:    ret ptr null
+;
   br i1 %c, label %rec, label %end
 rec:
   %ret = call ptr @test5(i1 %c)
@@ -65,13 +124,32 @@ end:
 }
 
 define ptr @test5(i1 %c) {
-; FNATTR: define noalias ptr @test5
+; FNATTRS-LABEL: define noalias ptr @test5
+; FNATTRS-SAME: (i1 [[C:%.*]]) #[[ATTR1]] {
+; FNATTRS-NEXT:    [[RET:%.*]] = call ptr @test5_helper(i1 [[C]])
+; FNATTRS-NEXT:    ret ptr [[RET]]
+;
+; ATTRIBUTOR-LABEL: define ptr @test5
+; ATTRIBUTOR-SAME: (i1 [[C:%.*]]) #[[ATTR1]] {
+; ATTRIBUTOR-NEXT:    [[RET:%.*]] = call ptr @test5_helper(i1 [[C]]) #[[ATTR1]]
+; ATTRIBUTOR-NEXT:    ret ptr [[RET]]
+;
   %ret = call ptr @test5_helper(i1 %c)
   ret ptr %ret
 }
 
 ; Local analysis, but going through a self recursive phi
 define ptr @test6a() {
+; COMMON-LABEL: define nonnull ptr @test6a() {
+; COMMON-NEXT:  entry:
+; COMMON-NEXT:    [[RET:%.*]] = call ptr @ret_nonnull()
+; COMMON-NEXT:    br label [[LOOP:%.*]]
+; COMMON:       loop:
+; COMMON-NEXT:    [[PHI:%.*]] = phi ptr [ [[RET]], [[ENTRY:%.*]] ], [ [[PHI]], [[LOOP]] ]
+; COMMON-NEXT:    br i1 undef, label [[LOOP]], label [[EXIT:%.*]]
+; COMMON:       exit:
+; COMMON-NEXT:    ret ptr [[PHI]]
+;
 entry:
   %ret = call ptr @ret_nonnull()
   br label %loop
@@ -83,6 +161,17 @@ exit:
 }
 
 define ptr @test6b(i1 %c) {
+; COMMON-LABEL: define nonnull ptr @test6b
+; COMMON-SAME: (i1 [[C:%.*]]) {
+; COMMON-NEXT:  entry:
+; COMMON-NEXT:    [[RET:%.*]] = call ptr @ret_nonnull()
+; COMMON-NEXT:    br label [[LOOP:%.*]]
+; COMMON:       loop:
+; COMMON-NEXT:    [[PHI:%.*]] = phi ptr [ [[RET]], [[ENTRY:%.*]] ], [ [[PHI]], [[LOOP]] ]
+; COMMON-NEXT:    br i1 [[C]], label [[LOOP]], label [[EXIT:%.*]]
+; COMMON:       exit:
+; COMMON-NEXT:    ret ptr [[PHI]]
+;
 entry:
   %ret = call ptr @ret_nonnull()
   br label %loop
@@ -93,27 +182,65 @@ exit:
   ret ptr %phi
 }
 
-; FNATTR: define ptr @test7
 define ptr @test7(ptr %a) {
+; FNATTRS-LABEL: define ptr @test7
+; FNATTRS-SAME: (ptr readnone returned [[A:%.*]]) #[[ATTR0]] {
+; FNATTRS-NEXT:    ret ptr [[A]]
+;
+; ATTRIBUTOR-LABEL: define ptr @test7
+; ATTRIBUTOR-SAME: (ptr nofree readnone [[A:%.*]]) #[[ATTR0]] {
+; ATTRIBUTOR-NEXT:    ret ptr [[A]]
+;
   ret ptr %a
 }
 
-; FNATTR: define nonnull ptr @test8
 define ptr @test8(ptr %a) {
+; FNATTRS-LABEL: define nonnull ptr @test8
+; FNATTRS-SAME: (ptr readnone [[A:%.*]]) #[[ATTR0]] {
+; FNATTRS-NEXT:    [[B:%.*]] = getelementptr inbounds i8, ptr [[A]], i64 1
+; FNATTRS-NEXT:    ret ptr [[B]]
+;
+; ATTRIBUTOR-LABEL: define nonnull ptr @test8
+; ATTRIBUTOR-SAME: (ptr nofree readnone [[A:%.*]]) #[[ATTR0]] {
+; ATTRIBUTOR-NEXT:    [[B:%.*]] = getelementptr inbounds i8, ptr [[A]], i64 1
+; ATTRIBUTOR-NEXT:    ret ptr [[B]]
+;
   %b = getelementptr inbounds i8, ptr %a, i64 1
   ret ptr %b
 }
 
-; FNATTR: define ptr @test9
 define ptr @test9(ptr %a, i64 %n) {
+; FNATTRS-LABEL: define ptr @test9
+; FNATTRS-SAME: (ptr readnone [[A:%.*]], i64 [[N:%.*]]) #[[ATTR0]] {
+; FNATTRS-NEXT:    [[B:%.*]] = getelementptr inbounds i8, ptr [[A]], i64 [[N]]
+; FNATTRS-NEXT:    ret ptr [[B]]
+;
+; ATTRIBUTOR-LABEL: define ptr @test9
+; ATTRIBUTOR-SAME: (ptr nofree readnone [[A:%.*]], i64 [[N:%.*]]) #[[ATTR0]] {
+; ATTRIBUTOR-NEXT:    [[B:%.*]] = getelementptr inbounds i8, ptr [[A]], i64 [[N]]
+; ATTRIBUTOR-NEXT:    ret ptr [[B]]
+;
   %b = getelementptr inbounds i8, ptr %a, i64 %n
   ret ptr %b
 }
 
 declare void @llvm.assume(i1)
-; FNATTR: define ptr @test10
 ; FIXME: missing nonnull
 define ptr @test10(ptr %a, i64 %n) {
+; FNATTRS-LABEL: define ptr @test10
+; FNATTRS-SAME: (ptr readnone [[A:%.*]], i64 [[N:%.*]]) #[[ATTR3:[0-9]+]] {
+; FNATTRS-NEXT:    [[CMP:%.*]] = icmp ne i64 [[N]], 0
+; FNATTRS-NEXT:    call void @llvm.assume(i1 [[CMP]])
+; FNATTRS-NEXT:    [[B:%.*]] = getelementptr inbounds i8, ptr [[A]], i64 [[N]]
+; FNATTRS-NEXT:    ret ptr [[B]]
+;
+; ATTRIBUTOR-LABEL: define ptr @test10
+; ATTRIBUTOR-SAME: (ptr nofree readnone [[A:%.*]], i64 [[N:%.*]]) #[[ATTR3:[0-9]+]] {
+; ATTRIBUTOR-NEXT:    [[CMP:%.*]] = icmp ne i64 [[N]], 0
+; ATTRIBUTOR-NEXT:    call void @llvm.assume(i1 [[CMP]]) #[[ATTR14:[0-9]+]]
+; ATTRIBUTOR-NEXT:    [[B:%.*]] = getelementptr inbounds i8, ptr [[A]], i64 [[N]]
+; ATTRIBUTOR-NEXT:    ret ptr [[B]]
+;
   %cmp = icmp ne i64 %n, 0
   call void @llvm.assume(i1 %cmp)
   %b = getelementptr inbounds i8, ptr %a, i64 %n
@@ -124,9 +251,30 @@ define ptr @test10(ptr %a, i64 %n) {
 ; char* test11(char *p) {
 ;   return p? p: nonnull();
 ; }
-; FNATTR: define ptr @test11
 ; FIXME: missing nonnull
 define ptr @test11(ptr) local_unnamed_addr {
+; FNATTRS-LABEL: define ptr @test11
+; FNATTRS-SAME: (ptr readnone [[TMP0:%.*]]) local_unnamed_addr {
+; FNATTRS-NEXT:    [[TMP2:%.*]] = icmp eq ptr [[TMP0]], null
+; FNATTRS-NEXT:    br i1 [[TMP2]], label [[TMP3:%.*]], label [[TMP5:%.*]]
+; FNATTRS:       3:
+; FNATTRS-NEXT:    [[TMP4:%.*]] = tail call ptr @ret_nonnull()
+; FNATTRS-NEXT:    br label [[TMP5]]
+; FNATTRS:       5:
+; FNATTRS-NEXT:    [[TMP6:%.*]] = phi ptr [ [[TMP4]], [[TMP3]] ], [ [[TMP0]], [[TMP1:%.*]] ]
+; FNATTRS-NEXT:    ret ptr [[TMP6]]
+;
+; ATTRIBUTOR-LABEL: define ptr @test11
+; ATTRIBUTOR-SAME: (ptr [[TMP0:%.*]]) local_unnamed_addr {
+; ATTRIBUTOR-NEXT:    [[TMP2:%.*]] = icmp eq ptr [[TMP0]], null
+; ATTRIBUTOR-NEXT:    br i1 [[TMP2]], label [[TMP3:%.*]], label [[TMP5:%.*]]
+; ATTRIBUTOR:       3:
+; ATTRIBUTOR-NEXT:    [[TMP4:%.*]] = tail call ptr @ret_nonnull()
+; ATTRIBUTOR-NEXT:    br label [[TMP5]]
+; ATTRIBUTOR:       5:
+; ATTRIBUTOR-NEXT:    [[TMP6:%.*]] = phi ptr [ [[TMP4]], [[TMP3]] ], [ [[TMP0]], [[TMP1:%.*]] ]
+; ATTRIBUTOR-NEXT:    ret ptr [[TMP6]]
+;
   %2 = icmp eq ptr %0, null
   br i1 %2, label %3, label %5
 
@@ -143,6 +291,11 @@ define ptr @test11(ptr) local_unnamed_addr {
 ; Simple CallSite Test
 declare void @test12_helper(ptr)
 define void @test12(ptr nonnull %a) {
+; COMMON-LABEL: define void @test12
+; COMMON-SAME: (ptr nonnull [[A:%.*]]) {
+; COMMON-NEXT:    tail call void @test12_helper(ptr [[A]])
+; COMMON-NEXT:    ret void
+;
   tail call void @test12_helper(ptr %a)
   ret void
 }
@@ -151,6 +304,20 @@ define void @test12(ptr nonnull %a) {
 ; Simple Argument Tests
 declare ptr @unknown()
 define void @test13_helper() {
+; FNATTRS-LABEL: define void @test13_helper() {
+; FNATTRS-NEXT:    [[NONNULLPTR:%.*]] = tail call ptr @ret_nonnull()
+; FNATTRS-NEXT:    [[MAYBENULLPTR:%.*]] = tail call ptr @unknown()
+; FNATTRS-NEXT:    tail call void @test13(ptr [[NONNULLPTR]], ptr [[NONNULLPTR]], ptr [[MAYBENULLPTR]])
+; FNATTRS-NEXT:    tail call void @test13(ptr [[NONNULLPTR]], ptr [[MAYBENULLPTR]], ptr [[NONNULLPTR]])
+; FNATTRS-NEXT:    ret void
+;
+; ATTRIBUTOR-LABEL: define void @test13_helper() {
+; ATTRIBUTOR-NEXT:    [[NONNULLPTR:%.*]] = tail call ptr @ret_nonnull()
+; ATTRIBUTOR-NEXT:    [[MAYBENULLPTR:%.*]] = tail call ptr @unknown()
+; ATTRIBUTOR-NEXT:    tail call void @test13(ptr nocapture nofree nonnull readnone [[NONNULLPTR]], ptr nocapture nofree nonnull readnone [[NONNULLPTR]], ptr nocapture nofree readnone [[MAYBENULLPTR]])
+; ATTRIBUTOR-NEXT:    tail call void @test13(ptr nocapture nofree nonnull readnone [[NONNULLPTR]], ptr nocapture nofree readnone [[MAYBENULLPTR]], ptr nocapture nofree nonnull readnone [[NONNULLPTR]])
+; ATTRIBUTOR-NEXT:    ret void
+;
   %nonnullptr = tail call ptr @ret_nonnull()
   %maybenullptr = tail call ptr @unknown()
   tail call void @test13(ptr %nonnullptr, ptr %nonnullptr, ptr %maybenullptr)
@@ -158,6 +325,14 @@ define void @test13_helper() {
   ret void
 }
 define internal void @test13(ptr %a, ptr %b, ptr %c) {
+; FNATTRS-LABEL: define internal void @test13
+; FNATTRS-SAME: (ptr nocapture readnone [[A:%.*]], ptr nocapture readnone [[B:%.*]], ptr nocapture readnone [[C:%.*]]) #[[ATTR0]] {
+; FNATTRS-NEXT:    ret void
+;
+; ATTRIBUTOR-LABEL: define internal void @test13
+; ATTRIBUTOR-SAME: (ptr nocapture nofree readnone [[A:%.*]], ptr nocapture nofree readnone [[B:%.*]], ptr nocapture nofree readnone [[C:%.*]]) #[[ATTR4:[0-9]+]] {
+; ATTRIBUTOR-NEXT:    ret void
+;
   ret void
 }
 
@@ -177,7 +352,48 @@ declare nonnull ptr @nonnull()
 
 define internal ptr @f1(ptr %arg) {
 ; FIXME: missing nonnull It should be nonnull @f1(ptr nonnull readonly %arg)
-
+; FNATTRS-LABEL: define internal nonnull ptr @f1
+; FNATTRS-SAME: (ptr readonly [[ARG:%.*]]) #[[ATTR4:[0-9]+]] {
+; FNATTRS-NEXT:  bb:
+; FNATTRS-NEXT:    [[TMP:%.*]] = icmp eq ptr [[ARG]], null
+; FNATTRS-NEXT:    br i1 [[TMP]], label [[BB9:%.*]], label [[BB1:%.*]]
+; FNATTRS:       bb1:
+; FNATTRS-NEXT:    [[TMP2:%.*]] = load i32, ptr [[ARG]], align 4
+; FNATTRS-NEXT:    [[TMP3:%.*]] = icmp eq i32 [[TMP2]], 0
+; FNATTRS-NEXT:    br i1 [[TMP3]], label [[BB6:%.*]], label [[BB4:%.*]]
+; FNATTRS:       bb4:
+; FNATTRS-NEXT:    [[TMP5:%.*]] = getelementptr inbounds i32, ptr [[ARG]], i64 1
+; FNATTRS-NEXT:    [[TMP5B:%.*]] = tail call ptr @f3(ptr [[TMP5]])
+; FNATTRS-NEXT:    [[TMP5C:%.*]] = getelementptr inbounds i32, ptr [[TMP5B]], i64 -1
+; FNATTRS-NEXT:    br label [[BB9]]
+; FNATTRS:       bb6:
+; FNATTRS-NEXT:    [[TMP7:%.*]] = tail call ptr @f2(ptr [[ARG]])
+; FNATTRS-NEXT:    ret ptr [[TMP7]]
+; FNATTRS:       bb9:
+; FNATTRS-NEXT:    [[TMP10:%.*]] = phi ptr [ [[TMP5C]], [[BB4]] ], [ inttoptr (i64 4 to ptr), [[BB:%.*]] ]
+; FNATTRS-NEXT:    ret ptr [[TMP10]]
+;
+; ATTRIBUTOR-LABEL: define internal ptr @f1
+; ATTRIBUTOR-SAME: (ptr nofree readonly [[ARG:%.*]]) #[[ATTR5:[0-9]+]] {
+; ATTRIBUTOR-NEXT:  bb:
+; ATTRIBUTOR-NEXT:    [[TMP:%.*]] = icmp eq ptr [[ARG]], null
+; ATTRIBUTOR-NEXT:    br i1 [[TMP]], label [[BB9:%.*]], label [[BB1:%.*]]
+; ATTRIBUTOR:       bb1:
+; ATTRIBUTOR-NEXT:    [[TMP2:%.*]] = load i32, ptr [[ARG]], align 4
+; ATTRIBUTOR-NEXT:    [[TMP3:%.*]] = icmp eq i32 [[TMP2]], 0
+; ATTRIBUTOR-NEXT:    br i1 [[TMP3]], label [[BB6:%.*]], label [[BB4:%.*]]
+; ATTRIBUTOR:       bb4:
+; ATTRIBUTOR-NEXT:    [[TMP5:%.*]] = getelementptr inbounds i32, ptr [[ARG]], i64 1
+; ATTRIBUTOR-NEXT:    [[TMP5B:%.*]] = tail call ptr @f3(ptr readonly [[TMP5]]) #[[ATTR15:[0-9]+]]
+; ATTRIBUTOR-NEXT:    [[TMP5C:%.*]] = getelementptr inbounds i32, ptr [[TMP5B]], i64 -1
+; ATTRIBUTOR-NEXT:    br label [[BB9]]
+; ATTRIBUTOR:       bb6:
+; ATTRIBUTOR-NEXT:    [[TMP7:%.*]] = tail call ptr @f2(ptr readonly [[ARG]]) #[[ATTR15]]
+; ATTRIBUTOR-NEXT:    ret ptr [[TMP7]]
+; ATTRIBUTOR:       bb9:
+; ATTRIBUTOR-NEXT:    [[TMP10:%.*]] = phi ptr [ [[TMP5C]], [[BB4]] ], [ inttoptr (i64 4 to ptr), [[BB:%.*]] ]
+; ATTRIBUTOR-NEXT:    ret ptr [[TMP10]]
+;
 bb:
   %tmp = icmp eq ptr %arg, null
   br i1 %tmp, label %bb9, label %bb1
@@ -205,6 +421,18 @@ bb9:                                              ; preds = %bb4, %bb
 
 define internal ptr @f2(ptr %arg) {
 ; FIXME: missing nonnull. It should be nonnull @f2(ptr nonnull %arg)
+; FNATTRS-LABEL: define internal nonnull ptr @f2
+; FNATTRS-SAME: (ptr [[ARG:%.*]]) #[[ATTR4]] {
+; FNATTRS-NEXT:  bb:
+; FNATTRS-NEXT:    [[TMP:%.*]] = tail call ptr @f1(ptr [[ARG]])
+; FNATTRS-NEXT:    ret ptr [[TMP]]
+;
+; ATTRIBUTOR-LABEL: define internal ptr @f2
+; ATTRIBUTOR-SAME: (ptr readonly [[ARG:%.*]]) #[[ATTR5]] {
+; ATTRIBUTOR-NEXT:  bb:
+; ATTRIBUTOR-NEXT:    [[TMP:%.*]] = tail call ptr @f1(ptr readonly [[ARG]]) #[[ATTR15]]
+; ATTRIBUTOR-NEXT:    ret ptr [[TMP]]
+;
 bb:
 
 ; FIXME: missing nonnull. It should be @f1(ptr nonnull readonly %arg)
@@ -214,6 +442,18 @@ bb:
 
 define dso_local noalias ptr @f3(ptr %arg) {
 ; FIXME: missing nonnull. It should be nonnull @f3(ptr nonnull readonly %arg)
+; FNATTRS-LABEL: define dso_local noalias nonnull ptr @f3
+; FNATTRS-SAME: (ptr [[ARG:%.*]]) #[[ATTR4]] {
+; FNATTRS-NEXT:  bb:
+; FNATTRS-NEXT:    [[TMP:%.*]] = call ptr @f1(ptr [[ARG]])
+; FNATTRS-NEXT:    ret ptr [[TMP]]
+;
+; ATTRIBUTOR-LABEL: define dso_local noalias ptr @f3
+; ATTRIBUTOR-SAME: (ptr nofree readonly [[ARG:%.*]]) #[[ATTR5]] {
+; ATTRIBUTOR-NEXT:  bb:
+; ATTRIBUTOR-NEXT:    [[TMP:%.*]] = call ptr @f1(ptr nofree readonly [[ARG]]) #[[ATTR15]]
+; ATTRIBUTOR-NEXT:    ret ptr [[TMP]]
+;
 bb:
 ; FIXME: missing nonnull. It should be @f1(ptr nonnull readonly %arg)
   %tmp = call ptr @f1(ptr %arg)
@@ -222,7 +462,16 @@ bb:
 
 ; TEST 15
 define void @f15(ptr %arg) {
-
+; FNATTRS-LABEL: define void @f15
+; FNATTRS-SAME: (ptr [[ARG:%.*]]) {
+; FNATTRS-NEXT:    tail call void @use1(ptr dereferenceable(4) [[ARG]])
+; FNATTRS-NEXT:    ret void
+;
+; ATTRIBUTOR-LABEL: define void @f15
+; ATTRIBUTOR-SAME: (ptr nonnull [[ARG:%.*]]) {
+; ATTRIBUTOR-NEXT:    tail call void @use1(ptr nonnull dereferenceable(4) [[ARG]])
+; ATTRIBUTOR-NEXT:    ret void
+;
   tail call void @use1(ptr dereferenceable(4) %arg)
   ret void
 }
@@ -239,6 +488,28 @@ declare void @fun3(ptr, ptr, ptr) #1
 ; We can say that %a is nonnull but %b is not.
 define void @f16(ptr %a, ptr %b, i8 %c) {
 ; FIXME: missing nonnull on %a
+; FNATTRS-LABEL: define void @f16
+; FNATTRS-SAME: (ptr [[A:%.*]], ptr [[B:%.*]], i8 [[C:%.*]]) #[[ATTR6:[0-9]+]] {
+; FNATTRS-NEXT:    [[CMP:%.*]] = icmp eq i8 [[C]], 0
+; FNATTRS-NEXT:    br i1 [[CMP]], label [[IF_THEN:%.*]], label [[IF_ELSE:%.*]]
+; FNATTRS:       if.then:
+; FNATTRS-NEXT:    tail call void @fun2(ptr nonnull [[A]], ptr nonnull [[B]])
+; FNATTRS-NEXT:    ret void
+; FNATTRS:       if.else:
+; FNATTRS-NEXT:    tail call void @fun2(ptr nonnull [[A]], ptr [[B]])
+; FNATTRS-NEXT:    ret void
+;
+; ATTRIBUTOR-LABEL: define void @f16
+; ATTRIBUTOR-SAME: (ptr nonnull [[A:%.*]], ptr [[B:%.*]], i8 [[C:%.*]]) #[[ATTR7:[0-9]+]] {
+; ATTRIBUTOR-NEXT:    [[CMP:%.*]] = icmp eq i8 [[C]], 0
+; ATTRIBUTOR-NEXT:    br i1 [[CMP]], label [[IF_THEN:%.*]], label [[IF_ELSE:%.*]]
+; ATTRIBUTOR:       if.then:
+; ATTRIBUTOR-NEXT:    tail call void @fun2(ptr nonnull [[A]], ptr nonnull [[B]]) #[[ATTR16:[0-9]+]]
+; ATTRIBUTOR-NEXT:    ret void
+; ATTRIBUTOR:       if.else:
+; ATTRIBUTOR-NEXT:    tail call void @fun2(ptr nonnull [[A]], ptr [[B]]) #[[ATTR16]]
+; ATTRIBUTOR-NEXT:    ret void
+;
   %cmp = icmp eq i8 %c, 0
   br i1 %cmp, label %if.then, label %if.else
 if.then:
@@ -256,6 +527,34 @@ if.else:
 ; fun1(nonnull %a)
 ; We can say that %a is nonnull
 define void @f17(ptr %a, i8 %c) {
+; FNATTRS-LABEL: define void @f17
+; FNATTRS-SAME: (ptr [[A:%.*]], i8 [[C:%.*]]) #[[ATTR6]] {
+; FNATTRS-NEXT:    [[CMP:%.*]] = icmp eq i8 [[C]], 0
+; FNATTRS-NEXT:    br i1 [[CMP]], label [[IF_THEN:%.*]], label [[IF_ELSE:%.*]]
+; FNATTRS:       if.then:
+; FNATTRS-NEXT:    tail call void @fun0()
+; FNATTRS-NEXT:    br label [[CONT:%.*]]
+; FNATTRS:       if.else:
+; FNATTRS-NEXT:    tail call void @fun0()
+; FNATTRS-NEXT:    br label [[CONT]]
+; FNATTRS:       cont:
+; FNATTRS-NEXT:    tail call void @fun1(ptr nonnull [[A]])
+; FNATTRS-NEXT:    ret void
+;
+; ATTRIBUTOR-LABEL: define void @f17
+; ATTRIBUTOR-SAME: (ptr nonnull [[A:%.*]], i8 [[C:%.*]]) #[[ATTR7]] {
+; ATTRIBUTOR-NEXT:    [[CMP:%.*]] = icmp eq i8 [[C]], 0
+; ATTRIBUTOR-NEXT:    br i1 [[CMP]], label [[IF_THEN:%.*]], label [[IF_ELSE:%.*]]
+; ATTRIBUTOR:       if.then:
+; ATTRIBUTOR-NEXT:    tail call void @fun0() #[[ATTR16]]
+; ATTRIBUTOR-NEXT:    br label [[CONT:%.*]]
+; ATTRIBUTOR:       if.else:
+; ATTRIBUTOR-NEXT:    tail call void @fun0() #[[ATTR16]]
+; ATTRIBUTOR-NEXT:    br label [[CONT]]
+; ATTRIBUTOR:       cont:
+; ATTRIBUTOR-NEXT:    tail call void @fun1(ptr nonnull [[A]]) #[[ATTR16]]
+; ATTRIBUTOR-NEXT:    ret void
+;
   %cmp = icmp eq i8 %c, 0
   br i1 %cmp, label %if.then, label %if.else
 if.then:
@@ -280,6 +579,52 @@ cont:
 ; fun1(nonnull %a)
 
 define void @f18(ptr %a, ptr %b, i8 %c) {
+; FNATTRS-LABEL: define void @f18
+; FNATTRS-SAME: (ptr [[A:%.*]], ptr [[B:%.*]], i8 [[C:%.*]]) #[[ATTR6]] {
+; FNATTRS-NEXT:    [[CMP1:%.*]] = icmp eq i8 [[C]], 0
+; FNATTRS-NEXT:    br i1 [[CMP1]], label [[IF_THEN:%.*]], label [[IF_ELSE:%.*]]
+; FNATTRS:       if.then:
+; FNATTRS-NEXT:    tail call void @fun0()
+; FNATTRS-NEXT:    br label [[CONT:%.*]]
+; FNATTRS:       if.else:
+; FNATTRS-NEXT:    tail call void @fun0()
+; FNATTRS-NEXT:    br label [[CONT]]
+; FNATTRS:       cont:
+; FNATTRS-NEXT:    [[CMP2:%.*]] = icmp eq i8 [[C]], 1
+; FNATTRS-NEXT:    br i1 [[CMP2]], label [[CONT_THEN:%.*]], label [[CONT_ELSE:%.*]]
+; FNATTRS:       cont.then:
+; FNATTRS-NEXT:    tail call void @fun1(ptr nonnull [[B]])
+; FNATTRS-NEXT:    br label [[CONT2:%.*]]
+; FNATTRS:       cont.else:
+; FNATTRS-NEXT:    tail call void @fun0()
+; FNATTRS-NEXT:    br label [[CONT2]]
+; FNATTRS:       cont2:
+; FNATTRS-NEXT:    tail call void @fun1(ptr nonnull [[A]])
+; FNATTRS-NEXT:    ret void
+;
+; ATTRIBUTOR-LABEL: define void @f18
+; ATTRIBUTOR-SAME: (ptr nonnull [[A:%.*]], ptr [[B:%.*]], i8 [[C:%.*]]) #[[ATTR7]] {
+; ATTRIBUTOR-NEXT:    [[CMP1:%.*]] = icmp eq i8 [[C]], 0
+; ATTRIBUTOR-NEXT:    br i1 [[CMP1]], label [[IF_THEN:%.*]], label [[IF_ELSE:%.*]]
+; ATTRIBUTOR:       if.then:
+; ATTRIBUTOR-NEXT:    tail call void @fun0() #[[ATTR16]]
+; ATTRIBUTOR-NEXT:    br label [[CONT:%.*]]
+; ATTRIBUTOR:       if.else:
+; ATTRIBUTOR-NEXT:    tail call void @fun0() #[[ATTR16]]
+; ATTRIBUTOR-NEXT:    br label [[CONT]]
+; ATTRIBUTOR:       cont:
+; ATTRIBUTOR-NEXT:    [[CMP2:%.*]] = icmp eq i8 [[C]], 1
+; ATTRIBUTOR-NEXT:    br i1 [[CMP2]], label [[CONT_THEN:%.*]], label [[CONT_ELSE:%.*]]
+; ATTRIBUTOR:       cont.then:
+; ATTRIBUTOR-NEXT:    tail call void @fun1(ptr nonnull [[B]]) #[[ATTR16]]
+; ATTRIBUTOR-NEXT:    br label [[CONT2:%.*]]
+; ATTRIBUTOR:       cont.else:
+; ATTRIBUTOR-NEXT:    tail call void @fun0() #[[ATTR16]]
+; ATTRIBUTOR-NEXT:    br label [[CONT2]]
+; ATTRIBUTOR:       cont2:
+; ATTRIBUTOR-NEXT:    tail call void @fun1(ptr nonnull [[A]]) #[[ATTR16]]
+; ATTRIBUTOR-NEXT:    ret void
+;
   %cmp1 = icmp eq i8 %c, 0
   br i1 %cmp1, label %if.then, label %if.else
 if.then:
@@ -306,6 +651,34 @@ cont2:
 
 define void @f19(ptr %a, ptr %b, i8 %c) {
 ; FIXME: missing nonnull on %b
+; FNATTRS-LABEL: define void @f19
+; FNATTRS-SAME: (ptr [[A:%.*]], ptr [[B:%.*]], i8 [[C:%.*]]) #[[ATTR7:[0-9]+]] {
+; FNATTRS-NEXT:    br label [[LOOP_HEADER:%.*]]
+; FNATTRS:       loop.header:
+; FNATTRS-NEXT:    [[CMP2:%.*]] = icmp eq i8 [[C]], 0
+; FNATTRS-NEXT:    br i1 [[CMP2]], label [[LOOP_BODY:%.*]], label [[LOOP_EXIT:%.*]]
+; FNATTRS:       loop.body:
+; FNATTRS-NEXT:    tail call void @fun1(ptr nonnull [[B]])
+; FNATTRS-NEXT:    tail call void @fun1(ptr nonnull [[A]])
+; FNATTRS-NEXT:    br label [[LOOP_HEADER]]
+; FNATTRS:       loop.exit:
+; FNATTRS-NEXT:    tail call void @fun1(ptr nonnull [[B]])
+; FNATTRS-NEXT:    ret void
+;
+; ATTRIBUTOR-LABEL: define void @f19
+; ATTRIBUTOR-SAME: (ptr [[A:%.*]], ptr nonnull [[B:%.*]], i8 [[C:%.*]]) #[[ATTR8:[0-9]+]] {
+; ATTRIBUTOR-NEXT:    br label [[LOOP_HEADER:%.*]]
+; ATTRIBUTOR:       loop.header:
+; ATTRIBUTOR-NEXT:    [[CMP2:%.*]] = icmp eq i8 [[C]], 0
+; ATTRIBUTOR-NEXT:    br i1 [[CMP2]], label [[LOOP_BODY:%.*]], label [[LOOP_EXIT:%.*]]
+; ATTRIBUTOR:       loop.body:
+; ATTRIBUTOR-NEXT:    tail call void @fun1(ptr nonnull [[B]])
+; ATTRIBUTOR-NEXT:    tail call void @fun1(ptr nonnull [[A]])
+; ATTRIBUTOR-NEXT:    br label [[LOOP_HEADER]]
+; ATTRIBUTOR:       loop.exit:
+; ATTRIBUTOR-NEXT:    tail call void @fun1(ptr nonnull [[B]])
+; ATTRIBUTOR-NEXT:    ret void
+;
   br label %loop.header
 loop.header:
   %cmp2 = icmp eq i8 %c, 0
@@ -336,6 +709,16 @@ declare i8 @use1safecall(ptr %x) nounwind willreturn ; nounwind+willreturn guara
 
 define void @parent_poison(ptr %a) {
 ; FNATTR-LABEL: @parent_poison(ptr %a)
+; FNATTRS-LABEL: define void @parent_poison
+; FNATTRS-SAME: (ptr [[A:%.*]]) {
+; FNATTRS-NEXT:    call void @use1nonnull_without_noundef(ptr [[A]])
+; FNATTRS-NEXT:    ret void
+;
+; ATTRIBUTOR-LABEL: define void @parent_poison
+; ATTRIBUTOR-SAME: (ptr nonnull [[A:%.*]]) {
+; ATTRIBUTOR-NEXT:    call void @use1nonnull_without_noundef(ptr nonnull [[A]])
+; ATTRIBUTOR-NEXT:    ret void
+;
   call void @use1nonnull_without_noundef(ptr %a)
   ret void
 }
@@ -343,10 +726,12 @@ define void @parent_poison(ptr %a) {
 ; Can't extend non-null to parent for any argument because the 2nd call is not guaranteed to execute.
 
 define void @parent1(ptr %a, ptr %b, ptr %c) {
-; FNATTR-LABEL: @parent1(ptr %a, ptr %b, ptr %c)
-; FNATTR-NEXT:    call void @use3(ptr %c, ptr %a, ptr %b)
-; FNATTR-NEXT:    call void @use3nonnull(ptr %b, ptr %c, ptr %a)
-; FNATTR-NEXT:    ret void
+; COMMON-LABEL: define void @parent1
+; COMMON-SAME: (ptr [[A:%.*]], ptr [[B:%.*]], ptr [[C:%.*]]) {
+; COMMON-NEXT:    call void @use3(ptr [[C]], ptr [[A]], ptr [[B]])
+; COMMON-NEXT:    call void @use3nonnull(ptr [[B]], ptr [[C]], ptr [[A]])
+; COMMON-NEXT:    ret void
+;
   call void @use3(ptr %c, ptr %a, ptr %b)
   call void @use3nonnull(ptr %b, ptr %c, ptr %a)
   ret void
@@ -355,12 +740,20 @@ define void @parent1(ptr %a, ptr %b, ptr %c) {
 ; Extend non-null to parent for all arguments.
 
 define void @parent2(ptr %a, ptr %b, ptr %c) {
-; FNATTR-LABEL: @parent2(ptr nonnull %a, ptr nonnull %b, ptr nonnull %c)
-; FNATTR-NEXT:    call void @use3nonnull(ptr %b, ptr %c, ptr %a)
-; FNATTR-NEXT:    call void @use3(ptr %c, ptr %a, ptr %b)
+; FNATTRS-LABEL: define void @parent2
+; FNATTRS-SAME: (ptr nonnull [[A:%.*]], ptr nonnull [[B:%.*]], ptr nonnull [[C:%.*]]) {
+; FNATTRS-NEXT:    call void @use3nonnull(ptr [[B]], ptr [[C]], ptr [[A]])
+; FNATTRS-NEXT:    call void @use3(ptr [[C]], ptr [[A]], ptr [[B]])
+; FNATTRS-NEXT:    ret void
+;
+; ATTRIBUTOR-LABEL: define void @parent2
+; ATTRIBUTOR-SAME: (ptr nonnull [[A:%.*]], ptr nonnull [[B:%.*]], ptr nonnull [[C:%.*]]) {
+; ATTRIBUTOR-NEXT:    call void @use3nonnull(ptr nonnull [[B]], ptr nonnull [[C]], ptr nonnull [[A]])
+; ATTRIBUTOR-NEXT:    call void @use3(ptr [[C]], ptr [[A]], ptr [[B]])
+; ATTRIBUTOR-NEXT:    ret void
+;
 
 
-; FNATTR-NEXT:    ret void
   call void @use3nonnull(ptr %b, ptr %c, ptr %a)
   call void @use3(ptr %c, ptr %a, ptr %b)
   ret void
@@ -369,12 +762,20 @@ define void @parent2(ptr %a, ptr %b, ptr %c) {
 ; Extend non-null to parent for 1st argument.
 
 define void @parent3(ptr %a, ptr %b, ptr %c) {
-; FNATTR-LABEL: @parent3(ptr nonnull %a, ptr %b, ptr %c)
-; FNATTR-NEXT:    call void @use1nonnull(ptr %a)
-; FNATTR-NEXT:    call void @use3(ptr %c, ptr %b, ptr %a)
+; FNATTRS-LABEL: define void @parent3
+; FNATTRS-SAME: (ptr nonnull [[A:%.*]], ptr [[B:%.*]], ptr [[C:%.*]]) {
+; FNATTRS-NEXT:    call void @use1nonnull(ptr [[A]])
+; FNATTRS-NEXT:    call void @use3(ptr [[C]], ptr [[B]], ptr [[A]])
+; FNATTRS-NEXT:    ret void
+;
+; ATTRIBUTOR-LABEL: define void @parent3
+; ATTRIBUTOR-SAME: (ptr nonnull [[A:%.*]], ptr [[B:%.*]], ptr [[C:%.*]]) {
+; ATTRIBUTOR-NEXT:    call void @use1nonnull(ptr nonnull [[A]])
+; ATTRIBUTOR-NEXT:    call void @use3(ptr [[C]], ptr [[B]], ptr [[A]])
+; ATTRIBUTOR-NEXT:    ret void
+;
 
 
-; FNATTR-NEXT:  ret void
 
   call void @use1nonnull(ptr %a)
   call void @use3(ptr %c, ptr %b, ptr %a)
@@ -388,9 +789,20 @@ define void @parent4(ptr %a, ptr %b, ptr %c) {
 ; CHECK-NEXT:    call void @use2nonnull(ptr %c, ptr %b)
 ; CHECK-NEXT:    call void @use2(ptr %a, ptr %c)
 ; CHECK-NEXT:    call void @use1(ptr %b)
-
-
-; FNATTR: ret void
+; FNATTRS-LABEL: define void @parent4
+; FNATTRS-SAME: (ptr [[A:%.*]], ptr nonnull [[B:%.*]], ptr nonnull [[C:%.*]]) {
+; FNATTRS-NEXT:    call void @use2nonnull(ptr [[C]], ptr [[B]])
+; FNATTRS-NEXT:    call void @use2(ptr [[A]], ptr [[C]])
+; FNATTRS-NEXT:    call void @use1(ptr [[B]])
+; FNATTRS-NEXT:    ret void
+;
+; ATTRIBUTOR-LABEL: define void @parent4
+; ATTRIBUTOR-SAME: (ptr [[A:%.*]], ptr nonnull [[B:%.*]], ptr nonnull [[C:%.*]]) {
+; ATTRIBUTOR-NEXT:    call void @use2nonnull(ptr nonnull [[C]], ptr nonnull [[B]])
+; ATTRIBUTOR-NEXT:    call void @use2(ptr [[A]], ptr [[C]])
+; ATTRIBUTOR-NEXT:    call void @use1(ptr [[B]])
+; ATTRIBUTOR-NEXT:    ret void
+;
 
   call void @use2nonnull(ptr %c, ptr %b)
   call void @use2(ptr %a, ptr %c)
@@ -403,13 +815,24 @@ define void @parent4(ptr %a, ptr %b, ptr %c) {
 ; because it would incorrectly propagate the wrong information to its callers.
 
 define void @parent5(ptr %a, i1 %a_is_notnull) {
-; FNATTR: @parent5(ptr %a, i1 %a_is_notnull)
-; FNATTR-NEXT:    br i1 %a_is_notnull, label %t, label %f
-; FNATTR:       t:
-; FNATTR-NEXT:    call void @use1nonnull(ptr %a)
-; FNATTR-NEXT:    ret void
-; FNATTR:       f:
-; FNATTR-NEXT:    ret void
+; FNATTRS-LABEL: define void @parent5
+; FNATTRS-SAME: (ptr [[A:%.*]], i1 [[A_IS_NOTNULL:%.*]]) {
+; FNATTRS-NEXT:    br i1 [[A_IS_NOTNULL]], label [[T:%.*]], label [[F:%.*]]
+; FNATTRS:       t:
+; FNATTRS-NEXT:    call void @use1nonnull(ptr [[A]])
+; FNATTRS-NEXT:    ret void
+; FNATTRS:       f:
+; FNATTRS-NEXT:    ret void
+;
+; ATTRIBUTOR-LABEL: define void @parent5
+; ATTRIBUTOR-SAME: (ptr [[A:%.*]], i1 [[A_IS_NOTNULL:%.*]]) {
+; ATTRIBUTOR-NEXT:    br i1 [[A_IS_NOTNULL]], label [[T:%.*]], label [[F:%.*]]
+; ATTRIBUTOR:       t:
+; ATTRIBUTOR-NEXT:    call void @use1nonnull(ptr nonnull [[A]])
+; ATTRIBUTOR-NEXT:    ret void
+; ATTRIBUTOR:       f:
+; ATTRIBUTOR-NEXT:    ret void
+;
 
   br i1 %a_is_notnull, label %t, label %f
 t:
@@ -423,10 +846,18 @@ f:
 ; The volatile load can't trap, so we can guarantee that we'll get to the call.
 
 define i8 @parent6(ptr %a, ptr %b) {
-; FNATTR-LABEL: @parent6(ptr nonnull %a, ptr %b)
-; FNATTR-NEXT:    [[C:%.*]] = load volatile i8, ptr %b
-; FNATTR-NEXT:    call void @use1nonnull(ptr %a)
-; FNATTR-NEXT:    ret i8 [[C]]
+; FNATTRS-LABEL: define i8 @parent6
+; FNATTRS-SAME: (ptr nonnull [[A:%.*]], ptr [[B:%.*]]) {
+; FNATTRS-NEXT:    [[C:%.*]] = load volatile i8, ptr [[B]], align 1
+; FNATTRS-NEXT:    call void @use1nonnull(ptr [[A]])
+; FNATTRS-NEXT:    ret i8 [[C]]
+;
+; ATTRIBUTOR-LABEL: define i8 @parent6
+; ATTRIBUTOR-SAME: (ptr nonnull [[A:%.*]], ptr nofree [[B:%.*]]) {
+; ATTRIBUTOR-NEXT:    [[C:%.*]] = load volatile i8, ptr [[B]], align 1
+; ATTRIBUTOR-NEXT:    call void @use1nonnull(ptr nonnull [[A]])
+; ATTRIBUTOR-NEXT:    ret i8 [[C]]
+;
 
   %c = load volatile i8, ptr %b
   call void @use1nonnull(ptr %a)
@@ -436,13 +867,21 @@ define i8 @parent6(ptr %a, ptr %b) {
 ; The nonnull callsite is guaranteed to execute, so the argument must be nonnull throughout the parent.
 
 define i8 @parent7(ptr %a) {
-; FNATTR-LABEL: @parent7(ptr nonnull %a)
-; FNATTR-NEXT:    [[RET:%.*]] = call i8 @use1safecall(ptr %a)
-; FNATTR-NEXT:    call void @use1nonnull(ptr %a)
+; FNATTRS-LABEL: define i8 @parent7
+; FNATTRS-SAME: (ptr nonnull [[A:%.*]]) {
+; FNATTRS-NEXT:    [[RET:%.*]] = call i8 @use1safecall(ptr [[A]])
+; FNATTRS-NEXT:    call void @use1nonnull(ptr [[A]])
+; FNATTRS-NEXT:    ret i8 [[RET]]
+;
+; ATTRIBUTOR-LABEL: define i8 @parent7
+; ATTRIBUTOR-SAME: (ptr nonnull [[A:%.*]]) {
+; ATTRIBUTOR-NEXT:    [[RET:%.*]] = call i8 @use1safecall(ptr nonnull [[A]]) #[[ATTR16]]
+; ATTRIBUTOR-NEXT:    call void @use1nonnull(ptr nonnull [[A]])
+; ATTRIBUTOR-NEXT:    ret i8 [[RET]]
+;
 
 
 
-; FNATTR-NEXT: ret i8 [[RET]]
 
   %ret = call i8 @use1safecall(ptr %a)
   call void @use1nonnull(ptr %a)
@@ -454,17 +893,32 @@ define i8 @parent7(ptr %a) {
 declare i32 @esfp(...)
 
 define i1 @parent8(ptr %a, ptr %bogus1, ptr %b) personality ptr @esfp{
-; FNATTR-LABEL: @parent8(ptr nonnull %a, ptr nocapture readnone %bogus1, ptr nonnull %b)
-; FNATTR-NEXT:  entry:
-; FNATTR-NEXT:    invoke void @use2nonnull(ptr %a, ptr %b)
-; FNATTR-NEXT:    to label %cont unwind label %exc
-; FNATTR:       cont:
-; FNATTR-NEXT:    [[NULL_CHECK:%.*]] = icmp eq ptr %b, null
-; FNATTR-NEXT:    ret i1 [[NULL_CHECK]]
-; FNATTR:       exc:
-; FNATTR-NEXT:    [[LP:%.*]] = landingpad { ptr, i32 }
-; FNATTR-NEXT:    filter [0 x ptr] zeroinitializer
-; FNATTR-NEXT:    unreachable
+; FNATTRS-LABEL: define i1 @parent8
+; FNATTRS-SAME: (ptr nonnull [[A:%.*]], ptr nocapture readnone [[BOGUS1:%.*]], ptr nonnull [[B:%.*]]) #[[ATTR7]] personality ptr @esfp {
+; FNATTRS-NEXT:  entry:
+; FNATTRS-NEXT:    invoke void @use2nonnull(ptr [[A]], ptr [[B]])
+; FNATTRS-NEXT:    to label [[CONT:%.*]] unwind label [[EXC:%.*]]
+; FNATTRS:       cont:
+; FNATTRS-NEXT:    [[NULL_CHECK:%.*]] = icmp eq ptr [[B]], null
+; FNATTRS-NEXT:    ret i1 [[NULL_CHECK]]
+; FNATTRS:       exc:
+; FNATTRS-NEXT:    [[LP:%.*]] = landingpad { ptr, i32 }
+; FNATTRS-NEXT:    filter [0 x ptr] zeroinitializer
+; FNATTRS-NEXT:    unreachable
+;
+; ATTRIBUTOR-LABEL: define i1 @parent8
+; ATTRIBUTOR-SAME: (ptr nonnull [[A:%.*]], ptr nocapture nofree readnone [[BOGUS1:%.*]], ptr nonnull [[B:%.*]]) #[[ATTR8]] personality ptr @esfp {
+; ATTRIBUTOR-NEXT:  entry:
+; ATTRIBUTOR-NEXT:    invoke void @use2nonnull(ptr nonnull [[A]], ptr nonnull [[B]])
+; ATTRIBUTOR-NEXT:    to label [[CONT:%.*]] unwind label [[EXC:%.*]]
+; ATTRIBUTOR:       cont:
+; ATTRIBUTOR-NEXT:    [[NULL_CHECK:%.*]] = icmp eq ptr [[B]], null
+; ATTRIBUTOR-NEXT:    ret i1 [[NULL_CHECK]]
+; ATTRIBUTOR:       exc:
+; ATTRIBUTOR-NEXT:    [[LP:%.*]] = landingpad { ptr, i32 }
+; ATTRIBUTOR-NEXT:    filter [0 x ptr] zeroinitializer
+; ATTRIBUTOR-NEXT:    unreachable
+;
 
 entry:
   invoke void @use2nonnull(ptr %a, ptr %b)
@@ -480,69 +934,184 @@ exc:
   unreachable
 }
 
-; FNATTR: define nonnull ptr @gep1(
 define ptr @gep1(ptr %p) {
+; FNATTRS-LABEL: define nonnull ptr @gep1
+; FNATTRS-SAME: (ptr readnone [[P:%.*]]) #[[ATTR0]] {
+; FNATTRS-NEXT:    [[Q:%.*]] = getelementptr inbounds i32, ptr [[P]], i32 1
+; FNATTRS-NEXT:    ret ptr [[Q]]
+;
+; ATTRIBUTOR-LABEL: define nonnull ptr @gep1
+; ATTRIBUTOR-SAME: (ptr nofree readnone [[P:%.*]]) #[[ATTR0]] {
+; ATTRIBUTOR-NEXT:    [[Q:%.*]] = getelementptr inbounds i32, ptr [[P]], i32 1
+; ATTRIBUTOR-NEXT:    ret ptr [[Q]]
+;
   %q = getelementptr inbounds i32, ptr %p, i32 1
   ret ptr %q
 }
 
 define ptr @gep1_no_null_opt(ptr %p) #0 {
 ; Should't be able to derive nonnull based on gep.
-; FNATTR: define ptr @gep1_no_null_opt(
+; FNATTRS-LABEL: define ptr @gep1_no_null_opt
+; FNATTRS-SAME: (ptr readnone [[P:%.*]]) #[[ATTR8:[0-9]+]] {
+; FNATTRS-NEXT:    [[Q:%.*]] = getelementptr inbounds i32, ptr [[P]], i32 1
+; FNATTRS-NEXT:    ret ptr [[Q]]
+;
+; ATTRIBUTOR-LABEL: define ptr @gep1_no_null_opt
+; ATTRIBUTOR-SAME: (ptr nofree readnone [[P:%.*]]) #[[ATTR9:[0-9]+]] {
+; ATTRIBUTOR-NEXT:    [[Q:%.*]] = getelementptr inbounds i32, ptr [[P]], i32 1
+; ATTRIBUTOR-NEXT:    ret ptr [[Q]]
+;
   %q = getelementptr inbounds i32, ptr %p, i32 1
   ret ptr %q
 }
 
-; FNATTR: define ptr addrspace(3) @gep2(
 define ptr addrspace(3) @gep2(ptr addrspace(3) %p) {
+; FNATTRS-LABEL: define ptr addrspace(3) @gep2
+; FNATTRS-SAME: (ptr addrspace(3) readnone [[P:%.*]]) #[[ATTR0]] {
+; FNATTRS-NEXT:    [[Q:%.*]] = getelementptr inbounds i32, ptr addrspace(3) [[P]], i32 1
+; FNATTRS-NEXT:    ret ptr addrspace(3) [[Q]]
+;
+; ATTRIBUTOR-LABEL: define ptr addrspace(3) @gep2
+; ATTRIBUTOR-SAME: (ptr addrspace(3) nofree readnone [[P:%.*]]) #[[ATTR0]] {
+; ATTRIBUTOR-NEXT:    [[Q:%.*]] = getelementptr inbounds i32, ptr addrspace(3) [[P]], i32 1
+; ATTRIBUTOR-NEXT:    ret ptr addrspace(3) [[Q]]
+;
   %q = getelementptr inbounds i32, ptr addrspace(3) %p, i32 1
   ret ptr addrspace(3) %q
 }
 
-; FNATTR:     define ptr addrspace(3) @as(ptr addrspace(3) readnone returned dereferenceable(4) %p)
 ; FIXME: We should propagate dereferenceable here but *not* nonnull
 define ptr addrspace(3) @as(ptr addrspace(3) dereferenceable(4) %p) {
+; FNATTRS-LABEL: define ptr addrspace(3) @as
+; FNATTRS-SAME: (ptr addrspace(3) readnone returned dereferenceable(4) [[P:%.*]]) #[[ATTR0]] {
+; FNATTRS-NEXT:    ret ptr addrspace(3) [[P]]
+;
+; ATTRIBUTOR-LABEL: define ptr addrspace(3) @as
+; ATTRIBUTOR-SAME: (ptr addrspace(3) nofree readnone dereferenceable(4) [[P:%.*]]) #[[ATTR0]] {
+; ATTRIBUTOR-NEXT:    ret ptr addrspace(3) [[P]]
+;
   ret ptr addrspace(3) %p
 }
 
-; FNATTR: define internal nonnull ptr @g2()
 define internal ptr @g2() {
+; FNATTRS-LABEL: define internal nonnull ptr @g2
+; FNATTRS-SAME: () #[[ATTR0]] {
+; FNATTRS-NEXT:    ret ptr inttoptr (i64 4 to ptr)
+;
+; ATTRIBUTOR-LABEL: define internal ptr @g2
+; ATTRIBUTOR-SAME: () #[[ATTR10:[0-9]+]] {
+; ATTRIBUTOR-NEXT:    ret ptr inttoptr (i64 4 to ptr)
+;
   ret ptr inttoptr (i64 4 to ptr)
 }
 
 define  ptr @g1() {
- %c = call ptr @g2()
+; FNATTRS-LABEL: define nonnull ptr @g1
+; FNATTRS-SAME: () #[[ATTR0]] {
+; FNATTRS-NEXT:    [[C:%.*]] = call ptr @g2()
+; FNATTRS-NEXT:    ret ptr [[C]]
+;
+; ATTRIBUTOR-LABEL: define ptr @g1
+; ATTRIBUTOR-SAME: () #[[ATTR0]] {
+; ATTRIBUTOR-NEXT:    [[C:%.*]] = call ptr @g2() #[[ATTR10]]
+; ATTRIBUTOR-NEXT:    ret ptr [[C]]
+;
+  %c = call ptr @g2()
   ret ptr %c
 }
 
 declare void @use_i32_ptr(ptr) readnone nounwind
 define internal void @called_by_weak(ptr %a) {
+; FNATTRS-LABEL: define internal void @called_by_weak
+; FNATTRS-SAME: (ptr nocapture readnone [[A:%.*]]) #[[ATTR1]] {
+; FNATTRS-NEXT:    call void @use_i32_ptr(ptr [[A]])
+; FNATTRS-NEXT:    ret void
+;
+; ATTRIBUTOR-LABEL: define internal void @called_by_weak
+; ATTRIBUTOR-SAME: (ptr nocapture readnone [[A:%.*]]) #[[ATTR11:[0-9]+]] {
+; ATTRIBUTOR-NEXT:    call void @use_i32_ptr(ptr [[A]])
+; ATTRIBUTOR-NEXT:    ret void
+;
   call void @use_i32_ptr(ptr %a)
   ret void
 }
 
 ; Check we do not annotate the function interface of this weak function.
 define weak_odr void @weak_caller(ptr nonnull %a) {
+; FNATTRS-LABEL: define weak_odr void @weak_caller
+; FNATTRS-SAME: (ptr nonnull [[A:%.*]]) {
+; FNATTRS-NEXT:    call void @called_by_weak(ptr [[A]])
+; FNATTRS-NEXT:    ret void
+;
+; ATTRIBUTOR-LABEL: define weak_odr void @weak_caller
+; ATTRIBUTOR-SAME: (ptr nonnull [[A:%.*]]) {
+; ATTRIBUTOR-NEXT:    call void @called_by_weak(ptr nocapture nonnull readnone [[A]])
+; ATTRIBUTOR-NEXT:    ret void
+;
   call void @called_by_weak(ptr %a)
   ret void
 }
 
 ; Expect nonnull
 define internal void @control(ptr dereferenceable(4) %a) {
+; FNATTRS-LABEL: define internal void @control
+; FNATTRS-SAME: (ptr nocapture readnone dereferenceable(4) [[A:%.*]]) #[[ATTR1]] {
+; FNATTRS-NEXT:    call void @use_i32_ptr(ptr [[A]])
+; FNATTRS-NEXT:    ret void
+;
+; ATTRIBUTOR-LABEL: define internal void @control
+; ATTRIBUTOR-SAME: (ptr nocapture readnone dereferenceable(4) [[A:%.*]]) #[[ATTR11]] {
+; ATTRIBUTOR-NEXT:    call void @use_i32_ptr(ptr [[A]])
+; ATTRIBUTOR-NEXT:    ret void
+;
   call void @use_i32_ptr(ptr %a)
   ret void
 }
 ; Avoid nonnull as we do not touch naked functions
 define internal void @naked(ptr dereferenceable(4) %a) naked {
+; FNATTRS-LABEL: define internal void @naked
+; FNATTRS-SAME: (ptr dereferenceable(4) [[A:%.*]]) #[[ATTR10:[0-9]+]] {
+; FNATTRS-NEXT:    call void @use_i32_ptr(ptr [[A]])
+; FNATTRS-NEXT:    ret void
+;
+; ATTRIBUTOR-LABEL: define internal void @naked
+; ATTRIBUTOR-SAME: (ptr dereferenceable(4) [[A:%.*]]) #[[ATTR12:[0-9]+]] {
+; ATTRIBUTOR-NEXT:    call void @use_i32_ptr(ptr [[A]])
+; ATTRIBUTOR-NEXT:    ret void
+;
   call void @use_i32_ptr(ptr %a)
   ret void
 }
 ; Avoid nonnull as we do not touch optnone
 define internal void @optnone(ptr dereferenceable(4) %a) optnone noinline {
+; FNATTRS-LABEL: define internal void @optnone
+; FNATTRS-SAME: (ptr dereferenceable(4) [[A:%.*]]) #[[ATTR11:[0-9]+]] {
+; FNATTRS-NEXT:    call void @use_i32_ptr(ptr [[A]])
+; FNATTRS-NEXT:    ret void
+;
+; ATTRIBUTOR-LABEL: define internal void @optnone
+; ATTRIBUTOR-SAME: (ptr dereferenceable(4) [[A:%.*]]) #[[ATTR13:[0-9]+]] {
+; ATTRIBUTOR-NEXT:    call void @use_i32_ptr(ptr [[A]])
+; ATTRIBUTOR-NEXT:    ret void
+;
   call void @use_i32_ptr(ptr %a)
   ret void
 }
 define void @make_live(ptr nonnull dereferenceable(8) %a) {
+; FNATTRS-LABEL: define void @make_live
+; FNATTRS-SAME: (ptr nonnull dereferenceable(8) [[A:%.*]]) {
+; FNATTRS-NEXT:    call void @naked(ptr nonnull align 16 dereferenceable(8) [[A]])
+; FNATTRS-NEXT:    call void @control(ptr nonnull align 16 dereferenceable(8) [[A]])
+; FNATTRS-NEXT:    call void @optnone(ptr nonnull align 16 dereferenceable(8) [[A]])
+; FNATTRS-NEXT:    ret void
+;
+; ATTRIBUTOR-LABEL: define void @make_live
+; ATTRIBUTOR-SAME: (ptr nonnull dereferenceable(8) [[A:%.*]]) {
+; ATTRIBUTOR-NEXT:    call void @naked(ptr nonnull align 16 dereferenceable(8) [[A]])
+; ATTRIBUTOR-NEXT:    call void @control(ptr nocapture nonnull readnone align 16 dereferenceable(8) [[A]])
+; ATTRIBUTOR-NEXT:    call void @optnone(ptr nonnull align 16 dereferenceable(8) [[A]])
+; ATTRIBUTOR-NEXT:    ret void
+;
   call void @naked(ptr nonnull dereferenceable(8) align 16 %a)
   call void @control(ptr nonnull dereferenceable(8) align 16 %a)
   call void @optnone(ptr nonnull dereferenceable(8) align 16 %a)
@@ -558,21 +1127,35 @@ define void @make_live(ptr nonnull dereferenceable(8) %a) {
 declare void @h(ptr) willreturn nounwind
 declare i32 @g(ptr) willreturn nounwind
 define i32 @nonnull_exec_ctx_1(ptr %a, i32 %b) {
-; FNATTR-LABEL: define {{[^@]+}}@nonnull_exec_ctx_1
-; FNATTR-SAME: (ptr [[A:%.*]], i32 [[B:%.*]])
-; FNATTR-NEXT:  en:
-; FNATTR-NEXT:    [[TMP3:%.*]] = icmp eq i32 [[B:%.*]], 0
-; FNATTR-NEXT:    br i1 [[TMP3]], label [[EX:%.*]], label [[HD:%.*]]
-; FNATTR:       ex:
-; FNATTR-NEXT:    [[TMP5:%.*]] = tail call i32 @g(ptr nonnull [[A:%.*]])
-; FNATTR-NEXT:    ret i32 [[TMP5]]
-; FNATTR:       hd:
-; FNATTR-NEXT:    [[TMP7:%.*]] = phi i32 [ [[TMP8:%.*]], [[HD]] ], [ 0, [[EN:%.*]] ]
-; FNATTR-NEXT:    tail call void @h(ptr [[A]])
-; FNATTR-NEXT:    [[TMP8]] = add nuw i32 [[TMP7]], 1
-; FNATTR-NEXT:    [[TMP9:%.*]] = icmp eq i32 [[TMP8]], [[B]]
-; FNATTR-NEXT:    br i1 [[TMP9]], label [[EX]], label [[HD]]
+; FNATTRS-LABEL: define i32 @nonnull_exec_ctx_1
+; FNATTRS-SAME: (ptr [[A:%.*]], i32 [[B:%.*]]) #[[ATTR7]] {
+; FNATTRS-NEXT:  en:
+; FNATTRS-NEXT:    [[TMP3:%.*]] = icmp eq i32 [[B]], 0
+; FNATTRS-NEXT:    br i1 [[TMP3]], label [[EX:%.*]], label [[HD:%.*]]
+; FNATTRS:       ex:
+; FNATTRS-NEXT:    [[TMP5:%.*]] = tail call i32 @g(ptr nonnull [[A]])
+; FNATTRS-NEXT:    ret i32 [[TMP5]]
+; FNATTRS:       hd:
+; FNATTRS-NEXT:    [[TMP7:%.*]] = phi i32 [ [[TMP8:%.*]], [[HD]] ], [ 0, [[EN:%.*]] ]
+; FNATTRS-NEXT:    tail call void @h(ptr [[A]])
+; FNATTRS-NEXT:    [[TMP8]] = add nuw i32 [[TMP7]], 1
+; FNATTRS-NEXT:    [[TMP9:%.*]] = icmp eq i32 [[TMP8]], [[B]]
+; FNATTRS-NEXT:    br i1 [[TMP9]], label [[EX]], label [[HD]]
 ;
+; ATTRIBUTOR-LABEL: define i32 @nonnull_exec_ctx_1
+; ATTRIBUTOR-SAME: (ptr [[A:%.*]], i32 [[B:%.*]]) #[[ATTR8]] {
+; ATTRIBUTOR-NEXT:  en:
+; ATTRIBUTOR-NEXT:    [[TMP3:%.*]] = icmp eq i32 [[B]], 0
+; ATTRIBUTOR-NEXT:    br i1 [[TMP3]], label [[EX:%.*]], label [[HD:%.*]]
+; ATTRIBUTOR:       ex:
+; ATTRIBUTOR-NEXT:    [[TMP5:%.*]] = tail call i32 @g(ptr nonnull [[A]])
+; ATTRIBUTOR-NEXT:    ret i32 [[TMP5]]
+; ATTRIBUTOR:       hd:
+; ATTRIBUTOR-NEXT:    [[TMP7:%.*]] = phi i32 [ [[TMP8:%.*]], [[HD]] ], [ 0, [[EN:%.*]] ]
+; ATTRIBUTOR-NEXT:    tail call void @h(ptr [[A]])
+; ATTRIBUTOR-NEXT:    [[TMP8]] = add nuw i32 [[TMP7]], 1
+; ATTRIBUTOR-NEXT:    [[TMP9:%.*]] = icmp eq i32 [[TMP8]], [[B]]
+; ATTRIBUTOR-NEXT:    br i1 [[TMP9]], label [[EX]], label [[HD]]
 ;
 en:
   %tmp3 = icmp eq i32 %b, 0
@@ -591,23 +1174,39 @@ hd:
 }
 
 define i32 @nonnull_exec_ctx_1b(ptr %a, i32 %b) {
-; FNATTR-LABEL: define {{[^@]+}}@nonnull_exec_ctx_1b
-; FNATTR-SAME: (ptr [[A:%.*]], i32 [[B:%.*]])
-; FNATTR-NEXT:  en:
-; FNATTR-NEXT:    [[TMP3:%.*]] = icmp eq i32 [[B:%.*]], 0
-; FNATTR-NEXT:    br i1 [[TMP3]], label [[EX:%.*]], label [[HD:%.*]]
-; FNATTR:       ex:
-; FNATTR-NEXT:    [[TMP5:%.*]] = tail call i32 @g(ptr nonnull [[A:%.*]])
-; FNATTR-NEXT:    ret i32 [[TMP5]]
-; FNATTR:       hd:
-; FNATTR-NEXT:    [[TMP7:%.*]] = phi i32 [ [[TMP8:%.*]], [[HD2:%.*]] ], [ 0, [[EN:%.*]] ]
-; FNATTR-NEXT:    tail call void @h(ptr [[A]])
-; FNATTR-NEXT:    br label [[HD2]]
-; FNATTR:       hd2:
-; FNATTR-NEXT:    [[TMP8]] = add nuw i32 [[TMP7]], 1
-; FNATTR-NEXT:    [[TMP9:%.*]] = icmp eq i32 [[TMP8]], [[B]]
-; FNATTR-NEXT:    br i1 [[TMP9]], label [[EX]], label [[HD]]
+; FNATTRS-LABEL: define i32 @nonnull_exec_ctx_1b
+; FNATTRS-SAME: (ptr [[A:%.*]], i32 [[B:%.*]]) #[[ATTR7]] {
+; FNATTRS-NEXT:  en:
+; FNATTRS-NEXT:    [[TMP3:%.*]] = icmp eq i32 [[B]], 0
+; FNATTRS-NEXT:    br i1 [[TMP3]], label [[EX:%.*]], label [[HD:%.*]]
+; FNATTRS:       ex:
+; FNATTRS-NEXT:    [[TMP5:%.*]] = tail call i32 @g(ptr nonnull [[A]])
+; FNATTRS-NEXT:    ret i32 [[TMP5]]
+; FNATTRS:       hd:
+; FNATTRS-NEXT:    [[TMP7:%.*]] = phi i32 [ [[TMP8:%.*]], [[HD2:%.*]] ], [ 0, [[EN:%.*]] ]
+; FNATTRS-NEXT:    tail call void @h(ptr [[A]])
+; FNATTRS-NEXT:    br label [[HD2]]
+; FNATTRS:       hd2:
+; FNATTRS-NEXT:    [[TMP8]] = add nuw i32 [[TMP7]], 1
+; FNATTRS-NEXT:    [[TMP9:%.*]] = icmp eq i32 [[TMP8]], [[B]]
+; FNATTRS-NEXT:    br i1 [[TMP9]], label [[EX]], label [[HD]]
 ;
+; ATTRIBUTOR-LABEL: define i32 @nonnull_exec_ctx_1b
+; ATTRIBUTOR-SAME: (ptr [[A:%.*]], i32 [[B:%.*]]) #[[ATTR8]] {
+; ATTRIBUTOR-NEXT:  en:
+; ATTRIBUTOR-NEXT:    [[TMP3:%.*]] = icmp eq i32 [[B]], 0
+; ATTRIBUTOR-NEXT:    br i1 [[TMP3]], label [[EX:%.*]], label [[HD:%.*]]
+; ATTRIBUTOR:       ex:
+; ATTRIBUTOR-NEXT:    [[TMP5:%.*]] = tail call i32 @g(ptr nonnull [[A]])
+; ATTRIBUTOR-NEXT:    ret i32 [[TMP5]]
+; ATTRIBUTOR:       hd:
+; ATTRIBUTOR-NEXT:    [[TMP7:%.*]] = phi i32 [ [[TMP8:%.*]], [[HD2:%.*]] ], [ 0, [[EN:%.*]] ]
+; ATTRIBUTOR-NEXT:    tail call void @h(ptr [[A]])
+; ATTRIBUTOR-NEXT:    br label [[HD2]]
+; ATTRIBUTOR:       hd2:
+; ATTRIBUTOR-NEXT:    [[TMP8]] = add nuw i32 [[TMP7]], 1
+; ATTRIBUTOR-NEXT:    [[TMP9:%.*]] = icmp eq i32 [[TMP8]], [[B]]
+; ATTRIBUTOR-NEXT:    br i1 [[TMP9]], label [[EX]], label [[HD]]
 ;
 en:
   %tmp3 = icmp eq i32 %b, 0
@@ -629,21 +1228,35 @@ hd2:
 }
 
 define i32 @nonnull_exec_ctx_2(ptr %a, i32 %b) willreturn nounwind {
-; FNATTR-LABEL: define {{[^@]+}}@nonnull_exec_ctx_2
-; FNATTR-SAME: (ptr [[A:%.*]], i32 [[B:%.*]])
-; FNATTR-NEXT:  en:
-; FNATTR-NEXT:    [[TMP3:%.*]] = icmp eq i32 [[B:%.*]], 0
-; FNATTR-NEXT:    br i1 [[TMP3]], label [[EX:%.*]], label [[HD:%.*]]
-; FNATTR:       ex:
-; FNATTR-NEXT:    [[TMP5:%.*]] = tail call i32 @g(ptr nonnull [[A:%.*]])
-; FNATTR-NEXT:    ret i32 [[TMP5]]
-; FNATTR:       hd:
-; FNATTR-NEXT:    [[TMP7:%.*]] = phi i32 [ [[TMP8:%.*]], [[HD]] ], [ 0, [[EN:%.*]] ]
-; FNATTR-NEXT:    tail call void @h(ptr [[A]])
-; FNATTR-NEXT:    [[TMP8]] = add nuw i32 [[TMP7]], 1
-; FNATTR-NEXT:    [[TMP9:%.*]] = icmp eq i32 [[TMP8]], [[B]]
-; FNATTR-NEXT:    br i1 [[TMP9]], label [[EX]], label [[HD]]
+; FNATTRS-LABEL: define i32 @nonnull_exec_ctx_2
+; FNATTRS-SAME: (ptr [[A:%.*]], i32 [[B:%.*]]) #[[ATTR6]] {
+; FNATTRS-NEXT:  en:
+; FNATTRS-NEXT:    [[TMP3:%.*]] = icmp eq i32 [[B]], 0
+; FNATTRS-NEXT:    br i1 [[TMP3]], label [[EX:%.*]], label [[HD:%.*]]
+; FNATTRS:       ex:
+; FNATTRS-NEXT:    [[TMP5:%.*]] = tail call i32 @g(ptr nonnull [[A]])
+; FNATTRS-NEXT:    ret i32 [[TMP5]]
+; FNATTRS:       hd:
+; FNATTRS-NEXT:    [[TMP7:%.*]] = phi i32 [ [[TMP8:%.*]], [[HD]] ], [ 0, [[EN:%.*]] ]
+; FNATTRS-NEXT:    tail call void @h(ptr [[A]])
+; FNATTRS-NEXT:    [[TMP8]] = add nuw i32 [[TMP7]], 1
+; FNATTRS-NEXT:    [[TMP9:%.*]] = icmp eq i32 [[TMP8]], [[B]]
+; FNATTRS-NEXT:    br i1 [[TMP9]], label [[EX]], label [[HD]]
 ;
+; ATTRIBUTOR-LABEL: define i32 @nonnull_exec_ctx_2
+; ATTRIBUTOR-SAME: (ptr nonnull [[A:%.*]], i32 [[B:%.*]]) #[[ATTR7]] {
+; ATTRIBUTOR-NEXT:  en:
+; ATTRIBUTOR-NEXT:    [[TMP3:%.*]] = icmp eq i32 [[B]], 0
+; ATTRIBUTOR-NEXT:    br i1 [[TMP3]], label [[EX:%.*]], label [[HD:%.*]]
+; ATTRIBUTOR:       ex:
+; ATTRIBUTOR-NEXT:    [[TMP5:%.*]] = tail call i32 @g(ptr nonnull [[A]])
+; ATTRIBUTOR-NEXT:    ret i32 [[TMP5]]
+; ATTRIBUTOR:       hd:
+; ATTRIBUTOR-NEXT:    [[TMP7:%.*]] = phi i32 [ [[TMP8:%.*]], [[HD]] ], [ 0, [[EN:%.*]] ]
+; ATTRIBUTOR-NEXT:    tail call void @h(ptr nonnull [[A]])
+; ATTRIBUTOR-NEXT:    [[TMP8]] = add nuw i32 [[TMP7]], 1
+; ATTRIBUTOR-NEXT:    [[TMP9:%.*]] = icmp eq i32 [[TMP8]], [[B]]
+; ATTRIBUTOR-NEXT:    br i1 [[TMP9]], label [[EX]], label [[HD]]
 ;
 en:
   %tmp3 = icmp eq i32 %b, 0
@@ -662,23 +1275,39 @@ hd:
 }
 
 define i32 @nonnull_exec_ctx_2b(ptr %a, i32 %b) willreturn nounwind {
-; FNATTR-LABEL: define {{[^@]+}}@nonnull_exec_ctx_2b
-; FNATTR-SAME: (ptr [[A:%.*]], i32 [[B:%.*]])
-; FNATTR-NEXT:  en:
-; FNATTR-NEXT:    [[TMP3:%.*]] = icmp eq i32 [[B:%.*]], 0
-; FNATTR-NEXT:    br i1 [[TMP3]], label [[EX:%.*]], label [[HD:%.*]]
-; FNATTR:       ex:
-; FNATTR-NEXT:    [[TMP5:%.*]] = tail call i32 @g(ptr nonnull [[A:%.*]])
-; FNATTR-NEXT:    ret i32 [[TMP5]]
-; FNATTR:       hd:
-; FNATTR-NEXT:    [[TMP7:%.*]] = phi i32 [ [[TMP8:%.*]], [[HD2:%.*]] ], [ 0, [[EN:%.*]] ]
-; FNATTR-NEXT:    tail call void @h(ptr [[A]])
-; FNATTR-NEXT:    br label [[HD2]]
-; FNATTR:       hd2:
-; FNATTR-NEXT:    [[TMP8]] = add nuw i32 [[TMP7]], 1
-; FNATTR-NEXT:    [[TMP9:%.*]] = icmp eq i32 [[TMP8]], [[B]]
-; FNATTR-NEXT:    br i1 [[TMP9]], label [[EX]], label [[HD]]
+; FNATTRS-LABEL: define i32 @nonnull_exec_ctx_2b
+; FNATTRS-SAME: (ptr [[A:%.*]], i32 [[B:%.*]]) #[[ATTR6]] {
+; FNATTRS-NEXT:  en:
+; FNATTRS-NEXT:    [[TMP3:%.*]] = icmp eq i32 [[B]], 0
+; FNATTRS-NEXT:    br i1 [[TMP3]], label [[EX:%.*]], label [[HD:%.*]]
+; FNATTRS:       ex:
+; FNATTRS-NEXT:    [[TMP5:%.*]] = tail call i32 @g(ptr nonnull [[A]])
+; FNATTRS-NEXT:    ret i32 [[TMP5]]
+; FNATTRS:       hd:
+; FNATTRS-NEXT:    [[TMP7:%.*]] = phi i32 [ [[TMP8:%.*]], [[HD2:%.*]] ], [ 0, [[EN:%.*]] ]
+; FNATTRS-NEXT:    tail call void @h(ptr [[A]])
+; FNATTRS-NEXT:    br label [[HD2]]
+; FNATTRS:       hd2:
+; FNATTRS-NEXT:    [[TMP8]] = add nuw i32 [[TMP7]], 1
+; FNATTRS-NEXT:    [[TMP9:%.*]] = icmp eq i32 [[TMP8]], [[B]]
+; FNATTRS-NEXT:    br i1 [[TMP9]], label [[EX]], label [[HD]]
 ;
+; ATTRIBUTOR-LABEL: define i32 @nonnull_exec_ctx_2b
+; ATTRIBUTOR-SAME: (ptr nonnull [[A:%.*]], i32 [[B:%.*]]) #[[ATTR7]] {
+; ATTRIBUTOR-NEXT:  en:
+; ATTRIBUTOR-NEXT:    [[TMP3:%.*]] = icmp eq i32 [[B]], 0
+; ATTRIBUTOR-NEXT:    br i1 [[TMP3]], label [[EX:%.*]], label [[HD:%.*]]
+; ATTRIBUTOR:       ex:
+; ATTRIBUTOR-NEXT:    [[TMP5:%.*]] = tail call i32 @g(ptr nonnull [[A]])
+; ATTRIBUTOR-NEXT:    ret i32 [[TMP5]]
+; ATTRIBUTOR:       hd:
+; ATTRIBUTOR-NEXT:    [[TMP7:%.*]] = phi i32 [ [[TMP8:%.*]], [[HD2:%.*]] ], [ 0, [[EN:%.*]] ]
+; ATTRIBUTOR-NEXT:    tail call void @h(ptr nonnull [[A]])
+; ATTRIBUTOR-NEXT:    br label [[HD2]]
+; ATTRIBUTOR:       hd2:
+; ATTRIBUTOR-NEXT:    [[TMP8]] = add nuw i32 [[TMP7]], 1
+; ATTRIBUTOR-NEXT:    [[TMP9:%.*]] = icmp eq i32 [[TMP8]], [[B]]
+; ATTRIBUTOR-NEXT:    br i1 [[TMP9]], label [[EX]], label [[HD]]
 ;
 en:
   %tmp3 = icmp eq i32 %b, 0
@@ -704,21 +1333,22 @@ declare void @sink(ptr)
 
 ; FIXME: the sink argument should be marked nonnull as in @PR43833_simple.
 define void @PR43833(ptr %0, i32 %1) {
-; FNATTR-LABEL: @PR43833(
-; FNATTR-NEXT:    [[TMP3:%.*]] = icmp sgt i32 [[TMP1:%.*]], 1
-; FNATTR-NEXT:    br i1 [[TMP3]], label [[TMP4:%.*]], label [[TMP7:%.*]]
-; FNATTR:       4:
-; FNATTR-NEXT:    [[TMP5:%.*]] = zext i32 [[TMP1]] to i64
-; FNATTR-NEXT:    [[TMP6:%.*]] = getelementptr inbounds i32, ptr [[TMP0:%.*]], i64 [[TMP5]]
-; FNATTR-NEXT:    br label [[TMP8:%.*]]
-; FNATTR:       7:
-; FNATTR-NEXT:    ret void
-; FNATTR:       8:
-; FNATTR-NEXT:    [[TMP9:%.*]] = phi i32 [ 1, [[TMP4]] ], [ [[TMP10:%.*]], [[TMP8]] ]
-; FNATTR-NEXT:    tail call void @sink(ptr [[TMP6]])
-; FNATTR-NEXT:    [[TMP10]] = add nuw nsw i32 [[TMP9]], 1
-; FNATTR-NEXT:    [[TMP11:%.*]] = icmp eq i32 [[TMP10]], [[TMP1]]
-; FNATTR-NEXT:    br i1 [[TMP11]], label [[TMP7]], label [[TMP8]]
+; COMMON-LABEL: define void @PR43833
+; COMMON-SAME: (ptr [[TMP0:%.*]], i32 [[TMP1:%.*]]) {
+; COMMON-NEXT:    [[TMP3:%.*]] = icmp sgt i32 [[TMP1]], 1
+; COMMON-NEXT:    br i1 [[TMP3]], label [[TMP4:%.*]], label [[TMP7:%.*]]
+; COMMON:       4:
+; COMMON-NEXT:    [[TMP5:%.*]] = zext i32 [[TMP1]] to i64
+; COMMON-NEXT:    [[TMP6:%.*]] = getelementptr inbounds i32, ptr [[TMP0]], i64 [[TMP5]]
+; COMMON-NEXT:    br label [[TMP8:%.*]]
+; COMMON:       7:
+; COMMON-NEXT:    ret void
+; COMMON:       8:
+; COMMON-NEXT:    [[TMP9:%.*]] = phi i32 [ 1, [[TMP4]] ], [ [[TMP10:%.*]], [[TMP8]] ]
+; COMMON-NEXT:    tail call void @sink(ptr [[TMP6]])
+; COMMON-NEXT:    [[TMP10]] = add nuw nsw i32 [[TMP9]], 1
+; COMMON-NEXT:    [[TMP11:%.*]] = icmp eq i32 [[TMP10]], [[TMP1]]
+; COMMON-NEXT:    br i1 [[TMP11]], label [[TMP7]], label [[TMP8]]
 ;
   %3 = icmp sgt i32 %1, 1
   br i1 %3, label %4, label %7
@@ -741,22 +1371,22 @@ define void @PR43833(ptr %0, i32 %1) {
 
 ; Adjusted from PR43833
 define void @PR43833_simple(ptr %0, i32 %1) {
-; FNATTR-LABEL: @PR43833_simple(
-; FNATTR-NEXT:    [[TMP3:%.*]] = icmp ne i32 [[TMP1:%.*]], 0
-; FNATTR-NEXT:    br i1 [[TMP3]], label [[TMP4:%.*]], label [[TMP7:%.*]]
-; FNATTR:       4:
-; FNATTR-NEXT:    [[TMP5:%.*]] = zext i32 [[TMP1]] to i64
-; FNATTR-NEXT:    [[TMP6:%.*]] = getelementptr inbounds i32, ptr [[TMP0:%.*]], i64 [[TMP5]]
-; FNATTR-NEXT:    br label [[TMP8:%.*]]
-; FNATTR:       7:
-; FNATTR-NEXT:    ret void
-; FNATTR:       8:
-; FNATTR-NEXT:    [[TMP9:%.*]] = phi i32 [ 1, [[TMP4]] ], [ [[TMP10:%.*]], [[TMP8]] ]
-; FNATTR-NEXT:    tail call void @sink(ptr [[TMP6]])
-; FNATTR-NEXT:    [[TMP10]] = add nuw nsw i32 [[TMP9]], 1
-; FNATTR-NEXT:    [[TMP11:%.*]] = icmp eq i32 [[TMP10]], [[TMP1]]
-; FNATTR-NEXT:    br i1 [[TMP11]], label [[TMP7]], label [[TMP8]]
-;
+; COMMON-LABEL: define void @PR43833_simple
+; COMMON-SAME: (ptr [[TMP0:%.*]], i32 [[TMP1:%.*]]) {
+; COMMON-NEXT:    [[TMP3:%.*]] = icmp ne i32 [[TMP1]], 0
+; COMMON-NEXT:    br i1 [[TMP3]], label [[TMP4:%.*]], label [[TMP7:%.*]]
+; COMMON:       4:
+; COMMON-NEXT:    [[TMP5:%.*]] = zext i32 [[TMP1]] to i64
+; COMMON-NEXT:    [[TMP6:%.*]] = getelementptr inbounds i32, ptr [[TMP0]], i64 [[TMP5]]
+; COMMON-NEXT:    br label [[TMP8:%.*]]
+; COMMON:       7:
+; COMMON-NEXT:    ret void
+; COMMON:       8:
+; COMMON-NEXT:    [[TMP9:%.*]] = phi i32 [ 1, [[TMP4]] ], [ [[TMP10:%.*]], [[TMP8]] ]
+; COMMON-NEXT:    tail call void @sink(ptr [[TMP6]])
+; COMMON-NEXT:    [[TMP10]] = add nuw nsw i32 [[TMP9]], 1
+; COMMON-NEXT:    [[TMP11:%.*]] = icmp eq i32 [[TMP10]], [[TMP1]]
+; COMMON-NEXT:    br i1 [[TMP11]], label [[TMP7]], label [[TMP8]]
 ;
   %3 = icmp ne i32 %1, 0
   br i1 %3, label %4, label %7

diff  --git a/llvm/test/Transforms/FunctionAttrs/norecurse.ll b/llvm/test/Transforms/FunctionAttrs/norecurse.ll
index 43409563129469..e1c624dc2ce50d 100644
--- a/llvm/test/Transforms/FunctionAttrs/norecurse.ll
+++ b/llvm/test/Transforms/FunctionAttrs/norecurse.ll
@@ -1,54 +1,80 @@
-; NOTE: Assertions have been autogenerated by utils/update_test_checks.py UTC_ARGS: --function-signature --check-attributes
-; RUN: opt < %s -aa-pipeline=basic-aa -passes='cgscc(function-attrs),rpo-function-attrs' -S | FileCheck %s
+; NOTE: Assertions have been autogenerated by utils/update_test_checks.py UTC_ARGS: --function-signature --check-attributes --check-globals
+; RUN: opt < %s -aa-pipeline=basic-aa -passes='cgscc(function-attrs),rpo-function-attrs' -S | FileCheck --check-prefixes=COMMON,FNATTRS %s
+; RUN: opt -passes=attributor-light -S < %s | FileCheck --check-prefixes=COMMON,ATTRIBUTOR %s
+
 
 define i32 @leaf() {
-; CHECK: Function Attrs: mustprogress nofree norecurse nosync nounwind willreturn memory(none)
-; CHECK-LABEL: define {{[^@]+}}@leaf
-; CHECK-SAME: () #[[ATTR0:[0-9]+]] {
-; CHECK-NEXT:    ret i32 1
+; COMMON: Function Attrs: mustprogress nofree norecurse nosync nounwind willreturn memory(none)
+; COMMON-LABEL: define {{[^@]+}}@leaf
+; COMMON-SAME: () #[[ATTR0:[0-9]+]] {
+; COMMON-NEXT:    ret i32 1
 ;
   ret i32 1
 }
 
 define i32 @self_rec() {
-; CHECK: Function Attrs: nofree nosync nounwind memory(none)
-; CHECK-LABEL: define {{[^@]+}}@self_rec
-; CHECK-SAME: () #[[ATTR1:[0-9]+]] {
-; CHECK-NEXT:    [[A:%.*]] = call i32 @self_rec()
-; CHECK-NEXT:    ret i32 4
+; FNATTRS: Function Attrs: nofree nosync nounwind memory(none)
+; FNATTRS-LABEL: define {{[^@]+}}@self_rec
+; FNATTRS-SAME: () #[[ATTR1:[0-9]+]] {
+; FNATTRS-NEXT:    [[A:%.*]] = call i32 @self_rec()
+; FNATTRS-NEXT:    ret i32 4
+;
+; ATTRIBUTOR: Function Attrs: nofree nosync nounwind memory(none)
+; ATTRIBUTOR-LABEL: define {{[^@]+}}@self_rec
+; ATTRIBUTOR-SAME: () #[[ATTR1:[0-9]+]] {
+; ATTRIBUTOR-NEXT:    [[A:%.*]] = call i32 @self_rec() #[[ATTR1]]
+; ATTRIBUTOR-NEXT:    ret i32 4
 ;
   %a = call i32 @self_rec()
   ret i32 4
 }
 
 define i32 @indirect_rec() {
-; CHECK: Function Attrs: nofree nosync nounwind memory(none)
-; CHECK-LABEL: define {{[^@]+}}@indirect_rec
-; CHECK-SAME: () #[[ATTR1]] {
-; CHECK-NEXT:    [[A:%.*]] = call i32 @indirect_rec2()
-; CHECK-NEXT:    ret i32 [[A]]
+; FNATTRS: Function Attrs: nofree nosync nounwind memory(none)
+; FNATTRS-LABEL: define {{[^@]+}}@indirect_rec
+; FNATTRS-SAME: () #[[ATTR1]] {
+; FNATTRS-NEXT:    [[A:%.*]] = call i32 @indirect_rec2()
+; FNATTRS-NEXT:    ret i32 [[A]]
+;
+; ATTRIBUTOR: Function Attrs: nofree nosync nounwind memory(none)
+; ATTRIBUTOR-LABEL: define {{[^@]+}}@indirect_rec
+; ATTRIBUTOR-SAME: () #[[ATTR1]] {
+; ATTRIBUTOR-NEXT:    [[A:%.*]] = call i32 @indirect_rec2() #[[ATTR1]]
+; ATTRIBUTOR-NEXT:    ret i32 [[A]]
 ;
   %a = call i32 @indirect_rec2()
   ret i32 %a
 }
 
 define i32 @indirect_rec2() {
-; CHECK: Function Attrs: nofree nosync nounwind memory(none)
-; CHECK-LABEL: define {{[^@]+}}@indirect_rec2
-; CHECK-SAME: () #[[ATTR1]] {
-; CHECK-NEXT:    [[A:%.*]] = call i32 @indirect_rec()
-; CHECK-NEXT:    ret i32 [[A]]
+; FNATTRS: Function Attrs: nofree nosync nounwind memory(none)
+; FNATTRS-LABEL: define {{[^@]+}}@indirect_rec2
+; FNATTRS-SAME: () #[[ATTR1]] {
+; FNATTRS-NEXT:    [[A:%.*]] = call i32 @indirect_rec()
+; FNATTRS-NEXT:    ret i32 [[A]]
+;
+; ATTRIBUTOR: Function Attrs: nofree nosync nounwind memory(none)
+; ATTRIBUTOR-LABEL: define {{[^@]+}}@indirect_rec2
+; ATTRIBUTOR-SAME: () #[[ATTR1]] {
+; ATTRIBUTOR-NEXT:    [[A:%.*]] = call i32 @indirect_rec() #[[ATTR1]]
+; ATTRIBUTOR-NEXT:    ret i32 [[A]]
 ;
   %a = call i32 @indirect_rec()
   ret i32 %a
 }
 
 define i32 @extern() {
-; CHECK: Function Attrs: nofree nosync memory(none)
-; CHECK-LABEL: define {{[^@]+}}@extern
-; CHECK-SAME: () #[[ATTR2:[0-9]+]] {
-; CHECK-NEXT:    [[A:%.*]] = call i32 @k()
-; CHECK-NEXT:    ret i32 [[A]]
+; FNATTRS: Function Attrs: nofree nosync memory(none)
+; FNATTRS-LABEL: define {{[^@]+}}@extern
+; FNATTRS-SAME: () #[[ATTR2:[0-9]+]] {
+; FNATTRS-NEXT:    [[A:%.*]] = call i32 @k()
+; FNATTRS-NEXT:    ret i32 [[A]]
+;
+; ATTRIBUTOR: Function Attrs: nosync memory(none)
+; ATTRIBUTOR-LABEL: define {{[^@]+}}@extern
+; ATTRIBUTOR-SAME: () #[[ATTR2:[0-9]+]] {
+; ATTRIBUTOR-NEXT:    [[A:%.*]] = call i32 @k()
+; ATTRIBUTOR-NEXT:    ret i32 [[A]]
 ;
   %a = call i32 @k()
   ret i32 %a
@@ -57,11 +83,17 @@ define i32 @extern() {
 declare i32 @k() readnone
 
 define void @intrinsic(ptr %dest, ptr %src, i32 %len) {
-; CHECK: Function Attrs: mustprogress nofree nosync nounwind willreturn memory(argmem: readwrite)
-; CHECK-LABEL: define {{[^@]+}}@intrinsic
-; CHECK-SAME: (ptr nocapture writeonly [[DEST:%.*]], ptr nocapture readonly [[SRC:%.*]], i32 [[LEN:%.*]]) #[[ATTR4:[0-9]+]] {
-; CHECK-NEXT:    call void @llvm.memcpy.p0.p0.i32(ptr [[DEST]], ptr [[SRC]], i32 [[LEN]], i1 false)
-; CHECK-NEXT:    ret void
+; FNATTRS: Function Attrs: mustprogress nofree nosync nounwind willreturn memory(argmem: readwrite)
+; FNATTRS-LABEL: define {{[^@]+}}@intrinsic
+; FNATTRS-SAME: (ptr nocapture writeonly [[DEST:%.*]], ptr nocapture readonly [[SRC:%.*]], i32 [[LEN:%.*]]) #[[ATTR4:[0-9]+]] {
+; FNATTRS-NEXT:    call void @llvm.memcpy.p0.p0.i32(ptr [[DEST]], ptr [[SRC]], i32 [[LEN]], i1 false)
+; FNATTRS-NEXT:    ret void
+;
+; ATTRIBUTOR: Function Attrs: mustprogress nofree norecurse nosync nounwind willreturn memory(argmem: readwrite)
+; ATTRIBUTOR-LABEL: define {{[^@]+}}@intrinsic
+; ATTRIBUTOR-SAME: (ptr nocapture nofree writeonly [[DEST:%.*]], ptr nocapture nofree readonly [[SRC:%.*]], i32 [[LEN:%.*]]) #[[ATTR4:[0-9]+]] {
+; ATTRIBUTOR-NEXT:    call void @llvm.memcpy.p0.p0.i32(ptr nocapture writeonly [[DEST]], ptr nocapture readonly [[SRC]], i32 [[LEN]], i1 false) #[[ATTR7:[0-9]+]]
+; ATTRIBUTOR-NEXT:    ret void
 ;
   call void @llvm.memcpy.p0.p0.i32(ptr %dest, ptr %src, i32 %len, i1 false)
   ret void
@@ -70,89 +102,155 @@ define void @intrinsic(ptr %dest, ptr %src, i32 %len) {
 declare void @llvm.memcpy.p0.p0.i32(ptr, ptr, i32, i1)
 
 define internal i32 @called_by_norecurse() {
-; CHECK: Function Attrs: nofree norecurse nosync memory(none)
-; CHECK-LABEL: define {{[^@]+}}@called_by_norecurse
-; CHECK-SAME: () #[[ATTR6:[0-9]+]] {
-; CHECK-NEXT:    [[A:%.*]] = call i32 @k()
-; CHECK-NEXT:    ret i32 [[A]]
+; FNATTRS: Function Attrs: nofree norecurse nosync memory(none)
+; FNATTRS-LABEL: define {{[^@]+}}@called_by_norecurse
+; FNATTRS-SAME: () #[[ATTR6:[0-9]+]] {
+; FNATTRS-NEXT:    [[A:%.*]] = call i32 @k()
+; FNATTRS-NEXT:    ret i32 [[A]]
+;
+; ATTRIBUTOR: Function Attrs: nosync memory(none)
+; ATTRIBUTOR-LABEL: define {{[^@]+}}@called_by_norecurse
+; ATTRIBUTOR-SAME: () #[[ATTR2]] {
+; ATTRIBUTOR-NEXT:    [[A:%.*]] = call i32 @k()
+; ATTRIBUTOR-NEXT:    ret i32 [[A]]
 ;
   %a = call i32 @k()
   ret i32 %a
 }
 
 define void @m() norecurse {
-; CHECK: Function Attrs: nofree norecurse nosync memory(none)
-; CHECK-LABEL: define {{[^@]+}}@m
-; CHECK-SAME: () #[[ATTR6]] {
-; CHECK-NEXT:    [[A:%.*]] = call i32 @called_by_norecurse()
-; CHECK-NEXT:    ret void
+; FNATTRS: Function Attrs: nofree norecurse nosync memory(none)
+; FNATTRS-LABEL: define {{[^@]+}}@m
+; FNATTRS-SAME: () #[[ATTR6]] {
+; FNATTRS-NEXT:    [[A:%.*]] = call i32 @called_by_norecurse()
+; FNATTRS-NEXT:    ret void
+;
+; ATTRIBUTOR: Function Attrs: norecurse nosync memory(none)
+; ATTRIBUTOR-LABEL: define {{[^@]+}}@m
+; ATTRIBUTOR-SAME: () #[[ATTR6:[0-9]+]] {
+; ATTRIBUTOR-NEXT:    [[A:%.*]] = call i32 @called_by_norecurse() #[[ATTR2]]
+; ATTRIBUTOR-NEXT:    ret void
 ;
   %a = call i32 @called_by_norecurse()
   ret void
 }
 
 define internal i32 @called_by_norecurse_indirectly() {
-; CHECK: Function Attrs: nofree norecurse nosync memory(none)
-; CHECK-LABEL: define {{[^@]+}}@called_by_norecurse_indirectly
-; CHECK-SAME: () #[[ATTR6]] {
-; CHECK-NEXT:    [[A:%.*]] = call i32 @k()
-; CHECK-NEXT:    ret i32 [[A]]
+; FNATTRS: Function Attrs: nofree norecurse nosync memory(none)
+; FNATTRS-LABEL: define {{[^@]+}}@called_by_norecurse_indirectly
+; FNATTRS-SAME: () #[[ATTR6]] {
+; FNATTRS-NEXT:    [[A:%.*]] = call i32 @k()
+; FNATTRS-NEXT:    ret i32 [[A]]
+;
+; ATTRIBUTOR: Function Attrs: nosync memory(none)
+; ATTRIBUTOR-LABEL: define {{[^@]+}}@called_by_norecurse_indirectly
+; ATTRIBUTOR-SAME: () #[[ATTR2]] {
+; ATTRIBUTOR-NEXT:    [[A:%.*]] = call i32 @k()
+; ATTRIBUTOR-NEXT:    ret i32 [[A]]
 ;
   %a = call i32 @k()
   ret i32 %a
 }
 
 define internal void @o() {
-; CHECK: Function Attrs: nofree norecurse nosync memory(none)
-; CHECK-LABEL: define {{[^@]+}}@o
-; CHECK-SAME: () #[[ATTR6]] {
-; CHECK-NEXT:    [[A:%.*]] = call i32 @called_by_norecurse_indirectly()
-; CHECK-NEXT:    ret void
+; FNATTRS: Function Attrs: nofree norecurse nosync memory(none)
+; FNATTRS-LABEL: define {{[^@]+}}@o
+; FNATTRS-SAME: () #[[ATTR6]] {
+; FNATTRS-NEXT:    [[A:%.*]] = call i32 @called_by_norecurse_indirectly()
+; FNATTRS-NEXT:    ret void
+;
+; ATTRIBUTOR: Function Attrs: nosync memory(none)
+; ATTRIBUTOR-LABEL: define {{[^@]+}}@o
+; ATTRIBUTOR-SAME: () #[[ATTR2]] {
+; ATTRIBUTOR-NEXT:    [[A:%.*]] = call i32 @called_by_norecurse_indirectly() #[[ATTR2]]
+; ATTRIBUTOR-NEXT:    ret void
 ;
   %a = call i32 @called_by_norecurse_indirectly()
   ret void
 }
 
 define void @p() norecurse {
-; CHECK: Function Attrs: nofree norecurse nosync memory(none)
-; CHECK-LABEL: define {{[^@]+}}@p
-; CHECK-SAME: () #[[ATTR6]] {
-; CHECK-NEXT:    call void @o()
-; CHECK-NEXT:    ret void
+; FNATTRS: Function Attrs: nofree norecurse nosync memory(none)
+; FNATTRS-LABEL: define {{[^@]+}}@p
+; FNATTRS-SAME: () #[[ATTR6]] {
+; FNATTRS-NEXT:    call void @o()
+; FNATTRS-NEXT:    ret void
+;
+; ATTRIBUTOR: Function Attrs: norecurse nosync memory(none)
+; ATTRIBUTOR-LABEL: define {{[^@]+}}@p
+; ATTRIBUTOR-SAME: () #[[ATTR6]] {
+; ATTRIBUTOR-NEXT:    call void @o() #[[ATTR2]]
+; ATTRIBUTOR-NEXT:    ret void
 ;
   call void @o()
   ret void
 }
 
 define internal i32 @escapes_as_parameter(ptr %p) {
-; CHECK: Function Attrs: nofree nosync memory(none)
-; CHECK-LABEL: define {{[^@]+}}@escapes_as_parameter
-; CHECK-SAME: (ptr nocapture readnone [[P:%.*]]) #[[ATTR2]] {
-; CHECK-NEXT:    [[A:%.*]] = call i32 @k()
-; CHECK-NEXT:    ret i32 [[A]]
+; FNATTRS: Function Attrs: nofree nosync memory(none)
+; FNATTRS-LABEL: define {{[^@]+}}@escapes_as_parameter
+; FNATTRS-SAME: (ptr nocapture readnone [[P:%.*]]) #[[ATTR2]] {
+; FNATTRS-NEXT:    [[A:%.*]] = call i32 @k()
+; FNATTRS-NEXT:    ret i32 [[A]]
+;
+; ATTRIBUTOR: Function Attrs: nosync memory(none)
+; ATTRIBUTOR-LABEL: define {{[^@]+}}@escapes_as_parameter
+; ATTRIBUTOR-SAME: (ptr nocapture nofree readnone [[P:%.*]]) #[[ATTR2]] {
+; ATTRIBUTOR-NEXT:    [[A:%.*]] = call i32 @k()
+; ATTRIBUTOR-NEXT:    ret i32 [[A]]
 ;
   %a = call i32 @k()
   ret i32 %a
 }
 
 define internal void @q() {
-; CHECK: Function Attrs: nofree norecurse nosync memory(none)
-; CHECK-LABEL: define {{[^@]+}}@q
-; CHECK-SAME: () #[[ATTR6]] {
-; CHECK-NEXT:    [[A:%.*]] = call i32 @escapes_as_parameter(ptr @escapes_as_parameter)
-; CHECK-NEXT:    ret void
+; FNATTRS: Function Attrs: nofree norecurse nosync memory(none)
+; FNATTRS-LABEL: define {{[^@]+}}@q
+; FNATTRS-SAME: () #[[ATTR6]] {
+; FNATTRS-NEXT:    [[A:%.*]] = call i32 @escapes_as_parameter(ptr @escapes_as_parameter)
+; FNATTRS-NEXT:    ret void
+;
+; ATTRIBUTOR: Function Attrs: norecurse nosync memory(none)
+; ATTRIBUTOR-LABEL: define {{[^@]+}}@q
+; ATTRIBUTOR-SAME: () #[[ATTR6]] {
+; ATTRIBUTOR-NEXT:    [[A:%.*]] = call i32 @escapes_as_parameter(ptr nonnull @escapes_as_parameter) #[[ATTR2]]
+; ATTRIBUTOR-NEXT:    ret void
 ;
   %a = call i32 @escapes_as_parameter(ptr @escapes_as_parameter)
   ret void
 }
 
 define void @r() norecurse {
-; CHECK: Function Attrs: nofree norecurse nosync memory(none)
-; CHECK-LABEL: define {{[^@]+}}@r
-; CHECK-SAME: () #[[ATTR6]] {
-; CHECK-NEXT:    call void @q()
-; CHECK-NEXT:    ret void
+; FNATTRS: Function Attrs: nofree norecurse nosync memory(none)
+; FNATTRS-LABEL: define {{[^@]+}}@r
+; FNATTRS-SAME: () #[[ATTR6]] {
+; FNATTRS-NEXT:    call void @q()
+; FNATTRS-NEXT:    ret void
+;
+; ATTRIBUTOR: Function Attrs: norecurse nosync memory(none)
+; ATTRIBUTOR-LABEL: define {{[^@]+}}@r
+; ATTRIBUTOR-SAME: () #[[ATTR6]] {
+; ATTRIBUTOR-NEXT:    call void @q() #[[ATTR2]]
+; ATTRIBUTOR-NEXT:    ret void
 ;
   call void @q()
   ret void
 }
+;.
+; FNATTRS: attributes #[[ATTR0]] = { mustprogress nofree norecurse nosync nounwind willreturn memory(none) }
+; FNATTRS: attributes #[[ATTR1]] = { nofree nosync nounwind memory(none) }
+; FNATTRS: attributes #[[ATTR2]] = { nofree nosync memory(none) }
+; FNATTRS: attributes #[[ATTR3:[0-9]+]] = { memory(none) }
+; FNATTRS: attributes #[[ATTR4]] = { mustprogress nofree nosync nounwind willreturn memory(argmem: readwrite) }
+; FNATTRS: attributes #[[ATTR5:[0-9]+]] = { nocallback nofree nounwind willreturn memory(argmem: readwrite) }
+; FNATTRS: attributes #[[ATTR6]] = { nofree norecurse nosync memory(none) }
+;.
+; ATTRIBUTOR: attributes #[[ATTR0]] = { mustprogress nofree norecurse nosync nounwind willreturn memory(none) }
+; ATTRIBUTOR: attributes #[[ATTR1]] = { nofree nosync nounwind memory(none) }
+; ATTRIBUTOR: attributes #[[ATTR2]] = { nosync memory(none) }
+; ATTRIBUTOR: attributes #[[ATTR3:[0-9]+]] = { memory(none) }
+; ATTRIBUTOR: attributes #[[ATTR4]] = { mustprogress nofree norecurse nosync nounwind willreturn memory(argmem: readwrite) }
+; ATTRIBUTOR: attributes #[[ATTR5:[0-9]+]] = { nocallback nofree nounwind willreturn memory(argmem: readwrite) }
+; ATTRIBUTOR: attributes #[[ATTR6]] = { norecurse nosync memory(none) }
+; ATTRIBUTOR: attributes #[[ATTR7]] = { nofree willreturn }
+;.

diff  --git a/llvm/test/Transforms/FunctionAttrs/nounwind.ll b/llvm/test/Transforms/FunctionAttrs/nounwind.ll
index fd0872cba1e75d..afa9ae31b5fba4 100644
--- a/llvm/test/Transforms/FunctionAttrs/nounwind.ll
+++ b/llvm/test/Transforms/FunctionAttrs/nounwind.ll
@@ -1,23 +1,30 @@
 ; NOTE: Assertions have been autogenerated by utils/update_test_checks.py UTC_ARGS: --function-signature --check-attributes
-; RUN: opt < %s -passes=function-attrs -S | FileCheck %s
+; RUN: opt -passes=function-attrs -S < %s | FileCheck --check-prefixes=COMMON,FNATTRS %s
+; RUN: opt -passes=attributor-light -S < %s | FileCheck --check-prefixes=COMMON,ATTRIBUTOR %s
 
 ; TEST 1
 define i32 @foo1() {
-; CHECK: Function Attrs: mustprogress nofree norecurse nosync nounwind willreturn memory(none)
-; CHECK-LABEL: define {{[^@]+}}@foo1
-; CHECK-SAME: () #[[ATTR0:[0-9]+]] {
-; CHECK-NEXT:    ret i32 1
+; COMMON: Function Attrs: mustprogress nofree norecurse nosync nounwind willreturn memory(none)
+; COMMON-LABEL: define {{[^@]+}}@foo1
+; COMMON-SAME: () #[[ATTR0:[0-9]+]] {
+; COMMON-NEXT:    ret i32 1
 ;
   ret i32 1
 }
 
 ; TEST 2
 define i32 @scc1_foo() {
-; CHECK: Function Attrs: nofree nosync nounwind memory(none)
-; CHECK-LABEL: define {{[^@]+}}@scc1_foo
-; CHECK-SAME: () #[[ATTR1:[0-9]+]] {
-; CHECK-NEXT:    [[TMP1:%.*]] = call i32 @scc1_bar()
-; CHECK-NEXT:    ret i32 1
+; FNATTRS: Function Attrs: nofree nosync nounwind memory(none)
+; FNATTRS-LABEL: define {{[^@]+}}@scc1_foo
+; FNATTRS-SAME: () #[[ATTR1:[0-9]+]] {
+; FNATTRS-NEXT:    [[TMP1:%.*]] = call i32 @scc1_bar()
+; FNATTRS-NEXT:    ret i32 1
+;
+; ATTRIBUTOR: Function Attrs: nofree nosync nounwind memory(none)
+; ATTRIBUTOR-LABEL: define {{[^@]+}}@scc1_foo
+; ATTRIBUTOR-SAME: () #[[ATTR1:[0-9]+]] {
+; ATTRIBUTOR-NEXT:    [[TMP1:%.*]] = call i32 @scc1_bar() #[[ATTR1]]
+; ATTRIBUTOR-NEXT:    ret i32 1
 ;
   %1 = call i32 @scc1_bar()
   ret i32 1
@@ -26,11 +33,17 @@ define i32 @scc1_foo() {
 
 ; TEST 3
 define i32 @scc1_bar() {
-; CHECK: Function Attrs: nofree nosync nounwind memory(none)
-; CHECK-LABEL: define {{[^@]+}}@scc1_bar
-; CHECK-SAME: () #[[ATTR1]] {
-; CHECK-NEXT:    [[TMP1:%.*]] = call i32 @scc1_foo()
-; CHECK-NEXT:    ret i32 1
+; FNATTRS: Function Attrs: nofree nosync nounwind memory(none)
+; FNATTRS-LABEL: define {{[^@]+}}@scc1_bar
+; FNATTRS-SAME: () #[[ATTR1]] {
+; FNATTRS-NEXT:    [[TMP1:%.*]] = call i32 @scc1_foo()
+; FNATTRS-NEXT:    ret i32 1
+;
+; ATTRIBUTOR: Function Attrs: nofree nosync nounwind memory(none)
+; ATTRIBUTOR-LABEL: define {{[^@]+}}@scc1_bar
+; ATTRIBUTOR-SAME: () #[[ATTR1]] {
+; ATTRIBUTOR-NEXT:    [[TMP1:%.*]] = call i32 @scc1_foo() #[[ATTR1]]
+; ATTRIBUTOR-NEXT:    ret i32 1
 ;
   %1 = call i32 @scc1_foo()
   ret i32 1
@@ -40,9 +53,9 @@ declare i32 @non_nounwind()
 
 ; TEST 4
 define void @call_non_nounwind(){
-; CHECK-LABEL: define {{[^@]+}}@call_non_nounwind() {
-; CHECK-NEXT:    [[TMP1:%.*]] = tail call i32 @non_nounwind()
-; CHECK-NEXT:    ret void
+; COMMON-LABEL: define {{[^@]+}}@call_non_nounwind() {
+; COMMON-NEXT:    [[TMP1:%.*]] = tail call i32 @non_nounwind()
+; COMMON-NEXT:    ret void
 ;
   tail call i32 @non_nounwind()
   ret void
@@ -57,14 +70,14 @@ define void @call_non_nounwind(){
 ; }
 
 define i32 @maybe_throw(i1 zeroext %0) {
-; CHECK-LABEL: define {{[^@]+}}@maybe_throw
-; CHECK-SAME: (i1 zeroext [[TMP0:%.*]]) {
-; CHECK-NEXT:    br i1 [[TMP0]], label [[TMP2:%.*]], label [[TMP3:%.*]]
-; CHECK:       2:
-; CHECK-NEXT:    tail call void @__cxa_rethrow()
-; CHECK-NEXT:    unreachable
-; CHECK:       3:
-; CHECK-NEXT:    ret i32 -1
+; COMMON-LABEL: define {{[^@]+}}@maybe_throw
+; COMMON-SAME: (i1 zeroext [[TMP0:%.*]]) {
+; COMMON-NEXT:    br i1 [[TMP0]], label [[TMP2:%.*]], label [[TMP3:%.*]]
+; COMMON:       2:
+; COMMON-NEXT:    tail call void @__cxa_rethrow()
+; COMMON-NEXT:    unreachable
+; COMMON:       3:
+; COMMON-NEXT:    ret i32 -1
 ;
   br i1 %0, label %2, label %3
 
@@ -88,18 +101,18 @@ declare void @__cxa_rethrow()
 ; }
 
 define i32 @catch_thing() personality ptr @__gxx_personality_v0 {
-; CHECK-LABEL: define {{[^@]+}}@catch_thing() personality ptr @__gxx_personality_v0 {
-; CHECK-NEXT:    invoke void @__cxa_rethrow()
-; CHECK-NEXT:    to label [[TMP1:%.*]] unwind label [[TMP2:%.*]]
-; CHECK:       1:
-; CHECK-NEXT:    unreachable
-; CHECK:       2:
-; CHECK-NEXT:    [[TMP3:%.*]] = landingpad { ptr, i32 }
-; CHECK-NEXT:    catch ptr null
-; CHECK-NEXT:    [[TMP4:%.*]] = extractvalue { ptr, i32 } [[TMP3]], 0
-; CHECK-NEXT:    [[TMP5:%.*]] = tail call ptr @__cxa_begin_catch(ptr [[TMP4]])
-; CHECK-NEXT:    tail call void @__cxa_end_catch()
-; CHECK-NEXT:    ret i32 -1
+; COMMON-LABEL: define {{[^@]+}}@catch_thing() personality ptr @__gxx_personality_v0 {
+; COMMON-NEXT:    invoke void @__cxa_rethrow()
+; COMMON-NEXT:    to label [[TMP1:%.*]] unwind label [[TMP2:%.*]]
+; COMMON:       1:
+; COMMON-NEXT:    unreachable
+; COMMON:       2:
+; COMMON-NEXT:    [[TMP3:%.*]] = landingpad { ptr, i32 }
+; COMMON-NEXT:    catch ptr null
+; COMMON-NEXT:    [[TMP4:%.*]] = extractvalue { ptr, i32 } [[TMP3]], 0
+; COMMON-NEXT:    [[TMP5:%.*]] = tail call ptr @__cxa_begin_catch(ptr [[TMP4]])
+; COMMON-NEXT:    tail call void @__cxa_end_catch()
+; COMMON-NEXT:    ret i32 -1
 ;
   invoke void @__cxa_rethrow() #1
   to label %1 unwind label %2
@@ -117,9 +130,9 @@ define i32 @catch_thing() personality ptr @__gxx_personality_v0 {
 }
 
 define i32 @catch_thing_user() {
-; CHECK-LABEL: define {{[^@]+}}@catch_thing_user() {
-; CHECK-NEXT:    [[CATCH_THING_CALL:%.*]] = call i32 @catch_thing()
-; CHECK-NEXT:    ret i32 [[CATCH_THING_CALL]]
+; COMMON-LABEL: define {{[^@]+}}@catch_thing_user() {
+; COMMON-NEXT:    [[CATCH_THING_CALL:%.*]] = call i32 @catch_thing()
+; COMMON-NEXT:    ret i32 [[CATCH_THING_CALL]]
 ;
   %catch_thing_call = call i32 @catch_thing()
   ret i32 %catch_thing_call
@@ -130,18 +143,18 @@ declare void @abort() nounwind
 @catch_ty = external global ptr
 
 define void @catch_specific_landingpad() personality ptr @__gxx_personality_v0 {
-; CHECK: Function Attrs: noreturn
-; CHECK-LABEL: define {{[^@]+}}@catch_specific_landingpad
-; CHECK-SAME: () #[[ATTR3:[0-9]+]] personality ptr @__gxx_personality_v0 {
-; CHECK-NEXT:    invoke void @do_throw()
-; CHECK-NEXT:    to label [[UNREACHABLE:%.*]] unwind label [[LPAD:%.*]]
-; CHECK:       lpad:
-; CHECK-NEXT:    [[LP:%.*]] = landingpad { ptr, i32 }
-; CHECK-NEXT:    catch ptr @catch_ty
-; CHECK-NEXT:    call void @abort()
-; CHECK-NEXT:    unreachable
-; CHECK:       unreachable:
-; CHECK-NEXT:    unreachable
+; COMMON: Function Attrs: noreturn
+; COMMON-LABEL: define {{[^@]+}}@catch_specific_landingpad
+; COMMON-SAME: () #[[ATTR3:[0-9]+]] personality ptr @__gxx_personality_v0 {
+; COMMON-NEXT:    invoke void @do_throw()
+; COMMON-NEXT:    to label [[UNREACHABLE:%.*]] unwind label [[LPAD:%.*]]
+; COMMON:       lpad:
+; COMMON-NEXT:    [[LP:%.*]] = landingpad { ptr, i32 }
+; COMMON-NEXT:    catch ptr @catch_ty
+; COMMON-NEXT:    call void @abort()
+; COMMON-NEXT:    unreachable
+; COMMON:       unreachable:
+; COMMON-NEXT:    unreachable
 ;
   invoke void @do_throw()
   to label %unreachable unwind label %lpad
@@ -157,18 +170,18 @@ unreachable:
 }
 
 define void @catch_all_landingpad() personality ptr @__gxx_personality_v0 {
-; CHECK: Function Attrs: noreturn nounwind
-; CHECK-LABEL: define {{[^@]+}}@catch_all_landingpad
-; CHECK-SAME: () #[[ATTR4:[0-9]+]] personality ptr @__gxx_personality_v0 {
-; CHECK-NEXT:    invoke void @do_throw()
-; CHECK-NEXT:    to label [[UNREACHABLE:%.*]] unwind label [[LPAD:%.*]]
-; CHECK:       lpad:
-; CHECK-NEXT:    [[LP:%.*]] = landingpad { ptr, i32 }
-; CHECK-NEXT:    catch ptr null
-; CHECK-NEXT:    call void @abort()
-; CHECK-NEXT:    unreachable
-; CHECK:       unreachable:
-; CHECK-NEXT:    unreachable
+; COMMON: Function Attrs: noreturn nounwind
+; COMMON-LABEL: define {{[^@]+}}@catch_all_landingpad
+; COMMON-SAME: () #[[ATTR4:[0-9]+]] personality ptr @__gxx_personality_v0 {
+; COMMON-NEXT:    invoke void @do_throw()
+; COMMON-NEXT:    to label [[UNREACHABLE:%.*]] unwind label [[LPAD:%.*]]
+; COMMON:       lpad:
+; COMMON-NEXT:    [[LP:%.*]] = landingpad { ptr, i32 }
+; COMMON-NEXT:    catch ptr null
+; COMMON-NEXT:    call void @abort()
+; COMMON-NEXT:    unreachable
+; COMMON:       unreachable:
+; COMMON-NEXT:    unreachable
 ;
   invoke void @do_throw()
   to label %unreachable unwind label %lpad
@@ -184,18 +197,18 @@ unreachable:
 }
 
 define void @filter_specific_landingpad() personality ptr @__gxx_personality_v0 {
-; CHECK: Function Attrs: noreturn
-; CHECK-LABEL: define {{[^@]+}}@filter_specific_landingpad
-; CHECK-SAME: () #[[ATTR3]] personality ptr @__gxx_personality_v0 {
-; CHECK-NEXT:    invoke void @do_throw()
-; CHECK-NEXT:    to label [[UNREACHABLE:%.*]] unwind label [[LPAD:%.*]]
-; CHECK:       lpad:
-; CHECK-NEXT:    [[LP:%.*]] = landingpad { ptr, i32 }
-; CHECK-NEXT:    filter [1 x ptr] [ptr @catch_ty]
-; CHECK-NEXT:    call void @abort()
-; CHECK-NEXT:    unreachable
-; CHECK:       unreachable:
-; CHECK-NEXT:    unreachable
+; COMMON: Function Attrs: noreturn
+; COMMON-LABEL: define {{[^@]+}}@filter_specific_landingpad
+; COMMON-SAME: () #[[ATTR3]] personality ptr @__gxx_personality_v0 {
+; COMMON-NEXT:    invoke void @do_throw()
+; COMMON-NEXT:    to label [[UNREACHABLE:%.*]] unwind label [[LPAD:%.*]]
+; COMMON:       lpad:
+; COMMON-NEXT:    [[LP:%.*]] = landingpad { ptr, i32 }
+; COMMON-NEXT:    filter [1 x ptr] [ptr @catch_ty]
+; COMMON-NEXT:    call void @abort()
+; COMMON-NEXT:    unreachable
+; COMMON:       unreachable:
+; COMMON-NEXT:    unreachable
 ;
   invoke void @do_throw()
   to label %unreachable unwind label %lpad
@@ -211,18 +224,18 @@ unreachable:
 }
 
 define void @filter_none_landingpad() personality ptr @__gxx_personality_v0 {
-; CHECK: Function Attrs: noreturn nounwind
-; CHECK-LABEL: define {{[^@]+}}@filter_none_landingpad
-; CHECK-SAME: () #[[ATTR4]] personality ptr @__gxx_personality_v0 {
-; CHECK-NEXT:    invoke void @do_throw()
-; CHECK-NEXT:    to label [[UNREACHABLE:%.*]] unwind label [[LPAD:%.*]]
-; CHECK:       lpad:
-; CHECK-NEXT:    [[LP:%.*]] = landingpad { ptr, i32 }
-; CHECK-NEXT:    filter [0 x ptr] zeroinitializer
-; CHECK-NEXT:    call void @abort()
-; CHECK-NEXT:    unreachable
-; CHECK:       unreachable:
-; CHECK-NEXT:    unreachable
+; COMMON: Function Attrs: noreturn nounwind
+; COMMON-LABEL: define {{[^@]+}}@filter_none_landingpad
+; COMMON-SAME: () #[[ATTR4]] personality ptr @__gxx_personality_v0 {
+; COMMON-NEXT:    invoke void @do_throw()
+; COMMON-NEXT:    to label [[UNREACHABLE:%.*]] unwind label [[LPAD:%.*]]
+; COMMON:       lpad:
+; COMMON-NEXT:    [[LP:%.*]] = landingpad { ptr, i32 }
+; COMMON-NEXT:    filter [0 x ptr] zeroinitializer
+; COMMON-NEXT:    call void @abort()
+; COMMON-NEXT:    unreachable
+; COMMON:       unreachable:
+; COMMON-NEXT:    unreachable
 ;
   invoke void @do_throw()
   to label %unreachable unwind label %lpad
@@ -238,18 +251,18 @@ unreachable:
 }
 
 define void @cleanup_landingpad() personality ptr @__gxx_personality_v0 {
-; CHECK: Function Attrs: noreturn
-; CHECK-LABEL: define {{[^@]+}}@cleanup_landingpad
-; CHECK-SAME: () #[[ATTR3]] personality ptr @__gxx_personality_v0 {
-; CHECK-NEXT:    invoke void @do_throw()
-; CHECK-NEXT:    to label [[UNREACHABLE:%.*]] unwind label [[LPAD:%.*]]
-; CHECK:       lpad:
-; CHECK-NEXT:    [[LP:%.*]] = landingpad { ptr, i32 }
-; CHECK-NEXT:    cleanup
-; CHECK-NEXT:    call void @abort()
-; CHECK-NEXT:    unreachable
-; CHECK:       unreachable:
-; CHECK-NEXT:    unreachable
+; COMMON: Function Attrs: noreturn
+; COMMON-LABEL: define {{[^@]+}}@cleanup_landingpad
+; COMMON-SAME: () #[[ATTR3]] personality ptr @__gxx_personality_v0 {
+; COMMON-NEXT:    invoke void @do_throw()
+; COMMON-NEXT:    to label [[UNREACHABLE:%.*]] unwind label [[LPAD:%.*]]
+; COMMON:       lpad:
+; COMMON-NEXT:    [[LP:%.*]] = landingpad { ptr, i32 }
+; COMMON-NEXT:    cleanup
+; COMMON-NEXT:    call void @abort()
+; COMMON-NEXT:    unreachable
+; COMMON:       unreachable:
+; COMMON-NEXT:    unreachable
 ;
   invoke void @do_throw()
   to label %unreachable unwind label %lpad
@@ -265,17 +278,29 @@ unreachable:
 }
 
 define void @cleanuppad() personality ptr @__gxx_personality_v0 {
-; CHECK: Function Attrs: noreturn
-; CHECK-LABEL: define {{[^@]+}}@cleanuppad
-; CHECK-SAME: () #[[ATTR3]] personality ptr @__gxx_personality_v0 {
-; CHECK-NEXT:    invoke void @do_throw()
-; CHECK-NEXT:    to label [[UNREACHABLE:%.*]] unwind label [[CPAD:%.*]]
-; CHECK:       cpad:
-; CHECK-NEXT:    [[CP:%.*]] = cleanuppad within none []
-; CHECK-NEXT:    call void @abort()
-; CHECK-NEXT:    unreachable
-; CHECK:       unreachable:
-; CHECK-NEXT:    unreachable
+; FNATTRS: Function Attrs: noreturn
+; FNATTRS-LABEL: define {{[^@]+}}@cleanuppad
+; FNATTRS-SAME: () #[[ATTR3]] personality ptr @__gxx_personality_v0 {
+; FNATTRS-NEXT:    invoke void @do_throw()
+; FNATTRS-NEXT:    to label [[UNREACHABLE:%.*]] unwind label [[CPAD:%.*]]
+; FNATTRS:       cpad:
+; FNATTRS-NEXT:    [[CP:%.*]] = cleanuppad within none []
+; FNATTRS-NEXT:    call void @abort()
+; FNATTRS-NEXT:    unreachable
+; FNATTRS:       unreachable:
+; FNATTRS-NEXT:    unreachable
+;
+; ATTRIBUTOR: Function Attrs: noreturn nounwind
+; ATTRIBUTOR-LABEL: define {{[^@]+}}@cleanuppad
+; ATTRIBUTOR-SAME: () #[[ATTR4]] personality ptr @__gxx_personality_v0 {
+; ATTRIBUTOR-NEXT:    invoke void @do_throw()
+; ATTRIBUTOR-NEXT:    to label [[UNREACHABLE:%.*]] unwind label [[CPAD:%.*]]
+; ATTRIBUTOR:       cpad:
+; ATTRIBUTOR-NEXT:    [[CP:%.*]] = cleanuppad within none []
+; ATTRIBUTOR-NEXT:    call void @abort()
+; ATTRIBUTOR-NEXT:    unreachable
+; ATTRIBUTOR:       unreachable:
+; ATTRIBUTOR-NEXT:    unreachable
 ;
   invoke void @do_throw()
   to label %unreachable unwind label %cpad
@@ -290,23 +315,41 @@ unreachable:
 }
 
 define void @catchswitch_cleanuppad() personality ptr @__gxx_personality_v0 {
-; CHECK: Function Attrs: noreturn
-; CHECK-LABEL: define {{[^@]+}}@catchswitch_cleanuppad
-; CHECK-SAME: () #[[ATTR3]] personality ptr @__gxx_personality_v0 {
-; CHECK-NEXT:    invoke void @do_throw()
-; CHECK-NEXT:    to label [[UNREACHABLE:%.*]] unwind label [[CS:%.*]]
-; CHECK:       cs:
-; CHECK-NEXT:    [[TOK:%.*]] = catchswitch within none [label %catch] unwind label [[CPAD:%.*]]
-; CHECK:       catch:
-; CHECK-NEXT:    [[C:%.*]] = catchpad within [[TOK]] [ptr @catch_ty, i32 0, ptr null]
-; CHECK-NEXT:    call void @abort()
-; CHECK-NEXT:    unreachable
-; CHECK:       cpad:
-; CHECK-NEXT:    [[CP:%.*]] = cleanuppad within none []
-; CHECK-NEXT:    call void @abort()
-; CHECK-NEXT:    unreachable
-; CHECK:       unreachable:
-; CHECK-NEXT:    unreachable
+; FNATTRS: Function Attrs: noreturn
+; FNATTRS-LABEL: define {{[^@]+}}@catchswitch_cleanuppad
+; FNATTRS-SAME: () #[[ATTR3]] personality ptr @__gxx_personality_v0 {
+; FNATTRS-NEXT:    invoke void @do_throw()
+; FNATTRS-NEXT:    to label [[UNREACHABLE:%.*]] unwind label [[CS:%.*]]
+; FNATTRS:       cs:
+; FNATTRS-NEXT:    [[TOK:%.*]] = catchswitch within none [label %catch] unwind label [[CPAD:%.*]]
+; FNATTRS:       catch:
+; FNATTRS-NEXT:    [[C:%.*]] = catchpad within [[TOK]] [ptr @catch_ty, i32 0, ptr null]
+; FNATTRS-NEXT:    call void @abort()
+; FNATTRS-NEXT:    unreachable
+; FNATTRS:       cpad:
+; FNATTRS-NEXT:    [[CP:%.*]] = cleanuppad within none []
+; FNATTRS-NEXT:    call void @abort()
+; FNATTRS-NEXT:    unreachable
+; FNATTRS:       unreachable:
+; FNATTRS-NEXT:    unreachable
+;
+; ATTRIBUTOR: Function Attrs: noreturn nounwind
+; ATTRIBUTOR-LABEL: define {{[^@]+}}@catchswitch_cleanuppad
+; ATTRIBUTOR-SAME: () #[[ATTR4]] personality ptr @__gxx_personality_v0 {
+; ATTRIBUTOR-NEXT:    invoke void @do_throw()
+; ATTRIBUTOR-NEXT:    to label [[UNREACHABLE:%.*]] unwind label [[CS:%.*]]
+; ATTRIBUTOR:       cs:
+; ATTRIBUTOR-NEXT:    [[TOK:%.*]] = catchswitch within none [label %catch] unwind label [[CPAD:%.*]]
+; ATTRIBUTOR:       catch:
+; ATTRIBUTOR-NEXT:    [[C:%.*]] = catchpad within [[TOK]] [ptr @catch_ty, i32 0, ptr null]
+; ATTRIBUTOR-NEXT:    call void @abort()
+; ATTRIBUTOR-NEXT:    unreachable
+; ATTRIBUTOR:       cpad:
+; ATTRIBUTOR-NEXT:    [[CP:%.*]] = cleanuppad within none []
+; ATTRIBUTOR-NEXT:    call void @abort()
+; ATTRIBUTOR-NEXT:    unreachable
+; ATTRIBUTOR:       unreachable:
+; ATTRIBUTOR-NEXT:    unreachable
 ;
   invoke void @do_throw()
   to label %unreachable unwind label %cs

diff  --git a/llvm/test/Transforms/FunctionAttrs/readattrs.ll b/llvm/test/Transforms/FunctionAttrs/readattrs.ll
index 94ffde15d338a1..086da40d3fde73 100644
--- a/llvm/test/Transforms/FunctionAttrs/readattrs.ll
+++ b/llvm/test/Transforms/FunctionAttrs/readattrs.ll
@@ -1,39 +1,78 @@
 ; NOTE: Assertions have been autogenerated by utils/update_test_checks.py UTC_ARGS: --function-signature --check-attributes
-; RUN: opt < %s -passes=function-attrs -S | FileCheck %s
+; RUN: opt < %s -passes=function-attrs -S | FileCheck --check-prefixes=COMMON,FNATTRS %s
+; RUN: opt < %s -passes=attributor-light -S | FileCheck --check-prefixes=COMMON,ATTRIBUTOR %s
+; RUN: opt < %s -passes=attributor-light-cgscc -S | FileCheck --check-prefixes=COMMON,ATTRIBUTOR-CGSCC %s
 
 @x = global i32 0
 
 declare void @test1_1(ptr %x1_1, ptr nocapture readonly %y1_1, ...)
 
 define void @test1_2(ptr %x1_2, ptr %y1_2, ptr %z1_2) {
-; CHECK-LABEL: define {{[^@]+}}@test1_2
-; CHECK-SAME: (ptr [[X1_2:%.*]], ptr nocapture readonly [[Y1_2:%.*]], ptr [[Z1_2:%.*]]) {
-; CHECK-NEXT:    call void (ptr, ptr, ...) @test1_1(ptr [[X1_2]], ptr [[Y1_2]], ptr [[Z1_2]])
-; CHECK-NEXT:    store i32 0, ptr @x, align 4
-; CHECK-NEXT:    ret void
+; FNATTRS-LABEL: define {{[^@]+}}@test1_2
+; FNATTRS-SAME: (ptr [[X1_2:%.*]], ptr nocapture readonly [[Y1_2:%.*]], ptr [[Z1_2:%.*]]) {
+; FNATTRS-NEXT:    call void (ptr, ptr, ...) @test1_1(ptr [[X1_2]], ptr [[Y1_2]], ptr [[Z1_2]])
+; FNATTRS-NEXT:    store i32 0, ptr @x, align 4
+; FNATTRS-NEXT:    ret void
+;
+; ATTRIBUTOR-LABEL: define {{[^@]+}}@test1_2
+; ATTRIBUTOR-SAME: (ptr [[X1_2:%.*]], ptr nocapture nofree readonly [[Y1_2:%.*]], ptr [[Z1_2:%.*]]) {
+; ATTRIBUTOR-NEXT:    call void (ptr, ptr, ...) @test1_1(ptr [[X1_2]], ptr nocapture nofree readonly [[Y1_2]], ptr [[Z1_2]])
+; ATTRIBUTOR-NEXT:    store i32 0, ptr @x, align 4
+; ATTRIBUTOR-NEXT:    ret void
+;
+; ATTRIBUTOR-CGSCC-LABEL: define {{[^@]+}}@test1_2
+; ATTRIBUTOR-CGSCC-SAME: (ptr [[X1_2:%.*]], ptr nocapture nofree readonly [[Y1_2:%.*]], ptr [[Z1_2:%.*]]) {
+; ATTRIBUTOR-CGSCC-NEXT:    call void (ptr, ptr, ...) @test1_1(ptr [[X1_2]], ptr nocapture nofree readonly [[Y1_2]], ptr [[Z1_2]])
+; ATTRIBUTOR-CGSCC-NEXT:    store i32 0, ptr @x, align 4
+; ATTRIBUTOR-CGSCC-NEXT:    ret void
 ;
   call void (ptr, ptr, ...) @test1_1(ptr %x1_2, ptr %y1_2, ptr %z1_2)
   store i32 0, ptr @x
   ret void
 }
 
+; TODO: Missing with attributor-light: argmem: none, inaccessiblemem: none
 define ptr @test2(ptr %p) {
-; CHECK: Function Attrs: mustprogress nofree norecurse nosync nounwind willreturn memory(write, argmem: none, inaccessiblemem: none)
-; CHECK-LABEL: define {{[^@]+}}@test2
-; CHECK-SAME: (ptr readnone returned [[P:%.*]]) #[[ATTR0:[0-9]+]] {
-; CHECK-NEXT:    store i32 0, ptr @x, align 4
-; CHECK-NEXT:    ret ptr [[P]]
+; FNATTRS: Function Attrs: mustprogress nofree norecurse nosync nounwind willreturn memory(write, argmem: none, inaccessiblemem: none)
+; FNATTRS-LABEL: define {{[^@]+}}@test2
+; FNATTRS-SAME: (ptr readnone returned [[P:%.*]]) #[[ATTR0:[0-9]+]] {
+; FNATTRS-NEXT:    store i32 0, ptr @x, align 4
+; FNATTRS-NEXT:    ret ptr [[P]]
+;
+; ATTRIBUTOR: Function Attrs: mustprogress nofree norecurse nosync nounwind willreturn memory(write)
+; ATTRIBUTOR-LABEL: define {{[^@]+}}@test2
+; ATTRIBUTOR-SAME: (ptr nofree readnone [[P:%.*]]) #[[ATTR0:[0-9]+]] {
+; ATTRIBUTOR-NEXT:    store i32 0, ptr @x, align 4
+; ATTRIBUTOR-NEXT:    ret ptr [[P]]
+;
+; ATTRIBUTOR-CGSCC: Function Attrs: mustprogress nofree norecurse nosync nounwind willreturn memory(write)
+; ATTRIBUTOR-CGSCC-LABEL: define {{[^@]+}}@test2
+; ATTRIBUTOR-CGSCC-SAME: (ptr nofree readnone [[P:%.*]]) #[[ATTR0:[0-9]+]] {
+; ATTRIBUTOR-CGSCC-NEXT:    store i32 0, ptr @x, align 4
+; ATTRIBUTOR-CGSCC-NEXT:    ret ptr [[P]]
 ;
   store i32 0, ptr @x
   ret ptr %p
 }
 
 define i1 @test3(ptr %p, ptr %q) {
-; CHECK: Function Attrs: mustprogress nofree norecurse nosync nounwind willreturn memory(none)
-; CHECK-LABEL: define {{[^@]+}}@test3
-; CHECK-SAME: (ptr readnone [[P:%.*]], ptr readnone [[Q:%.*]]) #[[ATTR1:[0-9]+]] {
-; CHECK-NEXT:    [[A:%.*]] = icmp ult ptr [[P]], [[Q]]
-; CHECK-NEXT:    ret i1 [[A]]
+; FNATTRS: Function Attrs: mustprogress nofree norecurse nosync nounwind willreturn memory(none)
+; FNATTRS-LABEL: define {{[^@]+}}@test3
+; FNATTRS-SAME: (ptr readnone [[P:%.*]], ptr readnone [[Q:%.*]]) #[[ATTR1:[0-9]+]] {
+; FNATTRS-NEXT:    [[A:%.*]] = icmp ult ptr [[P]], [[Q]]
+; FNATTRS-NEXT:    ret i1 [[A]]
+;
+; ATTRIBUTOR: Function Attrs: mustprogress nofree norecurse nosync nounwind willreturn memory(none)
+; ATTRIBUTOR-LABEL: define {{[^@]+}}@test3
+; ATTRIBUTOR-SAME: (ptr nofree readnone [[P:%.*]], ptr nofree readnone [[Q:%.*]]) #[[ATTR1:[0-9]+]] {
+; ATTRIBUTOR-NEXT:    [[A:%.*]] = icmp ult ptr [[P]], [[Q]]
+; ATTRIBUTOR-NEXT:    ret i1 [[A]]
+;
+; ATTRIBUTOR-CGSCC: Function Attrs: mustprogress nofree norecurse nosync nounwind willreturn memory(none)
+; ATTRIBUTOR-CGSCC-LABEL: define {{[^@]+}}@test3
+; ATTRIBUTOR-CGSCC-SAME: (ptr nofree readnone [[P:%.*]], ptr nofree readnone [[Q:%.*]]) #[[ATTR1:[0-9]+]] {
+; ATTRIBUTOR-CGSCC-NEXT:    [[A:%.*]] = icmp ult ptr [[P]], [[Q]]
+; ATTRIBUTOR-CGSCC-NEXT:    ret i1 [[A]]
 ;
   %A = icmp ult ptr %p, %q
   ret i1 %A
@@ -42,11 +81,23 @@ define i1 @test3(ptr %p, ptr %q) {
 declare void @test4_1(ptr nocapture) readonly
 
 define void @test4_2(ptr %p) {
-; CHECK: Function Attrs: nofree memory(read)
-; CHECK-LABEL: define {{[^@]+}}@test4_2
-; CHECK-SAME: (ptr nocapture readonly [[P:%.*]]) #[[ATTR3:[0-9]+]] {
-; CHECK-NEXT:    call void @test4_1(ptr [[P]])
-; CHECK-NEXT:    ret void
+; FNATTRS: Function Attrs: nofree memory(read)
+; FNATTRS-LABEL: define {{[^@]+}}@test4_2
+; FNATTRS-SAME: (ptr nocapture readonly [[P:%.*]]) #[[ATTR3:[0-9]+]] {
+; FNATTRS-NEXT:    call void @test4_1(ptr [[P]])
+; FNATTRS-NEXT:    ret void
+;
+; ATTRIBUTOR: Function Attrs: nosync memory(read)
+; ATTRIBUTOR-LABEL: define {{[^@]+}}@test4_2
+; ATTRIBUTOR-SAME: (ptr nocapture readonly [[P:%.*]]) #[[ATTR3:[0-9]+]] {
+; ATTRIBUTOR-NEXT:    call void @test4_1(ptr nocapture readonly [[P]]) #[[ATTR3]]
+; ATTRIBUTOR-NEXT:    ret void
+;
+; ATTRIBUTOR-CGSCC: Function Attrs: nosync memory(read)
+; ATTRIBUTOR-CGSCC-LABEL: define {{[^@]+}}@test4_2
+; ATTRIBUTOR-CGSCC-SAME: (ptr nocapture readonly [[P:%.*]]) #[[ATTR3:[0-9]+]] {
+; ATTRIBUTOR-CGSCC-NEXT:    call void @test4_1(ptr nocapture readonly [[P]]) #[[ATTR3]]
+; ATTRIBUTOR-CGSCC-NEXT:    ret void
 ;
   call void @test4_1(ptr %p)
   ret void
@@ -54,11 +105,23 @@ define void @test4_2(ptr %p) {
 
 ; Missed optz'n: we could make %q readnone, but don't break test6!
 define void @test5(ptr %p, ptr %q) {
-; CHECK: Function Attrs: mustprogress nofree norecurse nosync nounwind willreturn memory(argmem: write)
-; CHECK-LABEL: define {{[^@]+}}@test5
-; CHECK-SAME: (ptr nocapture writeonly [[P:%.*]], ptr [[Q:%.*]]) #[[ATTR4:[0-9]+]] {
-; CHECK-NEXT:    store ptr [[Q]], ptr [[P]], align 8
-; CHECK-NEXT:    ret void
+; FNATTRS: Function Attrs: mustprogress nofree norecurse nosync nounwind willreturn memory(argmem: write)
+; FNATTRS-LABEL: define {{[^@]+}}@test5
+; FNATTRS-SAME: (ptr nocapture writeonly [[P:%.*]], ptr [[Q:%.*]]) #[[ATTR4:[0-9]+]] {
+; FNATTRS-NEXT:    store ptr [[Q]], ptr [[P]], align 8
+; FNATTRS-NEXT:    ret void
+;
+; ATTRIBUTOR: Function Attrs: mustprogress nofree norecurse nosync nounwind willreturn memory(argmem: write)
+; ATTRIBUTOR-LABEL: define {{[^@]+}}@test5
+; ATTRIBUTOR-SAME: (ptr nocapture nofree nonnull writeonly [[P:%.*]], ptr nofree writeonly [[Q:%.*]]) #[[ATTR4:[0-9]+]] {
+; ATTRIBUTOR-NEXT:    store ptr [[Q]], ptr [[P]], align 8
+; ATTRIBUTOR-NEXT:    ret void
+;
+; ATTRIBUTOR-CGSCC: Function Attrs: mustprogress nofree norecurse nosync nounwind willreturn memory(argmem: write)
+; ATTRIBUTOR-CGSCC-LABEL: define {{[^@]+}}@test5
+; ATTRIBUTOR-CGSCC-SAME: (ptr nocapture nofree nonnull writeonly [[P:%.*]], ptr nofree writeonly [[Q:%.*]]) #[[ATTR4:[0-9]+]] {
+; ATTRIBUTOR-CGSCC-NEXT:    store ptr [[Q]], ptr [[P]], align 8
+; ATTRIBUTOR-CGSCC-NEXT:    ret void
 ;
   store ptr %q, ptr %p
   ret void
@@ -68,11 +131,23 @@ declare void @test6_1()
 
 ; This is not a missed optz'n.
 define void @test6_2(ptr %p, ptr %q) {
-; CHECK-LABEL: define {{[^@]+}}@test6_2
-; CHECK-SAME: (ptr nocapture writeonly [[P:%.*]], ptr [[Q:%.*]]) {
-; CHECK-NEXT:    store ptr [[Q]], ptr [[P]], align 8
-; CHECK-NEXT:    call void @test6_1()
-; CHECK-NEXT:    ret void
+; FNATTRS-LABEL: define {{[^@]+}}@test6_2
+; FNATTRS-SAME: (ptr nocapture writeonly [[P:%.*]], ptr [[Q:%.*]]) {
+; FNATTRS-NEXT:    store ptr [[Q]], ptr [[P]], align 8
+; FNATTRS-NEXT:    call void @test6_1()
+; FNATTRS-NEXT:    ret void
+;
+; ATTRIBUTOR-LABEL: define {{[^@]+}}@test6_2
+; ATTRIBUTOR-SAME: (ptr nocapture nofree nonnull writeonly [[P:%.*]], ptr nofree [[Q:%.*]]) {
+; ATTRIBUTOR-NEXT:    store ptr [[Q]], ptr [[P]], align 8
+; ATTRIBUTOR-NEXT:    call void @test6_1()
+; ATTRIBUTOR-NEXT:    ret void
+;
+; ATTRIBUTOR-CGSCC-LABEL: define {{[^@]+}}@test6_2
+; ATTRIBUTOR-CGSCC-SAME: (ptr nocapture nofree nonnull writeonly [[P:%.*]], ptr nofree [[Q:%.*]]) {
+; ATTRIBUTOR-CGSCC-NEXT:    store ptr [[Q]], ptr [[P]], align 8
+; ATTRIBUTOR-CGSCC-NEXT:    call void @test6_1()
+; ATTRIBUTOR-CGSCC-NEXT:    ret void
 ;
   store ptr %q, ptr %p
   call void @test6_1()
@@ -81,43 +156,91 @@ define void @test6_2(ptr %p, ptr %q) {
 
 ; inalloca parameters are always considered written
 define void @test7_1(ptr inalloca(i32) %a) {
-; CHECK: Function Attrs: mustprogress nofree norecurse nosync nounwind willreturn memory(argmem: readwrite)
-; CHECK-LABEL: define {{[^@]+}}@test7_1
-; CHECK-SAME: (ptr nocapture inalloca(i32) [[A:%.*]]) #[[ATTR5:[0-9]+]] {
-; CHECK-NEXT:    ret void
+; FNATTRS: Function Attrs: mustprogress nofree norecurse nosync nounwind willreturn memory(argmem: readwrite)
+; FNATTRS-LABEL: define {{[^@]+}}@test7_1
+; FNATTRS-SAME: (ptr nocapture inalloca(i32) [[A:%.*]]) #[[ATTR5:[0-9]+]] {
+; FNATTRS-NEXT:    ret void
+;
+; ATTRIBUTOR: Function Attrs: mustprogress nofree norecurse nosync nounwind willreturn memory(none)
+; ATTRIBUTOR-LABEL: define {{[^@]+}}@test7_1
+; ATTRIBUTOR-SAME: (ptr nocapture nofree nonnull writeonly inalloca(i32) [[A:%.*]]) #[[ATTR1]] {
+; ATTRIBUTOR-NEXT:    ret void
+;
+; ATTRIBUTOR-CGSCC: Function Attrs: mustprogress nofree norecurse nosync nounwind willreturn memory(none)
+; ATTRIBUTOR-CGSCC-LABEL: define {{[^@]+}}@test7_1
+; ATTRIBUTOR-CGSCC-SAME: (ptr nocapture nofree nonnull writeonly inalloca(i32) [[A:%.*]]) #[[ATTR1]] {
+; ATTRIBUTOR-CGSCC-NEXT:    ret void
 ;
   ret void
 }
 
 ; preallocated parameters are always considered written
 define void @test7_2(ptr preallocated(i32) %a) {
-; CHECK: Function Attrs: mustprogress nofree norecurse nosync nounwind willreturn memory(argmem: readwrite)
-; CHECK-LABEL: define {{[^@]+}}@test7_2
-; CHECK-SAME: (ptr nocapture preallocated(i32) [[A:%.*]]) #[[ATTR5]] {
-; CHECK-NEXT:    ret void
+; FNATTRS: Function Attrs: mustprogress nofree norecurse nosync nounwind willreturn memory(argmem: readwrite)
+; FNATTRS-LABEL: define {{[^@]+}}@test7_2
+; FNATTRS-SAME: (ptr nocapture preallocated(i32) [[A:%.*]]) #[[ATTR5]] {
+; FNATTRS-NEXT:    ret void
+;
+; ATTRIBUTOR: Function Attrs: mustprogress nofree norecurse nosync nounwind willreturn memory(none)
+; ATTRIBUTOR-LABEL: define {{[^@]+}}@test7_2
+; ATTRIBUTOR-SAME: (ptr nocapture nofree nonnull writeonly preallocated(i32) [[A:%.*]]) #[[ATTR1]] {
+; ATTRIBUTOR-NEXT:    ret void
+;
+; ATTRIBUTOR-CGSCC: Function Attrs: mustprogress nofree norecurse nosync nounwind willreturn memory(none)
+; ATTRIBUTOR-CGSCC-LABEL: define {{[^@]+}}@test7_2
+; ATTRIBUTOR-CGSCC-SAME: (ptr nocapture nofree nonnull writeonly preallocated(i32) [[A:%.*]]) #[[ATTR1]] {
+; ATTRIBUTOR-CGSCC-NEXT:    ret void
 ;
   ret void
 }
 
 define ptr @test8_1(ptr %p) {
-; CHECK: Function Attrs: mustprogress nofree norecurse nosync nounwind willreturn memory(none)
-; CHECK-LABEL: define {{[^@]+}}@test8_1
-; CHECK-SAME: (ptr readnone returned [[P:%.*]]) #[[ATTR1]] {
-; CHECK-NEXT:  entry:
-; CHECK-NEXT:    ret ptr [[P]]
+; FNATTRS: Function Attrs: mustprogress nofree norecurse nosync nounwind willreturn memory(none)
+; FNATTRS-LABEL: define {{[^@]+}}@test8_1
+; FNATTRS-SAME: (ptr readnone returned [[P:%.*]]) #[[ATTR1]] {
+; FNATTRS-NEXT:  entry:
+; FNATTRS-NEXT:    ret ptr [[P]]
+;
+; ATTRIBUTOR: Function Attrs: mustprogress nofree norecurse nosync nounwind willreturn memory(none)
+; ATTRIBUTOR-LABEL: define {{[^@]+}}@test8_1
+; ATTRIBUTOR-SAME: (ptr nofree readnone [[P:%.*]]) #[[ATTR1]] {
+; ATTRIBUTOR-NEXT:  entry:
+; ATTRIBUTOR-NEXT:    ret ptr [[P]]
+;
+; ATTRIBUTOR-CGSCC: Function Attrs: mustprogress nofree norecurse nosync nounwind willreturn memory(none)
+; ATTRIBUTOR-CGSCC-LABEL: define {{[^@]+}}@test8_1
+; ATTRIBUTOR-CGSCC-SAME: (ptr nofree readnone [[P:%.*]]) #[[ATTR1]] {
+; ATTRIBUTOR-CGSCC-NEXT:  entry:
+; ATTRIBUTOR-CGSCC-NEXT:    ret ptr [[P]]
 ;
 entry:
   ret ptr %p
 }
 
 define void @test8_2(ptr %p) {
-; CHECK: Function Attrs: mustprogress nofree norecurse nosync nounwind willreturn memory(argmem: write)
-; CHECK-LABEL: define {{[^@]+}}@test8_2
-; CHECK-SAME: (ptr writeonly [[P:%.*]]) #[[ATTR4]] {
-; CHECK-NEXT:  entry:
-; CHECK-NEXT:    [[CALL:%.*]] = call ptr @test8_1(ptr [[P]])
-; CHECK-NEXT:    store i32 10, ptr [[CALL]], align 4
-; CHECK-NEXT:    ret void
+; FNATTRS: Function Attrs: mustprogress nofree norecurse nosync nounwind willreturn memory(argmem: write)
+; FNATTRS-LABEL: define {{[^@]+}}@test8_2
+; FNATTRS-SAME: (ptr writeonly [[P:%.*]]) #[[ATTR4]] {
+; FNATTRS-NEXT:  entry:
+; FNATTRS-NEXT:    [[CALL:%.*]] = call ptr @test8_1(ptr [[P]])
+; FNATTRS-NEXT:    store i32 10, ptr [[CALL]], align 4
+; FNATTRS-NEXT:    ret void
+;
+; ATTRIBUTOR: Function Attrs: mustprogress nofree norecurse nosync nounwind willreturn memory(write)
+; ATTRIBUTOR-LABEL: define {{[^@]+}}@test8_2
+; ATTRIBUTOR-SAME: (ptr nocapture nofree writeonly [[P:%.*]]) #[[ATTR0]] {
+; ATTRIBUTOR-NEXT:  entry:
+; ATTRIBUTOR-NEXT:    [[CALL:%.*]] = call ptr @test8_1(ptr nofree readnone [[P]]) #[[ATTR13:[0-9]+]]
+; ATTRIBUTOR-NEXT:    store i32 10, ptr [[CALL]], align 4
+; ATTRIBUTOR-NEXT:    ret void
+;
+; ATTRIBUTOR-CGSCC: Function Attrs: mustprogress nofree nosync nounwind willreturn memory(write)
+; ATTRIBUTOR-CGSCC-LABEL: define {{[^@]+}}@test8_2
+; ATTRIBUTOR-CGSCC-SAME: (ptr nofree writeonly [[P:%.*]]) #[[ATTR5:[0-9]+]] {
+; ATTRIBUTOR-CGSCC-NEXT:  entry:
+; ATTRIBUTOR-CGSCC-NEXT:    [[CALL:%.*]] = call ptr @test8_1(ptr nofree readnone [[P]]) #[[ATTR13:[0-9]+]]
+; ATTRIBUTOR-CGSCC-NEXT:    store i32 10, ptr [[CALL]], align 4
+; ATTRIBUTOR-CGSCC-NEXT:    ret void
 ;
 entry:
   %call = call ptr @test8_1(ptr %p)
@@ -128,11 +251,23 @@ entry:
 declare void @llvm.masked.scatter.v4i32.v4p0(<4 x i32>%val, <4 x ptr>, i32, <4 x i1>)
 
 define void @test9(<4 x ptr> %ptrs, <4 x i32>%val) {
-; CHECK: Function Attrs: mustprogress nofree nosync nounwind willreturn memory(write)
-; CHECK-LABEL: define {{[^@]+}}@test9
-; CHECK-SAME: (<4 x ptr> [[PTRS:%.*]], <4 x i32> [[VAL:%.*]]) #[[ATTR7:[0-9]+]] {
-; CHECK-NEXT:    call void @llvm.masked.scatter.v4i32.v4p0(<4 x i32> [[VAL]], <4 x ptr> [[PTRS]], i32 4, <4 x i1> <i1 true, i1 false, i1 true, i1 false>)
-; CHECK-NEXT:    ret void
+; FNATTRS: Function Attrs: mustprogress nofree nosync nounwind willreturn memory(write)
+; FNATTRS-LABEL: define {{[^@]+}}@test9
+; FNATTRS-SAME: (<4 x ptr> [[PTRS:%.*]], <4 x i32> [[VAL:%.*]]) #[[ATTR7:[0-9]+]] {
+; FNATTRS-NEXT:    call void @llvm.masked.scatter.v4i32.v4p0(<4 x i32> [[VAL]], <4 x ptr> [[PTRS]], i32 4, <4 x i1> <i1 true, i1 false, i1 true, i1 false>)
+; FNATTRS-NEXT:    ret void
+;
+; ATTRIBUTOR: Function Attrs: mustprogress nofree norecurse nosync nounwind willreturn memory(write)
+; ATTRIBUTOR-LABEL: define {{[^@]+}}@test9
+; ATTRIBUTOR-SAME: (<4 x ptr> [[PTRS:%.*]], <4 x i32> [[VAL:%.*]]) #[[ATTR0]] {
+; ATTRIBUTOR-NEXT:    call void @llvm.masked.scatter.v4i32.v4p0(<4 x i32> [[VAL]], <4 x ptr> [[PTRS]], i32 4, <4 x i1> <i1 true, i1 false, i1 true, i1 false>) #[[ATTR14:[0-9]+]]
+; ATTRIBUTOR-NEXT:    ret void
+;
+; ATTRIBUTOR-CGSCC: Function Attrs: mustprogress nofree norecurse nosync nounwind willreturn memory(write)
+; ATTRIBUTOR-CGSCC-LABEL: define {{[^@]+}}@test9
+; ATTRIBUTOR-CGSCC-SAME: (<4 x ptr> [[PTRS:%.*]], <4 x i32> [[VAL:%.*]]) #[[ATTR0]] {
+; ATTRIBUTOR-CGSCC-NEXT:    call void @llvm.masked.scatter.v4i32.v4p0(<4 x i32> [[VAL]], <4 x ptr> [[PTRS]], i32 4, <4 x i1> <i1 true, i1 false, i1 true, i1 false>) #[[ATTR14:[0-9]+]]
+; ATTRIBUTOR-CGSCC-NEXT:    ret void
 ;
   call void @llvm.masked.scatter.v4i32.v4p0(<4 x i32>%val, <4 x ptr> %ptrs, i32 4, <4 x i1><i1 true, i1 false, i1 true, i1 false>)
   ret void
@@ -140,11 +275,23 @@ define void @test9(<4 x ptr> %ptrs, <4 x i32>%val) {
 
 declare <4 x i32> @llvm.masked.gather.v4i32.v4p0(<4 x ptr>, i32, <4 x i1>, <4 x i32>)
 define <4 x i32> @test10(<4 x ptr> %ptrs) {
-; CHECK: Function Attrs: mustprogress nofree nosync nounwind willreturn memory(read)
-; CHECK-LABEL: define {{[^@]+}}@test10
-; CHECK-SAME: (<4 x ptr> [[PTRS:%.*]]) #[[ATTR9:[0-9]+]] {
-; CHECK-NEXT:    [[RES:%.*]] = call <4 x i32> @llvm.masked.gather.v4i32.v4p0(<4 x ptr> [[PTRS]], i32 4, <4 x i1> <i1 true, i1 false, i1 true, i1 false>, <4 x i32> undef)
-; CHECK-NEXT:    ret <4 x i32> [[RES]]
+; FNATTRS: Function Attrs: mustprogress nofree nosync nounwind willreturn memory(read)
+; FNATTRS-LABEL: define {{[^@]+}}@test10
+; FNATTRS-SAME: (<4 x ptr> [[PTRS:%.*]]) #[[ATTR9:[0-9]+]] {
+; FNATTRS-NEXT:    [[RES:%.*]] = call <4 x i32> @llvm.masked.gather.v4i32.v4p0(<4 x ptr> [[PTRS]], i32 4, <4 x i1> <i1 true, i1 false, i1 true, i1 false>, <4 x i32> undef)
+; FNATTRS-NEXT:    ret <4 x i32> [[RES]]
+;
+; ATTRIBUTOR: Function Attrs: mustprogress nofree norecurse nosync nounwind willreturn memory(read)
+; ATTRIBUTOR-LABEL: define {{[^@]+}}@test10
+; ATTRIBUTOR-SAME: (<4 x ptr> [[PTRS:%.*]]) #[[ATTR7:[0-9]+]] {
+; ATTRIBUTOR-NEXT:    [[RES:%.*]] = call <4 x i32> @llvm.masked.gather.v4i32.v4p0(<4 x ptr> [[PTRS]], i32 4, <4 x i1> <i1 true, i1 false, i1 true, i1 false>, <4 x i32> undef) #[[ATTR15:[0-9]+]]
+; ATTRIBUTOR-NEXT:    ret <4 x i32> [[RES]]
+;
+; ATTRIBUTOR-CGSCC: Function Attrs: mustprogress nofree norecurse nosync nounwind willreturn memory(read)
+; ATTRIBUTOR-CGSCC-LABEL: define {{[^@]+}}@test10
+; ATTRIBUTOR-CGSCC-SAME: (<4 x ptr> [[PTRS:%.*]]) #[[ATTR8:[0-9]+]] {
+; ATTRIBUTOR-CGSCC-NEXT:    [[RES:%.*]] = call <4 x i32> @llvm.masked.gather.v4i32.v4p0(<4 x ptr> [[PTRS]], i32 4, <4 x i1> <i1 true, i1 false, i1 true, i1 false>, <4 x i32> undef) #[[ATTR15:[0-9]+]]
+; ATTRIBUTOR-CGSCC-NEXT:    ret <4 x i32> [[RES]]
 ;
   %res = call <4 x i32> @llvm.masked.gather.v4i32.v4p0(<4 x ptr> %ptrs, i32 4, <4 x i1><i1 true, i1 false, i1 true, i1 false>, <4 x i32>undef)
   ret <4 x i32> %res
@@ -152,11 +299,23 @@ define <4 x i32> @test10(<4 x ptr> %ptrs) {
 
 declare <4 x i32> @test11_1(<4 x ptr>) argmemonly nounwind readonly
 define <4 x i32> @test11_2(<4 x ptr> %ptrs) {
-; CHECK: Function Attrs: nofree nounwind memory(argmem: read)
-; CHECK-LABEL: define {{[^@]+}}@test11_2
-; CHECK-SAME: (<4 x ptr> [[PTRS:%.*]]) #[[ATTR11:[0-9]+]] {
-; CHECK-NEXT:    [[RES:%.*]] = call <4 x i32> @test11_1(<4 x ptr> [[PTRS]])
-; CHECK-NEXT:    ret <4 x i32> [[RES]]
+; FNATTRS: Function Attrs: nofree nounwind memory(argmem: read)
+; FNATTRS-LABEL: define {{[^@]+}}@test11_2
+; FNATTRS-SAME: (<4 x ptr> [[PTRS:%.*]]) #[[ATTR11:[0-9]+]] {
+; FNATTRS-NEXT:    [[RES:%.*]] = call <4 x i32> @test11_1(<4 x ptr> [[PTRS]])
+; FNATTRS-NEXT:    ret <4 x i32> [[RES]]
+;
+; ATTRIBUTOR: Function Attrs: nosync nounwind memory(argmem: read)
+; ATTRIBUTOR-LABEL: define {{[^@]+}}@test11_2
+; ATTRIBUTOR-SAME: (<4 x ptr> [[PTRS:%.*]]) #[[ATTR9:[0-9]+]] {
+; ATTRIBUTOR-NEXT:    [[RES:%.*]] = call <4 x i32> @test11_1(<4 x ptr> [[PTRS]]) #[[ATTR3]]
+; ATTRIBUTOR-NEXT:    ret <4 x i32> [[RES]]
+;
+; ATTRIBUTOR-CGSCC: Function Attrs: nosync nounwind memory(argmem: read)
+; ATTRIBUTOR-CGSCC-LABEL: define {{[^@]+}}@test11_2
+; ATTRIBUTOR-CGSCC-SAME: (<4 x ptr> [[PTRS:%.*]]) #[[ATTR10:[0-9]+]] {
+; ATTRIBUTOR-CGSCC-NEXT:    [[RES:%.*]] = call <4 x i32> @test11_1(<4 x ptr> [[PTRS]]) #[[ATTR3]]
+; ATTRIBUTOR-CGSCC-NEXT:    ret <4 x i32> [[RES]]
 ;
   %res = call <4 x i32> @test11_1(<4 x ptr> %ptrs)
   ret <4 x i32> %res
@@ -164,22 +323,46 @@ define <4 x i32> @test11_2(<4 x ptr> %ptrs) {
 
 declare <4 x i32> @test12_1(<4 x ptr>) argmemonly nounwind
 define <4 x i32> @test12_2(<4 x ptr> %ptrs) {
-; CHECK: Function Attrs: nounwind memory(argmem: readwrite)
-; CHECK-LABEL: define {{[^@]+}}@test12_2
-; CHECK-SAME: (<4 x ptr> [[PTRS:%.*]]) #[[ATTR12:[0-9]+]] {
-; CHECK-NEXT:    [[RES:%.*]] = call <4 x i32> @test12_1(<4 x ptr> [[PTRS]])
-; CHECK-NEXT:    ret <4 x i32> [[RES]]
+; FNATTRS: Function Attrs: nounwind memory(argmem: readwrite)
+; FNATTRS-LABEL: define {{[^@]+}}@test12_2
+; FNATTRS-SAME: (<4 x ptr> [[PTRS:%.*]]) #[[ATTR12:[0-9]+]] {
+; FNATTRS-NEXT:    [[RES:%.*]] = call <4 x i32> @test12_1(<4 x ptr> [[PTRS]])
+; FNATTRS-NEXT:    ret <4 x i32> [[RES]]
+;
+; ATTRIBUTOR: Function Attrs: nounwind memory(argmem: readwrite)
+; ATTRIBUTOR-LABEL: define {{[^@]+}}@test12_2
+; ATTRIBUTOR-SAME: (<4 x ptr> [[PTRS:%.*]]) #[[ATTR10:[0-9]+]] {
+; ATTRIBUTOR-NEXT:    [[RES:%.*]] = call <4 x i32> @test12_1(<4 x ptr> [[PTRS]])
+; ATTRIBUTOR-NEXT:    ret <4 x i32> [[RES]]
+;
+; ATTRIBUTOR-CGSCC: Function Attrs: nounwind memory(argmem: readwrite)
+; ATTRIBUTOR-CGSCC-LABEL: define {{[^@]+}}@test12_2
+; ATTRIBUTOR-CGSCC-SAME: (<4 x ptr> [[PTRS:%.*]]) #[[ATTR11:[0-9]+]] {
+; ATTRIBUTOR-CGSCC-NEXT:    [[RES:%.*]] = call <4 x i32> @test12_1(<4 x ptr> [[PTRS]])
+; ATTRIBUTOR-CGSCC-NEXT:    ret <4 x i32> [[RES]]
 ;
   %res = call <4 x i32> @test12_1(<4 x ptr> %ptrs)
   ret <4 x i32> %res
 }
 
 define i32 @volatile_load(ptr %p) {
-; CHECK: Function Attrs: mustprogress nofree norecurse nounwind willreturn memory(argmem: readwrite, inaccessiblemem: readwrite)
-; CHECK-LABEL: define {{[^@]+}}@volatile_load
-; CHECK-SAME: (ptr [[P:%.*]]) #[[ATTR13:[0-9]+]] {
-; CHECK-NEXT:    [[LOAD:%.*]] = load volatile i32, ptr [[P]], align 4
-; CHECK-NEXT:    ret i32 [[LOAD]]
+; FNATTRS: Function Attrs: mustprogress nofree norecurse nounwind willreturn memory(argmem: readwrite, inaccessiblemem: readwrite)
+; FNATTRS-LABEL: define {{[^@]+}}@volatile_load
+; FNATTRS-SAME: (ptr [[P:%.*]]) #[[ATTR13:[0-9]+]] {
+; FNATTRS-NEXT:    [[LOAD:%.*]] = load volatile i32, ptr [[P]], align 4
+; FNATTRS-NEXT:    ret i32 [[LOAD]]
+;
+; ATTRIBUTOR: Function Attrs: mustprogress nofree norecurse nounwind willreturn memory(argmem: readwrite)
+; ATTRIBUTOR-LABEL: define {{[^@]+}}@volatile_load
+; ATTRIBUTOR-SAME: (ptr nofree [[P:%.*]]) #[[ATTR11:[0-9]+]] {
+; ATTRIBUTOR-NEXT:    [[LOAD:%.*]] = load volatile i32, ptr [[P]], align 4
+; ATTRIBUTOR-NEXT:    ret i32 [[LOAD]]
+;
+; ATTRIBUTOR-CGSCC: Function Attrs: mustprogress nofree norecurse nounwind willreturn memory(argmem: readwrite)
+; ATTRIBUTOR-CGSCC-LABEL: define {{[^@]+}}@volatile_load
+; ATTRIBUTOR-CGSCC-SAME: (ptr nofree [[P:%.*]]) #[[ATTR12:[0-9]+]] {
+; ATTRIBUTOR-CGSCC-NEXT:    [[LOAD:%.*]] = load volatile i32, ptr [[P]], align 4
+; ATTRIBUTOR-CGSCC-NEXT:    ret i32 [[LOAD]]
 ;
   %load = load volatile i32, ptr %p
   ret i32 %load
@@ -193,13 +376,29 @@ declare void @escape_readonly_ptr(ptr %addr, ptr readonly %ptr)
 ; is marked as readnone/only. However, the functions can write the pointer into
 ; %addr, causing the store to write to %escaped_then_written.
 define void @unsound_readnone(ptr %ignored, ptr %escaped_then_written) {
-; CHECK-LABEL: define {{[^@]+}}@unsound_readnone
-; CHECK-SAME: (ptr nocapture readnone [[IGNORED:%.*]], ptr [[ESCAPED_THEN_WRITTEN:%.*]]) {
-; CHECK-NEXT:    [[ADDR:%.*]] = alloca ptr, align 8
-; CHECK-NEXT:    call void @escape_readnone_ptr(ptr [[ADDR]], ptr [[ESCAPED_THEN_WRITTEN]])
-; CHECK-NEXT:    [[ADDR_LD:%.*]] = load ptr, ptr [[ADDR]], align 8
-; CHECK-NEXT:    store i8 0, ptr [[ADDR_LD]], align 1
-; CHECK-NEXT:    ret void
+; FNATTRS-LABEL: define {{[^@]+}}@unsound_readnone
+; FNATTRS-SAME: (ptr nocapture readnone [[IGNORED:%.*]], ptr [[ESCAPED_THEN_WRITTEN:%.*]]) {
+; FNATTRS-NEXT:    [[ADDR:%.*]] = alloca ptr, align 8
+; FNATTRS-NEXT:    call void @escape_readnone_ptr(ptr [[ADDR]], ptr [[ESCAPED_THEN_WRITTEN]])
+; FNATTRS-NEXT:    [[ADDR_LD:%.*]] = load ptr, ptr [[ADDR]], align 8
+; FNATTRS-NEXT:    store i8 0, ptr [[ADDR_LD]], align 1
+; FNATTRS-NEXT:    ret void
+;
+; ATTRIBUTOR-LABEL: define {{[^@]+}}@unsound_readnone
+; ATTRIBUTOR-SAME: (ptr nocapture nofree readnone [[IGNORED:%.*]], ptr nofree [[ESCAPED_THEN_WRITTEN:%.*]]) {
+; ATTRIBUTOR-NEXT:    [[ADDR:%.*]] = alloca ptr, align 8
+; ATTRIBUTOR-NEXT:    call void @escape_readnone_ptr(ptr [[ADDR]], ptr nofree [[ESCAPED_THEN_WRITTEN]])
+; ATTRIBUTOR-NEXT:    [[ADDR_LD:%.*]] = load ptr, ptr [[ADDR]], align 8
+; ATTRIBUTOR-NEXT:    store i8 0, ptr [[ADDR_LD]], align 1
+; ATTRIBUTOR-NEXT:    ret void
+;
+; ATTRIBUTOR-CGSCC-LABEL: define {{[^@]+}}@unsound_readnone
+; ATTRIBUTOR-CGSCC-SAME: (ptr nocapture nofree readnone [[IGNORED:%.*]], ptr nofree [[ESCAPED_THEN_WRITTEN:%.*]]) {
+; ATTRIBUTOR-CGSCC-NEXT:    [[ADDR:%.*]] = alloca ptr, align 8
+; ATTRIBUTOR-CGSCC-NEXT:    call void @escape_readnone_ptr(ptr [[ADDR]], ptr nofree [[ESCAPED_THEN_WRITTEN]])
+; ATTRIBUTOR-CGSCC-NEXT:    [[ADDR_LD:%.*]] = load ptr, ptr [[ADDR]], align 8
+; ATTRIBUTOR-CGSCC-NEXT:    store i8 0, ptr [[ADDR_LD]], align 1
+; ATTRIBUTOR-CGSCC-NEXT:    ret void
 ;
   %addr = alloca ptr
   call void @escape_readnone_ptr(ptr %addr, ptr %escaped_then_written)
@@ -209,13 +408,29 @@ define void @unsound_readnone(ptr %ignored, ptr %escaped_then_written) {
 }
 
 define void @unsound_readonly(ptr %ignored, ptr %escaped_then_written) {
-; CHECK-LABEL: define {{[^@]+}}@unsound_readonly
-; CHECK-SAME: (ptr nocapture readnone [[IGNORED:%.*]], ptr [[ESCAPED_THEN_WRITTEN:%.*]]) {
-; CHECK-NEXT:    [[ADDR:%.*]] = alloca ptr, align 8
-; CHECK-NEXT:    call void @escape_readonly_ptr(ptr [[ADDR]], ptr [[ESCAPED_THEN_WRITTEN]])
-; CHECK-NEXT:    [[ADDR_LD:%.*]] = load ptr, ptr [[ADDR]], align 8
-; CHECK-NEXT:    store i8 0, ptr [[ADDR_LD]], align 1
-; CHECK-NEXT:    ret void
+; FNATTRS-LABEL: define {{[^@]+}}@unsound_readonly
+; FNATTRS-SAME: (ptr nocapture readnone [[IGNORED:%.*]], ptr [[ESCAPED_THEN_WRITTEN:%.*]]) {
+; FNATTRS-NEXT:    [[ADDR:%.*]] = alloca ptr, align 8
+; FNATTRS-NEXT:    call void @escape_readonly_ptr(ptr [[ADDR]], ptr [[ESCAPED_THEN_WRITTEN]])
+; FNATTRS-NEXT:    [[ADDR_LD:%.*]] = load ptr, ptr [[ADDR]], align 8
+; FNATTRS-NEXT:    store i8 0, ptr [[ADDR_LD]], align 1
+; FNATTRS-NEXT:    ret void
+;
+; ATTRIBUTOR-LABEL: define {{[^@]+}}@unsound_readonly
+; ATTRIBUTOR-SAME: (ptr nocapture nofree readnone [[IGNORED:%.*]], ptr nofree [[ESCAPED_THEN_WRITTEN:%.*]]) {
+; ATTRIBUTOR-NEXT:    [[ADDR:%.*]] = alloca ptr, align 8
+; ATTRIBUTOR-NEXT:    call void @escape_readonly_ptr(ptr [[ADDR]], ptr nofree [[ESCAPED_THEN_WRITTEN]])
+; ATTRIBUTOR-NEXT:    [[ADDR_LD:%.*]] = load ptr, ptr [[ADDR]], align 8
+; ATTRIBUTOR-NEXT:    store i8 0, ptr [[ADDR_LD]], align 1
+; ATTRIBUTOR-NEXT:    ret void
+;
+; ATTRIBUTOR-CGSCC-LABEL: define {{[^@]+}}@unsound_readonly
+; ATTRIBUTOR-CGSCC-SAME: (ptr nocapture nofree readnone [[IGNORED:%.*]], ptr nofree [[ESCAPED_THEN_WRITTEN:%.*]]) {
+; ATTRIBUTOR-CGSCC-NEXT:    [[ADDR:%.*]] = alloca ptr, align 8
+; ATTRIBUTOR-CGSCC-NEXT:    call void @escape_readonly_ptr(ptr [[ADDR]], ptr nofree [[ESCAPED_THEN_WRITTEN]])
+; ATTRIBUTOR-CGSCC-NEXT:    [[ADDR_LD:%.*]] = load ptr, ptr [[ADDR]], align 8
+; ATTRIBUTOR-CGSCC-NEXT:    store i8 0, ptr [[ADDR_LD]], align 1
+; ATTRIBUTOR-CGSCC-NEXT:    ret void
 ;
   %addr = alloca ptr
   call void @escape_readonly_ptr(ptr %addr, ptr %escaped_then_written)
@@ -225,10 +440,20 @@ define void @unsound_readonly(ptr %ignored, ptr %escaped_then_written) {
 }
 
 define void @fptr_test1a(ptr %p, ptr %f) {
-; CHECK-LABEL: define {{[^@]+}}@fptr_test1a
-; CHECK-SAME: (ptr nocapture readnone [[P:%.*]], ptr nocapture readonly [[F:%.*]]) {
-; CHECK-NEXT:    call void [[F]](ptr nocapture readnone [[P]])
-; CHECK-NEXT:    ret void
+; FNATTRS-LABEL: define {{[^@]+}}@fptr_test1a
+; FNATTRS-SAME: (ptr nocapture readnone [[P:%.*]], ptr nocapture readonly [[F:%.*]]) {
+; FNATTRS-NEXT:    call void [[F]](ptr nocapture readnone [[P]])
+; FNATTRS-NEXT:    ret void
+;
+; ATTRIBUTOR-LABEL: define {{[^@]+}}@fptr_test1a
+; ATTRIBUTOR-SAME: (ptr nocapture nofree [[P:%.*]], ptr nocapture nofree nonnull [[F:%.*]]) {
+; ATTRIBUTOR-NEXT:    call void [[F]](ptr nocapture nofree readnone [[P]])
+; ATTRIBUTOR-NEXT:    ret void
+;
+; ATTRIBUTOR-CGSCC-LABEL: define {{[^@]+}}@fptr_test1a
+; ATTRIBUTOR-CGSCC-SAME: (ptr nocapture nofree [[P:%.*]], ptr nocapture nofree nonnull [[F:%.*]]) {
+; ATTRIBUTOR-CGSCC-NEXT:    call void [[F]](ptr nocapture nofree readnone [[P]])
+; ATTRIBUTOR-CGSCC-NEXT:    ret void
 ;
   call void %f(ptr nocapture readnone %p)
   ret void
@@ -236,31 +461,63 @@ define void @fptr_test1a(ptr %p, ptr %f) {
 
 ; Can't infer readnone here because call might capture %p
 define void @fptr_test1b(ptr %p, ptr %f) {
-; CHECK-LABEL: define {{[^@]+}}@fptr_test1b
-; CHECK-SAME: (ptr [[P:%.*]], ptr nocapture readonly [[F:%.*]]) {
-; CHECK-NEXT:    call void [[F]](ptr readnone [[P]])
-; CHECK-NEXT:    ret void
+; FNATTRS-LABEL: define {{[^@]+}}@fptr_test1b
+; FNATTRS-SAME: (ptr [[P:%.*]], ptr nocapture readonly [[F:%.*]]) {
+; FNATTRS-NEXT:    call void [[F]](ptr readnone [[P]])
+; FNATTRS-NEXT:    ret void
+;
+; ATTRIBUTOR-LABEL: define {{[^@]+}}@fptr_test1b
+; ATTRIBUTOR-SAME: (ptr nofree [[P:%.*]], ptr nocapture nofree nonnull [[F:%.*]]) {
+; ATTRIBUTOR-NEXT:    call void [[F]](ptr nofree readnone [[P]])
+; ATTRIBUTOR-NEXT:    ret void
+;
+; ATTRIBUTOR-CGSCC-LABEL: define {{[^@]+}}@fptr_test1b
+; ATTRIBUTOR-CGSCC-SAME: (ptr nofree [[P:%.*]], ptr nocapture nofree nonnull [[F:%.*]]) {
+; ATTRIBUTOR-CGSCC-NEXT:    call void [[F]](ptr nofree readnone [[P]])
+; ATTRIBUTOR-CGSCC-NEXT:    ret void
 ;
   call void %f(ptr readnone %p)
   ret void
 }
 
 define void @fptr_test1c(ptr %p, ptr %f) {
-; CHECK: Function Attrs: nofree memory(read)
-; CHECK-LABEL: define {{[^@]+}}@fptr_test1c
-; CHECK-SAME: (ptr readnone [[P:%.*]], ptr nocapture readonly [[F:%.*]]) #[[ATTR3]] {
-; CHECK-NEXT:    call void [[F]](ptr readnone [[P]]) #[[ATTR2:[0-9]+]]
-; CHECK-NEXT:    ret void
+; FNATTRS: Function Attrs: nofree memory(read)
+; FNATTRS-LABEL: define {{[^@]+}}@fptr_test1c
+; FNATTRS-SAME: (ptr readnone [[P:%.*]], ptr nocapture readonly [[F:%.*]]) #[[ATTR3]] {
+; FNATTRS-NEXT:    call void [[F]](ptr readnone [[P]]) #[[ATTR2:[0-9]+]]
+; FNATTRS-NEXT:    ret void
+;
+; ATTRIBUTOR: Function Attrs: memory(read)
+; ATTRIBUTOR-LABEL: define {{[^@]+}}@fptr_test1c
+; ATTRIBUTOR-SAME: (ptr nofree readonly [[P:%.*]], ptr nocapture nofree nonnull readonly [[F:%.*]]) #[[ATTR2:[0-9]+]] {
+; ATTRIBUTOR-NEXT:    call void [[F]](ptr nofree readnone [[P]]) #[[ATTR2]]
+; ATTRIBUTOR-NEXT:    ret void
+;
+; ATTRIBUTOR-CGSCC: Function Attrs: memory(read)
+; ATTRIBUTOR-CGSCC-LABEL: define {{[^@]+}}@fptr_test1c
+; ATTRIBUTOR-CGSCC-SAME: (ptr nofree readonly [[P:%.*]], ptr nocapture nofree nonnull readonly [[F:%.*]]) #[[ATTR2:[0-9]+]] {
+; ATTRIBUTOR-CGSCC-NEXT:    call void [[F]](ptr nofree readnone [[P]]) #[[ATTR2]]
+; ATTRIBUTOR-CGSCC-NEXT:    ret void
 ;
   call void %f(ptr readnone %p) readonly
   ret void
 }
 
 define void @fptr_test2a(ptr %p, ptr %f) {
-; CHECK-LABEL: define {{[^@]+}}@fptr_test2a
-; CHECK-SAME: (ptr nocapture readonly [[P:%.*]], ptr nocapture readonly [[F:%.*]]) {
-; CHECK-NEXT:    call void [[F]](ptr nocapture readonly [[P]])
-; CHECK-NEXT:    ret void
+; FNATTRS-LABEL: define {{[^@]+}}@fptr_test2a
+; FNATTRS-SAME: (ptr nocapture readonly [[P:%.*]], ptr nocapture readonly [[F:%.*]]) {
+; FNATTRS-NEXT:    call void [[F]](ptr nocapture readonly [[P]])
+; FNATTRS-NEXT:    ret void
+;
+; ATTRIBUTOR-LABEL: define {{[^@]+}}@fptr_test2a
+; ATTRIBUTOR-SAME: (ptr nocapture nofree [[P:%.*]], ptr nocapture nofree nonnull [[F:%.*]]) {
+; ATTRIBUTOR-NEXT:    call void [[F]](ptr nocapture nofree readonly [[P]])
+; ATTRIBUTOR-NEXT:    ret void
+;
+; ATTRIBUTOR-CGSCC-LABEL: define {{[^@]+}}@fptr_test2a
+; ATTRIBUTOR-CGSCC-SAME: (ptr nocapture nofree [[P:%.*]], ptr nocapture nofree nonnull [[F:%.*]]) {
+; ATTRIBUTOR-CGSCC-NEXT:    call void [[F]](ptr nocapture nofree readonly [[P]])
+; ATTRIBUTOR-CGSCC-NEXT:    ret void
 ;
   call void %f(ptr nocapture readonly %p)
   ret void
@@ -268,43 +525,99 @@ define void @fptr_test2a(ptr %p, ptr %f) {
 
 define void @fptr_test2b(ptr %p, ptr %f) {
   ; Can't infer readonly here because call might capture %p
-; CHECK-LABEL: define {{[^@]+}}@fptr_test2b
-; CHECK-SAME: (ptr [[P:%.*]], ptr nocapture readonly [[F:%.*]]) {
-; CHECK-NEXT:    call void [[F]](ptr readonly [[P]])
-; CHECK-NEXT:    ret void
+; FNATTRS-LABEL: define {{[^@]+}}@fptr_test2b
+; FNATTRS-SAME: (ptr [[P:%.*]], ptr nocapture readonly [[F:%.*]]) {
+; FNATTRS-NEXT:    call void [[F]](ptr readonly [[P]])
+; FNATTRS-NEXT:    ret void
+;
+; ATTRIBUTOR-LABEL: define {{[^@]+}}@fptr_test2b
+; ATTRIBUTOR-SAME: (ptr nofree [[P:%.*]], ptr nocapture nofree nonnull [[F:%.*]]) {
+; ATTRIBUTOR-NEXT:    call void [[F]](ptr nofree readonly [[P]])
+; ATTRIBUTOR-NEXT:    ret void
+;
+; ATTRIBUTOR-CGSCC-LABEL: define {{[^@]+}}@fptr_test2b
+; ATTRIBUTOR-CGSCC-SAME: (ptr nofree [[P:%.*]], ptr nocapture nofree nonnull [[F:%.*]]) {
+; ATTRIBUTOR-CGSCC-NEXT:    call void [[F]](ptr nofree readonly [[P]])
+; ATTRIBUTOR-CGSCC-NEXT:    ret void
 ;
   call void %f(ptr readonly %p)
   ret void
 }
 
 define void @fptr_test2c(ptr %p, ptr %f) {
-; CHECK: Function Attrs: nofree memory(read)
-; CHECK-LABEL: define {{[^@]+}}@fptr_test2c
-; CHECK-SAME: (ptr readonly [[P:%.*]], ptr nocapture readonly [[F:%.*]]) #[[ATTR3]] {
-; CHECK-NEXT:    call void [[F]](ptr readonly [[P]]) #[[ATTR2]]
-; CHECK-NEXT:    ret void
+; FNATTRS: Function Attrs: nofree memory(read)
+; FNATTRS-LABEL: define {{[^@]+}}@fptr_test2c
+; FNATTRS-SAME: (ptr readonly [[P:%.*]], ptr nocapture readonly [[F:%.*]]) #[[ATTR3]] {
+; FNATTRS-NEXT:    call void [[F]](ptr readonly [[P]]) #[[ATTR2]]
+; FNATTRS-NEXT:    ret void
+;
+; ATTRIBUTOR: Function Attrs: memory(read)
+; ATTRIBUTOR-LABEL: define {{[^@]+}}@fptr_test2c
+; ATTRIBUTOR-SAME: (ptr nofree readonly [[P:%.*]], ptr nocapture nofree nonnull readonly [[F:%.*]]) #[[ATTR2]] {
+; ATTRIBUTOR-NEXT:    call void [[F]](ptr nofree readonly [[P]]) #[[ATTR2]]
+; ATTRIBUTOR-NEXT:    ret void
+;
+; ATTRIBUTOR-CGSCC: Function Attrs: memory(read)
+; ATTRIBUTOR-CGSCC-LABEL: define {{[^@]+}}@fptr_test2c
+; ATTRIBUTOR-CGSCC-SAME: (ptr nofree readonly [[P:%.*]], ptr nocapture nofree nonnull readonly [[F:%.*]]) #[[ATTR2]] {
+; ATTRIBUTOR-CGSCC-NEXT:    call void [[F]](ptr nofree readonly [[P]]) #[[ATTR2]]
+; ATTRIBUTOR-CGSCC-NEXT:    ret void
 ;
   call void %f(ptr readonly %p) readonly
   ret void
 }
 
 define void @alloca_recphi() {
-; CHECK: Function Attrs: nofree norecurse nosync nounwind memory(none)
-; CHECK-LABEL: define {{[^@]+}}@alloca_recphi
-; CHECK-SAME: () #[[ATTR14:[0-9]+]] {
-; CHECK-NEXT:  entry:
-; CHECK-NEXT:    [[A:%.*]] = alloca [8 x i32], align 4
-; CHECK-NEXT:    [[A_END:%.*]] = getelementptr i32, ptr [[A]], i64 8
-; CHECK-NEXT:    br label [[LOOP:%.*]]
-; CHECK:       loop:
-; CHECK-NEXT:    [[P:%.*]] = phi ptr [ [[A]], [[ENTRY:%.*]] ], [ [[P_NEXT:%.*]], [[LOOP]] ]
-; CHECK-NEXT:    store i32 0, ptr [[P]], align 4
-; CHECK-NEXT:    [[TMP0:%.*]] = load i32, ptr [[P]], align 4
-; CHECK-NEXT:    [[P_NEXT]] = getelementptr i32, ptr [[P]], i64 1
-; CHECK-NEXT:    [[C:%.*]] = icmp ne ptr [[P_NEXT]], [[A_END]]
-; CHECK-NEXT:    br i1 [[C]], label [[LOOP]], label [[EXIT:%.*]]
-; CHECK:       exit:
-; CHECK-NEXT:    ret void
+; FNATTRS: Function Attrs: nofree norecurse nosync nounwind memory(none)
+; FNATTRS-LABEL: define {{[^@]+}}@alloca_recphi
+; FNATTRS-SAME: () #[[ATTR14:[0-9]+]] {
+; FNATTRS-NEXT:  entry:
+; FNATTRS-NEXT:    [[A:%.*]] = alloca [8 x i32], align 4
+; FNATTRS-NEXT:    [[A_END:%.*]] = getelementptr i32, ptr [[A]], i64 8
+; FNATTRS-NEXT:    br label [[LOOP:%.*]]
+; FNATTRS:       loop:
+; FNATTRS-NEXT:    [[P:%.*]] = phi ptr [ [[A]], [[ENTRY:%.*]] ], [ [[P_NEXT:%.*]], [[LOOP]] ]
+; FNATTRS-NEXT:    store i32 0, ptr [[P]], align 4
+; FNATTRS-NEXT:    [[TMP0:%.*]] = load i32, ptr [[P]], align 4
+; FNATTRS-NEXT:    [[P_NEXT]] = getelementptr i32, ptr [[P]], i64 1
+; FNATTRS-NEXT:    [[C:%.*]] = icmp ne ptr [[P_NEXT]], [[A_END]]
+; FNATTRS-NEXT:    br i1 [[C]], label [[LOOP]], label [[EXIT:%.*]]
+; FNATTRS:       exit:
+; FNATTRS-NEXT:    ret void
+;
+; ATTRIBUTOR: Function Attrs: nofree norecurse nosync nounwind memory(none)
+; ATTRIBUTOR-LABEL: define {{[^@]+}}@alloca_recphi
+; ATTRIBUTOR-SAME: () #[[ATTR12:[0-9]+]] {
+; ATTRIBUTOR-NEXT:  entry:
+; ATTRIBUTOR-NEXT:    [[A:%.*]] = alloca [8 x i32], align 4
+; ATTRIBUTOR-NEXT:    [[A_END:%.*]] = getelementptr i32, ptr [[A]], i64 8
+; ATTRIBUTOR-NEXT:    br label [[LOOP:%.*]]
+; ATTRIBUTOR:       loop:
+; ATTRIBUTOR-NEXT:    [[P:%.*]] = phi ptr [ [[A]], [[ENTRY:%.*]] ], [ [[P_NEXT:%.*]], [[LOOP]] ]
+; ATTRIBUTOR-NEXT:    store i32 0, ptr [[P]], align 4
+; ATTRIBUTOR-NEXT:    [[TMP0:%.*]] = load i32, ptr [[P]], align 4
+; ATTRIBUTOR-NEXT:    [[P_NEXT]] = getelementptr i32, ptr [[P]], i64 1
+; ATTRIBUTOR-NEXT:    [[C:%.*]] = icmp ne ptr [[P_NEXT]], [[A_END]]
+; ATTRIBUTOR-NEXT:    br i1 [[C]], label [[LOOP]], label [[EXIT:%.*]]
+; ATTRIBUTOR:       exit:
+; ATTRIBUTOR-NEXT:    ret void
+;
+; ATTRIBUTOR-CGSCC: Function Attrs: mustprogress nofree norecurse nosync nounwind willreturn memory(none)
+; ATTRIBUTOR-CGSCC-LABEL: define {{[^@]+}}@alloca_recphi
+; ATTRIBUTOR-CGSCC-SAME: () #[[ATTR1]] {
+; ATTRIBUTOR-CGSCC-NEXT:  entry:
+; ATTRIBUTOR-CGSCC-NEXT:    [[A:%.*]] = alloca [8 x i32], align 4
+; ATTRIBUTOR-CGSCC-NEXT:    [[A_END:%.*]] = getelementptr i32, ptr [[A]], i64 8
+; ATTRIBUTOR-CGSCC-NEXT:    br label [[LOOP:%.*]]
+; ATTRIBUTOR-CGSCC:       loop:
+; ATTRIBUTOR-CGSCC-NEXT:    [[P:%.*]] = phi ptr [ [[A]], [[ENTRY:%.*]] ], [ [[P_NEXT:%.*]], [[LOOP]] ]
+; ATTRIBUTOR-CGSCC-NEXT:    store i32 0, ptr [[P]], align 4
+; ATTRIBUTOR-CGSCC-NEXT:    [[TMP0:%.*]] = load i32, ptr [[P]], align 4
+; ATTRIBUTOR-CGSCC-NEXT:    [[P_NEXT]] = getelementptr i32, ptr [[P]], i64 1
+; ATTRIBUTOR-CGSCC-NEXT:    [[C:%.*]] = icmp ne ptr [[P_NEXT]], [[A_END]]
+; ATTRIBUTOR-CGSCC-NEXT:    br i1 [[C]], label [[LOOP]], label [[EXIT:%.*]]
+; ATTRIBUTOR-CGSCC:       exit:
+; ATTRIBUTOR-CGSCC-NEXT:    ret void
 ;
 entry:
   %a = alloca [8 x i32]
@@ -328,41 +641,83 @@ declare void @readonly_param(ptr nocapture readonly %p)
 
 ; FIXME: While this can't be readnone, this could be readonly.
 define void @op_bundle_readnone_deopt(ptr %p) {
-; CHECK-LABEL: define {{[^@]+}}@op_bundle_readnone_deopt
-; CHECK-SAME: (ptr nocapture [[P:%.*]]) {
-; CHECK-NEXT:    call void @readnone_param(ptr [[P]]) [ "deopt"() ]
-; CHECK-NEXT:    ret void
+; FNATTRS-LABEL: define {{[^@]+}}@op_bundle_readnone_deopt
+; FNATTRS-SAME: (ptr nocapture [[P:%.*]]) {
+; FNATTRS-NEXT:    call void @readnone_param(ptr [[P]]) [ "deopt"() ]
+; FNATTRS-NEXT:    ret void
+;
+; ATTRIBUTOR-LABEL: define {{[^@]+}}@op_bundle_readnone_deopt
+; ATTRIBUTOR-SAME: (ptr nocapture nofree [[P:%.*]]) {
+; ATTRIBUTOR-NEXT:    call void @readnone_param(ptr nocapture nofree [[P]]) [ "deopt"() ]
+; ATTRIBUTOR-NEXT:    ret void
+;
+; ATTRIBUTOR-CGSCC-LABEL: define {{[^@]+}}@op_bundle_readnone_deopt
+; ATTRIBUTOR-CGSCC-SAME: (ptr nocapture nofree [[P:%.*]]) {
+; ATTRIBUTOR-CGSCC-NEXT:    call void @readnone_param(ptr nocapture nofree [[P]]) [ "deopt"() ]
+; ATTRIBUTOR-CGSCC-NEXT:    ret void
 ;
   call void @readnone_param(ptr %p) ["deopt"()]
   ret void
 }
 
 define void @op_bundle_readnone_unknown(ptr %p) {
-; CHECK-LABEL: define {{[^@]+}}@op_bundle_readnone_unknown
-; CHECK-SAME: (ptr nocapture [[P:%.*]]) {
-; CHECK-NEXT:    call void @readnone_param(ptr [[P]]) [ "unknown"() ]
-; CHECK-NEXT:    ret void
+; FNATTRS-LABEL: define {{[^@]+}}@op_bundle_readnone_unknown
+; FNATTRS-SAME: (ptr nocapture [[P:%.*]]) {
+; FNATTRS-NEXT:    call void @readnone_param(ptr [[P]]) [ "unknown"() ]
+; FNATTRS-NEXT:    ret void
+;
+; ATTRIBUTOR-LABEL: define {{[^@]+}}@op_bundle_readnone_unknown
+; ATTRIBUTOR-SAME: (ptr nocapture nofree [[P:%.*]]) {
+; ATTRIBUTOR-NEXT:    call void @readnone_param(ptr nocapture nofree [[P]]) [ "unknown"() ]
+; ATTRIBUTOR-NEXT:    ret void
+;
+; ATTRIBUTOR-CGSCC-LABEL: define {{[^@]+}}@op_bundle_readnone_unknown
+; ATTRIBUTOR-CGSCC-SAME: (ptr nocapture nofree [[P:%.*]]) {
+; ATTRIBUTOR-CGSCC-NEXT:    call void @readnone_param(ptr nocapture nofree [[P]]) [ "unknown"() ]
+; ATTRIBUTOR-CGSCC-NEXT:    ret void
 ;
   call void @readnone_param(ptr %p) ["unknown"()]
   ret void
 }
 
 define void @op_bundle_readonly_deopt(ptr %p) {
-; CHECK-LABEL: define {{[^@]+}}@op_bundle_readonly_deopt
-; CHECK-SAME: (ptr nocapture readonly [[P:%.*]]) {
-; CHECK-NEXT:    call void @readonly_param(ptr [[P]]) [ "deopt"() ]
-; CHECK-NEXT:    ret void
+; FNATTRS-LABEL: define {{[^@]+}}@op_bundle_readonly_deopt
+; FNATTRS-SAME: (ptr nocapture readonly [[P:%.*]]) {
+; FNATTRS-NEXT:    call void @readonly_param(ptr [[P]]) [ "deopt"() ]
+; FNATTRS-NEXT:    ret void
+;
+; ATTRIBUTOR-LABEL: define {{[^@]+}}@op_bundle_readonly_deopt
+; ATTRIBUTOR-SAME: (ptr nocapture nofree [[P:%.*]]) {
+; ATTRIBUTOR-NEXT:    call void @readonly_param(ptr nocapture nofree [[P]]) [ "deopt"() ]
+; ATTRIBUTOR-NEXT:    ret void
+;
+; ATTRIBUTOR-CGSCC-LABEL: define {{[^@]+}}@op_bundle_readonly_deopt
+; ATTRIBUTOR-CGSCC-SAME: (ptr nocapture nofree [[P:%.*]]) {
+; ATTRIBUTOR-CGSCC-NEXT:    call void @readonly_param(ptr nocapture nofree [[P]]) [ "deopt"() ]
+; ATTRIBUTOR-CGSCC-NEXT:    ret void
 ;
   call void @readonly_param(ptr %p) ["deopt"()]
   ret void
 }
 
 define void @op_bundle_readonly_unknown(ptr %p) {
-; CHECK-LABEL: define {{[^@]+}}@op_bundle_readonly_unknown
-; CHECK-SAME: (ptr nocapture [[P:%.*]]) {
-; CHECK-NEXT:    call void @readonly_param(ptr [[P]]) [ "unknown"() ]
-; CHECK-NEXT:    ret void
+; FNATTRS-LABEL: define {{[^@]+}}@op_bundle_readonly_unknown
+; FNATTRS-SAME: (ptr nocapture [[P:%.*]]) {
+; FNATTRS-NEXT:    call void @readonly_param(ptr [[P]]) [ "unknown"() ]
+; FNATTRS-NEXT:    ret void
+;
+; ATTRIBUTOR-LABEL: define {{[^@]+}}@op_bundle_readonly_unknown
+; ATTRIBUTOR-SAME: (ptr nocapture nofree [[P:%.*]]) {
+; ATTRIBUTOR-NEXT:    call void @readonly_param(ptr nocapture nofree [[P]]) [ "unknown"() ]
+; ATTRIBUTOR-NEXT:    ret void
+;
+; ATTRIBUTOR-CGSCC-LABEL: define {{[^@]+}}@op_bundle_readonly_unknown
+; ATTRIBUTOR-CGSCC-SAME: (ptr nocapture nofree [[P:%.*]]) {
+; ATTRIBUTOR-CGSCC-NEXT:    call void @readonly_param(ptr nocapture nofree [[P]]) [ "unknown"() ]
+; ATTRIBUTOR-CGSCC-NEXT:    ret void
 ;
   call void @readonly_param(ptr %p) ["unknown"()]
   ret void
 }
+;; NOTE: These prefixes are unused and the list is autogenerated. Do not add tests below this line:
+; COMMON: {{.*}}

diff  --git a/llvm/test/Transforms/FunctionAttrs/readnone.ll b/llvm/test/Transforms/FunctionAttrs/readnone.ll
index 6e09ae6da76dcf..40146633cc62e9 100644
--- a/llvm/test/Transforms/FunctionAttrs/readnone.ll
+++ b/llvm/test/Transforms/FunctionAttrs/readnone.ll
@@ -1,13 +1,35 @@
-; RUN: opt < %s -passes=function-attrs -S | FileCheck %s
+; NOTE: Assertions have been autogenerated by utils/update_test_checks.py UTC_ARGS: --version 3
+; RUN: opt -passes=function-attrs -S < %s | FileCheck --check-prefixes=COMMON,FNATTRS %s
+; RUN: opt -passes=attributor-light -S < %s | FileCheck --check-prefixes=COMMON,ATTRIBUTOR %s
 
-; CHECK: define void @bar(ptr nocapture readnone %0)
 define void @bar(ptr readonly %0) {
+; FNATTRS-LABEL: define void @bar
+; FNATTRS-SAME: (ptr nocapture readnone [[TMP0:%.*]]) #[[ATTR0:[0-9]+]] {
+; FNATTRS-NEXT:    call void @foo(ptr [[TMP0]])
+; FNATTRS-NEXT:    ret void
+;
+; ATTRIBUTOR-LABEL: define void @bar
+; ATTRIBUTOR-SAME: (ptr nocapture nofree readnone [[TMP0:%.*]]) #[[ATTR0:[0-9]+]] {
+; ATTRIBUTOR-NEXT:    call void @foo(ptr nocapture nofree readnone [[TMP0]]) #[[ATTR0]]
+; ATTRIBUTOR-NEXT:    ret void
+;
   call void @foo(ptr %0)
-    ret void
+  ret void
 }
 
-; CHECK: define void @foo(ptr nocapture readnone %0)
 define void @foo(ptr readonly %0) {
+; FNATTRS-LABEL: define void @foo
+; FNATTRS-SAME: (ptr nocapture readnone [[TMP0:%.*]]) #[[ATTR0]] {
+; FNATTRS-NEXT:    call void @bar(ptr [[TMP0]])
+; FNATTRS-NEXT:    ret void
+;
+; ATTRIBUTOR-LABEL: define void @foo
+; ATTRIBUTOR-SAME: (ptr nocapture nofree readnone [[TMP0:%.*]]) #[[ATTR0]] {
+; ATTRIBUTOR-NEXT:    call void @bar(ptr nocapture nofree readnone [[TMP0]]) #[[ATTR0]]
+; ATTRIBUTOR-NEXT:    ret void
+;
   call void @bar(ptr %0)
   ret void
 }
+;; NOTE: These prefixes are unused and the list is autogenerated. Do not add tests below this line:
+; COMMON: {{.*}}

diff  --git a/llvm/test/Transforms/FunctionAttrs/willreturn.ll b/llvm/test/Transforms/FunctionAttrs/willreturn.ll
index defb061c73e6a1..bf3f4adf7eaa17 100644
--- a/llvm/test/Transforms/FunctionAttrs/willreturn.ll
+++ b/llvm/test/Transforms/FunctionAttrs/willreturn.ll
@@ -1,13 +1,21 @@
 ; NOTE: Assertions have been autogenerated by utils/update_test_checks.py UTC_ARGS: --check-attributes
-; RUN: opt -passes=function-attrs -S %s | FileCheck %s
+; RUN: opt -passes=function-attrs -S %s | FileCheck --check-prefixes=COMMON,FNATTRS %s
+; RUN: opt -passes=attributor-light -S %s | FileCheck --check-prefixes=COMMON,ATTRIBUTOR %s
 
 define void @mustprogress_readnone() mustprogress {
-; CHECK: Function Attrs: mustprogress nofree norecurse noreturn nosync nounwind willreturn memory(none)
-; CHECK-LABEL: @mustprogress_readnone(
-; CHECK-NEXT:  entry:
-; CHECK-NEXT:    br label [[WHILE_BODY:%.*]]
-; CHECK:       while.body:
-; CHECK-NEXT:    br label [[WHILE_BODY]]
+; FNATTRS: Function Attrs: mustprogress nofree norecurse noreturn nosync nounwind willreturn memory(none)
+; FNATTRS-LABEL: @mustprogress_readnone(
+; FNATTRS-NEXT:  entry:
+; FNATTRS-NEXT:    br label [[WHILE_BODY:%.*]]
+; FNATTRS:       while.body:
+; FNATTRS-NEXT:    br label [[WHILE_BODY]]
+;
+; ATTRIBUTOR: Function Attrs: mustprogress nofree norecurse noreturn nosync nounwind memory(none)
+; ATTRIBUTOR-LABEL: @mustprogress_readnone(
+; ATTRIBUTOR-NEXT:  entry:
+; ATTRIBUTOR-NEXT:    br label [[WHILE_BODY:%.*]]
+; ATTRIBUTOR:       while.body:
+; ATTRIBUTOR-NEXT:    br label [[WHILE_BODY]]
 ;
 entry:
   br label %while.body
@@ -17,13 +25,21 @@ while.body:
 }
 
 define i32 @mustprogress_load(ptr %ptr) mustprogress {
-; CHECK: Function Attrs: mustprogress nofree norecurse noreturn nosync nounwind willreturn memory(argmem: read)
-; CHECK-LABEL: @mustprogress_load(
-; CHECK-NEXT:  entry:
-; CHECK-NEXT:    br label [[WHILE_BODY:%.*]]
-; CHECK:       while.body:
-; CHECK-NEXT:    [[R:%.*]] = load i32, ptr [[PTR:%.*]], align 4
-; CHECK-NEXT:    br label [[WHILE_BODY]]
+; FNATTRS: Function Attrs: mustprogress nofree norecurse noreturn nosync nounwind willreturn memory(argmem: read)
+; FNATTRS-LABEL: @mustprogress_load(
+; FNATTRS-NEXT:  entry:
+; FNATTRS-NEXT:    br label [[WHILE_BODY:%.*]]
+; FNATTRS:       while.body:
+; FNATTRS-NEXT:    [[R:%.*]] = load i32, ptr [[PTR:%.*]], align 4
+; FNATTRS-NEXT:    br label [[WHILE_BODY]]
+;
+; ATTRIBUTOR: Function Attrs: mustprogress nofree norecurse noreturn nosync nounwind memory(argmem: read)
+; ATTRIBUTOR-LABEL: @mustprogress_load(
+; ATTRIBUTOR-NEXT:  entry:
+; ATTRIBUTOR-NEXT:    br label [[WHILE_BODY:%.*]]
+; ATTRIBUTOR:       while.body:
+; ATTRIBUTOR-NEXT:    [[R:%.*]] = load i32, ptr [[PTR:%.*]], align 4
+; ATTRIBUTOR-NEXT:    br label [[WHILE_BODY]]
 ;
 entry:
   br label %while.body
@@ -34,13 +50,13 @@ while.body:
 }
 
 define void @mustprogress_store(ptr %ptr) mustprogress {
-; CHECK: Function Attrs: mustprogress nofree norecurse noreturn nosync nounwind memory(argmem: write)
-; CHECK-LABEL: @mustprogress_store(
-; CHECK-NEXT:  entry:
-; CHECK-NEXT:    br label [[WHILE_BODY:%.*]]
-; CHECK:       while.body:
-; CHECK-NEXT:    store i32 0, ptr [[PTR:%.*]], align 4
-; CHECK-NEXT:    br label [[WHILE_BODY]]
+; COMMON: Function Attrs: mustprogress nofree norecurse noreturn nosync nounwind memory(argmem: write)
+; COMMON-LABEL: @mustprogress_store(
+; COMMON-NEXT:  entry:
+; COMMON-NEXT:    br label [[WHILE_BODY:%.*]]
+; COMMON:       while.body:
+; COMMON-NEXT:    store i32 0, ptr [[PTR:%.*]], align 4
+; COMMON-NEXT:    br label [[WHILE_BODY]]
 ;
 entry:
   br label %while.body
@@ -53,21 +69,27 @@ while.body:
 declare void @unknown_fn()
 
 define void @mustprogress_call_unknown_fn() mustprogress {
-; CHECK: Function Attrs: mustprogress
-; CHECK-LABEL: @mustprogress_call_unknown_fn(
-; CHECK-NEXT:    call void @unknown_fn()
-; CHECK-NEXT:    ret void
+; COMMON: Function Attrs: mustprogress
+; COMMON-LABEL: @mustprogress_call_unknown_fn(
+; COMMON-NEXT:    call void @unknown_fn()
+; COMMON-NEXT:    ret void
 ;
   call void @unknown_fn()
   ret void
 }
 
 define i32 @mustprogress_call_known_functions(ptr %ptr) mustprogress {
-; CHECK: Function Attrs: mustprogress nofree norecurse noreturn nosync nounwind willreturn memory(argmem: read)
-; CHECK-LABEL: @mustprogress_call_known_functions(
-; CHECK-NEXT:    call void @mustprogress_readnone()
-; CHECK-NEXT:    [[R:%.*]] = call i32 @mustprogress_load(ptr [[PTR:%.*]])
-; CHECK-NEXT:    ret i32 [[R]]
+; FNATTRS: Function Attrs: mustprogress nofree norecurse noreturn nosync nounwind willreturn memory(argmem: read)
+; FNATTRS-LABEL: @mustprogress_call_known_functions(
+; FNATTRS-NEXT:    call void @mustprogress_readnone()
+; FNATTRS-NEXT:    [[R:%.*]] = call i32 @mustprogress_load(ptr [[PTR:%.*]])
+; FNATTRS-NEXT:    ret i32 [[R]]
+;
+; ATTRIBUTOR: Function Attrs: mustprogress nofree norecurse nosync nounwind willreturn memory(argmem: read)
+; ATTRIBUTOR-LABEL: @mustprogress_call_known_functions(
+; ATTRIBUTOR-NEXT:    call void @mustprogress_readnone() #[[ATTR9:[0-9]+]]
+; ATTRIBUTOR-NEXT:    [[R:%.*]] = call i32 @mustprogress_load(ptr nocapture nofree readonly [[PTR:%.*]]) #[[ATTR12:[0-9]+]]
+; ATTRIBUTOR-NEXT:    ret i32 [[R]]
 ;
   call void @mustprogress_readnone()
   %r = call i32 @mustprogress_load(ptr %ptr)
@@ -77,16 +99,27 @@ define i32 @mustprogress_call_known_functions(ptr %ptr) mustprogress {
 declare i32 @__gxx_personality_v0(...)
 
 define i64 @mustprogress_mayunwind() mustprogress personality ptr @__gxx_personality_v0 {
-; CHECK: Function Attrs: mustprogress nofree nosync nounwind willreturn memory(none)
-; CHECK-LABEL: @mustprogress_mayunwind(
-; CHECK-NEXT:    [[A:%.*]] = invoke i64 @fn_noread()
-; CHECK-NEXT:    to label [[A:%.*]] unwind label [[B:%.*]]
-; CHECK:       A:
-; CHECK-NEXT:    ret i64 10
-; CHECK:       B:
-; CHECK-NEXT:    [[VAL:%.*]] = landingpad { ptr, i32 }
-; CHECK-NEXT:    catch ptr null
-; CHECK-NEXT:    ret i64 0
+; FNATTRS: Function Attrs: mustprogress nofree nosync nounwind willreturn memory(none)
+; FNATTRS-LABEL: @mustprogress_mayunwind(
+; FNATTRS-NEXT:    [[A:%.*]] = invoke i64 @fn_noread()
+; FNATTRS-NEXT:    to label [[A:%.*]] unwind label [[B:%.*]]
+; FNATTRS:       A:
+; FNATTRS-NEXT:    ret i64 10
+; FNATTRS:       B:
+; FNATTRS-NEXT:    [[VAL:%.*]] = landingpad { ptr, i32 }
+; FNATTRS-NEXT:    catch ptr null
+; FNATTRS-NEXT:    ret i64 0
+;
+; ATTRIBUTOR: Function Attrs: mustprogress nosync nounwind willreturn memory(none)
+; ATTRIBUTOR-LABEL: @mustprogress_mayunwind(
+; ATTRIBUTOR-NEXT:    [[A:%.*]] = invoke i64 @fn_noread()
+; ATTRIBUTOR-NEXT:    to label [[A:%.*]] unwind label [[B:%.*]]
+; ATTRIBUTOR:       A:
+; ATTRIBUTOR-NEXT:    ret i64 10
+; ATTRIBUTOR:       B:
+; ATTRIBUTOR-NEXT:    [[VAL:%.*]] = landingpad { ptr, i32 }
+; ATTRIBUTOR-NEXT:    catch ptr null
+; ATTRIBUTOR-NEXT:    ret i64 0
 ;
   %a = invoke i64 @fn_noread()
   to label %A unwind label %B
@@ -101,18 +134,31 @@ B:
 
 ; Function without loops or non-willreturn calls will return.
 define void @willreturn_no_loop(i1 %c, ptr %p) {
-; CHECK: Function Attrs: mustprogress willreturn
-; CHECK-LABEL: @willreturn_no_loop(
-; CHECK-NEXT:    br i1 [[C:%.*]], label [[IF:%.*]], label [[ELSE:%.*]]
-; CHECK:       if:
-; CHECK-NEXT:    [[TMP1:%.*]] = load atomic i32, ptr [[P:%.*]] seq_cst, align 4
-; CHECK-NEXT:    call void @fn_willreturn()
-; CHECK-NEXT:    br label [[END:%.*]]
-; CHECK:       else:
-; CHECK-NEXT:    store atomic i32 0, ptr [[P]] seq_cst, align 4
-; CHECK-NEXT:    br label [[END]]
-; CHECK:       end:
-; CHECK-NEXT:    ret void
+; FNATTRS: Function Attrs: mustprogress willreturn
+; FNATTRS-LABEL: @willreturn_no_loop(
+; FNATTRS-NEXT:    br i1 [[C:%.*]], label [[IF:%.*]], label [[ELSE:%.*]]
+; FNATTRS:       if:
+; FNATTRS-NEXT:    [[TMP1:%.*]] = load atomic i32, ptr [[P:%.*]] seq_cst, align 4
+; FNATTRS-NEXT:    call void @fn_willreturn()
+; FNATTRS-NEXT:    br label [[END:%.*]]
+; FNATTRS:       else:
+; FNATTRS-NEXT:    store atomic i32 0, ptr [[P]] seq_cst, align 4
+; FNATTRS-NEXT:    br label [[END]]
+; FNATTRS:       end:
+; FNATTRS-NEXT:    ret void
+;
+; ATTRIBUTOR: Function Attrs: mustprogress willreturn
+; ATTRIBUTOR-LABEL: @willreturn_no_loop(
+; ATTRIBUTOR-NEXT:    br i1 [[C:%.*]], label [[IF:%.*]], label [[ELSE:%.*]]
+; ATTRIBUTOR:       if:
+; ATTRIBUTOR-NEXT:    [[TMP1:%.*]] = load atomic i32, ptr [[P:%.*]] seq_cst, align 4
+; ATTRIBUTOR-NEXT:    call void @fn_willreturn() #[[ATTR11:[0-9]+]]
+; ATTRIBUTOR-NEXT:    br label [[END:%.*]]
+; ATTRIBUTOR:       else:
+; ATTRIBUTOR-NEXT:    store atomic i32 0, ptr [[P]] seq_cst, align 4
+; ATTRIBUTOR-NEXT:    br label [[END]]
+; ATTRIBUTOR:       end:
+; ATTRIBUTOR-NEXT:    ret void
 ;
   br i1 %c, label %if, label %else
 
@@ -131,9 +177,9 @@ end:
 
 ; Calls a function that is not guaranteed to return, not willreturn.
 define void @willreturn_non_returning_function(i1 %c, ptr %p) {
-; CHECK-LABEL: @willreturn_non_returning_function(
-; CHECK-NEXT:    call void @unknown_fn()
-; CHECK-NEXT:    ret void
+; COMMON-LABEL: @willreturn_non_returning_function(
+; COMMON-NEXT:    call void @unknown_fn()
+; COMMON-NEXT:    ret void
 ;
   call void @unknown_fn()
   ret void
@@ -141,11 +187,11 @@ define void @willreturn_non_returning_function(i1 %c, ptr %p) {
 
 ; Infinite loop without mustprogress, will not return.
 define void @willreturn_loop() {
-; CHECK: Function Attrs: nofree norecurse noreturn nosync nounwind memory(none)
-; CHECK-LABEL: @willreturn_loop(
-; CHECK-NEXT:    br label [[LOOP:%.*]]
-; CHECK:       loop:
-; CHECK-NEXT:    br label [[LOOP]]
+; COMMON: Function Attrs: nofree norecurse noreturn nosync nounwind memory(none)
+; COMMON-LABEL: @willreturn_loop(
+; COMMON-NEXT:    br label [[LOOP:%.*]]
+; COMMON:       loop:
+; COMMON-NEXT:    br label [[LOOP]]
 ;
   br label %loop
 
@@ -156,17 +202,17 @@ loop:
 ; Finite loop. Could be willreturn but not detected.
 ; FIXME
 define void @willreturn_finite_loop() {
-; CHECK: Function Attrs: nofree norecurse nosync nounwind memory(none)
-; CHECK-LABEL: @willreturn_finite_loop(
-; CHECK-NEXT:  entry:
-; CHECK-NEXT:    br label [[LOOP:%.*]]
-; CHECK:       loop:
-; CHECK-NEXT:    [[I:%.*]] = phi i32 [ 0, [[ENTRY:%.*]] ], [ [[I_INC:%.*]], [[LOOP]] ]
-; CHECK-NEXT:    [[I_INC]] = add nuw i32 [[I]], 1
-; CHECK-NEXT:    [[C:%.*]] = icmp ne i32 [[I_INC]], 100
-; CHECK-NEXT:    br i1 [[C]], label [[LOOP]], label [[END:%.*]]
-; CHECK:       end:
-; CHECK-NEXT:    ret void
+; COMMON: Function Attrs: nofree norecurse nosync nounwind memory(none)
+; COMMON-LABEL: @willreturn_finite_loop(
+; COMMON-NEXT:  entry:
+; COMMON-NEXT:    br label [[LOOP:%.*]]
+; COMMON:       loop:
+; COMMON-NEXT:    [[I:%.*]] = phi i32 [ 0, [[ENTRY:%.*]] ], [ [[I_INC:%.*]], [[LOOP]] ]
+; COMMON-NEXT:    [[I_INC]] = add nuw i32 [[I]], 1
+; COMMON-NEXT:    [[C:%.*]] = icmp ne i32 [[I_INC]], 100
+; COMMON-NEXT:    br i1 [[C]], label [[LOOP]], label [[END:%.*]]
+; COMMON:       end:
+; COMMON-NEXT:    ret void
 ;
 entry:
   br label %loop
@@ -183,10 +229,15 @@ end:
 
 ; Infinite recursion without mustprogress, will not return.
 define void @willreturn_recursion() {
-; CHECK: Function Attrs: nofree nosync nounwind memory(none)
-; CHECK-LABEL: @willreturn_recursion(
-; CHECK-NEXT:    tail call void @willreturn_recursion()
-; CHECK-NEXT:    ret void
+; FNATTRS: Function Attrs: nofree nosync nounwind memory(none)
+; FNATTRS-LABEL: @willreturn_recursion(
+; FNATTRS-NEXT:    tail call void @willreturn_recursion()
+; FNATTRS-NEXT:    ret void
+;
+; ATTRIBUTOR: Function Attrs: nofree nosync nounwind memory(none)
+; ATTRIBUTOR-LABEL: @willreturn_recursion(
+; ATTRIBUTOR-NEXT:    tail call void @willreturn_recursion() #[[ATTR9]]
+; ATTRIBUTOR-NEXT:    ret void
 ;
   tail call void @willreturn_recursion()
   ret void
@@ -194,13 +245,13 @@ define void @willreturn_recursion() {
 
 ; Irreducible infinite loop, will not return.
 define void @willreturn_irreducible(i1 %c) {
-; CHECK: Function Attrs: nofree norecurse noreturn nosync nounwind memory(none)
-; CHECK-LABEL: @willreturn_irreducible(
-; CHECK-NEXT:    br i1 [[C:%.*]], label [[BB1:%.*]], label [[BB2:%.*]]
-; CHECK:       bb1:
-; CHECK-NEXT:    br label [[BB2]]
-; CHECK:       bb2:
-; CHECK-NEXT:    br label [[BB1]]
+; COMMON: Function Attrs: nofree norecurse noreturn nosync nounwind memory(none)
+; COMMON-LABEL: @willreturn_irreducible(
+; COMMON-NEXT:    br i1 [[C:%.*]], label [[BB1:%.*]], label [[BB2:%.*]]
+; COMMON:       bb1:
+; COMMON-NEXT:    br label [[BB2]]
+; COMMON:       bb2:
+; COMMON-NEXT:    br label [[BB1]]
 ;
   br i1 %c, label %bb1, label %bb2
 
@@ -212,9 +263,9 @@ bb2:
 }
 
 define linkonce i32 @square(i32) {
-; CHECK-LABEL: @square(
-; CHECK-NEXT:    [[TMP2:%.*]] = mul nsw i32 [[TMP0:%.*]], [[TMP0]]
-; CHECK-NEXT:    ret i32 [[TMP2]]
+; COMMON-LABEL: @square(
+; COMMON-NEXT:    [[TMP2:%.*]] = mul nsw i32 [[TMP0:%.*]], [[TMP0]]
+; COMMON-NEXT:    ret i32 [[TMP2]]
 ;
   %2 = mul nsw i32 %0, %0
   ret i32 %2

diff  --git a/llvm/test/Transforms/FunctionAttrs/writeonly.ll b/llvm/test/Transforms/FunctionAttrs/writeonly.ll
index 0c8ec05223b36a..633068129eac69 100644
--- a/llvm/test/Transforms/FunctionAttrs/writeonly.ll
+++ b/llvm/test/Transforms/FunctionAttrs/writeonly.ll
@@ -1,24 +1,38 @@
 ; NOTE: Assertions have been autogenerated by utils/update_test_checks.py UTC_ARGS: --function-signature --check-attributes
-; RUN: opt < %s -passes=function-attrs -S | FileCheck %s
+; RUN: opt -passes=function-attrs -S < %s | FileCheck --check-prefixes=COMMON,FNATTRS %s
+; RUN: opt -passes=attributor-light -S < %s | FileCheck --check-prefixes=COMMON,ATTRIBUTOR %s
 
 define void @nouses-argworn-funrn(ptr writeonly %.aaa) {
-; CHECK: Function Attrs: mustprogress nofree norecurse nosync nounwind willreturn memory(none)
-; CHECK-LABEL: define {{[^@]+}}@nouses-argworn-funrn
-; CHECK-SAME: (ptr nocapture readnone [[DOTAAA:%.*]]) #[[ATTR0:[0-9]+]] {
-; CHECK-NEXT:  nouses-argworn-funrn_entry:
-; CHECK-NEXT:    ret void
+; FNATTRS: Function Attrs: mustprogress nofree norecurse nosync nounwind willreturn memory(none)
+; FNATTRS-LABEL: define {{[^@]+}}@nouses-argworn-funrn
+; FNATTRS-SAME: (ptr nocapture readnone [[DOTAAA:%.*]]) #[[ATTR0:[0-9]+]] {
+; FNATTRS-NEXT:  nouses-argworn-funrn_entry:
+; FNATTRS-NEXT:    ret void
+;
+; ATTRIBUTOR: Function Attrs: mustprogress nofree norecurse nosync nounwind willreturn memory(none)
+; ATTRIBUTOR-LABEL: define {{[^@]+}}@nouses-argworn-funrn
+; ATTRIBUTOR-SAME: (ptr nocapture nofree readnone [[DOTAAA:%.*]]) #[[ATTR0:[0-9]+]] {
+; ATTRIBUTOR-NEXT:  nouses-argworn-funrn_entry:
+; ATTRIBUTOR-NEXT:    ret void
 ;
 nouses-argworn-funrn_entry:
   ret void
 }
 
 define void @nouses-argworn-funro(ptr writeonly %.aaa, ptr %.bbb) {
-; CHECK: Function Attrs: mustprogress nofree norecurse nosync nounwind willreturn memory(argmem: read)
-; CHECK-LABEL: define {{[^@]+}}@nouses-argworn-funro
-; CHECK-SAME: (ptr nocapture readnone [[DOTAAA:%.*]], ptr nocapture readonly [[DOTBBB:%.*]]) #[[ATTR1:[0-9]+]] {
-; CHECK-NEXT:  nouses-argworn-funro_entry:
-; CHECK-NEXT:    [[VAL:%.*]] = load i32, ptr [[DOTBBB]], align 4
-; CHECK-NEXT:    ret void
+; FNATTRS: Function Attrs: mustprogress nofree norecurse nosync nounwind willreturn memory(argmem: read)
+; FNATTRS-LABEL: define {{[^@]+}}@nouses-argworn-funro
+; FNATTRS-SAME: (ptr nocapture readnone [[DOTAAA:%.*]], ptr nocapture readonly [[DOTBBB:%.*]]) #[[ATTR1:[0-9]+]] {
+; FNATTRS-NEXT:  nouses-argworn-funro_entry:
+; FNATTRS-NEXT:    [[VAL:%.*]] = load i32, ptr [[DOTBBB]], align 4
+; FNATTRS-NEXT:    ret void
+;
+; ATTRIBUTOR: Function Attrs: mustprogress nofree norecurse nosync nounwind willreturn memory(argmem: read)
+; ATTRIBUTOR-LABEL: define {{[^@]+}}@nouses-argworn-funro
+; ATTRIBUTOR-SAME: (ptr nocapture nofree readnone [[DOTAAA:%.*]], ptr nocapture nofree nonnull readonly [[DOTBBB:%.*]]) #[[ATTR1:[0-9]+]] {
+; ATTRIBUTOR-NEXT:  nouses-argworn-funro_entry:
+; ATTRIBUTOR-NEXT:    [[VAL:%.*]] = load i32, ptr [[DOTBBB]], align 4
+; ATTRIBUTOR-NEXT:    ret void
 ;
 nouses-argworn-funro_entry:
   %val = load i32 , ptr %.bbb
@@ -30,12 +44,19 @@ nouses-argworn-funro_entry:
 @d-ccc = internal global %_type_of_d-ccc <{ ptr null, i8 1, i8 13, i8 0, i8 -127 }>, align 8
 
 define void @nouses-argworn-funwo(ptr writeonly %.aaa) {
-; CHECK: Function Attrs: mustprogress nofree norecurse nosync nounwind willreturn memory(write, argmem: none, inaccessiblemem: none)
-; CHECK-LABEL: define {{[^@]+}}@nouses-argworn-funwo
-; CHECK-SAME: (ptr nocapture readnone [[DOTAAA:%.*]]) #[[ATTR2:[0-9]+]] {
-; CHECK-NEXT:  nouses-argworn-funwo_entry:
-; CHECK-NEXT:    store i8 0, ptr getelementptr inbounds ([[_TYPE_OF_D_CCC:%.*]], ptr @d-ccc, i32 0, i32 3), align 1
-; CHECK-NEXT:    ret void
+; FNATTRS: Function Attrs: mustprogress nofree norecurse nosync nounwind willreturn memory(write, argmem: none, inaccessiblemem: none)
+; FNATTRS-LABEL: define {{[^@]+}}@nouses-argworn-funwo
+; FNATTRS-SAME: (ptr nocapture readnone [[DOTAAA:%.*]]) #[[ATTR2:[0-9]+]] {
+; FNATTRS-NEXT:  nouses-argworn-funwo_entry:
+; FNATTRS-NEXT:    store i8 0, ptr getelementptr inbounds ([[_TYPE_OF_D_CCC:%.*]], ptr @d-ccc, i32 0, i32 3), align 1
+; FNATTRS-NEXT:    ret void
+;
+; ATTRIBUTOR: Function Attrs: mustprogress nofree norecurse nosync nounwind willreturn memory(write)
+; ATTRIBUTOR-LABEL: define {{[^@]+}}@nouses-argworn-funwo
+; ATTRIBUTOR-SAME: (ptr nocapture nofree readnone [[DOTAAA:%.*]]) #[[ATTR2:[0-9]+]] {
+; ATTRIBUTOR-NEXT:  nouses-argworn-funwo_entry:
+; ATTRIBUTOR-NEXT:    store i8 0, ptr getelementptr inbounds ([[_TYPE_OF_D_CCC:%.*]], ptr @d-ccc, i32 0, i32 3), align 1
+; ATTRIBUTOR-NEXT:    ret void
 ;
 nouses-argworn-funwo_entry:
   store i8 0, ptr getelementptr inbounds (%_type_of_d-ccc, ptr @d-ccc, i32 0, i32 3)
@@ -43,11 +64,17 @@ nouses-argworn-funwo_entry:
 }
 
 define void @test_store(ptr %p) {
-; CHECK: Function Attrs: mustprogress nofree norecurse nosync nounwind willreturn memory(argmem: write)
-; CHECK-LABEL: define {{[^@]+}}@test_store
-; CHECK-SAME: (ptr nocapture writeonly [[P:%.*]]) #[[ATTR3:[0-9]+]] {
-; CHECK-NEXT:    store i8 0, ptr [[P]], align 1
-; CHECK-NEXT:    ret void
+; FNATTRS: Function Attrs: mustprogress nofree norecurse nosync nounwind willreturn memory(argmem: write)
+; FNATTRS-LABEL: define {{[^@]+}}@test_store
+; FNATTRS-SAME: (ptr nocapture writeonly [[P:%.*]]) #[[ATTR3:[0-9]+]] {
+; FNATTRS-NEXT:    store i8 0, ptr [[P]], align 1
+; FNATTRS-NEXT:    ret void
+;
+; ATTRIBUTOR: Function Attrs: mustprogress nofree norecurse nosync nounwind willreturn memory(argmem: write)
+; ATTRIBUTOR-LABEL: define {{[^@]+}}@test_store
+; ATTRIBUTOR-SAME: (ptr nocapture nofree nonnull writeonly [[P:%.*]]) #[[ATTR3:[0-9]+]] {
+; ATTRIBUTOR-NEXT:    store i8 0, ptr [[P]], align 1
+; ATTRIBUTOR-NEXT:    ret void
 ;
   store i8 0, ptr %p
   ret void
@@ -55,13 +82,21 @@ define void @test_store(ptr %p) {
 
 @G = external global ptr
 define i8 @test_store_capture(ptr %p) {
-; CHECK: Function Attrs: mustprogress nofree norecurse nosync nounwind willreturn memory(readwrite, argmem: read, inaccessiblemem: none)
-; CHECK-LABEL: define {{[^@]+}}@test_store_capture
-; CHECK-SAME: (ptr [[P:%.*]]) #[[ATTR4:[0-9]+]] {
-; CHECK-NEXT:    store ptr [[P]], ptr @G, align 8
-; CHECK-NEXT:    [[P2:%.*]] = load ptr, ptr @G, align 8
-; CHECK-NEXT:    [[V:%.*]] = load i8, ptr [[P2]], align 1
-; CHECK-NEXT:    ret i8 [[V]]
+; FNATTRS: Function Attrs: mustprogress nofree norecurse nosync nounwind willreturn memory(readwrite, argmem: read, inaccessiblemem: none)
+; FNATTRS-LABEL: define {{[^@]+}}@test_store_capture
+; FNATTRS-SAME: (ptr [[P:%.*]]) #[[ATTR4:[0-9]+]] {
+; FNATTRS-NEXT:    store ptr [[P]], ptr @G, align 8
+; FNATTRS-NEXT:    [[P2:%.*]] = load ptr, ptr @G, align 8
+; FNATTRS-NEXT:    [[V:%.*]] = load i8, ptr [[P2]], align 1
+; FNATTRS-NEXT:    ret i8 [[V]]
+;
+; ATTRIBUTOR: Function Attrs: mustprogress nofree norecurse nosync nounwind willreturn
+; ATTRIBUTOR-LABEL: define {{[^@]+}}@test_store_capture
+; ATTRIBUTOR-SAME: (ptr nofree [[P:%.*]]) #[[ATTR4:[0-9]+]] {
+; ATTRIBUTOR-NEXT:    store ptr [[P]], ptr @G, align 8
+; ATTRIBUTOR-NEXT:    [[P2:%.*]] = load ptr, ptr @G, align 8
+; ATTRIBUTOR-NEXT:    [[V:%.*]] = load i8, ptr [[P2]], align 1
+; ATTRIBUTOR-NEXT:    ret i8 [[V]]
 ;
   store ptr %p, ptr @G
   %p2 = load ptr, ptr @G
@@ -70,12 +105,19 @@ define i8 @test_store_capture(ptr %p) {
 }
 
 define void @test_addressing(ptr %p) {
-; CHECK: Function Attrs: mustprogress nofree norecurse nosync nounwind willreturn memory(argmem: write)
-; CHECK-LABEL: define {{[^@]+}}@test_addressing
-; CHECK-SAME: (ptr nocapture writeonly [[P:%.*]]) #[[ATTR3]] {
-; CHECK-NEXT:    [[GEP:%.*]] = getelementptr i8, ptr [[P]], i64 8
-; CHECK-NEXT:    store i32 0, ptr [[GEP]], align 4
-; CHECK-NEXT:    ret void
+; FNATTRS: Function Attrs: mustprogress nofree norecurse nosync nounwind willreturn memory(argmem: write)
+; FNATTRS-LABEL: define {{[^@]+}}@test_addressing
+; FNATTRS-SAME: (ptr nocapture writeonly [[P:%.*]]) #[[ATTR3]] {
+; FNATTRS-NEXT:    [[GEP:%.*]] = getelementptr i8, ptr [[P]], i64 8
+; FNATTRS-NEXT:    store i32 0, ptr [[GEP]], align 4
+; FNATTRS-NEXT:    ret void
+;
+; ATTRIBUTOR: Function Attrs: mustprogress nofree norecurse nosync nounwind willreturn memory(argmem: write)
+; ATTRIBUTOR-LABEL: define {{[^@]+}}@test_addressing
+; ATTRIBUTOR-SAME: (ptr nocapture nofree writeonly [[P:%.*]]) #[[ATTR3]] {
+; ATTRIBUTOR-NEXT:    [[GEP:%.*]] = getelementptr i8, ptr [[P]], i64 8
+; ATTRIBUTOR-NEXT:    store i32 0, ptr [[GEP]], align 4
+; ATTRIBUTOR-NEXT:    ret void
 ;
   %gep = getelementptr i8, ptr %p, i64 8
   store i32 0, ptr %gep
@@ -83,12 +125,19 @@ define void @test_addressing(ptr %p) {
 }
 
 define void @test_readwrite(ptr %p) {
-; CHECK: Function Attrs: mustprogress nofree norecurse nosync nounwind willreturn memory(argmem: readwrite)
-; CHECK-LABEL: define {{[^@]+}}@test_readwrite
-; CHECK-SAME: (ptr nocapture [[P:%.*]]) #[[ATTR5:[0-9]+]] {
-; CHECK-NEXT:    [[V:%.*]] = load i8, ptr [[P]], align 1
-; CHECK-NEXT:    store i8 [[V]], ptr [[P]], align 1
-; CHECK-NEXT:    ret void
+; FNATTRS: Function Attrs: mustprogress nofree norecurse nosync nounwind willreturn memory(argmem: readwrite)
+; FNATTRS-LABEL: define {{[^@]+}}@test_readwrite
+; FNATTRS-SAME: (ptr nocapture [[P:%.*]]) #[[ATTR5:[0-9]+]] {
+; FNATTRS-NEXT:    [[V:%.*]] = load i8, ptr [[P]], align 1
+; FNATTRS-NEXT:    store i8 [[V]], ptr [[P]], align 1
+; FNATTRS-NEXT:    ret void
+;
+; ATTRIBUTOR: Function Attrs: mustprogress nofree norecurse nosync nounwind willreturn memory(argmem: readwrite)
+; ATTRIBUTOR-LABEL: define {{[^@]+}}@test_readwrite
+; ATTRIBUTOR-SAME: (ptr nocapture nofree nonnull [[P:%.*]]) #[[ATTR5:[0-9]+]] {
+; ATTRIBUTOR-NEXT:    [[V:%.*]] = load i8, ptr [[P]], align 1
+; ATTRIBUTOR-NEXT:    store i8 [[V]], ptr [[P]], align 1
+; ATTRIBUTOR-NEXT:    ret void
 ;
   %v = load i8, ptr %p
   store i8 %v, ptr %p
@@ -96,22 +145,34 @@ define void @test_readwrite(ptr %p) {
 }
 
 define void @test_volatile(ptr %p) {
-; CHECK: Function Attrs: nofree norecurse nounwind memory(argmem: readwrite, inaccessiblemem: readwrite)
-; CHECK-LABEL: define {{[^@]+}}@test_volatile
-; CHECK-SAME: (ptr [[P:%.*]]) #[[ATTR6:[0-9]+]] {
-; CHECK-NEXT:    store volatile i8 0, ptr [[P]], align 1
-; CHECK-NEXT:    ret void
+; FNATTRS: Function Attrs: nofree norecurse nounwind memory(argmem: readwrite, inaccessiblemem: readwrite)
+; FNATTRS-LABEL: define {{[^@]+}}@test_volatile
+; FNATTRS-SAME: (ptr [[P:%.*]]) #[[ATTR6:[0-9]+]] {
+; FNATTRS-NEXT:    store volatile i8 0, ptr [[P]], align 1
+; FNATTRS-NEXT:    ret void
+;
+; ATTRIBUTOR: Function Attrs: mustprogress nofree norecurse nounwind willreturn memory(argmem: readwrite)
+; ATTRIBUTOR-LABEL: define {{[^@]+}}@test_volatile
+; ATTRIBUTOR-SAME: (ptr nofree [[P:%.*]]) #[[ATTR6:[0-9]+]] {
+; ATTRIBUTOR-NEXT:    store volatile i8 0, ptr [[P]], align 1
+; ATTRIBUTOR-NEXT:    ret void
 ;
   store volatile i8 0, ptr %p
   ret void
 }
 
 define void @test_atomicrmw(ptr %p) {
-; CHECK: Function Attrs: mustprogress nofree norecurse nounwind willreturn memory(argmem: readwrite)
-; CHECK-LABEL: define {{[^@]+}}@test_atomicrmw
-; CHECK-SAME: (ptr nocapture [[P:%.*]]) #[[ATTR7:[0-9]+]] {
-; CHECK-NEXT:    [[TMP1:%.*]] = atomicrmw add ptr [[P]], i8 0 seq_cst, align 1
-; CHECK-NEXT:    ret void
+; FNATTRS: Function Attrs: mustprogress nofree norecurse nounwind willreturn memory(argmem: readwrite)
+; FNATTRS-LABEL: define {{[^@]+}}@test_atomicrmw
+; FNATTRS-SAME: (ptr nocapture [[P:%.*]]) #[[ATTR7:[0-9]+]] {
+; FNATTRS-NEXT:    [[TMP1:%.*]] = atomicrmw add ptr [[P]], i8 0 seq_cst, align 1
+; FNATTRS-NEXT:    ret void
+;
+; ATTRIBUTOR: Function Attrs: mustprogress nofree norecurse nounwind willreturn memory(argmem: readwrite)
+; ATTRIBUTOR-LABEL: define {{[^@]+}}@test_atomicrmw
+; ATTRIBUTOR-SAME: (ptr nocapture nofree nonnull [[P:%.*]]) #[[ATTR6]] {
+; ATTRIBUTOR-NEXT:    [[TMP1:%.*]] = atomicrmw add ptr [[P]], i8 0 seq_cst, align 1
+; ATTRIBUTOR-NEXT:    ret void
 ;
   atomicrmw add ptr %p, i8 0  seq_cst
   ret void
@@ -121,10 +182,10 @@ define void @test_atomicrmw(ptr %p) {
 declare void @direct1_callee(ptr %p)
 
 define void @direct1(ptr %p) {
-; CHECK-LABEL: define {{[^@]+}}@direct1
-; CHECK-SAME: (ptr [[P:%.*]]) {
-; CHECK-NEXT:    call void @direct1_callee(ptr [[P]])
-; CHECK-NEXT:    ret void
+; COMMON-LABEL: define {{[^@]+}}@direct1
+; COMMON-SAME: (ptr [[P:%.*]]) {
+; COMMON-NEXT:    call void @direct1_callee(ptr [[P]])
+; COMMON-NEXT:    ret void
 ;
   call void @direct1_callee(ptr %p)
   ret void
@@ -134,11 +195,17 @@ declare void @direct2_callee(ptr %p) writeonly
 
 ; writeonly w/o nocapture is not enough
 define void @direct2(ptr %p) {
-; CHECK: Function Attrs: memory(write)
-; CHECK-LABEL: define {{[^@]+}}@direct2
-; CHECK-SAME: (ptr [[P:%.*]]) #[[ATTR8:[0-9]+]] {
-; CHECK-NEXT:    call void @direct2_callee(ptr [[P]])
-; CHECK-NEXT:    ret void
+; FNATTRS: Function Attrs: memory(write)
+; FNATTRS-LABEL: define {{[^@]+}}@direct2
+; FNATTRS-SAME: (ptr [[P:%.*]]) #[[ATTR8:[0-9]+]] {
+; FNATTRS-NEXT:    call void @direct2_callee(ptr [[P]])
+; FNATTRS-NEXT:    ret void
+;
+; ATTRIBUTOR: Function Attrs: memory(write)
+; ATTRIBUTOR-LABEL: define {{[^@]+}}@direct2
+; ATTRIBUTOR-SAME: (ptr writeonly [[P:%.*]]) #[[ATTR7:[0-9]+]] {
+; ATTRIBUTOR-NEXT:    call void @direct2_callee(ptr [[P]]) #[[ATTR7]]
+; ATTRIBUTOR-NEXT:    ret void
 ;
   call void @direct2_callee(ptr %p)
   ; read back from global, read through pointer...
@@ -146,11 +213,17 @@ define void @direct2(ptr %p) {
 }
 
 define void @direct2b(ptr %p) {
-; CHECK: Function Attrs: memory(write)
-; CHECK-LABEL: define {{[^@]+}}@direct2b
-; CHECK-SAME: (ptr nocapture [[P:%.*]]) #[[ATTR8]] {
-; CHECK-NEXT:    call void @direct2_callee(ptr nocapture [[P]])
-; CHECK-NEXT:    ret void
+; FNATTRS: Function Attrs: memory(write)
+; FNATTRS-LABEL: define {{[^@]+}}@direct2b
+; FNATTRS-SAME: (ptr nocapture [[P:%.*]]) #[[ATTR8]] {
+; FNATTRS-NEXT:    call void @direct2_callee(ptr nocapture [[P]])
+; FNATTRS-NEXT:    ret void
+;
+; ATTRIBUTOR: Function Attrs: memory(write)
+; ATTRIBUTOR-LABEL: define {{[^@]+}}@direct2b
+; ATTRIBUTOR-SAME: (ptr nocapture writeonly [[P:%.*]]) #[[ATTR7]] {
+; ATTRIBUTOR-NEXT:    call void @direct2_callee(ptr nocapture writeonly [[P]]) #[[ATTR7]]
+; ATTRIBUTOR-NEXT:    ret void
 ;
   call void @direct2_callee(ptr nocapture %p)
   ret void
@@ -159,61 +232,87 @@ define void @direct2b(ptr %p) {
 declare void @direct3_callee(ptr nocapture writeonly %p)
 
 define void @direct3(ptr %p) {
-; CHECK-LABEL: define {{[^@]+}}@direct3
-; CHECK-SAME: (ptr nocapture writeonly [[P:%.*]]) {
-; CHECK-NEXT:    call void @direct3_callee(ptr [[P]])
-; CHECK-NEXT:    ret void
+; FNATTRS-LABEL: define {{[^@]+}}@direct3
+; FNATTRS-SAME: (ptr nocapture writeonly [[P:%.*]]) {
+; FNATTRS-NEXT:    call void @direct3_callee(ptr [[P]])
+; FNATTRS-NEXT:    ret void
+;
+; ATTRIBUTOR-LABEL: define {{[^@]+}}@direct3
+; ATTRIBUTOR-SAME: (ptr nocapture writeonly [[P:%.*]]) {
+; ATTRIBUTOR-NEXT:    call void @direct3_callee(ptr nocapture writeonly [[P]])
+; ATTRIBUTOR-NEXT:    ret void
 ;
   call void @direct3_callee(ptr %p)
   ret void
 }
 
 define void @direct3b(ptr %p) {
-; CHECK-LABEL: define {{[^@]+}}@direct3b
-; CHECK-SAME: (ptr [[P:%.*]]) {
-; CHECK-NEXT:    call void @direct3_callee(ptr [[P]]) [ "may-read-and-capture"(ptr [[P]]) ]
-; CHECK-NEXT:    ret void
+; COMMON-LABEL: define {{[^@]+}}@direct3b
+; COMMON-SAME: (ptr [[P:%.*]]) {
+; COMMON-NEXT:    call void @direct3_callee(ptr [[P]]) [ "may-read-and-capture"(ptr [[P]]) ]
+; COMMON-NEXT:    ret void
 ;
   call void @direct3_callee(ptr %p) ["may-read-and-capture"(ptr %p)]
   ret void
 }
 
 define void @direct3c(ptr %p) {
-; CHECK-LABEL: define {{[^@]+}}@direct3c
-; CHECK-SAME: (ptr nocapture [[P:%.*]]) {
-; CHECK-NEXT:    call void @direct3_callee(ptr [[P]]) [ "may-read"() ]
-; CHECK-NEXT:    ret void
+; FNATTRS-LABEL: define {{[^@]+}}@direct3c
+; FNATTRS-SAME: (ptr nocapture [[P:%.*]]) {
+; FNATTRS-NEXT:    call void @direct3_callee(ptr [[P]]) [ "may-read"() ]
+; FNATTRS-NEXT:    ret void
+;
+; ATTRIBUTOR-LABEL: define {{[^@]+}}@direct3c
+; ATTRIBUTOR-SAME: (ptr nocapture [[P:%.*]]) {
+; ATTRIBUTOR-NEXT:    call void @direct3_callee(ptr nocapture [[P]]) [ "may-read"() ]
+; ATTRIBUTOR-NEXT:    ret void
 ;
   call void @direct3_callee(ptr %p) ["may-read"()]
   ret void
 }
 
 define void @fptr_test1(ptr %p, ptr %f) {
-; CHECK-LABEL: define {{[^@]+}}@fptr_test1
-; CHECK-SAME: (ptr [[P:%.*]], ptr nocapture readonly [[F:%.*]]) {
-; CHECK-NEXT:    call void [[F]](ptr [[P]])
-; CHECK-NEXT:    ret void
+; FNATTRS-LABEL: define {{[^@]+}}@fptr_test1
+; FNATTRS-SAME: (ptr [[P:%.*]], ptr nocapture readonly [[F:%.*]]) {
+; FNATTRS-NEXT:    call void [[F]](ptr [[P]])
+; FNATTRS-NEXT:    ret void
+;
+; ATTRIBUTOR-LABEL: define {{[^@]+}}@fptr_test1
+; ATTRIBUTOR-SAME: (ptr [[P:%.*]], ptr nocapture nofree nonnull [[F:%.*]]) {
+; ATTRIBUTOR-NEXT:    call void [[F]](ptr [[P]])
+; ATTRIBUTOR-NEXT:    ret void
 ;
   call void %f(ptr %p)
   ret void
 }
 
 define void @fptr_test2(ptr %p, ptr %f) {
-; CHECK-LABEL: define {{[^@]+}}@fptr_test2
-; CHECK-SAME: (ptr nocapture writeonly [[P:%.*]], ptr nocapture readonly [[F:%.*]]) {
-; CHECK-NEXT:    call void [[F]](ptr nocapture writeonly [[P]])
-; CHECK-NEXT:    ret void
+; FNATTRS-LABEL: define {{[^@]+}}@fptr_test2
+; FNATTRS-SAME: (ptr nocapture writeonly [[P:%.*]], ptr nocapture readonly [[F:%.*]]) {
+; FNATTRS-NEXT:    call void [[F]](ptr nocapture writeonly [[P]])
+; FNATTRS-NEXT:    ret void
+;
+; ATTRIBUTOR-LABEL: define {{[^@]+}}@fptr_test2
+; ATTRIBUTOR-SAME: (ptr nocapture [[P:%.*]], ptr nocapture nofree nonnull [[F:%.*]]) {
+; ATTRIBUTOR-NEXT:    call void [[F]](ptr nocapture writeonly [[P]])
+; ATTRIBUTOR-NEXT:    ret void
 ;
   call void %f(ptr nocapture writeonly %p)
   ret void
 }
 
 define void @fptr_test3(ptr %p, ptr %f) {
-; CHECK: Function Attrs: memory(write)
-; CHECK-LABEL: define {{[^@]+}}@fptr_test3
-; CHECK-SAME: (ptr nocapture [[P:%.*]], ptr nocapture readonly [[F:%.*]]) #[[ATTR8]] {
-; CHECK-NEXT:    call void [[F]](ptr nocapture [[P]]) #[[ATTR8]]
-; CHECK-NEXT:    ret void
+; FNATTRS: Function Attrs: memory(write)
+; FNATTRS-LABEL: define {{[^@]+}}@fptr_test3
+; FNATTRS-SAME: (ptr nocapture [[P:%.*]], ptr nocapture readonly [[F:%.*]]) #[[ATTR8]] {
+; FNATTRS-NEXT:    call void [[F]](ptr nocapture [[P]]) #[[ATTR8]]
+; FNATTRS-NEXT:    ret void
+;
+; ATTRIBUTOR: Function Attrs: memory(write)
+; ATTRIBUTOR-LABEL: define {{[^@]+}}@fptr_test3
+; ATTRIBUTOR-SAME: (ptr nocapture writeonly [[P:%.*]], ptr nocapture nofree nonnull writeonly [[F:%.*]]) #[[ATTR7]] {
+; ATTRIBUTOR-NEXT:    call void [[F]](ptr nocapture [[P]]) #[[ATTR7]]
+; ATTRIBUTOR-NEXT:    ret void
 ;
   call void %f(ptr nocapture %p) writeonly
   ret void


        


More information about the llvm-commits mailing list