VerilatorがSystem Verilogファイルを字句解析、構文解析した後は、V3AstNode
というクラスに構造的に組み上げる。これをダンプしているのが以下のログファイルに相当する。
Verilator Tree Dump (format 0x3900) from <e0> to <e94> NETLIST 0x7fffe4eb4f40 <e1#> {a0aa} $root [1ps/1ps] 1: MODULE 0x7fffe4eca660 <e94#> {c1ai} simple_assign L0 [1ps] 1:2: PORT 0x7fffe4ecb3d0 <e19#> {c3az} in1 1:2: VAR 0x7fffe4ecb8d0 <e22#> {c3az} @dt=0@ in1 INPUT PORT 1:2:1: BASICDTYPE 0x7fffe4ecb4b0 <e21#> {c3ak} @dt=this@(nw1) logic kwd=logic 1:2:1:1: RANGE 0x7fffe4ec4f50 <e18#> {c3ar} 1:2:1:1:2: CONST 0x7fffe4ecb590 <e16#> {c3at} @dt=0x7fffe4ecad20@(G/swu32/3) ?32?sh4 1:2:1:1:3: CONST 0x7fffe4ecb720 <e17#> {c3aw} @dt=0x7fffe4ecb190@(G/swu32/1) ?32?sh0 1:2: PORT 0x7fffe4ecc5e0 <e50#> {c4az} in2 1:2: VAR 0x7fffe4eccdf0 <e49#> {c4az} @dt=0@ in2 INPUT PORT 1:2:1: BASICDTYPE 0x7fffe4ecc6c0 <e48#> {c4ak} @dt=this@(nw1) logic kwd=logic 1:2:1:1: RANGE 0x7fffe4ecca50 <e45#> {c4ar} 1:2:1:1:2: CONST 0x7fffe4eccb10 <e43#> {c4at} @dt=0x7fffe4ecad20@(G/swu32/3) ?32?sh4 1:2:1:1:3: CONST 0x7fffe4eccc80 <e44#> {c4aw} @dt=0x7fffe4ecb190@(G/swu32/1) ?32?sh0 1:2: PORT 0x7fffe4ecdb00 <e78#> {c5az} out 1:2: VAR 0x7fffe4ece0c0 <e77#> {c5az} @dt=0@ out OUTPUT PORT 1:2:1: BASICDTYPE 0x7fffe4ecdbe0 <e76#> {c5al} @dt=this@(nw1) logic kwd=logic 1:2:1:1: RANGE 0x7fffe4ecdcc0 <e73#> {c5ar} 1:2:1:1:2: CONST 0x7fffe4ecdd80 <e71#> {c5at} @dt=0x7fffe4ecad20@(G/swu32/3) ?32?sh4 1:2:1:1:3: CONST 0x7fffe4ecdf10 <e72#> {c5aw} @dt=0x7fffe4ecb190@(G/swu32/1) ?32?sh0 1:2: ASSIGNW 0x7fffe4ece860 <e93#> {c8am} @dt=0@ 1:2:1: AND 0x7fffe4ece7a0 <e91#> {c8as} @dt=0@ 1:2:1:1: PARSEREF 0x7fffe4ece540 <e88#> {c8ao} in1 [TEXT] 1:2:1:2: PARSEREF 0x7fffe4ece6c0 <e89#> {c8au} in2 [TEXT] 1:2:2: PARSEREF 0x7fffe4ece3c0 <e92#> {c8ai} out [TEXT] 3: TYPETABLE 0x7fffe4eb55e0 <e2#> {a0aa} detailed -> BASICDTYPE 0x7fffe4ecb190 <e13#> {c3aw} @dt=this@(G/swu32/1) logic [GENERIC] kwd=logic range=[31:0] detailed -> BASICDTYPE 0x7fffe4ecad20 <e8#> {c3at} @dt=this@(G/swu32/3) logic [GENERIC] kwd=logic range=[31:0] 3:1: BASICDTYPE 0x7fffe4ecad20 <e8#> {c3at} @dt=this@(G/swu32/3) logic [GENERIC] kwd=logic range=[31:0] 3:1: BASICDTYPE 0x7fffe4ecb190 <e13#> {c3aw} @dt=this@(G/swu32/1) logic [GENERIC] kwd=logic range=[31:0]
まずは手始めにこれがどのように実現されているのかを観察してみる。
V3AstNode
クラスの実体はV3Ast.h
に定義されている。
src/V3Ast.h
class AstNode VL_NOT_FINAL { // v ASTNODE_PREFETCH depends on below ordering of members AstNode* m_nextp; // Next peer in the parent's list AstNode* m_backp; // Node that points to this one (via next/op1/op2/...) AstNode* m_op1p; // Generic pointer 1 ...
このクラスをダンプするメソッドもAstNode
自体に定義されている。
void AstNode::dumpTree(std::ostream& os, const string& indent, int maxDepth) const { static int s_debugFileline = v3Global.opt.debugSrcLevel("fileline"); // --debugi-fileline 9 os << indent << " " << this << '\n'; if (debug() > 8) { os << indent << " "; dumpPtrs(os); } if (s_debugFileline >= 9) os << fileline()->warnContextSecondary(); if (maxDepth == 1) { if (op1p() || op2p() || op3p() || op4p()) os << indent << "1: ...(maxDepth)\n"; } else { for (const AstNode* nodep = op1p(); nodep; nodep = nodep->nextp()) { nodep->dumpTree(os, indent + "1:", maxDepth - 1); } for (const AstNode* nodep = op2p(); nodep; nodep = nodep->nextp()) { nodep->dumpTree(os, indent + "2:", maxDepth - 1); } for (const AstNode* nodep = op3p(); nodep; nodep = nodep->nextp()) { nodep->dumpTree(os, indent + "3:", maxDepth - 1); } for (const AstNode* nodep = op4p(); nodep; nodep = nodep->nextp()) { nodep->dumpTree(os, indent + "4:", maxDepth - 1); } } }
dumpTree()
を再帰的に見ているのは見ての通りなのだが、カギになるのはos << indent << " " << this << '\n';
の部分で、クラスの内部構造をダンプできるように<<
をオーバライドしている。なるほど、こうやって使うのか。
src/V3Ast.h
inline std::ostream& operator<<(std::ostream& os, const AstNode* rhs) { if (!rhs) { os << "nullptr"; } else { rhs->dump(os); } return os; }
src/V3AstNodes.cpp
void AstNode::dump(std::ostream& str) const { str << typeName() << " " << nodeAddr(this) //<< " " << nodeAddr(m_backp) << " <e" << std::dec << editCount() << ((editCount() >= editCountLast()) ? "#>" : ">") << " {" << fileline()->filenameLetters() << std::dec << fileline()->lastLineno() << fileline()->firstColumnLetters() << "}"; if (user1p()) str << " u1=" << nodeAddr(user1p()); if (user2p()) str << " u2=" << nodeAddr(user2p()); if (user3p()) str << " u3=" << nodeAddr(user3p()); if (user4p()) str << " u4=" << nodeAddr(user4p()); if (user5p()) str << " u5=" << nodeAddr(user5p()); if (hasDType()) { // Final @ so less likely to by accident read it as a nodep if (dtypep() == this) { str << " @dt=this@"; } else { str << " @dt=" << nodeAddr(dtypep()) << "@"; } if (AstNodeDType* dtp = dtypep()) dtp->dumpSmall(str); } else { // V3Broken will throw an error if (dtypep()) str << " %Error-dtype-exp=null,got=" << nodeAddr(dtypep()); } if (name() != "") { if (VN_IS(this, Const)) { str << " " << name(); // Already quoted } else { str << " " << V3OutFormatter::quoteNameControls(name()); } }
この全体になって先ほどのダンプファイルを読んでみると以下のように解釈すればいいことが分かる。