Support lower dimension looping in foreach loops (#3172).

This commit is contained in:
Wilson Snyder 2021-12-11 20:39:58 -05:00
parent c1652979d5
commit 6b0601fd54
8 changed files with 60 additions and 23 deletions

View File

@ -19,6 +19,7 @@ Verilator 4.217 devel
**Minor:**
* Support lower dimension looping in foreach loops (#3172). [Ehab Ibrahim]
* Support up to 64 bit enums for .next/.prev/.name (#3244). [Alexander Grobman]
* Fix MSWIN compile error (#2681). [Unai Martinez-Corral]
* Fix break under foreach loop (#3230).

View File

@ -183,6 +183,15 @@ public:
static AstConst* parseParamLiteral(FileLine* fl, const string& literal);
};
class AstEmpty final : public AstNode {
// Represents something missing, e.g. a missing argument in FOREACH
public:
AstEmpty(FileLine* fl)
: ASTGEN_SUPER_Empty(fl) {}
ASTNODE_NODE_FUNCS(Empty)
virtual bool same(const AstNode* samep) const override { return true; }
};
class AstEmptyQueue final : public AstNodeMath {
public:
AstEmptyQueue(FileLine* fl)

View File

@ -1305,6 +1305,7 @@ class LinkDotFindVisitor final : public AstNVisitor {
argrefp = largrefp;
// Insert argref's name into symbol table
m_statep->insertSym(m_curSymp, argrefp->name(), argrefp, nullptr);
} else if (VN_IS(argp, Empty)) {
} else {
argp->v3error("'foreach' loop variable expects simple variable name");
}

View File

@ -3810,16 +3810,17 @@ private:
// Major dimension first
while (AstNode* argsp
= loopsp->elementsp()) { // Loop advances due to below varp->unlinkFrBack()
const bool empty = VN_IS(argsp, Empty);
AstVar* const varp = VN_CAST(argsp, Var);
UASSERT_OBJ(varp, argsp, "Missing foreach loop variable");
varp->usedLoopIdx(true);
varp->unlinkFrBack();
fromDtp = fromDtp->skipRefp();
UASSERT_OBJ(varp || empty, argsp, "Missing foreach loop variable");
if (varp) varp->usedLoopIdx(true);
argsp->unlinkFrBack();
if (!fromDtp) {
argsp->v3error("foreach loop variables exceed number of indices of array");
VL_DO_DANGLING(nodep->unlinkFrBack()->deleteTree(), nodep);
return;
}
fromDtp = fromDtp->skipRefp();
UINFO(9, "- foreachArg " << argsp << endl);
UINFO(9, "- from on " << fromp << endl);
UINFO(9, "- from dtp " << fromDtp << endl);
@ -3828,7 +3829,9 @@ private:
AstNode* bodyPointp = new AstBegin{fl, "[EditWrapper]", nullptr};
AstNode* loopp = nullptr;
if (const AstNodeArrayDType* const adtypep = VN_CAST(fromDtp, NodeArrayDType)) {
loopp = createForeachLoopRanged(nodep, bodyPointp, varp, adtypep->declRange());
if (varp) {
loopp = createForeachLoopRanged(nodep, bodyPointp, varp, adtypep->declRange());
}
// Prep for next
fromDtp = fromDtp->subDTypep();
} else if (AstBasicDType* const adtypep = VN_CAST(fromDtp, BasicDType)) {
@ -3838,21 +3841,25 @@ private:
VL_DO_DANGLING(nodep->unlinkFrBack()->deleteTree(), nodep);
return;
}
loopp = createForeachLoopRanged(nodep, bodyPointp, varp, adtypep->declRange());
if (varp) {
loopp = createForeachLoopRanged(nodep, bodyPointp, varp, adtypep->declRange());
}
// Prep for next
fromDtp = nullptr;
} else if (VN_IS(fromDtp, DynArrayDType) || VN_IS(fromDtp, QueueDType)) {
auto* const leftp = new AstConst{fl, AstConst::Signed32{}, 0};
auto* const sizep
= new AstCMethodHard{fl, fromp->cloneTree(false), "size", nullptr};
sizep->dtypeSetSigned32();
sizep->didWidth(true);
sizep->protect(false);
AstNode* const condp
= new AstLt{fl, new AstVarRef{fl, varp, VAccess::READ}, sizep};
AstNode* const incp = new AstAdd{fl, new AstConst{fl, AstConst::Signed32{}, 1},
new AstVarRef{fl, varp, VAccess::READ}};
loopp = createForeachLoop(nodep, bodyPointp, varp, leftp, condp, incp);
if (varp) {
auto* const leftp = new AstConst{fl, AstConst::Signed32{}, 0};
auto* const sizep
= new AstCMethodHard{fl, fromp->cloneTree(false), "size", nullptr};
sizep->dtypeSetSigned32();
sizep->didWidth(true);
sizep->protect(false);
AstNode* const condp
= new AstLt{fl, new AstVarRef{fl, varp, VAccess::READ}, sizep};
AstNode* const incp = new AstAdd{fl, new AstConst{fl, AstConst::Signed32{}, 1},
new AstVarRef{fl, varp, VAccess::READ}};
loopp = createForeachLoop(nodep, bodyPointp, varp, leftp, condp, incp);
}
// Prep for next
fromDtp = fromDtp->subDTypep();
} else if (const AstAssocArrayDType* const adtypep
@ -3897,13 +3904,16 @@ private:
return;
}
// New loop goes UNDER previous loop
if (!newp) {
newp = loopp;
} else {
lastBodyPointp->replaceWith(loopp);
if (varp) {
if (!newp) {
newp = loopp;
} else {
lastBodyPointp->replaceWith(loopp);
}
lastBodyPointp = bodyPointp;
}
lastBodyPointp = bodyPointp;
}
// The parser validates we don't have "foreach (array[,,,])"
UASSERT_OBJ(newp, nodep, "foreach has no non-empty loop variable");
if (bodyp) {
lastBodyPointp->replaceWith(bodyp);

View File

@ -3523,7 +3523,8 @@ for_step_assignment<nodep>: // ==IEEE: for_step_assignment
loop_variables<nodep>: // IEEE: loop_variables
parseRefBase { $$ = $1; }
| loop_variables ',' parseRefBase { $$ = $1; $1->addNext($3); }
| loop_variables ',' parseRefBase { $$ = $1; $$->addNext($3); }
| ',' parseRefBase { $$ = new AstEmpty{$1}; $$->addNext($2); }
;
//************************************************
@ -5007,6 +5008,8 @@ idArrayedForeach<nodep>: // IEEE: id + select (under foreach expression)
// // To avoid conflicts we allow expr as first element, must post-check
| idArrayed '[' expr ',' loop_variables ']'
{ $3 = AstNode::addNextNull($3, $5); $$ = new AstSelLoopVars($2, $1, $3); }
| idArrayed '[' ',' loop_variables ']'
{ $4 = AstNode::addNextNull(new AstEmpty{$3}, $4); $$ = new AstSelLoopVars($2, $1, $4); }
;
// VarRef without any dots or vectorizaion

View File

@ -71,6 +71,14 @@ module t (/*AUTOARG*/);
end
`checkh(sum, 64'h0030128ab2a8e557);
// comma syntax
sum = 0;
foreach (array[,index_b]) begin
$display(index_b);
sum = crc(sum, 0, index_b, 0, 0);
end
`checkh(sum, 64'h0000000006000000);
//
sum = 0;
foreach (larray[index_a]) begin

View File

@ -7,4 +7,7 @@
%Error: t/t_foreach_type_bad.v:23:21: Illegal to foreach loop on basic 'BASICDTYPE 'bit''
23 | foreach (b[i, j, k]);
| ^
%Error: t/t_foreach_type_bad.v:25:18: Illegal to foreach loop on basic 'BASICDTYPE 'real''
25 | foreach (r[, i]);
| ^
%Error: Exiting due to

View File

@ -22,6 +22,8 @@ module t (/*AUTOARG*/);
foreach (b[i, j, k]); // extra loop var
foreach (r[, i]); // no loop var and extra
$stop;
end