[llvm] [Tablegen] Add keywork `dump`. (PR #68793)
Francesco Petrogalli via llvm-commits
llvm-commits at lists.llvm.org
Wed Oct 11 04:20:06 PDT 2023
https://github.com/fpetrogalli created https://github.com/llvm/llvm-project/pull/68793
The keyword is intended for debugging purpose. It prints a message to stderr.
This patch is based on code originally written by Adam Nemet, and on the feedback received by the reviewers in
https://reviews.llvm.org/D157492.
>From 004967c45f8ee41528cf9fb22472ae1b247548b7 Mon Sep 17 00:00:00 2001
From: Francesco Petrogalli <francesco.petrogalli at apple.com>
Date: Wed, 11 Oct 2023 01:29:06 +0200
Subject: [PATCH] [Tablegen] Add keywork `dump`.
The keyword is intended for debugging purpose. It prints a message to
stderr.
This patch is based on code originally written by Adam Nemet, and on
the feedback received by the reviewers in
https://reviews.llvm.org/D157492.
---
llvm/docs/TableGen/ProgRef.rst | 27 ++++++++++---
llvm/include/llvm/TableGen/Error.h | 1 +
llvm/include/llvm/TableGen/Record.h | 16 ++++++++
llvm/lib/TableGen/Error.cpp | 7 ++++
llvm/lib/TableGen/Record.cpp | 10 +++++
llvm/lib/TableGen/TGLexer.cpp | 51 ++++++++++++------------
llvm/lib/TableGen/TGLexer.h | 1 +
llvm/lib/TableGen/TGParser.cpp | 61 +++++++++++++++++++++++++++--
llvm/lib/TableGen/TGParser.h | 4 ++
llvm/test/TableGen/dump.td | 46 ++++++++++++++++++++++
10 files changed, 191 insertions(+), 33 deletions(-)
create mode 100644 llvm/test/TableGen/dump.td
diff --git a/llvm/docs/TableGen/ProgRef.rst b/llvm/docs/TableGen/ProgRef.rst
index 45e25573b0e00f0..9f916eafd6053e8 100644
--- a/llvm/docs/TableGen/ProgRef.rst
+++ b/llvm/docs/TableGen/ProgRef.rst
@@ -202,10 +202,10 @@ TableGen has the following reserved keywords, which cannot be used as
identifiers::
assert bit bits class code
- dag def else false foreach
- defm defset defvar field if
- in include int let list
- multiclass string then true
+ dag def dump else false
+ foreach defm defset defvar field
+ if in include int let
+ list multiclass string then true
.. warning::
The ``field`` reserved word is deprecated, except when used with the
@@ -571,7 +571,7 @@ files.
TableGenFile: (`Statement` | `IncludeDirective`
:| `PreprocessorDirective`)*
Statement: `Assert` | `Class` | `Def` | `Defm` | `Defset` | `Defvar`
- :| `Foreach` | `If` | `Let` | `MultiClass`
+ :| `Dump` | `Foreach` | `If` | `Let` | `MultiClass`
The following sections describe each of these top-level statements.
@@ -1275,6 +1275,23 @@ be nested.
This loop defines records named ``R0``, ``R1``, ``R2``, and ``R3``, along
with ``F0``, ``F1``, ``F2``, and ``F3``.
+``dump`` --- print messages to stderr
+-------------------------------------
+
+A ``dump`` statement prints the input string to standard error
+output. It is intended for debugging purpose.
+
+.. productionlist::
+ Dump: "dump" `string` ";"
+
+For example, it can be used in combination with `!repl` to investigate
+the values passed to a multiclass:
+
+.. code-block:: text
+
+ multiclass MC<dag s> {
+ dump "s = " # !repl(s);
+ }
``if`` --- select statements based on a test
--------------------------------------------
diff --git a/llvm/include/llvm/TableGen/Error.h b/llvm/include/llvm/TableGen/Error.h
index 2e639224c9c0312..ce8dc6aa0397e10 100644
--- a/llvm/include/llvm/TableGen/Error.h
+++ b/llvm/include/llvm/TableGen/Error.h
@@ -43,6 +43,7 @@ void PrintError(const RecordVal *RecVal, const Twine &Msg);
[[noreturn]] void PrintFatalError(const RecordVal *RecVal, const Twine &Msg);
void CheckAssert(SMLoc Loc, Init *Condition, Init *Message);
+void CheckDump(SMLoc Loc, Init *Message);
extern SourceMgr SrcMgr;
extern unsigned ErrorsPrinted;
diff --git a/llvm/include/llvm/TableGen/Record.h b/llvm/include/llvm/TableGen/Record.h
index 5d6877cfacdcf19..f220b713efcd8f0 100644
--- a/llvm/include/llvm/TableGen/Record.h
+++ b/llvm/include/llvm/TableGen/Record.h
@@ -1641,6 +1641,15 @@ class Record {
: Loc(Loc), Condition(Condition), Message(Message) {}
};
+ struct DumpInfo {
+ SMLoc Loc;
+ Init *Message;
+
+ // User-defined constructor to support std::make_unique(). It can be
+ // removed in C++20 when braced initialization is supported.
+ DumpInfo(SMLoc Loc, Init *Message) : Loc(Loc), Message(Message) {}
+ };
+
private:
Init *Name;
// Location where record was instantiated, followed by the location of
@@ -1652,6 +1661,7 @@ class Record {
SmallVector<Init *, 0> TemplateArgs;
SmallVector<RecordVal, 0> Values;
SmallVector<AssertionInfo, 0> Assertions;
+ SmallVector<DumpInfo, 0> Dumps;
// All superclasses in the inheritance forest in post-order (yes, it
// must be a forest; diamond-shaped inheritance is not allowed).
@@ -1742,6 +1752,7 @@ class Record {
ArrayRef<RecordVal> getValues() const { return Values; }
ArrayRef<AssertionInfo> getAssertions() const { return Assertions; }
+ ArrayRef<DumpInfo> getDumps() const { return Dumps; }
ArrayRef<std::pair<Record *, SMRange>> getSuperClasses() const {
return SuperClasses;
@@ -1802,11 +1813,16 @@ class Record {
Assertions.push_back(AssertionInfo(Loc, Condition, Message));
}
+ void addDump(SMLoc Loc, Init *Message) {
+ Dumps.push_back(DumpInfo(Loc, Message));
+ }
+
void appendAssertions(const Record *Rec) {
Assertions.append(Rec->Assertions);
}
void checkRecordAssertions();
+ void checkRecordDumps();
void checkUnusedTemplateArgs();
bool isSubClassOf(const Record *R) const {
diff --git a/llvm/lib/TableGen/Error.cpp b/llvm/lib/TableGen/Error.cpp
index ebe9129ebaeb59c..f3a43b87889ca5c 100644
--- a/llvm/lib/TableGen/Error.cpp
+++ b/llvm/lib/TableGen/Error.cpp
@@ -170,4 +170,11 @@ void CheckAssert(SMLoc Loc, Init *Condition, Init *Message) {
}
}
+void CheckDump(SMLoc Loc, Init *Message) {
+ if (auto *MessageInit = dyn_cast<StringInit>(Message))
+ PrintNote(MessageInit->getValue());
+ else
+ PrintNote("(dump message is not a string)");
+}
+
} // end namespace llvm
diff --git a/llvm/lib/TableGen/Record.cpp b/llvm/lib/TableGen/Record.cpp
index 24b48b453e63e55..b7d355ec406d3a7 100644
--- a/llvm/lib/TableGen/Record.cpp
+++ b/llvm/lib/TableGen/Record.cpp
@@ -3119,6 +3119,16 @@ void Record::checkRecordAssertions() {
}
}
+void Record::checkRecordDumps() {
+ RecordResolver R(*this);
+ R.setFinal(true);
+
+ for (const auto &Dump : getDumps()) {
+ Init *Message = Dump.Message->resolveReferences(R);
+ CheckDump(Dump.Loc, Message);
+ }
+}
+
// Report a warning if the record has unused template arguments.
void Record::checkUnusedTemplateArgs() {
for (const Init *TA : getTemplateArgs()) {
diff --git a/llvm/lib/TableGen/TGLexer.cpp b/llvm/lib/TableGen/TGLexer.cpp
index d5140e91fce9e94..c811a67d930d481 100644
--- a/llvm/lib/TableGen/TGLexer.cpp
+++ b/llvm/lib/TableGen/TGLexer.cpp
@@ -346,31 +346,32 @@ tgtok::TokKind TGLexer::LexIdentifier() {
StringRef Str(IdentStart, CurPtr-IdentStart);
tgtok::TokKind Kind = StringSwitch<tgtok::TokKind>(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) {
diff --git a/llvm/lib/TableGen/TGLexer.h b/llvm/lib/TableGen/TGLexer.h
index 4429c91b7c9cf76..2e2aa59f344083d 100644
--- a/llvm/lib/TableGen/TGLexer.h
+++ b/llvm/lib/TableGen/TGLexer.h
@@ -98,6 +98,7 @@ enum TokKind {
Defm,
Defset,
Defvar,
+ Dump,
Foreach,
If,
Let,
diff --git a/llvm/lib/TableGen/TGParser.cpp b/llvm/lib/TableGen/TGParser.cpp
index 2e61925f55651ea..1f117234f9eee31 100644
--- a/llvm/lib/TableGen/TGParser.cpp
+++ b/llvm/lib/TableGen/TGParser.cpp
@@ -376,7 +376,7 @@ bool TGParser::AddSubMultiClass(MultiClass *CurMC,
/// Add a record, foreach loop, or assertion to the current context.
bool TGParser::addEntry(RecordsEntry E) {
- assert((!!E.Rec + !!E.Loop + !!E.Assertion) == 1 &&
+ assert((!!E.Rec + !!E.Loop + !!E.Assertion + !!E.Dump) == 1 &&
"RecordsEntry has invalid number of items");
// If we are parsing a loop, add it to the loop's entries.
@@ -404,6 +404,11 @@ bool TGParser::addEntry(RecordsEntry E) {
return false;
}
+ if (E.Dump) {
+ CheckDump(E.Dump->Loc, E.Dump->Message);
+ return false;
+ }
+
// It must be a record, so finish it off.
return addDefOne(std::move(E.Rec));
}
@@ -498,6 +503,18 @@ bool TGParser::resolve(const std::vector<RecordsEntry> &Source,
else
CheckAssert(E.Assertion->Loc, Condition, Message);
+ } else if (E.Dump) {
+ MapResolver R;
+ for (const auto &S : Substs)
+ R.set(S.first, S.second);
+ Init *Message = E.Dump->Message->resolveReferences(R);
+
+ if (Dest)
+ Dest->push_back(
+ std::make_unique<Record::DumpInfo>(E.Dump->Loc, Message));
+ else
+ CheckDump(E.Dump->Loc, Message);
+
} else {
auto Rec = std::make_unique<Record>(*E.Rec);
if (Loc)
@@ -545,6 +562,9 @@ bool TGParser::addDefOne(std::unique_ptr<Record> Rec) {
// Check the assertions.
Rec->checkRecordAssertions();
+ // Run the dumps.
+ Rec->checkRecordDumps();
+
// If ObjectBody has template arguments, it's an error.
assert(Rec->getTemplateArgs().empty() && "How'd this get template args?");
@@ -3405,6 +3425,7 @@ bool TGParser::ParseTemplateArgList(Record *CurRec) {
/// BodyItem ::= Declaration ';'
/// BodyItem ::= LET ID OptionalBitList '=' Value ';'
/// BodyItem ::= Defvar
+/// BodyItem ::= Dump
/// BodyItem ::= Assert
///
bool TGParser::ParseBodyItem(Record *CurRec) {
@@ -3414,6 +3435,9 @@ bool TGParser::ParseBodyItem(Record *CurRec) {
if (Lex.getCode() == tgtok::Defvar)
return ParseDefvar(CurRec);
+ if (Lex.getCode() == tgtok::Dump)
+ return ParseDump(nullptr, CurRec);
+
if (Lex.getCode() != tgtok::Let) {
if (!ParseDeclaration(CurRec, false))
return true;
@@ -3510,6 +3534,10 @@ bool TGParser::ApplyLetStack(RecordsEntry &Entry) {
if (Entry.Assertion)
return false;
+ // Let bindings are not applied to dumps.
+ if (Entry.Dump)
+ return false;
+
for (auto &E : Entry.Loop->Entries) {
if (ApplyLetStack(E))
return true;
@@ -4090,13 +4118,14 @@ bool TGParser::ParseMultiClass() {
while (Lex.getCode() != tgtok::r_brace) {
switch (Lex.getCode()) {
default:
- return TokError("expected 'assert', 'def', 'defm', 'defvar', "
+ return TokError("expected 'assert', 'def', 'defm', 'defvar', 'dump', "
"'foreach', 'if', or 'let' in multiclass body");
case tgtok::Assert:
case tgtok::Def:
case tgtok::Defm:
case tgtok::Defvar:
+ case tgtok::Dump:
case tgtok::Foreach:
case tgtok::If:
case tgtok::Let:
@@ -4244,11 +4273,13 @@ bool TGParser::ParseObject(MultiClass *MC) {
switch (Lex.getCode()) {
default:
return TokError(
- "Expected assert, class, def, defm, defset, foreach, if, or let");
+ "Expected assert, class, def, defm, defset, dump, foreach, if, or let");
case tgtok::Assert: return ParseAssert(MC);
case tgtok::Def: return ParseDef(MC);
case tgtok::Defm: return ParseDefm(MC);
case tgtok::Defvar: return ParseDefvar();
+ case tgtok::Dump:
+ return ParseDump(MC);
case tgtok::Foreach: return ParseForeach(MC);
case tgtok::If: return ParseIf(MC);
case tgtok::Let: return ParseTopLevelLet(MC);
@@ -4359,3 +4390,27 @@ LLVM_DUMP_METHOD void MultiClass::dump() const {
E.dump();
}
#endif
+
+bool TGParser::ParseDump(MultiClass *CurMultiClass, Record *CurRec) {
+ assert(Lex.getCode() == tgtok::Dump && "Unknown tok");
+ Lex.Lex(); // eat the operation
+
+ Init *Message = ParseValue(CurRec);
+ if (!Message)
+ return true;
+
+ std::string Msg;
+ if (auto SI = dyn_cast<StringInit>(Message))
+ Msg = SI->getValue();
+
+ if (!consume(tgtok::semi))
+ return TokError("expected ';'");
+
+ SMLoc ConditionLoc = Lex.getLoc();
+ if (CurRec)
+ CurRec->addDump(ConditionLoc, Message);
+ else
+ addEntry(std::make_unique<Record::DumpInfo>(ConditionLoc, Message));
+
+ return false;
+}
diff --git a/llvm/lib/TableGen/TGParser.h b/llvm/lib/TableGen/TGParser.h
index d42cdad88a843da..c5365ff2709243f 100644
--- a/llvm/lib/TableGen/TGParser.h
+++ b/llvm/lib/TableGen/TGParser.h
@@ -41,6 +41,7 @@ struct RecordsEntry {
std::unique_ptr<Record> Rec;
std::unique_ptr<ForeachLoop> Loop;
std::unique_ptr<Record::AssertionInfo> Assertion;
+ std::unique_ptr<Record::DumpInfo> Dump;
void dump() const;
@@ -49,6 +50,8 @@ struct RecordsEntry {
RecordsEntry(std::unique_ptr<ForeachLoop> Loop) : Loop(std::move(Loop)) {}
RecordsEntry(std::unique_ptr<Record::AssertionInfo> Assertion)
: Assertion(std::move(Assertion)) {}
+ RecordsEntry(std::unique_ptr<Record::DumpInfo> Dump)
+ : Dump(std::move(Dump)) {}
};
/// ForeachLoop - Record the iteration state associated with a for loop.
@@ -262,6 +265,7 @@ class TGParser {
bool ParseDef(MultiClass *CurMultiClass);
bool ParseDefset();
bool ParseDefvar(Record *CurRec = nullptr);
+ bool ParseDump(MultiClass *CurMultiClass, Record *CurRec = nullptr);
bool ParseForeach(MultiClass *CurMultiClass);
bool ParseIf(MultiClass *CurMultiClass);
bool ParseIfBody(MultiClass *CurMultiClass, StringRef Kind);
diff --git a/llvm/test/TableGen/dump.td b/llvm/test/TableGen/dump.td
new file mode 100644
index 000000000000000..ea0870a9a993138
--- /dev/null
+++ b/llvm/test/TableGen/dump.td
@@ -0,0 +1,46 @@
+// RUN: llvm-tblgen %s -o - 2>&1 >/dev/null | FileCheck %s
+
+// CHECK-LABEL: Debug message
+dump "Debug message";
+
+// CHECK-LABEL: The Value of A is:
+// CHECK-NEXT: a { // A
+// CHECK-NEXT: string A = "some text";
+// CHECK-NEXT: dag X = (op op);
+// CHECK-NEXT: }
+def op;
+class A {
+ string A = "some text";
+ dag X =(op op);
+}
+def a : A;
+dump "The Value of A is: \n" # !repr(a);
+
+
+// CHECK-LABEL: b { // A
+// CHECK-NEXT: string A = "some text";
+// CHECK-NEXT: dag X = (op op);
+// CHECK-NEXT: }
+def b : A;
+dump !repr(b) ;
+
+// CHECK-LABEL: got a pair of values ["some other text" : 12], and an empty record:
+// CHECK-NEXT: X {
+// CHECK-NEXT: }
+defvar value_A = "some other text";
+defvar value_B = 12;
+def X;
+dump "got a pair of values [" # !repr(value_A) # " : " # !repr(value_B) # "], " # "and an empty record:\n" # !repr(X);
+
+multiclass MC<dag s> {
+ dump "s = " # !repr(s);
+ dump "args[0] = " # !repr(!getdagarg<A>(s,0));
+ def A;
+}
+// CHECK-LABEL: note: s = (op a)
+// CHECK-NEXT: args[0] = a { // A
+// CHECK-NEXT: string A = "some text";
+// CHECK-NEXT: dag X = (op op);
+// CHECK-NEXT: }
+defm X : MC<(op a)>;
+
More information about the llvm-commits
mailing list