diff --git a/docs/CONTRIBUTORS b/docs/CONTRIBUTORS index 0c3be357f..df73f955e 100644 --- a/docs/CONTRIBUTORS +++ b/docs/CONTRIBUTORS @@ -71,6 +71,7 @@ Ludwig Rogiers Lukasz Dalek Maarten De Braekeleer Maciej Sobkowski +Marcel Chang Marco Widmer Mariusz Glebocki Markus Krause diff --git a/docs/guide/exe_verilator.rst b/docs/guide/exe_verilator.rst index a54510df4..b800261a2 100644 --- a/docs/guide/exe_verilator.rst +++ b/docs/guide/exe_verilator.rst @@ -373,6 +373,12 @@ Summary: <--dump-tree>` may be useful if the dump files are large and not desired. +.. option:: --dump-tree-dot + + Rarely needed. Enable dumping Ast .tree.dot debug files in Graphviz + Dot format. This option implies :vlopt:`--dump-tree`, unless + :vlopt:`--dumpi-tree` was passed explicitly. + .. option:: --dump-tree-addrids Rarely needed - for developer use. Replace AST node addresses with diff --git a/docs/internals.rst b/docs/internals.rst index c62881026..40cc6b3aa 100644 --- a/docs/internals.rst +++ b/docs/internals.rst @@ -1137,6 +1137,16 @@ Similarly the ``NETLIST`` has a list of modules referred to by its ``op1p()`` pointer. +.tree.dot Output +---------------- + +``*.tree.dot`` files are dumps of the AST Tree in `Graphviz +`__ dot format. This can be used to +visualize the AST Tree. The vertices correspond to ``AstNode`` +instances, and the edges represent the pointers (``op1p``, + ``op2p``, etc) between the nodes. + + Debugging with GDB ------------------ diff --git a/src/V3Ast.cpp b/src/V3Ast.cpp index 8f7b102ef..c83af8820 100644 --- a/src/V3Ast.cpp +++ b/src/V3Ast.cpp @@ -1141,6 +1141,46 @@ void AstNode::dumpTreeFile(const string& filename, bool append, bool doDump, boo } } +static void drawChildren(std::ostream& os, const AstNode* thisp, const AstNode* childp, const std::string& childName) { + if (childp) { + os << "\tn" << cvtToHex(thisp) << " -> n" << cvtToHex(childp) << " [" + << "label=\"" << childName << "\" color=red];\n"; + for (const AstNode* nodep = childp; nodep; nodep = nodep->nextp()) { + nodep->dumpTreeDot(os); + if (nodep->nextp()) { + os << "\tn" << cvtToHex(nodep) << " -> n" << cvtToHex(nodep->nextp()) << " [" + << "label=\"next\" color=red];\n"; + os << "\t{rank=same; n" << cvtToHex(nodep) << ", n" << cvtToHex(nodep->nextp()) + << "}\n"; + } + } + } +} + +void AstNode::dumpTreeDot(std::ostream& os) const { + os << "\tn" << cvtToHex(this) << "\t[" + << "label=\"" << typeName() << "\\n" < treedotp{V3File::new_ofstream(filename, append)}; + if (treedotp->fail()) v3fatal("Can't write " << filename); + *treedotp << "digraph vTree{\n"; + *treedotp << "\tgraph\t[label=\"" << filename + ".dot" << "\",\n"; + *treedotp << "\t\t labelloc=t, labeljust=l,\n"; + *treedotp << "\t\t //size=\"7.5,10\",\n" << "];\n"; + dumpTreeDot(*treedotp); + *treedotp << "}\n"; + } +} + void AstNode::v3errorEndFatal(std::ostringstream& str) const { v3errorEnd(str); assert(0); // LCOV_EXCL_LINE diff --git a/src/V3Ast.h b/src/V3Ast.h index 01a6af34c..b00436897 100644 --- a/src/V3Ast.h +++ b/src/V3Ast.h @@ -1862,6 +1862,8 @@ public: void dumpTreeFile(const string& filename, bool append = false, bool doDump = true, bool doCheck = true); static void dumpTreeFileGdb(const AstNode* nodep, const char* filenamep = nullptr); + void dumpTreeDot(std::ostream& os = std::cout) const; + void dumpTreeDotFile(const string& filename, bool append = false, bool doDump = true); // METHODS - queries // Changes control flow, disable some optimizations diff --git a/src/V3Global.cpp b/src/V3Global.cpp index 5510386f0..34cbe343b 100644 --- a/src/V3Global.cpp +++ b/src/V3Global.cpp @@ -93,8 +93,11 @@ string V3Global::digitsFilename(int number) { } void V3Global::dumpCheckGlobalTree(const string& stagename, int newNumber, bool doDump) { - v3Global.rootp()->dumpTreeFile(v3Global.debugFilename(stagename + ".tree", newNumber), false, - doDump); + const string treeFilename = v3Global.debugFilename(stagename + ".tree", newNumber); + v3Global.rootp()->dumpTreeFile(treeFilename, false, doDump); + if (v3Global.opt.dumpTreeDot()) { + v3Global.rootp()->dumpTreeDotFile(treeFilename + ".dot", false, doDump); + } if (v3Global.opt.stats()) V3Stats::statsStage(stagename); } diff --git a/src/V3Options.cpp b/src/V3Options.cpp index 4456f601e..235cef99f 100644 --- a/src/V3Options.cpp +++ b/src/V3Options.cpp @@ -820,6 +820,11 @@ void V3Options::notify() { // Mark options as available m_available = true; + + // --dump-tree-dot will turn on tree dumping. + if (!m_dumpLevel.count("tree") && m_dumpLevel.count("tree-dot")) { + m_dumpLevel["tree"] = m_dumpLevel["tree-dot"]; + } } //###################################################################### diff --git a/src/V3Options.h b/src/V3Options.h index 69870a701..4acf71308 100644 --- a/src/V3Options.h +++ b/src/V3Options.h @@ -454,6 +454,7 @@ public: bool decoration() const { return m_decoration; } bool dpiHdrOnly() const { return m_dpiHdrOnly; } bool dumpDefines() const { return m_dumpLevel.count("defines") && m_dumpLevel.at("defines"); } + bool dumpTreeDot() const { return m_dumpLevel.count("tree-dot") && m_dumpLevel.at("tree-dot"); } bool exe() const { return m_exe; } bool flatten() const { return m_flatten; } bool gmake() const { return m_gmake; } diff --git a/test_regress/t/t_dump_tree_dot.pl b/test_regress/t/t_dump_tree_dot.pl new file mode 100755 index 000000000..8caebed51 --- /dev/null +++ b/test_regress/t/t_dump_tree_dot.pl @@ -0,0 +1,20 @@ +#!/usr/bin/env perl +if (!$::Driver) { use FindBin; exec("$FindBin::Bin/bootstrap.pl", @ARGV, $0); die; } +# DESCRIPTION: Verilator: Verilog Test driver/expect definition +# +# Copyright 2003 by Wilson Snyder. This program is free software; you +# can redistribute it and/or modify it under the terms of either the GNU +# Lesser General Public License Version 3 or the Perl Artistic License +# Version 2.0. +# SPDX-License-Identifier: LGPL-3.0-only OR Artistic-2.0 + +scenarios(vlt => 1); + +top_filename("t/t_EXAMPLE.v"); + +lint( + v_flags => ["--lint-only --dump-tree-dot"], + ); + +ok(1); +1;