Пример #1
0
    virtual void visit(AstNodeClassDType* nodep, AstNUser*) {
	if (m_traVscp) {
	    if (nodep->packed() && !v3Global.opt.traceStructs()) {
		// Everything downstream is packed, so deal with as one trace unit
		// This may not be the nicest for user presentation, but is a much faster way to trace
		addTraceDecl(VNumRange());
	    } else {
		if (!nodep->packed()) {
		    addIgnore("Unsupported: Unpacked struct/union");
		} else {
		    for (AstMemberDType* itemp = nodep->membersp(); itemp; itemp=itemp->nextp()->castMemberDType()) {
			AstNodeDType* subtypep = itemp->subDTypep()->skipRefp();
			string oldShowname = m_traShowname;
			AstNode* oldValuep = m_traValuep;
			{
			    m_traShowname += string(" ")+itemp->prettyName();
			    if (nodep->castStructDType()) {
				m_traValuep = new AstSel(nodep->fileline(), m_traValuep->cloneTree(true),
							 itemp->lsb(), subtypep->width());
				subtypep->accept(*this);
				m_traValuep->deleteTree(); m_traValuep = NULL;
			    } else { // Else union, replicate fields
				subtypep->accept(*this);
			    }
			}
			m_traShowname = oldShowname;
			m_traValuep = oldValuep;
		    }
		}
	    }
	}
    }
Пример #2
0
    // VISITORS
    virtual void visit(AstNodeVarRef* nodep, AstNUser*) {
	if (nodep->varScopep() == m_elimVarScp) {
	    // Substitute in the new tree
	    // It's possible we substitute into something that will be reduced more later
	    // however, as we never delete the top Always/initial statement, all should be well.
	    m_didReplace = true;
	    if (nodep->lvalue()) nodep->v3fatalSrc("Can't replace lvalue assignments with const var");
	    AstNode* substp = m_replaceTreep->cloneTree(false);
	    if (nodep->castNodeVarRef()
		&& substp->castNodeVarRef()
		&& nodep->same(substp)) {
		// Prevent a infinite loop...
		substp->v3fatalSrc("Replacing node with itself; perhaps circular logic?");
	    }
	    // Which fileline() to use?
	    // If replacing with logic, an error/warning is likely to want to point to the logic
	    // IE what we're replacing with.
	    // However a VARREF should point to the original as it's otherwise confusing
	    // to throw warnings that point to a PIN rather than where the pin us used.
	    if (substp->castVarRef()) substp->fileline(nodep->fileline());
	    // Make the substp an rvalue like nodep. This facilitate the hashing in dedupe.
	    if (AstNodeVarRef* varrefp = substp->castNodeVarRef()) varrefp->lvalue(false);
	    nodep->replaceWith(substp);
	    nodep->deleteTree(); VL_DANGLING(nodep);
	}
    }
Пример #3
0
    void replaceCaseFast(AstCase* nodep) {
	// CASEx(cexpr,....
	// ->  tree of IF(msb,  IF(msb-1, 11, 10)
	//                      IF(msb-1, 01, 00))
	AstNode* cexprp = nodep->exprp()->unlinkFrBack();

	if (debug()>=9) {
	    for (uint32_t i=0; i<(1UL<<m_caseWidth); i++) {
		if (AstNode* itemp = m_valueItem[i]) {
		    UINFO(9,"Value "<<hex<<i<<" "<<itemp<<endl);
		}
	    }
	}

	// Handle any assertions
	replaceCaseParallel(nodep, m_caseNoOverlapsAllCovered);

	AstNode::user3ClearTree();
	AstNode* ifrootp = replaceCaseFastRecurse(cexprp, m_caseWidth-1, 0UL);
	// Case expressions can't be linked twice, so clone them
	if (ifrootp && !ifrootp->user3()) ifrootp = ifrootp->cloneTree(true);

	if (ifrootp) nodep->replaceWith(ifrootp);
	else nodep->unlinkFrBack();
	nodep->deleteTree(); nodep=NULL;
	cexprp->deleteTree(); cexprp=NULL;
	if (debug()>=9) ifrootp->dumpTree(cout,"    _simp: ");
    }
