[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