diff --git a/nodist/code_coverage.dat b/nodist/code_coverage.dat index d725dad4b..88c21b5f1 100644 --- a/nodist/code_coverage.dat +++ b/nodist/code_coverage.dat @@ -23,10 +23,6 @@ source_globs("include/*/*.c") # Note *'s are removed when using fastcov remove_source("/usr/*") remove_source("*/include/sysc/*") -remove_source("*/V3ClkGater.cpp") -remove_source("*/V3ClkGater.h") -remove_source("*/V3GraphDfa.cpp") -remove_source("*/V3GraphDfa.h") remove_source("*/V3Lexer_pregen.yy.cpp") remove_source("*/V3PreLex_pregen.yy.cpp") remove_source("*/verilog.c") diff --git a/src/Makefile_obj.in b/src/Makefile_obj.in index abb5f1a36..c2e832bdc 100644 --- a/src/Makefile_obj.in +++ b/src/Makefile_obj.in @@ -203,7 +203,6 @@ RAW_OBJS = \ V3Graph.o \ V3GraphAlg.o \ V3GraphAcyc.o \ - V3GraphDfa.o \ V3GraphPathChecker.o \ V3GraphTest.o \ V3Hash.o \ diff --git a/src/V3Assert.cpp b/src/V3Assert.cpp index 196113a02..72803b93e 100644 --- a/src/V3Assert.cpp +++ b/src/V3Assert.cpp @@ -20,7 +20,6 @@ #include "V3Global.h" #include "V3Assert.h" #include "V3Ast.h" -#include "V3GraphDfa.h" #include "V3Stats.h" //###################################################################### diff --git a/src/V3GraphDfa.cpp b/src/V3GraphDfa.cpp deleted file mode 100644 index f004fdbc4..000000000 --- a/src/V3GraphDfa.cpp +++ /dev/null @@ -1,607 +0,0 @@ -// -*- mode: C++; c-file-style: "cc-mode" -*- -//************************************************************************* -// DESCRIPTION: Verilator: Graph optimizations -// -// Code available from: https://verilator.org -// -//************************************************************************* -// -// Copyright 2005-2021 by Wilson Snyder. This program is free software; you -// can redistribute it and/or modify it under the terms of either the GNU -// Lesser General Public License Version 3 or the Perl Artistic License -// Version 2.0. -// SPDX-License-Identifier: LGPL-3.0-only OR Artistic-2.0 -// -//************************************************************************* - -#include "config_build.h" -#include "verilatedos.h" - -#include "V3Global.h" -#include "V3GraphDfa.h" -#include "V3GraphAlg.h" - -#include -#include -#include - -//###################################################################### -//###################################################################### -// Algorithms - find starting node - -DfaVertex* DfaGraph::findStart() { - DfaVertex* startp = nullptr; - for (V3GraphVertex* vertexp = this->verticesBeginp(); vertexp; - vertexp = vertexp->verticesNextp()) { - if (DfaVertex* const vvertexp = dynamic_cast(vertexp)) { - if (vvertexp->start()) { - UASSERT_OBJ(!startp, vertexp, "Multiple start points in NFA graph"); - startp = vvertexp; - } - } else { - vertexp->v3fatalSrc("Non DfaVertex in DfaGraph"); - } - } - if (!startp) v3fatalSrc("No start point in NFA graph"); - return startp; -} - -//###################################################################### -//###################################################################### -// Algorithms - convert NFA to a DFA -// Uses the Subset Construction Algorithm - -class GraphNfaToDfa final : GraphAlg<> { - // We have two types of nodes in one graph, NFA and DFA nodes. - // Edges from NFA to NFA come from the user, and indicate input or epsilon transitions - // Edges from DFA to NFA indicate the NFA from which that DFA was formed. - // Edges from DFA to DFA indicate a completed input transition -private: - // TYPES - using DfaStates = std::deque; - using HashMap = std::multimap; - - // MEMBERS - uint32_t m_step; // Processing step, so we can avoid clearUser all the time - HashMap m_hashMap; // Dfa Vertex for each set of NFA vertexes - -#ifdef VL_CPPCHECK - static int debug() { return 9; } -#else - static int debug() { return 0; } -#endif - - // METHODS - DfaGraph* graphp() { return static_cast(m_graphp); } - static bool nfaState(V3GraphVertex* vertexp) { return vertexp->color() == 0; } - // static bool dfaState(V3GraphVertex* vertexp) { return vertexp->color()==1; } - - void nextStep() { m_step++; } - - bool unseenNfaThisStep(V3GraphVertex* vertexp) { - // A nfa node not already seen this processing step - return (nfaState(vertexp) && !(vertexp->user() == m_step)); - } - - DfaVertex* newDfaVertex(DfaVertex* nfaTemplatep = nullptr) { - DfaVertex* const vertexp = new DfaVertex(graphp()); - vertexp->color(1); // Mark as dfa - if (nfaTemplatep && nfaTemplatep->start()) vertexp->start(true); - if (nfaTemplatep && nfaTemplatep->accepting()) vertexp->accepting(true); - UINFO(9, " New " << vertexp << endl); - return vertexp; - } - - // Hashing - static uint32_t hashVertex(V3GraphVertex* vertexp) { - union { - const void* up; - struct { - uint32_t upper; - uint32_t lower; - } l; - } u; - u.l.upper = 0; - u.l.lower = 0; - u.up = vertexp; - return u.l.upper ^ u.l.lower; - } - - 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* const nfaStatep = static_cast(dfaEdgep->top()); - hash ^= hashVertex(nfaStatep); - if (debug()) { - UASSERT_OBJ(nfaStatep->user() != m_step, nfaStatep, - "DFA state points to duplicate NFA state."); - nfaStatep->user(m_step); - } - } - } - return hash; - } - - uint32_t hashDfaOrigins(const DfaStates& nfasWithInput) { - // Find the NFA states this dfa came from, - uint32_t hash = 0; - for (DfaVertex* nfaStatep : nfasWithInput) hash ^= hashVertex(nfaStatep); - return hash; - } - - 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 (DfaVertex* nfaStatep : nfasWithInput) { - 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) - return (num1s == num2s); - } - - void insertDfaOrigins(DfaVertex* dfaStatep) { - // Record the NFA states this dfa came from - uint32_t hash = hashDfaOrigins(dfaStatep); - m_hashMap.emplace(hash, dfaStatep); - } - - DfaVertex* findDfaOrigins(const DfaStates& nfasWithInput) { - // Find another DFA state which comes from the identical set of NFA states - // The order of the nodes is not deterministic; the hash thus must - // not depend on order of edges - uint32_t hash = hashDfaOrigins(nfasWithInput); - - const auto eqrange = m_hashMap.equal_range(hash); - for (auto it = eqrange.first; it != eqrange.second; ++it) { - DfaVertex* const testp = it->second; - if (compareDfaOrigins(nfasWithInput, testp)) { - UINFO(9, " DFA match for set: " << testp << endl); - return testp; // Identical - } - } - return nullptr; // No match - } - - void findNfasWithInput(DfaVertex* dfaStatep, const DfaInput& input, DfaStates& nfasWithInput) { - // Return all NFA states, with the given input transition from - // the nfa states a given dfa state was constructed from. - nextStep(); - nfasWithInput.clear(); // NFAs with given input - - // Foreach NFA state (this DFA state was formed from) - for (V3GraphEdge* dfaEdgep = dfaStatep->outBeginp(); dfaEdgep; - dfaEdgep = dfaEdgep->outNextp()) { - if (nfaState(dfaEdgep->top())) { - const DfaVertex* const nfaStatep = static_cast(dfaEdgep->top()); - // Foreach input transition (on this nfaStatep) - for (V3GraphEdge* nfaEdgep = nfaStatep->outBeginp(); nfaEdgep; - nfaEdgep = nfaEdgep->outNextp()) { - const DfaEdge* const cNfaEdgep = static_cast(nfaEdgep); - if (cNfaEdgep->input().toNodep() == input.toNodep()) { - DfaVertex* const nextStatep = static_cast(cNfaEdgep->top()); - if (unseenNfaThisStep(nextStatep)) { // Not processed? - nfasWithInput.push_back(nextStatep); - nextStatep->user(m_step); - UINFO(9, " Reachable " << nextStatep << endl); - } - } - } - } - } - - // Expand the nfasWithInput list to include epsilon states - // reachable by those on nfasWithInput - { - DfaStates nfasTodo = nfasWithInput; - nfasWithInput.clear(); // Now the completed list - while (!nfasTodo.empty()) { - DfaVertex* const nfaStatep = nfasTodo.front(); - nfasTodo.pop_front(); - nfasWithInput.push_back(nfaStatep); - // Foreach epsilon-reachable (on this nfaStatep) - for (V3GraphEdge* nfaEdgep = nfaStatep->outBeginp(); nfaEdgep; - nfaEdgep = nfaEdgep->outNextp()) { - const DfaEdge* const cNfaEdgep = static_cast(nfaEdgep); - if (cNfaEdgep->epsilon()) { - DfaVertex* const nextStatep = static_cast(cNfaEdgep->top()); - if (unseenNfaThisStep(nextStatep)) { // Not processed? - nfasTodo.push_back(nextStatep); - nextStatep->user(m_step); - UINFO(9, " Epsilon Reachable " << nextStatep << endl); - } - } - } - } - } - } - - void main() { - UINFO(5, "Dfa to Nfa conversion...\n"); - // Vertex::color() begin: 1 indicates vertex on DFA graph, 0=NFA graph - m_graphp->clearColors(); - // Vertex::m_user begin: # indicates processed this m_step number - m_graphp->userClearVertices(); - - if (debug() >= 6) m_graphp->dumpDotFilePrefixed("dfa_nfa"); - - // Find NFA start - DfaVertex* const nfaStartp = graphp()->findStart(); - - // Create new DFA State (start state) from the NFA states - DfaVertex* const dfaStartp = newDfaVertex(nfaStartp); - - DfaStates dfaUnprocps; // Unprocessed DFA nodes - dfaUnprocps.push_back(dfaStartp); - - UINFO(5, "Starting state conversion...\n"); - // Form DFA starting state from epsilon closure of NFA start - nextStep(); - DfaStates workps; - workps.push_back(nfaStartp); - - while (!workps.empty()) { // While work - DfaVertex* const nfaStatep = workps.back(); - workps.pop_back(); - // UINFO(9," Processing "<user(m_step); // Mark as processed - // Add a edge so we can find NFAs from a given DFA. - // The NFA will never see this edge, because we only look at TO edges. - new DfaEdge(graphp(), dfaStartp, nfaStatep, DfaEdge::NA()); - // Find epsilon closure of this nfa node, and destinations to work list - for (V3GraphEdge* nfaEdgep = nfaStatep->outBeginp(); nfaEdgep; - nfaEdgep = nfaEdgep->outNextp()) { - const DfaEdge* const cNfaEdgep = static_cast(nfaEdgep); - DfaVertex* const ecNfaStatep = static_cast(nfaEdgep->top()); - // UINFO(9," Consider "<top()<<" EP "<epsilon()<epsilon() && unseenNfaThisStep(ecNfaStatep)) { // Not processed? - workps.push_back(ecNfaStatep); - } - } - } - if (debug() >= 6) m_graphp->dumpDotFilePrefixed("dfa_start"); - insertDfaOrigins(dfaStartp); - - int i = 0; - UINFO(5, "Main state conversion...\n"); - while (!dfaUnprocps.empty()) { - DfaVertex* const dfaStatep = dfaUnprocps.back(); - dfaUnprocps.pop_back(); - UINFO(9, " On dfaState " << dfaStatep << endl); - - // From this dfaState, what corresponding nfaStates have what inputs? - std::unordered_set inputs; - // Foreach NFA state (this DFA state was formed from) - for (V3GraphEdge* dfaEdgep = dfaStatep->outBeginp(); dfaEdgep; - dfaEdgep = dfaEdgep->outNextp()) { - if (nfaState(dfaEdgep->top())) { - const DfaVertex* const nfaStatep = static_cast(dfaEdgep->top()); - // Foreach input on this nfaStatep - for (V3GraphEdge* nfaEdgep = nfaStatep->outBeginp(); nfaEdgep; - nfaEdgep = nfaEdgep->outNextp()) { - const DfaEdge* const cNfaEdgep = static_cast(nfaEdgep); - if (!cNfaEdgep->epsilon()) { - if (inputs.find(cNfaEdgep->input().toInt()) == inputs.end()) { - inputs.insert(cNfaEdgep->input().toInt()); - UINFO(9, " Input to " << dfaStatep << " is " - << (cNfaEdgep->input().toInt()) << " via " - << nfaStatep << endl); - } - } - } - } - } - - // Foreach input state (NFA inputs of this DFA state) - for (int inIt : inputs) { - const DfaInput input = inIt; - UINFO(9, " ===" << ++i << "=======================\n"); - UINFO(9, " On input " << cvtToHex(input.toNodep()) << endl); - - // Find all states reachable for given input - DfaStates nfasWithInput; - findNfasWithInput(dfaStatep, input, nfasWithInput /*ref*/); - - // nfasWithInput now maps to the DFA we want a transition to. - // Does a DFA already exist with this, and only this subset of NFA's? - DfaVertex* toDfaStatep = findDfaOrigins(nfasWithInput); - if (!toDfaStatep) { - // Doesn't exist, make new dfa state corresponding to this one, - toDfaStatep = newDfaVertex(); - dfaUnprocps.push_back(toDfaStatep); // Add to process list - // Track what nfa's point to it. - for (DfaStates::const_iterator nfaIt = nfasWithInput.begin(); - nfaIt != nfasWithInput.end(); ++nfaIt) { - UINFO(9, " NewContainsNfa " << *nfaIt << endl); - new DfaEdge(graphp(), toDfaStatep, *nfaIt, DfaEdge::NA()); - if ((*nfaIt)->accepting()) toDfaStatep->accepting(true); - } - insertDfaOrigins(toDfaStatep); - } - // Add input transition - new DfaEdge(graphp(), dfaStatep, toDfaStatep, input); - - if (debug() >= 6) m_graphp->dumpDotFilePrefixed("step"); - } - } - - // Remove old NFA states - UINFO(5, "Removing NFA states...\n"); - if (debug() >= 6) m_graphp->dumpDotFilePrefixed("dfa_withnfa"); - for (V3GraphVertex *nextp, *vertexp = m_graphp->verticesBeginp(); vertexp; - vertexp = nextp) { - nextp = vertexp->verticesNextp(); - if (nfaState(vertexp)) VL_DO_DANGLING(vertexp->unlinkDelete(m_graphp), vertexp); - } - - UINFO(5, "Done.\n"); - if (debug() >= 6) m_graphp->dumpDotFilePrefixed("dfa_done"); - } - -public: - GraphNfaToDfa(V3Graph* graphp, V3EdgeFuncP edgeFuncp) - : GraphAlg<>{graphp, edgeFuncp} { - m_step = 0; - main(); - } - ~GraphNfaToDfa() = default; -}; - -void DfaGraph::nfaToDfa() { GraphNfaToDfa(this, &V3GraphEdge::followAlwaysTrue); } - -//###################################################################### -//###################################################################### -// Algorithms - optimize a DFA structure -// -// Scan the DFA, cleaning up trailing states. - -class DfaGraphReduce final : GraphAlg<> { -private: - // METHODS -#ifdef VL_CPPCHECK - static int debug() { return 9; } -#else - static int debug() { return 0; } -#endif - DfaGraph* graphp() { return static_cast(m_graphp); } - - bool isDead(DfaVertex* vertexp) { - // A state is dead if not accepting, and goes nowhere - if (vertexp->accepting() || vertexp->start()) return false; - for (V3GraphEdge* edgep = vertexp->outBeginp(); edgep; edgep = edgep->outNextp()) { - if (edgep->top() != vertexp) return false; - } - return true; - } - - 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 (const DfaVertex* const vvertexp = dynamic_cast(vertexp)) { - if (vvertexp->accepting()) { - for (V3GraphEdge *nextp, *edgep = vertexp->outBeginp(); edgep; edgep = nextp) { - nextp = edgep->outNextp(); - VL_DO_DANGLING(edgep->unlinkDelete(), edgep); - } - } - } - } - } - - 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* const startp = graphp()->findStart(); - std::stack 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()) VL_DO_DANGLING(vertexp->unlinkDelete(m_graphp), vertexp); - } - } - - 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 - std::stack workps; - for (V3GraphVertex* vertexp = m_graphp->verticesBeginp(); vertexp; - vertexp = vertexp->verticesNextp()) { - if (DfaVertex* const vvertexp = dynamic_cast(vertexp)) { - workps.push(vvertexp); - vertexp->user(1); - } else { - // If ever remove this, need dyn cast below - vertexp->v3fatalSrc("Non DfaVertex in dfa graph"); - } - } - - // While deadness... Delete and find new dead nodes. - while (!workps.empty()) { - DfaVertex* const 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* const fromvertexp = static_cast(edgep->fromp()); - if (fromvertexp != vertexp && !fromvertexp->user()) { - workps.push(fromvertexp); - fromvertexp->user(1); - } - } - // Transitions to this state removed by the unlink function - VL_DO_DANGLING(vertexp->unlinkDelete(m_graphp), vertexp); - } - } - } - -public: - DfaGraphReduce(V3Graph* graphp, V3EdgeFuncP edgeFuncp) - : GraphAlg<>{graphp, edgeFuncp} { - if (debug() >= 6) m_graphp->dumpDotFilePrefixed("opt_in"); - optimize_accepting_out(); - if (debug() >= 6) m_graphp->dumpDotFilePrefixed("opt_acc"); - optimize_orphans(); - if (debug() >= 6) m_graphp->dumpDotFilePrefixed("opt_orph"); - optimize_no_outbound(); - if (debug() >= 6) m_graphp->dumpDotFilePrefixed("opt_noout"); - } - ~DfaGraphReduce() = default; -}; - -void DfaGraph::dfaReduce() { DfaGraphReduce(this, &V3GraphEdge::followAlwaysTrue); } - -//###################################################################### -//###################################################################### -// Algorithms - complement a DFA -// -// The traditional algorithm is to make a rejecting state, add edges to -// reject from all missing values, then swap accept and reject. Rather -// than swap at the end, it's faster if we swap up front, then do the edge -// changes. -// -// 1. Since we didn't log rejecting states, make a temp state (this will be -// the old accept, and new reject). -// -// 2. All vertexes except start/accept get edges to NEW accept for any -// non-existing case. Weedely we don't have a nice way of representing -// this so we just create a edge for each case and mark it "complemented." -// -// 3. Delete temp vertex (old accept/new reject) and related edges. -// The user's old accept is now the new accept. This is important as -// we want the virtual type of it to be intact. - -class DfaGraphComplement final : GraphAlg<> { -private: - // MEMBERS - DfaVertex* m_tempNewerReject; - - // METHODS - static int debug() { return 9; } - DfaGraph* graphp() { return static_cast(m_graphp); } - - void add_complement_edges() { - // Find accepting vertex - DfaVertex* acceptp = nullptr; - for (V3GraphVertex* vertexp = m_graphp->verticesBeginp(); vertexp; - vertexp = vertexp->verticesNextp()) { - if (DfaVertex* const vvertexp = dynamic_cast(vertexp)) { - if (vvertexp->accepting()) { - acceptp = vvertexp; - break; - } - } - } - if (!acceptp) v3fatalSrc("No accepting vertex in DFA"); - - // Remap edges - for (V3GraphVertex* vertexp = m_graphp->verticesBeginp(); vertexp; - vertexp = vertexp->verticesNextp()) { - if (DfaVertex* const vvertexp = dynamic_cast(vertexp)) { - // UINFO(9, " on vertex "<name()<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 - const DfaEdge* const vedgep = static_cast(edgep); - const DfaVertex* const tovertexp - = static_cast(edgep->top()); - if (tovertexp->accepting()) { - new DfaEdge(graphp(), vvertexp, m_tempNewerReject, vedgep); - VL_DO_DANGLING(edgep->unlinkDelete(), edgep); - } - - // 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* const newp - = new DfaEdge(graphp(), vvertexp, acceptp, vedgep); - newp->complement(!newp->complement()); - newp->user(1); - } - } - } - } - } - } - } - -public: - DfaGraphComplement(V3Graph* dfagraphp, V3EdgeFuncP edgeFuncp) - : GraphAlg<>{dfagraphp, edgeFuncp} { - if (debug() >= 6) m_graphp->dumpDotFilePrefixed("comp_in"); - - // Vertex::m_user begin: 1 indicates new edge, no more processing - m_graphp->userClearEdges(); - - m_tempNewerReject = new DfaVertex(graphp()); - add_complement_edges(); - if (debug() >= 6) m_graphp->dumpDotFilePrefixed("comp_preswap"); - - VL_DO_CLEAR(m_tempNewerReject->unlinkDelete(graphp()), m_tempNewerReject = nullptr); - if (debug() >= 6) m_graphp->dumpDotFilePrefixed("comp_out"); - } - ~DfaGraphComplement() = default; - VL_UNCOPYABLE(DfaGraphComplement); -}; - -void DfaGraph::dfaComplement() { DfaGraphComplement(this, &V3GraphEdge::followAlwaysTrue); } diff --git a/src/V3GraphDfa.h b/src/V3GraphDfa.h deleted file mode 100644 index 8550bdad4..000000000 --- a/src/V3GraphDfa.h +++ /dev/null @@ -1,152 +0,0 @@ -// -*- mode: C++; c-file-style: "cc-mode" -*- -//************************************************************************* -// DESCRIPTION: Verilator: Graph automata base class -// -// Code available from: https://verilator.org -// -//************************************************************************* -// -// Copyright 2003-2021 by Wilson Snyder. This program is free software; you -// can redistribute it and/or modify it under the terms of either the GNU -// Lesser General Public License Version 3 or the Perl Artistic License -// Version 2.0. -// SPDX-License-Identifier: LGPL-3.0-only OR Artistic-2.0 -// -//************************************************************************* - -#ifndef VERILATOR_V3GRAPHDFA_H_ -#define VERILATOR_V3GRAPHDFA_H_ -#include "config_build.h" -#include "verilatedos.h" - -#include "V3Ast.h" // for VNUser -#include "V3Global.h" -#include "V3Graph.h" - -class DfaGraph; -class DfaVertex; -class DfaEdge; - -//============================================================================= -// NFA/DFA Graphs -/// The NFA graph consists of: -/// DfaVertex(START) The starting point -/// DfaVertex() Interior states -/// DfaVertex(ACCEPT) The completion point -/// -/// Transitions include a list of all inputs (arbitrary user pointers), -/// or epsilon, represented as a empty list of inputs. -/// -/// We're only looking for matches, so the only accepting states are -/// at the end of the transformations. (If we want the complement, we -/// call complement and the algorithm makes a REJECT state, then flips -/// accept and reject for you.) -/// -/// Common transforms: -/// -/// "*": DfaVertex(START) --> [epsilon] -->DfaVertex(ACCEPT) -/// -/// "L": ...->[ON_L]-->DfaVtx-->[epsilon]-->DfaVtx(ACCEPT) -/// -/// "LR": ...->[ON_L]-->DfaVtx-->[epsilon]-->DfaVtx(ACCEPT) -/// ->[ON_R]-->DfaVtx-->[epsilon]-/ -/// -/// "L|R": ...->DfaVtx-->[epsilon]-->DfaVtx-->[ON_L]-->DfaVtx()->[epsilon]-->DfaVtx(ACCEPT) -/// \->[epsilon]-->DfaVtx-->[ON_R]-->DfaVtx()->[epsilon]-/ -/// -/// "L*": ...->DfaVtx-->[epsilon]-->DfaVtx-->[ON_L]-->DfaVtx()->[epsilon]-->DfaVtx(ACCEPT) -/// | ^\----[epsilon]<-------/ | -/// \->[epsilon]-----------------------------------------/ - -class DfaGraph final : public V3Graph { -public: - // CONSTRUCTORS - DfaGraph() = default; - virtual ~DfaGraph() override = default; - // METHODS - /// Find start node - DfaVertex* findStart(); - /// Convert automata: NFA to DFA - void nfaToDfa(); - /// Simplify a DFA automata - void dfaReduce(); - /// Complement result (must already be dfa) - void dfaComplement(); -}; - -//============================================================================= -// Vertex - -class DfaVertex VL_NOT_FINAL : public V3GraphVertex { - // Each DFA state is captured in this vertex. - // Start and accepting are members, rather than the more intuitive - // subclasses, as subclassing them would make it harder to inherit from here. - bool m_start; // Start state - bool m_accepting; // Accepting state? -public: - // CONSTRUCTORS - explicit DfaVertex(DfaGraph* graphp, bool start = false, bool accepting = false) - : V3GraphVertex{graphp} - , m_start{start} - , m_accepting{accepting} {} - using V3GraphVertex::clone; // We are overriding, not overloading clone(V3Graph*) - virtual DfaVertex* clone(DfaGraph* graphp) { - return new DfaVertex(graphp, start(), accepting()); - } - virtual ~DfaVertex() override = default; - // ACCESSORS - virtual string dotShape() const override { return (accepting() ? "doublecircle" : ""); } - virtual string dotColor() const override { - return start() ? "blue" : (color() ? "red" : "black"); - } - bool start() const { return m_start; } - void start(bool flag) { m_start = flag; } - bool accepting() const { return m_accepting; } - void accepting(bool flag) { m_accepting = flag; } -}; - -//============================================================================ -/// Abstract type indicating a specific "input" to the NFA -/// DFA assumes each .toInt() is unique -using DfaInput = VNUser; - -//============================================================================ -// Edge types - -class DfaEdge final : public V3GraphEdge { - DfaInput m_input; - bool m_complement; // Invert value when doing compare -public: - static DfaInput EPSILON() { return VNUser::fromInt(0); } - static DfaInput NA() { return VNUser::fromInt(1); } // as in not-applicable - // CONSTRUCTORS - DfaEdge(DfaGraph* graphp, DfaVertex* fromp, DfaVertex* top, const DfaInput& input) - : V3GraphEdge{graphp, fromp, top, 1} - , m_input{input} - , m_complement{false} {} - DfaEdge(DfaGraph* graphp, DfaVertex* fromp, DfaVertex* top, const DfaEdge* copyfrom) - : V3GraphEdge{graphp, fromp, top, copyfrom->weight()} - , m_input{copyfrom->input()} - , m_complement{copyfrom->complement()} {} - virtual ~DfaEdge() override = default; - // METHODS - virtual string dotColor() const override { - return (na() ? "yellow" : epsilon() ? "green" : "black"); - } - virtual string dotLabel() const override { - return (na() ? "" - : epsilon() ? "e" - : complement() ? ("not " + cvtToStr(input().toInt())) - : cvtToStr(input().toInt())); - } - virtual string dotStyle() const override { return (na() || cutable()) ? "dashed" : ""; } - bool epsilon() const { return input().toInt() == EPSILON().toInt(); } - bool na() const { return input().toInt() == NA().toInt(); } - bool complement() const { return m_complement; } - void complement(bool value) { m_complement = value; } - DfaInput input() const { return m_input; } -}; - -//============================================================================ - -#endif // Guard diff --git a/src/V3GraphTest.cpp b/src/V3GraphTest.cpp index ed14f17fc..e14297946 100644 --- a/src/V3GraphTest.cpp +++ b/src/V3GraphTest.cpp @@ -19,7 +19,6 @@ #include "V3Global.h" #include "V3Graph.h" -#include "V3GraphDfa.h" //###################################################################### //###################################################################### @@ -28,7 +27,7 @@ class V3GraphTest VL_NOT_FINAL { protected: // MEMBERS - DfaGraph m_graph; + V3Graph m_graph; // METHODS - for children virtual void runTest() = 0; // Run the test @@ -260,67 +259,6 @@ public: //====================================================================== -class DfaTestVertex final : public DfaVertex { - string m_name; - -public: - DfaTestVertex(DfaGraph* graphp, const string& name) - : DfaVertex{graphp} - , m_name{name} {} - virtual ~DfaTestVertex() override = default; - // ACCESSORS - virtual string name() const override { return m_name; } -}; - -class V3GraphTestDfa final : public V3GraphTest { - -public: - virtual string name() override { return "dfa"; } - virtual void runTest() override { - DfaGraph* const gp = &m_graph; - - // NFA Pattern for ( (LR) | (L*R)) Z - DfaTestVertex* const st = new DfaTestVertex(gp, "*START*"); - st->start(true); - DfaTestVertex* const sl = new DfaTestVertex(gp, "sL"); - DfaTestVertex* const srs = new DfaTestVertex(gp, "sR*"); - DfaTestVertex* const sls = new DfaTestVertex(gp, "sL*"); - DfaTestVertex* const sr = new DfaTestVertex(gp, "sR"); - DfaTestVertex* const sz = new DfaTestVertex(gp, "sZ"); - DfaTestVertex* const sac = new DfaTestVertex(gp, "*ACCEPT*"); - sac->accepting(true); - - VNUser L = VNUser::fromInt(0xaa); - VNUser R = VNUser::fromInt(0xbb); - VNUser Z = VNUser::fromInt(0xcc); - - new DfaEdge(gp, st, sl, DfaEdge::EPSILON()); - new DfaEdge(gp, sl, srs, L); - new DfaEdge(gp, srs, srs, R); - new DfaEdge(gp, srs, sz, Z); - new DfaEdge(gp, sz, sac, DfaEdge::EPSILON()); - - new DfaEdge(gp, st, sls, DfaEdge::EPSILON()); - new DfaEdge(gp, sls, sls, L); - new DfaEdge(gp, sls, sr, R); - new DfaEdge(gp, sr, sz, Z); - new DfaEdge(gp, sz, sac, DfaEdge::EPSILON()); - - dump(); - gp->nfaToDfa(); - dump(); - gp->dfaReduce(); - dump(); - - gp->dfaComplement(); - dump(); - gp->dfaReduce(); - dump(); - } -}; - -//====================================================================== - class V3GraphTestImport final : public V3GraphTest { #ifdef GRAPH_IMPORT @@ -332,8 +270,7 @@ class V3GraphTestImport final : public V3GraphTest { public: virtual string name() override { return "import"; } virtual void runTest() override { - DfaGraph* const gp = &m_graph; - if (V3GraphTest::debug()) DfaGraph::debug(9); + V3Graph* const gp = &m_graph; dotImport(); dump(); gp->acyclic(&V3GraphEdge::followAlwaysTrue); @@ -358,7 +295,6 @@ void V3Graph::selfTest() { { V3GraphTestStrong test; test.run(); } { V3GraphTestAcyc test; test.run(); } { V3GraphTestVars test; test.run(); } - { V3GraphTestDfa test; test.run(); } { V3GraphTestImport test; test.run(); } // clang-format on } diff --git a/test_regress/t/t_debug_graph_test.pl b/test_regress/t/t_debug_graph_test.pl index 961994545..64bc7a996 100755 --- a/test_regress/t/t_debug_graph_test.pl +++ b/test_regress/t/t_debug_graph_test.pl @@ -13,7 +13,7 @@ $ENV{VERILATOR_TEST_NO_GDB} and skip("Skipping due to VERILATOR_TEST_NO_GDB"); lint( # Check we can call dump() on graph, and other things - v_flags => ["--lint-only --debug --debugi-V3GraphTest 9 --debugi-V3GraphDfa 9 --debug-self-test"], + v_flags => ["--lint-only --debug --debugi-V3GraphTest 9 --debug-self-test"], ); ok(1);