[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