diff --git a/src/V3AstNodeExpr.h b/src/V3AstNodeExpr.h index cadf990ad..a1d388f45 100644 --- a/src/V3AstNodeExpr.h +++ b/src/V3AstNodeExpr.h @@ -5126,102 +5126,6 @@ public: bool sizeMattersThs() const override { return false; } int instrCount() const override { return INSTR_COUNT_BRANCH; } }; -class AstPostAdd final : public AstNodeTriop { - // Post-increment/add - // Children: lhsp: AstConst (1) as currently support only ++ not += - // Children: rhsp: tree with AstVarRef that is value to read before operation - // Children: thsp: tree with AstVarRef LValue that is stored after operation -public: - AstPostAdd(FileLine* fl, AstNodeExpr* lhsp, AstNodeExpr* rhsp, AstNodeExpr* thsp) - : ASTGEN_SUPER_PostAdd(fl, lhsp, rhsp, thsp) {} - ASTGEN_MEMBERS_AstPostAdd; - void numberOperate(V3Number& out, const V3Number& lhs, const V3Number& rhs, - const V3Number& ths) override { - V3ERROR_NA; // Need to modify lhs - } - string emitVerilog() override { return "%k(%r++)"; } - string emitC() override { V3ERROR_NA_RETURN(""); } - string emitSimpleOperator() override { V3ERROR_NA_RETURN(""); } - bool cleanOut() const override { V3ERROR_NA_RETURN(false); } - bool cleanLhs() const override { V3ERROR_NA_RETURN(false); } - bool cleanRhs() const override { V3ERROR_NA_RETURN(false); } - bool cleanThs() const override { V3ERROR_NA_RETURN(false); } - bool sizeMattersLhs() const override { V3ERROR_NA_RETURN(true); } - bool sizeMattersRhs() const override { V3ERROR_NA_RETURN(true); } - bool sizeMattersThs() const override { V3ERROR_NA_RETURN(true); } -}; -class AstPostSub final : public AstNodeTriop { - // Post-decrement/subtract - // Children: lhsp: AstConst (1) as currently support only -- not -= - // Children: rhsp: tree with AstVarRef that is value to read before operation - // Children: thsp: tree with AstVarRef LValue that is stored after operation -public: - AstPostSub(FileLine* fl, AstNodeExpr* lhsp, AstNodeExpr* rhsp, AstNodeExpr* thsp) - : ASTGEN_SUPER_PostSub(fl, lhsp, rhsp, thsp) {} - ASTGEN_MEMBERS_AstPostSub; - void numberOperate(V3Number& out, const V3Number& lhs, const V3Number& rhs, - const V3Number& ths) override { - V3ERROR_NA; // Need to modify lhs - } - string emitVerilog() override { return "%k(%r--)"; } - string emitC() override { V3ERROR_NA_RETURN(""); } - string emitSimpleOperator() override { V3ERROR_NA_RETURN(""); } - bool cleanOut() const override { V3ERROR_NA_RETURN(false); } - bool cleanLhs() const override { V3ERROR_NA_RETURN(false); } - bool cleanRhs() const override { V3ERROR_NA_RETURN(false); } - bool cleanThs() const override { V3ERROR_NA_RETURN(false); } - bool sizeMattersLhs() const override { V3ERROR_NA_RETURN(true); } - bool sizeMattersRhs() const override { V3ERROR_NA_RETURN(true); } - bool sizeMattersThs() const override { V3ERROR_NA_RETURN(true); } -}; -class AstPreAdd final : public AstNodeTriop { - // Pre-increment/add - // Children: lhsp: AstConst (1) as currently support only ++ not += - // Children: rhsp: tree with AstVarRef that is value to read before operation - // Children: thsp: tree with AstVarRef LValue that is stored after operation -public: - AstPreAdd(FileLine* fl, AstNodeExpr* lhsp, AstNodeExpr* rhsp, AstNodeExpr* thsp) - : ASTGEN_SUPER_PreAdd(fl, lhsp, rhsp, thsp) {} - ASTGEN_MEMBERS_AstPreAdd; - void numberOperate(V3Number& out, const V3Number& lhs, const V3Number& rhs, - const V3Number& ths) override { - V3ERROR_NA; // Need to modify lhs - } - string emitVerilog() override { return "%k(++%r)"; } - string emitC() override { V3ERROR_NA_RETURN(""); } - string emitSimpleOperator() override { V3ERROR_NA_RETURN(""); } - bool cleanOut() const override { V3ERROR_NA_RETURN(false); } - bool cleanLhs() const override { V3ERROR_NA_RETURN(false); } - bool cleanRhs() const override { V3ERROR_NA_RETURN(false); } - bool cleanThs() const override { V3ERROR_NA_RETURN(false); } - bool sizeMattersLhs() const override { V3ERROR_NA_RETURN(true); } - bool sizeMattersRhs() const override { V3ERROR_NA_RETURN(true); } - bool sizeMattersThs() const override { V3ERROR_NA_RETURN(true); } -}; -class AstPreSub final : public AstNodeTriop { - // Pre-decrement/subtract - // Children: lhsp: AstConst (1) as currently support only -- not -= - // Children: rhsp: tree with AstVarRef that is value to read before operation - // Children: thsp: tree with AstVarRef LValue that is stored after operation -public: - AstPreSub(FileLine* fl, AstNodeExpr* lhsp, AstNodeExpr* rhsp, AstNodeExpr* thsp) - : ASTGEN_SUPER_PreSub(fl, lhsp, rhsp, thsp) {} - ASTGEN_MEMBERS_AstPreSub; - void numberOperate(V3Number& out, const V3Number& lhs, const V3Number& rhs, - const V3Number& ths) override { - V3ERROR_NA; // Need to modify lhs - } - string emitVerilog() override { return "%k(--%r)"; } - string emitC() override { V3ERROR_NA_RETURN(""); } - string emitSimpleOperator() override { V3ERROR_NA_RETURN(""); } - bool cleanOut() const override { V3ERROR_NA_RETURN(false); } - bool cleanLhs() const override { V3ERROR_NA_RETURN(false); } - bool cleanRhs() const override { V3ERROR_NA_RETURN(false); } - bool cleanThs() const override { V3ERROR_NA_RETURN(false); } - bool sizeMattersLhs() const override { V3ERROR_NA_RETURN(true); } - bool sizeMattersRhs() const override { V3ERROR_NA_RETURN(true); } - bool sizeMattersThs() const override { V3ERROR_NA_RETURN(true); } -}; class AstPutcN final : public AstNodeTriop { // Verilog string.putc() public: @@ -5793,6 +5697,74 @@ public: int instrCount() const override { return widthInstrs() * 3; } bool isSystemFunc() const override { return true; } }; +class AstPostDec final : public AstNodeUniop { + // Post-decrement/subtract + // Children: lhsp: tree with AstVarRef to be decremented +public: + AstPostDec(FileLine* fl, AstNodeExpr* lhsp) + : ASTGEN_SUPER_PostDec(fl, lhsp) {} + ASTGEN_MEMBERS_AstPostDec; + void numberOperate(V3Number& out, const V3Number& lhs) override { + V3ERROR_NA; // Need to modify lhs + } + string emitVerilog() override { return "%k(%l--)"; } + string emitC() override { V3ERROR_NA_RETURN(""); } + string emitSimpleOperator() override { V3ERROR_NA_RETURN(""); } + bool cleanOut() const override { V3ERROR_NA_RETURN(false); } + bool cleanLhs() const override { V3ERROR_NA_RETURN(false); } + bool sizeMattersLhs() const override { V3ERROR_NA_RETURN(true); } +}; +class AstPostInc final : public AstNodeUniop { + // Post-increment/add + // Children: lhsp: tree with AstVarRef to be incremented +public: + AstPostInc(FileLine* fl, AstNodeExpr* lhsp) + : ASTGEN_SUPER_PostInc(fl, lhsp) {} + ASTGEN_MEMBERS_AstPostInc; + void numberOperate(V3Number& out, const V3Number& lhs) override { + V3ERROR_NA; // Need to modify lhs + } + string emitVerilog() override { return "%k(%l++)"; } + string emitC() override { V3ERROR_NA_RETURN(""); } + string emitSimpleOperator() override { V3ERROR_NA_RETURN(""); } + bool cleanOut() const override { V3ERROR_NA_RETURN(false); } + bool cleanLhs() const override { V3ERROR_NA_RETURN(false); } + bool sizeMattersLhs() const override { V3ERROR_NA_RETURN(true); } +}; +class AstPreDec final : public AstNodeUniop { + // Pre-decrement/subtract + // Children: lhsp: tree with AstVarRef to be decremented +public: + AstPreDec(FileLine* fl, AstNodeExpr* lhsp) + : ASTGEN_SUPER_PreDec(fl, lhsp) {} + ASTGEN_MEMBERS_AstPreDec; + void numberOperate(V3Number& out, const V3Number& lhs) override { + V3ERROR_NA; // Need to modify lhs + } + string emitVerilog() override { return "%k(--%l)"; } + string emitC() override { V3ERROR_NA_RETURN(""); } + string emitSimpleOperator() override { V3ERROR_NA_RETURN(""); } + bool cleanOut() const override { V3ERROR_NA_RETURN(false); } + bool cleanLhs() const override { V3ERROR_NA_RETURN(false); } + bool sizeMattersLhs() const override { V3ERROR_NA_RETURN(true); } +}; +class AstPreInc final : public AstNodeUniop { + // Pre-increment/add + // Children: lhsp: tree with AstVarRef to be incremented +public: + AstPreInc(FileLine* fl, AstNodeExpr* lhsp) + : ASTGEN_SUPER_PreInc(fl, lhsp) {} + ASTGEN_MEMBERS_AstPreInc; + void numberOperate(V3Number& out, const V3Number& lhs) override { + V3ERROR_NA; // Need to modify lhs + } + string emitVerilog() override { return "%k(++%l)"; } + string emitC() override { V3ERROR_NA_RETURN(""); } + string emitSimpleOperator() override { V3ERROR_NA_RETURN(""); } + bool cleanOut() const override { V3ERROR_NA_RETURN(false); } + bool cleanLhs() const override { V3ERROR_NA_RETURN(false); } + bool sizeMattersLhs() const override { V3ERROR_NA_RETURN(true); } +}; class AstRToIRoundS final : public AstNodeUniop { // Convert real to integer, with arbitrary sized output (not just "integer" format) public: diff --git a/src/V3AstNodeStmt.h b/src/V3AstNodeStmt.h index b81cebb3c..89b46011e 100644 --- a/src/V3AstNodeStmt.h +++ b/src/V3AstNodeStmt.h @@ -1427,6 +1427,40 @@ public: return new AstAssign{fileline(), lhsp, rhsp, controlp}; } }; +class AstAssignCompound final : public AstNodeAssign { + // Compound assignments (+=, -=, *=, ...) +public: + enum operation : uint8_t { + Add, + And, + Div, + ModDiv, + Mul, + Or, + ShiftL, + ShiftR, + ShiftRS, + Sub, + Xor, + }; + +private: + enum operation m_operation; + +public: + AstAssignCompound(AstAssignCompound::operation operation, FileLine* fl, AstNodeExpr* lhsp, + AstNodeExpr* rhsp, AstNode* timingControlp = nullptr) + : ASTGEN_SUPER_AssignCompound(fl, lhsp, rhsp, timingControlp) { + this->m_operation = operation; + dtypeFrom(lhsp); + } + ASTGEN_MEMBERS_AstAssignCompound; + AstNodeAssign* cloneType(AstNodeExpr* lhsp, AstNodeExpr* rhsp) override { + AstNode* const controlp = timingControlp() ? timingControlp()->cloneTree(false) : nullptr; + return new AstAssignCompound{operation(), fileline(), lhsp, rhsp, controlp}; + } + operation operation() { return m_operation; } +}; class AstAssignCont final : public AstNodeAssign { // Continuous procedural 'assign'. See AstAssignW for non-procedural version. public: diff --git a/src/V3LinkInc.cpp b/src/V3LinkInc.cpp index a770b7017..2154f8575 100644 --- a/src/V3LinkInc.cpp +++ b/src/V3LinkInc.cpp @@ -16,20 +16,20 @@ // V3LinkInc's Transformations: // // prepost_expr_visit -// PREADD/PRESUB +// PREINC/PREDEC // 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 +// POSTINC/POSTDEC // 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_stmt_visit -// PREADD/PRESUB/POSTADD/POSTSUB +// PREINC/PREDEC/POSTINC/POSTDEC // 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 @@ -47,6 +47,8 @@ #include "V3LinkInc.h" +#include "V3LinkLValue.h" + VL_DEFINE_DEBUG_FUNCTIONS; //###################################################################### @@ -55,7 +57,7 @@ class LinkIncVisitor final : public VNVisitor { // STATE AstNodeFTask* m_ftaskp = nullptr; // Function or task we're inside AstNodeModule* m_modp = nullptr; // Module we're inside - int m_modIncrementsNum = 0; // Var name counter + int m_modCompoundAssignmentsNum = 0; // Var name counter AstNode* m_insStmtp = nullptr; // Where to insert statement bool m_unsupportedHere = false; // Used to detect where it's not supported yet @@ -87,9 +89,9 @@ class LinkIncVisitor final : public VNVisitor { void visit(AstNodeModule* nodep) override { if (nodep->dead()) return; VL_RESTORER(m_modp); - VL_RESTORER(m_modIncrementsNum); + VL_RESTORER(m_modCompoundAssignmentsNum); m_modp = nodep; - m_modIncrementsNum = 0; + m_modCompoundAssignmentsNum = 0; iterateChildren(nodep); } void visit(AstNodeFTask* nodep) override { @@ -175,8 +177,8 @@ class LinkIncVisitor final : public VNVisitor { } void visit(AstStmtExpr* nodep) override { AstNodeExpr* const exprp = nodep->exprp(); - if (VN_IS(exprp, PostAdd) || VN_IS(exprp, PostSub) || VN_IS(exprp, PreAdd) - || VN_IS(exprp, PreSub)) { + if (VN_IS(exprp, PostInc) || VN_IS(exprp, PostDec) || VN_IS(exprp, PreInc) + || VN_IS(exprp, PreDec)) { // Repalce this StmtExpr with the expression, visiting it will turn it into a NodeStmt nodep->replaceWith(exprp->unlinkFrBack()); VL_DO_DANGLING(pushDeletep(nodep), nodep); @@ -204,15 +206,13 @@ class LinkIncVisitor final : public VNVisitor { void visit(AstLogIf* nodep) override { unsupported_visit(nodep); } void visit(AstCond* nodep) override { unsupported_visit(nodep); } void visit(AstPropSpec* nodep) override { unsupported_visit(nodep); } - void prepost_visit(AstNodeTriop* nodep) { + void prepost_visit(AstNodeUniop* const nodep) { // Check if we are underneath a statement - AstSelBit* const selbitp = VN_CAST(nodep->thsp(), SelBit); + AstSelBit* const selbitp = VN_CAST(nodep->lhsp(), SelBit); if (!m_insStmtp && selbitp && VN_IS(selbitp->fromp(), NodeVarRef) && !selbitp->bitp()->isPure()) { prepost_stmt_sel_visit(nodep); } else { - // Purity check was deferred at creation in verilog.y, check now - nodep->thsp()->purityCheck(); if (!m_insStmtp) { prepost_stmt_visit(nodep); } else { @@ -220,23 +220,72 @@ class LinkIncVisitor final : public VNVisitor { } } } - void prepost_stmt_sel_visit(AstNodeTriop* nodep) { + AstNodeExpr* getOperationp(AstNode* const nodep, AstNodeExpr* const lhsp, + AstNodeExpr* const rhsp) { + if (VN_IS(nodep, PreDec) || VN_IS(nodep, PostDec)) { + return new AstSub{nodep->fileline(), lhsp, rhsp}; + } + if (VN_IS(nodep, PreInc) || VN_IS(nodep, PostInc)) { + return new AstAdd{nodep->fileline(), lhsp, rhsp}; + } + if (AstAssignCompound* assignp = VN_CAST(nodep, AssignCompound)) { + switch (assignp->operation()) { + case AstAssignCompound::operation::Add: + return new AstAdd{nodep->fileline(), lhsp, rhsp}; + case AstAssignCompound::operation::And: + return new AstAnd{nodep->fileline(), lhsp, rhsp}; + case AstAssignCompound::operation::Div: + return new AstDiv{nodep->fileline(), lhsp, rhsp}; + case AstAssignCompound::operation::ModDiv: + return new AstModDiv{nodep->fileline(), lhsp, rhsp}; + case AstAssignCompound::operation::Mul: + return new AstMul{nodep->fileline(), lhsp, rhsp}; + case AstAssignCompound::operation::Or: return new AstOr{nodep->fileline(), lhsp, rhsp}; + case AstAssignCompound::operation::ShiftL: + return new AstShiftL{nodep->fileline(), lhsp, rhsp}; + case AstAssignCompound::operation::ShiftR: + return new AstShiftR{nodep->fileline(), lhsp, rhsp}; + case AstAssignCompound::operation::ShiftRS: + return new AstShiftRS{nodep->fileline(), lhsp, rhsp}; + case AstAssignCompound::operation::Sub: + return new AstSub{nodep->fileline(), lhsp, rhsp}; + case AstAssignCompound::operation::Xor: + return new AstXor{nodep->fileline(), lhsp, rhsp}; + } + } + nodep->v3fatalSrc("Unhandled compound assignment operation"); + } + void prepost_stmt_sel_visit(AstNodeUniop* const nodep) { // Special case array[something]++, see comments at file top // UINFOTREE(9, nodep, "", "pp-stmt-sel-in"); iterateChildren(nodep); - AstConst* const constp = VN_AS(nodep->lhsp(), Const); - UASSERT_OBJ(nodep, constp, "Expecting CONST"); - AstConst* const newconstp = constp->cloneTree(true); + FileLine* fl = nodep->fileline(); + V3Number numOne{fl, 32, 1, false}; + AstNodeExpr* const exprp = new AstConst{nodep->fileline(), numOne}; - AstSelBit* const rdSelbitp = VN_CAST(nodep->rhsp(), SelBit); - AstNodeExpr* const rdFromp = rdSelbitp->fromp()->unlinkFrBack(); + prepost_stmt_sel_visit(nodep, nodep->lhsp(), exprp); + } + void prepost_stmt_sel_visit(AstAssignCompound* const nodep) { + // Special case array[something] += expr, see comments at file top + // UINFOTREE(9, nodep, "", "pp-stmt-sel-in"); + iterateChildren(nodep); + AstNodeExpr* const exprp = nodep->rhsp()->unlinkFrBack(); + + prepost_stmt_sel_visit(nodep, nodep->lhsp(), exprp); + } + void prepost_stmt_sel_visit(AstNode* const nodep, AstNodeExpr* const lhsp, + AstNodeExpr* const exprp) { + AstSelBit* const rdSelbitp = VN_AS(lhsp, SelBit); + AstNodeVarRef* const rdFromp = VN_AS(rdSelbitp->fromp()->cloneTreePure(true), NodeVarRef); + rdFromp->access(VAccess::READ); AstNodeExpr* const rdBitp = rdSelbitp->bitp()->unlinkFrBack(); - AstSelBit* const wrSelbitp = VN_CAST(nodep->thsp(), SelBit); + AstSelBit* const wrSelbitp = VN_CAST(lhsp, SelBit); AstNodeExpr* const wrFromp = wrSelbitp->fromp()->unlinkFrBack(); + V3LinkLValue::linkLValueSet(wrFromp); // Prepare a temporary variable FileLine* const fl = nodep->fileline(); - const string name = "__VincIndex"s + cvtToStr(++m_modIncrementsNum); + const string name = "__VtempIndex"s + cvtToStr(++m_modCompoundAssignmentsNum); AstVar* const varp = new AstVar{ fl, VVarType::BLOCKTEMP, name, VFlagChildDType{}, new AstRefDType{fl, AstRefDType::FlagTypeOfExpr{}, rdBitp->cloneTree(true)}}; @@ -255,60 +304,60 @@ class LinkIncVisitor final : public VNVisitor { AstNodeExpr* const storeTop = new AstSelBit{fl, wrFromp, new AstVarRef{fl, varp, VAccess::READ}}; - 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}}; - } + AstAssign* assignp + = new AstAssign{nodep->fileline(), storeTop, getOperationp(nodep, valuep, exprp)}; newp->addNext(assignp); // if (debug() >= 9) newp->dumpTreeAndNext("-pp-stmt-sel-new: "); nodep->replaceWith(newp); VL_DO_DANGLING(nodep->deleteTree(), nodep); } - void prepost_stmt_visit(AstNodeTriop* nodep) { + void prepost_stmt_visit(AstNodeUniop* const nodep) { iterateChildren(nodep); - AstConst* const constp = VN_AS(nodep->lhsp(), Const); - UASSERT_OBJ(nodep, constp, "Expecting CONST"); - AstConst* const newconstp = constp->cloneTree(true); + AstNodeExpr* const storeTop = nodep->lhsp()->cloneTreePure(true); + AstNodeExpr* const valuep = nodep->lhsp()->unlinkFrBack(); + FileLine* const fl = nodep->fileline(); + V3Number numOne{fl, 32, 1, false}; + AstNodeExpr* const exprp = new AstConst{nodep->fileline(), numOne}; - AstNodeExpr* const storeTop = nodep->thsp()->unlinkFrBack(); - AstNodeExpr* const valuep = nodep->rhsp()->unlinkFrBack(); + prepost_stmt_visit(nodep, exprp, storeTop, valuep); + } + void prepost_stmt_visit(AstAssignCompound* const nodep) { + iterateChildren(nodep); + AstNodeExpr* const exprp = nodep->rhsp()->unlinkFrBack(); + AstNodeExpr* const storeTop = nodep->lhsp()->cloneTreePure(true); + AstNodeExpr* const valuep = nodep->lhsp()->unlinkFrBack(); - 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}}; - } + prepost_stmt_visit(nodep, exprp, storeTop, valuep); + } + void prepost_stmt_visit(AstNode* const nodep, AstNodeExpr* const exprp, + AstNodeExpr* const storeTop, AstNodeExpr* const valuep) { + V3LinkLValue::linkLValueSet(valuep, false); + AstAssign* const assignp + = new AstAssign{nodep->fileline(), storeTop, getOperationp(nodep, valuep, exprp)}; nodep->replaceWith(assignp); VL_DO_DANGLING(nodep->deleteTree(), nodep); } - void prepost_expr_visit(AstNodeTriop* nodep) { + void prepost_expr_visit(AstNodeUniop* const nodep) { iterateChildren(nodep); if (m_unsupportedHere) { nodep->v3warn(E_UNSUPPORTED, "Unsupported: Pre/post increment/decrement operator" " within a logical expression (&&, ||, ?:, etc.)"); return; } - AstNodeExpr* const readp = nodep->rhsp(); - AstNodeExpr* const writep = nodep->thsp()->unlinkFrBack(); + AstNodeExpr* const readp = nodep->lhsp(); + AstNodeExpr* const writep = nodep->lhsp()->cloneTreePure(true); + V3LinkLValue::linkLValueSet(readp, false); - AstConst* const constp = VN_AS(nodep->lhsp(), Const); - UASSERT_OBJ(nodep, constp, "Expecting CONST"); - AstConst* const newconstp = constp->cloneTree(true); + FileLine* const fl = nodep->fileline(); + V3Number numOne{fl, 32, 1, false}; + AstNodeExpr* const newconstp = new AstConst{nodep->fileline(), numOne}; // Prepare a temporary variable - FileLine* const fl = nodep->fileline(); - const string name = "__Vincrement"s + cvtToStr(++m_modIncrementsNum); + const string name = "__Vincrement"s + cvtToStr(++m_modCompoundAssignmentsNum); AstVar* const varp = new AstVar{ fl, VVarType::BLOCKTEMP, name, VFlagChildDType{}, - new AstRefDType{fl, AstRefDType::FlagTypeOfExpr{}, readp->cloneTree(true)}}; + new AstRefDType{fl, AstRefDType::FlagTypeOfExpr{}, readp->cloneTreePure(true)}}; varp->lifetime(VLifetime::AUTOMATIC_EXPLICIT); if (m_ftaskp) varp->funcLocal(true); @@ -316,15 +365,10 @@ class LinkIncVisitor final : public VNVisitor { insertOnTop(varp); // Define what operation will we be doing - AstNodeExpr* operp; - if (VN_IS(nodep, PostSub) || VN_IS(nodep, PreSub)) { - operp = new AstSub{fl, readp->cloneTreePure(true), newconstp}; - } else { - operp = new AstAdd{fl, readp->cloneTreePure(true), newconstp}; - } + AstNodeExpr* const operp = getOperationp(nodep, readp->cloneTreePure(true), newconstp); - if (VN_IS(nodep, PreAdd) || VN_IS(nodep, PreSub)) { - // PreAdd/PreSub operations + if (VN_IS(nodep, PreInc) || VN_IS(nodep, PreDec)) { + // PreInc/PreDec operations // Immediately after declaration - increment it by one AstAssign* const assignp = new AstAssign{fl, new AstVarRef{fl, varp, VAccess::WRITE}, operp}; @@ -332,7 +376,7 @@ class LinkIncVisitor final : public VNVisitor { assignp->addNext(new AstAssign{fl, writep, new AstVarRef{fl, varp, VAccess::READ}}); insertBeforeStmt(nodep, assignp); } else { - // PostAdd/PostSub operations + // PostInc/PostDec operations // Assign the original variable to the temporary one AstAssign* const assignp = new AstAssign{fl, new AstVarRef{fl, varp, VAccess::WRITE}, readp->cloneTreePure(true)}; @@ -345,10 +389,19 @@ class LinkIncVisitor final : public VNVisitor { nodep->replaceWith(new AstVarRef{readp->fileline(), varp, VAccess::READ}); VL_DO_DANGLING(nodep->deleteTree(), nodep); } - void visit(AstPreAdd* nodep) override { prepost_visit(nodep); } - void visit(AstPostAdd* nodep) override { prepost_visit(nodep); } - void visit(AstPreSub* nodep) override { prepost_visit(nodep); } - void visit(AstPostSub* nodep) override { prepost_visit(nodep); } + void visit(AstPreInc* nodep) override { prepost_visit(nodep); } + void visit(AstPostInc* nodep) override { prepost_visit(nodep); } + void visit(AstPreDec* nodep) override { prepost_visit(nodep); } + void visit(AstPostDec* nodep) override { prepost_visit(nodep); } + void visit(AstAssignCompound* nodep) override { + AstSelBit* const selbitp = VN_CAST(nodep->lhsp(), SelBit); + if (!m_insStmtp && selbitp && VN_IS(selbitp->fromp(), NodeVarRef) + && !selbitp->bitp()->isPure()) { + prepost_stmt_sel_visit(nodep); + } else { + prepost_stmt_visit(nodep); + } + } void visit(AstGenFor* nodep) override { iterateChildren(nodep); } void visit(AstNode* nodep) override { iterateChildren(nodep); } diff --git a/src/V3LinkLValue.cpp b/src/V3LinkLValue.cpp index 2fa1af559..5a0613383 100644 --- a/src/V3LinkLValue.cpp +++ b/src/V3LinkLValue.cpp @@ -269,18 +269,15 @@ class LinkLValueVisitor final : public VNVisitor { iterateAndNextNull(nodep->thsp()); } // cppcheck-suppress constParameterPointer - void prepost_visit(AstNodeTriop* nodep) { + void prepost_visit(AstNodeUniop* nodep) { VL_RESTORER(m_setRefLvalue); - m_setRefLvalue = VAccess::NOCHANGE; - iterateAndNextNull(nodep->lhsp()); - iterateAndNextNull(nodep->rhsp()); m_setRefLvalue = VAccess::WRITE; - iterateAndNextNull(nodep->thsp()); + iterateAndNextNull(nodep->lhsp()); } - void visit(AstPreAdd* nodep) override { prepost_visit(nodep); } - void visit(AstPostAdd* nodep) override { prepost_visit(nodep); } - void visit(AstPreSub* nodep) override { prepost_visit(nodep); } - void visit(AstPostSub* nodep) override { prepost_visit(nodep); } + void visit(AstPreInc* nodep) override { prepost_visit(nodep); } + void visit(AstPostInc* nodep) override { prepost_visit(nodep); } + void visit(AstPreDec* nodep) override { prepost_visit(nodep); } + void visit(AstPostDec* nodep) override { prepost_visit(nodep); } // Nodes that change LValue state void visit(AstSel* nodep) override { @@ -381,9 +378,9 @@ void V3LinkLValue::linkLValue(AstNetlist* nodep) { { LinkLValueVisitor{nodep, VAccess::NOCHANGE}; } // Destruct before checking V3Global::dumpCheckGlobalTree("linklvalue", 0, dumpTreeEitherLevel() >= 6); } -void V3LinkLValue::linkLValueSet(AstNode* nodep) { +void V3LinkLValue::linkLValueSet(AstNode* const nodep, const bool isLValue) { // Called by later link functions when it is known a node needs // to be converted to a lvalue. UINFO(9, __FUNCTION__ << ": "); - { LinkLValueVisitor{nodep, VAccess::WRITE}; } + { LinkLValueVisitor{nodep, isLValue ? VAccess::WRITE : VAccess::READ}; } } diff --git a/src/V3LinkLValue.h b/src/V3LinkLValue.h index 008edc65c..473eaac96 100644 --- a/src/V3LinkLValue.h +++ b/src/V3LinkLValue.h @@ -28,7 +28,7 @@ class AstNode; class V3LinkLValue final { public: static void linkLValue(AstNetlist* nodep) VL_MT_DISABLED; - static void linkLValueSet(AstNode* nodep) VL_MT_DISABLED; + static void linkLValueSet(AstNode* nodep, bool isLValue = true) VL_MT_DISABLED; }; #endif // Guard diff --git a/src/V3Number.h b/src/V3Number.h index c5d1a57bf..2d0e0dcc3 100644 --- a/src/V3Number.h +++ b/src/V3Number.h @@ -528,8 +528,8 @@ public: m_data.num()[0].m_value = value; opCleanThis(); } - V3Number(FileLine* flp, int width, uint32_t value) { - init(nullptr, width, true); + V3Number(FileLine* flp, int width, uint32_t value, bool sized = true) { + init(nullptr, width, sized); m_fileline = flp; m_data.num()[0].m_value = value; opCleanThis(); diff --git a/src/verilog.y b/src/verilog.y index f13d40a8c..c9cea87d8 100644 --- a/src/verilog.y +++ b/src/verilog.y @@ -3790,48 +3790,40 @@ foperator_assignment: // IEEE: operator_assignment (for first part fexprLvalue '=' delay_or_event_controlE expr { $$ = new AstAssign{$2, $1, $4, $3}; } // | fexprLvalue yP_PLUSEQ expr - { $$ = new AstAssign{$2, $1, new AstAdd{$2, $1->cloneTreePure(true), $3}}; } + { $$ = new AstAssignCompound{AstAssignCompound::operation::Add, $2, $1, $3}; } | fexprLvalue yP_MINUSEQ expr - { $$ = new AstAssign{$2, $1, new AstSub{$2, $1->cloneTreePure(true), $3}}; } + { $$ = new AstAssignCompound{AstAssignCompound::operation::Sub, $2, $1, $3}; } | fexprLvalue yP_TIMESEQ expr - { $$ = new AstAssign{$2, $1, new AstMul{$2, $1->cloneTreePure(true), $3}}; } + { $$ = new AstAssignCompound{AstAssignCompound::operation::Mul, $2, $1, $3}; } | fexprLvalue yP_DIVEQ expr - { $$ = new AstAssign{$2, $1, new AstDiv{$2, $1->cloneTreePure(true), $3}}; } + { $$ = new AstAssignCompound{AstAssignCompound::operation::Div, $2, $1, $3}; } | fexprLvalue yP_MODEQ expr - { $$ = new AstAssign{$2, $1, new AstModDiv{$2, $1->cloneTreePure(true), $3}}; } + { $$ = new AstAssignCompound{AstAssignCompound::operation::ModDiv, $2, $1, $3}; } | fexprLvalue yP_ANDEQ expr - { $$ = new AstAssign{$2, $1, new AstAnd{$2, $1->cloneTreePure(true), $3}}; } + { $$ = new AstAssignCompound{AstAssignCompound::operation::And, $2, $1, $3}; } | fexprLvalue yP_OREQ expr - { $$ = new AstAssign{$2, $1, new AstOr{$2, $1->cloneTreePure(true), $3}}; } + { $$ = new AstAssignCompound{AstAssignCompound::operation::Or, $2, $1, $3}; } | fexprLvalue yP_XOREQ expr - { $$ = new AstAssign{$2, $1, new AstXor{$2, $1->cloneTreePure(true), $3}}; } + { $$ = new AstAssignCompound{AstAssignCompound::operation::Xor, $2, $1, $3}; } | fexprLvalue yP_SLEFTEQ expr - { $$ = new AstAssign{$2, $1, new AstShiftL{$2, $1->cloneTreePure(true), $3}}; } + { $$ = new AstAssignCompound{AstAssignCompound::operation::ShiftL, $2, $1, $3}; } | fexprLvalue yP_SRIGHTEQ expr - { $$ = new AstAssign{$2, $1, new AstShiftR{$2, $1->cloneTreePure(true), $3}}; } + { $$ = new AstAssignCompound{AstAssignCompound::operation::ShiftR, $2, $1, $3}; } | fexprLvalue yP_SSRIGHTEQ expr - { $$ = new AstAssign{$2, $1, new AstShiftRS{$2, $1->cloneTreePure(true), $3}}; } + { $$ = new AstAssignCompound{AstAssignCompound::operation::ShiftRS, $2, $1, $3}; } ; inc_or_dec_expression: // ==IEEE: inc_or_dec_expression // // Need fexprScope instead of variable_lvalue to prevent conflict ~l~exprScope yP_PLUSPLUS - { $$ = $1; $$ = new AstPostAdd{$2, new AstConst{$2, AstConst::StringToParse{}, "'b1"}, - // Purity checked in V3LinkInc - $1, $1->cloneTree(true)}; } + { $$ = $1; $$ = new AstPostInc{$2, $1}; } | ~l~exprScope yP_MINUSMINUS - { $$ = $1; $$ = new AstPostSub{$2, new AstConst{$2, AstConst::StringToParse{}, "'b1"}, - // Purity checked in V3LinkInc - $1, $1->cloneTree(true)}; } + { $$ = $1; $$ = new AstPostDec{$2, $1}; } // // Need expr instead of variable_lvalue to prevent conflict | yP_PLUSPLUS expr - { $$ = $1; $$ = new AstPreAdd{$1, new AstConst{$1, AstConst::StringToParse{}, "'b1"}, - // Purity checked in V3LinkInc - $2, $2->cloneTree(true)}; } + { $$ = $1; $$ = new AstPreInc{$1, $2}; } | yP_MINUSMINUS expr - { $$ = $1; $$ = new AstPreSub{$1, new AstConst{$1, AstConst::StringToParse{}, "'b1"}, - // Purity checked in V3LinkInc - $2, $2->cloneTree(true)}; } + { $$ = $1; $$ = new AstPreDec{$1, $2}; } ; finc_or_dec_expression: // ==IEEE: inc_or_dec_expression diff --git a/test_regress/t/t_assign_expr.v b/test_regress/t/t_assign_expr.v index 98ecc2759..c94ef41f1 100644 --- a/test_regress/t/t_assign_expr.v +++ b/test_regress/t/t_assign_expr.v @@ -9,11 +9,20 @@ `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); // verilog_format: on +int x = 0; + module t; int a; int b; int i; + int arr[5]; + + // function with side effects + function int foo(); + x += 1; + return 1; + endfunction; // verilator lint_off ASSIGNEQEXPR initial begin @@ -90,6 +99,22 @@ module t; `checkd(i, 18); `checkd(b, 7); + arr[0] = 3; + arr[0] += 4; + `checkd(arr[0], 7); + + arr[foo()] = 1; + `checkd(x, 1); + arr[foo()] += 2; + `checkd(arr[1], 3); + `checkd(x, 2); + + arr[foo() + 1] = 6; + `checkd(x, 3); + arr[foo() + 1] -= 5; + `checkd(arr[2], 1); + `checkd(x, 4); + $write("*-* All Finished *-*\n"); $finish; end diff --git a/test_regress/t/t_lint_sideeffect_bad.out b/test_regress/t/t_lint_sideeffect_bad.out index d1288696e..a5ab82daf 100644 --- a/test_regress/t/t_lint_sideeffect_bad.out +++ b/test_regress/t/t_lint_sideeffect_bad.out @@ -4,4 +4,16 @@ | ^ ... For warning description see https://verilator.org/warn/SIDEEFFECT?v=latest ... Use "/* verilator lint_off SIDEEFFECT */" and lint_on around source to disable this message. +%Warning-SIDEEFFECT: t/t_lint_sideeffect_bad.v:17:8: Expression side effect may be mishandled + : ... Suggest use a temporary variable in place of this expression + 17 | arr[postincrement_i()][postincrement_i()]++; + | ^ +%Warning-SIDEEFFECT: t/t_lint_sideeffect_bad.v:17:9: Expression side effect may be mishandled + : ... Suggest use a temporary variable in place of this expression + 17 | arr[postincrement_i()][postincrement_i()]++; + | ^~~~~~~~~~~~~~~ +%Warning-SIDEEFFECT: t/t_lint_sideeffect_bad.v:17:28: Expression side effect may be mishandled + : ... Suggest use a temporary variable in place of this expression + 17 | arr[postincrement_i()][postincrement_i()]++; + | ^~~~~~~~~~~~~~~ %Error: Exiting due to diff --git a/test_regress/t/t_stmt_incr_unsup.out b/test_regress/t/t_stmt_incr_unsup.out index 09d333b76..1652f2fac 100644 --- a/test_regress/t/t_stmt_incr_unsup.out +++ b/test_regress/t/t_stmt_incr_unsup.out @@ -4,4 +4,16 @@ | ^ ... For warning description see https://verilator.org/warn/SIDEEFFECT?v=latest ... Use "/* verilator lint_off SIDEEFFECT */" and lint_on around source to disable this message. +%Warning-SIDEEFFECT: t/t_stmt_incr_unsup.v:17:8: Expression side effect may be mishandled + : ... Suggest use a temporary variable in place of this expression + 17 | arr[postincrement_i()][postincrement_i()]++; + | ^ +%Warning-SIDEEFFECT: t/t_stmt_incr_unsup.v:17:9: Expression side effect may be mishandled + : ... Suggest use a temporary variable in place of this expression + 17 | arr[postincrement_i()][postincrement_i()]++; + | ^~~~~~~~~~~~~~~ +%Warning-SIDEEFFECT: t/t_stmt_incr_unsup.v:17:28: Expression side effect may be mishandled + : ... Suggest use a temporary variable in place of this expression + 17 | arr[postincrement_i()][postincrement_i()]++; + | ^~~~~~~~~~~~~~~ %Error: Exiting due to