[llvm] Add the 'initializes' attribute langref and support (PR #84803)

Haopeng Liu via llvm-commits llvm-commits at lists.llvm.org
Wed Apr 17 15:03:30 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 01/14] 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 02/14] 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 03/14] 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 04/14] 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"))

>From eaf5ca04cb5fc36115b8141fd3dd02da93d66eba Mon Sep 17 00:00:00 2001
From: Haopeng Liu <haopliu at google.com>
Date: Thu, 14 Mar 2024 17:04:48 +0000
Subject: [PATCH 05/14] Update the LangRef (round 2)

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

diff --git a/llvm/docs/LangRef.rst b/llvm/docs/LangRef.rst
index 0d0f21b8d203ef..2b48322ee8f313 100644
--- a/llvm/docs/LangRef.rst
+++ b/llvm/docs/LangRef.rst
@@ -1623,14 +1623,14 @@ Currently, only the following parameter attributes are defined:
 
 ``initialized((Lo1,Hi1),...)``
     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.
+    pointer parameter's memory, ``[%p+LoN, %p+HiN)``. Initialization of memory
+    means the first memory access is a non-volatile, non-atomic write. The
+    write must happen before the function returns. If the function unwinds,
+    the write may not happen.
 
-    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.
+    This attribute only holds for the memory accessed via this pointer
+    parameter. Other arbitrary accesses to the same memory via other pointers
+    are allowed.
 
     Note that this attribute does not apply to the unwind edge: the
     initializing function does not read the pointer before writing to it
@@ -1638,11 +1638,11 @@ Currently, only the following parameter attributes are defined:
     written to when unwinding happens.
 
     The ``writable`` or ``dereferenceable`` attribute does not imply the
-    ``initialized`` attribute, and ``initialized`` does not imply ``writeonly``
+    ``initialized`` attribute. The ``initialized`` does not imply ``writeonly``
     since ``initialized`` allows reading from the pointer after writing.
 
-    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
+    This attribute is a list of constant ranges in ascending order with no
+    overlapping or consecutive list elements. ``LoN/HiN`` are 64-bit ints, and
     negative values are allowed in case the argument points partway into
     an allocation.
 

>From 52cc649f5dd67a1acf6a8a0e7325260f6b0e77ae Mon Sep 17 00:00:00 2001
From: Haopeng Liu <haopliu at google.com>
Date: Thu, 14 Mar 2024 20:19:09 +0000
Subject: [PATCH 06/14] Update FoldingSet.h

---
 llvm/include/llvm/ADT/FoldingSet.h | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/llvm/include/llvm/ADT/FoldingSet.h b/llvm/include/llvm/ADT/FoldingSet.h
index 04d6f9df40ceb0..9e694c106333bf 100644
--- a/llvm/include/llvm/ADT/FoldingSet.h
+++ b/llvm/include/llvm/ADT/FoldingSet.h
@@ -363,7 +363,7 @@ class FoldingSetNodeID {
   }
 
   void AddRanges(const SmallVector<std::pair<int64_t, int64_t>, 16> &Ranges) {
-    unsigned Size = Ranges.size();
+    size_t Size = Ranges.size();
 
     unsigned NumsToInsert = 1 + 2 * Size;
     Bits.reserve(Bits.size() + NumsToInsert);
@@ -371,7 +371,7 @@ class FoldingSetNodeID {
     if (Size == 0)
       return;
 
-    for (const auto &[Start, End] : Ranges) {
+    for (auto [Start, End] : Ranges) {
       Bits.push_back(Start);
       Bits.push_back(End);
     }

>From f7fddb00d399abe65fbb8369b15d79b74899f3a2 Mon Sep 17 00:00:00 2001
From: Haopeng Liu <haopliu at google.com>
Date: Fri, 5 Apr 2024 21:09:27 +0000
Subject: [PATCH 07/14] Change the attribute type to ConstantRangeList

---
 llvm/docs/LangRef.rst                       |   7 +-
 llvm/include/llvm/ADT/FoldingSet.h          |  15 ---
 llvm/include/llvm/AsmParser/LLParser.h      |   5 +-
 llvm/include/llvm/IR/Attributes.h           |  37 ++++---
 llvm/include/llvm/IR/Attributes.td          |  12 +--
 llvm/include/llvm/IR/ConstantRangeList.h    | 108 +++++++++++++++++++
 llvm/lib/AsmParser/LLParser.cpp             | 111 ++++++++------------
 llvm/lib/Bitcode/Reader/BitcodeReader.cpp   |  18 ++--
 llvm/lib/Bitcode/Writer/BitcodeWriter.cpp   |  19 ++--
 llvm/lib/IR/AttributeImpl.h                 |  37 +++----
 llvm/lib/IR/Attributes.cpp                  |  86 ++++++++-------
 llvm/lib/IR/CMakeLists.txt                  |   1 +
 llvm/lib/IR/ConstantRangeList.cpp           |  44 ++++++++
 llvm/lib/IR/LLVMContextImpl.h               |   3 +
 llvm/lib/IR/Verifier.cpp                    |  46 ++++----
 llvm/unittests/IR/CMakeLists.txt            |   1 +
 llvm/unittests/IR/ConstantRangeListTest.cpp |  51 +++++++++
 llvm/utils/TableGen/Attributes.cpp          |   6 +-
 18 files changed, 397 insertions(+), 210 deletions(-)
 create mode 100644 llvm/include/llvm/IR/ConstantRangeList.h
 create mode 100644 llvm/lib/IR/ConstantRangeList.cpp
 create mode 100644 llvm/unittests/IR/ConstantRangeListTest.cpp

diff --git a/llvm/docs/LangRef.rst b/llvm/docs/LangRef.rst
index 2b48322ee8f313..e600fea284eb6e 100644
--- a/llvm/docs/LangRef.rst
+++ b/llvm/docs/LangRef.rst
@@ -1632,11 +1632,6 @@ Currently, only the following parameter attributes are defined:
     parameter. Other arbitrary accesses to the same memory via other pointers
     are allowed.
 
-    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. The ``initialized`` does not imply ``writeonly``
     since ``initialized`` allows reading from the pointer after writing.
@@ -1644,7 +1639,7 @@ Currently, only the following parameter attributes are defined:
     This attribute is a list of constant ranges in ascending order with no
     overlapping or consecutive list elements. ``LoN/HiN`` are 64-bit ints, and
     negative values are allowed in case the argument points partway into
-    an allocation.
+    an allocation. An empty list is not allowed.
 
 ``dead_on_unwind``
     At a high level, this attribute indicates that the pointer argument is dead
diff --git a/llvm/include/llvm/ADT/FoldingSet.h b/llvm/include/llvm/ADT/FoldingSet.h
index 9e694c106333bf..ddc3e52255d6c0 100644
--- a/llvm/include/llvm/ADT/FoldingSet.h
+++ b/llvm/include/llvm/ADT/FoldingSet.h
@@ -362,21 +362,6 @@ class FoldingSetNodeID {
     }
   }
 
-  void AddRanges(const SmallVector<std::pair<int64_t, int64_t>, 16> &Ranges) {
-    size_t Size = Ranges.size();
-
-    unsigned NumsToInsert = 1 + 2 * Size;
-    Bits.reserve(Bits.size() + NumsToInsert);
-    Bits.push_back(Size);
-    if (Size == 0)
-      return;
-
-    for (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 104bf06cf6604d..97a55bc506c850 100644
--- a/llvm/include/llvm/AsmParser/LLParser.h
+++ b/llvm/include/llvm/AsmParser/LLParser.h
@@ -275,7 +275,6 @@ namespace llvm {
       Loc = Lex.getLoc();
       return parseUInt64(Val);
     }
-    bool parseInt64(int64_t &Val);
     bool parseFlag(unsigned &Val);
 
     bool parseStringAttribute(AttrBuilder &B);
@@ -309,9 +308,6 @@ namespace llvm {
     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();
@@ -375,6 +371,7 @@ namespace llvm {
                                     std::vector<unsigned> &FwdRefAttrGrps,
                                     bool inAttrGrp, LocTy &BuiltinLoc);
     bool parseRangeAttr(AttrBuilder &B);
+    bool parseInitializedAttr(AttrBuilder &B);
     bool parseRequiredTypeAttr(AttrBuilder &B, lltok::Kind AttrToken,
                                Attribute::AttrKind AttrKind);
 
diff --git a/llvm/include/llvm/IR/Attributes.h b/llvm/include/llvm/IR/Attributes.h
index b9d479e9b85cb1..f39e59bc24b89f 100644
--- a/llvm/include/llvm/IR/Attributes.h
+++ b/llvm/include/llvm/IR/Attributes.h
@@ -38,6 +38,7 @@ class AttributeImpl;
 class AttributeListImpl;
 class AttributeSetNode;
 class ConstantRange;
+class ConstantRangeList;
 class FoldingSetNodeID;
 class Function;
 class LLVMContext;
@@ -94,8 +95,6 @@ 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;
@@ -109,8 +108,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 isConstantRangeListAttrKind(AttrKind Kind) {
+    return Kind >= FirstConstantRangeListAttr &&
+           Kind <= LastConstantRangeListAttr;
   }
 
   static bool canUseAsFnAttr(AttrKind Kind);
@@ -137,7 +137,7 @@ class Attribute {
   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);
+                       const ConstantRangeList &CRL);
 
   /// Return a uniquified Attribute object that has the specific
   /// alignment set.
@@ -193,12 +193,12 @@ 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;
 
+  /// Return true if the attribute is a ConstantRangeList attribute.
+  bool isConstantRangeListAttribute() const;
+
   /// Return true if the attribute is any kind of attribute.
   bool isValid() const { return pImpl; }
 
@@ -236,9 +236,9 @@ 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;
+  /// Return the attribute's value as a ConstantRangeList. This requires the
+  /// attribute to be a ConstantRangeList attribute.
+  ConstantRangeList getValueAsConstantRangeList() const;
 
   /// Returns the alignment field of an attribute as a byte alignment
   /// value.
@@ -281,6 +281,9 @@ class Attribute {
   /// Returns the value of the range attribute.
   ConstantRange getRange() const;
 
+  /// Returns the value of the initialized attribute.
+  ConstantRangeList getInitialized() const;
+
   /// The Attribute is converted to a string of equivalent mnemonic. This
   /// is, presumably, for writing out the mnemonics for the assembly writer.
   std::string getAsString(bool InAttrGrp = false) const;
@@ -1183,11 +1186,6 @@ 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);
 
@@ -1231,6 +1229,13 @@ class AttrBuilder {
   /// Add range attribute.
   AttrBuilder &addRangeAttr(const ConstantRange &CR);
 
+  /// Add a ConstantRangeList attribute with the given range.
+  AttrBuilder &addConstantRangeListAttr(Attribute::AttrKind Kind,
+                                        const ConstantRangeList &CRL);
+
+  /// Add initialized attribute.
+  AttrBuilder &addInitializedAttr(const ConstantRangeList &CRL);
+
   ArrayRef<Attribute> attrs() const { return Attrs; }
 
   bool operator==(const AttrBuilder &B) const;
diff --git a/llvm/include/llvm/IR/Attributes.td b/llvm/include/llvm/IR/Attributes.td
index 79df429a07ee45..911373020f9541 100644
--- a/llvm/include/llvm/IR/Attributes.td
+++ b/llvm/include/llvm/IR/Attributes.td
@@ -38,9 +38,6 @@ 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, []>;
 
@@ -50,6 +47,9 @@ class ComplexStrAttr<string S, list<AttrProperty> P> : Attr<S, P>;
 /// ConstantRange attribute.
 class ConstantRangeAttr<string S, list<AttrProperty> P> : Attr<S, P>;
 
+/// ConstantRangeList attribute.
+class ConstantRangeListAttr<string S, list<AttrProperty> P> : Attr<S, P>;
+
 /// Target-independent enum attributes.
 
 /// Alignment of parameter (5 bits) stored as log2 of alignment with +1 bias.
@@ -115,6 +115,9 @@ def FnRetThunkExtern : EnumAttr<"fn_ret_thunk_extern", [FnAttr]>;
 /// Pass structure in an alloca.
 def InAlloca : TypeAttr<"inalloca", [ParamAttr]>;
 
+/// Pointer argument memory is initialized.
+def Initialized : ConstantRangeListAttr<"initialized", [ParamAttr]>;
+
 /// Source said inlining was desirable.
 def InlineHint : EnumAttr<"inlinehint", [FnAttr]>;
 
@@ -321,9 +324,6 @@ 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/include/llvm/IR/ConstantRangeList.h b/llvm/include/llvm/IR/ConstantRangeList.h
new file mode 100644
index 00000000000000..f83c73d34afdd7
--- /dev/null
+++ b/llvm/include/llvm/IR/ConstantRangeList.h
@@ -0,0 +1,108 @@
+//===- ConstantRangeList.h - A list of constant range -----------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+//
+// Represent a list of signed ConstantRange and do NOT support wrap around the
+// end of the numeric range. Ranges in the list should have the same bitwidth.
+// Each range's lower should be less than its upper. Special lists (take 8-bit
+// as an example):
+//
+// {[0, 0)}     = Empty set
+// {[255, 255)} = Full Set
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_IR_CONSTANTRANGELIST_H
+#define LLVM_IR_CONSTANTRANGELIST_H
+
+#include "llvm/ADT/APInt.h"
+#include "llvm/IR/ConstantRange.h"
+#include <cstddef>
+#include <cstdint>
+
+namespace llvm {
+
+class raw_ostream;
+
+/// This class represents a list of constant ranges.
+class [[nodiscard]] ConstantRangeList {
+  SmallVector<ConstantRange, 2> Ranges;
+  // Whether the range list is sorted: [i].lower() < [i+1].lower()
+  bool Sorted = true;
+
+public:
+  /// Initialize a full or empty set for the specified bit width.
+  explicit ConstantRangeList(uint32_t BitWidth, bool isFullSet);
+
+  ConstantRangeList(int64_t Lower, int64_t Upper);
+
+  SmallVectorImpl<ConstantRange>::iterator begin() { return Ranges.begin(); }
+  SmallVectorImpl<ConstantRange>::iterator end() { return Ranges.end(); }
+  SmallVectorImpl<ConstantRange>::const_iterator begin() const {
+    return Ranges.begin();
+  }
+  SmallVectorImpl<ConstantRange>::const_iterator end() const {
+    return Ranges.end();
+  }
+  ConstantRange getRange(unsigned i) const {
+    assert(i < Ranges.size());
+    return Ranges[i];
+  }
+
+  /// Return true if this set contains no members.
+  bool isEmptySet() const {
+    return Ranges.size() == 1 && Ranges[0].isEmptySet();
+  }
+
+  /// Return true if this set contains all of the elements possible
+  /// for this data-type.
+  bool isFullSet() const { return Ranges.size() == 1 && Ranges[0].isFullSet(); }
+
+  /// Get the bit width of this ConstantRangeList.
+  uint32_t getBitWidth() const { return Ranges[0].getBitWidth(); }
+
+  size_t size() const { return Ranges.size(); }
+
+  void append(const ConstantRange &Range) {
+    assert(Range.getLower().slt(Range.getUpper()));
+    if (isFullSet())
+      return;
+    if (isEmptySet()) {
+      Ranges[0] = Range;
+      return;
+    }
+    if (Range.getLower().slt(Ranges[size() - 1].getLower()))
+      Sorted = false;
+    Ranges.push_back(Range);
+  }
+
+  void append(int64_t Lower, int64_t Upper) {
+    append(ConstantRange(APInt(64, StringRef(std::to_string(Lower)), 10),
+                         APInt(64, StringRef(std::to_string(Upper)), 10)));
+  }
+
+  /// Return true if this range is equal to another range.
+  bool operator==(const ConstantRangeList &CRL) const {
+    if (size() != CRL.size())
+      return false;
+    for (size_t i = 0; i < size(); ++i) {
+      if (Ranges[i] != CRL.Ranges[i])
+        return false;
+    }
+    return true;
+  }
+  bool operator!=(const ConstantRangeList &CRL) const {
+    return !operator==(CRL);
+  }
+
+  /// Print out the bounds to a stream.
+  void print(raw_ostream &OS) const;
+};
+
+} // end namespace llvm
+
+#endif // LLVM_IR_CONSTANTRANGELIST_H
diff --git a/llvm/lib/AsmParser/LLParser.cpp b/llvm/lib/AsmParser/LLParser.cpp
index b8e8d29696a46a..a834fbe203ff15 100644
--- a/llvm/lib/AsmParser/LLParser.cpp
+++ b/llvm/lib/AsmParser/LLParser.cpp
@@ -25,6 +25,7 @@
 #include "llvm/IR/CallingConv.h"
 #include "llvm/IR/Comdat.h"
 #include "llvm/IR/ConstantRange.h"
+#include "llvm/IR/ConstantRangeList.h"
 #include "llvm/IR/Constants.h"
 #include "llvm/IR/DebugInfoMetadata.h"
 #include "llvm/IR/DerivedTypes.h"
@@ -1566,13 +1567,6 @@ 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))
@@ -1605,6 +1599,8 @@ bool LLParser::parseEnumAttribute(Attribute::AttrKind Attr, AttrBuilder &B,
   }
   case Attribute::Range:
     return parseRangeAttr(B);
+  case Attribute::Initialized:
+    return parseInitializedAttr(B);
   default:
     B.addAttribute(Attr);
     Lex.Lex();
@@ -1857,16 +1853,6 @@ 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'
@@ -2381,54 +2367,6 @@ 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;
@@ -3116,6 +3054,49 @@ bool LLParser::parseRangeAttr(AttrBuilder &B) {
   return false;
 }
 
+/// parseInitializedAttr
+///   ::= initialized((Lo1,Hi1),(Lo2,Hi2),...)
+bool LLParser::parseInitializedAttr(AttrBuilder &B) {
+  Lex.Lex();
+
+  auto ParseAPSInt = [&](APInt &Val) {
+    if (Lex.getKind() != lltok::APSInt)
+      return tokError("expected integer");
+    Val = Lex.getAPSIntVal().extend(64);
+    Lex.Lex();
+    return false;
+  };
+
+  if (parseToken(lltok::lparen, "expected '('"))
+    return true;
+
+  ConstantRangeList CRL(64, false);
+  // Parse each constant range.
+  do {
+    APInt Lower, Upper;
+    if (parseToken(lltok::lparen, "expected'('"))
+      return true;
+
+    if (ParseAPSInt(Lower) || parseToken(lltok::comma, "expected ','") ||
+        ParseAPSInt(Upper))
+      return true;
+
+    if (Lower == Upper)
+      return tokError("the range should not represent the full or empty set!");
+
+    if (parseToken(lltok::rparen, "expected ')'"))
+      return true;
+
+    CRL.append(ConstantRange(Lower, Upper));
+  } while (EatIfPresent(lltok::comma));
+
+  if (parseToken(lltok::rparen, "expected ')'"))
+    return true;
+
+  B.addInitializedAttr(CRL);
+  return false;
+}
+
 /// parseOptionalOperandBundles
 ///    ::= /*empty*/
 ///    ::= '[' OperandBundle [, OperandBundle ]* ']'
diff --git a/llvm/lib/Bitcode/Reader/BitcodeReader.cpp b/llvm/lib/Bitcode/Reader/BitcodeReader.cpp
index 45dd33f8884492..b67bccdd2582ea 100644
--- a/llvm/lib/Bitcode/Reader/BitcodeReader.cpp
+++ b/llvm/lib/Bitcode/Reader/BitcodeReader.cpp
@@ -30,6 +30,7 @@
 #include "llvm/IR/CallingConv.h"
 #include "llvm/IR/Comdat.h"
 #include "llvm/IR/Constant.h"
+#include "llvm/IR/ConstantRangeList.h"
 #include "llvm/IR/Constants.h"
 #include "llvm/IR/DataLayout.h"
 #include "llvm/IR/DebugInfo.h"
@@ -2315,22 +2316,21 @@ Error BitcodeReader::parseAttributeGroupBlock() {
           i--;
 
           B.addConstantRangeAttr(Kind, MaybeCR.get());
-        } else if (Record[i] == 8 || Record[i] == 9) {
+        } else if (Record[i] == 8) {
           Attribute::AttrKind Kind;
           if (Error Err = parseAttrKind(Record[++i], &Kind))
             return Err;
-          if (!Attribute::isConstRangeListAttrKind(Kind))
-            return error("Not a const range list attribute");
+          if (!Attribute::isConstantRangeListAttrKind(Kind))
+            return error("Not a constant range list attribute");
 
-          SmallVector<std::pair<int64_t, int64_t>, 16> Ranges;
+          ConstantRangeList CRL(64, false);
           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));
+            int64_t Start = BitcodeReader::decodeSignRotatedValue(Record[++i]);
+            int64_t End = BitcodeReader::decodeSignRotatedValue(Record[++i]);
+            CRL.append(Start, End);
           }
-
-          B.addConstRangeListAttr(Kind, Ranges);
+          B.addConstantRangeListAttr(Kind, CRL);
         } else {
           return error("Invalid attribute group entry");
         }
