VerilatorのInternalドキュメントを読む。ソースコードを読んでいるだけではだんだん良く分からなくなってきたので、一応概要を確認しておく。 概要の続き。
コーディング規約
インデントスタイル
Verilator C++ソースのインデントをマッチさせるために、レベル毎に4つのスペースを使用し、タブは8列にして、1つおきのインデントレベルがタブストップになるようにする。
全てのファイルは標準的なインデントを保証するために、以下のマジックヘッダを挿入するべきである:
// -*- mode: C++; c-file-style: "cc-mode" -*-
このコメントにより、インデントスタイルはcc-modeに設定される(Verilatorは数年前のCC-modeの変更に先立って、GNUスタイルのインデントでデフォルトを上書きしていたが、c-set-styleはそれを元に戻す)。
astgen
スクリプト
いくつかのPassの実装は非常に多くの繰り返しが含まれているのと、AstNode
のサブクラスを実装する必要がある。しかし、繰り返しの最中の最中にはこれらは非常に可動的であり、C++のマクロにより処理される。
VerilatorではこれはPerlスクリプトastgen
を使って実装されており、C++コードをプリプロセスする。例えばV3Const.cpp
はvisit()
を関数を、各バイナリ演算をTREEOP
マクロを使って実装してある。
オリジナルのC++ソースコードはobj_opt
とobj_dbg
サブディレクトリ(前者はVerilator向けに最適化されたもの、後者はデバッグ版)に変換される。例えば、V3Const.cpp
はV3Const__gen.cpp
に変換される。
Visitor関数
VerilatorはVisitorデザインパタンを使ってPassのリファインと最適化を行う。これによりPassのアルゴリズムとそれが動作するASTを分離することができる。WikipediaではVisitorのコンセプトについての説明がある (http://en.wikipedia.org/wiki/Visitor_pattern: 日本語のページは https://ja.wikipedia.org/wiki/Visitor_%E3%83%91%E3%82%BF%E3%83%BC%E3%83%B3)。
上記で示したよううに、すべてのVisitorはAstNVisitor
からの派生クラスである。すべてのAstNode
の派生クラスはaccept
メソッドを実装する。accept
メソッドはAstNVisitor
の派生クラスのインスタンスへの参照を渡し、AstNVisitor
のvisitメソッドを実行してAstNode
インスタンス(例えばthis
)を誘起する。
困難な点としては、accept
の呼び出しが、引数として受け取ったノードを破壊するような編集を実行する可能性がある。AstNode
のacceptSubtreeReturnEdits
メソッドは、accept
を適用し、元のノードが破壊されても結果のノードを返すために用意されている(破壊されない場合は元のノードが返されるだけ)。
visitorクラスの動作は、さまざまな AstNode
派生クラスの visit 関数をオーバーロードすることで実現している。特定の実装が見つからない場合、システムは継承階層上でオーバーロードされた実装を順に探していきます。たとえば、AstIf
の accept を呼び出すと、順に以下のようになる。
void visit (AstIf* nodep, AstNUser* vup) void visit (AstNodeIf* nodep, AstNUser* vup) void visit (AstNodeStmt* nodep, AstNUser* vup) void visit (AstNode* nodep, AstNUser* vup)
visitor関数間のデータの受け渡しには3つの方法がある。
visitorクラスのメンバー変数である。これは一般的には「親」の情報を子供に渡すために使用される。
m_modp
がその例である。この変数はコンストラクタで NULL に設定され、そのノード(AstModule
のビジター)が設定し、その後、子供たちが反復され、クリアされる。AstModule
以下の子供には NULL が設定され、それ以外のノードには NULL が消去される。ネストしたアイテムがある場合(例えば、AstForの下にAstForがある場合)、変数はAstForのビジターでsave-set-restoreされる必要があり、そうしないと下のforを終了すると上のforの設定が失われます。ユーザー属性。各
AstNode
(注:visitorではなく AST ノード)は、5 つのユーザ属性を持ち、user1()
~user5()
メソッドを用いて整数値でアクセスしたり、user1p()
~user5p()
メソッドを用いてポインタ(AstNUser
型)でアクセスしたりすることができる(グラフ探索パッケージからの一般的な手法である)。 visitorは、まずAstNode::user#ClearTree()
を呼び出して使用したいノードをクリアし、その後、任意のノードのuser()
に必要なデータをマークすることができる。読者は単にnodep->user()
を呼び出すだけであるが、適切にキャストする必要がある場合もあるので、nodep->userp()->castSOMETYPE()
をよく見かけます。各ビジターの先頭には、user()
の内容がそのビジタークラスにどのように適用されるかを説明するコメントがある。例えば、以下のようになる。cpp // NODE STATE // Cleared entire netlist // AstModule::user1p() // bool. True to inline this module
これは、
AstNetlist
でuser1ClearTree()
が呼ばれることを表している。各AstModule
のuser1()
は、インライン化するかどうかを示すために使用される。これらのコメントは、特定の
AstNode
タイプのuser#()
が 2 つの異なる目的に使用されることがないようにするために重要である。user#ClearTree
の呼び出しは高速で、ツリーを歩くわけではないので、かなり頻繁に呼び出しても問題ないことに注意してください。たとえば、すべてのモジュールで共通して呼び出される。パラメータは、「通常の」関数の呼び出し側から呼び出し側への方法に近い形で、ビジター間で渡すことができる。これは、ほとんどのvisitor関数で無視される
AstNUser
型の2番目のvup
パラメータである。V3Width
はこれを行いるが、上記の方法よりも面倒であることがわかり、非推奨となりました。(V3Width
はほぼ最初に書かれたモジュールでした。vup
をいたるところで通さなければならないのはプログラムが遅くなるので、いつかはこの方式が削除されるかもしれません)
イテレータ
AstNode
はツリー内を歩き回るためにいくつかのイテレータを提供している。それぞれは2つの引数、visitorv
, (AstNVisitor
型)、オプションであるユーザデータのポインタvup
(`AstNUser*型)である。2番目の引数はVisitor関数の節で説明したパラメータを渡すための方法であるが、この方式は廃止されており新しいvisitorクラスでは使用するべきではない。
iterate()
AstNode
のaccept
メソッドを実行し、visitor関数を実行する
IterateAndNextIgnoreEdit()
リスト中のAstNode
に対してaccept
メソッドを実行する(例えば、nextp
とbackp
ポインタに接続されているリストなど)
iterateAndNext()
リスト中のAstNode
に対してaccept
メソッドを実行する。もしノードがaccept
により変更されるのであれば、ノードが変更されなくなるまでaccept
を適用し続ける。
iterateListBackwards()
リスト中のAstNode
に対してaccept
メソッドを、最後の要素から順番に適用する。
iterateChildren()
iterateAndNext
メソッドをop1p
からop4p
までの各子供要素に適用する。
iterateChildrenBackwards()
iterateListBackwards
メソッドをop1p
からop4p
までの各子供要素適用する。
派生クラスの識別
一般的な要件として、特定のAstNode
クラスを識別することが挙げられる。例えばVisitorはAstIf
とAstGenIf
に対して個別のvisit
メソッドを実装するのではなく、ベースクラスに対して1つのメソッドを実装する場合がある:
void visit (AstNodeIf* nodep, AstNUser* vup)
しかし、このメソッドは、AstGenIf
のために呼び出された場合、追加のコードを指定したい場合がある。Verilator は、C++ の dynamic_cast を使用して、可能なノードタイプごとに castSOMETYPE()
メソッドを提供することでこれを実現している。このメソッドは、その型にキャストされたオブジェクトへのポインタを返すか(SOMETYPE クラスまたは SOMETYPE の派生クラスの場合)、さもなければ NULL を返す。よって、私たちのvisitorメソッドは次のようになる:
if (nodep->castAstGenIf()) {
<code specific to AstGenIf>
}
共通のテストはAstNetlist
のためのもので、これはASTのルートとなるノードである。