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,22 @@ } 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) { + Args.reserve(End - Start); + for (auto i = Start; i < End; ++i) + Args.push_back(IntInit::get(getRecordKeeper(), i)); + } + return ListInit::get(Args, LHSi->getType()); + } + break; + } case STRCONCAT: { StringInit *LHSs = dyn_cast(LHS); StringInit *RHSs = dyn_cast(RHS); @@ -1331,6 +1347,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,26 @@ return nullptr; } break; + case BinOpInit::RANGE: + if (InitList.size() == 1) { + if (isa(ArgType)) { + ArgType = nullptr; + break; + } else if (!isa(ArgType)) { + Error(InitLoc, + Twine("expected list or int, got value of type '") + + ArgType->getAsString() + "'"); + return nullptr; + } + } else { + if (isa(dyn_cast(InitList[1])->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 +1503,36 @@ if (Code == BinOpInit::LISTREMOVE) Type = ArgType; + if (Code == BinOpInit::RANGE) { + Init *LHS, *RHS; + auto *Arg0 = dyn_cast(InitList[0]); + switch (InitList.size()) { + case 1: + if (isa(Arg0->getType())) { + // (0, !size(arg)) + LHS = IntInit::get(Records, 0); + RHS = UnOpInit::get(UnOpInit::SIZE, Arg0, IntRecTy::get(Records)) + ->Fold(CurRec); + } else { + assert(isa(Arg0->getType())); + // (0, arg) + LHS = IntInit::get(Records, 0); + RHS = Arg0; + } + break; + case 2: + assert(isa(Arg0->getType())); + assert(isa(dyn_cast(InitList[1])->getType())); + LHS = Arg0; + RHS = InitList[1]; + break; + default: + Error(OpLoc, "expected at most two integer values"); + 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 +2264,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 +2568,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,21 @@ +// 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 err0 = !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 err1 = !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 integer +defvar err3 = !range(0, 42, 255); +#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,16 @@ +// RUN: llvm-tblgen %s | FileCheck %s +// XFAIL: vg_leak + +defvar idxs = !range(8); + +// CHECK: def range_op_ranges { +def range_op_ranges { + // CHECK: list> r0 = {{\[}}[], [0], [0, 1], [0, 1, 2], [0, 1, 2, 3], [0, 1, 2, 3, 4], [0, 1, 2, 3, 4, 5], [0, 1, 2, 3, 4, 5, 6]]; + list> r0 = !foreach(i, idxs, !range(i)); + + // CHECK: list> rr = {{\[}}[0, 1, 2, 3, 4, 5, 6], [1, 2, 3, 4, 5], [2, 3, 4], [3], [], [], [], []]; + list> rr = !foreach(i, idxs, !range(i, !sub(7, i))); + + // CHECK: list i100 = [100, 101, 102, 103, 104, 105, 106, 107]; + list i100 = !foreach(i, !range(rr), !add(100, i)); +}