Support assignment expressions.

This commit is contained in:
Wilson Snyder 2022-11-19 15:23:37 -05:00
parent 21d80cdfa1
commit aacb38b776
15 changed files with 201 additions and 52 deletions

View File

@ -18,6 +18,7 @@ Verilator 5.003 devel
* Support randcase.
* Support pre_randomize and post_randomize.
* Support $timeunit and $timeprecision.
* Support assignment expressions.
* Add ENUMVALUE warning when value misused for enum (#726).
* Internal AST improvements, also affect XML format (#3721). [Geza Lore]
* Change ENDLABEL from warning into an error.

View File

@ -237,6 +237,7 @@ private:
// METHODS
void iterateNewStmt(AstNode* nodep) {
if (m_scopep) {
VL_RESTORER(m_logicVertexp);
UINFO(4, " STMT " << nodep << endl);
m_logicVertexp = new CdcLogicVertex(&m_graph, m_scopep, nodep, m_domainp);
if (m_domainp && m_domainp->hasClocked()) { // To/from a flop
@ -247,7 +248,6 @@ private:
m_logicVertexp->dstDomainSet(true);
}
iterateChildren(nodep);
m_logicVertexp = nullptr;
if (false && debug() >= 9) {
UINFO(9, "Trace Logic:\n");
@ -680,6 +680,7 @@ private:
}
}
void visit(AstAssignDly* nodep) override {
VL_RESTORER(m_inDly);
m_inDly = true;
iterateChildren(nodep);
m_inDly = false;

View File

@ -505,6 +505,8 @@ private:
nodep->deleteTree();
}
void visit(AstAssignDly* nodep) override {
VL_RESTORER(m_inDly);
VL_RESTORER(m_nextDlyp);
m_inDly = true;
m_nextDlyp
= VN_CAST(nodep->nextp(), AssignDly); // Next assignment in same block, maybe nullptr.
@ -537,8 +539,6 @@ private:
} else {
iterateChildren(nodep);
}
m_inDly = false;
m_nextDlyp = nullptr;
}
void visit(AstVarRef* nodep) override {

View File

@ -190,18 +190,18 @@ class DataflowExtractVisitor final : public VNVisitor {
}
void visit(AstAssignForce* nodep) override {
VL_RESTORER(m_inForceReleaseLhs);
iterate(nodep->rhsp());
UASSERT_OBJ(!m_inForceReleaseLhs, nodep, "Should not nest");
m_inForceReleaseLhs = true;
iterate(nodep->lhsp());
m_inForceReleaseLhs = false;
}
void visit(AstRelease* nodep) override {
VL_RESTORER(m_inForceReleaseLhs);
UASSERT_OBJ(!m_inForceReleaseLhs, nodep, "Should not nest");
m_inForceReleaseLhs = true;
iterate(nodep->lhsp());
m_inForceReleaseLhs = false;
}
void visit(AstNodeExpr* nodep) override { iterateChildrenConst(nodep); }

View File

@ -828,12 +828,13 @@ private:
void visit(AstNodeStmt* nodep) override {
if (nodep->user1SetOnce()) return; // Process once
VL_RESTORER(m_stmtp);
m_stmtp = nodep;
iterateChildren(nodep);
m_stmtp = nullptr;
}
void visit(AstNodeAssign* nodep) override {
if (nodep->user1SetOnce()) return; // Process once
VL_RESTORER(m_stmtp);
m_stmtp = nodep;
iterateChildren(nodep);
bool did = false;
@ -869,7 +870,6 @@ private:
}
// Cleanup common code
if (did) VL_DO_DANGLING(nodep->unlinkFrBack()->deleteTree(), nodep);
m_stmtp = nullptr;
}
//--------------------

View File

@ -396,15 +396,15 @@ class OrderBuildVisitor final : public VNVisitor {
void visit(AstAssignW* nodep) override { iterateLogic(nodep); }
void visit(AstAssignPre* nodep) override {
UASSERT_OBJ(!m_inPre, nodep, "Should not nest");
VL_RESTORER(m_inPre);
m_inPre = true;
iterateLogic(nodep);
m_inPre = false;
}
void visit(AstAssignPost* nodep) override {
UASSERT_OBJ(!m_inPost, nodep, "Should not nest");
VL_RESTORER(m_inPost);
m_inPost = true;
iterateLogic(nodep);
m_inPost = false;
}
//--- Verilator concoctions

View File

@ -759,6 +759,9 @@ private:
if (jumpingOver(nodep)) return;
if (!optimizable()) return; // Accelerate
checkNodeInfo(nodep);
VL_RESTORER(m_inDlyAssign);
if (VN_IS(nodep, AssignForce)) {
clearOptimizable(nodep, "Force");
} else if (VN_IS(nodep, AssignDly)) {
@ -793,7 +796,6 @@ private:
assignOutValue(nodep, vscp, fetchValue(nodep->rhsp()));
}
}
m_inDlyAssign = false;
}
void visit(AstArraySel* nodep) override {
checkNodeInfo(nodep);

View File

@ -156,9 +156,9 @@ class SliceVisitor final : public VNVisitor {
// This will potentially call this function again to resolve next level of slicing
return;
}
VL_RESTORER(m_assignp);
m_assignp = nodep;
iterateChildren(nodep);
m_assignp = nullptr;
}
}

