FPGA開発日記

カテゴリ別記事インデックス https://msyksphinz.github.io/github_pages , English Version https://fpgadevdiary.hatenadiary.com/

Verilatorのコンパイルフローを観察する (7. 008_LinkIncのソースコードを読む)

008_LinkIncVerilogファイル中の++--演算を処理するための関数のようだ。ヘッダファイルの説明書きを見てみよう。

  • src/V3LinkInc.cpp
 // V3LinkInc's Transformations:
 //
 //      prepost_stmt_visit
 //        PREADD/PRESUB
 //          Create a temporary __VIncrementX variable, assign the value of
 //          the current variable value to it, substitute the current
 //          variable with the temporary one in the statement.
 //          Increment/decrement the original variable with by the given
 //          value.
 //        POSTADD/POSTSUB
 //          Increment/decrement the current variable by the given value.
 //          Create a temporary __VIncrementX variable, assign the value of
 //          of the current variable (after the operation) to it. Substitute
 //          The original variable with the temporary one in the statement.
 //      prepost_non_stmt_visit
 //        PREADD/PRESUB/POSTADD/POSTSUB
 //          Increment/decrement the current variable by the given value.
 //          The order (pre/post) doesn't matter outside statements thus
 //          the pre/post operations are treated equally and there is no
 //          need for a temporary variable.

つまり、以下のような操作となる。

j = i++;  // は以下のように変換される。
VIncrementX = i;
i = i + 1;
j = VIncrementX;
i++; // は以下のように変換される
i = i + 1;

つまり、ステートメントの中では一度中間変数に値を格納してから変数のアップデート処理を行う必要があるが、単純な演算だとそのままインクリメントを行えばよい。これを制御しているのがsrc/V3LinkInc.cppのようだ。

  • src/V3LinkInc.cpp
     virtual void visit(AstPreAdd* nodep) override { prepost_visit(nodep); }
     virtual void visit(AstPostAdd* nodep) override { prepost_visit(nodep); }
     virtual void visit(AstPreSub* nodep) override { prepost_visit(nodep); }
     virtual void visit(AstPostSub* nodep) override { prepost_visit(nodep); }

ここではAstPreAddAstPostAddに焦点を当てて観察する。

prepost_visit()では上記のようにステートメントの中か、そうでないかで処理を切り替える。

     void prepost_visit(AstNodeTriop* nodep) {
         // Check if we are underneath a statement
         if (!m_insStmtp) {
             prepost_non_stmt_visit(nodep);
         } else {
             prepost_stmt_visit(nodep);
         }
     }

