[llvm] ab2187d - TableGen: Introduce `!range` operator for half-opened interval
NAKAMURA Takumi via llvm-commits
llvm-commits at lists.llvm.org
Tue Apr 25 06:38:45 PDT 2023
Author: NAKAMURA Takumi
Date: 2023-04-25T22:38:20+09:00
New Revision: ab2187d786a4cd7655c1ebe7dcb6564e657f7d65
URL: https://github.com/llvm/llvm-project/commit/ab2187d786a4cd7655c1ebe7dcb6564e657f7d65
DIFF: https://github.com/llvm/llvm-project/commit/ab2187d786a4cd7655c1ebe7dcb6564e657f7d65.diff
LOG: TableGen: Introduce `!range` operator for half-opened interval
`!range(a, b)` generates a list `[a,b)`. `a` is optional and `0` by default.
- `!range(-1, 4)` generates `[-1, 0, 1, 2, 3]`
- `!range(4)` generates `[0, 1, 2, 3]`
- `!range(2, 2)` generates `[]<list<int>>`
`!range(list)` is equivalent to `!range(0, !size(list))`.
Differential Revision: https://reviews.llvm.org/D145871
Added:
llvm/test/TableGen/range-op-fail.td
llvm/test/TableGen/range-op.td
Modified:
llvm/docs/TableGen/ProgRef.rst
llvm/include/llvm/TableGen/Record.h
llvm/lib/TableGen/Record.cpp
llvm/lib/TableGen/TGLexer.cpp
llvm/lib/TableGen/TGLexer.h
llvm/lib/TableGen/TGParser.cpp
Removed:
################################################################################
diff --git a/llvm/docs/TableGen/ProgRef.rst b/llvm/docs/TableGen/ProgRef.rst
index 9cada3b7aaf67..a5616a474b326 100644
--- a/llvm/docs/TableGen/ProgRef.rst
+++ b/llvm/docs/TableGen/ProgRef.rst
@@ -225,9 +225,9 @@ TableGen provides "bang operators" that have a wide variety of uses:
: !gt !head !if !interleave !isa
: !le !listconcat !listremove !listsplat !logtwo
: !lt !mul !ne !not !or
- : !setdagop !shl !size !sra !srl
- : !strconcat !sub !subst !substr !tail
- : !tolower !toupper !xor
+ : !range !setdagop !shl !size !sra
+ : !srl !strconcat !sub !subst !substr
+ : !tail !tolower !toupper !xor
The ``!cond`` operator has a slightly
diff erent
syntax compared to other bang operators, so it is defined separately:
@@ -1777,6 +1777,15 @@ and non-0 as true.
result. A logical OR can be performed if all the arguments are either
0 or 1.
+``!range([``\ *a*\ ``,``] *b*\ ``)``
+ This operator produces half-open range sequence ``[a : b)`` as ``list<int>``.
+ *a* is ``0`` by default. ``!range(4)`` is equivalent to ``!range(0, 4)``.
+ The result is `[0, 1, 2, 3]`.
+ If *a* ``>=`` *b*, then the result is `[]<list<int>>`.
+
+``!range([``\ *list*\ ``)``
+ Equivalent to ``!range(0, !size(list))``.
+
``!setdagop(``\ *dag*\ ``,`` *op*\ ``)``
This operator produces a DAG node with the same arguments as *dag*, but with its
operator replaced with *op*.
diff --git a/llvm/include/llvm/TableGen/Record.h b/llvm/include/llvm/TableGen/Record.h
index b4f06a9adcfc5..6ffb3a7f8c090 100644
--- a/llvm/include/llvm/TableGen/Record.h
+++ b/llvm/include/llvm/TableGen/Record.h
@@ -857,6 +857,7 @@ class BinOpInit : public OpInit, public FoldingSetNode {
LISTCONCAT,
LISTSPLAT,
LISTREMOVE,
+ RANGE,
STRCONCAT,
INTERLEAVE,
CONCAT,
diff --git a/llvm/lib/TableGen/Record.cpp b/llvm/lib/TableGen/Record.cpp
index df1a4d47ff96e..52c75261b38bf 100644
--- a/llvm/lib/TableGen/Record.cpp
+++ b/llvm/lib/TableGen/Record.cpp
@@ -1200,6 +1200,25 @@ std::optional<bool> BinOpInit::CompareInit(unsigned Opc, Init *LHS, Init *RHS) c
}
break;
}
+ case RANGE: {
+ auto *LHSi = dyn_cast<IntInit>(LHS);
+ auto *RHSi = dyn_cast<IntInit>(RHS);
+ if (!LHSi || !RHSi)
+ break;
+
+ auto Start = LHSi->getValue();
+ auto End = RHSi->getValue();
+ SmallVector<Init *, 8> Args;
+ if (Start < End) {
+ // Half-open interval (excludes `End`)
+ Args.reserve(End - Start);
+ for (auto i = Start; i < End; ++i)
+ Args.push_back(IntInit::get(getRecordKeeper(), i));
+ } else {
+ // Empty set
+ }
+ return ListInit::get(Args, LHSi->getType());
+ }
case STRCONCAT: {
StringInit *LHSs = dyn_cast<StringInit>(LHS);
StringInit *RHSs = dyn_cast<StringInit>(RHS);
@@ -1325,6 +1344,7 @@ std::string BinOpInit::getAsString() const {
case LISTCONCAT: Result = "!listconcat"; break;
case LISTSPLAT: Result = "!listsplat"; break;
case LISTREMOVE: Result = "!listremove"; break;
+ case RANGE: Result = "!range"; break;
case STRCONCAT: Result = "!strconcat"; break;
case INTERLEAVE: Result = "!interleave"; break;
case SETDAGOP: Result = "!setdagop"; break;
diff --git a/llvm/lib/TableGen/TGLexer.cpp b/llvm/lib/TableGen/TGLexer.cpp
index ba63d0b671ce6..24ec4031a7eda 100644
--- a/llvm/lib/TableGen/TGLexer.cpp
+++ b/llvm/lib/TableGen/TGLexer.cpp
@@ -585,6 +585,7 @@ tgtok::TokKind TGLexer::LexExclaim() {
.Case("listconcat", tgtok::XListConcat)
.Case("listsplat", tgtok::XListSplat)
.Case("listremove", tgtok::XListRemove)
+ .Case("range", tgtok::XRange)
.Case("strconcat", tgtok::XStrConcat)
.Case("interleave", tgtok::XInterleave)
.Case("substr", tgtok::XSubstr)
diff --git a/llvm/lib/TableGen/TGLexer.h b/llvm/lib/TableGen/TGLexer.h
index 54283c103b4be..35976c666d122 100644
--- a/llvm/lib/TableGen/TGLexer.h
+++ b/llvm/lib/TableGen/TGLexer.h
@@ -56,7 +56,7 @@ enum TokKind {
XSHL, XListConcat, XListSplat, XStrConcat, XInterleave, XSubstr, XFind,
XCast, XSubst, XForEach, XFilter, XFoldl, XHead, XTail, XSize, XEmpty, XIf,
XCond, XEq, XIsA, XDag, XNe, XLe, XLt, XGe, XGt, XSetDagOp, XGetDagOp,
- XExists, XListRemove, XToLower, XToUpper,
+ XExists, XListRemove, XToLower, XToUpper, XRange,
// Boolean literals.
TrueVal, FalseVal,
diff --git a/llvm/lib/TableGen/TGParser.cpp b/llvm/lib/TableGen/TGParser.cpp
index 14c529caa5947..629a72dc458de 100644
--- a/llvm/lib/TableGen/TGParser.cpp
+++ b/llvm/lib/TableGen/TGParser.cpp
@@ -1217,6 +1217,7 @@ Init *TGParser::ParseOperation(Record *CurRec, RecTy *ItemType) {
case tgtok::XListConcat:
case tgtok::XListSplat:
case tgtok::XListRemove:
+ case tgtok::XRange:
case tgtok::XStrConcat:
case tgtok::XInterleave:
case tgtok::XSetDagOp: { // Value ::= !binop '(' Value ',' Value ')'
@@ -1247,6 +1248,7 @@ Init *TGParser::ParseOperation(Record *CurRec, RecTy *ItemType) {
case tgtok::XListConcat: Code = BinOpInit::LISTCONCAT; break;
case tgtok::XListSplat: Code = BinOpInit::LISTSPLAT; break;
case tgtok::XListRemove: Code = BinOpInit::LISTREMOVE; break;
+ case tgtok::XRange: Code = BinOpInit::RANGE; break;
case tgtok::XStrConcat: Code = BinOpInit::STRCONCAT; break;
case tgtok::XInterleave: Code = BinOpInit::INTERLEAVE; break;
case tgtok::XSetDagOp: Code = BinOpInit::SETDAGOP; break;
@@ -1295,6 +1297,10 @@ Init *TGParser::ParseOperation(Record *CurRec, RecTy *ItemType) {
// We don't know the list type until we parse the first argument.
ArgType = ItemType;
break;
+ case tgtok::XRange:
+ Type = IntRecTy::get(Records)->getListTy();
+ // ArgType may be either Int or List.
+ break;
case tgtok::XStrConcat:
Type = StringRecTy::get(Records);
ArgType = StringRecTy::get(Records);
@@ -1379,6 +1385,27 @@ Init *TGParser::ParseOperation(Record *CurRec, RecTy *ItemType) {
return nullptr;
}
break;
+ case BinOpInit::RANGE:
+ if (InitList.size() == 1) {
+ if (isa<ListRecTy>(ArgType)) {
+ ArgType = nullptr; // Detect error if 2nd arg were present.
+ } else if (isa<IntRecTy>(ArgType)) {
+ // Assume 2nd arg should be IntRecTy
+ } else {
+ Error(InitLoc,
+ Twine("expected list or int, got value of type '") +
+ ArgType->getAsString() + "'");
+ return nullptr;
+ }
+ } else {
+ // Don't come here unless 1st arg is ListRecTy.
+ assert(isa<ListRecTy>(cast<TypedInit>(InitList[0])->getType()));
+ Error(InitLoc,
+ Twine("expected one list, got extra value of type '") +
+ ArgType->getAsString() + "'");
+ return nullptr;
+ }
+ break;
case BinOpInit::EQ:
case BinOpInit::NE:
if (!ArgType->typeIsConvertibleTo(IntRecTy::get(Records)) &&
@@ -1477,6 +1504,37 @@ Init *TGParser::ParseOperation(Record *CurRec, RecTy *ItemType) {
if (Code == BinOpInit::LISTREMOVE)
Type = ArgType;
+ if (Code == BinOpInit::RANGE) {
+ Init *LHS, *RHS;
+ auto ArgCount = InitList.size();
+ assert(ArgCount >= 1);
+ auto *Arg0 = cast<TypedInit>(InitList[0]);
+ auto *Arg0Ty = Arg0->getType();
+ if (ArgCount == 1) {
+ if (isa<ListRecTy>(Arg0Ty)) {
+ // (0, !size(arg))
+ LHS = IntInit::get(Records, 0);
+ RHS = UnOpInit::get(UnOpInit::SIZE, Arg0, IntRecTy::get(Records))
+ ->Fold(CurRec);
+ } else {
+ assert(isa<IntRecTy>(Arg0Ty));
+ // (0, arg)
+ LHS = IntInit::get(Records, 0);
+ RHS = Arg0;
+ }
+ } else if (ArgCount == 2) {
+ assert(isa<IntRecTy>(Arg0Ty));
+ auto *Arg1 = cast<TypedInit>(InitList[1]);
+ assert(isa<IntRecTy>(Arg1->getType()));
+ LHS = Arg0;
+ RHS = Arg1;
+ } else {
+ Error(OpLoc, "expected at most two values of integer");
+ return nullptr;
+ }
+ return BinOpInit::get(Code, LHS, RHS, Type)->Fold(CurRec);
+ }
+
// We allow multiple operands to associative operators like !strconcat as
// shorthand for nesting them.
if (Code == BinOpInit::STRCONCAT || Code == BinOpInit::LISTCONCAT ||
@@ -2208,6 +2266,8 @@ Init *TGParser::ParseOperationCond(Record *CurRec, RecTy *ItemType) {
/// SimpleValue ::= LISTCONCATTOK '(' Value ',' Value ')'
/// SimpleValue ::= LISTSPLATTOK '(' Value ',' Value ')'
/// SimpleValue ::= LISTREMOVETOK '(' Value ',' Value ')'
+/// SimpleValue ::= RANGE '(' Value ')'
+/// SimpleValue ::= RANGE '(' Value ',' Value ')'
/// SimpleValue ::= STRCONCATTOK '(' Value ',' Value ')'
/// SimpleValue ::= COND '(' [Value ':' Value,]+ ')'
///
@@ -2510,6 +2570,7 @@ Init *TGParser::ParseSimpleValue(Record *CurRec, RecTy *ItemType,
case tgtok::XListConcat:
case tgtok::XListSplat:
case tgtok::XListRemove:
+ case tgtok::XRange:
case tgtok::XStrConcat:
case tgtok::XInterleave:
case tgtok::XSetDagOp: // Value ::= !binop '(' Value ',' Value ')'
diff --git a/llvm/test/TableGen/range-op-fail.td b/llvm/test/TableGen/range-op-fail.td
new file mode 100644
index 0000000000000..3238d60abe7f8
--- /dev/null
+++ b/llvm/test/TableGen/range-op-fail.td
@@ -0,0 +1,45 @@
+// Each RUN line is scattered.
+
+defvar list_int = !range(4);
+
+#ifdef ERR_LIST_INT
+// RUN: not llvm-tblgen %s -DERR_LIST_INT 2>&1 | FileCheck -DFILE=%s %s --check-prefix=ERR_LIST_INT
+// ERR_LIST_INT: [[FILE]]:[[@LINE+1]]:32: error: expected one list, got extra value of type 'int'
+defvar errs = !range(list_int, 42);
+#endif
+
+#ifdef ERR_INT_LIST
+// RUN: not llvm-tblgen %s -DERR_INT_LIST 2>&1 | FileCheck -DFILE=%s %s --check-prefix=ERR_INT_LIST
+// ERR_INT_LIST: [[FILE]]:[[@LINE+1]]:25: error: expected value of type 'int', got 'list<int>'
+defvar errs = !range(0, list_int);
+#endif
+
+#ifdef ERR_TOO_MANY_ARGS
+// RUN: not llvm-tblgen %s -DERR_TOO_MANY_ARGS 2>&1 | FileCheck -DFILE=%s %s --check-prefix=ERR_TOO_MANY_ARGS
+// ERR_TOO_MANY_ARGS: [[FILE]]:[[@LINE+1]]:15: error: expected at most two values of integer
+defvar errs = !range(0, 42, 255);
+#endif
+
+#ifdef ERR_UNEXPECTED_TYPE_0
+// RUN: not llvm-tblgen %s -DERR_UNEXPECTED_TYPE_0 2>&1 | FileCheck -DFILE=%s %s --check-prefix=ERR_UNEXPECTED_TYPE_0
+// ERR_UNEXPECTED_TYPE_0: [[FILE]]:[[@LINE+1]]:22: error: expected list or int, got value of type 'string'
+defvar errs = !range("hoge");
+#endif
+
+#ifdef ERR_UNEXPECTED_TYPE_1
+// RUN: not llvm-tblgen %s -DERR_UNEXPECTED_TYPE_1 2>&1 | FileCheck -DFILE=%s %s --check-prefix=ERR_UNEXPECTED_TYPE_1
+// ERR_UNEXPECTED_TYPE_1: [[FILE]]:[[@LINE+1]]:22: error: expected list or int, got value of type 'string'
+defvar errs = !range("hoge", 42);
+#endif
+
+#ifdef ERR_UNEXPECTED_TYPE_2
+// RUN: not llvm-tblgen %s -DERR_UNEXPECTED_TYPE_2 2>&1 | FileCheck -DFILE=%s %s --check-prefix=ERR_UNEXPECTED_TYPE_2
+// ERR_UNEXPECTED_TYPE_2: [[FILE]]:[[@LINE+1]]:25: error: expected value of type 'int', got 'string'
+defvar errs = !range(6, "fuga");
+#endif
+
+#ifdef ERR_EMPTY_ARG
+// RUN: not llvm-tblgen %s -DERR_EMPTY_ARG 2>&1 | FileCheck -DFILE=%s %s --check-prefix=ERR_EMPTY_ARG
+// ERR_EMPTY_ARG: [[FILE]]:[[@LINE+1]]:22: error: Unknown or reserved token when parsing a value
+defvar errs = !range();
+#endif
diff --git a/llvm/test/TableGen/range-op.td b/llvm/test/TableGen/range-op.td
new file mode 100644
index 0000000000000..b7e587d47b8cb
--- /dev/null
+++ b/llvm/test/TableGen/range-op.td
@@ -0,0 +1,37 @@
+// RUN: llvm-tblgen %s | FileCheck %s
+// XFAIL: vg_leak
+
+// CHECK: def range_op_ranges {
+def range_op_ranges {
+ // !range(n) generates half-open interval "[0,n)"
+ // CHECK: list<int> idxs8 = [0, 1, 2, 3, 4, 5, 6, 7];
+ list<int> idxs8 = !range(8);
+
+ // !range(m, n) generates half-open interval "[m,n)"
+ // CHECK: list<int> idxs4 = [4, 5, 6, 7];
+ list<int> idxs4 = !range(4, 8);
+
+ // !range(m, n) generates empty set if m >= n
+ // CHECK: list<int> idxs84 = [];
+ list<int> idxs84 = !range(8, 4);
+
+ // !range(list) generates index values for the list. (Not a copy of the list)
+ // CHECK: list<int> idxsl = [0, 1, 2, 3];
+ list<int> idxsl = !range(idxs4);
+
+ // !range(emptylist) generates empty set
+ // CHECK: list<int> idxs0 = [];
+ list<int> idxs0 = !range(idxs84);
+
+ // Example: Dynamic !range(n)
+ // CHECK: list<list<int>> rn = {{\[}}[0, 1, 2, 3], [0, 1, 2, 3, 4], [0, 1, 2, 3, 4, 5], [0, 1, 2, 3, 4, 5, 6]];
+ list<list<int>> rn = !foreach(i, idxs4, !range(i));
+
+ // Example: Dynamic !range(m, n)
+ // CHECK: list<list<int>> rm = {{\[}}[0, 1, 2, 3, 4, 5, 6], [1, 2, 3, 4, 5], [2, 3, 4], [3], [], [], [], []];
+ list<list<int>> rm = !foreach(i, idxs8, !range(i, !sub(7, i)));
+
+ // Example: Dynamic !range(list)
+ // CHECK: list<list<int>> rl = {{\[}}[0, 1, 2, 3, 4, 5, 6], [0, 1, 2, 3, 4], [0, 1, 2], [0], [], [], [], []];
+ list<list<int>> rl = !foreach(r, rm, !range(r));
+}
More information about the llvm-commits
mailing list