diff --git a/llvm/docs/TableGen/ProgRef.rst b/llvm/docs/TableGen/ProgRef.rst --- a/llvm/docs/TableGen/ProgRef.rst +++ b/llvm/docs/TableGen/ProgRef.rst @@ -1782,6 +1782,15 @@ 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``. + *a* is ``0`` by default. ``!range(4)`` is equivalent to ``!range(0, 4)``. + Its result is `[0, 1, 2, 3]`. + The result is `[]>` if *a* ``>=`` *b*. + +``!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 --- a/llvm/include/llvm/TableGen/Record.h +++ b/llvm/include/llvm/TableGen/Record.h @@ -859,6 +859,7 @@ LISTCONCAT, LISTSPLAT, LISTREMOVE, + RANGE, STRCONCAT, INTERLEAVE, CONCAT, diff --git a/llvm/lib/TableGen/Record.cpp b/llvm/lib/TableGen/Record.cpp --- a/llvm/lib/TableGen/Record.cpp +++ b/llvm/lib/TableGen/Record.cpp @@ -1206,6 +1206,25 @@ } break; } + case RANGE: { + auto *LHSi = dyn_cast(LHS); + auto *RHSi = dyn_cast(RHS); + if (LHSi && RHSi) { + auto Start = LHSi->getValue(); + auto End = RHSi->getValue(); + SmallVector 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()); + } + break; + } case STRCONCAT: { StringInit *LHSs = dyn_cast(LHS); StringInit *RHSs = dyn_cast(RHS); @@ -1331,6 +1350,7 @@ 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.h b/llvm/lib/TableGen/TGLexer.h --- a/llvm/lib/TableGen/TGLexer.h +++ b/llvm/lib/TableGen/TGLexer.h @@ -56,7 +56,7 @@ 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/TGLexer.cpp b/llvm/lib/TableGen/TGLexer.cpp --- a/llvm/lib/TableGen/TGLexer.cpp +++ b/llvm/lib/TableGen/TGLexer.cpp @@ -585,6 +585,7 @@ .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/TGParser.cpp b/llvm/lib/TableGen/TGParser.cpp --- a/llvm/lib/TableGen/TGParser.cpp +++ b/llvm/lib/TableGen/TGParser.cpp @@ -1217,6 +1217,7 @@ 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 @@ 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 @@ // 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 @@ return nullptr; } break; + case BinOpInit::RANGE: + if (InitList.size() == 1) { + if (isa(ArgType)) { + ArgType = nullptr; // Detect error if 2nd arg were present. + } else if (isa(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(cast(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 @@ if (Code == BinOpInit::LISTREMOVE) Type = ArgType; + if (Code == BinOpInit::RANGE) { + Init *LHS, *RHS; + auto ArgCount = InitList.size(); + assert(ArgCount >= 1); + auto *Arg0 = cast(InitList[0]); + auto *Arg0Ty = Arg0->getType(); + if (ArgCount == 1) { + if (isa(Arg0Ty)) { + // (0, !size(arg)) + LHS = IntInit::get(Records, 0); + RHS = UnOpInit::get(UnOpInit::SIZE, Arg0, IntRecTy::get(Records)) + ->Fold(CurRec); + } else { + assert(isa(Arg0Ty)); + // (0, arg) + LHS = IntInit::get(Records, 0); + RHS = Arg0; + } + } else if (ArgCount == 2) { + assert(isa(Arg0Ty)); + auto *Arg1 = cast(InitList[1]); + assert(isa(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 @@ /// 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 @@ 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 --- /dev/null +++ b/llvm/test/TableGen/range-op-fail.td @@ -0,0 +1,33 @@ +// Each RUN line is scattered. + +defvar list_int = !range(4); + +#ifdef ERR0 +// RUN: not llvm-tblgen %s -DERR0 2>&1 | FileCheck -DFILE=%s %s --check-prefix=ERR0 +// ERR0: [[FILE]]:[[@LINE+1]]:32: error: expected one list, got extra value of type 'int' +defvar errs = !range(list_int, 42); +#endif + +#ifdef ERR1 +// RUN: not llvm-tblgen %s -DERR1 2>&1 | FileCheck -DFILE=%s %s --check-prefix=ERR1 +// ERR1: [[FILE]]:[[@LINE+1]]:25: error: expected value of type 'int', got 'list' +defvar errs = !range(0, list_int); +#endif + +#ifdef ERR2 +// RUN: not llvm-tblgen %s -DERR2 2>&1 | FileCheck -DFILE=%s %s --check-prefix=ERR2 +// ERR2: [[FILE]]:[[@LINE+1]]:15: error: expected at most two values of integer +defvar errs = !range(0, 42, 255); +#endif + +#ifdef ERR3 +// RUN: not llvm-tblgen %s -DERR3 2>&1 | FileCheck -DFILE=%s %s --check-prefix=ERR3 +// ERR3: [[FILE]]:[[@LINE+1]]:22: error: expected list or int, got value of type 'string' +defvar errs = !range("hoge", "fuga"); +#endif + +#ifdef ERR4 +// RUN: not llvm-tblgen %s -DERR4 2>&1 | FileCheck -DFILE=%s %s --check-prefix=ERR4 +// ERR4: [[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 --- /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 idxs8 = [0, 1, 2, 3, 4, 5, 6, 7]; + list idxs8 = !range(8); + + // !range(m, n) generates half-open interval "[m,n)" + // CHECK: list idxs4 = [4, 5, 6, 7]; + list idxs4 = !range(4, 8); + + // !range(m, n) generates empty set if m >= n + // CHECK: list idxs84 = []; + list idxs84 = !range(8, 4); + + // !range(list) generates index values for the list. (Not a copy of the list) + // CHECK: list idxsl = [0, 1, 2, 3]; + list idxsl = !range(idxs4); + + // !range(emptylist) generates empty set + // CHECK: list idxs0 = []; + list idxs0 = !range(idxs84); + + // Example: Dynamic !range(n) + // CHECK: list> rn = {{\[}}[0, 1, 2, 3], [0, 1, 2, 3, 4], [0, 1, 2, 3, 4, 5], [0, 1, 2, 3, 4, 5, 6]]; + list> rn = !foreach(i, idxs4, !range(i)); + + // Example: Dynamic !range(m, n) + // CHECK: list> rm = {{\[}}[0, 1, 2, 3, 4, 5, 6], [1, 2, 3, 4, 5], [2, 3, 4], [3], [], [], [], []]; + list> rm = !foreach(i, idxs8, !range(i, !sub(7, i))); + + // Example: Dynamic !range(list) + // CHECK: list> rl = {{\[}}[0, 1, 2, 3, 4, 5, 6], [0, 1, 2, 3, 4], [0, 1, 2], [0], [], [], [], []]; + list> rl = !foreach(r, rm, !range(r)); +}