Esempio n. 1
0
    AstNode* nafgCreateRecurse(V3GraphVertex* vertexp, uint32_t generation) {
	// Forewards follow user() marked previously and build tree
	AstNode* nodep = NULL;
	// OR across all edges found at this level
	//UINFO(9," nafgEnter: v "<<(void*)(vertexp)<<" "<<vertexp->name()<<endl);
	for (V3GraphEdge* edgep = vertexp->outBeginp(); edgep; edgep = edgep->outNextp()) {
	    if (edgep->user() == generation) {
		GaterEdge* cedgep = static_cast<GaterEdge*>(edgep);
		AstNode* eqnp = NULL;
		//UINFO(9," nafgFollow: "<<(void*)(edgep)<<" "<<edgep->name()<<endl);
		if (dynamic_cast<GaterHeadVertex*>(edgep->fromp())) {
		    // Just OR in all lower terms
		    eqnp = nafgCreateRecurse(edgep->top(), generation);
		} else if (GaterIfVertex* cVxp = dynamic_cast<GaterIfVertex*>(edgep->fromp())) {
		    // Edges from IFs represent a real IF branch in the equation tree
		    //UINFO(9,"  ifver "<<(void*)(edgep)<<" cc"<<edgep->dotColor()<<endl);
		    eqnp = cVxp->nodep()->condp()->cloneTree(true);
		    if (eqnp && cedgep->ifelseFalse()) {
			eqnp = new AstNot(eqnp->fileline(),eqnp);
		    }
		    // We need to AND this term onto whatever was found below it
		    AstNode* belowp = nafgCreateRecurse(edgep->top(), generation);
		    if (belowp) eqnp = new AstAnd(eqnp->fileline(),eqnp,belowp);
		}
		// Top level we could choose to make multiple gaters, or ORs under the gater
		// Right now we'll put OR lower down and let other optimizations deal
		if (nodep) nodep = new AstOr(eqnp->fileline(),nodep,eqnp);
		else nodep = eqnp;
		//if (debug()>=9) nodep->dumpTree(cout,"      followExpr: ");
	    }
	}
	//UINFO(9," nafgExit:  "<<(void*)(vertexp)<<" "<<vertexp->name()<<endl);
	return nodep;
    }
Esempio n. 2
0
int GaterVertex::sortCmp(const V3GraphVertex* rhsp) const {
    const GaterVertex* crhsp = static_cast<const GaterVertex*>(rhsp);
    // We really only care about ordering Var's together, but...
    // First put same type together
    if (typeNum() < crhsp->typeNum()) return -1;
    if (typeNum() > crhsp->typeNum()) return 1;
    // If variable, group by same input fanin
    // (know they're the same type based on above compare)
    if (dynamic_cast<const GaterVarVertex*>(this)) {
	// We've already sorted by edges, so just see if same tree
	// If this gets too slow, we could compute a hash up front
	V3GraphEdge* lEdgep = this->inBeginp();
	V3GraphEdge* rEdgep = rhsp->inBeginp();
	while (lEdgep && rEdgep) {
	    const GaterEdge* clEdgep = static_cast<const GaterEdge*>(lEdgep);
	    const GaterEdge* crEdgep = static_cast<const GaterEdge*>(rEdgep);
	    if (lEdgep->fromp()->rank() < rEdgep->fromp()->rank()) return -1;
	    if (lEdgep->fromp()->rank() > rEdgep->fromp()->rank()) return 1;
	    if (clEdgep->ifelse() < crEdgep->ifelse()) return -1;
	    if (clEdgep->ifelse() > crEdgep->ifelse()) return 1;
	    lEdgep = lEdgep->inNextp();
	    rEdgep = rEdgep->inNextp();
	}
	if (!lEdgep && !rEdgep) return 0;
	return lEdgep ? -1 : 1;
    }
    // Finally by rank of this vertex
    if (rank() < rhsp->rank()) return -1;
    if (rank() > rhsp->rank()) return 1;
    return 0;
}
Esempio n. 3
0
    // Returns only the result from the LAST vertex iterated over
    AstNUser* iterateInEdges(GateGraphBaseVisitor& v, AstNUser* vup=NULL) {
	AstNUser* retp = NULL;
	for (V3GraphEdge* edgep = inBeginp(); edgep; edgep = edgep->inNextp()) {
	    retp = dynamic_cast<GateEitherVertex*>(edgep->fromp())->accept(v, vup);
	}
	return retp;
    }
