[llvm] Add 'initialized' attribute langref (PR #84803)

Haopeng Liu via llvm-commits llvm-commits at lists.llvm.org
Thu Mar 14 09:50:39 PDT 2024


https://github.com/haopliu updated https://github.com/llvm/llvm-project/pull/84803

>From 2554c821dfa3893213f934253ecbca5d9d43ceab Mon Sep 17 00:00:00 2001
From: Haopeng Liu <haopliu at google.com>
Date: Mon, 11 Mar 2024 17:47:08 +0000
Subject: [PATCH 1/4] Add 'initialized' attribute langref

---
 llvm/docs/LangRef.rst | 22 ++++++++++++++++++++++
 1 file changed, 22 insertions(+)

diff --git a/llvm/docs/LangRef.rst b/llvm/docs/LangRef.rst
index b70220dec92615..39a555edfa80d6 100644
--- a/llvm/docs/LangRef.rst
+++ b/llvm/docs/LangRef.rst
@@ -1621,6 +1621,28 @@ Currently, only the following parameter attributes are defined:
     ``readonly`` or a ``memory`` attribute that does not contain
     ``argmem: write``.
 
+``initialized((Lo1,Hi1),...)``
+    This attribute is a list of const ranges in ascending order with no
+    overlapping or continuous. It indicates that the function initializes the
+    memory through the pointer argument, [%p+LoN, %p+HiN): there are no reads,
+    and no special accesses (such as volatile access or untrackable capture)
+    before the initialization in the function. LoN/HiN are 64-bit ints;
+    negative values are allowed in case a pointer to partway through the
+    allocation is passed to.
+
+    This attribute implies that the function initializes and does not read
+    before initialization through this pointer argument, even though it may
+    read the memory before initialization that the pointer points to, such
+    as through other arguments.
+
+    The ``writable`` or ``dereferenceable`` attribute does not imply
+    ``initialized`` attribute, and ``initialized`` does not imply ``writeonly``
+    since cases that read from the pointer after write, can be ``initialized``
+    but not ``writeonly``.
+
+    Note that this attribute does not apply to the unwind edge: the memory may
+    not actually be written to when unwinding happens.
+
 ``dead_on_unwind``
     At a high level, this attribute indicates that the pointer argument is dead
     if the call unwinds, in the sense that the caller will not depend on the

>From a9256bf15dd3e9bb836a467979f61569df98cf8e Mon Sep 17 00:00:00 2001
From: Haopeng Liu <haopliu at google.com>
Date: Tue, 12 Mar 2024 22:10:14 +0000
Subject: [PATCH 2/4] Update the LangRef

---
 llvm/docs/LangRef.rst | 27 +++++++++++++++------------
 1 file changed, 15 insertions(+), 12 deletions(-)

diff --git a/llvm/docs/LangRef.rst b/llvm/docs/LangRef.rst
index 39a555edfa80d6..0d0f21b8d203ef 100644
--- a/llvm/docs/LangRef.rst
+++ b/llvm/docs/LangRef.rst
@@ -1622,26 +1622,29 @@ Currently, only the following parameter attributes are defined:
     ``argmem: write``.
 
 ``initialized((Lo1,Hi1),...)``
-    This attribute is a list of const ranges in ascending order with no
-    overlapping or continuous. It indicates that the function initializes the
-    memory through the pointer argument, [%p+LoN, %p+HiN): there are no reads,
-    and no special accesses (such as volatile access or untrackable capture)
-    before the initialization in the function. LoN/HiN are 64-bit ints;
-    negative values are allowed in case a pointer to partway through the
-    allocation is passed to.
+    This attribute indicates that the function initializes the ranges of the
+    pointer parameter's memory, [%p+LoN, %p+HiN): there are no reads, and no
+    special accesses (such as volatile access or untrackable capture) before
+    the initialization write in the function.
 
     This attribute implies that the function initializes and does not read
     before initialization through this pointer argument, even though it may
     read the memory before initialization that the pointer points to, such
     as through other arguments.
 
-    The ``writable`` or ``dereferenceable`` attribute does not imply
+    Note that this attribute does not apply to the unwind edge: the
+    initializing function does not read the pointer before writing to it
+    regardless of unwinding or not, but the memory may not actually be
+    written to when unwinding happens.
+
+    The ``writable`` or ``dereferenceable`` attribute does not imply the
     ``initialized`` attribute, and ``initialized`` does not imply ``writeonly``
-    since cases that read from the pointer after write, can be ``initialized``
-    but not ``writeonly``.
+    since ``initialized`` allows reading from the pointer after writing.
 
-    Note that this attribute does not apply to the unwind edge: the memory may
-    not actually be written to when unwinding happens.
+    This attribute is a list of const ranges in ascending order with no
+    overlapping or adjoining list elements. LoN/HiN are 64-bit ints, and
+    negative values are allowed in case the argument points partway into
+    an allocation.
 
 ``dead_on_unwind``
     At a high level, this attribute indicates that the pointer argument is dead

>From 9150ece33c083b49efed2fca268b4539bf8996b6 Mon Sep 17 00:00:00 2001
From: Haopeng Liu <haopliu at google.com>
Date: Tue, 12 Mar 2024 22:54:21 +0000
Subject: [PATCH 3/4] Refactor the argument uses collecting to a function

---
 llvm/lib/Transforms/IPO/FunctionAttrs.cpp | 146 ++++++++++++----------
 1 file changed, 83 insertions(+), 63 deletions(-)

diff --git a/llvm/lib/Transforms/IPO/FunctionAttrs.cpp b/llvm/lib/Transforms/IPO/FunctionAttrs.cpp
index 7ebf265e17ba1f..a668dde567c016 100644
--- a/llvm/lib/Transforms/IPO/FunctionAttrs.cpp
+++ b/llvm/lib/Transforms/IPO/FunctionAttrs.cpp
@@ -580,45 +580,14 @@ struct ArgumentUsesTracker : public CaptureTracker {
   const SCCNodeSet &SCCNodes;
 };
 
-} // end anonymous namespace
-
-namespace llvm {
-
-template <> struct GraphTraits<ArgumentGraphNode *> {
-  using NodeRef = ArgumentGraphNode *;
-  using ChildIteratorType = SmallVectorImpl<ArgumentGraphNode *>::iterator;
-
-  static NodeRef getEntryNode(NodeRef A) { return A; }
-  static ChildIteratorType child_begin(NodeRef N) { return N->Uses.begin(); }
-  static ChildIteratorType child_end(NodeRef N) { return N->Uses.end(); }
-};
-
-template <>
-struct GraphTraits<ArgumentGraph *> : public GraphTraits<ArgumentGraphNode *> {
-  static NodeRef getEntryNode(ArgumentGraph *AG) { return AG->getEntryNode(); }
-
-  static ChildIteratorType nodes_begin(ArgumentGraph *AG) {
-    return AG->begin();
-  }
-
-  static ChildIteratorType nodes_end(ArgumentGraph *AG) { return AG->end(); }
-};
-
-} // end namespace llvm
-
-/// Returns Attribute::None, Attribute::ReadOnly or Attribute::ReadNone.
-static Attribute::AttrKind
-determinePointerAccessAttrs(Argument *A,
-                            const SmallPtrSet<Argument *, 8> &SCCNodes) {
+/// Get all uses of an argument in the function and store them to different
+/// lists: Reads, Writes, and SpecialUses.
+std::tuple<SmallVector<Instruction *, 16>, SmallVector<Instruction *, 16>,
+           SmallVector<Instruction *, 16>>
+getArgumentUses(Argument *A, const SmallPtrSet<Argument *, 8> &SCCNodes) {
   SmallVector<Use *, 32> Worklist;
   SmallPtrSet<Use *, 32> Visited;
-
-  // inalloca arguments are always clobbered by the call.
-  if (A->hasInAllocaAttr() || A->hasPreallocatedAttr())
-    return Attribute::None;
-
-  bool IsRead = false;
-  bool IsWrite = false;
+  SmallVector<Instruction *, 16> Reads, Writes, SpecialUses;
 
   for (Use &U : A->uses()) {
     Visited.insert(&U);
@@ -626,10 +595,6 @@ determinePointerAccessAttrs(Argument *A,
   }
 
   while (!Worklist.empty()) {
-    if (IsWrite && IsRead)
-      // No point in searching further..
-      return Attribute::None;
-
     Use *U = Worklist.pop_back_val();
     Instruction *I = cast<Instruction>(U->getUser());
 
@@ -649,7 +614,7 @@ determinePointerAccessAttrs(Argument *A,
     case Instruction::Invoke: {
       CallBase &CB = cast<CallBase>(*I);
       if (CB.isCallee(U)) {
-        IsRead = true;
+        Reads.push_back(I);
         // Note that indirect calls do not capture, see comment in
         // CaptureTracking for context
         continue;
@@ -668,12 +633,15 @@ determinePointerAccessAttrs(Argument *A,
           if (Visited.insert(&UU).second)
             Worklist.push_back(&UU);
       } else if (!CB.doesNotCapture(UseIndex)) {
-        if (!CB.onlyReadsMemory())
+        if (!CB.onlyReadsMemory()) {
           // If the callee can save a copy into other memory, then simply
           // scanning uses of the call is insufficient.  We have no way
           // of tracking copies of the pointer through memory to see
-          // if a reloaded copy is written to, thus we must give up.
-          return Attribute::None;
+          // if a reloaded copy is written to, thus we treat it as special
+          // uses.
+          SpecialUses.push_back(I);
+          continue;
+        }
         // Push users for processing once we finish this one
         if (!I->getType()->isVoidTy())
           for (Use &UU : I->uses())
@@ -698,12 +666,12 @@ determinePointerAccessAttrs(Argument *A,
       if (CB.doesNotAccessMemory(UseIndex)) {
         /* nop */
       } else if (!isModSet(ArgMR) || CB.onlyReadsMemory(UseIndex)) {
-        IsRead = true;
+        Reads.push_back(I);
       } else if (!isRefSet(ArgMR) ||
                  CB.dataOperandHasImpliedAttr(UseIndex, Attribute::WriteOnly)) {
-        IsWrite = true;
+        Writes.push_back(I);
       } else {
-        return Attribute::None;
+        SpecialUses.push_back(I);
       }
       break;
     }
@@ -711,23 +679,29 @@ determinePointerAccessAttrs(Argument *A,
     case Instruction::Load:
       // A volatile load has side effects beyond what readonly can be relied
       // upon.
-      if (cast<LoadInst>(I)->isVolatile())
-        return Attribute::None;
+      if (cast<LoadInst>(I)->isVolatile()) {
+        SpecialUses.push_back(I);
+        continue;
+      }
 
-      IsRead = true;
+      Reads.push_back(I);
       break;
 
     case Instruction::Store:
-      if (cast<StoreInst>(I)->getValueOperand() == *U)
+      if (cast<StoreInst>(I)->getValueOperand() == *U) {
         // untrackable capture
-        return Attribute::None;
+        SpecialUses.push_back(I);
+        continue;
+      }
 
       // A volatile store has side effects beyond what writeonly can be relied
       // upon.
-      if (cast<StoreInst>(I)->isVolatile())
-        return Attribute::None;
+      if (cast<StoreInst>(I)->isVolatile()) {
+        SpecialUses.push_back(I);
+        continue;
+      }
 
-      IsWrite = true;
+      Writes.push_back(I);
       break;
 
     case Instruction::ICmp:
@@ -735,15 +709,55 @@ determinePointerAccessAttrs(Argument *A,
       break;
 
     default:
-      return Attribute::None;
+      SpecialUses.push_back(I);
     }
   }
+  return {Reads, Writes, SpecialUses};
+}
+
+} // end anonymous namespace
+
+namespace llvm {
+
+template <> struct GraphTraits<ArgumentGraphNode *> {
+  using NodeRef = ArgumentGraphNode *;
+  using ChildIteratorType = SmallVectorImpl<ArgumentGraphNode *>::iterator;
+
+  static NodeRef getEntryNode(NodeRef A) { return A; }
+  static ChildIteratorType child_begin(NodeRef N) { return N->Uses.begin(); }
+  static ChildIteratorType child_end(NodeRef N) { return N->Uses.end(); }
+};
+
+template <>
+struct GraphTraits<ArgumentGraph *> : public GraphTraits<ArgumentGraphNode *> {
+  static NodeRef getEntryNode(ArgumentGraph *AG) { return AG->getEntryNode(); }
+
+  static ChildIteratorType nodes_begin(ArgumentGraph *AG) {
+    return AG->begin();
+  }
+
+  static ChildIteratorType nodes_end(ArgumentGraph *AG) { return AG->end(); }
+};
+
+} // end namespace llvm
+
+/// Returns Attribute::None, Attribute::ReadOnly or Attribute::ReadNone.
+static Attribute::AttrKind
+determinePointerAccessAttrs(Argument *A, SmallVector<Instruction *, 16> &Reads,
+                            SmallVector<Instruction *, 16> &Writes,
+                            SmallVector<Instruction *, 16> &SpecialUses) {
+  // inalloca arguments are always clobbered by the call.
+  if (A->hasInAllocaAttr() || A->hasPreallocatedAttr())
+    return Attribute::None;
+
+  if (!SpecialUses.empty())
+    return Attribute::None;
 
-  if (IsWrite && IsRead)
+  if (!Writes.empty() && !Reads.empty())
     return Attribute::None;
-  else if (IsRead)
+  else if (!Reads.empty())
     return Attribute::ReadOnly;
-  else if (IsWrite)
+  else if (!Writes.empty())
     return Attribute::WriteOnly;
   else
     return Attribute::ReadNone;
@@ -931,7 +945,9 @@ static void addArgumentAttrs(const SCCNodeSet &SCCNodes,
         // functions in the SCC.
         SmallPtrSet<Argument *, 8> Self;
         Self.insert(&A);
-        Attribute::AttrKind R = determinePointerAccessAttrs(&A, Self);
+        auto [Reads, Writes, SpecialUses] = getArgumentUses(&A, Self);
+        Attribute::AttrKind R =
+            determinePointerAccessAttrs(&A, Reads, Writes, SpecialUses);
         if (R != Attribute::None)
           if (addAccessAttr(&A, R))
             Changed.insert(F);
@@ -963,7 +979,9 @@ static void addArgumentAttrs(const SCCNodeSet &SCCNodes,
         // Infer the access attributes given the new nocapture one
         SmallPtrSet<Argument *, 8> Self;
         Self.insert(&*A);
-        Attribute::AttrKind R = determinePointerAccessAttrs(&*A, Self);
+        auto [Reads, Writes, SpecialUses] = getArgumentUses(&*A, Self);
+        Attribute::AttrKind R =
+            determinePointerAccessAttrs(&*A, Reads, Writes, SpecialUses);
         if (R != Attribute::None)
           addAccessAttr(A, R);
       }
@@ -1032,7 +1050,9 @@ static void addArgumentAttrs(const SCCNodeSet &SCCNodes,
     Attribute::AttrKind AccessAttr = Attribute::ReadNone;
     for (ArgumentGraphNode *N : ArgumentSCC) {
       Argument *A = N->Definition;
-      Attribute::AttrKind K = determinePointerAccessAttrs(A, ArgumentSCCNodes);
+      auto [Reads, Writes, SpecialUses] = getArgumentUses(A, ArgumentSCCNodes);
+      Attribute::AttrKind K =
+          determinePointerAccessAttrs(A, Reads, Writes, SpecialUses);
       AccessAttr = meetAccessAttr(AccessAttr, K);
       if (AccessAttr == Attribute::None)
         break;

>From 206b707e028743d7b4d3d975db4fb5b3c527f0df Mon Sep 17 00:00:00 2001
From: Haopeng Liu <haopliu at google.com>
Date: Thu, 14 Mar 2024 16:50:21 +0000
Subject: [PATCH 4/4] Add 'initialized' attribute support and undo the
 FunctionAttrs refactoring.

---
 llvm/include/llvm/ADT/FoldingSet.h          |  15 ++
 llvm/include/llvm/AsmParser/LLParser.h      |   5 +
 llvm/include/llvm/Bitcode/LLVMBitCodes.h    |   1 +
 llvm/include/llvm/IR/Attributes.h           |  19 +++
 llvm/include/llvm/IR/Attributes.td          |   6 +
 llvm/lib/AsmParser/LLParser.cpp             |  65 +++++++++
 llvm/lib/Bitcode/Reader/BitcodeReader.cpp   |  18 +++
 llvm/lib/Bitcode/Writer/BitcodeWriter.cpp   |  17 ++-
 llvm/lib/IR/AttributeImpl.h                 |  31 ++++-
 llvm/lib/IR/Attributes.cpp                  |  73 +++++++++-
 llvm/lib/IR/Verifier.cpp                    |  21 +++
 llvm/lib/Transforms/IPO/FunctionAttrs.cpp   | 146 +++++++++-----------
 llvm/lib/Transforms/Utils/CodeExtractor.cpp |   1 +
 llvm/utils/TableGen/Attributes.cpp          |  11 +-
 14 files changed, 336 insertions(+), 93 deletions(-)

diff --git a/llvm/include/llvm/ADT/FoldingSet.h b/llvm/include/llvm/ADT/FoldingSet.h
index ddc3e52255d6c0..04d6f9df40ceb0 100644
--- a/llvm/include/llvm/ADT/FoldingSet.h
+++ b/llvm/include/llvm/ADT/FoldingSet.h
@@ -362,6 +362,21 @@ class FoldingSetNodeID {
     }
   }
 
+  void AddRanges(const SmallVector<std::pair<int64_t, int64_t>, 16> &Ranges) {
+    unsigned Size = Ranges.size();
+
+    unsigned NumsToInsert = 1 + 2 * Size;
+    Bits.reserve(Bits.size() + NumsToInsert);
+    Bits.push_back(Size);
+    if (Size == 0)
+      return;
+
+    for (const auto &[Start, End] : Ranges) {
+      Bits.push_back(Start);
+      Bits.push_back(End);
+    }
+  }
+
   void AddBoolean(bool B) { AddInteger(B ? 1U : 0U); }
   void AddString(StringRef String);
   void AddNodeID(const FoldingSetNodeID &ID);
diff --git a/llvm/include/llvm/AsmParser/LLParser.h b/llvm/include/llvm/AsmParser/LLParser.h
index e85728aa3c0da0..104bf06cf6604d 100644
--- a/llvm/include/llvm/AsmParser/LLParser.h
+++ b/llvm/include/llvm/AsmParser/LLParser.h
@@ -275,6 +275,7 @@ namespace llvm {
       Loc = Lex.getLoc();
       return parseUInt64(Val);
     }
+    bool parseInt64(int64_t &Val);
     bool parseFlag(unsigned &Val);
 
     bool parseStringAttribute(AttrBuilder &B);
@@ -307,6 +308,10 @@ namespace llvm {
                                 bool AllowParens = false);
     bool parseOptionalCodeModel(CodeModel::Model &model);
     bool parseOptionalDerefAttrBytes(lltok::Kind AttrKind, uint64_t &Bytes);
+    bool parseConstRange(std::pair<int64_t, int64_t> &Range);
+    bool parseInitializedRanges(
+        lltok::Kind AttrKind,
+        SmallVector<std::pair<int64_t, int64_t>, 16> &Ranges);
     bool parseOptionalUWTableKind(UWTableKind &Kind);
     bool parseAllocKind(AllocFnKind &Kind);
     std::optional<MemoryEffects> parseMemoryAttr();
diff --git a/llvm/include/llvm/Bitcode/LLVMBitCodes.h b/llvm/include/llvm/Bitcode/LLVMBitCodes.h
index c0a52d64a101d0..b2f38716b86f05 100644
--- a/llvm/include/llvm/Bitcode/LLVMBitCodes.h
+++ b/llvm/include/llvm/Bitcode/LLVMBitCodes.h
@@ -725,6 +725,7 @@ enum AttributeKindCodes {
   ATTR_KIND_CORO_ONLY_DESTROY_WHEN_COMPLETE = 90,
   ATTR_KIND_DEAD_ON_UNWIND = 91,
   ATTR_KIND_RANGE = 92,
+  ATTR_KIND_INITIALIZED = 93,
 };
 
 enum ComdatSelectionKindCodes {
diff --git a/llvm/include/llvm/IR/Attributes.h b/llvm/include/llvm/IR/Attributes.h
index 0c2a02514ba0e6..b9d479e9b85cb1 100644
--- a/llvm/include/llvm/IR/Attributes.h
+++ b/llvm/include/llvm/IR/Attributes.h
@@ -94,6 +94,8 @@ class Attribute {
 
   static const unsigned NumIntAttrKinds = LastIntAttr - FirstIntAttr + 1;
   static const unsigned NumTypeAttrKinds = LastTypeAttr - FirstTypeAttr + 1;
+  static const unsigned NumConstRangeListAttrKinds =
+      LastConstRangeListAttr - FirstConstRangeListAttr + 1;
 
   static bool isEnumAttrKind(AttrKind Kind) {
     return Kind >= FirstEnumAttr && Kind <= LastEnumAttr;
@@ -107,6 +109,9 @@ class Attribute {
   static bool isConstantRangeAttrKind(AttrKind Kind) {
     return Kind >= FirstConstantRangeAttr && Kind <= LastConstantRangeAttr;
   }
+  static bool isConstRangeListAttrKind(AttrKind Kind) {
+    return Kind >= FirstConstRangeListAttr && Kind <= LastConstRangeListAttr;
+  }
 
   static bool canUseAsFnAttr(AttrKind Kind);
   static bool canUseAsParamAttr(AttrKind Kind);
@@ -131,6 +136,8 @@ class Attribute {
   static Attribute get(LLVMContext &Context, AttrKind Kind, Type *Ty);
   static Attribute get(LLVMContext &Context, AttrKind Kind,
                        const ConstantRange &CR);
+  static Attribute get(LLVMContext &Context, AttrKind Kind,
+                       SmallVector<std::pair<int64_t, int64_t>, 16> &Ranges);
 
   /// Return a uniquified Attribute object that has the specific
   /// alignment set.
@@ -186,6 +193,9 @@ class Attribute {
   /// Return true if the attribute is a type attribute.
   bool isTypeAttribute() const;
 
+  /// Return true if the attribute is a const range list attribute.
+  bool isConstRangeListAttribute() const;
+
   /// Return true if the attribute is a ConstantRange attribute.
   bool isConstantRangeAttribute() const;
 
@@ -226,6 +236,10 @@ class Attribute {
   /// attribute to be a ConstantRange attribute.
   ConstantRange getValueAsConstantRange() const;
 
+  /// Return the attribute's value as a const range list. This requires the
+  /// attribute to be a const range list attribute.
+  SmallVector<std::pair<int64_t, int64_t>, 16> getValueAsRanges() const;
+
   /// Returns the alignment field of an attribute as a byte alignment
   /// value.
   MaybeAlign getAlignment() const;
@@ -1169,6 +1183,11 @@ class AttrBuilder {
   /// Add a type attribute with the given type.
   AttrBuilder &addTypeAttr(Attribute::AttrKind Kind, Type *Ty);
 
+  /// Add a const range list attribute with the given ranges.
+  AttrBuilder &
+  addConstRangeListAttr(Attribute::AttrKind Kind,
+                        SmallVector<std::pair<int64_t, int64_t>, 16> &Ranges);
+
   /// This turns a byval type into the form used internally in Attribute.
   AttrBuilder &addByValAttr(Type *Ty);
 
diff --git a/llvm/include/llvm/IR/Attributes.td b/llvm/include/llvm/IR/Attributes.td
index cef8b17769f0d0..79df429a07ee45 100644
--- a/llvm/include/llvm/IR/Attributes.td
+++ b/llvm/include/llvm/IR/Attributes.td
@@ -38,6 +38,9 @@ class IntAttr<string S, list<AttrProperty> P> : Attr<S, P>;
 /// Type attribute.
 class TypeAttr<string S, list<AttrProperty> P> : Attr<S, P>;
 
+/// Const range list attribute.
+class ConstRangeListAttr<string S, list<AttrProperty> P>: Attr<S, P>;
+
 /// StringBool attribute.
 class StrBoolAttr<string S> : Attr<S, []>;
 
@@ -318,6 +321,9 @@ def Writable : EnumAttr<"writable", [ParamAttr]>;
 /// Function only writes to memory.
 def WriteOnly : EnumAttr<"writeonly", [ParamAttr]>;
 
+/// Pointer argument memory [%p+LoN, %p+HiN) is initialized.
+def Initialized : ConstRangeListAttr<"initialized", [ParamAttr]>;
+
 /// Zero extended before/after call.
 def ZExt : EnumAttr<"zeroext", [ParamAttr, RetAttr]>;
 
diff --git a/llvm/lib/AsmParser/LLParser.cpp b/llvm/lib/AsmParser/LLParser.cpp
index 78bcd94e23fae1..b8e8d29696a46a 100644
--- a/llvm/lib/AsmParser/LLParser.cpp
+++ b/llvm/lib/AsmParser/LLParser.cpp
@@ -1566,6 +1566,13 @@ bool LLParser::parseEnumAttribute(Attribute::AttrKind Attr, AttrBuilder &B,
     B.addDereferenceableOrNullAttr(Bytes);
     return false;
   }
+  case Attribute::Initialized: {
+    SmallVector<std::pair<int64_t, int64_t>, 16> Ranges;
+    if (parseInitializedRanges(lltok::kw_initialized, Ranges))
+      return true;
+    B.addConstRangeListAttr(Attribute::Initialized, Ranges);
+    return false;
+  }
   case Attribute::UWTable: {
     UWTableKind Kind;
     if (parseOptionalUWTableKind(Kind))
@@ -1850,6 +1857,16 @@ bool LLParser::parseUInt64(uint64_t &Val) {
   return false;
 }
 
+/// parseInt64
+///   ::= int64_t
+bool LLParser::parseInt64(int64_t &Val) {
+  if (Lex.getKind() != lltok::APSInt)
+    return tokError("expected signed integer");
+  Val = Lex.getAPSIntVal().extend(64).getSExtValue();
+  Lex.Lex();
+  return false;
+}
+
 /// parseTLSModel
 ///   := 'localdynamic'
 ///   := 'initialexec'
@@ -2364,6 +2381,54 @@ bool LLParser::parseOptionalDerefAttrBytes(lltok::Kind AttrKind,
   return false;
 }
 
+bool LLParser::parseConstRange(std::pair<int64_t, int64_t> &Range) {
+  LocTy LparenLoc = Lex.getLoc();
+  if (!EatIfPresent(lltok::lparen))
+    return error(LparenLoc, "expected'('");
+
+  if (parseInt64(Range.first))
+    return true;
+
+  if (EatIfPresent(lltok::comma)) {
+    if (parseInt64(Range.second)) {
+      return true;
+    }
+  }
+
+  LocTy RparenLoc = Lex.getLoc();
+  if (!EatIfPresent(lltok::rparen))
+    return error(RparenLoc, "expected ')'");
+
+  return false;
+}
+
+bool LLParser::parseInitializedRanges(
+    lltok::Kind AttrKind,
+    SmallVector<std::pair<int64_t, int64_t>, 16> &Ranges) {
+  assert(AttrKind == lltok::kw_initialized);
+  Ranges.clear();
+
+  if (!EatIfPresent(AttrKind))
+    return false;
+
+  LocTy LparenLoc = Lex.getLoc();
+  if (!EatIfPresent(lltok::lparen))
+    return error(LparenLoc, "expected '('");
+
+  // Parse each range.
+  do {
+    std::pair<int64_t, int64_t> Range;
+    if (parseConstRange(Range))
+      return true;
+    Ranges.push_back(Range);
+  } while (EatIfPresent(lltok::comma));
+
+  LocTy RparenLoc = Lex.getLoc();
+  if (!EatIfPresent(lltok::rparen))
+    return error(RparenLoc, "expected ')'");
+  return false;
+}
+
 bool LLParser::parseOptionalUWTableKind(UWTableKind &Kind) {
   Lex.Lex();
   Kind = UWTableKind::Default;
diff --git a/llvm/lib/Bitcode/Reader/BitcodeReader.cpp b/llvm/lib/Bitcode/Reader/BitcodeReader.cpp
index 9c63116114f3c5..45dd33f8884492 100644
--- a/llvm/lib/Bitcode/Reader/BitcodeReader.cpp
+++ b/llvm/lib/Bitcode/Reader/BitcodeReader.cpp
@@ -2129,6 +2129,8 @@ static Attribute::AttrKind getAttrFromCode(uint64_t Code) {
     return Attribute::DeadOnUnwind;
   case bitc::ATTR_KIND_RANGE:
     return Attribute::Range;
+  case bitc::ATTR_KIND_INITIALIZED:
+    return Attribute::Initialized;
   }
 }
 
@@ -2313,6 +2315,22 @@ Error BitcodeReader::parseAttributeGroupBlock() {
           i--;
 
           B.addConstantRangeAttr(Kind, MaybeCR.get());
+        } else if (Record[i] == 8 || Record[i] == 9) {
+          Attribute::AttrKind Kind;
+          if (Error Err = parseAttrKind(Record[++i], &Kind))
+            return Err;
+          if (!Attribute::isConstRangeListAttrKind(Kind))
+            return error("Not a const range list attribute");
+
+          SmallVector<std::pair<int64_t, int64_t>, 16> Ranges;
+          int RangeSize = Record[++i];
+          for (int Idx = 0; Idx < RangeSize; ++Idx) {
+            int Start = Record[++i];
+            int End = Record[++i];
+            Ranges.push_back(std::make_pair(Start, End));
+          }
+
+          B.addConstRangeListAttr(Kind, Ranges);
         } else {
           return error("Invalid attribute group entry");
         }
diff --git a/llvm/lib/Bitcode/Writer/BitcodeWriter.cpp b/llvm/lib/Bitcode/Writer/BitcodeWriter.cpp
index 597f49332fad25..027a9bf68abcbc 100644
--- a/llvm/lib/Bitcode/Writer/BitcodeWriter.cpp
+++ b/llvm/lib/Bitcode/Writer/BitcodeWriter.cpp
@@ -846,6 +846,8 @@ static uint64_t getAttrKindEncoding(Attribute::AttrKind Kind) {
     return bitc::ATTR_KIND_DEAD_ON_UNWIND;
   case Attribute::Range:
     return bitc::ATTR_KIND_RANGE;
+  case Attribute::Initialized:
+    return bitc::ATTR_KIND_INITIALIZED;
   case Attribute::EndAttrKinds:
     llvm_unreachable("Can not encode end-attribute kinds marker.");
   case Attribute::None:
@@ -930,11 +932,24 @@ void ModuleBitcodeWriter::writeAttributeGroupTable() {
         Record.push_back(getAttrKindEncoding(Attr.getKindAsEnum()));
         if (Ty)
           Record.push_back(VE.getTypeID(Attr.getValueAsType()));
-      } else {
+      } else if (Attr.isConstantRangeAttribute()) {
         assert(Attr.isConstantRangeAttribute());
         Record.push_back(7);
         Record.push_back(getAttrKindEncoding(Attr.getKindAsEnum()));
         emitConstantRange(Record, Attr.getValueAsConstantRange());
+      } else {
+        assert(Attr.isConstRangeListAttribute());
+        const auto &Ranges = Attr.getValueAsRanges();
+
+        Record.push_back(Ranges.empty() ? 8 : 9);
+        Record.push_back(getAttrKindEncoding(Attr.getKindAsEnum()));
+        Record.push_back(Ranges.size());
+        if (!Ranges.empty()) {
+          for (const auto &Range : Ranges) {
+            Record.push_back(Range.first);
+            Record.push_back(Range.second);
+          }
+        }
       }
     }
 
diff --git a/llvm/lib/IR/AttributeImpl.h b/llvm/lib/IR/AttributeImpl.h
index 9a6427bbc3d557..0c4db637a49aca 100644
--- a/llvm/lib/IR/AttributeImpl.h
+++ b/llvm/lib/IR/AttributeImpl.h
@@ -48,6 +48,7 @@ class AttributeImpl : public FoldingSetNode {
     StringAttrEntry,
     TypeAttrEntry,
     ConstantRangeAttrEntry,
+    ConstRangeListAttrEntry,
   };
 
   AttributeImpl(AttrEntryKind KindID) : KindID(KindID) {}
@@ -64,6 +65,9 @@ class AttributeImpl : public FoldingSetNode {
   bool isConstantRangeAttribute() const {
     return KindID == ConstantRangeAttrEntry;
   }
+  bool isConstRangeListAttribute() const {
+    return KindID == ConstRangeListAttrEntry;
+  }
 
   bool hasAttribute(Attribute::AttrKind A) const;
   bool hasAttribute(StringRef Kind) const;
@@ -79,6 +83,8 @@ class AttributeImpl : public FoldingSetNode {
 
   ConstantRange getValueAsConstantRange() const;
 
+  SmallVector<std::pair<int64_t, int64_t>, 16> getValueAsRanges() const;
+
   /// Used when sorting the attributes.
   bool operator<(const AttributeImpl &AI) const;
 
@@ -91,8 +97,10 @@ class AttributeImpl : public FoldingSetNode {
       Profile(ID, getKindAsString(), getValueAsString());
     else if (isTypeAttribute())
       Profile(ID, getKindAsEnum(), getValueAsType());
-    else
+    else if (isConstantRangeAttribute())
       Profile(ID, getKindAsEnum(), getValueAsConstantRange());
+    else
+      Profile(ID, getKindAsEnum(), getValueAsRanges());
   }
 
   static void Profile(FoldingSetNodeID &ID, Attribute::AttrKind Kind) {
@@ -124,6 +132,13 @@ class AttributeImpl : public FoldingSetNode {
     ID.AddInteger(CR.getLower());
     ID.AddInteger(CR.getUpper());
   }
+
+  static void
+  Profile(FoldingSetNodeID &ID, Attribute::AttrKind Kind,
+          const SmallVector<std::pair<int64_t, int64_t>, 16> &Ranges) {
+    ID.AddInteger(Kind);
+    ID.AddRanges(Ranges);
+  }
 };
 
 static_assert(std::is_trivially_destructible<AttributeImpl>::value,
@@ -222,6 +237,20 @@ class ConstantRangeAttributeImpl : public EnumAttributeImpl {
   ConstantRange getConstantRangeValue() const { return CR; }
 };
 
+class ConstRangeListAttributeImpl : public EnumAttributeImpl {
+  SmallVector<std::pair<int64_t, int64_t>, 16> Ranges;
+
+public:
+  ConstRangeListAttributeImpl(
+      Attribute::AttrKind Kind,
+      SmallVector<std::pair<int64_t, int64_t>, 16> &Ranges)
+      : EnumAttributeImpl(ConstRangeListAttrEntry, Kind), Ranges(Ranges) {}
+
+  SmallVector<std::pair<int64_t, int64_t>, 16> getRangesValue() const {
+    return Ranges;
+  }
+};
+
 class AttributeBitSet {
   /// Bitset with a bit for each available attribute Attribute::AttrKind.
   uint8_t AvailableAttrs[12] = {};
diff --git a/llvm/lib/IR/Attributes.cpp b/llvm/lib/IR/Attributes.cpp
index b2d9992cdc0258..f7d99de908730c 100644
--- a/llvm/lib/IR/Attributes.cpp
+++ b/llvm/lib/IR/Attributes.cpp
@@ -191,6 +191,29 @@ Attribute Attribute::get(LLVMContext &Context, Attribute::AttrKind Kind,
   return Attribute(PA);
 }
 
+Attribute Attribute::get(LLVMContext &Context, Attribute::AttrKind Kind,
+                         SmallVector<std::pair<int64_t, int64_t>, 16> &Ranges) {
+  assert(Attribute::isConstRangeListAttrKind(Kind) &&
+         "Not a const range list attribute");
+  LLVMContextImpl *pImpl = Context.pImpl;
+  FoldingSetNodeID ID;
+  ID.AddInteger(Kind);
+  ID.AddRanges(Ranges);
+
+  void *InsertPoint;
+  AttributeImpl *PA = pImpl->AttrsSet.FindNodeOrInsertPos(ID, InsertPoint);
+
+  if (!PA) {
+    // If we didn't find any existing attributes of the same shape then create a
+    // new one and insert it.
+    PA = new (pImpl->Alloc) ConstRangeListAttributeImpl(Kind, Ranges);
+    pImpl->AttrsSet.InsertNode(PA, InsertPoint);
+  }
+
+  // Return the Attribute that we found or created.
+  return Attribute(PA);
+}
+
 Attribute Attribute::getWithAlignment(LLVMContext &Context, Align A) {
   assert(A <= llvm::Value::MaximumAlignment && "Alignment too large.");
   return get(Context, Alignment, A.value());
@@ -317,10 +340,14 @@ bool Attribute::isConstantRangeAttribute() const {
   return pImpl && pImpl->isConstantRangeAttribute();
 }
 
+bool Attribute::isConstRangeListAttribute() const {
+  return pImpl && pImpl->isConstRangeListAttribute();
+}
+
 Attribute::AttrKind Attribute::getKindAsEnum() const {
   if (!pImpl) return None;
   assert((isEnumAttribute() || isIntAttribute() || isTypeAttribute() ||
-          isConstantRangeAttribute()) &&
+          isConstantRangeAttribute() || isConstRangeListAttribute()) &&
          "Invalid attribute type to get the kind as an enum!");
   return pImpl->getKindAsEnum();
 }
@@ -366,6 +393,15 @@ ConstantRange Attribute::getValueAsConstantRange() const {
   return pImpl->getValueAsConstantRange();
 }
 
+SmallVector<std::pair<int64_t, int64_t>, 16>
+Attribute::getValueAsRanges() const {
+  if (!pImpl)
+    return {};
+  assert(isConstRangeListAttribute() &&
+         "Invalid attribute type to get the value as a const range list!");
+  return pImpl->getValueAsRanges();
+}
+
 bool Attribute::hasAttribute(AttrKind Kind) const {
   return (pImpl && pImpl->hasAttribute(Kind)) || (!pImpl && Kind == None);
 }
@@ -616,6 +652,23 @@ std::string Attribute::getAsString(bool InAttrGrp) const {
     return Result;
   }
 
+  if (hasAttribute(Attribute::Initialized)) {
+    auto Ranges = getValueAsRanges();
+    if (Ranges.empty())
+      return "";
+
+    std::string Result = "initialized(";
+    raw_string_ostream OS(Result);
+    for (size_t i = 0; i < Ranges.size(); i++) {
+      auto [Start, End] = Ranges[i];
+      OS << "(" << Start << "," << End << ")";
+      if (i != Ranges.size() - 1)
+        OS << ",";
+    }
+    OS << ")";
+    return Result;
+  }
+
   // Convert target-dependent attributes to strings of the form:
   //
   //   "kind"
@@ -706,7 +759,7 @@ bool AttributeImpl::hasAttribute(StringRef Kind) const {
 
 Attribute::AttrKind AttributeImpl::getKindAsEnum() const {
   assert(isEnumAttribute() || isIntAttribute() || isTypeAttribute() ||
-         isConstantRangeAttribute());
+         isConstantRangeAttribute() || isConstRangeListAttribute());
   return static_cast<const EnumAttributeImpl *>(this)->getEnumKind();
 }
 
@@ -741,6 +794,13 @@ ConstantRange AttributeImpl::getValueAsConstantRange() const {
       ->getConstantRangeValue();
 }
 
+SmallVector<std::pair<int64_t, int64_t>, 16>
+AttributeImpl::getValueAsRanges() const {
+  assert(isConstRangeListAttribute());
+  return static_cast<const ConstRangeListAttributeImpl *>(this)
+      ->getRangesValue();
+}
+
 bool AttributeImpl::operator<(const AttributeImpl &AI) const {
   if (this == &AI)
     return false;
@@ -1948,6 +2008,12 @@ AttrBuilder &AttrBuilder::addConstantRangeAttr(Attribute::AttrKind Kind,
   return addAttribute(Attribute::get(Ctx, Kind, CR));
 }
 
+AttrBuilder &AttrBuilder::addConstRangeListAttr(
+    Attribute::AttrKind Kind,
+    SmallVector<std::pair<int64_t, int64_t>, 16> &Ranges) {
+  return addAttribute(Attribute::get(Ctx, Kind, Ranges));
+}
+
 AttrBuilder &AttrBuilder::addRangeAttr(const ConstantRange &CR) {
   return addConstantRangeAttr(Attribute::Range, CR);
 }
@@ -2040,7 +2106,8 @@ AttributeMask AttributeFuncs::typeIncompatible(Type *Ty,
           .addAttribute(Attribute::Dereferenceable)
           .addAttribute(Attribute::DereferenceableOrNull)
           .addAttribute(Attribute::Writable)
-          .addAttribute(Attribute::DeadOnUnwind);
+          .addAttribute(Attribute::DeadOnUnwind)
+          .addAttribute(Attribute::Initialized);
     if (ASK & ASK_UNSAFE_TO_DROP)
       Incompatible.addAttribute(Attribute::Nest)
           .addAttribute(Attribute::SwiftError)
diff --git a/llvm/lib/IR/Verifier.cpp b/llvm/lib/IR/Verifier.cpp
index 0e6c01802cfb8c..c4946a54f7bfef 100644
--- a/llvm/lib/IR/Verifier.cpp
+++ b/llvm/lib/IR/Verifier.cpp
@@ -1993,6 +1993,14 @@ void Verifier::verifyParameterAttrs(AttributeSet Attrs, Type *Ty,
           Attrs.hasAttribute(Attribute::ReadOnly)),
         "Attributes writable and readonly are incompatible!", V);
 
+  Check(!(Attrs.hasAttribute(Attribute::Initialized) &&
+          Attrs.hasAttribute(Attribute::ReadNone)),
+        "Attributes initialized and readnone are incompatible!", V);
+
+  Check(!(Attrs.hasAttribute(Attribute::Initialized) &&
+          Attrs.hasAttribute(Attribute::ReadOnly)),
+        "Attributes initialized and readonly are incompatible!", V);
+
   AttributeMask IncompatibleAttrs = AttributeFuncs::typeIncompatible(Ty);
   for (Attribute Attr : Attrs) {
     if (!Attr.isStringAttribute() &&
@@ -2044,6 +2052,19 @@ void Verifier::verifyParameterAttrs(AttributeSet Attrs, Type *Ty,
     Check(Ty->isIntOrIntVectorTy(CR.getBitWidth()),
           "Range bit width must match type bit width!", V);
   }
+  if (Attrs.hasAttribute(Attribute::Initialized)) {
+    auto Inits = Attrs.getAttribute(Attribute::Initialized).getValueAsRanges();
+    Check(!Inits.empty(), "Attribute 'initialized' does not support empty list",
+          V);
+
+    for (size_t i = 1; i < Inits.size(); i++) {
+      Check(Inits[i].first > Inits[i - 1].first,
+            "Attribute 'initialized' requires intervals in ascending order!",
+            V);
+      Check(Inits[i].first > Inits[i - 1].second,
+            "Attribute 'initialized' requires intervals merged!", V);
+    }
+  }
 }
 
 void Verifier::checkUnsignedBaseTenFuncAttr(AttributeList Attrs, StringRef Attr,
diff --git a/llvm/lib/Transforms/IPO/FunctionAttrs.cpp b/llvm/lib/Transforms/IPO/FunctionAttrs.cpp
index a668dde567c016..7ebf265e17ba1f 100644
--- a/llvm/lib/Transforms/IPO/FunctionAttrs.cpp
+++ b/llvm/lib/Transforms/IPO/FunctionAttrs.cpp
@@ -580,14 +580,45 @@ struct ArgumentUsesTracker : public CaptureTracker {
   const SCCNodeSet &SCCNodes;
 };
 
-/// Get all uses of an argument in the function and store them to different
-/// lists: Reads, Writes, and SpecialUses.
-std::tuple<SmallVector<Instruction *, 16>, SmallVector<Instruction *, 16>,
-           SmallVector<Instruction *, 16>>
-getArgumentUses(Argument *A, const SmallPtrSet<Argument *, 8> &SCCNodes) {
+} // end anonymous namespace
+
+namespace llvm {
+
+template <> struct GraphTraits<ArgumentGraphNode *> {
+  using NodeRef = ArgumentGraphNode *;
+  using ChildIteratorType = SmallVectorImpl<ArgumentGraphNode *>::iterator;
+
+  static NodeRef getEntryNode(NodeRef A) { return A; }
+  static ChildIteratorType child_begin(NodeRef N) { return N->Uses.begin(); }
+  static ChildIteratorType child_end(NodeRef N) { return N->Uses.end(); }
+};
+
+template <>
+struct GraphTraits<ArgumentGraph *> : public GraphTraits<ArgumentGraphNode *> {
+  static NodeRef getEntryNode(ArgumentGraph *AG) { return AG->getEntryNode(); }
+
+  static ChildIteratorType nodes_begin(ArgumentGraph *AG) {
+    return AG->begin();
+  }
+
+  static ChildIteratorType nodes_end(ArgumentGraph *AG) { return AG->end(); }
+};
+
+} // end namespace llvm
+
+/// Returns Attribute::None, Attribute::ReadOnly or Attribute::ReadNone.
+static Attribute::AttrKind
+determinePointerAccessAttrs(Argument *A,
+                            const SmallPtrSet<Argument *, 8> &SCCNodes) {
   SmallVector<Use *, 32> Worklist;
   SmallPtrSet<Use *, 32> Visited;
-  SmallVector<Instruction *, 16> Reads, Writes, SpecialUses;
+
+  // inalloca arguments are always clobbered by the call.
+  if (A->hasInAllocaAttr() || A->hasPreallocatedAttr())
+    return Attribute::None;
+
+  bool IsRead = false;
+  bool IsWrite = false;
 
   for (Use &U : A->uses()) {
     Visited.insert(&U);
@@ -595,6 +626,10 @@ getArgumentUses(Argument *A, const SmallPtrSet<Argument *, 8> &SCCNodes) {
   }
 
   while (!Worklist.empty()) {
+    if (IsWrite && IsRead)
+      // No point in searching further..
+      return Attribute::None;
+
     Use *U = Worklist.pop_back_val();
     Instruction *I = cast<Instruction>(U->getUser());
 
@@ -614,7 +649,7 @@ getArgumentUses(Argument *A, const SmallPtrSet<Argument *, 8> &SCCNodes) {
     case Instruction::Invoke: {
       CallBase &CB = cast<CallBase>(*I);
       if (CB.isCallee(U)) {
-        Reads.push_back(I);
+        IsRead = true;
         // Note that indirect calls do not capture, see comment in
         // CaptureTracking for context
         continue;
@@ -633,15 +668,12 @@ getArgumentUses(Argument *A, const SmallPtrSet<Argument *, 8> &SCCNodes) {
           if (Visited.insert(&UU).second)
             Worklist.push_back(&UU);
       } else if (!CB.doesNotCapture(UseIndex)) {
-        if (!CB.onlyReadsMemory()) {
+        if (!CB.onlyReadsMemory())
           // If the callee can save a copy into other memory, then simply
           // scanning uses of the call is insufficient.  We have no way
           // of tracking copies of the pointer through memory to see
-          // if a reloaded copy is written to, thus we treat it as special
-          // uses.
-          SpecialUses.push_back(I);
-          continue;
-        }
+          // if a reloaded copy is written to, thus we must give up.
+          return Attribute::None;
         // Push users for processing once we finish this one
         if (!I->getType()->isVoidTy())
           for (Use &UU : I->uses())
@@ -666,12 +698,12 @@ getArgumentUses(Argument *A, const SmallPtrSet<Argument *, 8> &SCCNodes) {
       if (CB.doesNotAccessMemory(UseIndex)) {
         /* nop */
       } else if (!isModSet(ArgMR) || CB.onlyReadsMemory(UseIndex)) {
-        Reads.push_back(I);
+        IsRead = true;
       } else if (!isRefSet(ArgMR) ||
                  CB.dataOperandHasImpliedAttr(UseIndex, Attribute::WriteOnly)) {
-        Writes.push_back(I);
+        IsWrite = true;
       } else {
-        SpecialUses.push_back(I);
+        return Attribute::None;
       }
       break;
     }
@@ -679,29 +711,23 @@ getArgumentUses(Argument *A, const SmallPtrSet<Argument *, 8> &SCCNodes) {
     case Instruction::Load:
       // A volatile load has side effects beyond what readonly can be relied
       // upon.
-      if (cast<LoadInst>(I)->isVolatile()) {
-        SpecialUses.push_back(I);
-        continue;
-      }
+      if (cast<LoadInst>(I)->isVolatile())
+        return Attribute::None;
 
-      Reads.push_back(I);
+      IsRead = true;
       break;
 
     case Instruction::Store:
-      if (cast<StoreInst>(I)->getValueOperand() == *U) {
+      if (cast<StoreInst>(I)->getValueOperand() == *U)
         // untrackable capture
-        SpecialUses.push_back(I);
-        continue;
-      }
+        return Attribute::None;
 
       // A volatile store has side effects beyond what writeonly can be relied
       // upon.
-      if (cast<StoreInst>(I)->isVolatile()) {
-        SpecialUses.push_back(I);
-        continue;
-      }
+      if (cast<StoreInst>(I)->isVolatile())
+        return Attribute::None;
 
-      Writes.push_back(I);
+      IsWrite = true;
       break;
 
     case Instruction::ICmp:
@@ -709,55 +735,15 @@ getArgumentUses(Argument *A, const SmallPtrSet<Argument *, 8> &SCCNodes) {
       break;
 
     default:
-      SpecialUses.push_back(I);
+      return Attribute::None;
     }
   }
-  return {Reads, Writes, SpecialUses};
-}
-
-} // end anonymous namespace
-
-namespace llvm {
-
-template <> struct GraphTraits<ArgumentGraphNode *> {
-  using NodeRef = ArgumentGraphNode *;
-  using ChildIteratorType = SmallVectorImpl<ArgumentGraphNode *>::iterator;
-
-  static NodeRef getEntryNode(NodeRef A) { return A; }
-  static ChildIteratorType child_begin(NodeRef N) { return N->Uses.begin(); }
-  static ChildIteratorType child_end(NodeRef N) { return N->Uses.end(); }
-};
-
-template <>
-struct GraphTraits<ArgumentGraph *> : public GraphTraits<ArgumentGraphNode *> {
-  static NodeRef getEntryNode(ArgumentGraph *AG) { return AG->getEntryNode(); }
-
-  static ChildIteratorType nodes_begin(ArgumentGraph *AG) {
-    return AG->begin();
-  }
-
-  static ChildIteratorType nodes_end(ArgumentGraph *AG) { return AG->end(); }
-};
-
-} // end namespace llvm
-
-/// Returns Attribute::None, Attribute::ReadOnly or Attribute::ReadNone.
-static Attribute::AttrKind
-determinePointerAccessAttrs(Argument *A, SmallVector<Instruction *, 16> &Reads,
-                            SmallVector<Instruction *, 16> &Writes,
-                            SmallVector<Instruction *, 16> &SpecialUses) {
-  // inalloca arguments are always clobbered by the call.
-  if (A->hasInAllocaAttr() || A->hasPreallocatedAttr())
-    return Attribute::None;
-
-  if (!SpecialUses.empty())
-    return Attribute::None;
 
-  if (!Writes.empty() && !Reads.empty())
+  if (IsWrite && IsRead)
     return Attribute::None;
-  else if (!Reads.empty())
+  else if (IsRead)
     return Attribute::ReadOnly;
-  else if (!Writes.empty())
+  else if (IsWrite)
     return Attribute::WriteOnly;
   else
     return Attribute::ReadNone;
@@ -945,9 +931,7 @@ static void addArgumentAttrs(const SCCNodeSet &SCCNodes,
         // functions in the SCC.
         SmallPtrSet<Argument *, 8> Self;
         Self.insert(&A);
-        auto [Reads, Writes, SpecialUses] = getArgumentUses(&A, Self);
-        Attribute::AttrKind R =
-            determinePointerAccessAttrs(&A, Reads, Writes, SpecialUses);
+        Attribute::AttrKind R = determinePointerAccessAttrs(&A, Self);
         if (R != Attribute::None)
           if (addAccessAttr(&A, R))
             Changed.insert(F);
@@ -979,9 +963,7 @@ static void addArgumentAttrs(const SCCNodeSet &SCCNodes,
         // Infer the access attributes given the new nocapture one
         SmallPtrSet<Argument *, 8> Self;
         Self.insert(&*A);
-        auto [Reads, Writes, SpecialUses] = getArgumentUses(&*A, Self);
-        Attribute::AttrKind R =
-            determinePointerAccessAttrs(&*A, Reads, Writes, SpecialUses);
+        Attribute::AttrKind R = determinePointerAccessAttrs(&*A, Self);
         if (R != Attribute::None)
           addAccessAttr(A, R);
       }
@@ -1050,9 +1032,7 @@ static void addArgumentAttrs(const SCCNodeSet &SCCNodes,
     Attribute::AttrKind AccessAttr = Attribute::ReadNone;
     for (ArgumentGraphNode *N : ArgumentSCC) {
       Argument *A = N->Definition;
-      auto [Reads, Writes, SpecialUses] = getArgumentUses(A, ArgumentSCCNodes);
-      Attribute::AttrKind K =
-          determinePointerAccessAttrs(A, Reads, Writes, SpecialUses);
+      Attribute::AttrKind K = determinePointerAccessAttrs(A, ArgumentSCCNodes);
       AccessAttr = meetAccessAttr(AccessAttr, K);
       if (AccessAttr == Attribute::None)
         break;
diff --git a/llvm/lib/Transforms/Utils/CodeExtractor.cpp b/llvm/lib/Transforms/Utils/CodeExtractor.cpp
index ab2d25c3f17c7d..6928553f0bdd3d 100644
--- a/llvm/lib/Transforms/Utils/CodeExtractor.cpp
+++ b/llvm/lib/Transforms/Utils/CodeExtractor.cpp
@@ -1000,6 +1000,7 @@ Function *CodeExtractor::constructFunction(const ValueSet &inputs,
       case Attribute::Writable:
       case Attribute::DeadOnUnwind:
       case Attribute::Range:
+      case Attribute::Initialized:
       //  These are not really attributes.
       case Attribute::None:
       case Attribute::EndAttrKinds:
diff --git a/llvm/utils/TableGen/Attributes.cpp b/llvm/utils/TableGen/Attributes.cpp
index d9fc7834416cfb..a39b5774d8db4c 100644
--- a/llvm/utils/TableGen/Attributes.cpp
+++ b/llvm/utils/TableGen/Attributes.cpp
@@ -53,7 +53,8 @@ void Attributes::emitTargetIndependentNames(raw_ostream &OS) {
   };
 
   // Emit attribute enums in the same order llvm::Attribute::operator< expects.
-  Emit({"EnumAttr", "TypeAttr", "IntAttr", "ConstantRangeAttr"},
+  Emit({"EnumAttr", "TypeAttr", "IntAttr", "ConstantRangeAttr",
+        "ConstRangeListAttr"},
        "ATTRIBUTE_ENUM");
   Emit({"StrBoolAttr"}, "ATTRIBUTE_STRBOOL");
   Emit({"ComplexStrAttr"}, "ATTRIBUTE_COMPLEXSTR");
@@ -64,8 +65,8 @@ void Attributes::emitTargetIndependentNames(raw_ostream &OS) {
   OS << "#ifdef GET_ATTR_ENUM\n";
   OS << "#undef GET_ATTR_ENUM\n";
   unsigned Value = 1; // Leave zero for AttrKind::None.
-  for (StringRef KindName :
-       {"EnumAttr", "TypeAttr", "IntAttr", "ConstantRangeAttr"}) {
+  for (StringRef KindName : {"EnumAttr", "TypeAttr", "IntAttr",
+                             "ConstantRangeAttr", "ConstRangeListAttr"}) {
     OS << "First" << KindName << " = " << Value << ",\n";
     for (auto *A : Records.getAllDerivedDefinitions(KindName)) {
       OS << A->getName() << " = " << Value << ",\n";
@@ -119,8 +120,8 @@ void Attributes::emitAttributeProperties(raw_ostream &OS) {
   OS << "#ifdef GET_ATTR_PROP_TABLE\n";
   OS << "#undef GET_ATTR_PROP_TABLE\n";
   OS << "static const uint8_t AttrPropTable[] = {\n";
-  for (StringRef KindName :
-       {"EnumAttr", "TypeAttr", "IntAttr", "ConstantRangeAttr"}) {
+  for (StringRef KindName : {"EnumAttr", "TypeAttr", "IntAttr",
+                             "ConstantRangeAttr", "ConstRangeListAttr"}) {
     for (auto *A : Records.getAllDerivedDefinitions(KindName)) {
       OS << "0";
       for (Init *P : *A->getValueAsListInit("Properties"))



More information about the llvm-commits mailing list