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 @@ -203,9 +203,9 @@ assert bit bits class code dag def else false foreach - defm defset defvar field if - in include int let list - multiclass string then true + defm defset defvar dump field + if in include int let + list multiclass string then true .. warning:: The ``field`` reserved word is deprecated, except when used with the @@ -1240,6 +1240,21 @@ Variables can also be defined with ``defvar`` in a record body. See `Defvar in a Record Body`_ for more details. +``dump`` --- print the content of a variable or record +------------------------------------------------------ + +A ``dump`` statement prints the content of a variable or a record to +standard error output. + +.. productionlist:: + Dump: "dump" `TokIdentifier` ";" + +Example: the following statements will print ``The value of X is 0`` +to standard output. + + defvar X = 0; + dump X; + ``foreach`` --- iterate over a sequence of statements ----------------------------------------------------- @@ -1671,6 +1686,14 @@ This operator performs signed division of *a* by *b*, and produces the quotient. Division by 0 produces an error. Division of INT64_MIN by -1 produces an error. +``!dump(``\ *name*\ ``)`` + This operator will print the value of *name*. + + The following example will instantiate the class ``X`` with a + value of ``n`` equal to ``0``, and print the value of X as the + string ``0`` to standard output: ``class X {...} def : + X;`` + ``!empty(``\ *a*\ ``)`` This operator produces 1 if the string, list, or DAG *a* is empty; 0 otherwise. A dag is empty if it has no arguments; the operator does not count. 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 @@ -845,7 +845,8 @@ SIZE, EMPTY, GETDAGOP, - LOG2 + LOG2, + DUMP }; 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 @@ -797,6 +797,17 @@ Init *UnOpInit::Fold(Record *CurRec, bool IsFinal) const { RecordKeeper &RK = getRecordKeeper(); switch (getOpcode()) { + case DUMP: { + if (LHS->isConcrete()) { + if (const auto *Def = dyn_cast(LHS)) + errs() << *Def->getDef() << "\n"; + else + errs() << LHS->getAsString() << "\n"; + return LHS; + } + break; + } + case TOLOWER: if (StringInit *LHSs = dyn_cast(LHS)) return StringInit::get(RK, LHSs->getValue().lower()); @@ -963,6 +974,9 @@ case TOUPPER: Result = "!toupper"; break; + case DUMP: + Result = "!dump"; + 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 @@ -98,6 +98,7 @@ Defm, Defset, Defvar, + Dump, // Dump is just a syntactic sugar on top of assert and !dump. Foreach, If, Let, @@ -136,6 +137,7 @@ XEmpty, XIf, XCond, + XDump, XEq, XIsA, XDag, 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 @@ -346,31 +346,32 @@ StringRef Str(IdentStart, CurPtr-IdentStart); tgtok::TokKind Kind = StringSwitch(Str) - .Case("int", tgtok::Int) - .Case("bit", tgtok::Bit) - .Case("bits", tgtok::Bits) - .Case("string", tgtok::String) - .Case("list", tgtok::List) - .Case("code", tgtok::Code) - .Case("dag", tgtok::Dag) - .Case("class", tgtok::Class) - .Case("def", tgtok::Def) - .Case("true", tgtok::TrueVal) - .Case("false", tgtok::FalseVal) - .Case("foreach", tgtok::Foreach) - .Case("defm", tgtok::Defm) - .Case("defset", tgtok::Defset) - .Case("multiclass", tgtok::MultiClass) - .Case("field", tgtok::Field) - .Case("let", tgtok::Let) - .Case("in", tgtok::In) - .Case("defvar", tgtok::Defvar) - .Case("include", tgtok::Include) - .Case("if", tgtok::If) - .Case("then", tgtok::Then) - .Case("else", tgtok::ElseKW) - .Case("assert", tgtok::Assert) - .Default(tgtok::Id); + .Case("int", tgtok::Int) + .Case("bit", tgtok::Bit) + .Case("bits", tgtok::Bits) + .Case("string", tgtok::String) + .Case("list", tgtok::List) + .Case("code", tgtok::Code) + .Case("dag", tgtok::Dag) + .Case("class", tgtok::Class) + .Case("def", tgtok::Def) + .Case("true", tgtok::TrueVal) + .Case("false", tgtok::FalseVal) + .Case("foreach", tgtok::Foreach) + .Case("defm", tgtok::Defm) + .Case("defset", tgtok::Defset) + .Case("multiclass", tgtok::MultiClass) + .Case("field", tgtok::Field) + .Case("let", tgtok::Let) + .Case("in", tgtok::In) + .Case("defvar", tgtok::Defvar) + .Case("include", tgtok::Include) + .Case("if", tgtok::If) + .Case("then", tgtok::Then) + .Case("else", tgtok::ElseKW) + .Case("assert", tgtok::Assert) + .Case("dump", tgtok::Dump) + .Default(tgtok::Id); // A couple of tokens require special processing. switch (Kind) { @@ -599,6 +600,7 @@ .Case("exists", tgtok::XExists) .Case("tolower", tgtok::XToLower) .Case("toupper", tgtok::XToUpper) + .Case("dump", tgtok::XDump) .Default(tgtok::Error); return Kind != tgtok::Error ? Kind : ReturnError(Start-1, "Unknown operator"); diff --git a/llvm/lib/TableGen/TGParser.h b/llvm/lib/TableGen/TGParser.h --- a/llvm/lib/TableGen/TGParser.h +++ b/llvm/lib/TableGen/TGParser.h @@ -266,6 +266,7 @@ bool ParseIf(MultiClass *CurMultiClass); bool ParseIfBody(MultiClass *CurMultiClass, StringRef Kind); bool ParseAssert(MultiClass *CurMultiClass, Record *CurRec = nullptr); + bool ParseDump(MultiClass *CurMultiClass, Record *CurRec = nullptr); bool ParseTopLevelLet(MultiClass *CurMultiClass); void ParseLetList(SmallVectorImpl &Result); 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 @@ -1341,6 +1341,26 @@ return (IsAOpInit::get(Type, LHS))->Fold(); } + case tgtok::XDump: { + // Value ::= !dump '(' String ',' Value ')' + Lex.Lex(); // eat the operation + if (!consume(tgtok::l_paren)) { + TokError("expected '('"); + return nullptr; + } + TypedInit *RHS = dyn_cast(ParseSimpleValue(CurRec)); + if (!RHS) { + Error(Lex.getLoc(), "Expected value as second operand"); + return nullptr; + } + + if (!consume(tgtok::r_paren)) { + TokError("expected ')'"); + return nullptr; + } + + return (UnOpInit::get(UnOpInit::DUMP, RHS, RHS->getType()))->Fold(CurRec); + } case tgtok::XExists: { // Value ::= !exists '<' Type '>' '(' Value ')' @@ -3361,6 +3381,9 @@ if (Lex.getCode() == tgtok::Assert) return ParseAssert(nullptr, CurRec); + if (Lex.getCode() == tgtok::Dump) + return ParseDump(nullptr, CurRec); + if (Lex.getCode() == tgtok::Defvar) return ParseDefvar(CurRec); @@ -3826,6 +3849,35 @@ return false; } +bool TGParser::ParseDump(MultiClass *CurMultiClass, Record *CurRec) { + Lex.Lex(); // eat the operation + + TypedInit *RHS = dyn_cast(ParseSimpleValue(CurRec)); + if (!RHS) { + Error(Lex.getLoc(), "Exepcted value as second operand"); + return true; + } + if (!consume(tgtok::semi)) { + TokError("expected ';'"); + return true; + } + + // Turn this print str, expr into an assert: + // assert 1, !cast(!dump(expr)) + SMLoc ConditionLoc = Lex.getLoc(); + Init *Condition = BitInit::get(Records, true); + Init *XDump = + UnOpInit::get(UnOpInit::DUMP, RHS, RHS->getType())->Fold(CurRec); + Init *Message = + UnOpInit::get(UnOpInit::CAST, XDump, StringRecTy::get(Records)); + if (CurRec) + CurRec->addAssertion(ConditionLoc, Condition, Message); + else + addEntry(std::make_unique(ConditionLoc, Condition, + Message)); + return false; +} + /// ParseClass - Parse a tblgen class definition. /// /// ClassInst ::= CLASS ID TemplateArgList? ObjectBody @@ -3974,6 +4026,7 @@ /// MultiClassObject ::= DefInst /// MultiClassObject ::= DefMInst /// MultiClassObject ::= Defvar +/// MultiClassObject ::= Dump /// MultiClassObject ::= Foreach /// MultiClassObject ::= If /// MultiClassObject ::= LETCommand '{' ObjectList '}' @@ -4041,7 +4094,7 @@ switch (Lex.getCode()) { default: return TokError("expected 'assert', 'def', 'defm', 'defvar', " - "'foreach', 'if', or 'let' in multiclass body"); + "'foreach', 'if', 'dump', or 'let' in multiclass body"); case tgtok::Assert: case tgtok::Def: @@ -4050,6 +4103,7 @@ case tgtok::Foreach: case tgtok::If: case tgtok::Let: + case tgtok::Dump: if (ParseObject(CurMultiClass)) return true; break; @@ -4193,9 +4247,11 @@ bool TGParser::ParseObject(MultiClass *MC) { switch (Lex.getCode()) { default: - return TokError( - "Expected assert, class, def, defm, defset, foreach, if, or let"); + return TokError("Expected assert, class, def, defm, defset, foreach, " + "print, if, or let"); case tgtok::Assert: return ParseAssert(MC); + case tgtok::Dump: + return ParseDump(MC); case tgtok::Def: return ParseDef(MC); case tgtok::Defm: return ParseDefm(MC); case tgtok::Defvar: return ParseDefvar(); diff --git a/llvm/test/TableGen/dump.td b/llvm/test/TableGen/dump.td new file mode 100644 --- /dev/null +++ b/llvm/test/TableGen/dump.td @@ -0,0 +1,64 @@ +// RUN: llvm-tblgen %s -o %t 2>&1 | FileCheck %s + +defvar a = 1111; +defvar b = !add(!dump( a), 1); +dump b; +// CHECK-LABEL: 1111 +// CHECK-LABEL: 1112 + +class C { + int I = i; +} +def c: C<3>; +dump c; +// CHECK-LABEL: c { // C +// CHECK-NEXT: int I = 3; +// CHECK-NEXT: } + +dump C<5>; +// CHECK-LABEL: anonymous_{{.*}} { // C +// CHECK-NEXT: int I = 5; +// CHECK-NEXT: } + +defvar lst = [C<1>, C<2>]; +dump lst; +// CHECK-LABEL: [anonymous_{{.*}}, anonymous_{{.*}}] + +defvar p5 = !foreach(i, lst, !dump(i)); +// CHECK-LABEL: anonymous_{{.*}} { // C +// CHECK-NEXT: int I = 1; +// CHECK-NEXT: } +// CHECK-LABEL: anonymous_{{.*}} { // C +// CHECK-NEXT: int I = 2; +// CHECK-NEXT: } + +foreach e = lst in { + dump e; +} +// CHECK-LABEL: anonymous_{{.*}} { // C +// CHECK-NEXT: int I = 1; +// CHECK-NEXT: } +// CHECK-LABEL: anonymous_{{.*}} { // C +// CHECK-NEXT: int I = 2; +// CHECK-NEXT: } + +class CC { + dump "i in CC ="; + dump i; + int I = !add(i, 1); +} +def cc : CC<33>; +// CHECK-LABEL: i in CC = +// CHECK-NEXT: 33 + +class MCC { + int I = !add(i, 1); +} +multiclass MC { + dump "i in MCC = "; + dump i; + def : MCC; +} +defm : MC<27>; +// CHECK-LABEL: i in MCC = +// CHECK-NEXT: 27