Esempio n. 4
0
    void nafgMarkRecurse(V3GraphVertex* vertexp, uint32_t generation) {
	// Backwards mark user() on the path we recurse
	//UINFO(9," nafgMark: v "<<(void*)(vertexp)<<" "<<vertexp->name()<<endl);
	for (V3GraphEdge* edgep = vertexp->inBeginp(); edgep; edgep = edgep->inNextp()) {
	    //UINFO(9," nafgMark: "<<(void*)(edgep)<<" "<<edgep->name()<<endl);
	    edgep->user(generation);
	    nafgMarkRecurse(edgep->fromp(), generation);
	}
    }
Esempio n. 5
0
void GateVisitor::consumedMarkRecurse(GateEitherVertex* vertexp) {
    if (vertexp->user()) return;	// Already marked
    vertexp->user(true);
    if (!vertexp->consumed()) vertexp->setConsumed("propagated");
    // Walk sources and mark them too
    for (V3GraphEdge* edgep = vertexp->inBeginp(); edgep; edgep = edgep->inNextp()) {
	GateEitherVertex* eFromVertexp = (GateEitherVertex*)edgep->fromp();
	consumedMarkRecurse(eFromVertexp);
    }
}
Esempio n. 6
0
uint32_t V3GraphVertex::inHash() const {
    // We want the same hash ignoring the order of edges.
    // So we need an associative operator, like XOR.
    // However with XOR multiple edges to the same source will cancel out,
    // so we use ADD.  (Generally call this only after removing duplicates though)
    uint32_t hash=0;
    for (V3GraphEdge* edgep = this->inBeginp(); edgep; edgep=edgep->inNextp()) {
	hash += cvtToHash(edgep->fromp());
    }
    return hash;
}
Esempio n. 7
0
void V3GraphVertex::rerouteEdges(V3Graph* graphp) {
    // Make new edges for each from/to pair
    for (V3GraphEdge* iedgep = inBeginp(); iedgep; iedgep=iedgep->inNextp()) {
	for (V3GraphEdge* oedgep = outBeginp(); oedgep; oedgep=oedgep->outNextp()) {
	    new V3GraphEdge (graphp, iedgep->fromp(), oedgep->top(),
			     min(iedgep->weight(),oedgep->weight()),
			     iedgep->cutable() && oedgep->cutable());
	}
    }
    // Remove old edges
    unlinkEdges(graphp);
}
Esempio n. 8
0
    virtual AstNUser* visit(GateVarVertex *vvertexp, AstNUser*) {
	// Check that we haven't been here before
	if (vvertexp->varScp()->user2()) return NULL;
	vvertexp->varScp()->user2(true);

	AstNodeVarRef* dupVarRefp = (AstNodeVarRef*) vvertexp->iterateInEdges(*this, (AstNUser*) vvertexp);

	if (dupVarRefp && vvertexp->inSize1()) {
	    V3GraphEdge* edgep = vvertexp->inBeginp();
	    GateLogicVertex* lvertexp = (GateLogicVertex*)edgep->fromp();
	    if (!vvertexp->dedupable()) vvertexp->varScp()->v3fatalSrc("GateLogicVertex* visit should have returned NULL if consumer var vertex is not dedupable.");
	    GateOkVisitor okVisitor(lvertexp->nodep(), false, true);
	    if (okVisitor.isSimple()) {
		AstVarScope* dupVarScopep = dupVarRefp->varScopep();
		GateVarVertex* dupVvertexp = (GateVarVertex*) (dupVarScopep->user1p());
		UINFO(4,"replacing " << vvertexp << " with " << dupVvertexp << endl);
		++m_numDeduped;
		// Replace all of this varvertex's consumers with dupVarRefp
		for (V3GraphEdge* outedgep = vvertexp->outBeginp();outedgep;) {
		    GateLogicVertex* consumeVertexp = dynamic_cast<GateLogicVertex*>(outedgep->top());
		    AstNode* consumerp = consumeVertexp->nodep();
		    GateElimVisitor elimVisitor(consumerp,vvertexp->varScp(),dupVarRefp);
		    outedgep = outedgep->relinkFromp(dupVvertexp);
		}

		// Propogate attributes
		dupVvertexp->propagateAttrClocksFrom(vvertexp);

		// Remove inputs links
		while (V3GraphEdge* inedgep = vvertexp->inBeginp()) {
		    inedgep->unlinkDelete(); VL_DANGLING(inedgep);
		}
		// replaceAssigns() does the deleteTree on lvertexNodep in a later step
		AstNode* lvertexNodep = lvertexp->nodep();
		lvertexNodep->unlinkFrBack();
		vvertexp->varScp()->valuep(lvertexNodep);
		lvertexNodep = NULL;
		vvertexp->user(true);
		lvertexp->user(true);
	    }
	}
	return NULL;
    }