View File

@ -340,10 +340,10 @@ protected:
// of what is before vs. after
void visit(AstAssignDly* nodep) override {
VL_RESTORER(m_inDly);
m_inDly = true;
UINFO(4, " ASSIGNDLY " << nodep << endl);
iterateChildren(nodep);
m_inDly = false;
}
void visit(AstVarRef* nodep) override {
if (!m_stmtStackps.empty()) {

View File

@ -252,6 +252,7 @@ private:
// VISITORS
void visit(AstNodeAssign* nodep) override {
VL_RESTORER(m_ops);
m_ops = 0;
m_assignStep++;
iterateAndNextNull(nodep->rhsp());
@ -360,7 +361,7 @@ private:
doDeletes();
}
void visit(AstNode* nodep) override {
m_ops++;
++m_ops;
if (!nodep->isSubstOptimizable()) m_ops = SUBST_MAX_OPS_NA;
iterateChildren(nodep);
}

View File

@ -189,9 +189,9 @@ private:
iterateChildren(nodep);
}
void visit(AstAssignW* nodep) override {
VL_RESTORER(m_assignwp);
m_assignwp = nodep;
VL_DO_DANGLING(iterateChildren(nodep), nodep); // May delete nodep.
m_assignwp = nullptr;
}
void visit(AstNodeFTaskRef* nodep) override {
// Includes handling AstMethodCall, AstNew

View File

@ -1229,6 +1229,8 @@ class TristateVisitor final : public TristateBaseVisitor {
void visit(AstOr* nodep) override { visitAndOr(nodep, false); }
void visitAssign(AstNodeAssign* nodep) {
VL_RESTORER(m_alhs);
VL_RESTORER(m_currentStrength);
if (m_graphing) {
if (AstAssignW* assignWp = VN_CAST(nodep, AssignW)) addToAssignmentList(assignWp);
@ -1279,9 +1281,6 @@ class TristateVisitor final : public TristateBaseVisitor {
}
}
iterateAndNextNull(nodep->lhsp());
// back to default strength
m_currentStrength = VStrength::STRONG;
m_alhs = false;
}
}
void visit(AstAssignW* nodep) override { visitAssign(nodep); }

View File

@ -2691,18 +2691,30 @@ genvar_initialization<nodep>: // ==IEEE: genvar_initialization
;
genvar_iteration<nodep>: // ==IEEE: genvar_iteration
varRefBase '=' expr { $$ = new AstAssign($2,$1,$3); }
| varRefBase yP_PLUSEQ expr { $$ = new AstAssign($2,$1,new AstAdd ($2,$1->cloneTree(true),$3)); }
| varRefBase yP_MINUSEQ expr { $$ = new AstAssign($2,$1,new AstSub ($2,$1->cloneTree(true),$3)); }
| varRefBase yP_TIMESEQ expr { $$ = new AstAssign($2,$1,new AstMul ($2,$1->cloneTree(true),$3)); }
| varRefBase yP_DIVEQ expr { $$ = new AstAssign($2,$1,new AstDiv ($2,$1->cloneTree(true),$3)); }
| varRefBase yP_MODEQ expr { $$ = new AstAssign($2,$1,new AstModDiv ($2,$1->cloneTree(true),$3)); }
| varRefBase yP_ANDEQ expr { $$ = new AstAssign($2,$1,new AstAnd ($2,$1->cloneTree(true),$3)); }
| varRefBase yP_OREQ expr { $$ = new AstAssign($2,$1,new AstOr ($2,$1->cloneTree(true),$3)); }
| varRefBase yP_XOREQ expr { $$ = new AstAssign($2,$1,new AstXor ($2,$1->cloneTree(true),$3)); }
| varRefBase yP_SLEFTEQ expr { $$ = new AstAssign($2,$1,new AstShiftL ($2,$1->cloneTree(true),$3)); }
| varRefBase yP_SRIGHTEQ expr { $$ = new AstAssign($2,$1,new AstShiftR ($2,$1->cloneTree(true),$3)); }
| varRefBase yP_SSRIGHTEQ expr { $$ = new AstAssign($2,$1,new AstShiftRS($2,$1->cloneTree(true),$3)); }
varRefBase '=' expr
{ $$ = new AstAssign{$2, $1, $3}; }
| varRefBase yP_PLUSEQ expr
{ $$ = new AstAssign{$2, $1, new AstAdd{$2, $1->cloneTree(true), $3}}; }
| varRefBase yP_MINUSEQ expr
{ $$ = new AstAssign{$2, $1, new AstSub{$2, $1->cloneTree(true), $3}}; }
| varRefBase yP_TIMESEQ expr
{ $$ = new AstAssign{$2, $1, new AstMul{$2, $1->cloneTree(true), $3}}; }
| varRefBase yP_DIVEQ expr
{ $$ = new AstAssign{$2, $1, new AstDiv{$2, $1->cloneTree(true), $3}}; }
| varRefBase yP_MODEQ expr
{ $$ = new AstAssign{$2, $1, new AstModDiv{$2, $1->cloneTree(true), $3}}; }
| varRefBase yP_ANDEQ expr
{ $$ = new AstAssign{$2, $1, new AstAnd{$2, $1->cloneTree(true), $3}}; }
| varRefBase yP_OREQ expr
{ $$ = new AstAssign{$2, $1, new AstOr{$2, $1->cloneTree(true), $3}}; }
| varRefBase yP_XOREQ expr
{ $$ = new AstAssign{$2, $1, new AstXor{$2, $1->cloneTree(true), $3}}; }
| varRefBase yP_SLEFTEQ expr
{ $$ = new AstAssign{$2, $1, new AstShiftL{$2, $1->cloneTree(true), $3}}; }
| varRefBase yP_SRIGHTEQ expr
{ $$ = new AstAssign{$2, $1, new AstShiftR{$2, $1->cloneTree(true), $3}}; }
| varRefBase yP_SSRIGHTEQ expr
{ $$ = new AstAssign{$2, $1, new AstShiftRS{$2, $1->cloneTree(true), $3}}; }
// // inc_or_dec_operator
// When support ++ as a real AST type, maybe AstWhile::precondsp() becomes generic AstNodeExprStmt?
| yP_PLUSPLUS varRefBase
@ -3425,17 +3437,28 @@ foperator_assignment<nodep>: // IEEE: operator_assignment (for first part of
| fexprLvalue '=' yD_FOPEN '(' expr ',' expr ')' { $$ = new AstFOpen($3,$1,$5,$7); }
//
//UNSUP ~f~exprLvalue yP_PLUS(etc) expr { UNSUP }
| fexprLvalue yP_PLUSEQ expr { $$ = new AstAssign($2,$1,new AstAdd ($2,$1->cloneTree(true),$3)); }
| fexprLvalue yP_MINUSEQ expr { $$ = new AstAssign($2,$1,new AstSub ($2,$1->cloneTree(true),$3)); }
| fexprLvalue yP_TIMESEQ expr { $$ = new AstAssign($2,$1,new AstMul ($2,$1->cloneTree(true),$3)); }
| fexprLvalue yP_DIVEQ expr { $$ = new AstAssign($2,$1,new AstDiv ($2,$1->cloneTree(true),$3)); }
| fexprLvalue yP_MODEQ expr { $$ = new AstAssign($2,$1,new AstModDiv ($2,$1->cloneTree(true),$3)); }
| fexprLvalue yP_ANDEQ expr { $$ = new AstAssign($2,$1,new AstAnd ($2,$1->cloneTree(true),$3)); }
| fexprLvalue yP_OREQ expr { $$ = new AstAssign($2,$1,new AstOr ($2,$1->cloneTree(true),$3)); }
| fexprLvalue yP_XOREQ expr { $$ = new AstAssign($2,$1,new AstXor ($2,$1->cloneTree(true),$3)); }
| fexprLvalue yP_SLEFTEQ expr { $$ = new AstAssign($2,$1,new AstShiftL ($2,$1->cloneTree(true),$3)); }
| fexprLvalue yP_SRIGHTEQ expr { $$ = new AstAssign($2,$1,new AstShiftR ($2,$1->cloneTree(true),$3)); }
| fexprLvalue yP_SSRIGHTEQ expr { $$ = new AstAssign($2,$1,new AstShiftRS($2,$1->cloneTree(true),$3)); }
| fexprLvalue yP_PLUSEQ expr
{ $$ = new AstAssign{$2, $1, new AstAdd{$2, $1->cloneTree(true), $3}}; }
| fexprLvalue yP_MINUSEQ expr
{ $$ = new AstAssign{$2, $1, new AstSub{$2, $1->cloneTree(true), $3}}; }
| fexprLvalue yP_TIMESEQ expr
{ $$ = new AstAssign{$2, $1, new AstMul{$2, $1->cloneTree(true), $3}}; }
| fexprLvalue yP_DIVEQ expr
{ $$ = new AstAssign{$2, $1, new AstDiv{$2, $1->cloneTree(true), $3}}; }
| fexprLvalue yP_MODEQ expr
{ $$ = new AstAssign{$2, $1, new AstModDiv{$2, $1->cloneTree(true), $3}}; }
| fexprLvalue yP_ANDEQ expr
{ $$ = new AstAssign{$2, $1, new AstAnd{$2, $1->cloneTree(true), $3}}; }
| fexprLvalue yP_OREQ expr
{ $$ = new AstAssign{$2, $1, new AstOr{$2, $1->cloneTree(true), $3}}; }
| fexprLvalue yP_XOREQ expr
{ $$ = new AstAssign{$2, $1, new AstXor{$2, $1->cloneTree(true), $3}}; }
| fexprLvalue yP_SLEFTEQ expr
{ $$ = new AstAssign{$2, $1, new AstShiftL{$2, $1->cloneTree(true), $3}}; }
| fexprLvalue yP_SRIGHTEQ expr
{ $$ = new AstAssign{$2, $1, new AstShiftR{$2, $1->cloneTree(true), $3}}; }
| fexprLvalue yP_SSRIGHTEQ expr
{ $$ = new AstAssign{$2, $1, new AstShiftRS{$2, $1->cloneTree(true), $3}}; }
//UNSUP replace above with:
//UNSUP BISONPRE_COPY(operator_assignment,{s/~f~/f/g}) // {copied}
;
@ -4403,18 +4426,42 @@ expr<nodeExprp>: // IEEE: part of expression/constant_expression/
//
// // IEEE: '(' operator_assignment ')'
// // Need exprScope of variable_lvalue to prevent conflict
//UNSUP '(' ~p~exprScope '=' expr ')' { UNSUP }
//UNSUP '(' ~p~exprScope yP_PLUSEQ expr ')' { UNSUP }
//UNSUP '(' ~p~exprScope yP_MINUSEQ expr ')' { UNSUP }
//UNSUP '(' ~p~exprScope yP_TIMESEQ expr ')' { UNSUP }
//UNSUP '(' ~p~exprScope yP_DIVEQ expr ')' { UNSUP }
//UNSUP '(' ~p~exprScope yP_MODEQ expr ')' { UNSUP }
//UNSUP '(' ~p~exprScope yP_ANDEQ expr ')' { UNSUP }
//UNSUP '(' ~p~exprScope yP_OREQ expr ')' { UNSUP }
//UNSUP '(' ~p~exprScope yP_XOREQ expr ')' { UNSUP }
//UNSUP '(' ~p~exprScope yP_SLEFTEQ expr ')' { UNSUP }
//UNSUP '(' ~p~exprScope yP_SRIGHTEQ expr ')' { UNSUP }
//UNSUP '(' ~p~exprScope yP_SSRIGHTEQ expr ')' { UNSUP }
| '(' ~p~exprScope '=' expr ')'
{ $$ = new AstExprStmt{$1, new AstAssign{$3, $2, $4},
$2->cloneTree(true)}; }
| '(' ~p~exprScope yP_PLUSEQ expr ')'
{ $$ = new AstExprStmt{$1, new AstAssign{$3, $2, new AstAdd{$3, $2->cloneTree(true), $4}},
$2->cloneTree(true)}; }
| '(' ~p~exprScope yP_MINUSEQ expr ')'
{ $$ = new AstExprStmt{$1, new AstAssign{$3, $2, new AstSub{$3, $2->cloneTree(true), $4}},
$2->cloneTree(true)}; }
| '(' ~p~exprScope yP_TIMESEQ expr ')'
{ $$ = new AstExprStmt{$1, new AstAssign{$3, $2, new AstMul{$3, $2->cloneTree(true), $4}},
$2->cloneTree(true)}; }
| '(' ~p~exprScope yP_DIVEQ expr ')'
{ $$ = new AstExprStmt{$1, new AstAssign{$3, $2, new AstDiv{$3, $2->cloneTree(true), $4}},
$2->cloneTree(true)}; }
| '(' ~p~exprScope yP_MODEQ expr ')'
{ $$ = new AstExprStmt{$1, new AstAssign{$3, $2, new AstModDiv{$3, $2->cloneTree(true), $4}},
$2->cloneTree(true)}; }
| '(' ~p~exprScope yP_ANDEQ expr ')'
{ $$ = new AstExprStmt{$1, new AstAssign{$3, $2, new AstAnd{$3, $2->cloneTree(true), $4}},
$2->cloneTree(true)}; }
| '(' ~p~exprScope yP_OREQ expr ')'
{ $$ = new AstExprStmt{$1, new AstAssign{$3, $2, new AstOr{$3, $2->cloneTree(true), $4}},
$2->cloneTree(true)}; }
| '(' ~p~exprScope yP_XOREQ expr ')'
{ $$ = new AstExprStmt{$1, new AstAssign{$3, $2, new AstXor{$3, $2->cloneTree(true), $4}},
$2->cloneTree(true)}; }
| '(' ~p~exprScope yP_SLEFTEQ expr ')'
{ $$ = new AstExprStmt{$1, new AstAssign{$3, $2, new AstShiftL{$3, $2->cloneTree(true), $4}},
$2->cloneTree(true)}; }
| '(' ~p~exprScope yP_SRIGHTEQ expr ')'
{ $$ = new AstExprStmt{$1, new AstAssign{$3, $2, new AstShiftR{$3, $2->cloneTree(true), $4}},
$2->cloneTree(true)}; }
| '(' ~p~exprScope yP_SSRIGHTEQ expr ')'
{ $$ = new AstExprStmt{$1, new AstAssign{$3, $2, new AstShiftRS{$3, $2->cloneTree(true), $4}},
$2->cloneTree(true)}; }
//
// // IEEE: expression binary_operator expression
| ~l~expr '+' ~r~expr { $$ = new AstAdd ($2,$1,$3); }

21
test_regress/t/t_assign_expr.pl Executable file
View File

@ -0,0 +1,21 @@
#!/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 Wilson Snyder. 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(
);
execute(
check_finished => 1,
);
ok(1);
1;

View File

@ -0,0 +1,77 @@
// DESCRIPTION: Verilator: Verilog Test module
//
// This file ONLY is placed under the Creative Commons Public Domain, for
// any use, without warranty, 2022 by Wilson Snyder.
// SPDX-License-Identifier: CC0-1.0
`define checkd(gotv,expv) do if ((gotv) !== (expv)) begin $write("%%Error: %s:%0d: got=%0d exp=%0d\n", `__FILE__,`__LINE__, (gotv), (expv)); $stop; end while(0);
module t(/*AUTOARG*/);
int a;
int b;
int i;
initial begin
a = 10;
i = (a = 2);
`checkd(a, 2); `checkd(i, 2);
a = 10;
i = (a += 2);
`checkd(a, 12); `checkd(i, 12);
a = 10;
i = (a -= 2);
`checkd(a, 8); `checkd(i, 8);
a = 10;
i = (a *= 2);
`checkd(a, 20); `checkd(i, 20);
a = 10;
i = (a /= 2);
`checkd(a, 5); `checkd(i, 5);
a = 11;
i = (a %= 2);
`checkd(a, 1); `checkd(i, 1);
a = 10;
i = (a &= 2);
`checkd(a, 2); `checkd(i, 2);
a = 8;
i = (a |= 2);
`checkd(a, 10); `checkd(i, 10);
a = 10;
i = (a ^= 2);
`checkd(a, 8); `checkd(i, 8);
a = 10;
i = (a <<= 2);
`checkd(a, 40); `checkd(i, 40);
a = 10;
i = (a >>= 2);
`checkd(a, 2); `checkd(i, 2);
a = 10;
i = (a >>>= 2);
`checkd(a, 2); `checkd(i, 2);
a = 10;
i = (a = (b = 5));
`checkd(a, 5); `checkd(i, 5); `checkd(b, 5);
a = 10;
b = 6;
i = ((a += (b += 1) + 1));
`checkd(a, 18); `checkd(i, 18); `checkd(b, 7);
$write("*-* All Finished *-*\n");
$finish;
end
endmodule