Internals: Convert foreach loops in V3Begin as well as V3Width (#5283)

This commit is contained in:
Arkadiusz Kozdra 2024-08-02 14:29:05 +02:00 committed by GitHub
parent 2f5c58b345
commit a61178bd89
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
5 changed files with 186 additions and 159 deletions

View File

@ -134,6 +134,9 @@ class BeginVisitor final : public VNVisitor {
dotNames(nodep, "__FORK__");
nodep->name("");
}
void visit(AstForeach* nodep) override {
VL_DO_DANGLING(V3Begin::convertToWhile(nodep), nodep);
}
void visit(AstNodeAssign* nodep) override {
// Keep begin under assignment (in nodep->timingControlp())
VL_RESTORER(m_keepBegins);
@ -369,3 +372,150 @@ void V3Begin::debeginAll(AstNetlist* nodep) {
} // Destruct before checking
V3Global::dumpCheckGlobalTree("begin", 0, dumpTreeEitherLevel() >= 3);
}
static AstNode* createForeachLoop(AstNodeForeach* nodep, AstNode* bodysp, AstVar* varp,
AstNodeExpr* leftp, AstNodeExpr* rightp, VNType nodeType) {
FileLine* const fl = varp->fileline();
AstNodeExpr* varRefp = new AstVarRef{fl, varp, VAccess::READ};
AstNodeExpr* condp;
bool inc = true;
switch (nodeType) {
case VNType::atLteS: condp = new AstLteS{fl, varRefp, rightp}; break;
case VNType::atLt: condp = new AstLt{fl, varRefp, rightp}; break;
case VNType::atGteS:
condp = new AstGteS{fl, varRefp, rightp};
inc = false;
break;
default: UASSERT_OBJ(0, varp, "Missing comparison handling"); break;
}
AstNodeExpr* incp;
if (inc)
incp = new AstAdd{fl, varRefp->cloneTree(false), new AstConst{fl, 1}};
else
incp = new AstSub{fl, varRefp->cloneTree(false), new AstConst{fl, 1}};
AstWhile* const whilep = new AstWhile{
fl, condp, bodysp, new AstAssign{fl, new AstVarRef{fl, varp, VAccess::WRITE}, incp}};
AstNode* const stmtsp = varp; // New statements for outer loop
stmtsp->addNext(new AstAssign{fl, new AstVarRef{fl, varp, VAccess::WRITE}, leftp});
stmtsp->addNext(whilep);
return stmtsp;
}
static AstNode* createForeachLoopRanged(AstNodeForeach* nodep, AstNode* bodysp, AstVar* varp,
const VNumRange& declRange) {
FileLine* const fl = varp->fileline();
V3Number left{nodep, 32}, right{nodep, 32};
left.isSigned(true);
right.isSigned(true);
left.setLongS(declRange.left());
right.setLongS(declRange.right());
AstNodeExpr* const leftp = new AstConst{fl, left};
AstNodeExpr* const rightp = new AstConst{fl, right};
return createForeachLoop(nodep, bodysp, varp, leftp, rightp,
declRange.left() <= declRange.right() ? VNType::atLteS
: VNType::atGteS);
}
AstNode* V3Begin::convertToWhile(AstForeach* nodep) {
// if (debug()) dumpTree(cout, "- foreach-old: ");
const AstSelLoopVars* const loopsp = VN_CAST(nodep->arrayp(), SelLoopVars);
UASSERT_OBJ(loopsp, nodep, "No loop variables under foreach");
AstNodeExpr* const fromp = loopsp->fromp();
UASSERT_OBJ(fromp->dtypep(), fromp, "Missing data type");
AstNodeDType* fromDtp = fromp->dtypep()->skipRefp();
// Split into for loop
// We record where the body needs to eventually go with bodyPointp
AstNode* bodyPointp = new AstBegin{nodep->fileline(), "[EditWrapper]", nullptr};
AstNode* newp = nullptr;
AstNode* lastp = nodep;
// Major dimension first
for (AstNode *argsp = loopsp->elementsp(), *next_argsp; argsp; argsp = next_argsp) {
next_argsp = argsp->nextp();
const bool empty = VN_IS(argsp, Empty);
AstVar* const varp = VN_CAST(argsp, Var);
UASSERT_OBJ(varp || empty, argsp, "Missing foreach loop variable");
if (varp) varp->unlinkFrBack()->usedLoopIdx(true);
UASSERT_OBJ(fromDtp, argsp, "more loop vars than dimensions");
fromDtp = fromDtp->skipRefp();
FileLine* const fl = argsp->fileline();
if (varp) {
AstNode* loopp = nullptr;
VNRelinker handle;
lastp->unlinkFrBack(&handle);
if (const AstNodeArrayDType* const adtypep = VN_CAST(fromDtp, NodeArrayDType)) {
loopp = createForeachLoopRanged(nodep, bodyPointp, varp, adtypep->declRange());
} else if (AstBasicDType* const adtypep = VN_CAST(fromDtp, BasicDType)) {
if (adtypep->isString()) {
AstConst* const leftp = new AstConst{fl, 0};
AstNodeExpr* const rightp = new AstLenN{fl, fromp->cloneTreePure(false)};
loopp
= createForeachLoop(nodep, bodyPointp, varp, leftp, rightp, VNType::atLt);
} else {
UASSERT_OBJ(adtypep->isRanged(), varp, "foreach on basic " << adtypep);
loopp = createForeachLoopRanged(nodep, bodyPointp, varp, adtypep->declRange());
}
} else if (VN_IS(fromDtp, DynArrayDType) || VN_IS(fromDtp, QueueDType)) {
AstConst* const leftp = new AstConst{fl, 0};
AstNodeExpr* const rightp
= new AstCMethodHard{fl, fromp->cloneTreePure(false), "size"};
rightp->dtypeSetSigned32();
rightp->protect(false);
loopp = createForeachLoop(nodep, bodyPointp, varp, leftp, rightp, VNType::atLt);
} 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))
AstVar* const first_varp = new AstVar{
fl, VVarType::BLOCKTEMP, varp->name() + "__Vfirst", VFlagBitPacked{}, 1};
first_varp->usedLoopIdx(true);
first_varp->lifetime(VLifetime::AUTOMATIC);
AstNodeExpr* const firstp
= new AstCMethodHard{fl, fromp->cloneTreePure(false), "first",
new AstVarRef{fl, varp, VAccess::READWRITE}};
firstp->dtypeSetSigned32();
AstNodeExpr* const nextp
= new AstCMethodHard{fl, fromp->cloneTreePure(false), "next",
new AstVarRef{fl, varp, VAccess::READWRITE}};
nextp->dtypeSetSigned32();
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}};
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);
loopp = varp;
loopp->addNext(first_varp);
loopp->addNext(
new AstIf{fl, new AstNeq{fl, new AstConst{fl, 0}, firstp}, ifbodyp});
}
UASSERT_OBJ(loopp, argsp, "unable to foreach " << fromDtp);
// New loop goes UNDER previous loop
handle.relink(loopp);
lastp = bodyPointp;
if (!newp) newp = loopp;
}
// Prep for next
fromDtp = fromDtp->subDTypep();
}
// The parser validates we don't have "foreach (array[,,,])"
AstNode* const bodyp = nodep->stmtsp();
UASSERT_OBJ(newp, nodep, "foreach has no non-empty loop variable");
if (bodyp) {
bodyPointp->replaceWith(bodyp->unlinkFrBackWithNext());
} else {
bodyPointp->unlinkFrBack();
}
VL_DO_DANGLING(bodyPointp->deleteTree(), bodyPointp);
VL_DO_DANGLING(nodep->deleteTree(), nodep);
// if (debug()) newp->dumpTreeAndNext(cout, "- foreach-new: ");
return newp;
}

