void test(string lhss, string op, string rhss, string exps) { char* l1 = strdup(lhss.c_str()); char* r1 = strdup(rhss.c_str()); char* e1 = strdup(exps.c_str()); V3Number lhnum (new FileLine ("ck",__LINE__), l1); V3Number rhnum (new FileLine ("ck",__LINE__), r1); V3Number expnum (new FileLine("ck",__LINE__), e1); V3Number gotnum (new FileLine("ck",__LINE__), expnum.width()); if (op=="redOr") gotnum.opRedOr (lhnum); else if (op=="redAnd") gotnum.opRedAnd (lhnum); else if (op=="redXor") gotnum.opRedXor (lhnum); else if (op=="redXnor") gotnum.opRedXnor (lhnum); else if (op=="concat") gotnum.opConcat (lhnum,rhnum); else if (op=="repl") gotnum.opRepl (lhnum,rhnum); else if (op=="~") gotnum.opNot (lhnum); else if (op=="!") gotnum.opLogNot (lhnum); else if (op=="negate") gotnum.opNegate (lhnum); else if (op=="+") gotnum.opAdd (lhnum,rhnum); else if (op=="-") gotnum.opSub (lhnum,rhnum); else if (op=="*") gotnum.opMul (lhnum,rhnum); else if (op=="/") gotnum.opDiv (lhnum,rhnum); else if (op=="%") gotnum.opModDiv (lhnum,rhnum); else if (op=="&") gotnum.opAnd (lhnum,rhnum); else if (op=="|") gotnum.opOr (lhnum,rhnum); else if (op=="<") gotnum.opLt (lhnum,rhnum); else if (op==">") gotnum.opGt (lhnum,rhnum); else if (op==">>") gotnum.opShiftR (lhnum,rhnum); else if (op=="<<") gotnum.opShiftL (lhnum,rhnum); else if (op=="==") gotnum.opEq (lhnum,rhnum); else if (op=="===") gotnum.opCaseEq (lhnum,rhnum); else if (op=="==?") gotnum.opWildEq (lhnum,rhnum); else if (op=="!=") gotnum.opNeq (lhnum,rhnum); else if (op=="!==") gotnum.opCaseNeq (lhnum,rhnum); else if (op=="!=?") gotnum.opWildNeq (lhnum,rhnum); else if (op=="<=") gotnum.opLte (lhnum,rhnum); else if (op==">=") gotnum.opGte (lhnum,rhnum); else if (op=="&&") gotnum.opLogAnd (lhnum,rhnum); else if (op=="||") gotnum.opLogOr (lhnum,rhnum); else v3fatalSrc("Bad opcode: "<<op); UINFO(0,"------- Test:\n" <<" "<<lhnum<<" "<<op<<endl <<" "<<rhnum<<endl <<" = "<<expnum<<endl <<" =? "<<gotnum<<endl); V3Number ok (new FileLine("ck",__LINE__), 1); ok.opCaseEq(expnum,gotnum); if (ok.toUInt()!=1) { v3fatalSrc("%Error:Test FAILED\n"); } }
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; }
bool compareDfaOrigins(const DfaStates& nfasWithInput, DfaVertex* dfa2p) { // Return true if the NFA nodes both DFAs came from are the same list // Assume there are no duplicates in either input list or NFAs under dfa2 nextStep(); // Mark all input vertexes int num1s = 0; for (DfaStates::const_iterator nfaIt=nfasWithInput.begin(); nfaIt!=nfasWithInput.end(); ++nfaIt) { DfaVertex* nfaStatep = *nfaIt; nfaStatep->user(m_step); num1s++; } if (!num1s) v3fatalSrc("DFA node construction that contains no NFA states"); // Check comparison; must all be marked // (Check all in dfa2p were in dfa1p) int num2s = 0; for (V3GraphEdge* dfaEdgep = dfa2p->outBeginp(); dfaEdgep; dfaEdgep=dfaEdgep->outNextp()) { if (nfaState(dfaEdgep->top())) { if (dfaEdgep->top()->user() != m_step) return false; num2s++; } } // If we saw all of the nodes, then they have the same number of hits // (Else something in dfa1p that wasn't in dfa2p) if (num1s != num2s) return false; // Match return true; }
void LinkCellsGraph::loopsMessageCb(V3GraphVertex* vertexp) { if (LinkCellsVertex* vvertexp = dynamic_cast<LinkCellsVertex*>(vertexp)) { vvertexp->modp()->v3error("Recursive module (module instantiates itself): " <<vvertexp->modp()->prettyName()); V3Error::abortIfErrors(); } else { // Everything should match above, but... v3fatalSrc("Recursive instantiations"); } }
void V3Error::init() { for (int i=0; i<V3ErrorCode::_ENUM_MAX; i++) { s_describedEachWarn[i] = false; s_pretendError[i] = V3ErrorCode(i).pretendError(); } if (string(V3ErrorCode(V3ErrorCode::_ENUM_MAX).ascii()) != " MAX") { v3fatalSrc("Enum table in V3ErrorCode::EC_ascii() is munged"); } }
void V3Graph::test() { // Execute all of the tests UINFO(2,__FUNCTION__<<": "<<endl); { V3GraphTestStrong test; test.run(); } { V3GraphTestAcyc test; test.run(); } { V3GraphTestVars test; test.run(); } { V3GraphTestDfa test; test.run(); } { V3GraphTestImport test; test.run(); } if (V3GraphTest::debug()) v3fatalSrc("Exiting due to graph testing enabled"); }
void V3Stats::statsReport() { UINFO(2,__FUNCTION__<<": "<<endl); // Open stats file string filename = v3Global.opt.makeDir()+"/"+v3Global.opt.prefix()+"__stats.txt"; ofstream* ofp (V3File::new_ofstream(filename)); if (ofp->fail()) v3fatalSrc("Can't write "<<filename); StatsReport reporter (ofp); // Cleanup ofp->close(); delete ofp; VL_DANGLING(ofp); }
void lifeToAbove() { // Any varrefs under a if/else branch affect statements outside and after the if/else if (!m_aboveLifep) v3fatalSrc("Pushing life when already at the top level"); for (LifeMap::iterator it = m_map.begin(); it!=m_map.end(); ++it) { AstVarScope* nodep = it->first; m_aboveLifep->complexAssignFind(nodep); if (it->second.everSet()) { // Record there may be an assignment, so we don't constant propagate across the if. complexAssignFind(nodep); } else { // Record consumption, so we don't eliminate earlier assignments consumedFind(nodep); } } }
void V3TSP::selfTestString() { typedef TspGraphTmpl<string> Graph; Graph graph; graph.addVertex("0"); graph.addVertex("1"); graph.addVertex("2"); graph.addVertex("3"); graph.addEdge("0", "1", 3943); graph.addEdge("0", "2", 3456); graph.addEdge("0", "3", 4920); graph.addEdge("1", "2", 2730); graph.addEdge("1", "3", 8199); graph.addEdge("2", "3", 4130); Graph minGraph; graph.makeMinSpanningTree(&minGraph); if (debug() >= 6) minGraph.dumpGraphFilePrefixed("minGraph"); std::vector<string> oddDegree = minGraph.getOddDegreeKeys(); Graph matching; graph.perfectMatching(oddDegree, &matching); if (debug() >= 6) matching.dumpGraphFilePrefixed("matching"); minGraph.combineGraph(matching); std::vector<string> result; minGraph.findEulerTour(&result); std::vector<string> expect; expect.push_back("0"); expect.push_back("2"); expect.push_back("1"); expect.push_back("2"); expect.push_back("3"); if (expect != result) { for (std::vector<string>::iterator it = result.begin(); it != result.end(); ++it) { cout<<*it<<" "; } cout<<endl; v3fatalSrc("TSP string self-test fail. Result (above) did not match expectation."); } }
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); } } } } } } }
uint32_t hashDfaOrigins(DfaVertex* dfaStatep) { // Find the NFA states this dfa came from, // Record a checksum, so we can search for it later by the list of nfa nodes. // The order of the nodes is not deterministic; the hash thus must not depend on order of edges uint32_t hash = 0; // Foreach NFA state (this DFA state was formed from) if (debug()) nextStep(); for (V3GraphEdge* dfaEdgep = dfaStatep->outBeginp(); dfaEdgep; dfaEdgep=dfaEdgep->outNextp()) { if (nfaState(dfaEdgep->top())) { DfaVertex* nfaStatep = static_cast<DfaVertex*>(dfaEdgep->top()); hash ^= hashVertex(nfaStatep); if (debug()) { if (nfaStatep->user()==m_step) v3fatalSrc("DFA state points to duplicate NFA state."); nfaStatep->user(m_step); } } } return hash; }
void V3Hashed::dumpFile(const string& filename, bool tree) { const VL_UNIQUE_PTR<ofstream> logp (V3File::new_ofstream(filename)); if (logp->fail()) v3fatalSrc("Can't write "<<filename); map<int,int> dist; V3Hash lasthash; int num_in_bucket = 0; for (HashMmap::iterator it=begin(); 1; ++it) { if (lasthash != it->first || it==end()) { if (it!=end()) lasthash = it->first; if (num_in_bucket) { if (dist.find(num_in_bucket)==dist.end()) { dist.insert(make_pair(num_in_bucket,1)); } else { ++dist[num_in_bucket]; } } num_in_bucket = 0; } if (it==end()) break; num_in_bucket++; } *logp <<"\n*** STATS:\n"<<endl; *logp<<" #InBucket Occurrences\n"; for (map<int,int>::iterator it=dist.begin(); it!=dist.end(); ++it) { *logp<<" "<<setw(9)<<it->first<<" "<<setw(12)<<it->second<<endl; } *logp <<"\n*** Dump:\n"<<endl; for (HashMmap::iterator it=begin(); it!=end(); ++it) { if (lasthash != it->first) { lasthash = it->first; *logp <<" "<<it->first<<endl; } *logp <<"\t"<<it->second<<endl; // Dumping the entire tree may make nearly N^2 sized dumps, // because the nodes under this one may also be in the hash table! if (tree) it->second->dumpTree(*logp,"\t\t"); } }
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 simplifyPli() { // For now, we'll not gate any logic with PLI lvariables in it. In // the future we may move PLI statements. One way to do so is to // all below pli VARs to go IfVertex -> PliVertex -> VarVertex then // they all must get colored the same. There may be a lot of // duplicated edges to the PLI; they'll need cleanup. All of the // later optimizations need to deal, and the GaterBodyVisitor needs // to know how to move them. if (m_pliVertexp) { // Follow PLI out edges to find all relevant variables for (V3GraphEdge* nextp,* edgep = m_pliVertexp->outBeginp(); edgep; edgep = nextp) { nextp = edgep->outNextp(); // We may edit the list if (GaterVarVertex* vVxp = dynamic_cast<GaterVarVertex*>(edgep->top())) { vVxp->unlinkDelete(&m_graph); vVxp=NULL; edgep=NULL; } else { m_graph.dump(); v3fatalSrc("PLI vertex points to non-signal"); } } m_pliVertexp->unlinkDelete(&m_graph); m_pliVertexp = NULL; } }
string AstVar::vlArgType(bool named, bool forReturn, bool forFunc) const { if (forReturn) named=false; if (forReturn) v3fatalSrc("verilator internal data is never passed as return, but as first argument"); string arg; if (isWide() && isInOnly()) arg += "const "; AstBasicDType* bdtypep = basicp(); bool strtype = bdtypep && bdtypep->keyword()==AstBasicDTypeKwd::STRING; if (bdtypep && bdtypep->keyword()==AstBasicDTypeKwd::CHARPTR) { arg += "const char*"; } else if (bdtypep && bdtypep->keyword()==AstBasicDTypeKwd::SCOPEPTR) { arg += "const VerilatedScope*"; } else if (bdtypep && bdtypep->keyword()==AstBasicDTypeKwd::DOUBLE) { arg += "double"; } else if (bdtypep && bdtypep->keyword()==AstBasicDTypeKwd::FLOAT) { arg += "float"; } else if (strtype) { if (isInOnly()) arg += "const "; arg += "string"; } else if (widthMin() <= 8) { arg += "CData"; } else if (widthMin() <= 16) { arg += "SData"; } else if (widthMin() <= VL_WORDSIZE) { arg += "IData"; } else if (isQuad()) { arg += "QData"; } else if (isWide()) { arg += "WData"; // []'s added later } if (isWide() && !strtype) { arg += " (& "+name(); arg += ")["+cvtToStr(widthWords())+"]"; } else { if (forFunc && (isOutput() || (strtype && isInput()))) arg += "&"; if (named) arg += " "+name(); } return arg; }
void scoreboardPopStmt() { //UINFO(9," pop"<<endl); if (m_stmtStackps.empty()) v3fatalSrc("Stack underflow"); m_stmtStackps.pop_back(); }
static bool followCyclic(const V3GraphEdge* edgep) { const SplitEdge* oedgep = dynamic_cast<const SplitEdge*>(edgep); if (!oedgep) v3fatalSrc("Following edge of non-SplitEdge type"); if (oedgep->ignoreThisStep()) return false; return true; }
void findEulerTourRecurse(vl_unordered_set<unsigned>* markedEdgesp, Vertex* startp, std::vector<T_Key>* sortedOutp) { Vertex* cur_vertexp = startp; // Go on a random tour. Fun! std::vector<Vertex*> tour; do { UINFO(6, "Adding "<<cur_vertexp->key()<<" to tour.\n"); tour.push_back(cur_vertexp); // Look for an arbitrary edge we've not yet marked for (V3GraphEdge* edgep = cur_vertexp->outBeginp(); edgep; edgep = edgep->outNextp()) { vluint32_t edgeId = edgep->user(); if (markedEdgesp->end() == markedEdgesp->find(edgeId)) { // This edge is not yet marked, so follow it. markedEdgesp->insert(edgeId); Vertex* neighborp = castVertexp(edgep->top()); UINFO(6, "following edge "<<edgeId <<" from "<<cur_vertexp->key() <<" to "<<neighborp->key()<<endl); cur_vertexp = neighborp; goto found; } } v3fatalSrc("No unmarked edges found in tour"); found: ; } while (cur_vertexp != startp); UINFO(6, "stopped, got back to start of tour @ "<<cur_vertexp->key()<<endl); // Look for nodes on the tour that still have // un-marked edges. If we find one, recurse. for (typename std::vector<Vertex*>::iterator it = tour.begin(); it != tour.end(); ++it) { Vertex* vxp = *it; bool recursed; do { recursed = false; // Look for an arbitrary edge at vxp we've not yet marked for (V3GraphEdge* edgep = vxp->outBeginp(); edgep; edgep = edgep->outNextp()) { vluint32_t edgeId = edgep->user(); if (markedEdgesp->end() == markedEdgesp->find(edgeId)) { UINFO(6, "Recursing.\n"); findEulerTourRecurse(markedEdgesp, vxp, sortedOutp); recursed = true; goto recursed; } } recursed: ; } while (recursed); sortedOutp->push_back(vxp->key()); } UINFO(6, "Tour was: "); for (typename std::vector<Vertex*>::iterator it = tour.begin(); it != tour.end(); ++it) { Vertex* vxp = *it; UINFONL(6, " "<<vxp->key()); } UINFONL(6, "\n"); }
void V3TSP::selfTestStates() { // Linear test -- coords all along the x-axis { V3TSP::StateVec states; TspTestState s10(10,0); TspTestState s60(60,0); TspTestState s20(20,0); TspTestState s100(100,0); TspTestState s5(5,0); states.push_back(&s10); states.push_back(&s60); states.push_back(&s20); states.push_back(&s100); states.push_back(&s5); V3TSP::StateVec result; tspSort(states, &result); V3TSP::StateVec expect; expect.push_back(&s100); expect.push_back(&s60); expect.push_back(&s20); expect.push_back(&s10); expect.push_back(&s5); if (expect != result) { for (V3TSP::StateVec::iterator it = result.begin(); it != result.end(); ++it) { const TspTestState* statep = dynamic_cast<const TspTestState*>(*it); cout<<statep->xpos()<<" "; } cout<<endl; v3fatalSrc("TSP linear self-test fail. Result (above) did not match expectation."); } } // Second test. Coords are distributed in 2D space. // Test that tspSort() will rotate the list for minimum cost. { V3TSP::StateVec states; TspTestState a(0,0); TspTestState b(100,0); TspTestState c(200,0); TspTestState d(200,100); TspTestState e(150,150); TspTestState f(0,150); TspTestState g(0,100); states.push_back(&a); states.push_back(&b); states.push_back(&c); states.push_back(&d); states.push_back(&e); states.push_back(&f); states.push_back(&g); V3TSP::StateVec result; tspSort(states, &result); V3TSP::StateVec expect; expect.push_back(&f); expect.push_back(&g); expect.push_back(&a); expect.push_back(&b); expect.push_back(&c); expect.push_back(&d); expect.push_back(&e); if (expect != result) { for (V3TSP::StateVec::iterator it = result.begin(); it != result.end(); ++it) { const TspTestState* statep = dynamic_cast<const TspTestState*>(*it); cout<<statep->xpos()<<","<<statep->ypos()<<" "; } cout<<endl; v3fatalSrc("TSP 2d cycle=false self-test fail. Result (above) did not match expectation."); } } }
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; }