Fix array indexing side effects in compound assignments (#7519)

This commit is contained in:
Kamil Danecki 2026-05-02 02:35:51 +02:00 committed by GitHub
parent 25d4827bd5
commit 659274e45d
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
10 changed files with 294 additions and 197 deletions

View File

@ -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:

View File

@ -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:

View File

@ -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); }

View File

@ -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}; }
}

View File

@ -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

View File

@ -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();

View File

@ -3790,48 +3790,40 @@ foperator_assignment<nodeStmtp>: // 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<nodeExprp>: // ==IEEE: inc_or_dec_expression
// // Need fexprScope instead of variable_lvalue to prevent conflict
~l~exprScope yP_PLUSPLUS
{ $<fl>$ = $<fl>1; $$ = new AstPostAdd{$2, new AstConst{$2, AstConst::StringToParse{}, "'b1"},
// Purity checked in V3LinkInc
$1, $1->cloneTree(true)}; }
{ $<fl>$ = $<fl>1; $$ = new AstPostInc{$2, $1}; }
| ~l~exprScope yP_MINUSMINUS
{ $<fl>$ = $<fl>1; $$ = new AstPostSub{$2, new AstConst{$2, AstConst::StringToParse{}, "'b1"},
// Purity checked in V3LinkInc
$1, $1->cloneTree(true)}; }
{ $<fl>$ = $<fl>1; $$ = new AstPostDec{$2, $1}; }
// // Need expr instead of variable_lvalue to prevent conflict
| yP_PLUSPLUS expr
{ $<fl>$ = $<fl>1; $$ = new AstPreAdd{$1, new AstConst{$1, AstConst::StringToParse{}, "'b1"},
// Purity checked in V3LinkInc
$2, $2->cloneTree(true)}; }
{ $<fl>$ = $<fl>1; $$ = new AstPreInc{$1, $2}; }
| yP_MINUSMINUS expr
{ $<fl>$ = $<fl>1; $$ = new AstPreSub{$1, new AstConst{$1, AstConst::StringToParse{}, "'b1"},
// Purity checked in V3LinkInc
$2, $2->cloneTree(true)}; }
{ $<fl>$ = $<fl>1; $$ = new AstPreDec{$1, $2}; }
;
finc_or_dec_expression<nodeExprp>: // ==IEEE: inc_or_dec_expression

View File

@ -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

View File

@ -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

View File

@ -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