diff --git a/llvm/lib/Bitcode/Writer/BitcodeWriter.cpp b/llvm/lib/Bitcode/Writer/BitcodeWriter.cpp
index 027a9bf68abcbc..cae78d5f0fd797 100644
--- a/llvm/lib/Bitcode/Writer/BitcodeWriter.cpp
+++ b/llvm/lib/Bitcode/Writer/BitcodeWriter.cpp
@@ -33,6 +33,7 @@
 #include "llvm/IR/BasicBlock.h"
 #include "llvm/IR/Comdat.h"
 #include "llvm/IR/Constant.h"
+#include "llvm/IR/ConstantRangeList.h"
 #include "llvm/IR/Constants.h"
 #include "llvm/IR/DebugInfoMetadata.h"
 #include "llvm/IR/DebugLoc.h"
@@ -933,22 +934,18 @@ void ModuleBitcodeWriter::writeAttributeGroupTable() {
         if (Ty)
           Record.push_back(VE.getTypeID(Attr.getValueAsType()));
       } 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);
+        assert(Attr.isConstantRangeListAttribute());
+        Record.push_back(8);
         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);
-          }
+        ConstantRangeList CRL = Attr.getValueAsConstantRangeList();
+        Record.push_back(CRL.size());
+        for (auto &CR : CRL) {
+          emitSignedInt64(Record, CR.getLower().getSExtValue());
+          emitSignedInt64(Record, CR.getUpper().getSExtValue());
         }
       }
     }
diff --git a/llvm/lib/IR/AttributeImpl.h b/llvm/lib/IR/AttributeImpl.h
index 0c4db637a49aca..2fae149e44edac 100644
--- a/llvm/lib/IR/AttributeImpl.h
+++ b/llvm/lib/IR/AttributeImpl.h
@@ -21,6 +21,7 @@
 #include "llvm/ADT/StringRef.h"
 #include "llvm/IR/Attributes.h"
 #include "llvm/IR/ConstantRange.h"
+#include "llvm/IR/ConstantRangeList.h"
 #include "llvm/Support/TrailingObjects.h"
 #include <cassert>
 #include <cstddef>
@@ -48,7 +49,7 @@ class AttributeImpl : public FoldingSetNode {
     StringAttrEntry,
     TypeAttrEntry,
     ConstantRangeAttrEntry,
-    ConstRangeListAttrEntry,
+    ConstantRangeListAttrEntry,
   };
 
   AttributeImpl(AttrEntryKind KindID) : KindID(KindID) {}