Пример #4
0
    virtual void visit(AstPackArrayDType* nodep, AstNUser*) {
	if (m_traVscp) {
	    if (!v3Global.opt.traceStructs()) {
		// Everything downstream is packed, so deal with as one trace unit
		// This may not be the nicest for user presentation, but is a much faster way to trace
		addTraceDecl(VNumRange());
	    } else {
		AstNodeDType* subtypep = nodep->subDTypep()->skipRefp();
		for (int i=nodep->lsb(); i<=nodep->msb(); ++i) {
		    string oldShowname = m_traShowname;
		    AstNode* oldValuep = m_traValuep;
		    {
			m_traShowname += string("(")+cvtToStr(i)+string(")");
			m_traValuep = new AstSel(nodep->fileline(), m_traValuep->cloneTree(true),
						 (i - nodep->lsb())*subtypep->width(),
						 subtypep->width());
			subtypep->accept(*this);
			m_traValuep->deleteTree(); m_traValuep = NULL;
		    }
		    m_traShowname = oldShowname;
		    m_traValuep = oldValuep;
		}
	    }
	}
    }
Пример #5
0
    virtual void visit(AstUnpackArrayDType* nodep, AstNUser*) {
	// Note more specific dtypes above
	if (m_traVscp) {
	    if ((int)nodep->arrayUnpackedElements() > v3Global.opt.traceMaxArray()) {
		addIgnore("Wide memory > --trace-max-array ents");
	    } else if (nodep->subDTypep()->skipRefp()->castBasicDType()  // Nothing lower than this array
		       && m_traVscp->dtypep()->skipRefp() == nodep) {  // Nothing above this array
		// Simple 1-D array, use exising V3EmitC runtime loop rather than unrolling
		// This will put "(index)" at end of signal name for us
		addTraceDecl(nodep->declRange());
	    } else {
		// Unroll now, as have no other method to get right signal names
		AstNodeDType* subtypep = nodep->subDTypep()->skipRefp();
		for (int i=nodep->lsb(); i<=nodep->msb(); ++i) {
		    string oldShowname = m_traShowname;
		    AstNode* oldValuep = m_traValuep;
		    {
			m_traShowname += string("(")+cvtToStr(i)+string(")");
			m_traValuep = new AstArraySel(nodep->fileline(), m_traValuep->cloneTree(true),
						      i - nodep->lsb());

			subtypep->accept(*this);
			m_traValuep->deleteTree(); m_traValuep = NULL;
		    }
		    m_traShowname = oldShowname;
		    m_traValuep = oldValuep;
		}
	    }
	}
    }
Пример #6
0
    virtual void visit(AstCoverToggle* nodep) {
	//nodep->dumpTree(cout,"ct:");
	//COVERTOGGLE(INC, ORIG, CHANGE) ->
	//   IF(ORIG ^ CHANGE) { INC; CHANGE = ORIG; }
	AstNode* incp = nodep->incp()->unlinkFrBack();
	AstNode* origp = nodep->origp()->unlinkFrBack();
	AstNode* changep = nodep->changep()->unlinkFrBack();
	AstIf* newp = new AstIf(nodep->fileline(),
				new AstXor(nodep->fileline(),
					   origp,
					   changep),
				incp, NULL);
	// We could add another IF to detect posedges, and only increment if so.
	// It's another whole branch though verus a potential memory miss.
	// We'll go with the miss.
	newp->addIfsp(new AstAssign(nodep->fileline(),
				    changep->cloneTree(false),
				    origp->cloneTree(false)));
	nodep->replaceWith(newp); nodep->deleteTree(); VL_DANGLING(nodep);
    }
Пример #7
0
    // VISITORS  //========== Case assertions
    virtual void visit(AstCase* nodep, AstNUser*) {
	nodep->iterateChildren(*this);
	if (!nodep->user1Inc()) {
	    bool has_default=false;
	    for (AstCaseItem* itemp = nodep->itemsp(); itemp; itemp=itemp->nextp()->castCaseItem()) {
		if (itemp->isDefault()) has_default=true;
	    }
	    if (nodep->fullPragma() || nodep->priorityPragma()) {
		// Simply need to add a default if there isn't one already
		++m_statAsFull;
		if (!has_default) {
		    nodep->addItemsp(new AstCaseItem(nodep->fileline(), NULL/*DEFAULT*/,
						     newFireAssert(nodep, "synthesis full_case, but non-match found")));
		}
	    }
	    if (nodep->parallelPragma() || nodep->uniquePragma() || nodep->unique0Pragma()) {
		// Need to check that one, and only one of the case items match at any moment
		// If there's a default, we allow none to match, else exactly one must match
		++m_statAsFull;
		if (!has_default && !nodep->itemsp()) {
		    // Not parallel, but harmlessly so.
		} else {
		    AstNode* propp = NULL;
		    for (AstCaseItem* itemp = nodep->itemsp(); itemp; itemp=itemp->nextp()->castCaseItem()) {
			for (AstNode* icondp = itemp->condsp(); icondp!=NULL; icondp=icondp->nextp()) {
			    AstNode* onep = new AstEq(icondp->fileline(),
						      nodep->exprp()->cloneTree(false),
						      icondp->cloneTree(false));
			    if (propp) propp = new AstConcat(icondp->fileline(), onep, propp);
			    else propp = onep;
			}
		    }
		    bool allow_none = has_default || nodep->unique0Pragma();
		    AstNode* ohot = (allow_none
				     ? (new AstOneHot0(nodep->fileline(), propp))->castNode()
				     : (new AstOneHot (nodep->fileline(), propp))->castNode());
		    AstIf* ifp = new AstIf (nodep->fileline(),
					    new AstLogNot (nodep->fileline(), ohot),
					    newFireAssert(nodep, "synthesis parallel_case, but multiple matches found"),
					    NULL);
		    ifp->branchPred(AstBranchPred::BP_UNLIKELY);
		    nodep->addNotParallelp(ifp);
		}
	    }
	}
    }
