while
ブロックの内部解析続き。break
文がどのように分解されているのかを観察する。以下のようなVerilogファイルを題材に観察している。
module while_jump; int i; initial begin while(i < 100) begin if (i == 10) break; i++; end end endmodule
これらがAstNodeに変換される処理はここでは置いておくとして、break
をどのように変換するのかを確かめている。これにはV3LinkJump.cpp
を観察するのが良さそうだ。
まず、visit()
が大量に定義されている。これはAstNode()
の種類に応じて処理を切り替えるために定義されているらしい。
119: // VISITORS 120: virtual void visit(AstNodeModule* nodep) override { 130: virtual void visit(AstNodeFTask* nodep) override { 135: virtual void visit(AstNodeBlock* nodep) override { 145: virtual void visit(AstRepeat* nodep) override { 173: virtual void visit(AstWait* nodep) override { 184: virtual void visit(AstWhile* nodep) override { 198: virtual void visit(AstReturn* nodep) override { 226: virtual void visit(AstBreak* nodep) override { 238: virtual void visit(AstContinue* nodep) override { 251: virtual void visit(AstDisable* nodep) override { 276: virtual void visit(AstVarRef* nodep) override { 279: virtual void visit(AstConst*) override {} 280: virtual void visit(AstNode* nodep) override { iterateChildren(nodep); }
まず見てみるのは、visit(AstWhile* nodep)
だ。ここではmloopp
に自分自身のブロックを設定し(つまりこれからwhile
文の内部に入るということを意味する。それからwhile
文の各ブロックの解析に入る。
virtual void visit(AstWhile* nodep) override { // Don't need to track AstRepeat/AstFor as they have already been converted VL_RESTORER(m_loopp); VL_RESTORER(m_loopInc); { m_loopp = nodep; m_loopInc = false; iterateAndNextNull(nodep->precondsp()); iterateAndNextNull(nodep->condp()); iterateAndNextNull(nodep->bodysp()); m_loopInc = true; iterateAndNextNull(nodep->incsp()); } }
そして内部でbreak
文に遭遇した場合、visit(AstBreak *nodep)
が呼び出される。
virtual void visit(AstBreak* nodep) override { iterateChildren(nodep); if (!m_loopp) { nodep->v3error("break isn't underneath a loop"); } else { // Jump to the end of the loop AstJumpLabel* labelp = findAddLabel(m_loopp, false); nodep->addNextHere(new AstJumpGo(nodep->fileline(), labelp)); } nodep->unlinkFrBack(); VL_DO_DANGLING(pushDeletep(nodep), nodep); }
まずm_loopp
がNULL以外でなければならない。これはループの外部でbreak
されることを禁止する。そしてfindAddLabel()
を呼び出す。これはwhile
ループ(m_loopp
ブロック)の直後にラベルを作成する。次のAstJumpGo()
でそのラベルにジャンプする文を生成する。
AstJumpLabel* findAddLabel(AstNode* nodep, bool endOfIter) { // Put label under given node, and if WHILE optionally at end of iteration UINFO(4, "Create label for " << nodep << endl); if (VN_IS(nodep, JumpLabel)) return VN_CAST(nodep, JumpLabel); // Done AstNode* underp = nullptr; bool under_and_next = true; if (VN_IS(nodep, NodeBlock)) { underp = VN_CAST(nodep, NodeBlock)->stmtsp(); } else if (VN_IS(nodep, NodeFTask)) { /* ... 中略 ... */ if (VN_IS(underp, JumpLabel)) { return VN_CAST(underp, JumpLabel); } else { // Move underp stuff to be under a new label AstJumpBlock* blockp = new AstJumpBlock(nodep->fileline(), nullptr); AstJumpLabel* labelp = new AstJumpLabel(nodep->fileline(), blockp); blockp->labelp(labelp); AstNRelinker repHandle; if (under_and_next) { underp->unlinkFrBackWithNext(&repHandle); } else { underp->unlinkFrBack(&repHandle); } repHandle.relink(blockp); blockp->addStmtsp(underp); // Keep any AstVars under the function not under the new JumpLabel for (AstNode *nextp, *varp = underp; varp; varp = nextp) { nextp = varp->nextp(); if (VN_IS(varp, Var)) blockp->addPrev(varp->unlinkFrBack()); } // Label goes last blockp->addEndStmtsp(labelp); return labelp; } }
最後のaddEndStmtsp()
がミソだと思う。while
文の最後にラベルを追加し、それを関数の戻り値として返す。それを上記のAstJumpGo()
でジャンプするという流れになる。従って生成されるAstは以下のようになる。
1:2:2:1:1: WHILE 0x7fffecd65e10 <e75#> {c5ad} 1:2:2:1:1:2: LT 0x7fffecd64740 <e47> {c5al} @dt=0x7fffecd64800@(G/nw1) 1:2:2:1:1:2:1: VARREF 0x7fffecd5f950 <e55#> {c5aj} @dt=0@ i [RV] <- VAR 0x7fffecd63d30 <e6> {c3af} @dt=0@ i [VSTATIC] VAR 1:2:2:1:1:2:2: CONST 0x7fffecd64450 <e16> {c5an} @dt=0x7fffecd645c0@(G/swu32/7) ?32?sh64 1:2:2:1:1:3: BEGIN 0x7fffecd64be0 <e21> {c5as} 1:2:2:1:1:3:1: IF 0x7fffecd656f0 <e33> {c6af} 1:2:2:1:1:3:1:1: EQ 0x7fffecd65430 <e34> {c6al} @dt=0x7fffecd64800@(G/nw1) 1:2:2:1:1:3:1:1:1: VARREF 0x7fffecd600a0 <e58#> {c6aj} @dt=0@ i [RV] <- VAR 0x7fffecd63d30 <e6> {c3af} @dt=0@ i [VSTATIC] VAR 1:2:2:1:1:3:1:1:2: CONST 0x7fffecd65100 <e30> {c6ao} @dt=0x7fffecd652b0@(G/swu32/4) ?32?sha 1:2:2:1:1:3:1:2: JUMPGO 0x7fffecd60f60 <e78#> {c6as} -> JUMPLABEL 0x7fffecd60e90 <e74#> {c5ad} -> JUMPBLOCK 0x7fffecd60dc0 <e76#> {c5ad} 1:2:2:1:1:3:1: POSTADD 0x7fffecd65c60 <e45> {c7ag} @dt=0@ 1:2:2:1:1:3:1:1: CONST 0x7fffecd658a0 <e42> {c7ag} @dt=0x7fffecd65a50@(G/wu32/1) ?32?h1 1:2:2:1:1:3:1:2: VARREF 0x7fffecd601e0 <e61#> {c7af} @dt=0@ i [RV] <- VAR 0x7fffecd63d30 <e6> {c3af} @dt=0@ i [VSTATIC] VAR 1:2:2:1:1:3:1:3: VARREF 0x7fffecd60320 <e64#> {c7af} @dt=0@ i [LV] => VAR 0x7fffecd63d30 <e6> {c3af} @dt=0@ i [VSTATIC] VAR 1:2:2:1:2: JUMPLABEL 0x7fffecd60e90 <e74#> {c5ad} -> JUMPBLOCK 0x7fffecd60dc0 <e76#> {c5ad}
これよく見たら昨日書いたブロック間違っているな。WHILEブロックは分解されるわけじゃないから、JumpGoは2方向への条件分岐ジャンプを定義しているわけではなく、条件が成立すればWHILE
の外へ、そうでなければすぐ下のブロックに進むような仕組みなっている。