@@ -65,8 +66,8 @@ class AttributeImpl : public FoldingSetNode {
   bool isConstantRangeAttribute() const {
     return KindID == ConstantRangeAttrEntry;
   }
-  bool isConstRangeListAttribute() const {
-    return KindID == ConstRangeListAttrEntry;
+  bool isConstantRangeListAttribute() const {
+    return KindID == ConstantRangeListAttrEntry;
   }
 
   bool hasAttribute(Attribute::AttrKind A) const;
@@ -83,7 +84,7 @@ class AttributeImpl : public FoldingSetNode {
 
   ConstantRange getValueAsConstantRange() const;
 
-  SmallVector<std::pair<int64_t, int64_t>, 16> getValueAsRanges() const;
+  ConstantRangeList getValueAsConstantRangeList() const;
 
   /// Used when sorting the attributes.
   bool operator<(const AttributeImpl &AI) const;
@@ -100,7 +101,7 @@ class AttributeImpl : public FoldingSetNode {
     else if (isConstantRangeAttribute())
       Profile(ID, getKindAsEnum(), getValueAsConstantRange());
     else
-      Profile(ID, getKindAsEnum(), getValueAsRanges());
+      Profile(ID, getKindAsEnum(), getValueAsConstantRangeList());
   }
 
   static void Profile(FoldingSetNodeID &ID, Attribute::AttrKind Kind) {
@@ -133,11 +134,14 @@ class AttributeImpl : public FoldingSetNode {
     ID.AddInteger(CR.getUpper());
   }
 
-  static void
-  Profile(FoldingSetNodeID &ID, Attribute::AttrKind Kind,
-          const SmallVector<std::pair<int64_t, int64_t>, 16> &Ranges) {
+  static void Profile(FoldingSetNodeID &ID, Attribute::AttrKind Kind,
+                      const ConstantRangeList &CRL) {
     ID.AddInteger(Kind);
-    ID.AddRanges(Ranges);
+    ID.AddInteger(CRL.size());
+    for (auto &CR : CRL) {
+      ID.AddInteger(CR.getLower());
+      ID.AddInteger(CR.getUpper());
+    }
   }
 };
 
@@ -237,18 +241,15 @@ class ConstantRangeAttributeImpl : public EnumAttributeImpl {
   ConstantRange getConstantRangeValue() const { return CR; }
 };
 
-class ConstRangeListAttributeImpl : public EnumAttributeImpl {
-  SmallVector<std::pair<int64_t, int64_t>, 16> Ranges;
+class ConstantRangeListAttributeImpl : public EnumAttributeImpl {
+  ConstantRangeList CRL;
 
 public:
-  ConstRangeListAttributeImpl(
-      Attribute::AttrKind Kind,
-      SmallVector<std::pair<int64_t, int64_t>, 16> &Ranges)
-      : EnumAttributeImpl(ConstRangeListAttrEntry, Kind), Ranges(Ranges) {}
+  ConstantRangeListAttributeImpl(Attribute::AttrKind Kind,
+                                 const ConstantRangeList &CRL)
+      : EnumAttributeImpl(ConstantRangeListAttrEntry, Kind), CRL(CRL) {}
 
-  SmallVector<std::pair<int64_t, int64_t>, 16> getRangesValue() const {
-    return Ranges;
-  }
+  ConstantRangeList getConstantRangeListValue() const { return CRL; }
 };
 
 class AttributeBitSet {
diff --git a/llvm/lib/IR/Attributes.cpp b/llvm/lib/IR/Attributes.cpp
index f7d99de908730c..5f0283ba76d217 100644
--- a/llvm/lib/IR/Attributes.cpp
+++ b/llvm/lib/IR/Attributes.cpp
@@ -25,6 +25,7 @@
 #include "llvm/Config/llvm-config.h"
 #include "llvm/IR/AttributeMask.h"
 #include "llvm/IR/ConstantRange.h"
+#include "llvm/IR/ConstantRangeList.h"
 #include "llvm/IR/Function.h"
 #include "llvm/IR/LLVMContext.h"
 #include "llvm/IR/Type.h"
@@ -192,13 +193,17 @@ Attribute Attribute::get(LLVMContext &Context, Attribute::AttrKind Kind,
 }
 
 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");
+                         const ConstantRangeList &CRL) {
+  assert(Attribute::isConstantRangeListAttrKind(Kind) &&
+         "Not a ConstantRangeList attribute");
   LLVMContextImpl *pImpl = Context.pImpl;
   FoldingSetNodeID ID;
   ID.AddInteger(Kind);
-  ID.AddRanges(Ranges);
+  ID.AddInteger(CRL.size());
+  for (auto &CR : CRL) {
+    ID.AddInteger(CR.getLower());
+    ID.AddInteger(CR.getUpper());
+  }
 
   void *InsertPoint;
   AttributeImpl *PA = pImpl->AttrsSet.FindNodeOrInsertPos(ID, InsertPoint);
@@ -206,7 +211,8 @@ Attribute Attribute::get(LLVMContext &Context, Attribute::AttrKind Kind,
   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);
+    PA = new (pImpl->ConstantRangeListAttributeAlloc.Allocate())
+        ConstantRangeListAttributeImpl(Kind, CRL);
     pImpl->AttrsSet.InsertNode(PA, InsertPoint);
   }
 
@@ -340,14 +346,14 @@ bool Attribute::isConstantRangeAttribute() const {
   return pImpl && pImpl->isConstantRangeAttribute();
 }
 
-bool Attribute::isConstRangeListAttribute() const {
-  return pImpl && pImpl->isConstRangeListAttribute();
+bool Attribute::isConstantRangeListAttribute() const {
+  return pImpl && pImpl->isConstantRangeListAttribute();
 }
 
 Attribute::AttrKind Attribute::getKindAsEnum() const {
   if (!pImpl) return None;
   assert((isEnumAttribute() || isIntAttribute() || isTypeAttribute() ||
-          isConstantRangeAttribute() || isConstRangeListAttribute()) &&
+          isConstantRangeAttribute() || isConstantRangeListAttribute()) &&
          "Invalid attribute type to get the kind as an enum!");
   return pImpl->getKindAsEnum();
 }
@@ -393,13 +399,10 @@ 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();
+ConstantRangeList Attribute::getValueAsConstantRangeList() const {
+  assert(isConstantRangeListAttribute() &&
+         "Invalid attribute type to get the value as a ConstantRangeList!");
+  return pImpl->getValueAsConstantRangeList();
 }
 
 bool Attribute::hasAttribute(AttrKind Kind) const {
@@ -486,6 +489,12 @@ ConstantRange Attribute::getRange() const {
   return pImpl->getValueAsConstantRange();
 }
 
+ConstantRangeList Attribute::getInitialized() const {
+  assert(hasAttribute(Attribute::Initialized) &&
+         "Trying to get initialized attr from non-ConstantRangeList attribute");
+  return pImpl->getValueAsConstantRangeList();
+}
+
 static const char *getModRefStr(ModRefInfo MR) {
   switch (MR) {
   case ModRefInfo::NoModRef:
@@ -653,19 +662,19 @@ std::string Attribute::getAsString(bool InAttrGrp) const {
   }
 
   if (hasAttribute(Attribute::Initialized)) {
-    auto Ranges = getValueAsRanges();
-    if (Ranges.empty())
-      return "";
-
-    std::string Result = "initialized(";
+    std::string Result;
     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)
+    ConstantRangeList CRL = getValueAsConstantRangeList();
+    OS << "initialized(";
+    size_t i = 0;
+    for (auto &CR : CRL) {
+      OS << "(" << CR.getLower() << "," << CR.getUpper() << ")";
+      if (i != CRL.size() - 1)
         OS << ",";
+      i++;
     }
     OS << ")";
+    OS.flush();
     return Result;
   }
 
@@ -759,7 +768,7 @@ bool AttributeImpl::hasAttribute(StringRef Kind) const {
 
 Attribute::AttrKind AttributeImpl::getKindAsEnum() const {
   assert(isEnumAttribute() || isIntAttribute() || isTypeAttribute() ||
-         isConstantRangeAttribute() || isConstRangeListAttribute());
+         isConstantRangeAttribute() || isConstantRangeListAttribute());
   return static_cast<const EnumAttributeImpl *>(this)->getEnumKind();
 }
 
@@ -794,11 +803,10 @@ 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();
+ConstantRangeList AttributeImpl::getValueAsConstantRangeList() const {
+  assert(isConstantRangeListAttribute());
+  return static_cast<const ConstantRangeListAttributeImpl *>(this)
+      ->getConstantRangeListValue();
 }
 
 bool AttributeImpl::operator<(const AttributeImpl &AI) const {
@@ -815,6 +823,8 @@ bool AttributeImpl::operator<(const AttributeImpl &AI) const {
     assert(!AI.isEnumAttribute() && "Non-unique attribute");
     assert(!AI.isTypeAttribute() && "Comparison of types would be unstable");
     assert(!AI.isConstantRangeAttribute() && "Unclear how to compare ranges");
+    assert(!AI.isConstantRangeListAttribute() &&
+           "Unclear how to compare range list");
     // TODO: Is this actually needed?
     assert(AI.isIntAttribute() && "Only possibility left");
     return getValueAsInt() < AI.getValueAsInt();
@@ -2008,16 +2018,20 @@ 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);
 }
 
+AttrBuilder &
+AttrBuilder::addConstantRangeListAttr(Attribute::AttrKind Kind,
+                                      const ConstantRangeList &CRL) {
+  return addAttribute(Attribute::get(Ctx, Kind, CRL));
+}
+
+AttrBuilder &AttrBuilder::addInitializedAttr(const ConstantRangeList &CRL) {
+  return addConstantRangeListAttr(Attribute::Initialized, CRL);
+}
+
 AttrBuilder &AttrBuilder::merge(const AttrBuilder &B) {
   // TODO: Could make this O(n) as we're merging two sorted lists.
   for (const auto &I : B.attrs())
diff --git a/llvm/lib/IR/CMakeLists.txt b/llvm/lib/IR/CMakeLists.txt
index f1668ee3be63b5..f8de1aa28044bd 100644
--- a/llvm/lib/IR/CMakeLists.txt
+++ b/llvm/lib/IR/CMakeLists.txt
@@ -9,6 +9,7 @@ add_llvm_component_library(LLVMCore
   Comdat.cpp
   ConstantFold.cpp
   ConstantRange.cpp
+  ConstantRangeList.cpp
   Constants.cpp
   ConvergenceVerifier.cpp
   Core.cpp
diff --git a/llvm/lib/IR/ConstantRangeList.cpp b/llvm/lib/IR/ConstantRangeList.cpp
new file mode 100644
index 00000000000000..96304ade997d35
--- /dev/null
+++ b/llvm/lib/IR/ConstantRangeList.cpp
@@ -0,0 +1,44 @@
+//===- ConstantRangeList.cpp - ConstantRangeList implementation -----------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+//
+// Represent a list of signed ConstantRange and do NOT support wrap around the
+// end of the numeric range. Ranges in the list should have the same bitwidth.
+// Each range's lower should be less than its upper. Special lists (take 8-bit
+// as an example):
+//
+// {[0, 0)}     = Empty set
+// {[255, 255)} = Full Set
+//
+//===----------------------------------------------------------------------===//
+
+#include "llvm/IR/ConstantRangeList.h"
+#include <cstddef>
+
+using namespace llvm;
+
+ConstantRangeList::ConstantRangeList(uint32_t BitWidth, bool Full) {
+  APInt Lower =
+      Full ? APInt::getMaxValue(BitWidth) : APInt::getMinValue(BitWidth);
+  Ranges.push_back(ConstantRange(Lower, Lower));
+}
+
+ConstantRangeList::ConstantRangeList(int64_t Lower, int64_t Upper) {
+  Ranges.push_back(
+      ConstantRange(APInt(64, StringRef(std::to_string(Lower)), 10),
+                    APInt(64, StringRef(std::to_string(Upper)), 10)));
+}
+
+void ConstantRangeList::print(raw_ostream &OS) const {
+  if (isFullSet())
+    OS << "full-set";
+  else if (isEmptySet())
+    OS << "empty-set";
+  else
+    for (const auto &Range : Ranges)
+      Range.print(OS);
+}
diff --git a/llvm/lib/IR/LLVMContextImpl.h b/llvm/lib/IR/LLVMContextImpl.h
index 547a02a6490ea5..04546b080babe7 100644
--- a/llvm/lib/IR/LLVMContextImpl.h
+++ b/llvm/lib/IR/LLVMContextImpl.h
@@ -57,6 +57,7 @@ class AttributeListImpl;
 class AttributeSetNode;
 class BasicBlock;
 class ConstantRangeAttributeImpl;
+class ConstantRangeListAttributeImpl;
 struct DiagnosticHandler;
 class DPMarker;
 class ElementCount;
@@ -1565,6 +1566,8 @@ class LLVMContextImpl {
   UniqueStringSaver Saver{Alloc};
   SpecificBumpPtrAllocator<ConstantRangeAttributeImpl>
       ConstantRangeAttributeAlloc;
+  SpecificBumpPtrAllocator<ConstantRangeListAttributeImpl>
+      ConstantRangeListAttributeAlloc;
 
   DenseMap<unsigned, IntegerType *> IntegerTypes;
 
diff --git a/llvm/lib/IR/Verifier.cpp b/llvm/lib/IR/Verifier.cpp
index c4946a54f7bfef..fcdbc2d9108657 100644
--- a/llvm/lib/IR/Verifier.cpp
+++ b/llvm/lib/IR/Verifier.cpp
@@ -72,6 +72,7 @@
 #include "llvm/IR/Comdat.h"
 #include "llvm/IR/Constant.h"
 #include "llvm/IR/ConstantRange.h"
+#include "llvm/IR/ConstantRangeList.h"
 #include "llvm/IR/Constants.h"
 #include "llvm/IR/ConvergenceVerifier.h"
 #include "llvm/IR/DataLayout.h"
@@ -1993,14 +1994,6 @@ 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() &&
@@ -2040,6 +2033,30 @@ void Verifier::verifyParameterAttrs(AttributeSet Attrs, Type *Ty,
     }
   }
 
+  if (Attrs.hasAttribute(Attribute::Initialized)) {
+    auto Inits = Attrs.getAttribute(Attribute::Initialized)
+                     .getValueAsConstantRangeList();
+    Check(!Inits.isEmptySet(),
+          "Attribute 'initialized' does not support empty list", V);
+    Check(!Inits.isFullSet(),
+          "Attribute 'initialized' does not support full list", V);
+
+    Check(Inits.getRange(0).getLower().slt(Inits.getRange(0).getUpper()),
+          "Attribute 'initialized' requires interval lower less than upper", V);
+    for (size_t i = 1; i < Inits.size(); i++) {
+      auto Previous = Inits.getRange(i - 1);
+      auto Current = Inits.getRange(i);
+      Check(Current.getLower().slt(Current.getUpper()),
+            "Attribute 'initialized' requires interval lower less than upper",
+            V);
+      Check(Current.getLower().sge(Previous.getLower()),
+            "Attribute 'initialized' requires intervals in ascending order!",
+            V);
+      Check(Current.getLower().sge(Previous.getUpper()),
+            "Attribute 'initialized' requires intervals merged!", V);
+    }
+  }
+
   if (Attrs.hasAttribute(Attribute::NoFPClass)) {
     uint64_t Val = Attrs.getAttribute(Attribute::NoFPClass).getValueAsInt();
     Check(Val != 0, "Attribute 'nofpclass' must have at least one test bit set",
@@ -2052,19 +2069,6 @@ 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/unittests/IR/CMakeLists.txt b/llvm/unittests/IR/CMakeLists.txt
index 803164b8f1eac6..4ee95f9c7ae3ce 100644
--- a/llvm/unittests/IR/CMakeLists.txt
+++ b/llvm/unittests/IR/CMakeLists.txt
@@ -17,6 +17,7 @@ add_llvm_unittest(IRTests
   BasicBlockDbgInfoTest.cpp
   CFGBuilder.cpp
   ConstantRangeTest.cpp
+  ConstantRangeListTest.cpp
   ConstantsTest.cpp
   DataLayoutTest.cpp
   DebugInfoTest.cpp
diff --git a/llvm/unittests/IR/ConstantRangeListTest.cpp b/llvm/unittests/IR/ConstantRangeListTest.cpp
new file mode 100644
index 00000000000000..db262a508a84ac
--- /dev/null
+++ b/llvm/unittests/IR/ConstantRangeListTest.cpp
@@ -0,0 +1,51 @@
+//===- ConstantRangeListTest.cpp - ConstantRangeList tests ----------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "llvm/IR/ConstantRangeList.h"
+#include "gtest/gtest.h"
+
+using namespace llvm;
+
+namespace {
+
+class ConstantRangeListTest : public ::testing::Test {
+public:
+  ConstantRangeList
+  GetCRL(SmallVectorImpl<std::pair<int64_t, int64_t>> &Ranges) {
+    ConstantRangeList Result(64, false);
+    for (const auto &[Start, End] : Ranges) {
+      Result.append(Start, End);
+    }
+    return Result;
+  }
+
+protected:
+  static ConstantRangeList Full;
+  static ConstantRangeList Empty;
+};
+
+ConstantRangeList ConstantRangeListTest::Full(64, true);
+ConstantRangeList ConstantRangeListTest::Empty(64, false);
+
+TEST_F(ConstantRangeListTest, Basics) {
+  EXPECT_TRUE(Full.isFullSet());
+  EXPECT_FALSE(Full.isEmptySet());
+
+  EXPECT_FALSE(Empty.isFullSet());
+  EXPECT_TRUE(Empty.isEmptySet());
+
+  ConstantRangeList Some1 = GetCRL({(0, 4), (8, 12)});
+  EXPECT_FALSE(Some1.isFullSet());
+  EXPECT_FALSE(Some1.isEmptySet());
+
+  ConstantRangeList Some2 = GetCRL({(0, 4), (8, 12)});
+  ConstantRangeList Some3 = GetCRL({(-4, 0), (8, 12)});
+  EXPECT_TRUE(Some1 == Some2 && Some1 != Some3);
+}
+
+} // anonymous namespace
diff --git a/llvm/utils/TableGen/Attributes.cpp b/llvm/utils/TableGen/Attributes.cpp
index a39b5774d8db4c..c50316985ef098 100644
--- a/llvm/utils/TableGen/Attributes.cpp
+++ b/llvm/utils/TableGen/Attributes.cpp
@@ -54,7 +54,7 @@ void Attributes::emitTargetIndependentNames(raw_ostream &OS) {
 
   // Emit attribute enums in the same order llvm::Attribute::operator< expects.
   Emit({"EnumAttr", "TypeAttr", "IntAttr", "ConstantRangeAttr",
-        "ConstRangeListAttr"},
+        "ConstantRangeListAttr"},
        "ATTRIBUTE_ENUM");
   Emit({"StrBoolAttr"}, "ATTRIBUTE_STRBOOL");
   Emit({"ComplexStrAttr"}, "ATTRIBUTE_COMPLEXSTR");
@@ -66,7 +66,7 @@ void Attributes::emitTargetIndependentNames(raw_ostream &OS) {
   OS << "#undef GET_ATTR_ENUM\n";
   unsigned Value = 1; // Leave zero for AttrKind::None.
   for (StringRef KindName : {"EnumAttr", "TypeAttr", "IntAttr",
-                             "ConstantRangeAttr", "ConstRangeListAttr"}) {
+                             "ConstantRangeAttr", "ConstantRangeListAttr"}) {
     OS << "First" << KindName << " = " << Value << ",\n";
     for (auto *A : Records.getAllDerivedDefinitions(KindName)) {
       OS << A->getName() << " = " << Value << ",\n";
@@ -121,7 +121,7 @@ void Attributes::emitAttributeProperties(raw_ostream &OS) {
   OS << "#undef GET_ATTR_PROP_TABLE\n";
   OS << "static const uint8_t AttrPropTable[] = {\n";
   for (StringRef KindName : {"EnumAttr", "TypeAttr", "IntAttr",
-                             "ConstantRangeAttr", "ConstRangeListAttr"}) {
+                             "ConstantRangeAttr", "ConstantRangeListAttr"}) {
     for (auto *A : Records.getAllDerivedDefinitions(KindName)) {
       OS << "0";
       for (Init *P : *A->getValueAsListInit("Properties"))

>From 3b5955a7e0e75e26e69180a06dfadd6caec0a489 Mon Sep 17 00:00:00 2001
From: Haopeng Liu <haopliu at google.com>
Date: Fri, 5 Apr 2024 21:41:15 +0000
Subject: [PATCH 08/14] Rename attr to initializes

---
 llvm/docs/LangRef.rst                       |  6 ++--
 llvm/include/llvm/AsmParser/LLParser.h      |  3 +-
 llvm/include/llvm/Bitcode/LLVMBitCodes.h    |  2 +-
 llvm/include/llvm/IR/Attributes.h           |  8 +++---
 llvm/include/llvm/IR/Attributes.td          |  2 +-
 llvm/lib/AsmParser/LLParser.cpp             | 12 ++++----
 llvm/lib/Bitcode/Reader/BitcodeReader.cpp   |  4 +--
 llvm/lib/Bitcode/Writer/BitcodeWriter.cpp   |  4 +--
 llvm/lib/IR/Attributes.cpp                  | 16 +++++------
 llvm/lib/IR/Verifier.cpp                    | 16 +++++------
 llvm/lib/Transforms/Utils/CodeExtractor.cpp |  2 +-
 llvm/unittests/IR/ConstantRangeListTest.cpp | 32 ++++++++++-----------
 12 files changed, 52 insertions(+), 55 deletions(-)

diff --git a/llvm/docs/LangRef.rst b/llvm/docs/LangRef.rst
index e600fea284eb6e..3f7cef4f6493ec 100644
--- a/llvm/docs/LangRef.rst
+++ b/llvm/docs/LangRef.rst
@@ -1621,7 +1621,7 @@ Currently, only the following parameter attributes are defined:
     ``readonly`` or a ``memory`` attribute that does not contain
     ``argmem: write``.
 
-``initialized((Lo1,Hi1),...)``
+``initializes((Lo1,Hi1),...)``
     This attribute indicates that the function initializes the ranges of the
     pointer parameter's memory, ``[%p+LoN, %p+HiN)``. Initialization of memory
     means the first memory access is a non-volatile, non-atomic write. The
@@ -1633,8 +1633,8 @@ Currently, only the following parameter attributes are defined:
     are allowed.
 
     The ``writable`` or ``dereferenceable`` attribute does not imply the
-    ``initialized`` attribute. The ``initialized`` does not imply ``writeonly``
-    since ``initialized`` allows reading from the pointer after writing.
+    ``initializes`` attribute. The ``initializes`` does not imply ``writeonly``
+    since ``initializes`` allows reading from the pointer after writing.
 
     This attribute is a list of constant ranges in ascending order with no
     overlapping or consecutive list elements. ``LoN/HiN`` are 64-bit ints, and
diff --git a/llvm/include/llvm/AsmParser/LLParser.h b/llvm/include/llvm/AsmParser/LLParser.h
index 97a55bc506c850..24dad3157debe2 100644
--- a/llvm/include/llvm/AsmParser/LLParser.h
+++ b/llvm/include/llvm/AsmParser/LLParser.h
@@ -307,7 +307,6 @@ 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 parseOptionalUWTableKind(UWTableKind &Kind);
     bool parseAllocKind(AllocFnKind &Kind);
     std::optional<MemoryEffects> parseMemoryAttr();
@@ -371,7 +370,7 @@ namespace llvm {
                                     std::vector<unsigned> &FwdRefAttrGrps,
                                     bool inAttrGrp, LocTy &BuiltinLoc);
     bool parseRangeAttr(AttrBuilder &B);
-    bool parseInitializedAttr(AttrBuilder &B);
+    bool parseInitializesAttr(AttrBuilder &B);
     bool parseRequiredTypeAttr(AttrBuilder &B, lltok::Kind AttrToken,
                                Attribute::AttrKind AttrKind);
 
diff --git a/llvm/include/llvm/Bitcode/LLVMBitCodes.h b/llvm/include/llvm/Bitcode/LLVMBitCodes.h
index b2f38716b86f05..b925fe68072d23 100644
--- a/llvm/include/llvm/Bitcode/LLVMBitCodes.h
+++ b/llvm/include/llvm/Bitcode/LLVMBitCodes.h
@@ -725,7 +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,
+  ATTR_KIND_INITIALIZES = 93,
 };
 
 enum ComdatSelectionKindCodes {
diff --git a/llvm/include/llvm/IR/Attributes.h b/llvm/include/llvm/IR/Attributes.h
index f39e59bc24b89f..cb579746aa526c 100644
--- a/llvm/include/llvm/IR/Attributes.h
+++ b/llvm/include/llvm/IR/Attributes.h
@@ -281,8 +281,8 @@ class Attribute {
   /// Returns the value of the range attribute.
   ConstantRange getRange() const;
 
-  /// Returns the value of the initialized attribute.
-  ConstantRangeList getInitialized() const;
+  /// Returns the value of the initializes attribute.
+  ConstantRangeList getInitializes() const;
 
   /// The Attribute is converted to a string of equivalent mnemonic. This
   /// is, presumably, for writing out the mnemonics for the assembly writer.
@@ -1233,8 +1233,8 @@ class AttrBuilder {
   AttrBuilder &addConstantRangeListAttr(Attribute::AttrKind Kind,
                                         const ConstantRangeList &CRL);
 
-  /// Add initialized attribute.
-  AttrBuilder &addInitializedAttr(const ConstantRangeList &CRL);
+  /// Add initializes attribute.
+  AttrBuilder &addInitializesAttr(const ConstantRangeList &CRL);
 
   ArrayRef<Attribute> attrs() const { return Attrs; }
 
diff --git a/llvm/include/llvm/IR/Attributes.td b/llvm/include/llvm/IR/Attributes.td
index 911373020f9541..f35ea40b5e94c6 100644
--- a/llvm/include/llvm/IR/Attributes.td
+++ b/llvm/include/llvm/IR/Attributes.td
@@ -116,7 +116,7 @@ def FnRetThunkExtern : EnumAttr<"fn_ret_thunk_extern", [FnAttr]>;
 def InAlloca : TypeAttr<"inalloca", [ParamAttr]>;
 
 /// Pointer argument memory is initialized.
-def Initialized : ConstantRangeListAttr<"initialized", [ParamAttr]>;
+def Initializes : ConstantRangeListAttr<"initializes", [ParamAttr]>;
 
 /// Source said inlining was desirable.
 def InlineHint : EnumAttr<"inlinehint", [FnAttr]>;
diff --git a/llvm/lib/AsmParser/LLParser.cpp b/llvm/lib/AsmParser/LLParser.cpp
index a834fbe203ff15..28af4968095786 100644
--- a/llvm/lib/AsmParser/LLParser.cpp
+++ b/llvm/lib/AsmParser/LLParser.cpp
@@ -1599,8 +1599,8 @@ bool LLParser::parseEnumAttribute(Attribute::AttrKind Attr, AttrBuilder &B,
   }
   case Attribute::Range:
     return parseRangeAttr(B);
-  case Attribute::Initialized:
-    return parseInitializedAttr(B);
+  case Attribute::Initializes:
+    return parseInitializesAttr(B);
   default:
     B.addAttribute(Attr);
     Lex.Lex();
@@ -3054,9 +3054,9 @@ bool LLParser::parseRangeAttr(AttrBuilder &B) {
   return false;
 }
 
-/// parseInitializedAttr
-///   ::= initialized((Lo1,Hi1),(Lo2,Hi2),...)
-bool LLParser::parseInitializedAttr(AttrBuilder &B) {
+/// parseInitializesAttr
+///   ::= initializes((Lo1,Hi1),(Lo2,Hi2),...)
+bool LLParser::parseInitializesAttr(AttrBuilder &B) {
   Lex.Lex();
 
   auto ParseAPSInt = [&](APInt &Val) {
@@ -3093,7 +3093,7 @@ bool LLParser::parseInitializedAttr(AttrBuilder &B) {
   if (parseToken(lltok::rparen, "expected ')'"))
     return true;
 
-  B.addInitializedAttr(CRL);
+  B.addInitializesAttr(CRL);
   return false;
 }
 
diff --git a/llvm/lib/Bitcode/Reader/BitcodeReader.cpp b/llvm/lib/Bitcode/Reader/BitcodeReader.cpp
index b67bccdd2582ea..41bfb92ac211a5 100644
--- a/llvm/lib/Bitcode/Reader/BitcodeReader.cpp
+++ b/llvm/lib/Bitcode/Reader/BitcodeReader.cpp
@@ -2130,8 +2130,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;
+  case bitc::ATTR_KIND_INITIALIZES:
+    return Attribute::Initializes;
   }
 }
 
diff --git a/llvm/lib/Bitcode/Writer/BitcodeWriter.cpp b/llvm/lib/Bitcode/Writer/BitcodeWriter.cpp
index cae78d5f0fd797..3b428d088282ea 100644
--- a/llvm/lib/Bitcode/Writer/BitcodeWriter.cpp
+++ b/llvm/lib/Bitcode/Writer/BitcodeWriter.cpp
@@ -847,8 +847,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::Initializes:
+    return bitc::ATTR_KIND_INITIALIZES;
   case Attribute::EndAttrKinds:
     llvm_unreachable("Can not encode end-attribute kinds marker.");
   case Attribute::None:
diff --git a/llvm/lib/IR/Attributes.cpp b/llvm/lib/IR/Attributes.cpp
index 5f0283ba76d217..5729116897593d 100644
--- a/llvm/lib/IR/Attributes.cpp
+++ b/llvm/lib/IR/Attributes.cpp
@@ -489,9 +489,9 @@ ConstantRange Attribute::getRange() const {
   return pImpl->getValueAsConstantRange();
 }
 
-ConstantRangeList Attribute::getInitialized() const {
-  assert(hasAttribute(Attribute::Initialized) &&
-         "Trying to get initialized attr from non-ConstantRangeList attribute");
+ConstantRangeList Attribute::getInitializes() const {
+  assert(hasAttribute(Attribute::Initializes) &&
+         "Trying to get initializes attr from non-ConstantRangeList attribute");
   return pImpl->getValueAsConstantRangeList();
 }
 
@@ -661,11 +661,11 @@ std::string Attribute::getAsString(bool InAttrGrp) const {
     return Result;
   }
 
-  if (hasAttribute(Attribute::Initialized)) {
+  if (hasAttribute(Attribute::Initializes)) {
     std::string Result;
     raw_string_ostream OS(Result);
     ConstantRangeList CRL = getValueAsConstantRangeList();
-    OS << "initialized(";
+    OS << "initializes(";
     size_t i = 0;
     for (auto &CR : CRL) {
       OS << "(" << CR.getLower() << "," << CR.getUpper() << ")";
@@ -2028,8 +2028,8 @@ AttrBuilder::addConstantRangeListAttr(Attribute::AttrKind Kind,
   return addAttribute(Attribute::get(Ctx, Kind, CRL));
 }
 
-AttrBuilder &AttrBuilder::addInitializedAttr(const ConstantRangeList &CRL) {
-  return addConstantRangeListAttr(Attribute::Initialized, CRL);
+AttrBuilder &AttrBuilder::addInitializesAttr(const ConstantRangeList &CRL) {
+  return addConstantRangeListAttr(Attribute::Initializes, CRL);
 }
 
 AttrBuilder &AttrBuilder::merge(const AttrBuilder &B) {
@@ -2121,7 +2121,7 @@ AttributeMask AttributeFuncs::typeIncompatible(Type *Ty,
           .addAttribute(Attribute::DereferenceableOrNull)
           .addAttribute(Attribute::Writable)
           .addAttribute(Attribute::DeadOnUnwind)
-          .addAttribute(Attribute::Initialized);
+          .addAttribute(Attribute::Initializes);
     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 fcdbc2d9108657..8af30fb8d6ff7a 100644
--- a/llvm/lib/IR/Verifier.cpp
+++ b/llvm/lib/IR/Verifier.cpp
@@ -2033,27 +2033,27 @@ void Verifier::verifyParameterAttrs(AttributeSet Attrs, Type *Ty,
     }
   }
 
-  if (Attrs.hasAttribute(Attribute::Initialized)) {
-    auto Inits = Attrs.getAttribute(Attribute::Initialized)
+  if (Attrs.hasAttribute(Attribute::Initializes)) {
+    auto Inits = Attrs.getAttribute(Attribute::Initializes)
                      .getValueAsConstantRangeList();
     Check(!Inits.isEmptySet(),
-          "Attribute 'initialized' does not support empty list", V);
+          "Attribute 'initializes' does not support empty list", V);
     Check(!Inits.isFullSet(),
-          "Attribute 'initialized' does not support full list", V);
+          "Attribute 'initializes' does not support full list", V);
 
     Check(Inits.getRange(0).getLower().slt(Inits.getRange(0).getUpper()),
-          "Attribute 'initialized' requires interval lower less than upper", V);
+          "Attribute 'initializes' requires interval lower less than upper", V);
     for (size_t i = 1; i < Inits.size(); i++) {
       auto Previous = Inits.getRange(i - 1);
       auto Current = Inits.getRange(i);
       Check(Current.getLower().slt(Current.getUpper()),
-            "Attribute 'initialized' requires interval lower less than upper",
+            "Attribute 'initializes' requires interval lower less than upper",
             V);
       Check(Current.getLower().sge(Previous.getLower()),
-            "Attribute 'initialized' requires intervals in ascending order!",
+            "Attribute 'initializes' requires intervals in ascending order!",
             V);
       Check(Current.getLower().sge(Previous.getUpper()),
-            "Attribute 'initialized' requires intervals merged!", V);
+            "Attribute 'initializes' requires intervals merged!", V);
     }
   }
 
diff --git a/llvm/lib/Transforms/Utils/CodeExtractor.cpp b/llvm/lib/Transforms/Utils/CodeExtractor.cpp
index 6928553f0bdd3d..098c67eea1c36c 100644
--- a/llvm/lib/Transforms/Utils/CodeExtractor.cpp
+++ b/llvm/lib/Transforms/Utils/CodeExtractor.cpp
@@ -1000,7 +1000,7 @@ Function *CodeExtractor::constructFunction(const ValueSet &inputs,
       case Attribute::Writable:
       case Attribute::DeadOnUnwind:
       case Attribute::Range:
-      case Attribute::Initialized:
+      case Attribute::Initializes:
       //  These are not really attributes.
       case Attribute::None:
       case Attribute::EndAttrKinds:
diff --git a/llvm/unittests/IR/ConstantRangeListTest.cpp b/llvm/unittests/IR/ConstantRangeListTest.cpp
index db262a508a84ac..6e2506ec4b1f91 100644
--- a/llvm/unittests/IR/ConstantRangeListTest.cpp
+++ b/llvm/unittests/IR/ConstantRangeListTest.cpp
@@ -14,16 +14,6 @@ using namespace llvm;
 namespace {
 
 class ConstantRangeListTest : public ::testing::Test {
-public:
-  ConstantRangeList
-  GetCRL(SmallVectorImpl<std::pair<int64_t, int64_t>> &Ranges) {
-    ConstantRangeList Result(64, false);
-    for (const auto &[Start, End] : Ranges) {
-      Result.append(Start, End);
-    }
-    return Result;
-  }
-
 protected:
   static ConstantRangeList Full;
   static ConstantRangeList Empty;
@@ -39,13 +29,21 @@ TEST_F(ConstantRangeListTest, Basics) {
   EXPECT_FALSE(Empty.isFullSet());
   EXPECT_TRUE(Empty.isEmptySet());
 
-  ConstantRangeList Some1 = GetCRL({(0, 4), (8, 12)});
-  EXPECT_FALSE(Some1.isFullSet());
-  EXPECT_FALSE(Some1.isEmptySet());
-
-  ConstantRangeList Some2 = GetCRL({(0, 4), (8, 12)});
-  ConstantRangeList Some3 = GetCRL({(-4, 0), (8, 12)});
-  EXPECT_TRUE(Some1 == Some2 && Some1 != Some3);
+  ConstantRangeList CRL1(64, false);
+  CRL1.append(0, 4);
+  CRL1.append(8, 12);
+  EXPECT_FALSE(CRL1.isFullSet());
+  EXPECT_FALSE(CRL1.isEmptySet());
+
+  ConstantRangeList CRL2(64, false);
+  CRL2.append(0, 4);
+  CRL2.append(8, 12);
+  EXPECT_TRUE(CRL1 == CRL2);
+
+  ConstantRangeList CRL3(64, false);
+  CRL3.append(-4, 0);
+  CRL3.append(8, 12);
+  EXPECT_TRUE(CRL1 != CRL3);
 }
 
 } // anonymous namespace

>From 6a1df7a305e36c8c5ea99bc49e3fb2100ded8279 Mon Sep 17 00:00:00 2001
From: Haopeng Liu <haopliu at google.com>
Date: Tue, 9 Apr 2024 17:56:23 +0000
Subject: [PATCH 09/14] Update CRL about accessing the range of an
 EmptySet/FullSet

---
 llvm/include/llvm/IR/ConstantRangeList.h | 20 +++++++++++++++-----
 llvm/lib/IR/Verifier.cpp                 |  4 ++--
 2 files changed, 17 insertions(+), 7 deletions(-)

diff --git a/llvm/include/llvm/IR/ConstantRangeList.h b/llvm/include/llvm/IR/ConstantRangeList.h
index f83c73d34afdd7..c64bf0dd664348 100644
--- a/llvm/include/llvm/IR/ConstantRangeList.h
+++ b/llvm/include/llvm/IR/ConstantRangeList.h
@@ -14,6 +14,9 @@
 // {[0, 0)}     = Empty set
 // {[255, 255)} = Full Set
 //
+// For EmptySet or FullSet, the list size is 1 but it's not allowed to access
+// the range.
+//
 //===----------------------------------------------------------------------===//
 
 #ifndef LLVM_IR_CONSTANTRANGELIST_H
@@ -40,16 +43,21 @@ class [[nodiscard]] ConstantRangeList {
 
   ConstantRangeList(int64_t Lower, int64_t Upper);
 
-  SmallVectorImpl<ConstantRange>::iterator begin() { return Ranges.begin(); }
+  /// It's not allowed to access EmptySet's or FullSet's range.
+  SmallVectorImpl<ConstantRange>::iterator begin() {
+    assert(!isEmptySet() && !isFullSet());
+    return Ranges.begin();
+  }
   SmallVectorImpl<ConstantRange>::iterator end() { return Ranges.end(); }
   SmallVectorImpl<ConstantRange>::const_iterator begin() const {
+    assert(!isEmptySet() && !isFullSet());
     return Ranges.begin();
   }
   SmallVectorImpl<ConstantRange>::const_iterator end() const {
     return Ranges.end();
   }
   ConstantRange getRange(unsigned i) const {
-    assert(i < Ranges.size());
+    assert(!isEmptySet() && !isFullSet() && i < Ranges.size());
     return Ranges[i];
   }
 
@@ -65,10 +73,12 @@ class [[nodiscard]] ConstantRangeList {
   /// Get the bit width of this ConstantRangeList.
   uint32_t getBitWidth() const { return Ranges[0].getBitWidth(); }
 
+  /// For EmptySet or FullSet, the CRL size is 1 not 0.
   size_t size() const { return Ranges.size(); }
 
   void append(const ConstantRange &Range) {
     assert(Range.getLower().slt(Range.getUpper()));
+    assert(getBitWidth() == Range.getBitWidth());
     if (isFullSet())
       return;
     if (isEmptySet()) {
@@ -81,8 +91,8 @@ class [[nodiscard]] ConstantRangeList {
   }
 
   void append(int64_t Lower, int64_t Upper) {
-    append(ConstantRange(APInt(64, StringRef(std::to_string(Lower)), 10),
-                         APInt(64, StringRef(std::to_string(Upper)), 10)));
+    append(ConstantRange(APInt(64, Lower, /*isSigned=*/true),
+                         APInt(64, Upper, /*isSigned=*/true)));
   }
 
   /// Return true if this range is equal to another range.
@@ -99,7 +109,7 @@ class [[nodiscard]] ConstantRangeList {
     return !operator==(CRL);
   }
 
-  /// Print out the bounds to a stream.
+  /// Print out the ranges to a stream.
   void print(raw_ostream &OS) const;
 };
 
diff --git a/llvm/lib/IR/Verifier.cpp b/llvm/lib/IR/Verifier.cpp
index 8af30fb8d6ff7a..a76a478a92866d 100644
--- a/llvm/lib/IR/Verifier.cpp
+++ b/llvm/lib/IR/Verifier.cpp
@@ -2049,10 +2049,10 @@ void Verifier::verifyParameterAttrs(AttributeSet Attrs, Type *Ty,
       Check(Current.getLower().slt(Current.getUpper()),
             "Attribute 'initializes' requires interval lower less than upper",
             V);
-      Check(Current.getLower().sge(Previous.getLower()),
+      Check(Current.getLower().sgt(Previous.getLower()),
             "Attribute 'initializes' requires intervals in ascending order!",
             V);
-      Check(Current.getLower().sge(Previous.getUpper()),
+      Check(Current.getLower().sgt(Previous.getUpper()),
             "Attribute 'initializes' requires intervals merged!", V);
     }
   }

>From fe2e0a5302961248fe8c26ff07866e1639a22e2c Mon Sep 17 00:00:00 2001
From: Haopeng Liu <haopliu at google.com>
Date: Fri, 12 Apr 2024 04:45:34 +0000
Subject: [PATCH 10/14] Change CRL.insert to be ordered and no overlapping

---
 llvm/include/llvm/IR/ConstantRangeList.h    | 29 ++++-------
 llvm/lib/AsmParser/LLParser.cpp             |  2 +-
 llvm/lib/Bitcode/Reader/BitcodeReader.cpp   |  3 +-
 llvm/lib/IR/ConstantRangeList.cpp           | 53 ++++++++++++++++++---
 llvm/unittests/IR/ConstantRangeListTest.cpp | 37 +++++++++++---
 5 files changed, 88 insertions(+), 36 deletions(-)

diff --git a/llvm/include/llvm/IR/ConstantRangeList.h b/llvm/include/llvm/IR/ConstantRangeList.h
index c64bf0dd664348..7577aab5240bc1 100644
--- a/llvm/include/llvm/IR/ConstantRangeList.h
+++ b/llvm/include/llvm/IR/ConstantRangeList.h
@@ -7,9 +7,9 @@
 //===----------------------------------------------------------------------===//
 //
 // Represent a list of signed ConstantRange and do NOT support wrap around the
-// end of the numeric range. Ranges in the list should have the same bitwidth.
-// Each range's lower should be less than its upper. Special lists (take 8-bit
-// as an example):
+// end of the numeric range. Ranges in the list are ordered and no overlapping.
+// Ranges should have the same bitwidth. Each range's lower should be less than
+// its upper. Special lists (take 8-bit as an example):
 //
 // {[0, 0)}     = Empty set
 // {[255, 255)} = Full Set
@@ -34,8 +34,6 @@ class raw_ostream;
 /// This class represents a list of constant ranges.
 class [[nodiscard]] ConstantRangeList {
   SmallVector<ConstantRange, 2> Ranges;
-  // Whether the range list is sorted: [i].lower() < [i+1].lower()
-  bool Sorted = true;
 
 public:
   /// Initialize a full or empty set for the specified bit width.
@@ -76,22 +74,11 @@ class [[nodiscard]] ConstantRangeList {
   /// For EmptySet or FullSet, the CRL size is 1 not 0.
   size_t size() const { return Ranges.size(); }
 
-  void append(const ConstantRange &Range) {
-    assert(Range.getLower().slt(Range.getUpper()));
-    assert(getBitWidth() == Range.getBitWidth());
-    if (isFullSet())
-      return;
-    if (isEmptySet()) {
-      Ranges[0] = Range;
-      return;
-    }
-    if (Range.getLower().slt(Ranges[size() - 1].getLower()))
-      Sorted = false;
-    Ranges.push_back(Range);
-  }
-
-  void append(int64_t Lower, int64_t Upper) {
-    append(ConstantRange(APInt(64, Lower, /*isSigned=*/true),
+  /// Insert a range to Ranges. Keep the list ordered
+  /// and no overlapping (merge ranges if needed).
+  void insert(const ConstantRange &Range);
+  void insert(int64_t Lower, int64_t Upper) {
+    insert(ConstantRange(APInt(64, Lower, /*isSigned=*/true),
                          APInt(64, Upper, /*isSigned=*/true)));
   }
 
diff --git a/llvm/lib/AsmParser/LLParser.cpp b/llvm/lib/AsmParser/LLParser.cpp
index 28af4968095786..090bba46cf1e31 100644
--- a/llvm/lib/AsmParser/LLParser.cpp
+++ b/llvm/lib/AsmParser/LLParser.cpp
@@ -3087,7 +3087,7 @@ bool LLParser::parseInitializesAttr(AttrBuilder &B) {
     if (parseToken(lltok::rparen, "expected ')'"))
       return true;
 
-    CRL.append(ConstantRange(Lower, Upper));
+    CRL.insert(ConstantRange(Lower, Upper));
   } while (EatIfPresent(lltok::comma));
 
   if (parseToken(lltok::rparen, "expected ')'"))
diff --git a/llvm/lib/Bitcode/Reader/BitcodeReader.cpp b/llvm/lib/Bitcode/Reader/BitcodeReader.cpp
index 41bfb92ac211a5..6046d686527814 100644
--- a/llvm/lib/Bitcode/Reader/BitcodeReader.cpp
+++ b/llvm/lib/Bitcode/Reader/BitcodeReader.cpp
@@ -2325,10 +2325,11 @@ Error BitcodeReader::parseAttributeGroupBlock() {
 
           ConstantRangeList CRL(64, false);
           int RangeSize = Record[++i];
+          assert(i + RangeSize < e);
           for (int Idx = 0; Idx < RangeSize; ++Idx) {
             int64_t Start = BitcodeReader::decodeSignRotatedValue(Record[++i]);
             int64_t End = BitcodeReader::decodeSignRotatedValue(Record[++i]);
-            CRL.append(Start, End);
+            CRL.insert(Start, End);
           }
           B.addConstantRangeListAttr(Kind, CRL);
         } else {
diff --git a/llvm/lib/IR/ConstantRangeList.cpp b/llvm/lib/IR/ConstantRangeList.cpp
index 96304ade997d35..845bad082bc3d0 100644
--- a/llvm/lib/IR/ConstantRangeList.cpp
+++ b/llvm/lib/IR/ConstantRangeList.cpp
@@ -7,13 +7,16 @@
 //===----------------------------------------------------------------------===//
 //
 // Represent a list of signed ConstantRange and do NOT support wrap around the
-// end of the numeric range. Ranges in the list should have the same bitwidth.
-// Each range's lower should be less than its upper. Special lists (take 8-bit
-// as an example):
+// end of the numeric range. Ranges in the list are ordered and no overlapping.
+// Ranges should have the same bitwidth. Each range's lower should be less than
+// its upper. Special lists (take 8-bit as an example):
 //
 // {[0, 0)}     = Empty set
 // {[255, 255)} = Full Set
 //
+// For EmptySet or FullSet, the list size is 1 but it's not allowed to access
+// the range.
+//
 //===----------------------------------------------------------------------===//
 
 #include "llvm/IR/ConstantRangeList.h"
@@ -27,10 +30,46 @@ ConstantRangeList::ConstantRangeList(uint32_t BitWidth, bool Full) {
   Ranges.push_back(ConstantRange(Lower, Lower));
 }
 
-ConstantRangeList::ConstantRangeList(int64_t Lower, int64_t Upper) {
-  Ranges.push_back(
-      ConstantRange(APInt(64, StringRef(std::to_string(Lower)), 10),
-                    APInt(64, StringRef(std::to_string(Upper)), 10)));
+void ConstantRangeList::insert(const ConstantRange &Range) {
+  assert(Range.getLower().slt(Range.getUpper()));
+  assert(getBitWidth() == Range.getBitWidth());
+  if (isFullSet())
+    return;
+  if (isEmptySet()) {
+    Ranges[0] = Range;
+    return;
+  }
+
+  ConstantRange RangeToInsert = Range;
+  SmallVector<ConstantRange, 2> ExistingRanges(Ranges.begin(), Ranges.end());
+  Ranges.clear();
+  for (size_t i = 0; i < ExistingRanges.size(); i++) {
+    const ConstantRange &CurRange = ExistingRanges[i];
+    if (CurRange.getUpper().slt(RangeToInsert.getLower())) {
+      // Case1: No overlap and CurRange is before ToInsert.
+      // |--CurRange--|
+      //                 |--ToInsert--|
+      Ranges.push_back(CurRange);
+      continue;
+    } else if (RangeToInsert.getUpper().slt(CurRange.getLower())) {
+      // Case2: No overlap and CurRange is after ToInsert.
+      //                                 |--CurRange--|
+      //                 |--ToInsert--|
+      // insert the range.
+      Ranges.push_back(RangeToInsert);
+      for (size_t j = i; j < ExistingRanges.size(); j++)
+        Ranges.push_back(ExistingRanges[j]);
+      return;
+    } else {
+      // Case3: Overlap.
+      APInt NewLower =
+          APIntOps::smin(CurRange.getLower(), RangeToInsert.getLower());
+      APInt NewUpper =
+          APIntOps::smax(CurRange.getUpper(), RangeToInsert.getUpper());
+      RangeToInsert = ConstantRange(NewLower, NewUpper);
+    }
+  }
+  Ranges.push_back(RangeToInsert);
 }
 
 void ConstantRangeList::print(raw_ostream &OS) const {
diff --git a/llvm/unittests/IR/ConstantRangeListTest.cpp b/llvm/unittests/IR/ConstantRangeListTest.cpp
index 6e2506ec4b1f91..10c9a791b7bb7b 100644
--- a/llvm/unittests/IR/ConstantRangeListTest.cpp
+++ b/llvm/unittests/IR/ConstantRangeListTest.cpp
@@ -30,20 +30,45 @@ TEST_F(ConstantRangeListTest, Basics) {
   EXPECT_TRUE(Empty.isEmptySet());
 
   ConstantRangeList CRL1(64, false);
-  CRL1.append(0, 4);
-  CRL1.append(8, 12);
+  CRL1.insert(0, 4);
+  CRL1.insert(8, 12);
   EXPECT_FALSE(CRL1.isFullSet());
   EXPECT_FALSE(CRL1.isEmptySet());
 
   ConstantRangeList CRL2(64, false);
-  CRL2.append(0, 4);
-  CRL2.append(8, 12);
+  CRL2.insert(0, 4);
+  CRL2.insert(8, 12);
   EXPECT_TRUE(CRL1 == CRL2);
 
   ConstantRangeList CRL3(64, false);
-  CRL3.append(-4, 0);
-  CRL3.append(8, 12);
+  CRL3.insert(-4, 0);
+  CRL3.insert(8, 12);
   EXPECT_TRUE(CRL1 != CRL3);
 }
 
+TEST_F(ConstantRangeListTest, Insert) {
+  ConstantRangeList CRL(64, false);
+  CRL.insert(0, 4);
+  CRL.insert(8, 12);
+  // No overlap, left
+  CRL.insert(-8, -4);
+  // No overlap, right
+  CRL.insert(16, 20);
+  // No overlap, middle
+  CRL.insert(13, 15);
+  // Overlap with left
+  CRL.insert(-6, -2);
+  // Overlap with right
+  CRL.insert(5, 9);
+  // Overlap with left and right
+  CRL.insert(14, 18);
+  // Overlap cross ranges
+  CRL.insert(2, 14);
+
+  ConstantRangeList Expected(64, false);
+  Expected.insert(-8, -2);
+  Expected.insert(0, 20);
+  EXPECT_TRUE(CRL == Expected);
+}
+
 } // anonymous namespace

>From f73df045295136b3fe64bc0193a738b9a9721ee4 Mon Sep 17 00:00:00 2001
From: Haopeng Liu <haopliu at google.com>
Date: Tue, 16 Apr 2024 22:46:24 +0000
Subject: [PATCH 11/14] Optimize CRL::insert for common cases

---
 llvm/include/llvm/IR/ConstantRangeList.h  |  2 +-
 llvm/lib/Bitcode/Reader/BitcodeReader.cpp |  2 +-
 llvm/lib/IR/ConstantRangeList.cpp         | 65 +++++++++++++----------
 3 files changed, 38 insertions(+), 31 deletions(-)

diff --git a/llvm/include/llvm/IR/ConstantRangeList.h b/llvm/include/llvm/IR/ConstantRangeList.h
index 7577aab5240bc1..d778ab4a2c09be 100644
--- a/llvm/include/llvm/IR/ConstantRangeList.h
+++ b/llvm/include/llvm/IR/ConstantRangeList.h
@@ -76,7 +76,7 @@ class [[nodiscard]] ConstantRangeList {
 
   /// Insert a range to Ranges. Keep the list ordered
   /// and no overlapping (merge ranges if needed).
-  void insert(const ConstantRange &Range);
+  void insert(const ConstantRange &NewRange);
   void insert(int64_t Lower, int64_t Upper) {
     insert(ConstantRange(APInt(64, Lower, /*isSigned=*/true),
                          APInt(64, Upper, /*isSigned=*/true)));
diff --git a/llvm/lib/Bitcode/Reader/BitcodeReader.cpp b/llvm/lib/Bitcode/Reader/BitcodeReader.cpp
index 6046d686527814..ffb0122a3803f3 100644
--- a/llvm/lib/Bitcode/Reader/BitcodeReader.cpp
+++ b/llvm/lib/Bitcode/Reader/BitcodeReader.cpp
@@ -2325,7 +2325,7 @@ Error BitcodeReader::parseAttributeGroupBlock() {
 
           ConstantRangeList CRL(64, false);
           int RangeSize = Record[++i];
-          assert(i + RangeSize < e);
+          assert(i + 2 * RangeSize < e);
           for (int Idx = 0; Idx < RangeSize; ++Idx) {
             int64_t Start = BitcodeReader::decodeSignRotatedValue(Record[++i]);
             int64_t End = BitcodeReader::decodeSignRotatedValue(Record[++i]);
diff --git a/llvm/lib/IR/ConstantRangeList.cpp b/llvm/lib/IR/ConstantRangeList.cpp
index 845bad082bc3d0..07b1661db0e306 100644
--- a/llvm/lib/IR/ConstantRangeList.cpp
+++ b/llvm/lib/IR/ConstantRangeList.cpp
@@ -30,46 +30,53 @@ ConstantRangeList::ConstantRangeList(uint32_t BitWidth, bool Full) {
   Ranges.push_back(ConstantRange(Lower, Lower));
 }
 
-void ConstantRangeList::insert(const ConstantRange &Range) {
-  assert(Range.getLower().slt(Range.getUpper()));
-  assert(getBitWidth() == Range.getBitWidth());
+void ConstantRangeList::insert(const ConstantRange &NewRange) {
+  assert(NewRange.getLower().slt(NewRange.getUpper()));
+  assert(getBitWidth() == NewRange.getBitWidth());
+  // Handle common cases.
   if (isFullSet())
     return;
   if (isEmptySet()) {
-    Ranges[0] = Range;
+    Ranges[0] = NewRange;
+    return;
+  }
+  if (Ranges.back().getUpper().slt(NewRange.getLower())) {
+    Ranges.push_back(NewRange);
+    return;
+  }
+  if (NewRange.getUpper().slt(Ranges.front().getLower())) {
+    Ranges.insert(Ranges.begin(), NewRange);
     return;
   }
 
-  ConstantRange RangeToInsert = Range;
+  // Slow insert.
   SmallVector<ConstantRange, 2> ExistingRanges(Ranges.begin(), Ranges.end());
-  Ranges.clear();
-  for (size_t i = 0; i < ExistingRanges.size(); i++) {
-    const ConstantRange &CurRange = ExistingRanges[i];
-    if (CurRange.getUpper().slt(RangeToInsert.getLower())) {
-      // Case1: No overlap and CurRange is before ToInsert.
-      // |--CurRange--|
-      //                 |--ToInsert--|
-      Ranges.push_back(CurRange);
-      continue;
-    } else if (RangeToInsert.getUpper().slt(CurRange.getLower())) {
-      // Case2: No overlap and CurRange is after ToInsert.
-      //                                 |--CurRange--|
-      //                 |--ToInsert--|
-      // insert the range.
-      Ranges.push_back(RangeToInsert);
-      for (size_t j = i; j < ExistingRanges.size(); j++)
-        Ranges.push_back(ExistingRanges[j]);
-      return;
+  auto LowerBound =
+      std::lower_bound(ExistingRanges.begin(), ExistingRanges.end(), NewRange,
+                       [](const ConstantRange &a, const ConstantRange &b) {
+                         return a.getLower().slt(b.getLower());
+                       });
+  Ranges.erase(Ranges.begin() + (LowerBound - ExistingRanges.begin()),
+               Ranges.end());
+  if (!Ranges.empty() && NewRange.getLower().slt(Ranges.back().getUpper())) {
+    APInt NewLower = Ranges.back().getLower();
+    APInt NewUpper =
+        APIntOps::smax(NewRange.getUpper(), Ranges.back().getUpper());
+    Ranges.back() = ConstantRange(NewLower, NewUpper);
+  } else {
+    Ranges.push_back(NewRange);
+  }
+  for (auto Iter = LowerBound; Iter != ExistingRanges.end(); Iter++) {
+    if (Ranges.back().getUpper().slt(Iter->getLower())) {
+      Ranges.push_back(*Iter);
     } else {
-      // Case3: Overlap.
-      APInt NewLower =
-          APIntOps::smin(CurRange.getLower(), RangeToInsert.getLower());
+      APInt NewLower = Ranges.back().getLower();
       APInt NewUpper =
-          APIntOps::smax(CurRange.getUpper(), RangeToInsert.getUpper());
-      RangeToInsert = ConstantRange(NewLower, NewUpper);
+          APIntOps::smax(Iter->getUpper(), Ranges.back().getUpper());
+      Ranges.back() = ConstantRange(NewLower, NewUpper);
     }
   }
-  Ranges.push_back(RangeToInsert);
+  return;
 }
 
 void ConstantRangeList::print(raw_ostream &OS) const {

>From 63c45f681a1d2c0c02cf2d14c71d239d2e12a967 Mon Sep 17 00:00:00 2001
From: Haopeng Liu <haopliu at google.com>
Date: Tue, 16 Apr 2024 23:58:56 +0000
Subject: [PATCH 12/14] Update ConstantRangeList

---
 llvm/include/llvm/IR/ConstantRangeList.h    | 49 +++++----------------
 llvm/lib/AsmParser/LLParser.cpp             |  2 +-
 llvm/lib/Bitcode/Reader/BitcodeReader.cpp   |  2 +-
 llvm/lib/IR/Attributes.cpp                  |  8 +---
 llvm/lib/IR/ConstantRangeList.cpp           | 34 +++-----------
 llvm/lib/IR/Verifier.cpp                    |  6 +--
 llvm/unittests/IR/ConstantRangeListTest.cpp | 28 +++---------
 7 files changed, 30 insertions(+), 99 deletions(-)

diff --git a/llvm/include/llvm/IR/ConstantRangeList.h b/llvm/include/llvm/IR/ConstantRangeList.h
index d778ab4a2c09be..cf473d1da82981 100644
--- a/llvm/include/llvm/IR/ConstantRangeList.h
+++ b/llvm/include/llvm/IR/ConstantRangeList.h
@@ -9,13 +9,7 @@
 // Represent a list of signed ConstantRange and do NOT support wrap around the
 // end of the numeric range. Ranges in the list are ordered and no overlapping.
 // Ranges should have the same bitwidth. Each range's lower should be less than
-// its upper. Special lists (take 8-bit as an example):
-//
-// {[0, 0)}     = Empty set
-// {[255, 255)} = Full Set
-//
-// For EmptySet or FullSet, the list size is 1 but it's not allowed to access
-// the range.
+// its upper.
 //
 //===----------------------------------------------------------------------===//
 
@@ -36,61 +30,38 @@ class [[nodiscard]] ConstantRangeList {
   SmallVector<ConstantRange, 2> Ranges;
 
 public:
-  /// Initialize a full or empty set for the specified bit width.
-  explicit ConstantRangeList(uint32_t BitWidth, bool isFullSet);
-
-  ConstantRangeList(int64_t Lower, int64_t Upper);
-
-  /// It's not allowed to access EmptySet's or FullSet's range.
-  SmallVectorImpl<ConstantRange>::iterator begin() {
-    assert(!isEmptySet() && !isFullSet());
-    return Ranges.begin();
-  }
+  SmallVectorImpl<ConstantRange>::iterator begin() { return Ranges.begin(); }
   SmallVectorImpl<ConstantRange>::iterator end() { return Ranges.end(); }
   SmallVectorImpl<ConstantRange>::const_iterator begin() const {
-    assert(!isEmptySet() && !isFullSet());
     return Ranges.begin();
   }
   SmallVectorImpl<ConstantRange>::const_iterator end() const {
     return Ranges.end();
   }
   ConstantRange getRange(unsigned i) const {
-    assert(!isEmptySet() && !isFullSet() && i < Ranges.size());
+    assert(i < Ranges.size());
     return Ranges[i];
   }
 
-  /// Return true if this set contains no members.
-  bool isEmptySet() const {
-    return Ranges.size() == 1 && Ranges[0].isEmptySet();
-  }
-
-  /// Return true if this set contains all of the elements possible
-  /// for this data-type.
-  bool isFullSet() const { return Ranges.size() == 1 && Ranges[0].isFullSet(); }
+  /// Return true if this list contains no members.
+  bool empty() const { return Ranges.empty(); }
 
   /// Get the bit width of this ConstantRangeList.
-  uint32_t getBitWidth() const { return Ranges[0].getBitWidth(); }
+  uint32_t getBitWidth() const { return 64; }
 
-  /// For EmptySet or FullSet, the CRL size is 1 not 0.
+  /// Return the size of this ConstantRangeList.
   size_t size() const { return Ranges.size(); }
 
-  /// Insert a range to Ranges. Keep the list ordered
-  /// and no overlapping (merge ranges if needed).
+  /// Insert a new range to Ranges.
   void insert(const ConstantRange &NewRange);
   void insert(int64_t Lower, int64_t Upper) {
     insert(ConstantRange(APInt(64, Lower, /*isSigned=*/true),
                          APInt(64, Upper, /*isSigned=*/true)));
   }
 
-  /// Return true if this range is equal to another range.
+  /// Return true if this range list is equal to another range list.
   bool operator==(const ConstantRangeList &CRL) const {
-    if (size() != CRL.size())
-      return false;
-    for (size_t i = 0; i < size(); ++i) {
-      if (Ranges[i] != CRL.Ranges[i])
-        return false;
-    }
-    return true;
+    return Ranges == CRL.Ranges;
   }
   bool operator!=(const ConstantRangeList &CRL) const {
     return !operator==(CRL);
diff --git a/llvm/lib/AsmParser/LLParser.cpp b/llvm/lib/AsmParser/LLParser.cpp
index 090bba46cf1e31..030fee0b65b3aa 100644
--- a/llvm/lib/AsmParser/LLParser.cpp
+++ b/llvm/lib/AsmParser/LLParser.cpp
@@ -3070,7 +3070,7 @@ bool LLParser::parseInitializesAttr(AttrBuilder &B) {
   if (parseToken(lltok::lparen, "expected '('"))
     return true;
 
-  ConstantRangeList CRL(64, false);
+  ConstantRangeList CRL;
   // Parse each constant range.
   do {
     APInt Lower, Upper;
diff --git a/llvm/lib/Bitcode/Reader/BitcodeReader.cpp b/llvm/lib/Bitcode/Reader/BitcodeReader.cpp
index ffb0122a3803f3..bcad43bdd30bfe 100644
--- a/llvm/lib/Bitcode/Reader/BitcodeReader.cpp
+++ b/llvm/lib/Bitcode/Reader/BitcodeReader.cpp
@@ -2323,7 +2323,7 @@ Error BitcodeReader::parseAttributeGroupBlock() {
           if (!Attribute::isConstantRangeListAttrKind(Kind))
             return error("Not a constant range list attribute");
 
-          ConstantRangeList CRL(64, false);
+          ConstantRangeList CRL;
           int RangeSize = Record[++i];
           assert(i + 2 * RangeSize < e);
           for (int Idx = 0; Idx < RangeSize; ++Idx) {
diff --git a/llvm/lib/IR/Attributes.cpp b/llvm/lib/IR/Attributes.cpp
index 5729116897593d..cc809f8b904738 100644
--- a/llvm/lib/IR/Attributes.cpp
+++ b/llvm/lib/IR/Attributes.cpp
@@ -666,13 +666,9 @@ std::string Attribute::getAsString(bool InAttrGrp) const {
     raw_string_ostream OS(Result);
     ConstantRangeList CRL = getValueAsConstantRangeList();
     OS << "initializes(";
-    size_t i = 0;
-    for (auto &CR : CRL) {
+    interleaveComma(CRL, OS, [&](ConstantRange CR) {
       OS << "(" << CR.getLower() << "," << CR.getUpper() << ")";
-      if (i != CRL.size() - 1)
-        OS << ",";
-      i++;
-    }
+    });
     OS << ")";
     OS.flush();
     return Result;
diff --git a/llvm/lib/IR/ConstantRangeList.cpp b/llvm/lib/IR/ConstantRangeList.cpp
index 07b1661db0e306..51e56fdb24d4f2 100644
--- a/llvm/lib/IR/ConstantRangeList.cpp
+++ b/llvm/lib/IR/ConstantRangeList.cpp
@@ -9,13 +9,7 @@
 // Represent a list of signed ConstantRange and do NOT support wrap around the
 // end of the numeric range. Ranges in the list are ordered and no overlapping.
 // Ranges should have the same bitwidth. Each range's lower should be less than
-// its upper. Special lists (take 8-bit as an example):
-//
-// {[0, 0)}     = Empty set
-// {[255, 255)} = Full Set
-//
-// For EmptySet or FullSet, the list size is 1 but it's not allowed to access
-// the range.
+// its upper.
 //
 //===----------------------------------------------------------------------===//
 
@@ -24,23 +18,14 @@
 
 using namespace llvm;
 
-ConstantRangeList::ConstantRangeList(uint32_t BitWidth, bool Full) {
-  APInt Lower =
-      Full ? APInt::getMaxValue(BitWidth) : APInt::getMinValue(BitWidth);
-  Ranges.push_back(ConstantRange(Lower, Lower));
-}
-
 void ConstantRangeList::insert(const ConstantRange &NewRange) {
+  if (NewRange.isEmptySet())
+    return;
+  assert(!NewRange.isFullSet() && "Do not support full set");
   assert(NewRange.getLower().slt(NewRange.getUpper()));
   assert(getBitWidth() == NewRange.getBitWidth());
   // Handle common cases.
-  if (isFullSet())
-    return;
-  if (isEmptySet()) {
-    Ranges[0] = NewRange;
-    return;
-  }
-  if (Ranges.back().getUpper().slt(NewRange.getLower())) {
+  if (empty() || Ranges.back().getUpper().slt(NewRange.getLower())) {
     Ranges.push_back(NewRange);
     return;
   }
@@ -80,11 +65,6 @@ void ConstantRangeList::insert(const ConstantRange &NewRange) {
 }
 
 void ConstantRangeList::print(raw_ostream &OS) const {
-  if (isFullSet())
-    OS << "full-set";
-  else if (isEmptySet())
-    OS << "empty-set";
-  else
-    for (const auto &Range : Ranges)
-      Range.print(OS);
+  for (const auto &Range : Ranges)
+    Range.print(OS);
 }
diff --git a/llvm/lib/IR/Verifier.cpp b/llvm/lib/IR/Verifier.cpp
index a76a478a92866d..9082dc78788116 100644
--- a/llvm/lib/IR/Verifier.cpp
+++ b/llvm/lib/IR/Verifier.cpp
@@ -2036,10 +2036,8 @@ void Verifier::verifyParameterAttrs(AttributeSet Attrs, Type *Ty,
   if (Attrs.hasAttribute(Attribute::Initializes)) {
     auto Inits = Attrs.getAttribute(Attribute::Initializes)
                      .getValueAsConstantRangeList();
-    Check(!Inits.isEmptySet(),
-          "Attribute 'initializes' does not support empty list", V);
-    Check(!Inits.isFullSet(),
-          "Attribute 'initializes' does not support full list", V);
+    Check(!Inits.empty(), "Attribute 'initializes' does not support empty list",
+          V);
 
     Check(Inits.getRange(0).getLower().slt(Inits.getRange(0).getUpper()),
           "Attribute 'initializes' requires interval lower less than upper", V);
diff --git a/llvm/unittests/IR/ConstantRangeListTest.cpp b/llvm/unittests/IR/ConstantRangeListTest.cpp
index 10c9a791b7bb7b..1b66de0f996d3c 100644
--- a/llvm/unittests/IR/ConstantRangeListTest.cpp
+++ b/llvm/unittests/IR/ConstantRangeListTest.cpp
@@ -13,41 +13,27 @@ using namespace llvm;
 
 namespace {
 
-class ConstantRangeListTest : public ::testing::Test {
-protected:
-  static ConstantRangeList Full;
-  static ConstantRangeList Empty;
-};
-
-ConstantRangeList ConstantRangeListTest::Full(64, true);
-ConstantRangeList ConstantRangeListTest::Empty(64, false);
+using ConstantRangeListTest = ::testing::Test;
 
 TEST_F(ConstantRangeListTest, Basics) {
-  EXPECT_TRUE(Full.isFullSet());
-  EXPECT_FALSE(Full.isEmptySet());
-
-  EXPECT_FALSE(Empty.isFullSet());
-  EXPECT_TRUE(Empty.isEmptySet());
-
-  ConstantRangeList CRL1(64, false);
+  ConstantRangeList CRL1;
   CRL1.insert(0, 4);
   CRL1.insert(8, 12);
-  EXPECT_FALSE(CRL1.isFullSet());
-  EXPECT_FALSE(CRL1.isEmptySet());
+  EXPECT_FALSE(CRL1.empty());
 
-  ConstantRangeList CRL2(64, false);
+  ConstantRangeList CRL2;
   CRL2.insert(0, 4);
   CRL2.insert(8, 12);
   EXPECT_TRUE(CRL1 == CRL2);
 
-  ConstantRangeList CRL3(64, false);
+  ConstantRangeList CRL3;
   CRL3.insert(-4, 0);
   CRL3.insert(8, 12);
   EXPECT_TRUE(CRL1 != CRL3);
 }
 
 TEST_F(ConstantRangeListTest, Insert) {
-  ConstantRangeList CRL(64, false);
+  ConstantRangeList CRL;
   CRL.insert(0, 4);
   CRL.insert(8, 12);
   // No overlap, left
@@ -65,7 +51,7 @@ TEST_F(ConstantRangeListTest, Insert) {
   // Overlap cross ranges
   CRL.insert(2, 14);
 
-  ConstantRangeList Expected(64, false);
+  ConstantRangeList Expected;
   Expected.insert(-8, -2);
   Expected.insert(0, 20);
   EXPECT_TRUE(CRL == Expected);

>From dcc2b387763a062083049f97bf6f9993120a088e Mon Sep 17 00:00:00 2001
From: Haopeng Liu <haopliu at google.com>
Date: Wed, 17 Apr 2024 04:16:11 +0000
Subject: [PATCH 13/14] Add unit tests for BitcodeReader/Verifier/Assembler

---
 llvm/include/llvm/IR/ConstantRangeList.h      | 10 ++-
 llvm/lib/AsmParser/LLParser.cpp               |  4 +-
 llvm/lib/Bitcode/Reader/BitcodeReader.cpp     |  2 +-
 llvm/lib/IR/Attributes.cpp                    |  9 ++-
 .../initializes-attribute-invalid.ll          | 71 +++++++++++++++++++
 llvm/test/Bitcode/attributes.ll               |  5 ++
 llvm/test/Verifier/initializes-attr.ll        | 31 ++++++++
 7 files changed, 125 insertions(+), 7 deletions(-)
 create mode 100644 llvm/test/Assembler/initializes-attribute-invalid.ll
 create mode 100644 llvm/test/Verifier/initializes-attr.ll

diff --git a/llvm/include/llvm/IR/ConstantRangeList.h b/llvm/include/llvm/IR/ConstantRangeList.h
index cf473d1da82981..e1f3ee3b2726b3 100644
--- a/llvm/include/llvm/IR/ConstantRangeList.h
+++ b/llvm/include/llvm/IR/ConstantRangeList.h
@@ -52,13 +52,21 @@ class [[nodiscard]] ConstantRangeList {
   /// Return the size of this ConstantRangeList.
   size_t size() const { return Ranges.size(); }
 
-  /// Insert a new range to Ranges.
+  /// Insert a new range to Ranges and keep the list ordered.
   void insert(const ConstantRange &NewRange);
   void insert(int64_t Lower, int64_t Upper) {
     insert(ConstantRange(APInt(64, Lower, /*isSigned=*/true),
                          APInt(64, Upper, /*isSigned=*/true)));
   }
 
+  // Append a new Range to Ranges. Caller should make sure
+  // the list is still ordered after appending.
+  void append(const ConstantRange &Range) { Ranges.push_back(Range); }
+  void append(int64_t Lower, int64_t Upper) {
+    append(ConstantRange(APInt(64, Lower, /*isSigned=*/true),
+                         APInt(64, Upper, /*isSigned=*/true)));
+  }
+
   /// Return true if this range list is equal to another range list.
   bool operator==(const ConstantRangeList &CRL) const {
     return Ranges == CRL.Ranges;
diff --git a/llvm/lib/AsmParser/LLParser.cpp b/llvm/lib/AsmParser/LLParser.cpp
index 030fee0b65b3aa..9656f8b14b18e2 100644
--- a/llvm/lib/AsmParser/LLParser.cpp
+++ b/llvm/lib/AsmParser/LLParser.cpp
@@ -3074,7 +3074,7 @@ bool LLParser::parseInitializesAttr(AttrBuilder &B) {
   // Parse each constant range.
   do {
     APInt Lower, Upper;
-    if (parseToken(lltok::lparen, "expected'('"))
+    if (parseToken(lltok::lparen, "expected '('"))
       return true;
 
     if (ParseAPSInt(Lower) || parseToken(lltok::comma, "expected ','") ||
@@ -3087,7 +3087,7 @@ bool LLParser::parseInitializesAttr(AttrBuilder &B) {
     if (parseToken(lltok::rparen, "expected ')'"))
       return true;
 
-    CRL.insert(ConstantRange(Lower, Upper));
+    CRL.append(ConstantRange(Lower, Upper));
   } while (EatIfPresent(lltok::comma));
 
   if (parseToken(lltok::rparen, "expected ')'"))
diff --git a/llvm/lib/Bitcode/Reader/BitcodeReader.cpp b/llvm/lib/Bitcode/Reader/BitcodeReader.cpp
index bcad43bdd30bfe..7bcc3e211b8dff 100644
--- a/llvm/lib/Bitcode/Reader/BitcodeReader.cpp
+++ b/llvm/lib/Bitcode/Reader/BitcodeReader.cpp
@@ -2329,7 +2329,7 @@ Error BitcodeReader::parseAttributeGroupBlock() {
           for (int Idx = 0; Idx < RangeSize; ++Idx) {
             int64_t Start = BitcodeReader::decodeSignRotatedValue(Record[++i]);
             int64_t End = BitcodeReader::decodeSignRotatedValue(Record[++i]);
-            CRL.insert(Start, End);
+            CRL.append(Start, End);
           }
           B.addConstantRangeListAttr(Kind, CRL);
         } else {
diff --git a/llvm/lib/IR/Attributes.cpp b/llvm/lib/IR/Attributes.cpp
index cc809f8b904738..f61c1b08df8f02 100644
--- a/llvm/lib/IR/Attributes.cpp
+++ b/llvm/lib/IR/Attributes.cpp
@@ -666,9 +666,12 @@ std::string Attribute::getAsString(bool InAttrGrp) const {
     raw_string_ostream OS(Result);
     ConstantRangeList CRL = getValueAsConstantRangeList();
     OS << "initializes(";
-    interleaveComma(CRL, OS, [&](ConstantRange CR) {
-      OS << "(" << CR.getLower() << "," << CR.getUpper() << ")";
-    });
+    interleave(
+        CRL, OS,
+        [&](ConstantRange CR) {
+          OS << "(" << CR.getLower() << "," << CR.getUpper() << ")";
+        },
+        ",");
     OS << ")";
     OS.flush();
     return Result;
diff --git a/llvm/test/Assembler/initializes-attribute-invalid.ll b/llvm/test/Assembler/initializes-attribute-invalid.ll
new file mode 100644
index 00000000000000..842d67b2fb5cc5
--- /dev/null
+++ b/llvm/test/Assembler/initializes-attribute-invalid.ll
@@ -0,0 +1,71 @@
+; RUN: split-file %s %t
+; RUN: not llvm-as < %s %t/outer_left_parenthesis.ll -o /dev/null 2>&1 | FileCheck %s --check-prefix=OUTER-LEFT
+; RUN: not llvm-as < %s %t/inner_left_parenthesis.ll -o /dev/null 2>&1 | FileCheck %s --check-prefix=INNER-LEFT
+; RUN: not llvm-as < %s %t/inner_right_parenthesis.ll -o /dev/null 2>&1 | FileCheck %s --check-prefix=OUTER-RIGHT
+; RUN: not llvm-as < %s %t/outer_right_parenthesis.ll -o /dev/null 2>&1 | FileCheck %s --check-prefix=OUTER-RIGHT
+; RUN: not llvm-as < %s %t/integer.ll -o /dev/null 2>&1 | FileCheck %s --check-prefix=INTEGER
+; RUN: not llvm-as < %s %t/lower_equal_upper.ll -o /dev/null 2>&1 | FileCheck %s --check-prefix=LOWER-EQUAL-UPPER
+; RUN: not llvm-as < %s %t/inner_comma.ll -o /dev/null 2>&1 | FileCheck %s --check-prefix=INNER-COMMA
+; RUN: not llvm-as < %s %t/outer_comma.ll -o /dev/null 2>&1 | FileCheck %s --check-prefix=OUTER-COMMA
+; RUN: not llvm-as < %s %t/empty1.ll -o /dev/null 2>&1 | FileCheck %s --check-prefix=EMPTY1
+; RUN: not llvm-as < %s %t/empty2.ll -o /dev/null 2>&1 | FileCheck %s --check-prefix=EMPTY2
+
+;--- outer_left_parenthesis.ll
+; OUTER-LEFT: expected '('
+define void @foo(ptr initializes 0,4 %a) {
+  ret void
+}
+
+;--- inner_left_parenthesis.ll
+; INNER-LEFT: expected '('
+define void @foo(ptr initializes(0,4 %a) {
+  ret void
+}
+
+;--- inner_right_parenthesis.ll
+; INNER-RIGHT: expected ')'
+define void @foo(ptr initializes((0,4 %a) {
+  ret void
+}
+
+;--- outer_right_parenthesis.ll
+; OUTER-RIGHT: expected ')'
+define void @foo(ptr initializes((0,4) %a) {
+  ret void
+}
+
+;--- integer.ll
+; INTEGER: expected integer
+define void @foo(ptr initializes((0.5,4)) %a) {
+  ret void
+}
+
+;--- lower_equal_upper.ll
+; LOWER-EQUAL-UPPER: the range should not represent the full or empty set!
+define void @foo(ptr initializes((4,4)) %a) {
+  ret void
+}
+
+;--- inner_comma.ll
+; INNER-COMMA: expected ','
+define void @foo(ptr initializes((0 4)) %a) {
+  ret void
+}
+
+;--- outer_comma.ll
+; OUTER-COMMA: expected ')'
+define void @foo(ptr initializes((0,4) (8,12)) %a) {
+  ret void
+}
+
+;--- empty1.ll
+; EMPTY1: expected '('
+define void @foo(ptr initializes() %a) {
+  ret void
+}
+
+;--- empty2.ll
+; EMPTY2: expected integer
+define void @foo(ptr initializes(()) %a) {
+  ret void
+}
diff --git a/llvm/test/Bitcode/attributes.ll b/llvm/test/Bitcode/attributes.ll
index 26163b4d38c89d..c81c5779a45b4c 100644
--- a/llvm/test/Bitcode/attributes.ll
+++ b/llvm/test/Bitcode/attributes.ll
@@ -536,6 +536,11 @@ define void @wide_range_attribute(i128 range(i128 618970019642690137449562111, 6
   ret void
 }
 
+; CHECK: define void @initializes(ptr initializes((-4,0),(4,8)) %a)
+define void @initializes(ptr initializes((-4,0),(4,8)) %a) {
+  ret void
+}
+
 ; CHECK: attributes #0 = { noreturn }
 ; CHECK: attributes #1 = { nounwind }
 ; CHECK: attributes #2 = { memory(none) }
diff --git a/llvm/test/Verifier/initializes-attr.ll b/llvm/test/Verifier/initializes-attr.ll
new file mode 100644
index 00000000000000..5992da4dfde9ee
--- /dev/null
+++ b/llvm/test/Verifier/initializes-attr.ll
@@ -0,0 +1,31 @@
+; RUN: not llvm-as %s -o /dev/null 2>&1 | FileCheck %s
+
+; CHECK: Attribute 'initializes' requires interval lower less than upper
+; CHECK-NEXT: ptr @lower_greater_than_upper1
+define void @lower_greater_than_upper1(ptr initializes((4,0)) %a) {
+  ret void
+}
+
+; CHECK: Attribute 'initializes' requires interval lower less than upper
+; CHECK-NEXT: ptr @lower_greater_than_upper2
+define void @lower_greater_than_upper2(ptr initializes((0,4),(8,6)) %a) {
+  ret void
+}
+
+; CHECK: Attribute 'initializes' requires intervals in ascending order!
+; CHECK-NEXT: ptr @descending_order
+define void @descending_order(ptr initializes((8,12),(0,4)) %a) {
+  ret void
+}
+
+; CHECK: Attribute 'initializes' requires intervals merged!
+; CHECK-NEXT: ptr @overlapping1
+define void @overlapping1(ptr initializes((0,4),(4,8)) %a) {
+  ret void
+}
+
+; CHECK: Attribute 'initializes' requires intervals merged!
+; CHECK-NEXT: ptr @overlapping2
+define void @overlapping2(ptr initializes((0,4),(2,8)) %a) {
+  ret void
+}

>From 97233222a58ee71f3f64177867178eaf0d1932ba Mon Sep 17 00:00:00 2001
From: Haopeng Liu <haopliu at google.com>
Date: Wed, 17 Apr 2024 22:03:10 +0000
Subject: [PATCH 14/14] Add a space after commas

---
 llvm/docs/LangRef.rst                              |  2 +-
 llvm/lib/IR/Attributes.cpp                         |  9 +++------
 llvm/lib/IR/ConstantRangeList.cpp                  |  9 ++++-----
 .../Assembler/initializes-attribute-invalid.ll     | 14 +++++++-------
 llvm/test/Bitcode/attributes.ll                    |  4 ++--
 llvm/test/Verifier/initializes-attr.ll             | 10 +++++-----
 6 files changed, 22 insertions(+), 26 deletions(-)

diff --git a/llvm/docs/LangRef.rst b/llvm/docs/LangRef.rst
index 3f7cef4f6493ec..8a0a5e0d234d68 100644
--- a/llvm/docs/LangRef.rst
+++ b/llvm/docs/LangRef.rst
@@ -1621,7 +1621,7 @@ Currently, only the following parameter attributes are defined:
     ``readonly`` or a ``memory`` attribute that does not contain
     ``argmem: write``.
 
-``initializes((Lo1,Hi1),...)``
+``initializes((Lo1, Hi1), ...)``
     This attribute indicates that the function initializes the ranges of the
     pointer parameter's memory, ``[%p+LoN, %p+HiN)``. Initialization of memory
     means the first memory access is a non-volatile, non-atomic write. The
diff --git a/llvm/lib/IR/Attributes.cpp b/llvm/lib/IR/Attributes.cpp
index f61c1b08df8f02..f4d91bbbc8b2e1 100644
--- a/llvm/lib/IR/Attributes.cpp
+++ b/llvm/lib/IR/Attributes.cpp
@@ -666,12 +666,9 @@ std::string Attribute::getAsString(bool InAttrGrp) const {
     raw_string_ostream OS(Result);
     ConstantRangeList CRL = getValueAsConstantRangeList();
     OS << "initializes(";
-    interleave(
-        CRL, OS,
-        [&](ConstantRange CR) {
-          OS << "(" << CR.getLower() << "," << CR.getUpper() << ")";
-        },
-        ",");
+    interleaveComma(CRL, OS, [&](ConstantRange CR) {
+      OS << "(" << CR.getLower() << ", " << CR.getUpper() << ")";
+    });
     OS << ")";
     OS.flush();
     return Result;
diff --git a/llvm/lib/IR/ConstantRangeList.cpp b/llvm/lib/IR/ConstantRangeList.cpp
index 51e56fdb24d4f2..ce600de02bdb47 100644
--- a/llvm/lib/IR/ConstantRangeList.cpp
+++ b/llvm/lib/IR/ConstantRangeList.cpp
@@ -35,14 +35,13 @@ void ConstantRangeList::insert(const ConstantRange &NewRange) {
   }
 
   // Slow insert.
-  SmallVector<ConstantRange, 2> ExistingRanges(Ranges.begin(), Ranges.end());
   auto LowerBound =
-      std::lower_bound(ExistingRanges.begin(), ExistingRanges.end(), NewRange,
+      std::lower_bound(Ranges.begin(), Ranges.end(), NewRange,
                        [](const ConstantRange &a, const ConstantRange &b) {
                          return a.getLower().slt(b.getLower());
                        });
-  Ranges.erase(Ranges.begin() + (LowerBound - ExistingRanges.begin()),
-               Ranges.end());
+  SmallVector<ConstantRange, 2> ExistingTail(LowerBound, Ranges.end());
+  Ranges.erase(LowerBound, Ranges.end());
   if (!Ranges.empty() && NewRange.getLower().slt(Ranges.back().getUpper())) {
     APInt NewLower = Ranges.back().getLower();
     APInt NewUpper =
@@ -51,7 +50,7 @@ void ConstantRangeList::insert(const ConstantRange &NewRange) {
   } else {
     Ranges.push_back(NewRange);
   }
-  for (auto Iter = LowerBound; Iter != ExistingRanges.end(); Iter++) {
+  for (auto Iter = ExistingTail.begin(); Iter != ExistingTail.end(); Iter++) {
     if (Ranges.back().getUpper().slt(Iter->getLower())) {
       Ranges.push_back(*Iter);
     } else {
diff --git a/llvm/test/Assembler/initializes-attribute-invalid.ll b/llvm/test/Assembler/initializes-attribute-invalid.ll
index 842d67b2fb5cc5..4ba54ec56f67ab 100644
--- a/llvm/test/Assembler/initializes-attribute-invalid.ll
+++ b/llvm/test/Assembler/initializes-attribute-invalid.ll
@@ -12,37 +12,37 @@
 
 ;--- outer_left_parenthesis.ll
 ; OUTER-LEFT: expected '('
-define void @foo(ptr initializes 0,4 %a) {
+define void @foo(ptr initializes 0, 4 %a) {
   ret void
 }
 
 ;--- inner_left_parenthesis.ll
 ; INNER-LEFT: expected '('
-define void @foo(ptr initializes(0,4 %a) {
+define void @foo(ptr initializes(0, 4 %a) {
   ret void
 }
 
 ;--- inner_right_parenthesis.ll
 ; INNER-RIGHT: expected ')'
-define void @foo(ptr initializes((0,4 %a) {
+define void @foo(ptr initializes((0, 4 %a) {
   ret void
 }
 
 ;--- outer_right_parenthesis.ll
 ; OUTER-RIGHT: expected ')'
-define void @foo(ptr initializes((0,4) %a) {
+define void @foo(ptr initializes((0, 4) %a) {
   ret void
 }
 
 ;--- integer.ll
 ; INTEGER: expected integer
-define void @foo(ptr initializes((0.5,4)) %a) {
+define void @foo(ptr initializes((0.5, 4)) %a) {
   ret void
 }
 
 ;--- lower_equal_upper.ll
 ; LOWER-EQUAL-UPPER: the range should not represent the full or empty set!
-define void @foo(ptr initializes((4,4)) %a) {
+define void @foo(ptr initializes((4, 4)) %a) {
   ret void
 }
 
@@ -54,7 +54,7 @@ define void @foo(ptr initializes((0 4)) %a) {
 
 ;--- outer_comma.ll
 ; OUTER-COMMA: expected ')'
-define void @foo(ptr initializes((0,4) (8,12)) %a) {
+define void @foo(ptr initializes((0, 4) (8, 12)) %a) {
   ret void
 }
 
diff --git a/llvm/test/Bitcode/attributes.ll b/llvm/test/Bitcode/attributes.ll
index c81c5779a45b4c..006855b25564d4 100644
--- a/llvm/test/Bitcode/attributes.ll
+++ b/llvm/test/Bitcode/attributes.ll
@@ -536,8 +536,8 @@ define void @wide_range_attribute(i128 range(i128 618970019642690137449562111, 6
   ret void
 }
 
-; CHECK: define void @initializes(ptr initializes((-4,0),(4,8)) %a)
-define void @initializes(ptr initializes((-4,0),(4,8)) %a) {
+; CHECK: define void @initializes(ptr initializes((-4, 0), (4, 8)) %a)
+define void @initializes(ptr initializes((-4, 0), (4, 8)) %a) {
   ret void
 }
 
diff --git a/llvm/test/Verifier/initializes-attr.ll b/llvm/test/Verifier/initializes-attr.ll
index 5992da4dfde9ee..4c36493a2cbc43 100644
--- a/llvm/test/Verifier/initializes-attr.ll
+++ b/llvm/test/Verifier/initializes-attr.ll
@@ -2,30 +2,30 @@
 
 ; CHECK: Attribute 'initializes' requires interval lower less than upper
 ; CHECK-NEXT: ptr @lower_greater_than_upper1
-define void @lower_greater_than_upper1(ptr initializes((4,0)) %a) {
+define void @lower_greater_than_upper1(ptr initializes((4, 0)) %a) {
   ret void
 }
 
 ; CHECK: Attribute 'initializes' requires interval lower less than upper
 ; CHECK-NEXT: ptr @lower_greater_than_upper2
-define void @lower_greater_than_upper2(ptr initializes((0,4),(8,6)) %a) {
+define void @lower_greater_than_upper2(ptr initializes((0, 4), (8, 6)) %a) {
   ret void
 }
 
 ; CHECK: Attribute 'initializes' requires intervals in ascending order!
 ; CHECK-NEXT: ptr @descending_order
-define void @descending_order(ptr initializes((8,12),(0,4)) %a) {
+define void @descending_order(ptr initializes((8, 12), (0, 4)) %a) {
   ret void
 }
 
 ; CHECK: Attribute 'initializes' requires intervals merged!
 ; CHECK-NEXT: ptr @overlapping1
-define void @overlapping1(ptr initializes((0,4),(4,8)) %a) {
+define void @overlapping1(ptr initializes((0, 4), (4, 8)) %a) {
   ret void
 }
 
 ; CHECK: Attribute 'initializes' requires intervals merged!
 ; CHECK-NEXT: ptr @overlapping2
-define void @overlapping2(ptr initializes((0,4),(2,8)) %a) {
+define void @overlapping2(ptr initializes((0, 4), (2, 8)) %a) {
   ret void
 }



More information about the llvm-commits mailing list