mirror of
https://github.com/verilator/verilator.git
synced 2025-01-01 04:07:34 +00:00
Internals: Remove unused DfaGraph code.
This commit is contained in:
parent
37e3c6da70
commit
c496649c49
@ -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")
|
||||
|
@ -203,7 +203,6 @@ RAW_OBJS = \
|
||||
V3Graph.o \
|
||||
V3GraphAlg.o \
|
||||
V3GraphAcyc.o \
|
||||
V3GraphDfa.o \
|
||||
V3GraphPathChecker.o \
|
||||
V3GraphTest.o \
|
||||
V3Hash.o \
|
||||
|
@ -20,7 +20,6 @@
|
||||
#include "V3Global.h"
|
||||
#include "V3Assert.h"
|
||||
#include "V3Ast.h"
|
||||
#include "V3GraphDfa.h"
|
||||
#include "V3Stats.h"
|
||||
|
||||
//######################################################################
|
||||
|
@ -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 <map>
|
||||
#include <set>
|
||||
#include <stack>
|
||||
|
||||
//######################################################################
|
||||
//######################################################################
|
||||
// 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<DfaVertex*>(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<DfaVertex*>;
|
||||
using HashMap = std::multimap<vluint64_t, DfaVertex*>;
|
||||
|
||||
// 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<DfaGraph*>(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<DfaVertex*>(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<DfaVertex*>(dfaEdgep->top());
|
||||
// Foreach input transition (on this nfaStatep)
|
||||
for (V3GraphEdge* nfaEdgep = nfaStatep->outBeginp(); nfaEdgep;
|
||||
nfaEdgep = nfaEdgep->outNextp()) {
|
||||
const DfaEdge* const cNfaEdgep = static_cast<DfaEdge*>(nfaEdgep);
|
||||
if (cNfaEdgep->input().toNodep() == input.toNodep()) {
|
||||
DfaVertex* const nextStatep = static_cast<DfaVertex*>(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<DfaEdge*>(nfaEdgep);
|
||||
if (cNfaEdgep->epsilon()) {
|
||||
DfaVertex* const nextStatep = static_cast<DfaVertex*>(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 "<<nfaStatep<<endl);
|
||||
nfaStatep->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<DfaEdge*>(nfaEdgep);
|
||||
DfaVertex* const ecNfaStatep = static_cast<DfaVertex*>(nfaEdgep->top());
|
||||
// UINFO(9," Consider "<<nfaEdgep->top()<<" EP "<<cNfaEdgep->epsilon()<<endl);
|
||||
if (cNfaEdgep->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<int> 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<DfaVertex*>(dfaEdgep->top());
|
||||
// Foreach input on this nfaStatep
|
||||
for (V3GraphEdge* nfaEdgep = nfaStatep->outBeginp(); nfaEdgep;
|
||||
nfaEdgep = nfaEdgep->outNextp()) {
|
||||
const DfaEdge* const cNfaEdgep = static_cast<DfaEdge*>(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<DfaGraph*>(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<DfaVertex*>(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<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()) 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<DfaVertex*> workps;
|
||||
for (V3GraphVertex* vertexp = m_graphp->verticesBeginp(); vertexp;
|
||||
vertexp = vertexp->verticesNextp()) {
|
||||
if (DfaVertex* const vvertexp = dynamic_cast<DfaVertex*>(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<DfaVertex*>(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<DfaGraph*>(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<DfaVertex*>(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<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
|
||||
const DfaEdge* const vedgep = static_cast<DfaEdge*>(edgep);
|
||||
const DfaVertex* const tovertexp
|
||||
= static_cast<DfaVertex*>(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); }
|
152
src/V3GraphDfa.h
152
src/V3GraphDfa.h
@ -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
|
@ -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
|
||||
}
|
||||
|
@ -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);
|
||||
|
Loading…
Reference in New Issue
Block a user