Пример #8
0
    void addTraceDecl(const VNumRange& arrayRange) {
	VNumRange bitRange;
	AstBasicDType* bdtypep = m_traValuep->dtypep()->basicp();
	if (bdtypep) bitRange = bdtypep->nrange();
	AstTraceDecl* declp = new AstTraceDecl(m_traVscp->fileline(), m_traShowname, m_traValuep,
					       bitRange, arrayRange);

	if (m_initSubStmts && v3Global.opt.outputSplitCTrace()
	    && m_initSubStmts > v3Global.opt.outputSplitCTrace()) {
	    m_initSubFuncp = newCFuncSub(m_initFuncp);
	    m_initSubStmts = 0;
	}

	m_initSubFuncp->addStmtsp(declp);
	m_initSubStmts += EmitCBaseCounterVisitor(declp).count();

	m_chgFuncp->addStmtsp(new AstTraceInc(m_traVscp->fileline(), declp, m_traValuep->cloneTree(true)));
	// The full version will get constructed in V3Trace
    }
Пример #9
0
    bool forUnroller(AstNode* nodep,
		     AstAssign* initp,
		     AstNode* condp,
		     AstNode* precondsp,
		     AstNode* incp, AstNode* bodysp) {
	V3Number loopValue = V3Number(nodep->fileline());
	if (!simulateTree(initp->rhsp(), NULL, initp, loopValue)) {
	    return false;
	}
	AstNode* stmtsp = NULL;
	if (initp) {
	    initp->unlinkFrBack();	// Always a single statement; nextp() may be nodep
	    // Don't add to list, we do it once, and setting loop index isn't needed as we're constant propagating it
	}
	if (precondsp) {
	    precondsp->unlinkFrBackWithNext();
	    // cppcheck-suppress nullPointer  // addNextNull deals with it
	    stmtsp = stmtsp->addNextNull(precondsp);
	}
	if (bodysp) {
	    bodysp->unlinkFrBackWithNext();
	    // cppcheck-suppress nullPointer  // addNextNull deals with it
	    stmtsp = stmtsp->addNextNull(bodysp);  // Maybe null if no body
	}
	if (incp && !nodep->castGenFor()) {  // Generates don't need to increment loop index
	    incp->unlinkFrBackWithNext();
	    // cppcheck-suppress nullPointer  // addNextNull deals with it
	    stmtsp = stmtsp->addNextNull(incp);  // Maybe null if no body
	}
	// Mark variable to disable some later warnings
	m_forVarp->usedLoopIdx(true);

	AstNode* newbodysp = NULL;
	++m_statLoops;
	if (stmtsp) {
	    int times = 0;
	    while (1) {
		UINFO(8,"      Looping "<<loopValue<<endl);
		V3Number res = V3Number(nodep->fileline());
		if (!simulateTree(condp, &loopValue, NULL, res)) {
		    nodep->v3error("Loop unrolling failed.");
		    return false;
		}
		if (!res.isEqOne()) {
		    break;  // Done with the loop
		}
		else {
		    // Replace iterator values with constant.
		    AstNode* oneloopp = stmtsp->cloneTree(true);

		    m_varValuep = new AstConst(nodep->fileline(), loopValue);

		    // Iteration requires a back, so put under temporary node
		    if (oneloopp) {
			AstBegin* tempp = new AstBegin(oneloopp->fileline(),"[EditWrapper]",oneloopp);
			m_varModeReplace = true;
			tempp->stmtsp()->iterateAndNext(*this);
			m_varModeReplace = false;
			oneloopp = tempp->stmtsp()->unlinkFrBackWithNext(); tempp->deleteTree(); VL_DANGLING(tempp);
		    }
		    if (m_generate) {
			string index = AstNode::encodeNumber(m_varValuep->toSInt());
			string nname = m_beginName + "__BRA__" + index + "__KET__";
			oneloopp = new AstBegin(oneloopp->fileline(),nname,oneloopp,true);
		    }
		    pushDeletep(m_varValuep); m_varValuep=NULL;
		    if (newbodysp) newbodysp->addNext(oneloopp);
		    else newbodysp = oneloopp;

		    ++m_statIters;
		    if (++times > unrollCount()*3) {
			nodep->v3error("Loop unrolling took too long; probably this is an infinite loop, or set --unroll-count above "<<unrollCount());
			break;
		    }

		    // loopValue += valInc
		    AstAssign *incpass = incp->castAssign();
		    V3Number newLoopValue = V3Number(nodep->fileline());
		    if (!simulateTree(incpass->rhsp(), &loopValue, incpass, newLoopValue)) {
			nodep->v3error("Loop unrolling failed");
			return false;
		    }
		    loopValue.opAssign(newLoopValue);
		}
	    }
	}
	// Replace the FOR()
	if (newbodysp) nodep->replaceWith(newbodysp);
	else nodep->unlinkFrBack();
	if (bodysp) { pushDeletep(bodysp); VL_DANGLING(bodysp); }
	if (precondsp) { pushDeletep(precondsp); VL_DANGLING(precondsp); }
	if (initp) { pushDeletep(initp); VL_DANGLING(initp); }
	if (incp && !incp->backp()) { pushDeletep(incp); VL_DANGLING(incp); }
	if (debug()>=9) newbodysp->dumpTree(cout,"-  _new: ");
	return true;
    }
