From cd0cec0e5d1f7bd62f79c73009e28ef78cdab533 Mon Sep 17 00:00:00 2001 From: Mariusz Glebocki Date: Tue, 30 Jan 2024 11:41:08 +0100 Subject: [PATCH] WIP: V3Expand parallelization --- src/V3Expand.cpp | 442 ++++++++++++++++++++++++++--------------------- 1 file changed, 249 insertions(+), 193 deletions(-) diff --git a/src/V3Expand.cpp b/src/V3Expand.cpp index 7a91c2a262..5af74e7436 100644 --- a/src/V3Expand.cpp +++ b/src/V3Expand.cpp @@ -30,13 +30,18 @@ #include "V3Expand.h" #include "V3Const.h" +#include "V3Mutex.h" #include "V3Stats.h" +#include "V3ThreadPool.h" VL_DEFINE_DEBUG_FUNCTIONS; //###################################################################### // Find nodes with side effects, to mark as non-expandable +// TODO(mglb): remove this and make V3Stats::addStat* thread-safe. V3Stats is used in V3Const and stuff explodes +V3Mutex constifyLock; + class ExpandOkVisitor final : public VNVisitor { private: // NODE STATE @@ -69,20 +74,18 @@ class ExpandOkVisitor final : public VNVisitor { //###################################################################### // Expand state, as a visitor of each AstNode -class ExpandVisitor final : public VNVisitor { +class ExpandVisitorWorker VL_NOT_FINAL : public VNVisitor { private: - // NODE STATE - // AstNode::user1() -> bool. Processed - const VNUser1InUse m_inuser1; - // STATE - for current visit position (use VL_RESTORER) AstNode* m_stmtp = nullptr; // Current statement +public: // STATE - across all visitors VDouble0 m_statWides; // Statistic tracking VDouble0 m_statWideWords; // Statistic tracking VDouble0 m_statWideLimited; // Statistic tracking +private: // METHODS // Use state that ExpandOkVisitor calculated bool isImpure(AstNode* nodep) { @@ -203,6 +206,7 @@ class ExpandVisitor final : public VNVisitor { new AstShiftL{fl, llowp, new AstConst{fl, static_cast(loffset)}, VL_EDATASIZE}}}; + V3LockGuard l{constifyLock}; newp = V3Const::constifyEditCpp(newp); } else { newp = llowp; @@ -353,33 +357,6 @@ class ExpandVisitor final : public VNVisitor { return true; } - // VISITORS - void visit(AstExtend* nodep) override { - if (nodep->user1SetOnce()) return; // Process once - iterateChildren(nodep); - if (nodep->isWide()) { - // See under ASSIGN(EXTEND) - } else { - if (isImpure(nodep)) return; - AstNodeExpr* const lhsp = nodep->lhsp()->unlinkFrBack(); - AstNodeExpr* newp = lhsp; - if (nodep->isQuad()) { - if (lhsp->isQuad()) { - lhsp->dtypeFrom(nodep); // Just mark it, else nop - } else if (lhsp->isWide()) { - nodep->v3fatalSrc("extending larger thing into smaller?"); - } else { - UINFO(8, " EXTEND(q<-l) " << nodep << endl); - newp = new AstCCast{nodep->fileline(), lhsp, nodep}; - } - } else { // Long - UASSERT_OBJ(!(lhsp->isQuad() || lhsp->isWide()), nodep, - "extending larger thing into smaller?"); - lhsp->dtypeFrom(nodep); // Just mark it, else nop - } - VL_DO_DANGLING(replaceWithDelete(nodep, newp), nodep); - } - } bool expandWide(AstNodeAssign* nodep, AstExtend* rhsp) { UINFO(8, " Wordize ASSIGN(EXTEND) " << nodep << endl); if (!doExpandWide(nodep)) return false; @@ -393,107 +370,6 @@ class ExpandVisitor final : public VNVisitor { return true; } - void visit(AstSel* nodep) override { - if (nodep->user1SetOnce()) return; // Process once - iterateChildren(nodep); - // Remember, Sel's may have non-integer rhs, so need to optimize for that! - UASSERT_OBJ(nodep->widthMin() == nodep->widthConst(), nodep, "Width mismatch"); - if (VN_IS(nodep->backp(), NodeAssign) - && nodep == VN_AS(nodep->backp(), NodeAssign)->lhsp()) { - // Sel is an LHS assignment select - } else if (nodep->isWide()) { - // See under ASSIGN(WIDE) - } else if (nodep->fromp()->isWide()) { - if (isImpure(nodep)) return; - UINFO(8, " SEL(wide) " << nodep << endl); - UASSERT_OBJ(nodep->widthConst() <= 64, nodep, "Inconsistent width"); - // Selection amounts - // Check for constant shifts & save some constification work later. - // Grab lowest bit(s) - FileLine* const nfl = nodep->fileline(); - FileLine* const lfl = nodep->lsbp()->fileline(); - FileLine* const ffl = nodep->fromp()->fileline(); - AstNodeExpr* lowwordp - = newWordSel(ffl, nodep->fromp()->cloneTreePure(true), nodep->lsbp()); - if (nodep->isQuad() && !lowwordp->isQuad()) { - lowwordp = new AstCCast{nfl, lowwordp, nodep}; - } - AstNodeExpr* const lowp - = new AstShiftR{nfl, lowwordp, newSelBitBit(nodep->lsbp()), nodep->width()}; - // If > 1 bit, we might be crossing the word boundary - AstNodeExpr* midp = nullptr; - if (nodep->widthConst() > 1) { - const uint32_t midMsbOffset - = std::min(nodep->widthConst(), VL_EDATASIZE) - 1; - AstNodeExpr* const midMsbp = new AstAdd{lfl, new AstConst{lfl, midMsbOffset}, - nodep->lsbp()->cloneTreePure(true)}; - AstNodeExpr* midwordp = // SEL(from,[midwordnum]) - newWordSel(ffl, nodep->fromp()->cloneTreePure(true), midMsbp, 0); - // newWordSel clones the index, so delete it - VL_DO_DANGLING(midMsbp->deleteTree(), midMsbp); - if (nodep->isQuad() && !midwordp->isQuad()) { - midwordp = new AstCCast{nfl, midwordp, nodep}; - } - AstNodeExpr* const midshiftp = new AstSub{lfl, new AstConst{lfl, VL_EDATASIZE}, - newSelBitBit(nodep->lsbp())}; - // If we're selecting bit zero, then all 32 bits in the mid word - // get shifted << by 32 bits, so ignore them. - const V3Number zero{nodep, longOrQuadWidth(nodep)}; - midp = new AstCond{ - nfl, - // lsb % VL_EDATASIZE == 0 ? - - new AstEq{nfl, new AstConst{nfl, 0}, newSelBitBit(nodep->lsbp())}, - // 0 : - new AstConst{nfl, zero}, - // midword >> (VL_EDATASIZE - (lbs % VL_EDATASIZE)) - new AstShiftL{nfl, midwordp, midshiftp, nodep->width()}}; - } - // If > 32 bits, we might be crossing the second word boundary - AstNodeExpr* hip = nullptr; - if (nodep->widthConst() > VL_EDATASIZE) { - const uint32_t hiMsbOffset = nodep->widthConst() - 1; - AstNodeExpr* const hiMsbp = new AstAdd{lfl, new AstConst{lfl, hiMsbOffset}, - nodep->lsbp()->cloneTreePure(true)}; - AstNodeExpr* hiwordp = // SEL(from,[hiwordnum]) - newWordSel(ffl, nodep->fromp()->cloneTreePure(true), hiMsbp); - // newWordSel clones the index, so delete it - VL_DO_DANGLING(hiMsbp->deleteTree(), hiMsbp); - if (nodep->isQuad() && !hiwordp->isQuad()) { - hiwordp = new AstCCast{nfl, hiwordp, nodep}; - } - AstNodeExpr* const hishiftp = new AstCond{ - nfl, - // lsb % VL_EDATASIZE == 0 ? - new AstEq{nfl, new AstConst{nfl, 0}, newSelBitBit(nodep->lsbp())}, - // VL_EDATASIZE : - new AstConst{lfl, VL_EDATASIZE}, - // 64 - (lbs % VL_EDATASIZE) - new AstSub{lfl, new AstConst{lfl, 64}, newSelBitBit(nodep->lsbp())}}; - hip = new AstShiftL{nfl, hiwordp, hishiftp, nodep->width()}; - } - - AstNodeExpr* newp = lowp; - if (midp) newp = new AstOr{nfl, midp, newp}; - if (hip) newp = new AstOr{nfl, hip, newp}; - newp->dtypeFrom(nodep); - VL_DO_DANGLING(replaceWithDelete(nodep, newp), nodep); - } else { // Long/Quad from Long/Quad - // No isImpure() check - can handle side effects in below - UINFO(8, " SEL->SHIFT " << nodep << endl); - FileLine* const fl = nodep->fileline(); - AstNodeExpr* fromp = nodep->fromp()->unlinkFrBack(); - AstNodeExpr* const lsbp = nodep->lsbp()->unlinkFrBack(); - if (nodep->isQuad() && !fromp->isQuad()) fromp = new AstCCast{fl, fromp, nodep}; - // {large}>>32 requires 64-bit shift operation; then cast - AstNodeExpr* newp = new AstShiftR{fl, fromp, dropCondBound(lsbp), fromp->width()}; - newp->dtypeFrom(fromp); - if (!nodep->isQuad() && fromp->isQuad()) newp = new AstCCast{fl, newp, nodep}; - newp->dtypeFrom(nodep); - VL_DO_DANGLING(replaceWithDelete(nodep, newp), nodep); - } - } - bool expandWide(AstNodeAssign* nodep, AstSel* rhsp) { UASSERT_OBJ(nodep->widthMin() == rhsp->widthConst(), nodep, "Width mismatch"); if (!doExpandWide(nodep)) return false; @@ -581,6 +457,7 @@ class ExpandVisitor final : public VNVisitor { cleanmask.setMask(VL_BITBIT_E(destp->widthMin())); newp = new AstAnd{lfl, newp, new AstConst{lfl, cleanmask}}; } + V3LockGuard l{constifyLock}; AstNodeExpr* const orp = V3Const::constifyEditCpp(new AstOr{lfl, oldvalp, newp}); addWordAssign(nodep, w, destp, orp); @@ -604,6 +481,7 @@ class ExpandVisitor final : public VNVisitor { lfl, rhsp, new AstConst{lfl, static_cast(lsb)}, destp->width()}; AstNodeExpr* const cleaned = new AstAnd{lfl, shifted, new AstConst{lfl, cleanmask}}; + V3LockGuard l{constifyLock}; AstNodeExpr* const orp = V3Const::constifyEditCpp(new AstOr{lfl, oldvalp, cleaned}); insertBefore(nodep, new AstAssign{nfl, destp, orp}); @@ -684,27 +562,6 @@ class ExpandVisitor final : public VNVisitor { } } } - - void visit(AstConcat* nodep) override { - if (nodep->user1SetOnce()) return; // Process once - iterateChildren(nodep); - if (nodep->isWide()) { - // See under ASSIGN(WIDE) - } else { - // No isImpure() check - can handle side effects in below - UINFO(8, " CONCAT " << nodep << endl); - FileLine* const fl = nodep->fileline(); - AstNodeExpr* lhsp = nodep->lhsp()->unlinkFrBack(); - AstNodeExpr* rhsp = nodep->rhsp()->unlinkFrBack(); - const uint32_t rhsshift = rhsp->widthMin(); - if (nodep->isQuad() && !lhsp->isQuad()) lhsp = new AstCCast{fl, lhsp, nodep}; - if (nodep->isQuad() && !rhsp->isQuad()) rhsp = new AstCCast{fl, rhsp, nodep}; - AstNodeExpr* const newp = new AstOr{ - fl, new AstShiftL{fl, lhsp, new AstConst{fl, rhsshift}, nodep->width()}, rhsp}; - newp->dtypeFrom(nodep); // Unsigned - VL_DO_DANGLING(replaceWithDelete(nodep, newp), nodep); - } - } bool expandWide(AstNodeAssign* nodep, AstConcat* rhsp) { UINFO(8, " Wordize ASSIGN(CONCAT) " << nodep << endl); if (!doExpandWide(rhsp)) return false; @@ -723,44 +580,6 @@ class ExpandVisitor final : public VNVisitor { return true; } - void visit(AstReplicate* nodep) override { - if (nodep->user1SetOnce()) return; // Process once - iterateChildren(nodep); - if (nodep->isWide()) { - // See under ASSIGN(WIDE) - } else { - if (isImpure(nodep)) return; - FileLine* const fl = nodep->fileline(); - AstNodeExpr* lhsp = nodep->lhsp()->unlinkFrBack(); - AstNodeExpr* newp; - const int lhswidth = lhsp->widthMin(); - if (lhswidth == 1) { - UINFO(8, " REPLICATE(w1) " << nodep << endl); - newp = new AstNegate{fl, lhsp}; - } else { - UINFO(8, " REPLICATE " << nodep << endl); - const AstConst* const constp = VN_AS(nodep->rhsp(), Const); - UASSERT_OBJ(constp, nodep, - "Replication value isn't a constant. Checked earlier!"); - const uint32_t times = constp->toUInt(); - if (nodep->isQuad() && !lhsp->isQuad()) { lhsp = new AstCCast{fl, lhsp, nodep}; } - newp = lhsp->cloneTreePure(true); - for (unsigned repnum = 1; repnum < times; repnum++) { - const int rhsshift = repnum * lhswidth; - newp = new AstOr{ - fl, - new AstShiftL{fl, lhsp->cloneTreePure(true), - new AstConst{fl, static_cast(rhsshift)}, - nodep->width()}, - newp}; - newp->dtypeFrom(nodep); // Unsigned - } - VL_DO_DANGLING(lhsp->deleteTree(), lhsp); // Never used - } - newp->dtypeFrom(nodep); // Unsigned - VL_DO_DANGLING(replaceWithDelete(nodep, newp), nodep); - } - } bool expandWide(AstNodeAssign* nodep, AstReplicate* rhsp) { UINFO(8, " Wordize ASSIGN(REPLICATE) " << nodep << endl); if (!doExpandWide(rhsp)) return false; @@ -810,6 +629,196 @@ class ExpandVisitor final : public VNVisitor { VL_DO_DANGLING(replaceWithDelete(nodep, newp), nodep); } } + +public: + // VISITORS + void visit(AstExtend* nodep) override { + if (nodep->user1SetOnce()) return; // Process once + iterateChildren(nodep); + if (nodep->isWide()) { + // See under ASSIGN(EXTEND) + } else { + if (isImpure(nodep)) return; + AstNodeExpr* const lhsp = nodep->lhsp()->unlinkFrBack(); + AstNodeExpr* newp = lhsp; + if (nodep->isQuad()) { + if (lhsp->isQuad()) { + lhsp->dtypeFrom(nodep); // Just mark it, else nop + } else if (lhsp->isWide()) { + nodep->v3fatalSrc("extending larger thing into smaller?"); + } else { + UINFO(8, " EXTEND(q<-l) " << nodep << endl); + newp = new AstCCast{nodep->fileline(), lhsp, nodep}; + } + } else { // Long + UASSERT_OBJ(!(lhsp->isQuad() || lhsp->isWide()), nodep, + "extending larger thing into smaller?"); + lhsp->dtypeFrom(nodep); // Just mark it, else nop + } + VL_DO_DANGLING(replaceWithDelete(nodep, newp), nodep); + } + } + + void visit(AstSel* nodep) override { + if (nodep->user1SetOnce()) return; // Process once + iterateChildren(nodep); + // Remember, Sel's may have non-integer rhs, so need to optimize for that! + UASSERT_OBJ(nodep->widthMin() == nodep->widthConst(), nodep, "Width mismatch"); + if (VN_IS(nodep->backp(), NodeAssign) + && nodep == VN_AS(nodep->backp(), NodeAssign)->lhsp()) { + // Sel is an LHS assignment select + } else if (nodep->isWide()) { + // See under ASSIGN(WIDE) + } else if (nodep->fromp()->isWide()) { + if (isImpure(nodep)) return; + UINFO(8, " SEL(wide) " << nodep << endl); + UASSERT_OBJ(nodep->widthConst() <= 64, nodep, "Inconsistent width"); + // Selection amounts + // Check for constant shifts & save some constification work later. + // Grab lowest bit(s) + FileLine* const nfl = nodep->fileline(); + FileLine* const lfl = nodep->lsbp()->fileline(); + FileLine* const ffl = nodep->fromp()->fileline(); + AstNodeExpr* lowwordp + = newWordSel(ffl, nodep->fromp()->cloneTreePure(true), nodep->lsbp()); + if (nodep->isQuad() && !lowwordp->isQuad()) { + lowwordp = new AstCCast{nfl, lowwordp, nodep}; + } + AstNodeExpr* const lowp + = new AstShiftR{nfl, lowwordp, newSelBitBit(nodep->lsbp()), nodep->width()}; + // If > 1 bit, we might be crossing the word boundary + AstNodeExpr* midp = nullptr; + if (nodep->widthConst() > 1) { + const uint32_t midMsbOffset + = std::min(nodep->widthConst(), VL_EDATASIZE) - 1; + AstNodeExpr* const midMsbp = new AstAdd{lfl, new AstConst{lfl, midMsbOffset}, + nodep->lsbp()->cloneTreePure(true)}; + AstNodeExpr* midwordp = // SEL(from,[midwordnum]) + newWordSel(ffl, nodep->fromp()->cloneTreePure(true), midMsbp, 0); + // newWordSel clones the index, so delete it + VL_DO_DANGLING(midMsbp->deleteTree(), midMsbp); + if (nodep->isQuad() && !midwordp->isQuad()) { + midwordp = new AstCCast{nfl, midwordp, nodep}; + } + AstNodeExpr* const midshiftp = new AstSub{lfl, new AstConst{lfl, VL_EDATASIZE}, + newSelBitBit(nodep->lsbp())}; + // If we're selecting bit zero, then all 32 bits in the mid word + // get shifted << by 32 bits, so ignore them. + const V3Number zero{nodep, longOrQuadWidth(nodep)}; + midp = new AstCond{ + nfl, + // lsb % VL_EDATASIZE == 0 ? + + new AstEq{nfl, new AstConst{nfl, 0}, newSelBitBit(nodep->lsbp())}, + // 0 : + new AstConst{nfl, zero}, + // midword >> (VL_EDATASIZE - (lbs % VL_EDATASIZE)) + new AstShiftL{nfl, midwordp, midshiftp, nodep->width()}}; + } + // If > 32 bits, we might be crossing the second word boundary + AstNodeExpr* hip = nullptr; + if (nodep->widthConst() > VL_EDATASIZE) { + const uint32_t hiMsbOffset = nodep->widthConst() - 1; + AstNodeExpr* const hiMsbp = new AstAdd{lfl, new AstConst{lfl, hiMsbOffset}, + nodep->lsbp()->cloneTreePure(true)}; + AstNodeExpr* hiwordp = // SEL(from,[hiwordnum]) + newWordSel(ffl, nodep->fromp()->cloneTreePure(true), hiMsbp); + // newWordSel clones the index, so delete it + VL_DO_DANGLING(hiMsbp->deleteTree(), hiMsbp); + if (nodep->isQuad() && !hiwordp->isQuad()) { + hiwordp = new AstCCast{nfl, hiwordp, nodep}; + } + AstNodeExpr* const hishiftp = new AstCond{ + nfl, + // lsb % VL_EDATASIZE == 0 ? + new AstEq{nfl, new AstConst{nfl, 0}, newSelBitBit(nodep->lsbp())}, + // VL_EDATASIZE : + new AstConst{lfl, VL_EDATASIZE}, + // 64 - (lbs % VL_EDATASIZE) + new AstSub{lfl, new AstConst{lfl, 64}, newSelBitBit(nodep->lsbp())}}; + hip = new AstShiftL{nfl, hiwordp, hishiftp, nodep->width()}; + } + + AstNodeExpr* newp = lowp; + if (midp) newp = new AstOr{nfl, midp, newp}; + if (hip) newp = new AstOr{nfl, hip, newp}; + newp->dtypeFrom(nodep); + VL_DO_DANGLING(replaceWithDelete(nodep, newp), nodep); + } else { // Long/Quad from Long/Quad + // No isImpure() check - can handle side effects in below + UINFO(8, " SEL->SHIFT " << nodep << endl); + FileLine* const fl = nodep->fileline(); + AstNodeExpr* fromp = nodep->fromp()->unlinkFrBack(); + AstNodeExpr* const lsbp = nodep->lsbp()->unlinkFrBack(); + if (nodep->isQuad() && !fromp->isQuad()) fromp = new AstCCast{fl, fromp, nodep}; + // {large}>>32 requires 64-bit shift operation; then cast + AstNodeExpr* newp = new AstShiftR{fl, fromp, dropCondBound(lsbp), fromp->width()}; + newp->dtypeFrom(fromp); + if (!nodep->isQuad() && fromp->isQuad()) newp = new AstCCast{fl, newp, nodep}; + newp->dtypeFrom(nodep); + VL_DO_DANGLING(replaceWithDelete(nodep, newp), nodep); + } + } + + void visit(AstConcat* nodep) override { + if (nodep->user1SetOnce()) return; // Process once + iterateChildren(nodep); + if (nodep->isWide()) { + // See under ASSIGN(WIDE) + } else { + // No isImpure() check - can handle side effects in below + UINFO(8, " CONCAT " << nodep << endl); + FileLine* const fl = nodep->fileline(); + AstNodeExpr* lhsp = nodep->lhsp()->unlinkFrBack(); + AstNodeExpr* rhsp = nodep->rhsp()->unlinkFrBack(); + const uint32_t rhsshift = rhsp->widthMin(); + if (nodep->isQuad() && !lhsp->isQuad()) lhsp = new AstCCast{fl, lhsp, nodep}; + if (nodep->isQuad() && !rhsp->isQuad()) rhsp = new AstCCast{fl, rhsp, nodep}; + AstNodeExpr* const newp = new AstOr{ + fl, new AstShiftL{fl, lhsp, new AstConst{fl, rhsshift}, nodep->width()}, rhsp}; + newp->dtypeFrom(nodep); // Unsigned + VL_DO_DANGLING(replaceWithDelete(nodep, newp), nodep); + } + } + + void visit(AstReplicate* nodep) override { + if (nodep->user1SetOnce()) return; // Process once + iterateChildren(nodep); + if (nodep->isWide()) { + // See under ASSIGN(WIDE) + } else { + if (isImpure(nodep)) return; + FileLine* const fl = nodep->fileline(); + AstNodeExpr* lhsp = nodep->lhsp()->unlinkFrBack(); + AstNodeExpr* newp; + const int lhswidth = lhsp->widthMin(); + if (lhswidth == 1) { + UINFO(8, " REPLICATE(w1) " << nodep << endl); + newp = new AstNegate{fl, lhsp}; + } else { + UINFO(8, " REPLICATE " << nodep << endl); + const AstConst* const constp = VN_AS(nodep->rhsp(), Const); + UASSERT_OBJ(constp, nodep, + "Replication value isn't a constant. Checked earlier!"); + const uint32_t times = constp->toUInt(); + if (nodep->isQuad() && !lhsp->isQuad()) { lhsp = new AstCCast{fl, lhsp, nodep}; } + newp = lhsp->cloneTreePure(true); + for (unsigned repnum = 1; repnum < times; repnum++) { + const int rhsshift = repnum * lhswidth; + newp = new AstOr{ + fl, + new AstShiftL{fl, lhsp->cloneTreePure(true), + new AstConst{fl, static_cast(rhsshift)}, + nodep->width()}, + newp}; + newp->dtypeFrom(nodep); // Unsigned + } + VL_DO_DANGLING(lhsp->deleteTree(), lhsp); // Never used + } + newp->dtypeFrom(nodep); // Unsigned + VL_DO_DANGLING(replaceWithDelete(nodep, newp), nodep); + } + } void visit(AstEq* nodep) override { visitEqNeq(nodep); } void visit(AstNeq* nodep) override { visitEqNeq(nodep); } @@ -946,10 +955,57 @@ class ExpandVisitor final : public VNVisitor { void visit(AstVar*) override {} // Don't hit varrefs under vars void visit(AstNode* nodep) override { iterateChildren(nodep); } +public: + // CONSTRUCTORS + ExpandVisitorWorker() = default; +}; + +class ExpandVisitor final : public ExpandVisitorWorker { +private: + // NODE STATE + // AstNode::user1() -> bool. Processed + const VNUser1InUse m_inuser1; + +protected: + using ExpandVisitorWorker::visit; + + std::vector m_workerVisitors VL_GUARDED_BY(m_workerVisitorsLock); + V3Mutex m_workerVisitorsLock; + std::vector> m_futures; + + void visit(AstCFunc* nodep) override VL_REQUIRES_UNLOCKED(m_workerVisitorsLock) { + m_futures.push_back(V3ThreadPool::s().enqueue([nodep]() VL_REQUIRES_UNLOCKED(m_workerVisitorsLock) { + // thread_local ExpandVisitorWorker* myWorkerp = nullptr; + // if (myWorkerp == nullptr) { + // V3LockGuard lock(m_workerVisitorsLock); + // m_workerVisitors.emplace_back(); + // myWorkerp = &m_workerVisitors.back(); + // std::cout << "Created worker #" << m_workerVisitors.size() << std::endl; + // } + ExpandVisitorWorker myWorkerp{}; + myWorkerp.visit(nodep); + })); + } + + void visit(AstNodeModule* nodep) override VL_REQUIRES_UNLOCKED(m_workerVisitorsLock) { + iterateChildren(nodep); + for (const auto& f: m_futures) { f.wait(); } + m_futures.clear(); + V3LockGuard lock(m_workerVisitorsLock); + std::cout << "Completed work in a module using " << m_workerVisitors.size() << " workers" << std::endl; + std::cout << "In progress: " << V3ThreadPool::s().m_jobsInProgress << std::endl; + } + public: // CONSTRUCTORS explicit ExpandVisitor(AstNetlist* nodep) { iterate(nodep); } - ~ExpandVisitor() override { + ~ExpandVisitor() override VL_REQUIRES_UNLOCKED(m_workerVisitorsLock) { + V3LockGuard lock(m_workerVisitorsLock); + for (const auto& worker: m_workerVisitors) { + m_statWides += worker.m_statWides; + m_statWideLimited += worker.m_statWideLimited; + m_statWideWords += worker.m_statWideWords; + } V3Stats::addStat("Optimizations, expand wides", m_statWides); V3Stats::addStat("Optimizations, expand wide words", m_statWideWords); V3Stats::addStat("Optimizations, expand limited", m_statWideLimited);