Skip to content

Commit df330d7

Browse files
authored
[TableGen] Enhance !range bang operator (llvm#66489)
We add a third argument `step` to `!range` bang operator to make it with the same semantics as `range` in Python. `step` can be negative. `step` is 1 by default and `step` can't be 0. If `start` < `end` and `step` is negative, or `start` > `end` and `step` is positive, the result is an empty list.
1 parent 134ae13 commit df330d7

File tree

6 files changed

+168
-70
lines changed

6 files changed

+168
-70
lines changed

llvm/docs/TableGen/ProgRef.rst

Lines changed: 12 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1831,11 +1831,18 @@ and non-0 as true.
18311831
result. A logical OR can be performed if all the arguments are either
18321832
0 or 1.
18331833

1834-
``!range([``\ *a*\ ``,``] *b*\ ``)``
1835-
This operator produces half-open range sequence ``[a : b)`` as ``list<int>``.
1836-
*a* is ``0`` by default. ``!range(4)`` is equivalent to ``!range(0, 4)``.
1837-
The result is `[0, 1, 2, 3]`.
1838-
If *a* ``>=`` *b*, then the result is `[]<list<int>>`.
1834+
``!range([``\ *start*\ ``,]`` *end*\ ``[, ``\ *step*\ ``])``
1835+
This operator produces half-open range sequence ``[start : end : step)`` as
1836+
``list<int>``. *start* is ``0`` and *step* is ``1`` by default. *step* can
1837+
be negative and cannot be 0. If *start* ``<`` *end* and *step* is negative,
1838+
or *start* ``>`` *end* and *step* is positive, the result is an empty list
1839+
``[]<list<int>>``. For example:
1840+
* ``!range(4)`` is equivalent to ``!range(0, 4, 1)`` and the result is
1841+
`[0, 1, 2, 3]`.
1842+
* ``!range(1, 4)`` is equivalent to ``!range(1, 4, 1)`` and the result is
1843+
`[1, 2, 3]`.
1844+
* The result of ``!range(0, 4, 2)`` is `[0, 2]`.
1845+
* The results of ``!range(0, 4, -1)`` and ``!range(4, 0, 1)`` are empty.
18391846

18401847
``!range(``\ *list*\ ``)``
18411848
Equivalent to ``!range(0, !size(list))``.

llvm/include/llvm/TableGen/Record.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -911,7 +911,6 @@ class BinOpInit : public OpInit, public FoldingSetNode {
911911
LISTREMOVE,
912912
LISTELEM,
913913
LISTSLICE,
914-
RANGE,
915914
RANGEC,
916915
STRCONCAT,
917916
INTERLEAVE,
@@ -988,6 +987,7 @@ class TernOpInit : public OpInit, public FoldingSetNode {
988987
FILTER,
989988
IF,
990989
DAG,
990+
RANGE,
991991
SUBSTR,
992992
FIND,
993993
SETDAGARG,

llvm/lib/TableGen/Record.cpp

Lines changed: 34 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1287,7 +1287,6 @@ Init *BinOpInit::Fold(Record *CurRec) const {
12871287
}
12881288
return ListInit::get(Args, TheList->getElementType());
12891289
}
1290-
case RANGE:
12911290
case RANGEC: {
12921291
auto *LHSi = dyn_cast<IntInit>(LHS);
12931292
auto *RHSi = dyn_cast<IntInit>(RHS);
@@ -1487,8 +1486,9 @@ std::string BinOpInit::getAsString() const {
14871486
case GT: Result = "!gt"; break;
14881487
case LISTCONCAT: Result = "!listconcat"; break;
14891488
case LISTSPLAT: Result = "!listsplat"; break;
1490-
case LISTREMOVE: Result = "!listremove"; break;
1491-
case RANGE: Result = "!range"; break;
1489+
case LISTREMOVE:
1490+
Result = "!listremove";
1491+
break;
14921492
case STRCONCAT: Result = "!strconcat"; break;
14931493
case INTERLEAVE: Result = "!interleave"; break;
14941494
case SETDAGOP: Result = "!setdagop"; break;
@@ -1704,6 +1704,34 @@ Init *TernOpInit::Fold(Record *CurRec) const {
17041704
break;
17051705
}
17061706

1707+
case RANGE: {
1708+
auto *LHSi = dyn_cast<IntInit>(LHS);
1709+
auto *MHSi = dyn_cast<IntInit>(MHS);
1710+
auto *RHSi = dyn_cast<IntInit>(RHS);
1711+
if (!LHSi || !MHSi || !RHSi)
1712+
break;
1713+
1714+
auto Start = LHSi->getValue();
1715+
auto End = MHSi->getValue();
1716+
auto Step = RHSi->getValue();
1717+
if (Step == 0)
1718+
PrintError(CurRec->getLoc(), "Step of !range can't be 0");
1719+
1720+
SmallVector<Init *, 8> Args;
1721+
if (Start < End && Step > 0) {
1722+
Args.reserve((End - Start) / Step);
1723+
for (auto I = Start; I < End; I += Step)
1724+
Args.push_back(IntInit::get(getRecordKeeper(), I));
1725+
} else if (Start > End && Step < 0) {
1726+
Args.reserve((Start - End) / -Step);
1727+
for (auto I = Start; I > End; I += Step)
1728+
Args.push_back(IntInit::get(getRecordKeeper(), I));
1729+
} else {
1730+
// Empty set
1731+
}
1732+
return ListInit::get(Args, LHSi->getType());
1733+
}
1734+
17071735
case SUBSTR: {
17081736
StringInit *LHSs = dyn_cast<StringInit>(LHS);
17091737
IntInit *MHSi = dyn_cast<IntInit>(MHS);
@@ -1823,6 +1851,9 @@ std::string TernOpInit::getAsString() const {
18231851
case FILTER: Result = "!filter"; UnquotedLHS = true; break;
18241852
case FOREACH: Result = "!foreach"; UnquotedLHS = true; break;
18251853
case IF: Result = "!if"; break;
1854+
case RANGE:
1855+
Result = "!range";
1856+
break;
18261857
case SUBST: Result = "!subst"; break;
18271858
case SUBSTR: Result = "!substr"; break;
18281859
case FIND: Result = "!find"; break;

llvm/lib/TableGen/TGParser.cpp

Lines changed: 103 additions & 59 deletions
Original file line numberDiff line numberDiff line change
@@ -1408,7 +1408,6 @@ Init *TGParser::ParseOperation(Record *CurRec, RecTy *ItemType) {
14081408
case tgtok::XListConcat:
14091409
case tgtok::XListSplat:
14101410
case tgtok::XListRemove:
1411-
case tgtok::XRange:
14121411
case tgtok::XStrConcat:
14131412
case tgtok::XInterleave:
14141413
case tgtok::XGetDagArg:
@@ -1440,8 +1439,9 @@ Init *TGParser::ParseOperation(Record *CurRec, RecTy *ItemType) {
14401439
case tgtok::XGt: Code = BinOpInit::GT; break;
14411440
case tgtok::XListConcat: Code = BinOpInit::LISTCONCAT; break;
14421441
case tgtok::XListSplat: Code = BinOpInit::LISTSPLAT; break;
1443-
case tgtok::XListRemove: Code = BinOpInit::LISTREMOVE; break;
1444-
case tgtok::XRange: Code = BinOpInit::RANGE; break;
1442+
case tgtok::XListRemove:
1443+
Code = BinOpInit::LISTREMOVE;
1444+
break;
14451445
case tgtok::XStrConcat: Code = BinOpInit::STRCONCAT; break;
14461446
case tgtok::XInterleave: Code = BinOpInit::INTERLEAVE; break;
14471447
case tgtok::XSetDagOp: Code = BinOpInit::SETDAGOP; break;
@@ -1508,10 +1508,6 @@ Init *TGParser::ParseOperation(Record *CurRec, RecTy *ItemType) {
15081508
// We don't know the list type until we parse the first argument.
15091509
ArgType = ItemType;
15101510
break;
1511-
case tgtok::XRange:
1512-
Type = IntRecTy::get(Records)->getListTy();
1513-
// ArgType may be either Int or List.
1514-
break;
15151511
case tgtok::XStrConcat:
15161512
Type = StringRecTy::get(Records);
15171513
ArgType = StringRecTy::get(Records);
@@ -1596,27 +1592,6 @@ Init *TGParser::ParseOperation(Record *CurRec, RecTy *ItemType) {
15961592
return nullptr;
15971593
}
15981594
break;
1599-
case BinOpInit::RANGE:
1600-
if (InitList.size() == 1) {
1601-
if (isa<ListRecTy>(ArgType)) {
1602-
ArgType = nullptr; // Detect error if 2nd arg were present.
1603-
} else if (isa<IntRecTy>(ArgType)) {
1604-
// Assume 2nd arg should be IntRecTy
1605-
} else {
1606-
Error(InitLoc,
1607-
Twine("expected list or int, got value of type '") +
1608-
ArgType->getAsString() + "'");
1609-
return nullptr;
1610-
}
1611-
} else {
1612-
// Don't come here unless 1st arg is ListRecTy.
1613-
assert(isa<ListRecTy>(cast<TypedInit>(InitList[0])->getType()));
1614-
Error(InitLoc,
1615-
Twine("expected one list, got extra value of type '") +
1616-
ArgType->getAsString() + "'");
1617-
return nullptr;
1618-
}
1619-
break;
16201595
case BinOpInit::EQ:
16211596
case BinOpInit::NE:
16221597
if (!ArgType->typeIsConvertibleTo(IntRecTy::get(Records)) &&
@@ -1726,37 +1701,6 @@ Init *TGParser::ParseOperation(Record *CurRec, RecTy *ItemType) {
17261701
if (Code == BinOpInit::LISTREMOVE)
17271702
Type = ArgType;
17281703

1729-
if (Code == BinOpInit::RANGE) {
1730-
Init *LHS, *RHS;
1731-
auto ArgCount = InitList.size();
1732-
assert(ArgCount >= 1);
1733-
auto *Arg0 = cast<TypedInit>(InitList[0]);
1734-
auto *Arg0Ty = Arg0->getType();
1735-
if (ArgCount == 1) {
1736-
if (isa<ListRecTy>(Arg0Ty)) {
1737-
// (0, !size(arg))
1738-
LHS = IntInit::get(Records, 0);
1739-
RHS = UnOpInit::get(UnOpInit::SIZE, Arg0, IntRecTy::get(Records))
1740-
->Fold(CurRec);
1741-
} else {
1742-
assert(isa<IntRecTy>(Arg0Ty));
1743-
// (0, arg)
1744-
LHS = IntInit::get(Records, 0);
1745-
RHS = Arg0;
1746-
}
1747-
} else if (ArgCount == 2) {
1748-
assert(isa<IntRecTy>(Arg0Ty));
1749-
auto *Arg1 = cast<TypedInit>(InitList[1]);
1750-
assert(isa<IntRecTy>(Arg1->getType()));
1751-
LHS = Arg0;
1752-
RHS = Arg1;
1753-
} else {
1754-
Error(OpLoc, "expected at most two values of integer");
1755-
return nullptr;
1756-
}
1757-
return BinOpInit::get(Code, LHS, RHS, Type)->Fold(CurRec);
1758-
}
1759-
17601704
// We allow multiple operands to associative operators like !strconcat as
17611705
// shorthand for nesting them.
17621706
if (Code == BinOpInit::STRCONCAT || Code == BinOpInit::LISTCONCAT ||
@@ -1783,6 +1727,105 @@ Init *TGParser::ParseOperation(Record *CurRec, RecTy *ItemType) {
17831727
return ParseOperationForEachFilter(CurRec, ItemType);
17841728
}
17851729

1730+
case tgtok::XRange: {
1731+
SMLoc OpLoc = Lex.getLoc();
1732+
Lex.Lex(); // eat the operation
1733+
1734+
if (!consume(tgtok::l_paren)) {
1735+
TokError("expected '(' after !range operator");
1736+
return nullptr;
1737+
}
1738+
1739+
SmallVector<Init *, 2> Args;
1740+
bool FirstArgIsList = false;
1741+
for (;;) {
1742+
if (Args.size() >= 3) {
1743+
TokError("expected at most three values of integer");
1744+
return nullptr;
1745+
}
1746+
1747+
SMLoc InitLoc = Lex.getLoc();
1748+
Args.push_back(ParseValue(CurRec));
1749+
if (!Args.back())
1750+
return nullptr;
1751+
1752+
TypedInit *ArgBack = dyn_cast<TypedInit>(Args.back());
1753+
if (!ArgBack) {
1754+
Error(OpLoc, Twine("expected value to be a typed value, got '" +
1755+
Args.back()->getAsString() + "'"));
1756+
return nullptr;
1757+
}
1758+
1759+
RecTy *ArgBackType = ArgBack->getType();
1760+
if (!FirstArgIsList || Args.size() == 1) {
1761+
if (Args.size() == 1 && isa<ListRecTy>(ArgBackType)) {
1762+
FirstArgIsList = true; // Detect error if 2nd arg were present.
1763+
} else if (isa<IntRecTy>(ArgBackType)) {
1764+
// Assume 2nd arg should be IntRecTy
1765+
} else {
1766+
if (Args.size() != 1)
1767+
Error(InitLoc, Twine("expected value of type 'int', got '" +
1768+
ArgBackType->getAsString() + "'"));
1769+
else
1770+
Error(InitLoc, Twine("expected list or int, got value of type '") +
1771+
ArgBackType->getAsString() + "'");
1772+
return nullptr;
1773+
}
1774+
} else {
1775+
// Don't come here unless 1st arg is ListRecTy.
1776+
assert(isa<ListRecTy>(cast<TypedInit>(Args[0])->getType()));
1777+
Error(InitLoc, Twine("expected one list, got extra value of type '") +
1778+
ArgBackType->getAsString() + "'");
1779+
return nullptr;
1780+
}
1781+
if (!consume(tgtok::comma))
1782+
break;
1783+
}
1784+
1785+
if (!consume(tgtok::r_paren)) {
1786+
TokError("expected ')' in operator");
1787+
return nullptr;
1788+
}
1789+
1790+
Init *LHS, *MHS, *RHS;
1791+
auto ArgCount = Args.size();
1792+
assert(ArgCount >= 1);
1793+
auto *Arg0 = cast<TypedInit>(Args[0]);
1794+
auto *Arg0Ty = Arg0->getType();
1795+
if (ArgCount == 1) {
1796+
if (isa<ListRecTy>(Arg0Ty)) {
1797+
// (0, !size(arg), 1)
1798+
LHS = IntInit::get(Records, 0);
1799+
MHS = UnOpInit::get(UnOpInit::SIZE, Arg0, IntRecTy::get(Records))
1800+
->Fold(CurRec);
1801+
RHS = IntInit::get(Records, 1);
1802+
} else {
1803+
assert(isa<IntRecTy>(Arg0Ty));
1804+
// (0, arg, 1)
1805+
LHS = IntInit::get(Records, 0);
1806+
MHS = Arg0;
1807+
RHS = IntInit::get(Records, 1);
1808+
}
1809+
} else {
1810+
assert(isa<IntRecTy>(Arg0Ty));
1811+
auto *Arg1 = cast<TypedInit>(Args[1]);
1812+
assert(isa<IntRecTy>(Arg1->getType()));
1813+
LHS = Arg0;
1814+
MHS = Arg1;
1815+
if (ArgCount == 3) {
1816+
// (start, end, step)
1817+
auto *Arg2 = cast<TypedInit>(Args[2]);
1818+
assert(isa<IntRecTy>(Arg2->getType()));
1819+
RHS = Arg2;
1820+
} else
1821+
// (start, end, 1)
1822+
RHS = IntInit::get(Records, 1);
1823+
}
1824+
return TernOpInit::get(TernOpInit::RANGE, LHS, MHS, RHS,
1825+
IntRecTy::get(Records)->getListTy())
1826+
->Fold(CurRec);
1827+
}
1828+
17861829
case tgtok::XSetDagArg:
17871830
case tgtok::XSetDagName:
17881831
case tgtok::XDag:
@@ -2534,6 +2577,7 @@ Init *TGParser::ParseOperationCond(Record *CurRec, RecTy *ItemType) {
25342577
/// SimpleValue ::= LISTREMOVETOK '(' Value ',' Value ')'
25352578
/// SimpleValue ::= RANGE '(' Value ')'
25362579
/// SimpleValue ::= RANGE '(' Value ',' Value ')'
2580+
/// SimpleValue ::= RANGE '(' Value ',' Value ',' Value ')'
25372581
/// SimpleValue ::= STRCONCATTOK '(' Value ',' Value ')'
25382582
/// SimpleValue ::= COND '(' [Value ':' Value,]+ ')'
25392583
///

llvm/test/TableGen/range-op-fail.td

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -16,8 +16,8 @@ defvar errs = !range(0, list_int);
1616

1717
#ifdef ERR_TOO_MANY_ARGS
1818
// RUN: not llvm-tblgen %s -DERR_TOO_MANY_ARGS 2>&1 | FileCheck -DFILE=%s %s --check-prefix=ERR_TOO_MANY_ARGS
19-
// ERR_TOO_MANY_ARGS: [[FILE]]:[[@LINE+1]]:15: error: expected at most two values of integer
20-
defvar errs = !range(0, 42, 255);
19+
// ERR_TOO_MANY_ARGS: [[FILE]]:[[@LINE+1]]:34: error: expected at most three values of integer
20+
defvar errs = !range(0, 42, 255, 233);
2121
#endif
2222

2323
#ifdef ERR_UNEXPECTED_TYPE_0

llvm/test/TableGen/range-op.td

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,10 +11,26 @@ def range_op_ranges {
1111
// CHECK: list<int> idxs4 = [4, 5, 6, 7];
1212
list<int> idxs4 = !range(4, 8);
1313

14+
// !range(m, n, s) generates half-open interval "[m,n)" with step s
15+
// CHECK: list<int> step = [0, 2, 4, 6];
16+
list<int> step = !range(0, 8, 2);
17+
18+
// Step can be negative.
19+
// CHECK: list<int> negative_step = [8, 6, 4, 2];
20+
list<int> negative_step = !range(8, 0, -2);
21+
1422
// !range(m, n) generates empty set if m >= n
1523
// CHECK: list<int> idxs84 = [];
1624
list<int> idxs84 = !range(8, 4);
1725

26+
// !range(m, n, s) generates empty set if m < n and s < 0
27+
// CHECK: list<int> emptycase0 = [];
28+
list<int> emptycase0 = !range(4, 8, -1);
29+
30+
// !range(m, n, s) generates empty set if m > n and s > 0
31+
// CHECK: list<int> emptycase1 = [];
32+
list<int> emptycase1 = !range(8, 4, 1);
33+
1834
// !range(list) generates index values for the list. (Not a copy of the list)
1935
// CHECK: list<int> idxsl = [0, 1, 2, 3];
2036
list<int> idxsl = !range(idxs4);

0 commit comments

Comments
 (0)