View File

@ -23,12 +23,15 @@
#include "V3ThreadSafety.h"
class AstNetlist;
class AstNode;
class AstForeach;
//============================================================================
class V3Begin final {
public:
static void debeginAll(AstNetlist* nodep) VL_MT_DISABLED;
static AstNode* convertToWhile(AstForeach* nodep) VL_MT_DISABLED;
};
#endif // Guard

View File

@ -1563,6 +1563,10 @@ class TaskVisitor final : public VNVisitor {
// Done the loop
m_insStmtp = nullptr; // Next thing should be new statement
}
void visit(AstNodeForeach* nodep) override { // LCOV_EXCL_LINE
nodep->v3fatalSrc(
"Foreach statements should have been converted to while statements in V3Begin.cpp");
}
void visit(AstNodeFor* nodep) override { // LCOV_EXCL_LINE
nodep->v3fatalSrc(
"For statements should have been converted to while statements in V3Begin.cpp");

View File

@ -68,6 +68,7 @@
#include "V3Width.h"
#include "V3Ast.h"
#include "V3Begin.h"
#include "V3Const.h"
#include "V3Error.h"
#include "V3Global.h"
@ -4869,10 +4870,6 @@ class WidthVisitor final : public VNVisitor {
AstNodeExpr* const fromp = loopsp->fromp();
UASSERT_OBJ(fromp->dtypep(), fromp, "Missing data type");
AstNodeDType* fromDtp = fromp->dtypep()->skipRefp();
// Split into for loop
// We record where the body needs to eventually go with bodyPointp
AstNode* lastBodyPointp = nullptr;
AstNode* newp = nullptr;
// Major dimension first
for (AstNode *argsp = loopsp->elementsp(), *next_argsp; argsp; argsp = next_argsp) {
next_argsp = argsp->nextp();
@ -4890,166 +4887,35 @@ class WidthVisitor final : public VNVisitor {
UINFO(9, "- from on " << fromp << endl);
UINFO(9, "- from dtp " << fromDtp << endl);
if (VN_IS(nodep, ConstraintForeach)) {
// For now unsupported
userIterate(varp, nullptr);
// Prep for next
fromDtp = nullptr;
} else {
argsp->unlinkFrBack();
FileLine* const fl = argsp->fileline();
AstNode* bodyPointp = new AstBegin{fl, "[EditWrapper]", nullptr};
AstNode* loopp = nullptr;
if (const AstNodeArrayDType* const adtypep = VN_CAST(fromDtp, NodeArrayDType)) {
if (varp) {
loopp = createForeachLoopRanged(nodep, bodyPointp, varp,
adtypep->declRange());
}
// Prep for next
fromDtp = fromDtp->subDTypep();
} else if (AstBasicDType* const adtypep = VN_CAST(fromDtp, BasicDType)) {
if (adtypep->isString()) {
if (varp) {
AstConst* const leftp = new AstConst{fl, AstConst::Signed32{}, 0};
AstLt* const condp
= new AstLt{fl, new AstVarRef{fl, varp, VAccess::READ},
new AstLenN{fl, fromp->cloneTreePure(false)}};
AstAdd* 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);
}
} else if (!adtypep->isRanged()) {
argsp->v3error("Illegal to foreach loop on basic '"
+ fromDtp->prettyTypeName() + "'");
VL_DO_DANGLING(nodep->unlinkFrBack()->deleteTree(), nodep);
VL_DO_DANGLING(bodyPointp->deleteTree(), bodyPointp);
return;
} else {
if (varp) {
loopp = createForeachLoopRanged(nodep, bodyPointp, varp,
adtypep->declRange());
}
}
// Prep for next
fromDtp = nullptr;
} else if (VN_IS(fromDtp, DynArrayDType) || VN_IS(fromDtp, QueueDType)) {
if (varp) {
auto* const leftp = new AstConst{fl, AstConst::Signed32{}, 0};
auto* const sizep
= new AstCMethodHard{fl, fromp->cloneTreePure(false), "size"};
sizep->dtypeSetSigned32();
sizep->didWidth(true);
sizep->protect(false);
AstNodeExpr* const condp
= new AstLt{fl, new AstVarRef{fl, varp, VAccess::READ}, sizep};
AstNodeExpr* 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
= 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, VVarType::BLOCKTEMP, varp->name() + "__Vfirst", VFlagBitPacked{}, 1};
first_varp->usedLoopIdx(true);
first_varp->lifetime(VLifetime::AUTOMATIC);
AstNodeExpr* const firstp = new AstMethodCall{
fl, fromp->cloneTreePure(false), "first",
new AstArg{fl, "", new AstVarRef{fl, varp, VAccess::READWRITE}}};
AstNodeExpr* const nextp = new AstMethodCall{
fl, fromp->cloneTreePure(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}};
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 = stmtsp;
// Prep for next
fromDtp = fromDtp->subDTypep();
} else {
argsp->v3error("Illegal to foreach loop on '" + fromDtp->prettyTypeName()
+ "'");
if (VN_IS(fromDtp, NodeArrayDType) || VN_IS(fromDtp, DynArrayDType)
|| VN_IS(fromDtp, QueueDType)) {
// Nothing special here
} else if (AstBasicDType* const adtypep = VN_CAST(fromDtp, BasicDType)) {
if (!adtypep->isString() && !adtypep->isRanged()) {
argsp->v3error("Illegal 'foreach' loop on " << fromDtp->prettyTypeName()
<< " data type");
VL_DO_DANGLING(nodep->unlinkFrBack()->deleteTree(), nodep);
return;
}
// New loop goes UNDER previous loop
if (varp) {
if (!newp) {
newp = loopp;
} else {
lastBodyPointp->replaceWith(loopp);
}
lastBodyPointp = bodyPointp;
}
} else if (const AstAssocArrayDType* const adtypep
= VN_CAST(fromDtp, AssocArrayDType)) {
varp->dtypeFrom(adtypep->keyDTypep());
} else {
argsp->v3error("Illegal 'foreach' loop on " << fromDtp->prettyTypeName()
<< " data type");
VL_DO_DANGLING(nodep->unlinkFrBack()->deleteTree(), nodep);
return;
}
fromDtp = fromDtp->subDTypep();
}
// The parser validates we don't have "foreach (array[,,,])"
AstNode* const bodyp = nodep->stmtsp();
if (VN_IS(nodep, ConstraintForeach)) {
userIterateAndNext(bodyp, nullptr);
} else {
UASSERT_OBJ(newp, nodep, "foreach has no non-empty loop variable");
if (bodyp) {
lastBodyPointp->replaceWith(bodyp->unlinkFrBackWithNext());
} else {
lastBodyPointp->unlinkFrBack();
}
// if (debug()) newp->dumpTreeAndNext(cout, "- foreach-new: ");
nodep->replaceWith(newp);
if (lastBodyPointp) VL_DO_DANGLING(lastBodyPointp->deleteTree(), lastBodyPointp);
VL_DO_DANGLING(nodep->deleteTree(), nodep);
userIterateAndNext(bodyp, nullptr);
if (AstForeach* const loopp = VN_CAST(nodep, Foreach)) {
VL_DO_DANGLING(V3Begin::convertToWhile(loopp), nodep);
return;
}
}
AstNode* createForeachLoopRanged(AstNodeForeach* nodep, AstNode* bodysp, AstVar* varp,
const VNumRange& declRange) {
FileLine* const fl = varp->fileline();
AstNodeExpr* const leftp = new AstConst{fl, AstConst::Signed32{}, declRange.left()};
AstNodeExpr* const rightp = new AstConst{fl, AstConst::Signed32{}, declRange.right()};
AstNodeExpr* condp;
AstNodeExpr* incp;
if (declRange.left() < declRange.right()) {
condp = new AstLte{fl, new AstVarRef{fl, varp, VAccess::READ}, rightp};
incp = new AstAdd{fl, new AstConst{fl, AstConst::Signed32{}, 1},
new AstVarRef{fl, varp, VAccess::READ}};
} else {
condp = new AstGte{fl, new AstVarRef{fl, varp, VAccess::READ}, rightp};
incp = new AstSub{fl, new AstVarRef{fl, varp, VAccess::READ},
new AstConst{fl, AstConst::Signed32{}, 1}};
}
return createForeachLoop(nodep, bodysp, varp, leftp, condp, incp);
}
AstNode* createForeachLoop(AstNodeForeach* nodep, AstNode* bodysp, AstVar* varp,
AstNodeExpr* leftp, AstNodeExpr* condp, AstNodeExpr* incp) {
FileLine* const fl = varp->fileline();
auto* const whilep = new AstWhile{
fl, condp, bodysp, new AstAssign{fl, new AstVarRef{fl, varp, VAccess::WRITE}, incp}};
AstNode* const stmtsp = varp; // New statements for under new Begin
stmtsp->addNext(new AstAssign{fl, new AstVarRef{fl, varp, VAccess::WRITE}, leftp});
stmtsp->addNext(whilep);
return stmtsp;
}
void visit(AstNodeAssign* nodep) override {
// IEEE-2012 10.7, 11.8.2, 11.8.3, 11.5: (Careful of 11.8.1 which is

View File

@ -1,13 +1,17 @@
%Error: t/t_foreach_type_bad.v:19:18: Illegal to foreach loop on 'CLASSREFDTYPE 'Cls''
%Error: t/t_foreach_type_bad.v:19:18: Illegal 'foreach' loop on CLASSREFDTYPE 'Cls' data type
: ... note: In instance 't'
19 | foreach (c[i]);
| ^
%Error: t/t_foreach_type_bad.v:21:18: Illegal to foreach loop on basic 'BASICDTYPE 'real''
%Error: t/t_foreach_type_bad.v:21:18: Illegal 'foreach' loop on BASICDTYPE 'real' data type
: ... note: In instance 't'
21 | foreach (r[i]);
| ^
%Error: t/t_foreach_type_bad.v:23:21: Illegal to foreach loop on basic 'BASICDTYPE 'bit''
%Error: t/t_foreach_type_bad.v:23:21: Illegal 'foreach' loop on BASICDTYPE 'bit' data type
: ... note: In instance 't'
23 | foreach (b[i, j, k]);
| ^
%Error: t/t_foreach_type_bad.v:25:18: Illegal to foreach loop on basic 'BASICDTYPE 'real''
%Error: t/t_foreach_type_bad.v:25:18: Illegal 'foreach' loop on BASICDTYPE 'real' data type
: ... note: In instance 't'
25 | foreach (r[, i]);
| ^
%Error: Exiting due to