[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
Wed Jul 8 08:47:51 PDT 2026
https://github.com/kvederni updated https://github.com/llvm/llvm-project/pull/203623
>From 40b9bc2728f32e24cd647ec56316cf3c71da6098 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 1/2] [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 358f9a65a80af..2d3efb653683a 100644
--- a/llvm/include/llvm/Bitcode/LLVMBitCodes.h
+++ b/llvm/include/llvm/Bitcode/LLVMBitCodes.h
@@ -826,6 +826,7 @@ enum AttributeKindCodes {
ATTR_KIND_NOOUTLINE = 107,
ATTR_KIND_FLATTEN = 108,
ATTR_KIND_NOIPA = 109,
+ ATTR_KIND_RANGE_SET = 110,
};
enum ComdatSelectionKindCodes {
diff --git a/llvm/include/llvm/IR/Attributes.h b/llvm/include/llvm/IR/Attributes.h
index 049f699b2980b..2c157aab122ac 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;
@@ -1339,6 +1342,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);
@@ -1369,6 +1375,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 4e45100b54d38..5dbe593a7ba17 100644
--- a/llvm/include/llvm/IR/Attributes.td
+++ b/llvm/include/llvm/IR/Attributes.td
@@ -282,6 +282,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 f75335cd2c60a..aeeadde18ba33 100644
--- a/llvm/include/llvm/IR/Intrinsics.td
+++ b/llvm/include/llvm/IR/Intrinsics.td
@@ -173,10 +173,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 493f46438e68f..20d6b68f002ca 100644
--- a/llvm/lib/AsmParser/LLParser.cpp
+++ b/llvm/lib/AsmParser/LLParser.cpp
@@ -1733,6 +1733,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:
@@ -1989,6 +1991,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'
@@ -3396,17 +3410,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())
@@ -3414,8 +3417,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!");
@@ -3427,6 +3430,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 a5a11ab6221d4..cec11303ca40b 100644
--- a/llvm/lib/Bitcode/Reader/BitcodeReader.cpp
+++ b/llvm/lib/Bitcode/Reader/BitcodeReader.cpp
@@ -2304,6 +2304,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:
@@ -2582,7 +2584,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 a6e7941a06699..210ba1191526a 100644
--- a/llvm/lib/Bitcode/Writer/BitcodeWriter.cpp
+++ b/llvm/lib/Bitcode/Writer/BitcodeWriter.cpp
@@ -998,6 +998,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 8278b08463e83..1604b79c84ef2 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 f605af1d0a31b..c338f3d553270 100644
--- a/llvm/lib/IR/Verifier.cpp
+++ b/llvm/lib/IR/Verifier.cpp
@@ -2032,11 +2032,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);
}
@@ -2181,6 +2182,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,
@@ -3904,6 +3915,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 76fc2a4f38811..8df3d47400736 100644
--- a/llvm/utils/TableGen/Basic/CodeGenIntrinsics.cpp
+++ b/llvm/utils/TableGen/Basic/CodeGenIntrinsics.cpp
@@ -22,6 +22,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
@@ -31,6 +32,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
//===----------------------------------------------------------------------===//
@@ -566,6 +605,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)
@@ -634,6 +692,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 f17149dea6b07..866f32718d9bb 100644
--- a/llvm/utils/TableGen/Basic/CodeGenIntrinsics.h
+++ b/llvm/utils/TableGen/Basic/CodeGenIntrinsics.h
@@ -134,19 +134,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());
}
};
@@ -155,6 +162,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 330de1063d666..72d87c0f9630f 100644
--- a/llvm/utils/TableGen/Basic/IntrinsicEmitter.cpp
+++ b/llvm/utils/TableGen/Basic/IntrinsicEmitter.cpp
@@ -577,6 +577,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");
}
@@ -632,7 +634,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 << " });";
>From e3f5d847e4f4b96c88f84d2afef5e38f4a001657 Mon Sep 17 00:00:00 2001
From: Kirill Vedernikov <kvedernikov at nvidia.com>
Date: Fri, 26 Jun 2026 12:48:03 +0200
Subject: [PATCH 2/2] [IR][TableGen] Restrict RangeSet to immarg validation
Keep RangeSet local to intrinsic TableGen definitions instead of
modelling it as a general IR attribute.
---
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.h | 16 ++-
llvm/include/llvm/IR/Intrinsics.td | 8 +-
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/Intrinsics.cpp | 3 +
llvm/lib/IR/Verifier.cpp | 36 ++---
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 | 8 ++
llvm/test/TableGen/intrinsic-rangeset.td | 22 ++-
llvm/unittests/IR/AttributesTest.cpp | 9 --
.../TableGen/Basic/CodeGenIntrinsics.cpp | 29 ++--
llvm/utils/TableGen/Basic/CodeGenIntrinsics.h | 23 ++--
.../utils/TableGen/Basic/IntrinsicEmitter.cpp | 67 +++++++--
22 files changed, 154 insertions(+), 392 deletions(-)
delete mode 100644 llvm/test/Assembler/rangeset-attribute-invalid.ll
delete mode 100644 llvm/test/Assembler/rangeset-attribute.ll
diff --git a/llvm/include/llvm/AsmParser/LLParser.h b/llvm/include/llvm/AsmParser/LLParser.h
index 22df21290c015..788b56cb78f08 100644
--- a/llvm/include/llvm/AsmParser/LLParser.h
+++ b/llvm/include/llvm/AsmParser/LLParser.h
@@ -309,7 +309,6 @@ namespace llvm {
Loc = Lex.getLoc();
return parseUInt64(Val);
}
- bool parseAPInt(unsigned BitWidth, APInt &Val);
bool parseFlag(unsigned &Val);
bool parseStringAttribute(AttrBuilder &B);
@@ -413,7 +412,6 @@ 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 2d3efb653683a..358f9a65a80af 100644
--- a/llvm/include/llvm/Bitcode/LLVMBitCodes.h
+++ b/llvm/include/llvm/Bitcode/LLVMBitCodes.h
@@ -826,7 +826,6 @@ enum AttributeKindCodes {
ATTR_KIND_NOOUTLINE = 107,
ATTR_KIND_FLATTEN = 108,
ATTR_KIND_NOIPA = 109,
- ATTR_KIND_RANGE_SET = 110,
};
enum ComdatSelectionKindCodes {
diff --git a/llvm/include/llvm/IR/Attributes.h b/llvm/include/llvm/IR/Attributes.h
index 2c157aab122ac..049f699b2980b 100644
--- a/llvm/include/llvm/IR/Attributes.h
+++ b/llvm/include/llvm/IR/Attributes.h
@@ -355,9 +355,6 @@ 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;
@@ -1342,9 +1339,6 @@ 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);
@@ -1375,10 +1369,6 @@ 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 5dbe593a7ba17..4e45100b54d38 100644
--- a/llvm/include/llvm/IR/Attributes.td
+++ b/llvm/include/llvm/IR/Attributes.td
@@ -282,10 +282,6 @@ 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.h b/llvm/include/llvm/IR/Intrinsics.h
index 2f9239aa7b77a..c04560eb13b3d 100644
--- a/llvm/include/llvm/IR/Intrinsics.h
+++ b/llvm/include/llvm/IR/Intrinsics.h
@@ -25,15 +25,16 @@
namespace llvm {
-class Type;
-class FunctionType;
+class APInt;
+class AttributeList;
+class AttributeSet;
+class Constant;
class Function;
+class FunctionType;
class LLVMContext;
class Module;
-class AttributeList;
-class AttributeSet;
class raw_ostream;
-class Constant;
+class Type;
/// This namespace contains an enum with a value for every intrinsic/builtin
/// function known by LLVM. The enum values are returned by
@@ -325,6 +326,11 @@ LLVM_ABI Intrinsic::ID getDeinterleaveIntrinsicID(unsigned Factor);
LLVM_ABI void printImmArg(ID IID, unsigned ArgIdx, raw_ostream &OS,
const Constant *ImmArgVal);
+/// Return true if the immediate argument value is accepted by any RangeSet
+/// constraint specified for this intrinsic argument.
+LLVM_ABI bool isImmArgValueInRangeSet(ID IID, unsigned ArgIdx,
+ const APInt &Value);
+
} // namespace Intrinsic
} // namespace llvm
diff --git a/llvm/include/llvm/IR/Intrinsics.td b/llvm/include/llvm/IR/Intrinsics.td
index aeeadde18ba33..c69ddbbb52f1f 100644
--- a/llvm/include/llvm/IR/Intrinsics.td
+++ b/llvm/include/llvm/IR/Intrinsics.td
@@ -173,9 +173,9 @@ class ReadNone<ArgIndex idx> : IntrinsicProperty {
int ArgNo = idx.Value;
}
-// The return value or argument is in the union of the specified closed ranges.
+// The immediate 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 {
+class RangeSet<ArgIndex 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";
@@ -194,8 +194,8 @@ class RangeSet<AttrIndex idx, list<list<int>> ranges> : IntrinsicProperty {
// 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>
- : RangeSet<idx, [[lower, !sub(upper, 1)]]> {
+class Range<AttrIndex idx, int lower, int upper> : IntrinsicProperty {
+ int ArgNo = idx.Value;
int Lower = lower;
int Upper = upper;
}
diff --git a/llvm/lib/AsmParser/LLParser.cpp b/llvm/lib/AsmParser/LLParser.cpp
index 20d6b68f002ca..493f46438e68f 100644
--- a/llvm/lib/AsmParser/LLParser.cpp
+++ b/llvm/lib/AsmParser/LLParser.cpp
@@ -1733,8 +1733,6 @@ 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:
@@ -1991,18 +1989,6 @@ 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'
@@ -3410,6 +3396,17 @@ 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())
@@ -3417,8 +3414,8 @@ bool LLParser::parseRangeAttr(AttrBuilder &B) {
unsigned BitWidth = Ty->getPrimitiveSizeInBits();
- if (parseAPInt(BitWidth, Lower) || parseToken(lltok::comma, "expected ','") ||
- parseAPInt(BitWidth, Upper))
+ if (ParseAPSInt(BitWidth, Lower) ||
+ parseToken(lltok::comma, "expected ','") || ParseAPSInt(BitWidth, Upper))
return true;
if (Lower == Upper && !Lower.isZero())
return tokError("the range represent the empty set but limits aren't 0!");
@@ -3430,62 +3427,6 @@ 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 cec11303ca40b..a5a11ab6221d4 100644
--- a/llvm/lib/Bitcode/Reader/BitcodeReader.cpp
+++ b/llvm/lib/Bitcode/Reader/BitcodeReader.cpp
@@ -2304,8 +2304,6 @@ 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:
@@ -2584,9 +2582,7 @@ Error BitcodeReader::parseAttributeGroupBlock() {
}
i--;
- if (Kind == Attribute::RangeSet
- ? !AttributeFuncs::isOrderedRangeSet(Val)
- : !ConstantRangeList::isOrderedRanges(Val))
+ if (!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 210ba1191526a..a6e7941a06699 100644
--- a/llvm/lib/Bitcode/Writer/BitcodeWriter.cpp
+++ b/llvm/lib/Bitcode/Writer/BitcodeWriter.cpp
@@ -998,8 +998,6 @@ 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 1604b79c84ef2..8278b08463e83 100644
--- a/llvm/lib/CodeGen/SelectionDAG/TargetLowering.cpp
+++ b/llvm/lib/CodeGen/SelectionDAG/TargetLowering.cpp
@@ -70,11 +70,10 @@ 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::RangeSet, Attribute::NoFPClass})
+ for (const auto &Attr : {Attribute::Alignment, Attribute::Dereferenceable,
+ Attribute::DereferenceableOrNull, Attribute::NoAlias,
+ Attribute::NonNull, Attribute::NoUndef,
+ Attribute::Range, Attribute::NoFPClass})
CallerAttrs.removeAttribute(Attr);
if (CallerAttrs.hasAttributes())
diff --git a/llvm/lib/IR/Attributes.cpp b/llvm/lib/IR/Attributes.cpp
index dec94c313c884..4087b25951a1c 100644
--- a/llvm/lib/IR/Attributes.cpp
+++ b/llvm/lib/IR/Attributes.cpp
@@ -92,64 +92,6 @@ 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);
@@ -587,12 +529,6 @@ 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");
@@ -803,21 +739,6 @@ 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);
@@ -1229,11 +1150,6 @@ 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");
}
@@ -2455,17 +2371,9 @@ 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));
}
@@ -2571,33 +2479,6 @@ 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) {
@@ -2618,20 +2499,12 @@ 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)
- .addAttribute(Attribute::RangeSet);
+ Incompatible.addAttribute(Attribute::Range);
} 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/Intrinsics.cpp b/llvm/lib/IR/Intrinsics.cpp
index 6ddfc18c56501..70a1dbe55b212 100644
--- a/llvm/lib/IR/Intrinsics.cpp
+++ b/llvm/lib/IR/Intrinsics.cpp
@@ -1472,6 +1472,9 @@ Intrinsic::ID Intrinsic::getDeinterleaveIntrinsicID(unsigned Factor) {
return InterleaveIntrinsics[Factor - 2].Deinterleave;
}
+#define GET_INTRINSIC_IMMARG_RANGE_SET_CHECKS
+#include "llvm/IR/IntrinsicImpl.inc"
+
#define GET_INTRINSIC_PRETTY_PRINT_ARGUMENTS
#include "llvm/IR/IntrinsicImpl.inc"
diff --git a/llvm/lib/IR/Verifier.cpp b/llvm/lib/IR/Verifier.cpp
index c338f3d553270..b9cb6288a8ba9 100644
--- a/llvm/lib/IR/Verifier.cpp
+++ b/llvm/lib/IR/Verifier.cpp
@@ -2032,12 +2032,11 @@ void Verifier::verifyParameterAttrs(AttributeSet Attrs, Type *Ty,
V);
if (Attrs.hasAttribute(Attribute::ImmArg)) {
- unsigned AttrCount = Attrs.getNumAttributes() -
- Attrs.hasAttribute(Attribute::Range) -
- Attrs.hasAttribute(Attribute::RangeSet);
+ unsigned AttrCount =
+ Attrs.getNumAttributes() - Attrs.hasAttribute(Attribute::Range);
Check(AttrCount == 1,
"Attribute 'immarg' is incompatible with other attributes except the "
- "'range' and 'rangeset' attributes",
+ "'range' attribute",
V);
}
@@ -2182,16 +2181,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::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,
@@ -3915,19 +3904,12 @@ 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 (auto *CI = dyn_cast<ConstantInt>(ArgVal))
+ Check(Intrinsic::isImmArgValueInRangeSet(Call.getIntrinsicID(), i,
+ CI->getValue()),
+ "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 c04f1d74b882c..df69a66c67332 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' and 'rangeset' attributes
+; CHECK: Attribute 'immarg' is incompatible with other attributes except the 'range' attribute
declare void @llvm.immarg.byval(ptr byval(i32) immarg)
-; CHECK: Attribute 'immarg' is incompatible with other attributes except the 'range' and 'rangeset' attributes
+; CHECK: Attribute 'immarg' is incompatible with other attributes except the 'range' attribute
declare void @llvm.immarg.inalloca(ptr inalloca(i32) immarg)
-; CHECK: Attribute 'immarg' is incompatible with other attributes except the 'range' and 'rangeset' attributes
+; CHECK: Attribute 'immarg' is incompatible with other attributes except the 'range' attribute
declare void @llvm.immarg.inreg(i32 inreg immarg)
-; CHECK: Attribute 'immarg' is incompatible with other attributes except the 'range' and 'rangeset' attributes
+; CHECK: Attribute 'immarg' is incompatible with other attributes except the 'range' attribute
declare void @llvm.immarg.nest(ptr nest immarg)
-; CHECK: Attribute 'immarg' is incompatible with other attributes except the 'range' and 'rangeset' attributes
+; CHECK: Attribute 'immarg' is incompatible with other attributes except the 'range' attribute
declare void @llvm.immarg.sret(ptr sret(i32) immarg)
-; CHECK: Attribute 'immarg' is incompatible with other attributes except the 'range' and 'rangeset' attributes
+; CHECK: Attribute 'immarg' is incompatible with other attributes except the 'range' attribute
declare void @llvm.immarg.zeroext(i32 zeroext immarg)
-; CHECK: Attribute 'immarg' is incompatible with other attributes except the 'range' and 'rangeset' attributes
+; CHECK: Attribute 'immarg' is incompatible with other attributes except the 'range' attribute
declare void @llvm.immarg.signext(i32 signext immarg)
-; CHECK: Attribute 'immarg' is incompatible with other attributes except the 'range' and 'rangeset' attributes
+; CHECK: Attribute 'immarg' is incompatible with other attributes except the 'range' attribute
declare void @llvm.immarg.returned(i32 returned immarg)
-; CHECK: Attribute 'immarg' is incompatible with other attributes except the 'range' and 'rangeset' attributes
+; CHECK: Attribute 'immarg' is incompatible with other attributes except the 'range' attribute
declare void @llvm.immarg.noalias(ptr noalias immarg)
-; CHECK: Attribute 'immarg' is incompatible with other attributes except the 'range' and 'rangeset' attributes
+; CHECK: Attribute 'immarg' is incompatible with other attributes except the 'range' attribute
declare void @llvm.immarg.readnone(ptr readnone immarg)
-; CHECK: Attribute 'immarg' is incompatible with other attributes except the 'range' and 'rangeset' attributes
+; CHECK: Attribute 'immarg' is incompatible with other attributes except the 'range' attribute
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
deleted file mode 100644
index 98605a1ac4b3a..0000000000000
--- a/llvm/test/Assembler/rangeset-attribute-invalid.ll
+++ /dev/null
@@ -1,36 +0,0 @@
-; 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
deleted file mode 100644
index a47ab51040fb2..0000000000000
--- a/llvm/test/Assembler/rangeset-attribute.ll
+++ /dev/null
@@ -1,19 +0,0 @@
-; 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
index 795220cb3d00b..7a6629bde2485 100644
--- a/llvm/test/TableGen/intrinsic-rangeset-errors.td
+++ b/llvm/test/TableGen/intrinsic-rangeset-errors.td
@@ -1,6 +1,7 @@
// 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
+// RUN: not llvm-tblgen -gen-intrinsic-impl -I %p/../../include -DMISSING_IMMARG %s 2>&1 | FileCheck --check-prefix=MISSING-IMMARG %s -DFILE=%s
#define TEST_INTRINSICS_SUPPRESS_DEFS
include "llvm/IR/Intrinsics.td"
@@ -25,3 +26,10 @@ def int_rangeset_overlap : Intrinsic<
[llvm_i32_ty], [llvm_i16_ty],
[ImmArg<ArgIndex<0>>, RangeSet<ArgIndex<0>, [[0, 2], [2, 4]]>]>;
#endif
+
+#ifdef MISSING_IMMARG
+// MISSING-IMMARG: error: RangeSet requires ImmArg for the same argument index
+def int_rangeset_missing_immarg : Intrinsic<
+ [llvm_i32_ty], [llvm_i16_ty],
+ [RangeSet<ArgIndex<0>, [[0, 2]]>]>;
+#endif
diff --git a/llvm/test/TableGen/intrinsic-rangeset.td b/llvm/test/TableGen/intrinsic-rangeset.td
index a5a3101ce26bb..b14d70e26b062 100644
--- a/llvm/test/TableGen/intrinsic-rangeset.td
+++ b/llvm/test/TableGen/intrinsic-rangeset.td
@@ -3,10 +3,6 @@
#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]]>]>;
@@ -15,14 +11,16 @@ 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-NOT: Attribute::RangeSet
-// CHECK-DAG: Attribute::get(C, Attribute::RangeSet, ArrayRef<ConstantRange>{ConstantRange(APInt(BitWidth, 0, {{.*}} APInt(BitWidth, 3, {{.*}} ConstantRange(APInt(BitWidth, 5, {{.*}} APInt(BitWidth, 9,
+// CHECK: bool Intrinsic::isImmArgValueInRangeSet
-// CHECK-DAG: Attribute::get(C, Attribute::RangeSet, ArrayRef<ConstantRange>{ConstantRange(APInt(BitWidth, 0, {{.*}} APInt(BitWidth, 5, {{.*}} ConstantRange(APInt(BitWidth, 6, {{.*}} APInt(BitWidth, 9,
+// CHECK: case rangeset_coalesce_tst:
+// CHECK: case 0:
+// CHECK: APInt(Value.getBitWidth(), 0, {{.*}}APInt(Value.getBitWidth(), 5,
+// CHECK: APInt(Value.getBitWidth(), 6, {{.*}}APInt(Value.getBitWidth(), 9,
-// CHECK-DAG: Attribute::get(C, Attribute::RangeSet, ArrayRef<ConstantRange>{ConstantRange(APInt(BitWidth, 1, {{.*}} APInt(BitWidth, 2147483648,
+// CHECK: case rangeset_tst:
+// CHECK: case 0:
+// CHECK: APInt(Value.getBitWidth(), 0, {{.*}}APInt(Value.getBitWidth(), 3,
+// CHECK: APInt(Value.getBitWidth(), 5, {{.*}}APInt(Value.getBitWidth(), 9,
diff --git a/llvm/unittests/IR/AttributesTest.cpp b/llvm/unittests/IR/AttributesTest.cpp
index 6ecea71f7d310..5e795ef771713 100644
--- a/llvm/unittests/IR/AttributesTest.cpp
+++ b/llvm/unittests/IR/AttributesTest.cpp
@@ -466,7 +466,6 @@ TEST(Attributes, SetIntersect) {
V1 = FPClassTest::fcNan;
break;
case Attribute::Range:
- case Attribute::RangeSet:
break;
case Attribute::Captures:
V0 = CaptureInfo(CaptureComponents::AddressIsNull,
@@ -555,14 +554,6 @@ 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 8df3d47400736..a09fae9663b86 100644
--- a/llvm/utils/TableGen/Basic/CodeGenIntrinsics.cpp
+++ b/llvm/utils/TableGen/Basic/CodeGenIntrinsics.cpp
@@ -427,6 +427,11 @@ CodeGenIntrinsic::CodeGenIntrinsic(const Record *R,
for (auto &Attrs : ArgumentAttributes)
llvm::sort(Attrs);
+ for (const ImmArgRangeSet &RangeSet : ImmArgRangeSets)
+ if (!isParamImmArg(RangeSet.ArgNo))
+ PrintFatalError(TheDef->getLoc(),
+ "RangeSet requires ImmArg for the same argument index");
+
// Default values are not yet supported for overloaded intrinsics
// (overloaded support will come in a follow-up).
if (isOverloaded &&
@@ -610,20 +615,20 @@ void CodeGenIntrinsic::setProperty(const Record *R) {
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;
+ if (ArgNo < 1)
+ PrintFatalError(R->getLoc(), "RangeSet should only apply to arguments");
+ unsigned ParamNo = ArgNo - 1;
+
+ for (const ImmArgRangeSet &RangeSet : ImmArgRangeSets)
+ if (RangeSet.ArgNo == ParamNo)
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));
+ addImmArgRangeSet(ParamNo, std::move(Ranges));
} else if (R->isSubClassOf("ArgInfo")) {
unsigned ArgNo = R->getValueAsInt("ArgNo");
if (ArgNo < 1)
@@ -692,13 +697,9 @@ 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::addImmArgRangeSet(
+ unsigned ArgNo, SmallVector<std::pair<int64_t, int64_t>, 4> Ranges) {
+ ImmArgRangeSets.push_back({ArgNo, std::move(Ranges)});
}
void CodeGenIntrinsic::addPrettyPrintFunction(unsigned ArgIdx,
diff --git a/llvm/utils/TableGen/Basic/CodeGenIntrinsics.h b/llvm/utils/TableGen/Basic/CodeGenIntrinsics.h
index 866f32718d9bb..ede0811232eb3 100644
--- a/llvm/utils/TableGen/Basic/CodeGenIntrinsics.h
+++ b/llvm/utils/TableGen/Basic/CodeGenIntrinsics.h
@@ -134,36 +134,37 @@ 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 {
- 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());
+ return std::tie(Kind, Value, Value2) <
+ std::tie(Other.Kind, Other.Value, Other.Value2);
}
};
+ struct ImmArgRangeSet {
+ unsigned ArgNo;
+ SmallVector<std::pair<int64_t, int64_t>, 4> Ranges;
+ };
+
/// Vector of attributes for each argument.
SmallVector<SmallVector<ArgAttribute, 0>> ArgumentAttributes;
+ /// Range-set constraints for immediate arguments, indexed by argument number.
+ SmallVector<ImmArgRangeSet> ImmArgRangeSets;
+
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);
+ void addImmArgRangeSet(unsigned ArgNo,
+ 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 72d87c0f9630f..66d67c446a6f5 100644
--- a/llvm/utils/TableGen/Basic/IntrinsicEmitter.cpp
+++ b/llvm/utils/TableGen/Basic/IntrinsicEmitter.cpp
@@ -73,6 +73,8 @@ class IntrinsicEmitter {
function_ref<bool(const CodeGenIntrinsic &Int)> GetProperty);
void EmitGenerator(const CodeGenIntrinsicTable &Ints, raw_ostream &OS);
void EmitAttributes(const CodeGenIntrinsicTable &Ints, raw_ostream &OS);
+ void EmitImmArgRangeSetChecks(const CodeGenIntrinsicTable &Ints,
+ raw_ostream &OS);
void EmitPrettyPrintArguments(const CodeGenIntrinsicTable &Ints,
raw_ostream &OS);
void EmitDefaultArgValuesTable(const CodeGenIntrinsicTable &Ints,
@@ -130,6 +132,9 @@ void IntrinsicEmitter::run(raw_ostream &OS, bool Enums) {
// Emit the intrinsic parameter attributes.
EmitAttributes(Ints, OS);
+ // Emit immediate argument range-set checks.
+ EmitImmArgRangeSetChecks(Ints, OS);
+
// Emit the intrinsic ID -> pretty print table.
EmitIntrinsicToPrettyPrintTable(Ints, OS);
@@ -577,8 +582,6 @@ static StringRef getArgAttrEnumName(CodeGenIntrinsic::ArgAttrKind Kind) {
return "Dereferenceable";
case CodeGenIntrinsic::Range:
return "Range";
- case CodeGenIntrinsic::RangeSet:
- return "RangeSet";
}
llvm_unreachable("Unknown CodeGenIntrinsic::ArgAttrKind enum");
}
@@ -634,19 +637,7 @@ 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 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
+ else
OS << formatv(" Attribute::get(C, Attribute::{}),\n", AttrName);
}
OS << " });";
@@ -900,6 +891,52 @@ AttributeSet Intrinsic::getFnAttributes(LLVMContext &C, ID id) {{
NoFunctionAttrsID);
}
+void IntrinsicEmitter::EmitImmArgRangeSetChecks(
+ const CodeGenIntrinsicTable &Ints, raw_ostream &OS) {
+ IfDefEmitter IfDef(OS, "GET_INTRINSIC_IMMARG_RANGE_SET_CHECKS");
+ OS << R"(
+bool Intrinsic::isImmArgValueInRangeSet(ID IID, unsigned ArgIdx,
+ const APInt &Value) {
+ using namespace Intrinsic;
+ switch (IID) {
+)";
+
+ for (const CodeGenIntrinsic &Int : Ints) {
+ if (Int.ImmArgRangeSets.empty())
+ continue;
+
+ OS << " case " << Int.EnumName << ":\n";
+ OS << " switch (ArgIdx) {\n";
+ for (const CodeGenIntrinsic::ImmArgRangeSet &RangeSet :
+ Int.ImmArgRangeSets) {
+ OS << " case " << RangeSet.ArgNo << ":\n";
+ OS << " return ";
+ interleave(
+ RangeSet.Ranges,
+ [&](const std::pair<int64_t, int64_t> &Range) {
+ OS << formatv("ConstantRange(APInt(Value.getBitWidth(), {}, "
+ "/*isSigned=*/true, /*implicitTrunc=*/true), "
+ "APInt(Value.getBitWidth(), {}, "
+ "/*isSigned=*/true, "
+ "/*implicitTrunc=*/true)).contains(Value)",
+ Range.first, Range.second);
+ },
+ [&] { OS << " ||\n "; });
+ OS << ";\n";
+ }
+ OS << R"( default:
+ return true;
+ }
+)";
+ }
+
+ OS << R"( default:
+ return true;
+ }
+}
+)";
+}
+
void IntrinsicEmitter::EmitIntrinsicToPrettyPrintTable(
const CodeGenIntrinsicTable &Ints, raw_ostream &OS) {
EmitIntrinsicBitTable(Ints, OS, "GET_INTRINSIC_PRETTY_PRINT_TABLE", "PPTable",
More information about the llvm-commits
mailing list