diff --git a/src/V3Begin.cpp b/src/V3Begin.cpp index 8e37226d2..8d97efb8f 100644 --- a/src/V3Begin.cpp +++ b/src/V3Begin.cpp @@ -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; +} diff --git a/src/V3Begin.h b/src/V3Begin.h index ccb850209..ac65da30f 100644 --- a/src/V3Begin.h +++ b/src/V3Begin.h @@ -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 diff --git a/src/V3Task.cpp b/src/V3Task.cpp index 0b88636b4..31d286d80 100644 --- a/src/V3Task.cpp +++ b/src/V3Task.cpp @@ -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"); diff --git a/src/V3Width.cpp b/src/V3Width.cpp index 3a7856c11..66289b0e3 100644 --- a/src/V3Width.cpp +++ b/src/V3Width.cpp @@ -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 diff --git a/test_regress/t/t_foreach_type_bad.out b/test_regress/t/t_foreach_type_bad.out index df9bcdd48..2330a227e 100644 --- a/test_regress/t/t_foreach_type_bad.out +++ b/test_regress/t/t_foreach_type_bad.out @@ -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