ステートメント内ではない場合、これは単純だ。インクリメントするためのAstAssign(AstAdd())ノードを生成する。

     void prepost_non_stmt_visit(AstNodeTriop* nodep) {
         /* 中略 */
         AstAssign* assignp;
         if (VN_IS(nodep, PreSub) || VN_IS(nodep, PostSub)) {
             assignp = new AstAssign(nodep->fileline(), storetop,
                                     new AstSub(nodep->fileline(), valuep, newconstp));
         } else {
             assignp = new AstAssign(nodep->fileline(), storetop,
                                     new AstAdd(nodep->fileline(), valuep, newconstp));
         }

そうではない場合、ステートメント中の場合は、中間変数を作成する。

     void prepost_stmt_visit(AstNodeTriop* nodep) {
         /* 中略 */
         string name = string("__Vincrement") + cvtToStr(++m_modIncrementsNum);
         AstVar* varp = new AstVar(fl, AstVarType::BLOCKTEMP, name, VFlagChildDType(),
                                   varrefp->varp()->subDTypep()->cloneTree(true));

         // Declare the variable
         insertBeforeStmt(nodep, varp);

         // Define what operation will we be doing
         AstNode* operp;
         if (VN_IS(nodep, PostSub) || VN_IS(nodep, PreSub)) {
             operp = new AstSub(fl, new AstVarRef(fl, varrefp->varp(), VAccess::READ), newconstp);
         } else {
             operp = new AstAdd(fl, new AstVarRef(fl, varrefp->varp(), VAccess::READ), newconstp);
         }
         if (VN_IS(nodep, PreAdd) || VN_IS(nodep, PreSub)) {
             // PreAdd/PreSub operations
             // Immediately after declaration - increment it by one
             m_insStmtp->addHereThisAsNext(
                 new AstAssign(fl, new AstVarRef(fl, varp, VAccess::WRITE), operp));
             // Immediately after incrementing - assign it to the original variable
             m_insStmtp->addHereThisAsNext(
                 new AstAssign(fl, new AstVarRef(fl, varrefp->varp(), VAccess::WRITE),
                               new AstVarRef(fl, varp, VAccess::READ)));
         } else {
             // PostAdd/PostSub operations
             // assign the original variable to the temporary one
             m_insStmtp->addHereThisAsNext(
                 new AstAssign(fl, new AstVarRef(fl, varp, VAccess::WRITE),
                               new AstVarRef(fl, varrefp->varp(), VAccess::READ)));
             // Increment the original variable by one
             m_insStmtp->addHereThisAsNext(
                 new AstAssign(fl, new AstVarRef(fl, varrefp->varp(), VAccess::WRITE), operp));
         }

まず、++ii++のどちらにも関わらず、i + 1の演算を生成する(operp = new AstAdd(fl, new AstVarRef(fl, varrefp->varp(), VAccess::READ), newconstp);がそれに当たる)。

そして、++iの場合は「代入処理の前に」作成した演算を挿入する(次の2つのAstAssignがそれに相当していると思われる。)

i++の場合は、まずはオリジナルの値を代入する処理を置き、その次にインクリメントの処理を置く。つまり上記の逆になる。

このようにして、結果的に以下のようなAstが生成される。

  • test.sv
 module test;

 int i;
 int j;
 initial begin
   j = i++;
   j = ++i;
 end
 endmodule
  • 変換前
 Verilator Tree Dump (format 0x3900) from <e49> to <e79>
      NETLIST 0x7fffed7bef40 <e1> {a0aa}  $root [1ps/1ps]
     1: MODULE 0x7fffed7d3570 <e49#> {c1ai}  test  L0 [1ps]
     1:2: VAR 0x7fffed7d3ad0 <e6> {c3af} @dt=0@  i [VSTATIC]  VAR
     1:2:1: BASICDTYPE 0x7fffed7d39f0 <e7> {c3ab} @dt=this@(sw32)  int kwd=int range=[31:0]
     1:2: VAR 0x7fffed7d3f30 <e14> {c4af} @dt=0@  j [VSTATIC]  VAR
     1:2:1: BASICDTYPE 0x7fffed7d3e50 <e13> {c4ab} @dt=this@(sw32)  int kwd=int range=[31:0]
     1:2: INITIAL 0x7fffed7d5720 <e46> {c5ab}
     1:2:2: BEGIN 0x7fffed7d4220 <e15> {c5aj}
     1:2:2:1: ASSIGN 0x7fffed7d4d60 <e26> {c6af} @dt=0@
     1:2:2:1:1: POSTADD 0x7fffed7d4a40 <e27> {c6ai} @dt=0@
     1:2:2:1:1:1: CONST 0x7fffed7d46c0 <e23> {c6ai} @dt=0x7fffed7d4830@(G/wu32/1)  ?32?h1
     1:2:2:1:1:2: VARREF 0x7fffed7cfee0 <e52#> {c6ah} @dt=0@  i [RV] <- VAR 0x7fffed7d3ad0 <e6> {c3af} @dt=0@  i [VSTATIC]  VAR
     1:2:2:1:1:3: VARREF 0x7fffed7d0020 <e55#> {c6ah} @dt=0@  i [LV] => VAR 0x7fffed7d3ad0 <e6> {c3af} @dt=0@  i [VSTATIC]  VAR
     1:2:2:1:2: VARREF 0x7fffed7d0160 <e58#> {c6ad} @dt=0@  j [LV] => VAR 0x7fffed7d3f30 <e14> {c4af} @dt=0@  j [VSTATIC]  VAR
     1:2:2:1: ASSIGN 0x7fffed7d55c0 <e44> {c7af} @dt=0@
     1:2:2:1:1: PREADD 0x7fffed7d5500 <e42> {c7ah} @dt=0@
     1:2:2:1:1:1: CONST 0x7fffed7d51d0 <e38> {c7ah} @dt=0x7fffed7d4830@(G/wu32/1)  ?32?h1
     1:2:2:1:1:2: VARREF 0x7fffed7d02a0 <e61#> {c7aj} @dt=0@  i [RV] <- VAR 0x7fffed7d3ad0 <e6> {c3af} @dt=0@  i [VSTATIC]  VAR
     1:2:2:1:1:3: VARREF 0x7fffed7d03e0 <e64#> {c7aj} @dt=0@  i [LV] => VAR 0x7fffed7d3ad0 <e6> {c3af} @dt=0@  i [VSTATIC]  VAR
     1:2:2:1:2: VARREF 0x7fffed7d0570 <e67#> {c7ad} @dt=0@  j [LV] => VAR 0x7fffed7d3f30 <e14> {c4af} @dt=0@  j [VSTATIC]  VAR
     3: TYPETABLE 0x7fffed7bf5e0 <e2> {a0aa}
         detailed  ->  BASICDTYPE 0x7fffed7d4830 <e20> {c6ai} @dt=this@(G/wu32/1)  logic [GENERIC] kwd=logic range=[31:0]
     3:1: BASICDTYPE 0x7fffed7d4830 <e20> {c6ai} @dt=this@(G/wu32/1)  logic [GENERIC] kwd=logic range=[31:0]
  • 変換後
 Verilator Tree Dump (format 0x3900) from <e79> to <e147>
      NETLIST 0x7fffed7bef40 <e1> {a0aa}  $root [1ps/1ps]
     1: MODULE 0x7fffed7d3570 <e49> {c1ai}  test  L0 [1ps]
     1:2: VAR 0x7fffed7d3ad0 <e6> {c3af} @dt=0@  i [VSTATIC]  VAR
     1:2:1: BASICDTYPE 0x7fffed7d39f0 <e7> {c3ab} @dt=this@(sw32)  int kwd=int range=[31:0]
     1:2: VAR 0x7fffed7d3f30 <e14> {c4af} @dt=0@  j [VSTATIC]  VAR
     1:2:1: BASICDTYPE 0x7fffed7d3e50 <e13> {c4ab} @dt=this@(sw32)  int kwd=int range=[31:0]
     1:2: INITIAL 0x7fffed7d5720 <e46> {c5ab}
     1:2:2: BEGIN 0x7fffed7d4220 <e15> {c5aj}
     1:2:2:1: ASSIGN 0x7fffed7d1600 <e96#> {c6af} @dt=0@
     1:2:2:1:1: VARREF 0x7fffed7d14c0 <e92#> {c6af} @dt=0@  i [RV] <- VAR 0x7fffed7d3ad0 <e6> {c3af} @dt=0@  i [VSTATIC]  VAR
     1:2:2:1:2: VARREF 0x7fffed7d1380 <e93#> {c6af} @dt=0@  __Vincrement1 [LV] => VAR 0x7fffed7d10c0 <e102#> {c6af} @dt=0@  __Vincrement1 BLOCKTEMP
     1:2:2:1: ASSIGN 0x7fffed7d1800 <e104#> {c6af} @dt=0@
     1:2:2:1:1: ADD 0x7fffed7cf530 <e99#> {c6af} @dt=0@
     1:2:2:1:1:1: VARREF 0x7fffed7d1240 <e87#> {c6af} @dt=0@  i [RV] <- VAR 0x7fffed7d3ad0 <e6> {c3af} @dt=0@  i [VSTATIC]  VAR
     1:2:2:1:1:2: CONST 0x7fffed7cf9c0 <e88#> {c6ai} @dt=0x7fffed7d4830@(G/wu32/1)  ?32?h1
     1:2:2:1:2: VARREF 0x7fffed7d16c0 <e100#> {c6af} @dt=0@  i [LV] => VAR 0x7fffed7d3ad0 <e6> {c3af} @dt=0@  i [VSTATIC]  VAR
     1:2:2:1: VAR 0x7fffed7d10c0 <e102#> {c6af} @dt=0@  __Vincrement1 BLOCKTEMP
     1:2:2:1:1: BASICDTYPE 0x7fffed7d0fe0 <e81#> {c3ab} @dt=this@(sw32)  int kwd=int range=[31:0]
     1:2:2:1: ASSIGN 0x7fffed7d4d60 <e83#> {c6af} @dt=0@
     1:2:2:1:1: VARREF 0x7fffed7d18c0 <e107#> {c6ah} @dt=0@  __Vincrement1 [LV] => VAR 0x7fffed7d10c0 <e102#> {c6af} @dt=0@  __Vincrement1 BLOCKTEMP
     1:2:2:1:2: VARREF 0x7fffed7d0160 <e58> {c6ad} @dt=0@  j [LV] => VAR 0x7fffed7d3f30 <e14> {c4af} @dt=0@  j [VSTATIC]  VAR
     1:2:2:1: ASSIGN 0x7fffed7d2130 <e130#> {c7af} @dt=0@
     1:2:2:1:1: ADD 0x7fffed7d1f30 <e125#> {c7af} @dt=0@
     1:2:2:1:1:1: VARREF 0x7fffed7d1df0 <e121#> {c7af} @dt=0@  i [RV] <- VAR 0x7fffed7d3ad0 <e6> {c3af} @dt=0@  i [VSTATIC]  VAR
     1:2:2:1:1:2: CONST 0x7fffed7d1a00 <e122#> {c7ah} @dt=0x7fffed7d4830@(G/wu32/1)  ?32?h1
     1:2:2:1:2: VARREF 0x7fffed7d1ff0 <e126#> {c7af} @dt=0@  __Vincrement2 [LV] => VAR 0x7fffed7d1c70 <e137#> {c7af} @dt=0@  __Vincrement2 BLOCKTEMP
     1:2:2:1: ASSIGN 0x7fffed7d2470 <e139#> {c7af} @dt=0@
     1:2:2:1:1: VARREF 0x7fffed7d2330 <e134#> {c7af} @dt=0@  __Vincrement2 [RV] <- VAR 0x7fffed7d1c70 <e137#> {c7af} @dt=0@  __Vincrement2 BLOCKTEMP
     1:2:2:1:2: VARREF 0x7fffed7d21f0 <e135#> {c7af} @dt=0@  i [LV] => VAR 0x7fffed7d3ad0 <e6> {c3af} @dt=0@  i [VSTATIC]  VAR
     1:2:2:1: VAR 0x7fffed7d1c70 <e137#> {c7af} @dt=0@  __Vincrement2 BLOCKTEMP
     1:2:2:1:1: BASICDTYPE 0x7fffed7d1b90 <e114#> {c3ab} @dt=this@(sw32)  int kwd=int range=[31:0]
     1:2:2:1: ASSIGN 0x7fffed7d55c0 <e116#> {c7af} @dt=0@
     1:2:2:1:1: VARREF 0x7fffed7d2530 <e142#> {c7aj} @dt=0@  __Vincrement2 [LV] => VAR 0x7fffed7d1c70 <e137#> {c7af} @dt=0@  __Vincrement2 BLOCKTEMP
     1:2:2:1:2: VARREF 0x7fffed7d0570 <e67> {c7ad} @dt=0@  j [LV] => VAR 0x7fffed7d3f30 <e14> {c4af} @dt=0@  j [VSTATIC]  VAR
     3: TYPETABLE 0x7fffed7bf5e0 <e2> {a0aa}
         detailed  ->  BASICDTYPE 0x7fffed7d4830 <e20> {c6ai} @dt=this@(G/wu32/1)  logic [GENERIC] kwd=logic range=[31:0]
     3:1: BASICDTYPE 0x7fffed7d4830 <e20> {c6ai} @dt=this@(G/wu32/1)  logic [GENERIC] kwd=logic range=[31:0]
f:id:msyksphinz:20210408000239p:plain
f:id:msyksphinz:20210408000300p:plain