Пример #10
0
    void replaceCaseComplicated(AstCase* nodep) {
	// CASEx(cexpr,ITEM(icond1,istmts1),ITEM(icond2,istmts2),ITEM(default,istmts3))
	// ->  IF((cexpr==icond1),istmts1,
	//		         IF((EQ (AND MASK cexpr) (AND MASK icond1)
	//				,istmts2, istmts3
	AstNode* cexprp = nodep->exprp()->unlinkFrBack();
	// We'll do this in two stages.  First stage, convert the conditions to
	// the appropriate IF AND terms.
	if (debug()>=9) nodep->dumpTree(cout,"    _comp_IN:   ");
	bool hadDefault = false;
	for (AstCaseItem* itemp = nodep->itemsp(); itemp; itemp=itemp->nextp()->castCaseItem()) {
	    if (!itemp->condsp()) {
		// Default clause.  Just make true, we'll optimize it away later
		itemp->condsp(new AstConst(itemp->fileline(), AstConst::LogicTrue()));
		hadDefault = true;
	    } else {
		// Expressioned clause
		AstNode* icondNextp = NULL;
		AstNode* ifexprp = NULL;	// If expression to test
		for (AstNode* icondp = itemp->condsp(); icondp!=NULL; icondp=icondNextp) {
		    icondNextp = icondp->nextp();
		    icondp->unlinkFrBack();

		    AstNode* condp = NULL;  // Default is to use and1p/and2p
		    AstConst* iconstp = icondp->castConst();
		    if (iconstp && neverItem(nodep, iconstp)) {
			// X in casez can't ever be executed
			icondp->deleteTree(); icondp=NULL; iconstp=NULL;
			// For simplicity, make expression that is not equal, and let later
			// optimizations remove it
			condp = new AstConst(itemp->fileline(), AstConst::LogicFalse());
		    } else if (AstInsideRange* irangep = icondp->castInsideRange()) {
			// Similar logic in V3Width::visit(AstInside)
			AstNode* ap = AstGte::newTyped(itemp->fileline(),
						       cexprp->cloneTree(false),
						       irangep->lhsp()->unlinkFrBack());
			AstNode* bp = AstLte::newTyped(itemp->fileline(),
						       cexprp->cloneTree(false),
						       irangep->rhsp()->unlinkFrBack());
			condp = new AstAnd(itemp->fileline(), ap, bp);
		    } else if (iconstp && iconstp->num().isFourState()
			       && (nodep->casex() || nodep->casez() || nodep->caseInside())) {
			V3Number nummask (itemp->fileline(), iconstp->width());
			nummask.opBitsNonX(iconstp->num());
			V3Number numval  (itemp->fileline(), iconstp->width());
			numval.opBitsOne(iconstp->num());
			AstNode* and1p = new AstAnd(itemp->fileline(), cexprp->cloneTree(false),
						    new AstConst(itemp->fileline(), nummask));
			AstNode* and2p = new AstAnd(itemp->fileline(),
						    new AstConst(itemp->fileline(), numval),
						    new AstConst(itemp->fileline(), nummask));
			icondp->deleteTree(); icondp=NULL; iconstp=NULL;
			condp = AstEq::newTyped(itemp->fileline(), and1p, and2p);
		    } else {
			// Not a caseX mask, we can simply build CASEEQ(cexpr icond)
			AstNode* and1p = cexprp->cloneTree(false);
			AstNode* and2p = icondp;
			condp = AstEq::newTyped(itemp->fileline(), and1p, and2p);
		    }
		    if (!ifexprp) {
			ifexprp = condp;
		    } else {
			ifexprp = new AstLogOr(itemp->fileline(), ifexprp, condp);
		    }
		}
		// Replace expression in tree
		itemp->condsp(ifexprp);
	    }
	}
	cexprp->deleteTree(); cexprp=NULL;
	if (!hadDefault) {
	    // If there was no default, add a empty one, this greatly simplifies below code
	    // and constant propagation will just eliminate it for us later.
	    nodep->addItemsp(new AstCaseItem(nodep->fileline(),
					     new AstConst(nodep->fileline(), AstConst::LogicTrue()),
					     NULL));
	}
	if (debug()>=9) nodep->dumpTree(cout,"    _comp_COND: ");
	// Now build the IF statement tree
	// The tree can be quite huge.  Pull ever group of 8 out, and make a OR tree.
	// This reduces the depth for the bottom elements, at the cost of some of the top elements.
	// If we ever have profiling data, we should pull out the most common item from here and
	// instead make it the first IF branch.
	int depth = 0;
	AstNode* grouprootp = NULL;
	AstIf* groupnextp = NULL;
	AstIf* itemnextp = NULL;
	for (AstCaseItem* itemp = nodep->itemsp(); itemp; itemp=itemp->nextp()->castCaseItem()) {
	    AstNode* istmtsp = itemp->bodysp();   // Maybe null -- no action.
	    if (istmtsp) istmtsp->unlinkFrBackWithNext();
	    // Expressioned clause
	    AstNode* ifexprp = itemp->condsp()->unlinkFrBack();
	    {   // Prepare for next group
		if (++depth > CASE_ENCODER_GROUP_DEPTH) depth = 1;
		if (depth == 1) {  // First group or starting new group
		    itemnextp = NULL;
		    AstIf* newp = new AstIf(itemp->fileline(), ifexprp->cloneTree(true), NULL, NULL);
		    if (groupnextp) groupnextp->addElsesp(newp);
		    else grouprootp = newp;
		    groupnextp = newp;
		} else { // Continue group, modify if condition to OR in this new condition
		    AstNode* condp = groupnextp->condp()->unlinkFrBack();
		    groupnextp->condp(new AstOr(ifexprp->fileline(),
						condp,
						ifexprp->cloneTree(true)));
		}
	    }
	    {   // Make the new lower IF and attach in the tree
		AstNode* itemexprp = ifexprp;  ifexprp=NULL;
		if (depth == (CASE_ENCODER_GROUP_DEPTH)) { // End of group - can skip the condition
		    itemexprp->deleteTree(); itemexprp=NULL;
		    itemexprp = new AstConst(itemp->fileline(), AstConst::LogicTrue());
		}
		AstIf* newp = new AstIf(itemp->fileline(), itemexprp, istmtsp, NULL);
		if (itemnextp) itemnextp->addElsesp(newp);
		else groupnextp->addIfsp(newp);  // First in a new group
		itemnextp = newp;
	    }
	}
	if (debug()>=9) nodep->dumpTree(cout,"    _comp_TREE: ");
	// Handle any assertions
	replaceCaseParallel(nodep, false);
	// Replace the CASE... with IF...
	if (debug()>=9) grouprootp->dumpTree(cout,"     _new: ");
	if (grouprootp) nodep->replaceWith(grouprootp);
	else nodep->unlinkFrBack();
	nodep->deleteTree(); nodep=NULL;
    }
