[llvm] [IR][TableGen] Add rangeset IR attribute and RangeSet TableGen intrinsic property (PR #203623)

Kirill Vedernikov via llvm-commits llvm-commits at lists.llvm.org
Fri Jun 12 13:11:52 PDT 2026


https://github.com/kvederni created https://github.com/llvm/llvm-project/pull/203623

This change introduces a rangeset IR attribute and a TableGen intrinsic property for the same. Existing IR `range(<ty> lo, hi)` syntax and half-open semantics are unchanged. Existing `Range<idx, lo, hi>` keeps its current half-open semantics and continues to emit the existing range attribute.

IR syntax: `rangeset(<ty> (<lo0>, <hi0>), (<lo1>, <hi1>), ...)` where `(<lo>, <hi>)` are closed intervals and must be ordered and non-overlapped.

TableGen syntax: `RangeSet<idx, [[lo0, hi0], [lo1, hi1], ...]>` where `[lo, hi]` are closed ordered and non-overlapped intervals.

>From 2ceba3e66017cdf8950a67b01f8e397b6c99f2f2 Mon Sep 17 00:00:00 2001
From: Kirill Vedernikov <kvedernikov at nvidia.com>
Date: Fri, 12 Jun 2026 21:41:21 +0200
Subject: [PATCH] [IR][TableGen] Add rangeset IR attribute and RangeSet
 TableGen intrinsic property

This change introduces a rangeset IR attribute and a TableGen intrinsic
property for the same. Existing IR range(<ty> lo, hi) syntax and
half-open semantics are unchanged. Existing Range<idx, lo, hi> keeps its
current half-open semantics and continues to emit the existing range
attribute.

IR syntax: rangeset(<ty> (<lo0>, <hi0>), (<lo1>, <hi1>), ...) where
(<lo>, <hi>) are closed intervals and must be ordered and
non-overlapped.

TableGen syntax: RangeSet<idx, [[lo0, hi0], [lo1, hi1], ...]> where
[lo, hi] are closed ordered and non-overlapped intervals.
---
 llvm/include/llvm/AsmParser/LLParser.h        |   2 +
 llvm/include/llvm/Bitcode/LLVMBitCodes.h      |   1 +
 llvm/include/llvm/IR/Attributes.h             |  10 ++
 llvm/include/llvm/IR/Attributes.td            |   4 +
 llvm/include/llvm/IR/Intrinsics.td            |  23 +++-
 llvm/lib/AsmParser/LLParser.cpp               |  85 ++++++++++--
 llvm/lib/Bitcode/Reader/BitcodeReader.cpp     |   6 +-
 llvm/lib/Bitcode/Writer/BitcodeWriter.cpp     |   2 +
 .../CodeGen/SelectionDAG/TargetLowering.cpp   |   9 +-
 llvm/lib/IR/Attributes.cpp                    | 129 +++++++++++++++++-
 llvm/lib/IR/Verifier.cpp                      |  30 +++-
 llvm/test/Assembler/invalid-immarg.ll         |  22 +--
 .../Assembler/rangeset-attribute-invalid.ll   |  36 +++++
 llvm/test/Assembler/rangeset-attribute.ll     |  19 +++
 .../TableGen/intrinsic-rangeset-errors.td     |  27 ++++
 llvm/test/TableGen/intrinsic-rangeset.td      |  28 ++++
 llvm/unittests/IR/AttributesTest.cpp          |   9 ++
 .../TableGen/Basic/CodeGenIntrinsics.cpp      |  67 +++++++++
 llvm/utils/TableGen/Basic/CodeGenIntrinsics.h |  13 +-
 .../utils/TableGen/Basic/IntrinsicEmitter.cpp |  16 ++-
 20 files changed, 500 insertions(+), 38 deletions(-)
 create mode 100644 llvm/test/Assembler/rangeset-attribute-invalid.ll
 create mode 100644 llvm/test/Assembler/rangeset-attribute.ll
 create mode 100644 llvm/test/TableGen/intrinsic-rangeset-errors.td
 create mode 100644 llvm/test/TableGen/intrinsic-rangeset.td

diff --git a/llvm/include/llvm/AsmParser/LLParser.h b/llvm/include/llvm/AsmParser/LLParser.h
index 788b56cb78f08..22df21290c015 100644
--- a/llvm/include/llvm/AsmParser/LLParser.h
+++ b/llvm/include/llvm/AsmParser/LLParser.h
@@ -309,6 +309,7 @@ namespace llvm {
       Loc = Lex.getLoc();
       return parseUInt64(Val);
     }
+    bool parseAPInt(unsigned BitWidth, APInt &Val);
     bool parseFlag(unsigned &Val);
 
     bool parseStringAttribute(AttrBuilder &B);
@@ -412,6 +413,7 @@ namespace llvm {
                                     std::vector<unsigned> &FwdRefAttrGrps,
                                     bool inAttrGrp, LocTy &BuiltinLoc);
     bool parseRangeAttr(AttrBuilder &B);
+    bool parseRangeSetAttr(AttrBuilder &B);
     bool parseInitializesAttr(AttrBuilder &B);
     bool parseCapturesAttr(AttrBuilder &B);
     bool parseRequiredTypeAttr(AttrBuilder &B, lltok::Kind AttrToken,
diff --git a/llvm/include/llvm/Bitcode/LLVMBitCodes.h b/llvm/include/llvm/Bitcode/LLVMBitCodes.h
index 95787c595dff7..db8cdd7b93936 100644
--- a/llvm/include/llvm/Bitcode/LLVMBitCodes.h
+++ b/llvm/include/llvm/Bitcode/LLVMBitCodes.h
@@ -820,6 +820,7 @@ enum AttributeKindCodes {
   ATTR_KIND_DENORMAL_FPENV = 106,
   ATTR_KIND_NOOUTLINE = 107,
   ATTR_KIND_FLATTEN = 108,
+  ATTR_KIND_RANGE_SET = 109,
 };
 
 enum ComdatSelectionKindCodes {
diff --git a/llvm/include/llvm/IR/Attributes.h b/llvm/include/llvm/IR/Attributes.h
index 833f2a033a067..99691801f7f8f 100644
--- a/llvm/include/llvm/IR/Attributes.h
+++ b/llvm/include/llvm/IR/Attributes.h
@@ -355,6 +355,9 @@ class Attribute {
   /// Returns the value of the range attribute.
   LLVM_ABI const ConstantRange &getRange() const;
 
+  /// Returns the value of the rangeset attribute.
+  LLVM_ABI ArrayRef<ConstantRange> getRangeSet() const;
+
   /// Returns the value of the initializes attribute.
   LLVM_ABI ArrayRef<ConstantRange> getInitializes() const;
 
@@ -1337,6 +1340,9 @@ class AttrBuilder {
   /// Add range attribute.
   LLVM_ABI AttrBuilder &addRangeAttr(const ConstantRange &CR);
 
+  /// Add rangeset attribute.
+  LLVM_ABI AttrBuilder &addRangeSetAttr(ArrayRef<ConstantRange> CRs);
+
   /// Add a ConstantRangeList attribute with the given ranges.
   LLVM_ABI AttrBuilder &addConstantRangeListAttr(Attribute::AttrKind Kind,
                                                  ArrayRef<ConstantRange> Val);
@@ -1367,6 +1373,10 @@ enum AttributeSafetyKind : uint8_t {
 /// follows the same type rules as FPMathOperator.
 LLVM_ABI bool isNoFPClassCompatibleType(Type *Ty);
 
+/// Returns true if the ranges are ordered, non-overlapping, and canonical for
+/// the 'rangeset' attribute.
+LLVM_ABI bool isOrderedRangeSet(ArrayRef<ConstantRange> Ranges);
+
 /// Which attributes cannot be applied to a type. The argument \p AS
 /// is used as a hint for the attributes whose compatibility is being
 /// checked against \p Ty. This does not mean the return will be a
diff --git a/llvm/include/llvm/IR/Attributes.td b/llvm/include/llvm/IR/Attributes.td
index 34032e341d85e..5b1417d128868 100644
--- a/llvm/include/llvm/IR/Attributes.td
+++ b/llvm/include/llvm/IR/Attributes.td
@@ -279,6 +279,10 @@ def Preallocated : TypeAttr<"preallocated", IntersectPreserve, [FnAttr, ParamAtt
 /// Parameter or return value is within the specified range.
 def Range : ConstantRangeAttr<"range", IntersectCustom, [ParamAttr, RetAttr]>;
 
+/// Parameter or return value is within one of the specified ranges.
+def RangeSet : ConstantRangeListAttr<"rangeset", IntersectCustom,
+                                     [ParamAttr, RetAttr]>;
+
 /// Function does not access memory.
 def ReadNone : EnumAttr<"readnone", IntersectAnd, [ParamAttr]>;
 
diff --git a/llvm/include/llvm/IR/Intrinsics.td b/llvm/include/llvm/IR/Intrinsics.td
index b1b2bb2a72c65..70aa5656da425 100644
--- a/llvm/include/llvm/IR/Intrinsics.td
+++ b/llvm/include/llvm/IR/Intrinsics.td
@@ -154,10 +154,29 @@ class ReadNone<ArgIndex idx> : IntrinsicProperty {
   int ArgNo = idx.Value;
 }
 
+// The return value or argument is in the union of the specified closed ranges.
+// Range entries must be ordered and non-overlapping.
+class RangeSet<AttrIndex idx, list<list<int>> ranges> : IntrinsicProperty {
+  list<list<int>> ValidRanges = !filter(r, ranges, !eq(!size(r), 2));
+
+  assert !not(!empty(ranges)), "RangeSet requires at least one range";
+  assert !eq(!size(ValidRanges), !size(ranges)),
+         "RangeSet requires each range to have exactly two bounds";
+  assert !empty(!filter(r, ValidRanges, !lt(r[1], r[0]))),
+         "RangeSet requires lower <= upper for each range";
+  assert !empty(!filter(i, !range(1, !size(ValidRanges)),
+                        !le(ValidRanges[i][0],
+                            ValidRanges[!sub(i, 1)][1]))),
+         "RangeSet requires ordered and non-overlapping ranges";
+
+  int ArgNo = idx.Value;
+  list<list<int>> Ranges = ranges;
+}
+
 // The return value or argument is in the range [lower, upper),
 // where lower and upper are interpreted as signed integers.
-class Range<AttrIndex idx, int lower, int upper> : IntrinsicProperty {
-  int ArgNo = idx.Value;
+class Range<AttrIndex idx, int lower, int upper>
+    : RangeSet<idx, [[lower, !sub(upper, 1)]]> {
   int Lower = lower;
   int Upper = upper;
 }
diff --git a/llvm/lib/AsmParser/LLParser.cpp b/llvm/lib/AsmParser/LLParser.cpp
index 75e6add0ec76f..cee9612aeaa15 100644
--- a/llvm/lib/AsmParser/LLParser.cpp
+++ b/llvm/lib/AsmParser/LLParser.cpp
@@ -1700,6 +1700,8 @@ bool LLParser::parseEnumAttribute(Attribute::AttrKind Attr, AttrBuilder &B,
   }
   case Attribute::Range:
     return parseRangeAttr(B);
+  case Attribute::RangeSet:
+    return parseRangeSetAttr(B);
   case Attribute::Initializes:
     return parseInitializesAttr(B);
   case Attribute::Captures:
@@ -1956,6 +1958,18 @@ bool LLParser::parseUInt64(uint64_t &Val) {
   return false;
 }
 
+/// parseAPInt
+///   ::= apint
+bool LLParser::parseAPInt(unsigned BitWidth, APInt &Val) {
+  if (Lex.getKind() != lltok::APSInt)
+    return tokError("expected integer");
+  if (Lex.getAPSIntVal().getBitWidth() > BitWidth)
+    return tokError("integer is too large for the bit width of specified type");
+  Val = Lex.getAPSIntVal().extend(BitWidth);
+  Lex.Lex();
+  return false;
+}
+
 /// parseTLSModel
 ///   := 'localdynamic'
 ///   := 'initialexec'
@@ -3363,17 +3377,6 @@ bool LLParser::parseRangeAttr(AttrBuilder &B) {
   Type *Ty = nullptr;
   LocTy TyLoc;
 
-  auto ParseAPSInt = [&](unsigned BitWidth, APInt &Val) {
-    if (Lex.getKind() != lltok::APSInt)
-      return tokError("expected integer");
-    if (Lex.getAPSIntVal().getBitWidth() > BitWidth)
-      return tokError(
-          "integer is too large for the bit width of specified type");
-    Val = Lex.getAPSIntVal().extend(BitWidth);
-    Lex.Lex();
-    return false;
-  };
-
   if (parseToken(lltok::lparen, "expected '('") || parseType(Ty, TyLoc))
     return true;
   if (!Ty->isIntegerTy())
@@ -3381,8 +3384,8 @@ bool LLParser::parseRangeAttr(AttrBuilder &B) {
 
   unsigned BitWidth = Ty->getPrimitiveSizeInBits();
 
-  if (ParseAPSInt(BitWidth, Lower) ||
-      parseToken(lltok::comma, "expected ','") || ParseAPSInt(BitWidth, Upper))
+  if (parseAPInt(BitWidth, Lower) || parseToken(lltok::comma, "expected ','") ||
+      parseAPInt(BitWidth, Upper))
     return true;
   if (Lower == Upper && !Lower.isZero())
     return tokError("the range represent the empty set but limits aren't 0!");
@@ -3394,6 +3397,62 @@ bool LLParser::parseRangeAttr(AttrBuilder &B) {
   return false;
 }
 
+/// parseRangeSetAttr
+///   ::= rangeset(<ty> (<n>,<n>)[,(<n>,<n>)]*)
+bool LLParser::parseRangeSetAttr(AttrBuilder &B) {
+  Lex.Lex();
+
+  Type *Ty = nullptr;
+  LocTy TyLoc;
+
+  if (parseToken(lltok::lparen, "expected '('"))
+    return true;
+
+  TyLoc = Lex.getLoc();
+  if (Lex.getKind() != lltok::Type)
+    return tokError("expected type");
+  Ty = Lex.getTyVal();
+  Lex.Lex();
+
+  if (!Ty->isIntegerTy())
+    return error(TyLoc, "the rangeset must have integer type!");
+
+  unsigned BitWidth = Ty->getPrimitiveSizeInBits();
+
+  SmallVector<ConstantRange, 2> RangeList;
+  do {
+    APInt Lower, Upper;
+    if (parseToken(lltok::lparen, "expected '('") ||
+        parseAPInt(BitWidth, Lower) ||
+        parseToken(lltok::comma, "expected ','") ||
+        parseAPInt(BitWidth, Upper) ||
+        parseToken(lltok::rparen, "expected ')'"))
+      return true;
+
+    if (Upper.slt(Lower))
+      return tokError("rangeset requires lower <= upper");
+
+    ConstantRange Range = ConstantRange::getNonEmpty(Lower, Upper + 1);
+    if (!RangeList.empty() && !RangeList.back().isFullSet() &&
+        !RangeList.back().getUpper().isMinSignedValue() &&
+        Lower == RangeList.back().getUpper()) {
+      RangeList.back() = ConstantRange::getNonEmpty(RangeList.back().getLower(),
+                                                    Range.getUpper());
+      continue;
+    }
+    RangeList.push_back(Range);
+  } while (EatIfPresent(lltok::comma));
+
+  if (parseToken(lltok::rparen, "expected ')'"))
+    return true;
+
+  if (!AttributeFuncs::isOrderedRangeSet(RangeList))
+    return tokError("Invalid (unordered or overlapping) range set");
+
+  B.addRangeSetAttr(RangeList);
+  return false;
+}
+
 /// parseInitializesAttr
 ///   ::= initializes((Lo1,Hi1),(Lo2,Hi2),...)
 bool LLParser::parseInitializesAttr(AttrBuilder &B) {
diff --git a/llvm/lib/Bitcode/Reader/BitcodeReader.cpp b/llvm/lib/Bitcode/Reader/BitcodeReader.cpp
index f46d240e7ed9b..8e98d7e3d49ea 100644
--- a/llvm/lib/Bitcode/Reader/BitcodeReader.cpp
+++ b/llvm/lib/Bitcode/Reader/BitcodeReader.cpp
@@ -2286,6 +2286,8 @@ static Attribute::AttrKind getAttrFromCode(uint64_t Code) {
     return Attribute::DeadOnUnwind;
   case bitc::ATTR_KIND_RANGE:
     return Attribute::Range;
+  case bitc::ATTR_KIND_RANGE_SET:
+    return Attribute::RangeSet;
   case bitc::ATTR_KIND_INITIALIZES:
     return Attribute::Initializes;
   case bitc::ATTR_KIND_CORO_ELIDE_SAFE:
@@ -2545,7 +2547,9 @@ Error BitcodeReader::parseAttributeGroupBlock() {
           }
           i--;
 
-          if (!ConstantRangeList::isOrderedRanges(Val))
+          if (Kind == Attribute::RangeSet
+                  ? !AttributeFuncs::isOrderedRangeSet(Val)
+                  : !ConstantRangeList::isOrderedRanges(Val))
             return error("Invalid (unordered or overlapping) range list");
           B.addConstantRangeListAttr(Kind, Val);
         } else {
diff --git a/llvm/lib/Bitcode/Writer/BitcodeWriter.cpp b/llvm/lib/Bitcode/Writer/BitcodeWriter.cpp
index f4857461ca58e..afe090a007cd3 100644
--- a/llvm/lib/Bitcode/Writer/BitcodeWriter.cpp
+++ b/llvm/lib/Bitcode/Writer/BitcodeWriter.cpp
@@ -994,6 +994,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::RangeSet:
+    return bitc::ATTR_KIND_RANGE_SET;
   case Attribute::Initializes:
     return bitc::ATTR_KIND_INITIALIZES;
   case Attribute::NoExt:
diff --git a/llvm/lib/CodeGen/SelectionDAG/TargetLowering.cpp b/llvm/lib/CodeGen/SelectionDAG/TargetLowering.cpp
index 95e03a419fb43..136958fadbca5 100644
--- a/llvm/lib/CodeGen/SelectionDAG/TargetLowering.cpp
+++ b/llvm/lib/CodeGen/SelectionDAG/TargetLowering.cpp
@@ -70,10 +70,11 @@ bool TargetLowering::isInTailCallPosition(SelectionDAG &DAG, SDNode *Node,
   // the return. Ignore following attributes because they don't affect the
   // call sequence.
   AttrBuilder CallerAttrs(F.getContext(), F.getAttributes().getRetAttrs());
-  for (const auto &Attr : {Attribute::Alignment, Attribute::Dereferenceable,
-                           Attribute::DereferenceableOrNull, Attribute::NoAlias,
-                           Attribute::NonNull, Attribute::NoUndef,
-                           Attribute::Range, Attribute::NoFPClass})
+  for (const auto &Attr :
+       {Attribute::Alignment, Attribute::Dereferenceable,
+        Attribute::DereferenceableOrNull, Attribute::NoAlias,
+        Attribute::NonNull, Attribute::NoUndef, Attribute::Range,
+        Attribute::RangeSet, Attribute::NoFPClass})
     CallerAttrs.removeAttribute(Attr);
 
   if (CallerAttrs.hasAttributes())
diff --git a/llvm/lib/IR/Attributes.cpp b/llvm/lib/IR/Attributes.cpp
index 4087b25951a1c..dec94c313c884 100644
--- a/llvm/lib/IR/Attributes.cpp
+++ b/llvm/lib/IR/Attributes.cpp
@@ -92,6 +92,64 @@ unpackVScaleRangeArgs(uint64_t Value) {
                         MaxValue > 0 ? MaxValue : std::optional<unsigned>());
 }
 
+static void appendRangeSetRange(SmallVectorImpl<ConstantRange> &Ranges,
+                                const ConstantRange &CR) {
+  if (CR.isEmptySet())
+    return;
+  if (CR.isFullSet()) {
+    Ranges.assign(1, CR);
+    return;
+  }
+  if (Ranges.empty()) {
+    Ranges.push_back(CR);
+    return;
+  }
+  if (Ranges.front().isFullSet())
+    return;
+
+  APInt PreviousUpper = Ranges.back().getUpper();
+  if (PreviousUpper.isMinSignedValue())
+    return;
+  if (CR.getLower().sle(PreviousUpper)) {
+    APInt NewUpper = CR.getUpper().isMinSignedValue()
+                         ? APInt::getSignedMinValue(CR.getBitWidth())
+                         : APIntOps::smax(PreviousUpper, CR.getUpper());
+    Ranges.back() = ConstantRange(Ranges.back().getLower(), NewUpper);
+    if (Ranges.back().isFullSet())
+      Ranges.truncate(1);
+    return;
+  }
+
+  Ranges.push_back(CR);
+}
+
+static SmallVector<ConstantRange, 2>
+unionRangeSetLists(ArrayRef<ConstantRange> LHS, ArrayRef<ConstantRange> RHS) {
+  assert(AttributeFuncs::isOrderedRangeSet(LHS) &&
+         AttributeFuncs::isOrderedRangeSet(RHS) &&
+         "rangeset attribute must be ordered");
+  if (LHS.empty())
+    return SmallVector<ConstantRange, 2>(RHS);
+  if (RHS.empty())
+    return SmallVector<ConstantRange, 2>(LHS);
+
+  SmallVector<ConstantRange, 2> Result;
+  size_t LHSIdx = 0, RHSIdx = 0;
+  while (LHSIdx < LHS.size() || RHSIdx < RHS.size()) {
+    const ConstantRange *NextRange;
+    if (RHSIdx == RHS.size() ||
+        (LHSIdx < LHS.size() &&
+         LHS[LHSIdx].getLower().slt(RHS[RHSIdx].getLower())))
+      NextRange = &LHS[LHSIdx++];
+    else
+      NextRange = &RHS[RHSIdx++];
+    appendRangeSetRange(Result, *NextRange);
+    if (Result.size() == 1 && Result.front().isFullSet())
+      break;
+  }
+  return Result;
+}
+
 Attribute Attribute::get(LLVMContext &Context, Attribute::AttrKind Kind,
                          uint64_t Val) {
   bool IsIntAttr = Attribute::isIntAttrKind(Kind);
@@ -529,6 +587,12 @@ const ConstantRange &Attribute::getRange() const {
   return pImpl->getValueAsConstantRange();
 }
 
+ArrayRef<ConstantRange> Attribute::getRangeSet() const {
+  assert(hasAttribute(Attribute::RangeSet) &&
+         "Trying to get rangeset attr from non-ConstantRangeList attribute");
+  return pImpl->getValueAsConstantRangeList();
+}
+
 ArrayRef<ConstantRange> Attribute::getInitializes() const {
   assert(hasAttribute(Attribute::Initializes) &&
          "Trying to get initializes attr from non-ConstantRangeList attribute");
@@ -739,6 +803,21 @@ std::string Attribute::getAsString(bool InAttrGrp) const {
     return Result;
   }
 
+  if (hasAttribute(Attribute::RangeSet)) {
+    std::string Result;
+    raw_string_ostream OS(Result);
+    ArrayRef<ConstantRange> Ranges = getRangeSet();
+    assert(!Ranges.empty() &&
+           "rangeset attribute must have at least one range");
+    OS << "rangeset(";
+    OS << "i" << Ranges.front().getBitWidth() << " ";
+    interleaveComma(Ranges, OS, [&](const ConstantRange &CR) {
+      OS << "(" << CR.getLower() << ", " << CR.getUpper() - 1 << ")";
+    });
+    OS << ")";
+    return Result;
+  }
+
   if (hasAttribute(Attribute::Initializes)) {
     std::string Result;
     raw_string_ostream OS(Result);
@@ -1150,6 +1229,11 @@ AttributeSet::intersectWith(LLVMContext &C, AttributeSet Other) const {
         if (!NewRange.isFullSet())
           Intersected.addRangeAttr(NewRange);
       } break;
+      case Attribute::RangeSet: {
+        SmallVector<ConstantRange> NewRangeSet =
+            unionRangeSetLists(Attr0.getRangeSet(), Attr1.getRangeSet());
+        Intersected.addRangeSetAttr(NewRangeSet);
+      } break;
       default:
         llvm_unreachable("Unknown attribute with custom intersection rule");
       }
@@ -2371,9 +2455,17 @@ AttrBuilder &AttrBuilder::addRangeAttr(const ConstantRange &CR) {
   return addConstantRangeAttr(Attribute::Range, CR);
 }
 
+AttrBuilder &AttrBuilder::addRangeSetAttr(ArrayRef<ConstantRange> CRs) {
+  if (CRs.empty() || (CRs.size() == 1 && CRs.front().isFullSet()))
+    return *this;
+  return addAttribute(Attribute::get(Ctx, Attribute::RangeSet, CRs));
+}
+
 AttrBuilder &
 AttrBuilder::addConstantRangeListAttr(Attribute::AttrKind Kind,
                                       ArrayRef<ConstantRange> Val) {
+  if (Kind == Attribute::RangeSet)
+    return addRangeSetAttr(Val);
   return addAttribute(Attribute::get(Ctx, Kind, Val));
 }
 
@@ -2479,6 +2571,33 @@ bool AttributeFuncs::isNoFPClassCompatibleType(Type *Ty) {
   return FPMathOperator::isSupportedFloatingPointType(Ty);
 }
 
+bool AttributeFuncs::isOrderedRangeSet(ArrayRef<ConstantRange> Ranges) {
+  if (Ranges.empty())
+    return true;
+
+  for (unsigned I = 0; I < Ranges.size(); ++I) {
+    const ConstantRange &Range = Ranges[I];
+    if (Range.isEmptySet())
+      return false;
+    if (I != 0 && Range.getBitWidth() != Ranges.front().getBitWidth())
+      return false;
+    if (Range.isFullSet())
+      return Ranges.size() == 1;
+    if (!Range.getLower().slt(Range.getUpper()) &&
+        !Range.getUpper().isMinSignedValue())
+      return false;
+    if (I == 0)
+      continue;
+
+    const ConstantRange &PrevRange = Ranges[I - 1];
+    if (PrevRange.isFullSet() || PrevRange.getUpper().isMinSignedValue())
+      return false;
+    if (Range.getLower().sle(PrevRange.getUpper()))
+      return false;
+  }
+  return true;
+}
+
 /// Which attributes cannot be applied to a type.
 AttributeMask AttributeFuncs::typeIncompatible(Type *Ty, AttributeSet AS,
                                                AttributeSafetyKind ASK) {
@@ -2499,12 +2618,20 @@ AttributeMask AttributeFuncs::typeIncompatible(Type *Ty, AttributeSet AS,
   if (!Ty->isIntOrIntVectorTy()) {
     // Attributes that only apply to integers or vector of integers.
     if (ASK & ASK_SAFE_TO_DROP)
-      Incompatible.addAttribute(Attribute::Range);
+      Incompatible.addAttribute(Attribute::Range)
+          .addAttribute(Attribute::RangeSet);
   } else {
     Attribute RangeAttr = AS.getAttribute(Attribute::Range);
     if (RangeAttr.isValid() &&
         RangeAttr.getRange().getBitWidth() != Ty->getScalarSizeInBits())
       Incompatible.addAttribute(Attribute::Range);
+    Attribute RangeSetAttr = AS.getAttribute(Attribute::RangeSet);
+    if (RangeSetAttr.isValid()) {
+      ArrayRef<ConstantRange> Ranges = RangeSetAttr.getRangeSet();
+      if (!Ranges.empty() &&
+          Ranges.front().getBitWidth() != Ty->getScalarSizeInBits())
+        Incompatible.addAttribute(Attribute::RangeSet);
+    }
   }
 
   if (!Ty->isPointerTy()) {
diff --git a/llvm/lib/IR/Verifier.cpp b/llvm/lib/IR/Verifier.cpp
index 6d900ce0d917f..afc36dafc1620 100644
--- a/llvm/lib/IR/Verifier.cpp
+++ b/llvm/lib/IR/Verifier.cpp
@@ -2224,11 +2224,12 @@ void Verifier::verifyParameterAttrs(AttributeSet Attrs, Type *Ty,
           V);
 
   if (Attrs.hasAttribute(Attribute::ImmArg)) {
-    unsigned AttrCount =
-        Attrs.getNumAttributes() - Attrs.hasAttribute(Attribute::Range);
+    unsigned AttrCount = Attrs.getNumAttributes() -
+                         Attrs.hasAttribute(Attribute::Range) -
+                         Attrs.hasAttribute(Attribute::RangeSet);
     Check(AttrCount == 1,
           "Attribute 'immarg' is incompatible with other attributes except the "
-          "'range' attribute",
+          "'range' and 'rangeset' attributes",
           V);
   }
 
@@ -2373,6 +2374,16 @@ 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::RangeSet)) {
+    ArrayRef<ConstantRange> Ranges =
+        Attrs.getAttribute(Attribute::RangeSet).getRangeSet();
+    Check(!Ranges.empty(), "Attribute 'rangeset' does not support empty list",
+          V);
+    Check(AttributeFuncs::isOrderedRangeSet(Ranges),
+          "Attribute 'rangeset' does not support unordered ranges", V);
+    Check(Ty->isIntOrIntVectorTy(Ranges.front().getBitWidth()),
+          "RangeSet bit width must match type bit width!", V);
+  }
 }
 
 void Verifier::checkUnsignedBaseTenFuncAttr(AttributeList Attrs, StringRef Attr,
@@ -4066,6 +4077,19 @@ void Verifier::visitCallBase(CallBase &Call) {
                 Call);
         }
       }
+      if (Call.paramHasAttr(i, Attribute::RangeSet)) {
+        if (auto *CI = dyn_cast<ConstantInt>(ArgVal)) {
+          ArrayRef<ConstantRange> Ranges =
+              Call.getParamAttr(i, Attribute::RangeSet).getRangeSet();
+          bool IsContained = llvm::any_of(Ranges, [&](const ConstantRange &CR) {
+            return CR.contains(CI->getValue());
+          });
+          Check(IsContained,
+                "immarg value " + Twine(CI->getValue().getSExtValue()) +
+                    " out of rangeset",
+                Call);
+        }
+      }
     }
 
     if (Call.paramHasAttr(i, Attribute::Preallocated)) {
diff --git a/llvm/test/Assembler/invalid-immarg.ll b/llvm/test/Assembler/invalid-immarg.ll
index df69a66c67332..c04f1d74b882c 100644
--- a/llvm/test/Assembler/invalid-immarg.ll
+++ b/llvm/test/Assembler/invalid-immarg.ll
@@ -1,34 +1,34 @@
 ; RUN: not llvm-as < %s -o /dev/null 2>&1 | FileCheck %s
 
-; CHECK: Attribute 'immarg' is incompatible with other attributes except the 'range' attribute
+; CHECK: Attribute 'immarg' is incompatible with other attributes except the 'range' and 'rangeset' attributes
 declare void @llvm.immarg.byval(ptr byval(i32) immarg)
 
-; CHECK: Attribute 'immarg' is incompatible with other attributes except the 'range' attribute
+; CHECK: Attribute 'immarg' is incompatible with other attributes except the 'range' and 'rangeset' attributes
 declare void @llvm.immarg.inalloca(ptr inalloca(i32) immarg)
 
-; CHECK: Attribute 'immarg' is incompatible with other attributes except the 'range' attribute
+; CHECK: Attribute 'immarg' is incompatible with other attributes except the 'range' and 'rangeset' attributes
 declare void @llvm.immarg.inreg(i32 inreg immarg)
 
-; CHECK: Attribute 'immarg' is incompatible with other attributes except the 'range' attribute
+; CHECK: Attribute 'immarg' is incompatible with other attributes except the 'range' and 'rangeset' attributes
 declare void @llvm.immarg.nest(ptr nest immarg)
 
-; CHECK: Attribute 'immarg' is incompatible with other attributes except the 'range' attribute
+; CHECK: Attribute 'immarg' is incompatible with other attributes except the 'range' and 'rangeset' attributes
 declare void @llvm.immarg.sret(ptr sret(i32) immarg)
 
-; CHECK: Attribute 'immarg' is incompatible with other attributes except the 'range' attribute
+; CHECK: Attribute 'immarg' is incompatible with other attributes except the 'range' and 'rangeset' attributes
 declare void @llvm.immarg.zeroext(i32 zeroext immarg)
 
-; CHECK: Attribute 'immarg' is incompatible with other attributes except the 'range' attribute
+; CHECK: Attribute 'immarg' is incompatible with other attributes except the 'range' and 'rangeset' attributes
 declare void @llvm.immarg.signext(i32 signext immarg)
 
-; CHECK: Attribute 'immarg' is incompatible with other attributes except the 'range' attribute
+; CHECK: Attribute 'immarg' is incompatible with other attributes except the 'range' and 'rangeset' attributes
 declare void @llvm.immarg.returned(i32 returned immarg)
 
-; CHECK: Attribute 'immarg' is incompatible with other attributes except the 'range' attribute
+; CHECK: Attribute 'immarg' is incompatible with other attributes except the 'range' and 'rangeset' attributes
 declare void @llvm.immarg.noalias(ptr noalias immarg)
 
-; CHECK: Attribute 'immarg' is incompatible with other attributes except the 'range' attribute
+; CHECK: Attribute 'immarg' is incompatible with other attributes except the 'range' and 'rangeset' attributes
 declare void @llvm.immarg.readnone(ptr readnone immarg)
 
-; CHECK: Attribute 'immarg' is incompatible with other attributes except the 'range' attribute
+; CHECK: Attribute 'immarg' is incompatible with other attributes except the 'range' and 'rangeset' attributes
 declare void @llvm.immarg.readonly(ptr readonly immarg)
diff --git a/llvm/test/Assembler/rangeset-attribute-invalid.ll b/llvm/test/Assembler/rangeset-attribute-invalid.ll
new file mode 100644
index 0000000000000..98605a1ac4b3a
--- /dev/null
+++ b/llvm/test/Assembler/rangeset-attribute-invalid.ll
@@ -0,0 +1,36 @@
+; RUN: split-file %s %t
+; RUN: not llvm-as < %t/non_integer_type.ll -o /dev/null 2>&1 | FileCheck %s --check-prefix=NON-INTEGER-TYPE
+; RUN: not llvm-as < %t/missing_type.ll -o /dev/null 2>&1 | FileCheck %s --check-prefix=MISSING-TYPE
+; RUN: not llvm-as < %t/empty.ll -o /dev/null 2>&1 | FileCheck %s --check-prefix=EMPTY
+; RUN: not llvm-as < %t/bad_bounds.ll -o /dev/null 2>&1 | FileCheck %s --check-prefix=BAD-BOUNDS
+; RUN: not llvm-as < %t/overlap.ll -o /dev/null 2>&1 | FileCheck %s --check-prefix=OVERLAP
+; RUN: not llvm-as < %t/unordered.ll -o /dev/null 2>&1 | FileCheck %s --check-prefix=UNORDERED
+; RUN: not llvm-as < %t/missing_comma.ll -o /dev/null 2>&1 | FileCheck %s --check-prefix=MISSING-COMMA
+
+;--- non_integer_type.ll
+; NON-INTEGER-TYPE: the rangeset must have integer type!
+declare rangeset(float (0, 2)) float @non_integer_type()
+
+;--- missing_type.ll
+; MISSING-TYPE: expected type
+declare rangeset((0, 2)) i32 @missing_type()
+
+;--- empty.ll
+; EMPTY: expected '('
+declare rangeset(i32) i32 @empty()
+
+;--- bad_bounds.ll
+; BAD-BOUNDS: rangeset requires lower <= upper
+declare rangeset(i32 (7, 5)) i32 @bad_bounds()
+
+;--- overlap.ll
+; OVERLAP: Invalid (unordered or overlapping) range set
+declare rangeset(i32 (0, 2), (2, 4)) i32 @overlap()
+
+;--- unordered.ll
+; UNORDERED: Invalid (unordered or overlapping) range set
+declare rangeset(i32 (5, 8), (0, 2)) i32 @unordered()
+
+;--- missing_comma.ll
+; MISSING-COMMA: expected ','
+declare rangeset(i32 (0 2)) i32 @missing_comma()
diff --git a/llvm/test/Assembler/rangeset-attribute.ll b/llvm/test/Assembler/rangeset-attribute.ll
new file mode 100644
index 0000000000000..a47ab51040fb2
--- /dev/null
+++ b/llvm/test/Assembler/rangeset-attribute.ll
@@ -0,0 +1,19 @@
+; RUN: llvm-as < %s | llvm-dis | FileCheck %s
+
+; CHECK: declare rangeset(i32 (0, 2), (5, 8)) i32 @ret_rangeset()
+declare rangeset(i32 (0, 2), (5, 8)) i32 @ret_rangeset()
+
+; CHECK: declare void @param_rangeset(i32 rangeset(i32 (0, 2), (5, 8)))
+declare void @param_rangeset(i32 rangeset(i32 (0, 2), (5, 8)))
+
+; CHECK: declare rangeset(i32 (0, 2), (5, 8)) <4 x i32> @ret_rangeset_vec()
+declare rangeset(i32 (0, 2), (5, 8)) <4 x i32> @ret_rangeset_vec()
+
+; CHECK: declare void @param_rangeset_vec(<4 x i32> rangeset(i32 (0, 2), (5, 8)))
+declare void @param_rangeset_vec(<4 x i32> rangeset(i32 (0, 2), (5, 8)))
+
+; CHECK: declare rangeset(i32 (1, 2147483647)) i32 @ret_rangeset_terminal()
+declare rangeset(i32 (1, 2147483647)) i32 @ret_rangeset_terminal()
+
+; CHECK: declare rangeset(i32 (0, 4), (6, 8)) i32 @ret_rangeset_coalesced()
+declare rangeset(i32 (0, 2), (3, 4), (6, 8)) i32 @ret_rangeset_coalesced()
diff --git a/llvm/test/TableGen/intrinsic-rangeset-errors.td b/llvm/test/TableGen/intrinsic-rangeset-errors.td
new file mode 100644
index 0000000000000..795220cb3d00b
--- /dev/null
+++ b/llvm/test/TableGen/intrinsic-rangeset-errors.td
@@ -0,0 +1,27 @@
+// RUN: not llvm-tblgen -gen-intrinsic-impl -I %p/../../include -DEMPTY %s 2>&1 | FileCheck --check-prefix=EMPTY %s -DFILE=%s
+// RUN: not llvm-tblgen -gen-intrinsic-impl -I %p/../../include -DBAD_BOUNDS %s 2>&1 | FileCheck --check-prefix=BAD-BOUNDS %s -DFILE=%s
+// RUN: not llvm-tblgen -gen-intrinsic-impl -I %p/../../include -DOVERLAP %s 2>&1 | FileCheck --check-prefix=OVERLAP %s -DFILE=%s
+
+#define TEST_INTRINSICS_SUPPRESS_DEFS
+include "llvm/IR/Intrinsics.td"
+
+#ifdef EMPTY
+// EMPTY: error: assertion failed: RangeSet requires at least one range
+def int_rangeset_empty : Intrinsic<
+    [llvm_i32_ty], [llvm_i16_ty],
+    [ImmArg<ArgIndex<0>>, RangeSet<ArgIndex<0>, []>]>;
+#endif
+
+#ifdef BAD_BOUNDS
+// BAD-BOUNDS: error: assertion failed: RangeSet requires lower <= upper for each range
+def int_rangeset_bad_bounds : Intrinsic<
+    [llvm_i32_ty], [llvm_i16_ty],
+    [ImmArg<ArgIndex<0>>, RangeSet<ArgIndex<0>, [[4, 3]]>]>;
+#endif
+
+#ifdef OVERLAP
+// OVERLAP: error: assertion failed: RangeSet requires ordered and non-overlapping ranges
+def int_rangeset_overlap : Intrinsic<
+    [llvm_i32_ty], [llvm_i16_ty],
+    [ImmArg<ArgIndex<0>>, RangeSet<ArgIndex<0>, [[0, 2], [2, 4]]>]>;
+#endif
diff --git a/llvm/test/TableGen/intrinsic-rangeset.td b/llvm/test/TableGen/intrinsic-rangeset.td
new file mode 100644
index 0000000000000..a5a3101ce26bb
--- /dev/null
+++ b/llvm/test/TableGen/intrinsic-rangeset.td
@@ -0,0 +1,28 @@
+// RUN: llvm-tblgen -gen-intrinsic-impl -I %p/../../include %s | FileCheck %s
+
+#define TEST_INTRINSICS_SUPPRESS_DEFS
+include "llvm/IR/Intrinsics.td"
+
+def int_range_alias_tst : Intrinsic<
+    [llvm_i32_ty], [llvm_i16_ty],
+    [ImmArg<ArgIndex<0>>, Range<ArgIndex<0>, 0, 9>]>;
+
+def int_rangeset_tst : Intrinsic<
+    [llvm_i32_ty], [llvm_i16_ty],
+    [ImmArg<ArgIndex<0>>, RangeSet<ArgIndex<0>, [[0, 2], [5, 8]]>]>;
+
+def int_rangeset_coalesce_tst : Intrinsic<
+    [llvm_i32_ty], [llvm_i16_ty],
+    [ImmArg<ArgIndex<0>>, RangeSet<ArgIndex<0>, [[0, 2], [3, 4], [6, 8]]>]>;
+
+def int_rangeset_terminal_tst : Intrinsic<
+    [llvm_i32_ty], [],
+    [RangeSet<RetIndex, [[1, 2147483647]]>]>;
+
+// CHECK-DAG: Attribute::get(C, Attribute::Range, ConstantRange(APInt(BitWidth, 0, {{.*}} APInt(BitWidth, 9,
+
+// CHECK-DAG: Attribute::get(C, Attribute::RangeSet, ArrayRef<ConstantRange>{ConstantRange(APInt(BitWidth, 0, {{.*}} APInt(BitWidth, 3, {{.*}} ConstantRange(APInt(BitWidth, 5, {{.*}} APInt(BitWidth, 9,
+
+// CHECK-DAG: Attribute::get(C, Attribute::RangeSet, ArrayRef<ConstantRange>{ConstantRange(APInt(BitWidth, 0, {{.*}} APInt(BitWidth, 5, {{.*}} ConstantRange(APInt(BitWidth, 6, {{.*}} APInt(BitWidth, 9,
+
+// CHECK-DAG: Attribute::get(C, Attribute::RangeSet, ArrayRef<ConstantRange>{ConstantRange(APInt(BitWidth, 1, {{.*}} APInt(BitWidth, 2147483648,
diff --git a/llvm/unittests/IR/AttributesTest.cpp b/llvm/unittests/IR/AttributesTest.cpp
index 5e795ef771713..6ecea71f7d310 100644
--- a/llvm/unittests/IR/AttributesTest.cpp
+++ b/llvm/unittests/IR/AttributesTest.cpp
@@ -466,6 +466,7 @@ TEST(Attributes, SetIntersect) {
         V1 = FPClassTest::fcNan;
         break;
       case Attribute::Range:
+      case Attribute::RangeSet:
         break;
       case Attribute::Captures:
         V0 = CaptureInfo(CaptureComponents::AddressIsNull,
@@ -554,6 +555,14 @@ TEST(Attributes, SetIntersect) {
         ASSERT_EQ(Res->getAttribute(Kind).getRange(),
                   ConstantRange(APInt(32, 0), APInt(32, 20)));
         break;
+      case Attribute::RangeSet: {
+        ArrayRef<ConstantRange> RangeSet =
+            Res->getAttribute(Kind).getRangeSet();
+        ASSERT_EQ(RangeSet.size(), 2u);
+        ASSERT_EQ(RangeSet[0], CR0);
+        ASSERT_EQ(RangeSet[1], CR1);
+        break;
+      }
       case Attribute::Captures:
         ASSERT_EQ(Res->getCaptureInfo(),
                   CaptureInfo(CaptureComponents::AddressIsNull,
diff --git a/llvm/utils/TableGen/Basic/CodeGenIntrinsics.cpp b/llvm/utils/TableGen/Basic/CodeGenIntrinsics.cpp
index 36898ae014e3a..881ee1a709ed0 100644
--- a/llvm/utils/TableGen/Basic/CodeGenIntrinsics.cpp
+++ b/llvm/utils/TableGen/Basic/CodeGenIntrinsics.cpp
@@ -21,6 +21,7 @@
 #include "llvm/TableGen/Record.h"
 #include <algorithm>
 #include <cassert>
+#include <limits>
 using namespace llvm;
 
 // As the type of more than one return values is represented as an anonymous
@@ -30,6 +31,44 @@ using namespace llvm;
 // return is 257.
 static constexpr unsigned MaxNumReturn = 257;
 
+static std::pair<int64_t, int64_t>
+getHalfOpenRange(const Record *R, const ListInit *Range, StringRef AttrName) {
+  if (!Range || Range->size() != 2)
+    PrintFatalError(R->getLoc(),
+                    Twine(AttrName) + " range must have exactly two bounds");
+
+  auto GetBound = [&](const Init *Bound) -> int64_t {
+    if (const auto *II = dyn_cast<IntInit>(Bound))
+      return II->getValue();
+    PrintFatalError(R->getLoc(),
+                    Twine(AttrName) +
+                        " bound is not an integer: " + Bound->getAsString());
+  };
+
+  int64_t Lower = GetBound(Range->getElement(0));
+  int64_t Upper = GetBound(Range->getElement(1));
+  if (Upper == std::numeric_limits<int64_t>::max())
+    PrintFatalError(R->getLoc(),
+                    Twine(AttrName) + " closed upper bound is too large");
+  return std::pair<int64_t, int64_t>(Lower, Upper + 1);
+}
+
+static void
+appendHalfOpenRange(const Record *R,
+                    SmallVectorImpl<std::pair<int64_t, int64_t>> &Ranges,
+                    std::pair<int64_t, int64_t> Range) {
+  if (!Ranges.empty()) {
+    if (Range.first < Ranges.back().second)
+      PrintFatalError(R->getLoc(),
+                      "RangeSet ranges must be ordered and non-overlapping");
+    if (Range.first == Ranges.back().second) {
+      Ranges.back().second = Range.second;
+      return;
+    }
+  }
+  Ranges.push_back(Range);
+}
+
 //===----------------------------------------------------------------------===//
 // CodeGenIntrinsic Implementation
 //===----------------------------------------------------------------------===//
@@ -502,6 +541,25 @@ void CodeGenIntrinsic::setProperty(const Record *R) {
     int64_t Lower = R->getValueAsInt("Lower");
     int64_t Upper = R->getValueAsInt("Upper");
     addArgAttribute(ArgNo, Range, Lower, Upper);
+  } else if (R->isSubClassOf("RangeSet")) {
+    unsigned ArgNo = R->getValueAsInt("ArgNo");
+    const ListInit *RangeList = R->getValueAsListInit("Ranges");
+    SmallVector<std::pair<int64_t, int64_t>, 4> Ranges;
+
+    if (ArgNo < ArgumentAttributes.size()) {
+      for (const auto &Entry : ArgumentAttributes[ArgNo]) {
+        if (Entry.Kind != RangeSet)
+          continue;
+        PrintFatalError(R->getLoc(),
+                        "duplicate RangeSet for the same argument index");
+      }
+    }
+
+    for (const Init *Range : RangeList->getElements())
+      appendHalfOpenRange(
+          R, Ranges,
+          getHalfOpenRange(R, dyn_cast<ListInit>(Range), "RangeSet"));
+    addRangeSetAttribute(ArgNo, std::move(Ranges));
   } else if (R->isSubClassOf("ArgInfo")) {
     unsigned ArgNo = R->getValueAsInt("ArgNo");
     if (ArgNo < 1)
@@ -570,6 +628,15 @@ void CodeGenIntrinsic::addArgAttribute(unsigned Idx, ArgAttrKind AK, uint64_t V,
   ArgumentAttributes[Idx].emplace_back(AK, V, V2);
 }
 
+void CodeGenIntrinsic::addRangeSetAttribute(
+    unsigned Idx, SmallVector<std::pair<int64_t, int64_t>, 4> Ranges) {
+  if (Idx >= ArgumentAttributes.size())
+    ArgumentAttributes.resize(Idx + 1);
+  ArgAttribute Attr(RangeSet, 0, 0);
+  Attr.Ranges = std::move(Ranges);
+  ArgumentAttributes[Idx].push_back(std::move(Attr));
+}
+
 void CodeGenIntrinsic::addPrettyPrintFunction(unsigned ArgIdx,
                                               StringRef ArgName,
                                               StringRef FuncName) {
diff --git a/llvm/utils/TableGen/Basic/CodeGenIntrinsics.h b/llvm/utils/TableGen/Basic/CodeGenIntrinsics.h
index 7d2d21382b319..78d15083cef55 100644
--- a/llvm/utils/TableGen/Basic/CodeGenIntrinsics.h
+++ b/llvm/utils/TableGen/Basic/CodeGenIntrinsics.h
@@ -133,19 +133,26 @@ struct CodeGenIntrinsic {
     Alignment,
     Dereferenceable,
     Range,
+    RangeSet,
   };
 
   struct ArgAttribute {
     ArgAttrKind Kind;
     uint64_t Value;
     uint64_t Value2;
+    SmallVector<std::pair<int64_t, int64_t>, 4> Ranges;
 
     ArgAttribute(ArgAttrKind K, uint64_t V, uint64_t V2)
         : Kind(K), Value(V), Value2(V2) {}
 
     bool operator<(const ArgAttribute &Other) const {
-      return std::tie(Kind, Value, Value2) <
-             std::tie(Other.Kind, Other.Value, Other.Value2);
+      if (std::tie(Kind, Value, Value2) !=
+          std::tie(Other.Kind, Other.Value, Other.Value2))
+        return std::tie(Kind, Value, Value2) <
+               std::tie(Other.Kind, Other.Value, Other.Value2);
+      return std::lexicographical_compare(Ranges.begin(), Ranges.end(),
+                                          Other.Ranges.begin(),
+                                          Other.Ranges.end());
     }
   };
 
@@ -154,6 +161,8 @@ struct CodeGenIntrinsic {
 
   void addArgAttribute(unsigned Idx, ArgAttrKind AK, uint64_t V = 0,
                        uint64_t V2 = 0);
+  void addRangeSetAttribute(unsigned Idx,
+                            SmallVector<std::pair<int64_t, int64_t>, 4> Ranges);
 
   /// Structure to store pretty print and argument information.
   struct PrettyPrintArgInfo {
diff --git a/llvm/utils/TableGen/Basic/IntrinsicEmitter.cpp b/llvm/utils/TableGen/Basic/IntrinsicEmitter.cpp
index 02b2cd9850997..54b7d213873e9 100644
--- a/llvm/utils/TableGen/Basic/IntrinsicEmitter.cpp
+++ b/llvm/utils/TableGen/Basic/IntrinsicEmitter.cpp
@@ -524,6 +524,8 @@ static StringRef getArgAttrEnumName(CodeGenIntrinsic::ArgAttrKind Kind) {
     return "Dereferenceable";
   case CodeGenIntrinsic::Range:
     return "Range";
+  case CodeGenIntrinsic::RangeSet:
+    return "RangeSet";
   }
   llvm_unreachable("Unknown CodeGenIntrinsic::ArgAttrKind enum");
 }
@@ -579,7 +581,19 @@ static AttributeSet getIntrinsicArgAttributeSet(LLVMContext &C, unsigned ID,
                         "/*implicitTrunc=*/true), APInt(BitWidth, {}, "
                         "/*isSigned=*/true, /*implicitTrunc=*/true))),\n",
                         AttrName, (int64_t)Attr.Value, (int64_t)Attr.Value2);
-        else
+        else if (Attr.Kind == CodeGenIntrinsic::RangeSet) {
+          OS << formatv("      Attribute::get(C, Attribute::{}, "
+                        "ArrayRef<ConstantRange>{{",
+                        AttrName);
+          interleaveComma(Attr.Ranges, OS, [&](auto Range) {
+            OS << formatv("ConstantRange(APInt(BitWidth, {}, "
+                          "/*isSigned=*/true, /*implicitTrunc=*/true), "
+                          "APInt(BitWidth, {}, /*isSigned=*/true, "
+                          "/*implicitTrunc=*/true))",
+                          Range.first, Range.second);
+          });
+          OS << "}),\n";
+        } else
           OS << formatv("      Attribute::get(C, Attribute::{}),\n", AttrName);
       }
       OS << "    });";



More information about the llvm-commits mailing list