Fix break under foreach loop (#3230).

Internals: Move Foreach handling into V3Width.
This commit is contained in:
Wilson Snyder
2021-12-11 15:06:33 -05:00
parent 00ef0519f5
commit 984ee624ed
14 changed files with 315 additions and 80 deletions
+9 -73
View File
@@ -413,13 +413,8 @@ private:
}
virtual void visit(AstForeach* nodep) override {
// FOREACH(array,loopvars,body)
// -> BEGIN(declare vars, loopa=lowest; WHILE(loopa<=highest, ... body))
// nodep->dumpTree(cout, "-foreach-old:");
// FOREACH(array, loopvars, body)
UINFO(9, "FOREACH " << nodep << endl);
// nodep->dumpTree(cout, "-foreach-in:");
AstNode* newp = nodep->bodysp();
if (newp) newp->unlinkFrBackWithNext();
// Separate iteration vars from base from variable
// Input:
// v--- arrayp
@@ -433,81 +428,22 @@ private:
// 3. ASTSELLOOPVARS(first, var0..var1))
// 4. DOT(DOT(first, second), ASTSELBIT(third, var0))
AstNode* bracketp = nodep->arrayp();
AstNode* firstVarsp = nullptr;
while (AstDot* dotp = VN_CAST(bracketp, Dot)) { bracketp = dotp->rhsp(); }
while (AstDot* dotp = VN_CAST(bracketp, Dot)) bracketp = dotp->rhsp();
if (AstSelBit* const selp = VN_CAST(bracketp, SelBit)) {
firstVarsp = selp->rhsp()->unlinkFrBackWithNext();
selp->replaceWith(selp->fromp()->unlinkFrBack());
// Convert to AstSelLoopVars so V3LinkDot knows what's being defined
AstNode* const newp
= new AstSelLoopVars{selp->fileline(), selp->fromp()->unlinkFrBack(),
selp->rhsp()->unlinkFrBackWithNext()};
selp->replaceWith(newp);
VL_DO_DANGLING(selp->deleteTree(), selp);
} else if (AstSelLoopVars* const selp = VN_CAST(bracketp, SelLoopVars)) {
firstVarsp = selp->elementsp()->unlinkFrBackWithNext();
selp->replaceWith(selp->fromp()->unlinkFrBack());
VL_DO_DANGLING(selp->deleteTree(), selp);
// Ok
} else {
nodep->v3error(
"Syntax error; foreach missing bracketed index variable (IEEE 1800-2017 12.7.3)");
"Syntax error; foreach missing bracketed loop variable (IEEE 1800-2017 12.7.3)");
VL_DO_DANGLING(nodep->unlinkFrBack()->deleteTree(), nodep);
return;
}
AstNode* const arrayp = nodep->arrayp(); // Maybe different node since bracketp looked
if (!VN_IS(arrayp, ParseRef) && !VN_IS(arrayp, Dot)) {
// Code below needs to use other then attributes to figure out the bounds
// Also need to deal with queues, etc
arrayp->v3warn(E_UNSUPPORTED, "Unsupported: foreach on non-simple variable reference");
VL_DO_DANGLING(nodep->unlinkFrBack()->deleteTree(), nodep);
return;
}
// Split into for loop
// Must do innermost (last) variable first
int dimension = 1;
AstNode* lastVarsp = firstVarsp;
while (lastVarsp->nextp()) {
lastVarsp = lastVarsp->nextp();
dimension++;
}
for (AstNode* varsp = lastVarsp; varsp; varsp = varsp->backp()) {
UINFO(9, "foreachVar " << varsp << endl);
FileLine* const fl = varsp->fileline();
AstNode* const varp
= new AstVar(fl, AstVarType::BLOCKTEMP, varsp->name(), nodep->findSigned32DType());
// These will be the left and right dimensions and size of the array:
AstNode* const leftp = new AstAttrOf(
fl, AstAttrType::DIM_LEFT, arrayp->cloneTree(false), new AstConst(fl, dimension));
AstNode* const rightp = new AstAttrOf(
fl, AstAttrType::DIM_RIGHT, arrayp->cloneTree(false), new AstConst(fl, dimension));
AstNode* const sizep = new AstAttrOf(
fl, AstAttrType::DIM_SIZE, arrayp->cloneTree(false), new AstConst(fl, dimension));
AstNode* const stmtsp = varp;
// Assign left-dimension into the loop var:
stmtsp->addNext(
new AstAssign(fl, new AstVarRef(fl, varp->name(), VAccess::WRITE), leftp));
// This will turn into a constant bool for static arrays
AstNode* const notemptyp = new AstGt(fl, sizep, new AstConst(fl, 0));
// This will turn into a bool constant, indicating whether
// we count the loop variable up or down:
AstNode* const countupp
= new AstLte(fl, leftp->cloneTree(true), rightp->cloneTree(true));
AstNode* const comparep = new AstCond(
fl, countupp->cloneTree(true),
// Left increments up to right
new AstLte(fl, new AstVarRef(fl, varp->name(), VAccess::READ),
rightp->cloneTree(true)),
// Left decrements down to right
new AstGte(fl, new AstVarRef(fl, varp->name(), VAccess::READ), rightp));
// This will reduce to comparep for static arrays
AstNode* const condp = new AstAnd(fl, notemptyp, comparep);
AstNode* const incp = new AstAssign(
fl, new AstVarRef(fl, varp->name(), VAccess::WRITE),
new AstAdd(fl, new AstVarRef(fl, varp->name(), VAccess::READ),
new AstCond(fl, countupp, new AstConst(fl, 1), new AstConst(fl, -1))));
stmtsp->addNext(new AstWhile(fl, condp, newp, incp));
newp = new AstBegin(nodep->fileline(), "", stmtsp, false, true);
dimension--;
}
// newp->dumpTree(cout, "-foreach-new:");
VL_DO_DANGLING(firstVarsp->deleteTree(), firstVarsp);
nodep->replaceWith(newp);
VL_DO_DANGLING(nodep->deleteTree(), nodep);
}
virtual void visit(AstNodeModule* nodep) override {