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 @@ -227,7 +227,7 @@ : !lt !mul !ne !not !or : !range !setdagop !shl !size !sra : !srl !strconcat !sub !subst !substr - : !tail !tolower !toupper !xor + : !tail !tolower !toupper !xor !getdagargs !getdagargnames The ``!cond`` operator has a slightly different syntax compared to other bang operators, so it is defined separately: @@ -1711,6 +1711,17 @@ This operator produces 1 if *a* is greater than or equal to *b*; 0 otherwise. The arguments must be ``bit``, ``bits``, ``int``, or ``string`` values. +``!getdagargs<``\ *type*\ ``>(``\ *dag*\ ``)`` + This operator extracts the list of arguments of the given *dag*. + + Due to limitations of the type system, it is requires that the + arguments of the given *dag* can be converted to *type*. In + practice, this means that for each *arg* in the *dag* the + predicate ``!isa(arg)`` is true. + +``!getdagargnames(``\ *dag*\ ``)`` + This operator extracts the list of argument names of the given *dag*. + ``!getdagop(``\ *dag*\ ``)`` --or-- ``!getdagop<``\ *type*\ ``>(``\ *dag*\ ``)`` This operator produces the operator of the given *dag* node. Example: ``!getdagop((foo 1, 2))`` results in ``foo``. Recall that 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 @@ -781,7 +781,9 @@ SIZE, EMPTY, GETDAGOP, - LOG2 + LOG2, + GETDAGARGS, + GETDAGARGNAMES }; private: 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 @@ -23,6 +23,7 @@ #include "llvm/Support/Allocator.h" #include "llvm/Support/Casting.h" #include "llvm/Support/Compiler.h" +#include "llvm/Support/Debug.h" #include "llvm/Support/ErrorHandling.h" #include "llvm/Support/MathExtras.h" #include "llvm/Support/SMLoc.h" @@ -756,6 +757,42 @@ Init *UnOpInit::Fold(Record *CurRec, bool IsFinal) const { RecordKeeper &RK = getRecordKeeper(); switch (getOpcode()) { + case GETDAGARGS: { + if (DagInit *DAG = dyn_cast(LHS)) { + + auto ListTy = dyn_cast(getType()); + RecTy *ElementTy = ListTy->getElementType(); + assert(ListTy && "Type is expected to be ListRecTy"); + + // Check that the type of each of the argument can be converted + // to the type specified by the user in !getdagargs(dag). + ArrayRef Args = DAG->getArgs(); + for (Init *ArgInit : Args) { + Init *NewInit = ArgInit->getCastTo(ElementTy); + if (!NewInit) { + PrintFatalError(CurRec->getLoc(), + Twine("Operand `") + ArgInit->getAsString() + "`" + + " of dag `" + DAG->getAsString() + + +"` cannot be cast to type `" + + ElementTy->getAsString() + "`"); + } + } + return ListInit::get(Args, ElementTy); + } + break; + } + case GETDAGARGNAMES: + if (DagInit *DAG = dyn_cast(LHS)) { + auto ListTy = dyn_cast(getType()); + assert(ListTy && "Type is expected to be ListRecTy"); + SmallVector Inits; + for (StringInit *Name : DAG->getArgNames()) { + assert(Name && "Missing name"); + Inits.push_back(Name); + } + return ListInit::get(Inits, StringRecTy::get(RK)); + } + break; case TOLOWER: if (StringInit *LHSs = dyn_cast(LHS)) return StringInit::get(RK, LHSs->getValue().lower()); @@ -922,6 +959,12 @@ case TOUPPER: Result = "!toupper"; break; + case GETDAGARGS: + Result = "!getdagargs"; + break; + case GETDAGARGNAMES: + Result = "!getdagargnames"; + break; } return Result + "(" + LHS->getAsString() + ")"; } 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,8 @@ 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, XRange, + XExists, XListRemove, XToLower, XToUpper, XRange, XGetDagArgs, XGetDagArgNames, + // 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 @@ -595,6 +595,8 @@ .Case("exists", tgtok::XExists) .Case("tolower", tgtok::XToLower) .Case("toupper", tgtok::XToUpper) + .Case("getdagargs", tgtok::XGetDagArgs) + .Case("getdagargnames", tgtok::XGetDagArgNames) .Default(tgtok::Error); return Kind != tgtok::Error ? Kind : ReturnError(Start-1, "Unknown operator"); 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 @@ -1327,7 +1327,50 @@ return (IsAOpInit::get(Type, LHS))->Fold(); } + case tgtok::XGetDagArgs: { + // Value ::= ! getdagargs '<' Type '>' '(' Value ')' + Lex.Lex(); // eat the operation + + // Check the Type argument. + RecTy *CastToRecTy = ParseOperatorType(); + + if (!consume(tgtok::l_paren)) { + TokError("expected '('"); + return nullptr; + } + Init *DAG = ParseValue(CurRec); + if (!DAG) + return nullptr; + + if (!consume(tgtok::r_paren)) { + TokError("expected ')'"); + return nullptr; + } + + return UnOpInit::get(UnOpInit::GETDAGARGS, DAG, ListRecTy::get(CastToRecTy)) + ->Fold(CurRec); + } + case tgtok::XGetDagArgNames: { + // Value ::= ! getdagargnames '(' Value ')' + Lex.Lex(); // eat the operation + + if (!consume(tgtok::l_paren)) { + TokError("expected '('"); + return nullptr; + } + Init *DAG = ParseValue(CurRec); + if (!DAG) + return nullptr; + if (!consume(tgtok::r_paren)) { + TokError("expected ')'"); + return nullptr; + } + + return UnOpInit::get(UnOpInit::GETDAGARGNAMES, DAG, + ListRecTy::get(StringRecTy::get(Records))) + ->Fold(CurRec); + } case tgtok::XExists: { // Value ::= !exists '<' Type '>' '(' Value ')' Lex.Lex(); // eat the operation @@ -2753,6 +2796,8 @@ case tgtok::XRange: case tgtok::XStrConcat: case tgtok::XInterleave: + case tgtok::XGetDagArgs: + case tgtok::XGetDagArgNames: case tgtok::XSetDagOp: // Value ::= !binop '(' Value ',' Value ')' case tgtok::XIf: case tgtok::XCond: diff --git a/llvm/test/TableGen/getdagargnames.td b/llvm/test/TableGen/getdagargnames.td new file mode 100644 --- /dev/null +++ b/llvm/test/TableGen/getdagargnames.td @@ -0,0 +1,81 @@ +// RUN: llvm-tblgen %s | FileCheck %s + +class Base; +def operator : Base; +def A : Base; +def B : Base; +defvar DAG = (operator A:$a, B:$b); +class PrintMe t> { + list x = t; +} + +//////////////////////////////////////////////////////////////////////////////// +// Basic tests + +// CHECK-LABEL: def test_00 { +// CHECK-NEXT: list x = ["a", "b"]; +// CHECK-NEXT: } +def test_00: PrintMe; + +//////////////////////////////////////////////////////////////////////////////// +// Extract from a dag created with !dag. + +// CHECK-LABEL: def test_05 { +// CHECK-NEXT: list x = ["var1", "var2"]; +// CHECK-NEXT: } +def test_05: PrintMe; + +//////////////////////////////////////////////////////////////////////////////// +// Operate on the template parameter of a class. + +class Extract { + list ArgNames =!getdagargnames(D); +} + +// CHECK-LABEL: def test_06 { +// CHECK-NEXT: list ArgNames = ["A", "B"]; +// CHECK-NEXT: } +def test_06 : Extract<(operator A:$A, B:$B)>; + +//////////////////////////////////////////////////////////////////////////////// +// Make sure we can pass template parameters transformed by getdagarg +// via class inheritance. + +class Parent input> { + list Input = input; +} + +class Child : Parent; + +// CHECK-LABEL: def test_07 { +// CHECK-NEXT: list Input = ["X"]; +// CHECK-NEXT: } +def test_07 : Child<(operator (operator A:$A, B:$B):$X)>; + +//////////////////////////////////////////////////////////////////////////////// +// Make sure we can use defvar without knowing the underlying type of +// the argument of the dag in a multiclass; + +class UseBase x> { + list X = x; +} +multiclass UseDefVar { + defvar Input = !getdagargnames(input); + def _def: UseBase; +} + +// CHECK-LABEL: def test_08_def { +// CHECK-NEXT: list X = ["x", "y"]; +// CHECK-NEXT: } +defm test_08 : UseDefVar<(operator A:$x, B:$y)>; + +//////////////////////////////////////////////////////////////////////////////// +// Make sure the type of the result of !getdagargnames can be enquired by +// other operators + +class ChildWithSubListUse : Parent<[!interleave(!getdagargnames(input),"@")]>; + +// CHECK-LABEL: def test_09 { +// CHECK-NEXT: list Input = ["s@t"]; +// CHECK-NEXT: } +def test_09 : ChildWithSubListUse<(operator (operator A, B):$s, (A operator, B):$t)>; diff --git a/llvm/test/TableGen/getdagargs-error.td b/llvm/test/TableGen/getdagargs-error.td new file mode 100644 --- /dev/null +++ b/llvm/test/TableGen/getdagargs-error.td @@ -0,0 +1,19 @@ +// RUN: not llvm-tblgen 2>&1 %s | FileCheck %s + +//////////////////////////////////////////////////////////////////////////////// +// A dag that does not have uniform operands. + +class BaseA; +class BaseB; +class Base; + +def InstanceOfBaseA : BaseA; +def InstanceOfBaseB : BaseB; +def operator : Base; +class Extractor { + list ExtractedList = !getdagargs(input); +} + +// CHECK:[[@LINE+1]]:5: error: Operand `InstanceOfBaseB` of dag `(operator InstanceOfBaseA, InstanceOfBaseB)` cannot be cast to type `BaseA` +def test : Extractor<(operator InstanceOfBaseA, InstanceOfBaseB)>; + diff --git a/llvm/test/TableGen/getdagargs.td b/llvm/test/TableGen/getdagargs.td new file mode 100644 --- /dev/null +++ b/llvm/test/TableGen/getdagargs.td @@ -0,0 +1,106 @@ +// RUN: llvm-tblgen %s | FileCheck %s + +class Base; +def operator : Base; +def A : Base; +def B : Base; +defvar DAG = (operator A, B); +class PrintMe t> { + list x = t; +} + +//////////////////////////////////////////////////////////////////////////////// +// Basic tests + +// CHECK-LABEL: def test_00 { +// CHECK-NEXT: list x = [A, B]; +// CHECK-NEXT: } +def test_00: PrintMe(DAG)>; + +//////////////////////////////////////////////////////////////////////////////// +// Extract from a dag with names. + +defvar DagWithNames = (operator A:$A, B:$B); +// CHECK-LABEL: def test_04 { +// CHECK-NEXT: list x = [A, B]; +// CHECK-NEXT: } +def test_04: PrintMe(DagWithNames)>; + +//////////////////////////////////////////////////////////////////////////////// +// Extract from a dag created with !dag. + +// CHECK-LABEL: def test_05 { +// CHECK-NEXT: list x = [operator]; +// CHECK-NEXT: } +def test_05: PrintMe(!dag(A, [operator], ?))>; + +//////////////////////////////////////////////////////////////////////////////// +// Operate on the template parameter of a class. + +class Extract { + list Args =!getdagargs(D); +} + +// CHECK-LABEL: def test_06 { +// CHECK-NEXT: list Args = [A, B]; +// CHECK-NEXT: } +def test_06 : Extract<(operator A, B)>; + +//////////////////////////////////////////////////////////////////////////////// +// Make sure we can pass template parameters transformed by getdagarg +// via class inheritance. + +class Parent input> { + list Input = input; +} + +class Child : Parent(input)>; + +// CHECK-LABEL: def test_07 { +// CHECK-NEXT: list Input = [(operator A, B)]; +// CHECK-NEXT: } +def test_07 : Child<(operator (operator A, B))>; + +//////////////////////////////////////////////////////////////////////////////// +// Make sure we can use defvar without knowing the underlying type of +// the argument of the dag in a multiclass; + +class UseBase x> { + list X = x; +} +multiclass UseDefVar { + defvar Input = !getdagargs(input); + def _def: UseBase; +} + +// CHECK-LABEL: def test_08_def { +// CHECK-NEXT: list X = [A, B]; +// CHECK-NEXT: } +defm test_08 : UseDefVar<(operator A, B)>; + +//////////////////////////////////////////////////////////////////////////////// +// Make sure the type of the result of !getdagargs can be enquired by +// other operators + +class ChildWithSubListUse : Parent<[!head(!getdagargs(input))]>; + +// CHECK-LABEL: def test_09 { +// CHECK-NEXT: list Input = [(operator A, B)]; +// CHECK-NEXT: } +def test_09 : ChildWithSubListUse<(operator (operator A, B), (A operator, B))>; + +//////////////////////////////////////////////////////////////////////////////// +// A dag that does not have uniform operands. + +class BaseA; +class BaseB; +class CommonParent; + +def InstanceOfBaseA : BaseA, CommonParent; +def InstanceOfBaseB : BaseB, CommonParent; +class Extractor { + list ExtractedList = !getdagargs(input); +} + +def test_10 : Extractor<(operator InstanceOfBaseA, InstanceOfBaseB)>; +