Пример #11
0
    void forUnroller(AstNode* nodep,
		     AstNode* initp,
		     AstNode* precondsp, AstNode* condp,
		     AstNode* incp, AstNode* bodysp,
		     const V3Number& numInit,
		     AstNodeBiop* cmpInstrp, const V3Number& numStop,
		     AstNodeBiop* incInstrp, const V3Number& numInc) {
	UINFO(4, "   Unroll for var="<<numInit<<"; var<"<<numStop<<"; var+="<<numInc<<endl);
	UINFO(6, "    cmpI "<<cmpInstrp<<endl);
	UINFO(6, "    IncI "<<incInstrp<<endl);
	AstNode* stmtsp = NULL;
	if (initp) {
	    initp->unlinkFrBack();	// Always a single statement; nextp() may be nodep
	    // Don't add to list, we do it once, and setting loop index isn't needed as we're constant propagating it
	}
	if (precondsp) {
	    precondsp->unlinkFrBackWithNext();
	    // cppcheck-suppress nullPointer  // addNextNull deals with it
	    stmtsp = stmtsp->addNextNull(precondsp);
	}
	if (bodysp) {
	    bodysp->unlinkFrBackWithNext();
	    // cppcheck-suppress nullPointer  // addNextNull deals with it
	    stmtsp = stmtsp->addNextNull(bodysp);  // Maybe null if no body
	}
	if (incp && !nodep->castGenFor()) {  // Generates don't need to increment loop index
	    incp->unlinkFrBackWithNext();
	    // cppcheck-suppress nullPointer  // addNextNull deals with it
	    stmtsp = stmtsp->addNextNull(incp);  // Maybe null if no body
	}
	// Mark variable to disable some later warnings
	m_forVarp->usedLoopIdx(true);

	// If it's a While, then incp is already part of bodysp.
	V3Number loopValue(nodep->fileline(), m_forVarp->width());  // May differ in size from numInitp
	loopValue.opAssign(numInit);

	AstNode* newbodysp = NULL;
	++m_statLoops;
	if (stmtsp) {
	    int times = 0;
	    while (1) {
		UINFO(8,"      Looping "<<loopValue<<endl);
		// if loopValue<valStop
		V3Number contin (nodep->fileline(), 1);
		cmpInstrp->numberOperate(contin, loopValue, numStop);
		if (contin.isEqZero()) {
		    break;  // Done with the loop
		} else {
		    // Replace iterator values with constant.
		    AstNode* oneloopp = stmtsp->cloneTree(true);

		    m_varValuep = new AstConst(nodep->fileline(), loopValue);

		    // Iteration requires a back, so put under temporary node
		    if (oneloopp) {
			AstBegin* tempp = new AstBegin(oneloopp->fileline(),"[EditWrapper]",oneloopp);
			m_varModeReplace = true;
			tempp->stmtsp()->iterateAndNext(*this);
			m_varModeReplace = false;
			oneloopp = tempp->stmtsp()->unlinkFrBackWithNext(); tempp->deleteTree(); tempp=NULL;
		    }
		    if (m_generate) {
			string index = AstNode::encodeNumber(m_varValuep->toSInt());
			string nname = m_beginName + "__BRA__" + index + "__KET__";
			oneloopp = new AstBegin(oneloopp->fileline(),nname,oneloopp,true);
		    }

		    if (newbodysp) newbodysp->addNext(oneloopp);
		    else newbodysp = oneloopp;

		    ++m_statIters;
		    if (++times > unrollCount()*3) {
			nodep->v3error("Loop unrolling took too long; probably this is an infinite loop, or set --unroll-count above "<<unrollCount());
			break;
		    }

		    //loopValue += valInc
		    V3Number newnum(nodep->fileline(), m_forVarp->width());  // Can't increment in-place
		    incInstrp->numberOperate(newnum, loopValue, numInc);
		    loopValue.opAssign(newnum);

		    pushDeletep(m_varValuep); m_varValuep=NULL;
		}
	    }
	}
	// Replace the FOR()
	if (newbodysp) nodep->replaceWith(newbodysp);
	else nodep->unlinkFrBack();
	if (bodysp) { pushDeletep(bodysp); bodysp=NULL; }
	if (precondsp) { pushDeletep(precondsp); precondsp=NULL; }
	if (initp) { pushDeletep(initp); initp=NULL; }
	if (incp && !incp->backp()) { pushDeletep(incp); incp=NULL; }
	if (debug()>=9) newbodysp->dumpTree(cout,"-  _new: ");
    }