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 @@ -225,9 +225,9 @@ : !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 different syntax compared to other bang operators, so it is defined separately: @@ -1777,6 +1777,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)``. + The result is `[0, 1, 2, 3]`. + If *a* ``>=`` *b*, then the result is `[]>`. + +``!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 @@ -857,6 +857,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 @@ -1200,6 +1200,25 @@ } break; } + case RANGE: { + auto *LHSi = dyn_cast(LHS); + auto *RHSi = dyn_cast(RHS); + if (!LHSi || !RHSi) + break; + + 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()); + } case STRCONCAT: { StringInit *LHSs = dyn_cast(LHS); StringInit *RHSs = dyn_cast(RHS); @@ -1325,6 +1344,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,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' +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 --- /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)); +}