diff --git a/src/V3Ast.h b/src/V3Ast.h index 0e8f277a7..eaf3f6e58 100644 --- a/src/V3Ast.h +++ b/src/V3Ast.h @@ -1874,7 +1874,6 @@ public: return static_cast(addNext(nodep, newp)); } inline AstNode* addNext(AstNode* newp); - inline void addPrev(AstNode* newp); void addNextHere(AstNode* newp); // Insert newp at this->nextp void addHereThisAsNext(AstNode* newp); // Adds at old place of this, this becomes next void replaceWith(AstNode* newp); // Replace current node in tree with new node @@ -2207,10 +2206,6 @@ AstNode* AstNode::addNext(AstNode* nodep, AstNode* newp); // Inline method implementations AstNode* AstNode::addNext(AstNode* newp) { return addNext(this, newp); } -void AstNode::addPrev(AstNode* newp) { - replaceWith(newp); - newp->addNext(this); -} // Specialisations of privateTypeTest #include "V3Ast__gen_type_tests.h" // From ./astgen diff --git a/src/V3AstNodeOther.h b/src/V3AstNodeOther.h index cbafbfc8b..a88aa3ee6 100644 --- a/src/V3AstNodeOther.h +++ b/src/V3AstNodeOther.h @@ -2725,6 +2725,26 @@ public: // * = Add a newline for $display bool addNewline() const { return displayType().addNewline(); } }; +class AstDoWhile final : public AstNodeStmt { + // @astgen op1 := precondsp : List[AstNode] + // @astgen op2 := condp : AstNode + // @astgen op3 := stmtsp : List[AstNode] + // @astgen op4 := incsp : List[AstNode] +public: + AstDoWhile(FileLine* fl, AstNode* conditionp, AstNode* stmtsp = nullptr, + AstNode* incsp = nullptr) + : ASTGEN_SUPER_DoWhile(fl) { + condp(conditionp); + addStmtsp(stmtsp); + addIncsp(incsp); + } + ASTGEN_MEMBERS_AstDoWhile; + bool isGateOptimizable() const override { return false; } + int instrCount() const override { return INSTR_COUNT_BRANCH; } + bool same(const AstNode* /*samep*/) const override { return true; } + // Stop statement searchback here + bool isFirstInMyListOfStatements(AstNode* n) const override { return n == stmtsp(); } +}; class AstDumpCtl final : public AstNodeStmt { // $dumpon etc // Parents: expr diff --git a/src/V3LinkJump.cpp b/src/V3LinkJump.cpp index e42e37987..6e1b6f319 100644 --- a/src/V3LinkJump.cpp +++ b/src/V3LinkJump.cpp @@ -83,6 +83,14 @@ private: underp = nodep; under_and_next = false; // IE we skip the entire while } + } else if (AstDoWhile* const dowhilep = VN_CAST(nodep, DoWhile)) { + // Handle it the same as AstWhile, because it will be converted to it + if (endOfIter) { + underp = dowhilep->stmtsp(); + } else { + underp = nodep; + under_and_next = false; + } } else { nodep->v3fatalSrc("Unknown jump point for break/disable/continue"); return nullptr; @@ -113,7 +121,7 @@ private: // 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()); + if (VN_IS(varp, Var)) blockp->addHereThisAsNext(varp->unlinkFrBack()); } // Label goes last blockp->addEndStmtsp(labelp); @@ -189,6 +197,27 @@ private: iterateAndNextNull(nodep->incsp()); } } + void visit(AstDoWhile* nodep) override { + // It is converted to AstWhile in this visit method + VL_RESTORER(m_loopp); + VL_RESTORER(m_loopInc); + { + m_loopp = nodep; + m_loopInc = false; + iterateAndNextNull(nodep->precondsp()); + iterateAndNextNull(nodep->condp()); + iterateAndNextNull(nodep->stmtsp()); + m_loopInc = true; + iterateAndNextNull(nodep->incsp()); + } + AstNode* const condp = nodep->condp() ? nodep->condp()->unlinkFrBack() : nullptr; + AstNode* const bodyp = nodep->stmtsp() ? nodep->stmtsp()->unlinkFrBack() : nullptr; + AstNode* const incsp = nodep->incsp() ? nodep->incsp()->unlinkFrBack() : nullptr; + AstWhile* const whilep = new AstWhile{nodep->fileline(), condp, bodyp, incsp}; + nodep->replaceWith(whilep); + VL_DO_DANGLING(nodep->deleteTree(), nodep); + if (bodyp) whilep->addHereThisAsNext(bodyp->cloneTree(false)); + } void visit(AstForeach* nodep) override { VL_RESTORER(m_loopp); { @@ -212,14 +241,14 @@ private: } else { if (funcp && nodep->lhsp()) { // Set output variable to return value - nodep->addPrev(new AstAssign( + nodep->addHereThisAsNext(new AstAssign( nodep->fileline(), new AstVarRef(nodep->fileline(), VN_AS(funcp->fvarp(), Var), VAccess::WRITE), nodep->lhsp()->unlinkFrBackWithNext())); } // Jump to the end of the function call AstJumpLabel* const labelp = findAddLabel(m_ftaskp, false); - nodep->addPrev(new AstJumpGo(nodep->fileline(), labelp)); + nodep->addHereThisAsNext(new AstJumpGo(nodep->fileline(), labelp)); } nodep->unlinkFrBack(); VL_DO_DANGLING(pushDeletep(nodep), nodep); diff --git a/src/verilog.y b/src/verilog.y index 28f453728..1367e140d 100644 --- a/src/verilog.y +++ b/src/verilog.y @@ -3324,11 +3324,7 @@ statement_item: // IEEE: statement_item | yWHILE '(' expr ')' stmtBlock { $$ = new AstWhile{$1, $3, $5}; } // // for's first ';' is in for_initialization | statementFor { $$ = $1; } - | yDO stmtBlock yWHILE '(' expr ')' ';' { if ($2) { - $$ = $2->cloneTree(true); - $$->addNext(new AstWhile($1,$5,$2)); - } - else $$ = new AstWhile($1,$5); } + | yDO stmtBlock yWHILE '(' expr ')' ';' { $$ = new AstDoWhile{$1, $5, $2}; } // // IEEE says array_identifier here, but dotted accepted in VMM and 1800-2009 | yFOREACH '(' idClassSelForeach ')' stmtBlock { $$ = new AstForeach($1, $3, $5); } // diff --git a/test_regress/t/t_continue_do_while_bad.out b/test_regress/t/t_continue_do_while_bad.out new file mode 100644 index 000000000..797057234 --- /dev/null +++ b/test_regress/t/t_continue_do_while_bad.out @@ -0,0 +1,6 @@ +%Warning-INFINITELOOP: t/t_continue_do_while_bad.v:14:7: Infinite loop (condition always true) + 14 | do begin + | ^~ + ... For warning description see https://verilator.org/warn/INFINITELOOP?v=latest + ... Use "/* verilator lint_off INFINITELOOP */" and lint_on around source to disable this message. +%Error: Exiting due to diff --git a/test_regress/t/t_continue_do_while_bad.pl b/test_regress/t/t_continue_do_while_bad.pl new file mode 100755 index 000000000..f201521f0 --- /dev/null +++ b/test_regress/t/t_continue_do_while_bad.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 2022 by Antmicro Ltd. 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); + +compile( + expect_filename=>$Self->{golden_filename}, + verilator_flags2=> ['--assert'], + fails => 1, + ); + +ok(1); +1; diff --git a/test_regress/t/t_continue_do_while_bad.v b/test_regress/t/t_continue_do_while_bad.v new file mode 100644 index 000000000..e1ba1eae9 --- /dev/null +++ b/test_regress/t/t_continue_do_while_bad.v @@ -0,0 +1,24 @@ +// DESCRIPTION: Verilator: Verilog Test module +// +// This file ONLY is placed under the Creative Commons Public Domain, for +// any use, without warranty, 2022 by Antmicro Ltd. +// SPDX-License-Identifier: CC0-1.0 + +module t (/*AUTOARG*/ + clk + ); + + input clk; + + function void infinite_loop; + do begin + continue; + end + while (1); + endfunction + + always @(posedge clk) begin + infinite_loop(); + $stop; + end +endmodule diff --git a/test_regress/t/t_jumps_do_while.pl b/test_regress/t/t_jumps_do_while.pl new file mode 100755 index 000000000..c505d6263 --- /dev/null +++ b/test_regress/t/t_jumps_do_while.pl @@ -0,0 +1,22 @@ +#!/usr/bin/env perl +if (!$::Driver) { use FindBin; exec("$FindBin::Bin/bootstrap.pl", @ARGV, $0); die; } +# DESCRIPTION: Verilator: Verilog Test driver/expect definition +# +# Copyright 2022 by Antmicro Ltd. 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(simulator => 1); + +compile( + verilator_flags2 => ['--assert'], + ); + +execute( + check_finished => 1, + ); + +ok(1); +1; diff --git a/test_regress/t/t_jumps_do_while.v b/test_regress/t/t_jumps_do_while.v new file mode 100644 index 000000000..217e46b3e --- /dev/null +++ b/test_regress/t/t_jumps_do_while.v @@ -0,0 +1,173 @@ +// DESCRIPTION: Verilator: Verilog Test module +// +// This file ONLY is placed under the Creative Commons Public Domain, for +// any use, without warranty, 2022 by Antmicro Ltd. +// SPDX-License-Identifier: CC0-1.0 + +module t (/*AUTOARG*/ + clk + ); + + input clk; + + function bit test_1; + int iterations = 0; + do begin + iterations++; + break; + end + while (1); + return iterations == 1; + endfunction + + function bit test_2; + int iterations = 0; + do begin + break; + iterations++; + end + while (1); + return iterations == 0; + endfunction + + function bit test_3; + do + break; + while (1); + return 1'b1; + endfunction + + function bit test_4; + int incr = 0; + do begin + incr++; + break; + incr++; + end + while (1); + return incr == 1; + endfunction + + function bit test_5; + int incr = 0; + do begin + do + incr++; + while (incr < 9); + incr++; + break; + incr++; + end + while (1); + return incr == 10; + endfunction + + function bit test_6; + int incr = 0; + do begin + do begin + incr += 1; + incr += 2; + end + while (incr < 9); + incr++; + break; + incr++; + end + while (1); + return incr == 10; + endfunction + + function bit test_7; + int incr = 0; + do begin + do begin + incr += 1; + break; + incr += 2; + end + while (incr < 9); + incr++; + break; + incr++; + end + while (1); + return incr == 2; + endfunction + + function bit test_8; + int incr = 0; + do begin + incr++; + continue; + incr++; + end + while (0); + return incr == 1; + endfunction + + function bit test_9; + int incr = 0; + do begin + incr++; + continue; + incr++; + end + while (incr < 5); + return incr == 5; + endfunction + + function bit test_10; + do begin + continue; + end + while (0); + return 1'b1; + endfunction + + function bit test_11; + int incr = 0; + do begin + do + incr++; + while (0); + incr++; + continue; + incr++; + end + while (incr < 11); + return incr == 12; + endfunction + + function bit test_12; + int incr = 0; + do begin + do begin + incr++; + continue; + incr++; + end + while (0); + incr++; + continue; + incr++; + end + while (incr < 11); + return incr == 12; + endfunction + + always @(posedge clk) begin + bit [11:0] results = {test_1(), test_2(), test_3(), test_4(), test_5(), + test_6(), test_7(), test_8(), test_9(), test_10(), + test_11(), test_12()}; + + if (results == '1) begin + $write("*-* All Finished *-*\n"); + $finish; + end + else begin + $write("Results: %b\n", results); + $stop; + end + end +endmodule