forked from github/verilator
Fix associative array foreach loop (#3229).
This commit is contained in:
parent
984ee624ed
commit
740fee660e
1
Changes
1
Changes
@ -24,6 +24,7 @@ Verilator 4.217 devel
|
|||||||
* Fix break under foreach loop (#3230).
|
* Fix break under foreach loop (#3230).
|
||||||
* Fix VL_STREAML_FAST_QQI with 64 bit left-hand-side (#3232) (#3235)
|
* Fix VL_STREAML_FAST_QQI with 64 bit left-hand-side (#3232) (#3235)
|
||||||
* Fix $sformat of inputs/outputs (#3236). [Adrien Le Masle]
|
* Fix $sformat of inputs/outputs (#3236). [Adrien Le Masle]
|
||||||
|
* Fix associative array foreach loop (#3229).
|
||||||
|
|
||||||
|
|
||||||
Verilator 4.216 2021-12-05
|
Verilator 4.216 2021-12-05
|
||||||
|
@ -1392,6 +1392,10 @@ void AstJumpLabel::dump(std::ostream& str) const {
|
|||||||
str << "%Error:UNLINKED";
|
str << "%Error:UNLINKED";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
void AstLogOr::dump(std::ostream& str) const {
|
||||||
|
this->AstNode::dump(str);
|
||||||
|
if (sideEffect()) str << " [SIDE]";
|
||||||
|
}
|
||||||
void AstMemberSel::dump(std::ostream& str) const {
|
void AstMemberSel::dump(std::ostream& str) const {
|
||||||
this->AstNodeMath::dump(str);
|
this->AstNodeMath::dump(str);
|
||||||
str << " -> ";
|
str << " -> ";
|
||||||
|
@ -6590,6 +6590,11 @@ public:
|
|||||||
// Binary ops
|
// Binary ops
|
||||||
|
|
||||||
class AstLogOr final : public AstNodeBiop {
|
class AstLogOr final : public AstNodeBiop {
|
||||||
|
// LOGOR with optional side effects
|
||||||
|
// Side effects currently used in some V3Width code
|
||||||
|
// TBD if this concept is generally adopted for side-effect tracking
|
||||||
|
// versus V3Const tracking it itself
|
||||||
|
bool m_sideEffect = false; // Has side effect, relies on short-circuiting
|
||||||
public:
|
public:
|
||||||
AstLogOr(FileLine* fl, AstNode* lhsp, AstNode* rhsp)
|
AstLogOr(FileLine* fl, AstNode* lhsp, AstNode* rhsp)
|
||||||
: ASTGEN_SUPER_LogOr(fl, lhsp, rhsp) {
|
: ASTGEN_SUPER_LogOr(fl, lhsp, rhsp) {
|
||||||
@ -6602,6 +6607,11 @@ public:
|
|||||||
virtual void numberOperate(V3Number& out, const V3Number& lhs, const V3Number& rhs) override {
|
virtual void numberOperate(V3Number& out, const V3Number& lhs, const V3Number& rhs) override {
|
||||||
out.opLogOr(lhs, rhs);
|
out.opLogOr(lhs, rhs);
|
||||||
}
|
}
|
||||||
|
virtual bool same(const AstNode* samep) const override {
|
||||||
|
const AstLogOr* const sp = static_cast<const AstLogOr*>(samep);
|
||||||
|
return m_sideEffect == sp->m_sideEffect;
|
||||||
|
}
|
||||||
|
virtual void dump(std::ostream& str = std::cout) const override;
|
||||||
virtual string emitVerilog() override { return "%k(%l %f|| %r)"; }
|
virtual string emitVerilog() override { return "%k(%l %f|| %r)"; }
|
||||||
virtual string emitC() override { return "VL_LOGOR_%nq%lq%rq(%nw,%lw,%rw, %P, %li, %ri)"; }
|
virtual string emitC() override { return "VL_LOGOR_%nq%lq%rq(%nw,%lw,%rw, %P, %li, %ri)"; }
|
||||||
virtual string emitSimpleOperator() override { return "||"; }
|
virtual string emitSimpleOperator() override { return "||"; }
|
||||||
@ -6611,6 +6621,9 @@ public:
|
|||||||
virtual bool sizeMattersLhs() const override { return false; }
|
virtual bool sizeMattersLhs() const override { return false; }
|
||||||
virtual bool sizeMattersRhs() const override { return false; }
|
virtual bool sizeMattersRhs() const override { return false; }
|
||||||
virtual int instrCount() const override { return widthInstrs() + INSTR_COUNT_BRANCH; }
|
virtual int instrCount() const override { return widthInstrs() + INSTR_COUNT_BRANCH; }
|
||||||
|
virtual bool isPure() const override { return !m_sideEffect; }
|
||||||
|
void sideEffect(bool flag) { m_sideEffect = flag; }
|
||||||
|
bool sideEffect() const { return m_sideEffect; }
|
||||||
};
|
};
|
||||||
class AstLogAnd final : public AstNodeBiop {
|
class AstLogAnd final : public AstNodeBiop {
|
||||||
public:
|
public:
|
||||||
|
@ -3209,7 +3209,7 @@ private:
|
|||||||
TREEOP ("AstAnd {$lhsp, $rhsp.isAllOnes}", "replaceWLhs(nodep)");
|
TREEOP ("AstAnd {$lhsp, $rhsp.isAllOnes}", "replaceWLhs(nodep)");
|
||||||
TREEOP ("AstLogAnd{$lhsp, $rhsp.isNeqZero}", "replaceWLhs(nodep)");
|
TREEOP ("AstLogAnd{$lhsp, $rhsp.isNeqZero}", "replaceWLhs(nodep)");
|
||||||
TREEOP ("AstOr {$lhsp, $rhsp.isAllOnes, isTPure($lhsp)}", "replaceWRhs(nodep)"); //->allOnes
|
TREEOP ("AstOr {$lhsp, $rhsp.isAllOnes, isTPure($lhsp)}", "replaceWRhs(nodep)"); //->allOnes
|
||||||
TREEOP ("AstLogOr {$lhsp, $rhsp.isNeqZero, isTPure($lhsp)}", "replaceNum(nodep,1)");
|
TREEOP ("AstLogOr {$lhsp, $rhsp.isNeqZero, isTPure($lhsp), nodep->isPure()}", "replaceNum(nodep,1)");
|
||||||
TREEOP ("AstXor {$lhsp.isAllOnes, $rhsp}", "AstNot{$rhsp}");
|
TREEOP ("AstXor {$lhsp.isAllOnes, $rhsp}", "AstNot{$rhsp}");
|
||||||
TREEOP ("AstMul {$lhsp.isOne, $rhsp}", "replaceWRhs(nodep)");
|
TREEOP ("AstMul {$lhsp.isOne, $rhsp}", "replaceWRhs(nodep)");
|
||||||
TREEOP ("AstMulS {$lhsp.isOne, $rhsp}", "replaceWRhs(nodep)");
|
TREEOP ("AstMulS {$lhsp.isOne, $rhsp}", "replaceWRhs(nodep)");
|
||||||
@ -3383,7 +3383,7 @@ private:
|
|||||||
TREEOPV("AstOneHot0{$lhsp.width1}", "replaceNum(nodep,1)");
|
TREEOPV("AstOneHot0{$lhsp.width1}", "replaceNum(nodep,1)");
|
||||||
// Binary AND/OR is faster than logical and/or (usually)
|
// Binary AND/OR is faster than logical and/or (usually)
|
||||||
TREEOPV("AstLogAnd{$lhsp.width1, $rhsp.width1, isTPure($lhsp), isTPure($rhsp)}", "AstAnd{$lhsp,$rhsp}");
|
TREEOPV("AstLogAnd{$lhsp.width1, $rhsp.width1, isTPure($lhsp), isTPure($rhsp)}", "AstAnd{$lhsp,$rhsp}");
|
||||||
TREEOPV("AstLogOr {$lhsp.width1, $rhsp.width1, isTPure($lhsp), isTPure($rhsp)}", "AstOr{$lhsp,$rhsp}");
|
TREEOPV("AstLogOr {$lhsp.width1, $rhsp.width1, nodep->isPure(), isTPure($lhsp), isTPure($rhsp)}", "AstOr{$lhsp,$rhsp}");
|
||||||
TREEOPV("AstLogNot{$lhsp.width1, isTPure($lhsp)}", "AstNot{$lhsp}");
|
TREEOPV("AstLogNot{$lhsp.width1, isTPure($lhsp)}", "AstNot{$lhsp}");
|
||||||
// CONCAT(CONCAT({a},{b}),{c}) -> CONCAT({a},CONCAT({b},{c}))
|
// CONCAT(CONCAT({a},{b}),{c}) -> CONCAT({a},CONCAT({b},{c}))
|
||||||
// CONCAT({const},CONCAT({const},{c})) -> CONCAT((constifiedCONC{const|const},{c}))
|
// CONCAT({const},CONCAT({const},{c})) -> CONCAT((constifiedCONC{const|const},{c}))
|
||||||
|
@ -3812,6 +3812,7 @@ private:
|
|||||||
= loopsp->elementsp()) { // Loop advances due to below varp->unlinkFrBack()
|
= loopsp->elementsp()) { // Loop advances due to below varp->unlinkFrBack()
|
||||||
AstVar* const varp = VN_CAST(argsp, Var);
|
AstVar* const varp = VN_CAST(argsp, Var);
|
||||||
UASSERT_OBJ(varp, argsp, "Missing foreach loop variable");
|
UASSERT_OBJ(varp, argsp, "Missing foreach loop variable");
|
||||||
|
varp->usedLoopIdx(true);
|
||||||
varp->unlinkFrBack();
|
varp->unlinkFrBack();
|
||||||
fromDtp = fromDtp->skipRefp();
|
fromDtp = fromDtp->skipRefp();
|
||||||
if (!fromDtp) {
|
if (!fromDtp) {
|
||||||
@ -3854,6 +3855,42 @@ private:
|
|||||||
loopp = createForeachLoop(nodep, bodyPointp, varp, leftp, condp, incp);
|
loopp = createForeachLoop(nodep, bodyPointp, varp, leftp, condp, incp);
|
||||||
// Prep for next
|
// Prep for next
|
||||||
fromDtp = fromDtp->subDTypep();
|
fromDtp = fromDtp->subDTypep();
|
||||||
|
} else if (const AstAssocArrayDType* const adtypep
|
||||||
|
= VN_CAST(fromDtp, AssocArrayDType)) {
|
||||||
|
// Make this: var KEY_TYPE index;
|
||||||
|
// bit index__Vfirst;
|
||||||
|
// index__Vfirst = 0;
|
||||||
|
// if (0 != array.first(index))
|
||||||
|
// do body while (index__Vfirst || 0 != array.next(index))
|
||||||
|
varp->dtypeFrom(adtypep->keyDTypep());
|
||||||
|
AstVar* const first_varp = new AstVar{
|
||||||
|
fl, AstVarType::BLOCKTEMP, varp->name() + "__Vfirst", VFlagBitPacked{}, 1};
|
||||||
|
first_varp->usedLoopIdx(true);
|
||||||
|
AstNode* const firstp = new AstMethodCall{
|
||||||
|
fl, fromp->cloneTree(false), "first",
|
||||||
|
new AstArg{fl, "", new AstVarRef{fl, varp, VAccess::READWRITE}}};
|
||||||
|
AstNode* const nextp = new AstMethodCall{
|
||||||
|
fl, fromp->cloneTree(false), "next",
|
||||||
|
new AstArg{fl, "", new AstVarRef{fl, varp, VAccess::READWRITE}}};
|
||||||
|
AstNode* const first_clearp
|
||||||
|
= new AstAssign{fl, new AstVarRef{fl, first_varp, VAccess::WRITE},
|
||||||
|
new AstConst{fl, AstConst::BitFalse{}}};
|
||||||
|
auto* const orp = new AstLogOr{fl, new AstVarRef{fl, first_varp, VAccess::READ},
|
||||||
|
new AstNeq{fl, new AstConst{fl, 0}, nextp}};
|
||||||
|
orp->sideEffect(true);
|
||||||
|
AstNode* const whilep = new AstWhile{fl, orp, first_clearp};
|
||||||
|
first_clearp->addNext(bodyPointp);
|
||||||
|
AstNode* const ifbodyp
|
||||||
|
= new AstAssign{fl, new AstVarRef{fl, first_varp, VAccess::WRITE},
|
||||||
|
new AstConst{fl, AstConst::BitTrue{}}};
|
||||||
|
ifbodyp->addNext(whilep);
|
||||||
|
AstNode* const stmtsp = varp; // New statements for under new Begin
|
||||||
|
stmtsp->addNext(first_varp);
|
||||||
|
stmtsp->addNext(
|
||||||
|
new AstIf{fl, new AstNeq{fl, new AstConst{fl, 0}, firstp}, ifbodyp});
|
||||||
|
loopp = new AstBegin{nodep->fileline(), "", stmtsp, false, true};
|
||||||
|
// Prep for next
|
||||||
|
fromDtp = fromDtp->subDTypep();
|
||||||
} else {
|
} else {
|
||||||
argsp->v3error("Illegal to foreach loop on '" + fromDtp->prettyTypeName() + "'");
|
argsp->v3error("Illegal to foreach loop on '" + fromDtp->prettyTypeName() + "'");
|
||||||
VL_DO_DANGLING(nodep->unlinkFrBack()->deleteTree(), nodep);
|
VL_DO_DANGLING(nodep->unlinkFrBack()->deleteTree(), nodep);
|
||||||
|
@ -99,6 +99,16 @@ module t (/*AUTOARG*/
|
|||||||
`checkh(a[~65'hfe], ~ 90'hfee);
|
`checkh(a[~65'hfe], ~ 90'hfee);
|
||||||
end
|
end
|
||||||
|
|
||||||
|
begin
|
||||||
|
int a [string];
|
||||||
|
int sum;
|
||||||
|
sum = 0;
|
||||||
|
a["one"] = 1;
|
||||||
|
a["two"] = 2;
|
||||||
|
foreach (a[i]) sum += a[i];
|
||||||
|
`checkh(sum, 1 + 2);
|
||||||
|
end
|
||||||
|
|
||||||
$write("*-* All Finished *-*\n");
|
$write("*-* All Finished *-*\n");
|
||||||
$finish;
|
$finish;
|
||||||
end
|
end
|
||||||
|
Loading…
Reference in New Issue
Block a user