Fix array indexing side effects in compound assignments (#7519)
This commit is contained in:
parent
25d4827bd5
commit
659274e45d
|
|
@ -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:
|
||||
|
|
|
|||
|
|
@ -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:
|
||||
|
|
|
|||
|
|
@ -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); }
|
||||
|
||||
|
|
|
|||
|
|
@ -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}; }
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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();
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
Loading…
Reference in New Issue