// VISITs virtual void visit(AstNetlist* nodep, AstNUser*) { AstNode::user1ClearTree(); readModNames(); nodep->iterateChildren(*this); // Find levels in graph m_graph.removeRedundantEdges(&V3GraphEdge::followAlwaysTrue); m_graph.dumpDotFilePrefixed("linkcells"); m_graph.rank(); for (V3GraphVertex* itp = m_graph.verticesBeginp(); itp; itp=itp->verticesNextp()) { if (LinkCellsVertex* vvertexp = dynamic_cast<LinkCellsVertex*>(itp)) { // +1 so we leave level 1 for the new wrapper we'll make in a moment AstNodeModule* modp = vvertexp->modp(); modp->level(vvertexp->rank()+1); if (vvertexp == m_topVertexp && modp->level() != 2) { AstNodeModule* abovep = NULL; if (V3GraphEdge* edgep = vvertexp->inBeginp()) { if (LinkCellsVertex* eFromVertexp = dynamic_cast<LinkCellsVertex*>(edgep->fromp())) { abovep = eFromVertexp->modp(); } } v3error("Specified --top-module '"<<v3Global.opt.topModule() <<"' isn't at the top level, it's under another cell '" <<(abovep ? abovep->prettyName() : "UNKNOWN")<<"'"); } } } if (v3Global.opt.topModule()!="" && !m_topVertexp) { v3error("Specified --top-module '"<<v3Global.opt.topModule()<<"' was not found in design."); } }
void GateVisitor::warnSignals() { AstNode::user2ClearTree(); for (V3GraphVertex* itp = m_graph.verticesBeginp(); itp; itp=itp->verticesNextp()) { if (GateVarVertex* vvertexp = dynamic_cast<GateVarVertex*>(itp)) { AstVarScope* vscp = vvertexp->varScp(); AstNode* sp = vvertexp->rstSyncNodep(); AstNode* ap = vvertexp->rstAsyncNodep(); if (ap && sp && !vscp->varp()->user2()) { // This is somewhat wrong, as marking one flop as ok for sync // may mean a different flop now fails. However it's a pain to // then report a warning in a new place - we should report them all at once. // Instead we'll disable if any disabled if (!vscp->fileline()->warnIsOff(V3ErrorCode::SYNCASYNCNET) && !ap->fileline()->warnIsOff(V3ErrorCode::SYNCASYNCNET) && !sp->fileline()->warnIsOff(V3ErrorCode::SYNCASYNCNET) ) { vscp->varp()->user2(true); // Warn only once per signal vscp->v3warn(SYNCASYNCNET,"Signal flopped as both synchronous and async: "<<vscp->prettyName()<<endl <<ap->warnMore()<<"... Location of async usage"<<endl <<sp->warnMore()<<"... Location of sync usage"<<endl); } } } } }
void newAlwaysTrees(AstAlways* nodep) { // Across all variables we're moving uint32_t lastColor = 0; AstNode* lastExprp = NULL; for (V3GraphVertex* vertexp = m_graph.verticesBeginp(); vertexp; vertexp=vertexp->verticesNextp()) { if (GaterVarVertex* vVxp = dynamic_cast<GaterVarVertex*>(vertexp)) { if (!lastExprp || lastColor != vVxp->color()) { lastColor = vVxp->color(); // Create the block we've just finished if (lastExprp) newAlwaysTree(nodep, lastExprp); // Duplicate below // New expression for this color lastExprp = newExprFromGraph(vVxp); } // Mark variable to move if (vVxp->nodep()->user2p()) vVxp->nodep()->v3fatalSrc("One variable got marked under two gaters"); vVxp->nodep()->user2p(lastExprp); m_statBits += vVxp->nodep()->width(); // Moving a wide bus counts more! // There shouldn't be two possibilities we want to // move to, IE {A,B} <= Z because we marked such // things as unoptimizable } } // Create the final block we've just finished if (lastExprp) newAlwaysTree(nodep, lastExprp); // Duplicate above }
void V3Graph::userClearEdges() { // Clear user() in all of tree for (V3GraphVertex* vertexp = verticesBeginp(); vertexp; vertexp=vertexp->verticesNextp()) { for (V3GraphEdge* edgep = vertexp->outBeginp(); edgep; edgep=edgep->outNextp()) { edgep->user(0); edgep->userp(NULL); // Its a union, but might be different size than user() } } }
void GateVisitor::mergeAssigns() { GateMergeAssignsGraphVisitor merger(&m_graph); for (V3GraphVertex* itp = m_graph.verticesBeginp(); itp; itp=itp->verticesNextp()) { if (GateVarVertex* vvertexp = dynamic_cast<GateVarVertex*>(itp)) { merger.mergeAssignsTree(vvertexp); } } m_statAssignMerged += merger.numMergedAssigns(); }
void GateVisitor::consumedMark() { // Propagate consumed signals backwards to all producers into a consumed node m_graph.userClearVertices(); for (V3GraphVertex* vertexp = m_graph.verticesBeginp(); vertexp; vertexp=vertexp->verticesNextp()) { GateEitherVertex* evertexp = (GateEitherVertex*)vertexp; if (!evertexp->user() && evertexp->consumed()) { consumedMarkRecurse(evertexp); } } }
void V3Graph::userClearVertices() { // Clear user() in all of tree // We may use the userCnt trick in V3Ast later... (but gblCnt would be // in V3Graph instead of static - which has the complication of finding // the graph pointer given a vertex.) For now we don't call this often, and // the extra code on each read of user() would probably slow things // down more than help. for (V3GraphVertex* vertexp = verticesBeginp(); vertexp; vertexp=vertexp->verticesNextp()) { vertexp->user(0); vertexp->userp(NULL); // Its a union, but might be different size than user() } }
void dumpGraph(std::ostream& os, const string& nameComment) const { // UINFO(0) as controlled by caller os<<"At "<<nameComment<<", dumping graph. Keys:\n"; for (V3GraphVertex* vxp = verticesBeginp(); vxp; vxp = vxp->verticesNextp()) { Vertex* tspvp = castVertexp(vxp); os<<" "<<tspvp->key()<<endl; for (V3GraphEdge* edgep = tspvp->outBeginp(); edgep; edgep = edgep->outNextp()) { Vertex* neighborp = castVertexp(edgep->top()); os<<" has edge "<<edgep->user()<<" to "<<neighborp->key()<<endl; } } }
void combineGraph(const TspGraphTmpl& g) { vl_unordered_set<vluint32_t> edges_done; for (V3GraphVertex* vxp = g.verticesBeginp(); vxp; vxp = vxp->verticesNextp()) { Vertex* fromp = castVertexp(vxp); for (V3GraphEdge* edgep = fromp->outBeginp(); edgep; edgep = edgep->outNextp()) { Vertex* top = castVertexp(edgep->top()); if (edges_done.find(edgep->user()) == edges_done.end()) { addEdge(fromp->key(), top->key(), edgep->weight()); edges_done.insert(edgep->user()); } } } }
void optimize_accepting_out() { // Delete outbound edges from accepting states // (As once we've accepted, we no longer care about anything else.) for (V3GraphVertex* vertexp = m_graphp->verticesBeginp(); vertexp; vertexp=vertexp->verticesNextp()) { if (DfaVertex* vvertexp = dynamic_cast<DfaVertex*>(vertexp)) { if (vvertexp->accepting()) { for (V3GraphEdge* nextp,*edgep = vertexp->outBeginp(); edgep; edgep=nextp) { nextp = edgep->outNextp(); edgep->unlinkDelete(); edgep=NULL; } } } } }
std::vector<T_Key> getOddDegreeKeys() const { std::vector<T_Key> result; for (V3GraphVertex* vxp = verticesBeginp(); vxp; vxp = vxp->verticesNextp()) { Vertex* tspvp = castVertexp(vxp); vluint32_t degree = 0; for (V3GraphEdge* edgep = vxp->outBeginp(); edgep; edgep = edgep->outNextp()) { degree++; } if (degree & 1) { result.push_back(tspvp->key()); } } return result; }
// VISITORS virtual void visit(AstAlways* nodep, AstNUser*) { if (debug()>=9) cout<<endl<<endl<<endl; UINFO(5, "Gater: ALWAYS: "<<nodep<<endl); if (nodep->user4Inc()) return; clear(); if (debug()>=9) nodep->dumpTree(cout," Alwin: "); // Look for constructs we can't optimize // Form graph with Vertices at each IF, and each Variable m_aboveTrue = VU_IF | VU_ELSE; iterateChildrenAlw(nodep, true); // Other reasons to not optimize if (!m_numIfs) nonOptimizable(nodep, "No if statements"); // Something to optimize, oh my! if (m_nonopt!="") { UINFO(5, " Gater non-opt: "<<m_nonopt<<endl); } else { // Process it simplifyGraph(); // See how much moves uint32_t lastColor = 0; for (V3GraphVertex* vertexp = m_graph.verticesBeginp(); vertexp; vertexp=vertexp->verticesNextp()) { if (GaterVarVertex* vVxp = dynamic_cast<GaterVarVertex*>(vertexp)) { if (lastColor < vVxp->color()) { lastColor = vVxp->color(); } } } if (lastColor == 0) { // Nothing we moved! nonOptimizable(nodep, "Nothing moved"); } else if (lastColor > DOMAINS_MAX) { // Our move algorithm is fairly slow and if we're splitting // up too much it'll get really nasty. It's probably a bad // move for performance to split too much, anyhow, as the // number of gaters will result in calling many small c functions. nonOptimizable(nodep, "Too much moved"); } if (m_nonopt=="") { newAlwaysTrees(nodep); if (debug()>=9) nodep->dumpTree(cout," Gaterout: "); } } UINFO(5, " Gater done"<<endl); }
DfaVertex* DfaGraph::findStart() { DfaVertex* startp = NULL; for (V3GraphVertex* vertexp = this->verticesBeginp(); vertexp; vertexp=vertexp->verticesNextp()) { if (DfaVertex* vvertexp = dynamic_cast<DfaVertex*>(vertexp)) { if (vvertexp->start()) { if (startp) v3fatalSrc("Multiple start points in NFA graph"); startp = vvertexp; } } else { v3fatalSrc("Non DfaVertex in DfaGraph\n"); } } if (!startp) v3fatalSrc("No start point in NFA graph"); return startp; }
void simplifyGrandRecurse(V3GraphVertex* vertexp, uint32_t depth) { // From top-down delete any vars that grandparents source // IE A -> B -> C -> VAR // \----------^ //UINFO(9,"GRecurse "<<depth<<" "<<vertexp<<endl); // Mark all variables to be swept for (V3GraphEdge *nextp, *edgep = vertexp->outBeginp(); edgep; edgep=nextp) { nextp = edgep->outNextp(); // We may edit the list if (GaterVarVertex* toVxp = dynamic_cast<GaterVarVertex*>(edgep->top())) { if (toVxp->user() && toVxp->user() < depth) { // A recursion "above" us marked it, // Remove this edge, it's redundant with the upper edge edgep->unlinkDelete(); edgep=NULL; } else { GaterEdge* cedgep = static_cast<GaterEdge*>(edgep); if (cedgep->ifelseBoth()) { toVxp->user(depth); } } } } // Recurse for (V3GraphEdge* edgep = vertexp->outBeginp(); edgep; edgep=edgep->outNextp()) { simplifyGrandRecurse(edgep->top(), depth+1); } // Clean our marks for (V3GraphEdge* edgep = vertexp->outBeginp(); edgep; edgep=edgep->outNextp()) { V3GraphVertex* toVxp = edgep->top(); if (toVxp->user() && toVxp->user() < depth) { // A recursion "above" us marked it; don't mess with it } else { toVxp->user(0); // We marked it originally, so unmark now } } // Delete any If nodes with no children // Last, as want bottom-up cleanup for (V3GraphEdge *nextp, *edgep = vertexp->outBeginp(); edgep; edgep=nextp) { nextp = edgep->outNextp(); // We may edit the list if (GaterIfVertex* toVxp = dynamic_cast<GaterIfVertex*>(edgep->top())) { if (!toVxp->outBeginp()) { if (!nextp || nextp->top() != edgep->top()) { // Else next would disappear; we'll do it next loop toVxp->unlinkDelete(&m_graph); toVxp=NULL; edgep=NULL; } } } } }
void pruneDepsOnInputs() { for (V3GraphVertex* vertexp = m_graph.verticesBeginp(); vertexp; vertexp=vertexp->verticesNextp()) { if (!vertexp->outBeginp() && dynamic_cast<SplitVarStdVertex*>(vertexp)) { if (debug() >= 9) { SplitVarStdVertex* stdp = (SplitVarStdVertex*)(vertexp); UINFO(0, "Will prune deps on var "<<stdp->nodep()<<endl); stdp->nodep()->dumpTree(cout, "- "); } for (V3GraphEdge* edgep = vertexp->inBeginp(); edgep; edgep=edgep->inNextp()) { SplitEdge* oedgep = dynamic_cast<SplitEdge*>(edgep); oedgep->setIgnoreThisStep(); } } } }
void add_complement_edges() { // Find accepting vertex DfaVertex* acceptp = NULL; for (V3GraphVertex* vertexp = m_graphp->verticesBeginp(); vertexp; vertexp=vertexp->verticesNextp()) { if (DfaVertex* vvertexp = dynamic_cast<DfaVertex*>(vertexp)) { if (vvertexp->accepting()) { acceptp = vvertexp; break; } } } if (!acceptp) v3fatalSrc("No accepting vertex in DFA\n"); // Remap edges for (V3GraphVertex* vertexp = m_graphp->verticesBeginp(); vertexp; vertexp=vertexp->verticesNextp()) { if (DfaVertex* vvertexp = dynamic_cast<DfaVertex*>(vertexp)) { //UINFO(9, " on vertex "<<vvertexp->name()<<endl); if (!vvertexp->accepting() && vvertexp != m_tempNewerReject) { for (V3GraphEdge* nextp, *edgep = vertexp->outBeginp(); edgep; edgep=nextp) { nextp = edgep->outNextp(); if (!edgep->user()) { // Not processed // Old edges to accept now go to new reject DfaEdge* vedgep = static_cast<DfaEdge*>(edgep); DfaVertex* tovertexp = static_cast<DfaVertex*>(edgep->top()); if (tovertexp->accepting()) { new DfaEdge(graphp(), vvertexp, m_tempNewerReject, vedgep); edgep->unlinkDelete(); edgep=NULL; } // NOT of all values goes to accept // We make a edge for each value to OR, IE // edge(complemented,a) edge(complemented,b) means !(a | b) if (!tovertexp->accepting()) { // Note we must include edges moved above to reject DfaEdge* newp = new DfaEdge (graphp(), vvertexp, acceptp, vedgep); newp->complement(!newp->complement()); newp->user(1); } } } } } } }
void GateVisitor::consumedMove() { // Remove unused logic (logic that doesn't hit a combo block or a display statement) // We need the "usually" block logic to do a better job at this for (V3GraphVertex* vertexp = m_graph.verticesBeginp(); vertexp; vertexp=vertexp->verticesNextp()) { if (GateVarVertex* vvertexp = dynamic_cast<GateVarVertex*>(vertexp)) { if (!vvertexp->consumed() && !vvertexp->user()) { UINFO(8, "Unconsumed "<<vvertexp->varScp()<<endl); } } if (GateLogicVertex* lvertexp = dynamic_cast<GateLogicVertex*>(vertexp)) { AstNode* nodep = lvertexp->nodep(); AstActive* oldactp = lvertexp->activep(); // NULL under cfunc if (!lvertexp->consumed() && oldactp) { // Eventually: Move the statement to a new active block with "tracing-on" sensitivity UINFO(8," Remove unconsumed "<<nodep<<endl); nodep->unlinkFrBack(); pushDeletep(nodep); VL_DANGLING(nodep); } } } }
void GateVisitor::dedupe() { AstNode::user2ClearTree(); GateDedupeGraphVisitor deduper; // Traverse starting from each of the clocks for (V3GraphVertex* itp = m_graph.verticesBeginp(); itp; itp=itp->verticesNextp()) { if (GateVarVertex* vvertexp = dynamic_cast<GateVarVertex*>(itp)) { if (vvertexp->isClock()) { deduper.dedupeTree(vvertexp); } } } // Traverse starting from each of the outputs for (V3GraphVertex* itp = m_graph.verticesBeginp(); itp; itp=itp->verticesNextp()) { if (GateVarVertex* vvertexp = dynamic_cast<GateVarVertex*>(itp)) { if (vvertexp->isTop() && vvertexp->varScp()->varp()->isOutput()) { deduper.dedupeTree(vvertexp); } } } m_statDedupLogic += deduper.numDeduped(); }
void optimize_no_outbound() { // Non-accepting states with no outbound transitions may be // deleted. Then, any arcs feeding those states, and perhaps those // states... // Vertex::m_user begin: 1 indicates on the work list // (Otherwise we might have nodes on the list twice, and reference after deleting them.) m_graphp->userClearVertices(); // Find all dead vertexes stack<DfaVertex*> workps; for (V3GraphVertex* vertexp = m_graphp->verticesBeginp(); vertexp; vertexp=vertexp->verticesNextp()) { if (DfaVertex* vvertexp = dynamic_cast<DfaVertex*>(vertexp)) { workps.push(vvertexp); vertexp->user(1); } else { // If ever remove this, need dyn cast below v3fatalSrc("Non DfaVertex in dfa graph"); } } // While deadness... Delete and find new dead nodes. while (!workps.empty()) { DfaVertex* vertexp = workps.top(); workps.pop(); vertexp->user(0); if (isDead(vertexp)) { // Add nodes that go here to the work list for (V3GraphEdge* edgep = vertexp->inBeginp(); edgep; edgep=edgep->inNextp()) { DfaVertex* fromvertexp = static_cast<DfaVertex*>(edgep->fromp()); if (fromvertexp != vertexp && !fromvertexp->user()) { workps.push(static_cast<DfaVertex*>(fromvertexp)); fromvertexp->user(1); } } // Transitions to this state removed by the unlink function vertexp->unlinkDelete(m_graphp); vertexp=NULL; } } }
void optimize_orphans() { // Remove states that don't come from start // Presumably the previous optimization orphaned them. // Vertex::m_user begin: 1 indicates on the work list, 2 processed // (Otherwise we might have nodes on the list twice, and reference after deleting them.) m_graphp->userClearVertices(); DfaVertex* startp = graphp()->findStart(); stack<V3GraphVertex*> workps; workps.push(startp); // Mark all nodes connected to start while (!workps.empty()) { V3GraphVertex* vertexp = workps.top(); workps.pop(); vertexp->user(2); // Processed // Add nodes from here to the work list for (V3GraphEdge* edgep = vertexp->outBeginp(); edgep; edgep=edgep->outNextp()) { V3GraphVertex* tovertexp = edgep->top(); if (!tovertexp->user()) { workps.push(tovertexp); tovertexp->user(1); } } } // Delete all nodes not connected for (V3GraphVertex* nextp,*vertexp = m_graphp->verticesBeginp(); vertexp; vertexp=nextp) { nextp = vertexp->verticesNextp(); if (!vertexp->user()) { vertexp->unlinkDelete(m_graphp); vertexp=NULL; } } }
void GateVisitor::replaceAssigns() { for (V3GraphVertex* itp = m_graph.verticesBeginp(); itp; itp=itp->verticesNextp()) { if (GateVarVertex* vvertexp = dynamic_cast<GateVarVertex*>(itp)) { // Take the Comments/assigns that were moved to the VarScope and change them to a // simple value assignment AstVarScope* vscp = vvertexp->varScp(); if (vscp->valuep() && !vscp->valuep()->castNodeMath()) { //if (debug()>9) vscp->dumpTree(cout, "-vscPre: "); while (AstNode* delp=vscp->valuep()->castComment()) { delp->unlinkFrBack()->deleteTree(); VL_DANGLING(delp); } if (AstInitial* delp=vscp->valuep()->castInitial()) { AstNode* bodyp=delp->bodysp(); bodyp->unlinkFrBackWithNext(); delp->replaceWith(bodyp); delp->deleteTree(); VL_DANGLING(delp); } if (AstAlways* delp=vscp->valuep()->castAlways()) { AstNode* bodyp=delp->bodysp(); bodyp->unlinkFrBackWithNext(); delp->replaceWith(bodyp); delp->deleteTree(); VL_DANGLING(delp); } if (AstNodeAssign* delp=vscp->valuep()->castNodeAssign()) { AstNode* rhsp=delp->rhsp(); rhsp->unlinkFrBack(); delp->replaceWith(rhsp); delp->deleteTree(); VL_DANGLING(delp); } //if (debug()>9) {vscp->dumpTree(cout, "-vscDone: "); cout<<endl;} if (!vscp->valuep()->castNodeMath() || vscp->valuep()->nextp()) { vscp->dumpTree(cerr, "vscStrange: "); vscp->v3fatalSrc("Value of varscope not mathematical\n"); } } } } }
void graphColorSameFeeder() { // Assign same color to all destination vertices that have same // subgraph feeding into them // (I.E. all "from" nodes are common within each color) // We could hash, but instead it's faster to sort edges, so we know // the same edge list is always adjacent. The result is a // O(vertices*edges) loop, but we'd need that to hash too. if (debug()>9) { cout<<"PreColor:\n"; m_graph.dump(); } m_graph.sortEdges(); // Now sort vertices, so same inbound edge set ends up adjacent m_graph.sortVertices(); // Now walk and assign colors; same color is adjacent m_graph.clearColors(); m_graph.userClearEdges(); // Used by newExprFromGraph uint32_t color = 1; GaterVarVertex* lastVxp = NULL; for (V3GraphVertex* vertexp = m_graph.verticesBeginp(); vertexp; vertexp=vertexp->verticesNextp()) { if (GaterVarVertex* vVxp = dynamic_cast<GaterVarVertex*>(vertexp)) { if (!vVxp->inBeginp()) { // At this point, any variable not linked is an error // (It should have at least landed under the Head node) vVxp->nodep()->v3fatalSrc("Variable became stranded in clk gate detection\n"); } if (!lastVxp || vVxp->sortCmp(lastVxp)) { // Different sources for this new node color++; } vVxp->color(color); lastVxp = vVxp; } } if (debug()>9) { cout<<"PostColor:\n"; m_graph.dump(); } }
void V3Graph::dump(ostream& os) { // This generates a file used by graphviz, http://www.graphviz.org os<<" Graph:\n"; // Print vertices for (V3GraphVertex* vertexp = verticesBeginp(); vertexp; vertexp=vertexp->verticesNextp()) { os<<"\tNode: "<<vertexp->name(); if (vertexp->color()) os<<" color="<<vertexp->color(); os<<endl; // Print edges for (V3GraphEdge* edgep = vertexp->inBeginp(); edgep; edgep=edgep->inNextp()) { dumpEdge (os, vertexp, edgep); } for (V3GraphEdge* edgep = vertexp->outBeginp(); edgep; edgep=edgep->outNextp()) { dumpEdge (os, vertexp, edgep); } } }
void V3Graph::clear() { // Empty it of all points, as if making a new object // Delete the old edges for (V3GraphVertex* vertexp = verticesBeginp(); vertexp; vertexp=vertexp->verticesNextp()) { for (V3GraphEdge* edgep = vertexp->outBeginp(); edgep; /*BELOW*/) { V3GraphEdge* nextp = edgep->outNextp(); delete edgep; edgep = nextp; } vertexp->outUnlink(); } // Delete the old vertices for (V3GraphVertex* vertexp = verticesBeginp(); vertexp; /*BELOW*/) { V3GraphVertex* nextp = vertexp->verticesNextp(); delete vertexp; vertexp = nextp; } verticesUnlink(); }
void colorAlwaysGraph() { // Color the graph to indicate subsets, each of which // we can split into its own always block. m_graph.removeRedundantEdges(&V3GraphEdge::followAlwaysTrue); // Some vars are primary inputs to the always block; prune // edges on those vars. Reasoning: if two statements both depend // on primary input A, it's ok to split these statements. Whereas // if they both depend on locally-generated variable B, the statements // must be kept together. SplitEdge::incrementStep(); pruneDepsOnInputs(); // For any 'if' node whose deps have all been pruned // (meaning, its conditional expression only looks at primary // inputs) prune all edges that depend on the 'if'. for (V3GraphVertex* vertexp = m_graph.verticesBeginp(); vertexp; vertexp=vertexp->verticesNextp()) { SplitLogicVertex* logicp = dynamic_cast<SplitLogicVertex*>(vertexp); if (!logicp) continue; AstNodeIf* ifNodep = VN_CAST(logicp->nodep(), NodeIf); if (!ifNodep) continue; bool pruneMe = true; for (V3GraphEdge* edgep = logicp->outBeginp(); edgep; edgep = edgep->outNextp()) { SplitEdge* oedgep = dynamic_cast<SplitEdge*>(edgep); if (!oedgep->ignoreThisStep()) { // This if conditional depends on something we can't // prune -- a variable generated in the current block. pruneMe = false; // When we can't prune dependencies on the conditional, // give a hint about why... if (debug() >= 9) { V3GraphVertex* vxp = oedgep->top(); SplitNodeVertex* nvxp = dynamic_cast<SplitNodeVertex*>(vxp); UINFO(0, "Cannot prune if-node due to edge "<<oedgep<< " pointing to node "<<nvxp->nodep()<<endl); nvxp->nodep()->dumpTree(cout, "- "); } break; } } if (!pruneMe) continue; // This if can be split; prune dependencies on it. for (V3GraphEdge* edgep = logicp->inBeginp(); edgep; edgep = edgep->inNextp()) { SplitEdge* oedgep = dynamic_cast<SplitEdge*>(edgep); oedgep->setIgnoreThisStep(); } } if (debug()>=9) { m_graph.dumpDotFilePrefixed("splitg_nodup", false); } // Weak coloring to determine what needs to remain grouped // in a single always. This follows all edges excluding: // - those we pruned above // - PostEdges, which are done later m_graph.weaklyConnected(&SplitEdge::followScoreboard); }
void cleanupBlockGraph(AstNode* nodep) { // Transform the graph into what we need UINFO(5, "ReorderBlock "<<nodep<<endl); m_graph.removeRedundantEdges(&V3GraphEdge::followAlwaysTrue); if (debug()>=9) { m_graph.dumpDotFilePrefixed("reorderg_nodup", false); //m_graph.dump(); cout<<endl; } // Mark all the logic for this step // Vertex::m_user begin: true indicates logic for this step m_graph.userClearVertices(); for (AstNode* nextp=nodep; nextp; nextp=nextp->nextp()) { SplitLogicVertex* vvertexp = (SplitLogicVertex*)nextp->user3p(); vvertexp->user(true); } // If a var vertex has only inputs, it's a input-only node, // and can be ignored for coloring **this block only** SplitEdge::incrementStep(); pruneDepsOnInputs(); // For reordering this single block only, mark all logic // vertexes not involved with this step as unimportant for (V3GraphVertex* vertexp = m_graph.verticesBeginp(); vertexp; vertexp=vertexp->verticesNextp()) { if (SplitLogicVertex* vvertexp = dynamic_cast<SplitLogicVertex*>(vertexp)) { if (!vvertexp->user()) { for (V3GraphEdge* edgep = vertexp->inBeginp(); edgep; edgep=edgep->inNextp()) { SplitEdge* oedgep = dynamic_cast<SplitEdge*>(edgep); oedgep->setIgnoreThisStep(); } for (V3GraphEdge* edgep = vertexp->outBeginp(); edgep; edgep=edgep->outNextp()) { SplitEdge* oedgep = dynamic_cast<SplitEdge*>(edgep); oedgep->setIgnoreThisStep(); } } } } // Weak coloring to determine what needs to remain in order // This follows all step-relevant edges excluding PostEdges, which are done later m_graph.weaklyConnected(&SplitEdge::followScoreboard); // Add hard orderings between all nodes of same color, in the order they appeared vl_unordered_map<uint32_t, SplitLogicVertex*> lastOfColor; for (AstNode* nextp=nodep; nextp; nextp=nextp->nextp()) { SplitLogicVertex* vvertexp = (SplitLogicVertex*)nextp->user3p(); uint32_t color = vvertexp->color(); if (!color) nextp->v3fatalSrc("No node color assigned"); if (lastOfColor[color]) { new SplitStrictEdge(&m_graph, lastOfColor[color], vvertexp); } lastOfColor[color] = vvertexp; } // And a real ordering to get the statements into something reasonable // We don't care if there's cutable violations here... // Non-cutable violations should be impossible; as those edges are program-order if (debug()>=9) m_graph.dumpDotFilePrefixed((string)"splitg_preo", false); m_graph.acyclic(&SplitEdge::followCyclic); m_graph.rank(&SplitEdge::followCyclic); // Or order(), but that's more expensive if (debug()>=9) m_graph.dumpDotFilePrefixed((string)"splitg_opt", false); }
// From *this, populate *mstp with the minimum spanning tree. // *mstp must be initially empty. void makeMinSpanningTree(TspGraphTmpl* mstp) { UASSERT(mstp->empty(), "Output graph must start empty"); // Use Prim's algorithm to efficiently construct the MST. vl_unordered_set<Vertex*> visited_set; EdgeCmp cmp; typedef std::set<V3GraphEdge*, EdgeCmp&> PendingEdgeSet; // This is the set of pending edges from visited to unvisited // nodes. PendingEdgeSet pendingEdges(cmp); vluint32_t vertCount = 0; for (V3GraphVertex* vxp = verticesBeginp(); vxp; vxp = vxp->verticesNextp()) { mstp->addVertex(castVertexp(vxp)->key()); vertCount++; } // Choose an arbitrary start vertex and visit it; // all incident edges from this vertex go into a pending edge set. Vertex* start_vertexp = castVertexp(verticesBeginp()); visited_set.insert(start_vertexp); for (V3GraphEdge* edgep = start_vertexp->outBeginp(); edgep; edgep = edgep->outNextp()) { pendingEdges.insert(edgep); } // Repeatedly find the least costly edge in the pending set. // If it connects to an unvisited node, visit that node and update // the pending edge set. If it connects to an already visited node, // discard it and repeat again. unsigned edges_made = 0; while (!pendingEdges.empty()) { typename PendingEdgeSet::iterator firstIt = pendingEdges.begin(); V3GraphEdge* bestEdgep = *firstIt; pendingEdges.erase(firstIt); // bestEdgep->fromp() should be already seen Vertex* from_vertexp = castVertexp(bestEdgep->fromp()); UASSERT(visited_set.find(from_vertexp) != visited_set.end(), "Can't find vertex"); // If the neighbor is not yet visited, visit it and add its edges // to the pending set. Vertex* neighborp = castVertexp(bestEdgep->top()); if (visited_set.find(neighborp) == visited_set.end()) { int bestCost = bestEdgep->weight(); UINFO(6, "bestCost = "<<bestCost <<" from "<<from_vertexp->key() <<" to "<<neighborp->key()<<endl); // Create the edge in our output MST graph mstp->addEdge(from_vertexp->key(), neighborp->key(), bestCost); edges_made++; // Mark this vertex as visited visited_set.insert(neighborp); // Update the pending edges with new edges for (V3GraphEdge* edgep = neighborp->outBeginp(); edgep; edgep = edgep->outNextp()) { pendingEdges.insert(edgep); } } else { UINFO(6, "Discarding edge to already-visited neighbor " <<neighborp->key()<<endl); } } UASSERT(edges_made + 1 == vertCount, "Algorithm failed"); UASSERT(visited_set.size() == vertCount, "Algorithm failed"); }
void V3Graph::clearColors() { // Reset colors for (V3GraphVertex* vertexp = verticesBeginp(); vertexp; vertexp=vertexp->verticesNextp()) { vertexp->m_color = 0; } }
void V3Graph::dumpDotFile(const string& filename, bool colorAsSubgraph) { // This generates a file used by graphviz, http://www.graphviz.org // "hardcoded" parameters: const auto_ptr<ofstream> logp (V3File::new_ofstream(filename)); if (logp->fail()) v3fatalSrc("Can't write "<<filename); // Header *logp<<"digraph v3graph {\n"; *logp<<"\tgraph\t[label=\""<<filename<<"\",\n"; *logp<<"\t\t labelloc=t, labeljust=l,\n"; *logp<<"\t\t //size="<<"\"7.5,10\","<<"\n"; *logp<<"\t\t rankdir="<<dotRankDir()<<"];\n"; // List of all possible subgraphs typedef multimap<string,V3GraphVertex*> SubgraphMmap; SubgraphMmap subgraphs; for (V3GraphVertex* vertexp = verticesBeginp(); vertexp; vertexp=vertexp->verticesNextp()) { string vertexSubgraph = (colorAsSubgraph && vertexp->color()) ? cvtToStr(vertexp->color()) : ""; subgraphs.insert(make_pair(vertexSubgraph, vertexp)); } // We use a map here, as we don't want to corrupt anything (userp) in the graph, // and we don't care if this is slow. map<V3GraphVertex*,int> numMap; // Print vertices int n=0; string subgr; for (SubgraphMmap::iterator it = subgraphs.begin(); it!=subgraphs.end(); ++it) { string vertexSubgraph = it->first; V3GraphVertex* vertexp = it->second; numMap[vertexp] = n; if (subgr != vertexSubgraph) { if (subgr!="") *logp<<"\t};\n"; subgr = vertexSubgraph; if (subgr!="") *logp<<"\tsubgraph cluster_"<<subgr<<" {\n"; } if (subgr!="") *logp<<"\t"; *logp<<"\tn"<<vertexp->dotName()<<(n++) <<"\t[fontsize=8 " <<"label=\""<<(vertexp->name()!="" ? vertexp->name() : "\\N"); if (vertexp->rank()) *logp<<" r"<<vertexp->rank(); if (vertexp->fanout()) *logp<<" f"<<vertexp->fanout(); if (vertexp->color()) *logp<<"\\n c"<<vertexp->color(); *logp<<"\""; *logp<<", color="<<vertexp->dotColor(); if (vertexp->dotStyle()!="") *logp<<", style="<<vertexp->dotStyle(); if (vertexp->dotShape()!="") *logp<<", shape="<<vertexp->dotShape(); *logp<<"];\n"; } if (subgr!="") *logp<<"\t};\n"; // Print edges for (V3GraphVertex* vertexp = verticesBeginp(); vertexp; vertexp=vertexp->verticesNextp()) { for (V3GraphEdge* edgep = vertexp->outBeginp(); edgep; edgep=edgep->outNextp()) { if (edgep->weight()) { int fromVnum = numMap[edgep->fromp()]; int toVnum = numMap[edgep->top()]; *logp<<"\tn"<<edgep->fromp()->dotName()<<fromVnum <<" -> n"<<edgep->top()->dotName()<<toVnum <<" [" //<<"fontsize=8 label=\""<<(edgep->name()!="" ? edgep->name() : "\\E")<<"\"" <<"fontsize=8 label=\""<<(edgep->dotLabel()!="" ? edgep->dotLabel() : "")<<"\"" <<" weight="<<edgep->weight() <<" color="<<edgep->dotColor(); if (edgep->dotStyle()!="") *logp<<" style="<<edgep->dotStyle(); //if (edgep->cutable()) { *logp<<",constraint=false"; } // to rank without following edges *logp<<"];\n"; } } } // Vertex::m_user end, now unused // Trailer *logp << "}\n"; logp->close(); cout << "dot -Tpdf -o ~/a.pdf "<<filename<<endl; }
void GateVisitor::optimizeSignals(bool allowMultiIn) { for (V3GraphVertex* itp = m_graph.verticesBeginp(); itp; itp=itp->verticesNextp()) { if (GateVarVertex* vvertexp = dynamic_cast<GateVarVertex*>(itp)) { if (vvertexp->inEmpty()) { vvertexp->clearReducibleAndDedupable("inEmpty"); // Can't deal with no sources if (!vvertexp->isTop() // Ok if top inputs are driverless && !vvertexp->varScp()->varp()->valuep() && !vvertexp->varScp()->varp()->isSigPublic()) { UINFO(4, "No drivers "<<vvertexp->varScp()<<endl); if (0) { // If we warned here after constant propagation, what the user considered // reasonable logic may have disappeared. Issuing a warning would // thus be confusing. V3Undriven now handles this. vvertexp->varScp()->varp()->v3warn(UNDRIVEN,"Signal has no drivers " <<vvertexp->scopep()->prettyName()<<"." <<vvertexp->varScp()->varp()->prettyName()); } } } else if (!vvertexp->inSize1()) { vvertexp->clearReducibleAndDedupable("size!1"); // Can't deal with more than one src } // Reduce it? if (!vvertexp->reducible()) { UINFO(8, "SigNotRed "<<vvertexp->name()<<endl); } else { UINFO(8, "Sig "<<vvertexp->name()<<endl); GateLogicVertex* logicVertexp = dynamic_cast<GateLogicVertex*> (vvertexp->inBeginp()->fromp()); UINFO(8, " From "<<logicVertexp->name()<<endl); AstNode* logicp = logicVertexp->nodep(); if (logicVertexp->reducible()) { // Can we eliminate? GateOkVisitor okVisitor(logicp, vvertexp->isClock(), false); bool multiInputs = okVisitor.rhsVarRefs().size() > 1; // Was it ok? bool doit = okVisitor.isSimple(); if (doit && multiInputs) { if (!allowMultiIn) doit = false; // Doit if one input, or not used, or used only once, ignoring traces int n=0; for (V3GraphEdge* edgep = vvertexp->outBeginp(); edgep; edgep = edgep->outNextp()) { GateLogicVertex* consumeVertexp = dynamic_cast<GateLogicVertex*>(edgep->top()); if (!consumeVertexp->slow()) { // Not tracing or other slow path junk if (edgep->top()->outBeginp()) { // Destination is itself used n += edgep->weight(); } } if (n>1) { doit = false; break; } } } // Process it if (!doit) { if (allowMultiIn && (debug()>=9)) { UINFO(9, "Not ok simp"<<okVisitor.isSimple()<<" mi"<<multiInputs <<" ob"<<vvertexp->outBeginp()<<" on"<<(vvertexp->outBeginp()?vvertexp->outBeginp()->outNextp():0) <<" "<<vvertexp->name() <<endl); for (V3GraphEdge* edgep = vvertexp->outBeginp(); edgep; edgep = edgep->outNextp()) { GateLogicVertex* consumeVertexp = dynamic_cast<GateLogicVertex*>(edgep->top()); UINFO(9, " edge "<<edgep<<" to: "<<consumeVertexp->nodep()<<endl); } for (V3GraphEdge* edgep = vvertexp->inBeginp(); edgep; edgep = edgep->inNextp()) { GateLogicVertex* consumeVertexp = dynamic_cast<GateLogicVertex*>(edgep->fromp()); UINFO(9, " edge "<<edgep<<" from: "<<consumeVertexp->nodep()<<endl); } } } else { AstNode* substp = okVisitor.substTree(); if (debug()>=5) logicp->dumpTree(cout,"\telimVar: "); if (debug()>=5) substp->dumpTree(cout,"\t subst: "); ++m_statSigs; bool removedAllUsages = true; for (V3GraphEdge* edgep = vvertexp->outBeginp(); edgep; ) { GateLogicVertex* consumeVertexp = dynamic_cast<GateLogicVertex*>(edgep->top()); AstNode* consumerp = consumeVertexp->nodep(); if (!elimLogicOkOutputs(consumeVertexp, okVisitor/*ref*/)) { // Cannot optimize this replacement removedAllUsages = false; edgep = edgep->outNextp(); } else { optimizeElimVar(vvertexp->varScp(), substp, consumerp); // If the new replacement referred to a signal, // Correct the graph to point to this new generating variable const GateVarRefList& rhsVarRefs = okVisitor.rhsVarRefs(); for (GateVarRefList::const_iterator it = rhsVarRefs.begin(); it != rhsVarRefs.end(); ++it) { AstVarScope* newvarscp = (*it)->varScopep(); UINFO(9," Point-to-new vertex "<<newvarscp<<endl); GateVarVertex* varvertexp = makeVarVertex(newvarscp); new V3GraphEdge(&m_graph, varvertexp, consumeVertexp, 1); // Propagate clock attribute onto generating node varvertexp->propagateAttrClocksFrom(vvertexp); } // Remove the edge edgep->unlinkDelete(); VL_DANGLING(edgep); ++m_statRefs; edgep = vvertexp->outBeginp(); } } if (removedAllUsages) { // Remove input links while (V3GraphEdge* edgep = vvertexp->inBeginp()) { edgep->unlinkDelete(); VL_DANGLING(edgep); } // Clone tree so we remember it for tracing, and keep the pointer // to the "ALWAYS" part of the tree as part of this statement // That way if a later signal optimization that retained a pointer to the always // can optimize it further logicp->unlinkFrBack(); vvertexp->varScp()->valuep(logicp); logicp = NULL; // Mark the vertex so we don't mark it as being unconsumed in the next step vvertexp->user(true); logicVertexp->user(true); } } } } } } }