bool forUnrollCheck(AstNode* nodep, AstNode* initp, // Maybe under nodep (no nextp), or standalone (ignore nextp) AstNode* precondsp, AstNode* condp, AstNode* incp, // Maybe under nodep or in bodysp AstNode* bodysp) { // To keep the IF levels low, we return as each test fails. UINFO(4, " FOR Check "<<nodep<<endl); if (initp) UINFO(6, " Init "<<initp<<endl); if (precondsp) UINFO(6, " Pcon "<<precondsp<<endl); if (condp) UINFO(6, " Cond "<<condp<<endl); if (incp) UINFO(6, " Inc "<<incp<<endl); // Initial value check AstAssign* initAssp = VN_CAST(initp, Assign); if (!initAssp) return cantUnroll(nodep, "no initial assignment"); if (initp->nextp() && initp->nextp()!=nodep) nodep->v3fatalSrc("initial assignment shouldn't be a list"); if (!VN_IS(initAssp->lhsp(), VarRef)) return cantUnroll(nodep, "no initial assignment to simple variable"); // // Condition check if (condp->nextp()) nodep->v3fatalSrc("conditional shouldn't be a list"); // // Assignment of next value check AstAssign* incAssp = VN_CAST(incp, Assign); if (!incAssp) return cantUnroll(nodep, "no increment assignment"); if (incAssp->nextp()) nodep->v3fatalSrc("increment shouldn't be a list"); m_forVarp = VN_CAST(initAssp->lhsp(), VarRef)->varp(); m_forVscp = VN_CAST(initAssp->lhsp(), VarRef)->varScopep(); if (VN_IS(nodep, GenFor) && !m_forVarp->isGenVar()) { nodep->v3error("Non-genvar used in generate for: "<<m_forVarp->prettyName()<<endl); } if (m_generate) V3Const::constifyParamsEdit(initAssp->rhsp()); // rhsp may change // This check shouldn't be needed when using V3Simulate // however, for repeat loops, the loop variable is auto-generated // and the initp statements will reference a variable outside of the initp scope // alas, failing to simulate. AstConst* constInitp = VN_CAST(initAssp->rhsp(), Const); if (!constInitp) return cantUnroll(nodep, "non-constant initializer"); // // Now, make sure there's no assignment to this variable in the loop m_varModeCheck = true; m_varAssignHit = false; m_ignoreIncp = incp; iterateAndNextNull(precondsp); iterateAndNextNull(bodysp); iterateAndNextNull(incp); m_varModeCheck = false; m_ignoreIncp = NULL; if (m_varAssignHit) return cantUnroll(nodep, "genvar assigned *inside* loop"); // if (m_forVscp) { UINFO(8, " Loop Variable: "<<m_forVscp<<endl); } else { UINFO(8, " Loop Variable: "<<m_forVarp<<endl); } if (debug()>=9) nodep->dumpTree(cout,"- for: "); if (!m_generate) { AstAssign *incpAssign = VN_CAST(incp, Assign); if (!canSimulate(incpAssign->rhsp())) return cantUnroll(incp, "Unable to simulate increment"); if (!canSimulate(condp)) return cantUnroll(condp, "Unable to simulate condition"); // Check whether to we actually want to try and unroll. int loops; if (!countLoops(initAssp, condp, incp, unrollCount(), loops)) return cantUnroll(nodep, "Unable to simulate loop"); // Less than 10 statements in the body? int bodySize = 0; int bodyLimit = v3Global.opt.unrollStmts(); if (loops>0) bodyLimit = v3Global.opt.unrollStmts() / loops; if (bodySizeOverRecurse(precondsp, bodySize/*ref*/, bodyLimit) || bodySizeOverRecurse(bodysp, bodySize/*ref*/, bodyLimit) || bodySizeOverRecurse(incp, bodySize/*ref*/, bodyLimit)) { return cantUnroll(nodep, "too many statements"); } } // Finally, we can do it if (!forUnroller(nodep, initAssp, condp, precondsp, incp, bodysp)) { return cantUnroll(nodep, "Unable to unroll loop"); } VL_DANGLING(nodep); // Cleanup return true; }
bool forUnrollCheck(AstNode* nodep, AstNode* initp, // Maybe under nodep (no nextp), or standalone (ignore nextp) AstNode* precondsp, AstNode* condp, AstNode* incp, // Maybe under nodep or in bodysp AstNode* bodysp) { // To keep the IF levels low, we return as each test fails. UINFO(4, " FOR Check "<<nodep<<endl); if (initp) UINFO(6, " Init "<<initp<<endl); if (precondsp) UINFO(6, " Pcon "<<precondsp<<endl); if (condp) UINFO(6, " Cond "<<condp<<endl); if (incp) UINFO(6, " Inc "<<incp<<endl); // Initial value check AstAssign* initAssp = initp->castAssign(); if (!initAssp) return cantUnroll(nodep, "no initial assignment"); if (initp->nextp() && initp->nextp()!=nodep) nodep->v3fatalSrc("initial assignment shouldn't be a list"); if (!initAssp->lhsp()->castVarRef()) return cantUnroll(nodep, "no initial assignment to simple variable"); m_forVarp = initAssp->lhsp()->castVarRef()->varp(); m_forVscp = initAssp->lhsp()->castVarRef()->varScopep(); if (nodep->castGenFor() && !m_forVarp->isGenVar()) { nodep->v3error("Non-genvar used in generate for: "<<m_forVarp->name()<<endl); } if (m_generate) V3Const::constifyParamsEdit(initAssp->rhsp()); // rhsp may change AstConst* constInitp = initAssp->rhsp()->castConst(); if (!constInitp) return cantUnroll(nodep, "non-constant initializer"); // // Condition check if (condp->nextp()) nodep->v3fatalSrc("conditional shouldn't be a list"); // // Assignment of next value check AstAssign* incAssp = incp->castAssign(); if (!incAssp) return cantUnroll(nodep, "no increment assignment"); if (incAssp->nextp()) nodep->v3fatalSrc("increment shouldn't be a list"); AstNodeBiop* incInstrp = incAssp->rhsp()->castNodeBiop(); // if (m_forVscp) { UINFO(8, " Loop Variable: "<<m_forVscp<<endl); } else { UINFO(8, " Loop Variable: "<<m_forVarp<<endl); } if (debug()>=9) nodep->dumpTree(cout,"- for: "); // // Extract the constant loop bounds bool subtract = incInstrp->castSub(); { if (!subtract && !incInstrp->castAdd()) return cantUnroll(nodep, "missing add/sub for incrementer"); AstVarRef* incVarrp = (subtract ? incInstrp->lhsp()->castVarRef() : incInstrp->rhsp()->castVarRef()); if (!incVarrp) return cantUnroll(nodep, "missing variable in incrementer"); if (incVarrp->varp() != m_forVarp || incVarrp->varScopep() != m_forVscp) { return cantUnroll(nodep, "different variables in incrementer"); } } // // Adds have the # on the lhsp because V3Const pushes rhs consts over to the lhs // Subtracts have it on the rhs, because you write i=i-1; i=1-i is non-sensible. AstConst* preconstIncp = (subtract ? incInstrp->rhsp()->castConst() : incInstrp->lhsp()->castConst()); if (m_generate) preconstIncp = V3Const::constifyParamsEdit(preconstIncp)->castConst(); AstConst* constIncp = (subtract ? incInstrp->rhsp()->castConst() : incInstrp->lhsp()->castConst()); UINFO(8, " Inc expr ok: "<<constIncp<<endl); if (!constIncp) return cantUnroll(nodep, "non-constant increment"); if (constIncp->isZero()) return cantUnroll(nodep, "zero increment"); // Or we could loop forever below... bool lt = condp->castLt() || condp->castLtS(); bool lte = condp->castLte() || condp->castLteS(); bool gt = condp->castGt() || condp->castGtS(); bool gte = condp->castGte() || condp->castGteS(); if (!lt && !lte && !gt && !gte) return cantUnroll(nodep, "condition not <= or <"); AstNodeBiop* condBip = condp->castNodeBiop(); if (!condBip->lhsp()->castVarRef()) return cantUnroll(nodep, "no variable on lhs of condition"); if (condBip->lhsp()->castVarRef()->varp() != m_forVarp || condBip->lhsp()->castVarRef()->varScopep() != m_forVscp) return cantUnroll(nodep, "different variable in condition"); if (m_generate) V3Const::constifyParamsEdit(condBip->rhsp()); // rhsp may change AstConst* constStopp = condBip->rhsp()->castConst(); if (!constStopp) return cantUnroll(nodep, "non-constant final value"); UINFO(8, " Stop expr ok: "<<constStopp<<endl); // if (constInitp->width()>32 || constInitp->num().isFourState() || constStopp->width()>32 || constStopp->num().isFourState() || constIncp->width()>32 || constIncp->num().isFourState()) return cantUnroll(nodep, "init/final/increment too large or four state"); vlsint32_t valInit = constInitp->num().toSInt(); vlsint32_t valStop = constStopp->num().toSInt(); if (lte) valStop++; if (gte) valStop--; vlsint32_t valInc = constIncp->num().toSInt(); if (subtract) valInc = -valInc; UINFO(8," In Numbers: for (v="<<valInit<<"; v<"<<valStop<<"; v=v+"<<valInc<<")\n"); // if (!m_generate) { int loops = ((valStop - valInit)/valInc); if (loops < 0) { loops += (1ULL<<constStopp->width()); } // Will roll around UINFO(8, " ~Iters: "<<loops<<" c="<<unrollCount()<<endl); if (loops > unrollCount()) return cantUnroll(nodep, "too many iterations"); // Less than 10 statements in the body? int bodySize = 0; int bodyLimit = v3Global.opt.unrollStmts(); if (loops>0) bodyLimit = v3Global.opt.unrollStmts() / loops; if (bodySizeOverRecurse(precondsp, bodySize/*ref*/, bodyLimit) || bodySizeOverRecurse(bodysp, bodySize/*ref*/, bodyLimit) || bodySizeOverRecurse(incp, bodySize/*ref*/, bodyLimit)) { return cantUnroll(nodep, "too many statements"); } } // // Now, make sure there's no assignment to this variable in the loop m_varModeCheck = true; m_varAssignHit = false; m_ignoreIncp = incp; precondsp->iterateAndNext(*this); bodysp->iterateAndNext(*this); incp->iterateAndNext(*this); m_varModeCheck = false; m_ignoreIncp = NULL; if (m_varAssignHit) return cantUnroll(nodep, "genvar assigned *inside* loop"); // // Finally, we can do it forUnroller(nodep, initp, precondsp, condp, incp, bodysp, constInitp->num(), condBip, constStopp->num(), incInstrp, constIncp->num()); nodep = NULL; // Cleanup return true; }
bool forUnroller(AstNode* nodep, AstAssign* initp, AstNode* condp, AstNode* precondsp, AstNode* incp, AstNode* bodysp) { UINFO(9, "forUnroller "<<nodep<<endl); 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(); stmtsp = AstNode::addNextNull(stmtsp, precondsp); } if (bodysp) { bodysp->unlinkFrBackWithNext(); stmtsp = AstNode::addNextNull(stmtsp, bodysp); // Maybe null if no body } if (incp && !VN_IS(nodep, GenFor)) { // Generates don't need to increment loop index incp->unlinkFrBackWithNext(); stmtsp = AstNode::addNextNull(stmtsp, 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; iterateAndNextNull(tempp->stmtsp()); 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 = VN_CAST(incp, Assign); 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) newbodysp->dumpTree(cout,"- _new: "); return true; }