Esempio n. 9
0
    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;
	    }
	}
    }
Esempio n. 10
0
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);
			}
		    }
		}
	    }
	}
    }
}
Esempio n. 11
0
    virtual AstNUser* visit(GateVarVertex *vvertexp, AstNUser*) {
	for (V3GraphEdge* edgep = vvertexp->inBeginp(); edgep; ) {
	    V3GraphEdge* oldedgep = edgep;
	    edgep = edgep->inNextp();  // for recursive since the edge could be deleted
	    if (GateLogicVertex* lvertexp = dynamic_cast<GateLogicVertex*>(oldedgep->fromp())) {
		if (AstNodeAssign* assignp = lvertexp->nodep()->castNodeAssign()) {
		    //if (lvertexp->outSize1() && assignp->lhsp()->castSel()) {
		    if (assignp->lhsp()->castSel() && lvertexp->outSize1()) {
			UINFO(9, "assing to the nodep["<<assignp->lhsp()->castSel()->lsbConst()<<"]"<<endl);
			// first assign with Sel-lhs
			if (!m_activep) m_activep = lvertexp->activep();
			if (!m_logicvp) m_logicvp = lvertexp;
			if (!m_assignp) m_assignp = assignp;

			// not under the same active
			if (m_activep != lvertexp->activep()) {
			    m_activep = lvertexp->activep();
			    m_logicvp = lvertexp;
			    m_assignp = assignp;
			    continue;
			}

			AstSel* preselp = m_assignp->lhsp()->castSel();
			AstSel* curselp = assignp->lhsp()->castSel();
			if (!preselp || !curselp) continue;

			if (AstSel* newselp = merge(preselp, curselp)) {
			    UINFO(5, "assemble to new sel: "<<newselp<<endl);
			    // replace preSel with newSel
			    preselp->replaceWith(newselp); preselp->deleteTree(); VL_DANGLING(preselp);
			    // create new rhs for pre assignment
			    AstNode* newrhsp = new AstConcat(m_assignp->rhsp()->fileline(), m_assignp->rhsp()->cloneTree(false), assignp->rhsp()->cloneTree(false));
			    AstNode* oldrhsp = m_assignp->rhsp();
			    oldrhsp->replaceWith(newrhsp); oldrhsp->deleteTree(); VL_DANGLING(oldrhsp);
			    m_assignp->dtypeChgWidthSigned(m_assignp->width()+assignp->width(), m_assignp->width()+assignp->width(), AstNumeric::fromBool(true));
			    // don't need to delete, will be handled
			    //assignp->unlinkFrBack(); assignp->deleteTree(); VL_DANGLING(assignp);

			    // update the graph
			    {
				// delete all inedges to lvertexp
				if (!lvertexp->inEmpty()) { 
				    for (V3GraphEdge* ledgep = lvertexp->inBeginp(); ledgep; ) {
					V3GraphEdge* oedgep = ledgep;
					ledgep = ledgep->inNextp();
					GateEitherVertex* fromvp = dynamic_cast<GateEitherVertex*>(oedgep->fromp());
					new V3GraphEdge(m_graphp, fromvp, m_logicvp, 1);
					oedgep->unlinkDelete(); VL_DANGLING(oedgep);
				    }
				}
				// delete all outedges to lvertexp, only one
				oldedgep->unlinkDelete(); VL_DANGLING(oldedgep);
			    }
			    ++m_numMergedAssigns;
			} else {
			    m_assignp = assignp;
			    m_logicvp = lvertexp;
			}
		    }
		}
	    }
	}
	return NULL;
    }
Esempio n. 12
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;
}
Esempio n. 13
0
    // 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");
    }