From 9a82689d00667b118b4814ec0bbfb3ae1bde0145 Mon Sep 17 00:00:00 2001 From: Justin Viiret Date: Tue, 4 Apr 2017 14:36:52 +1000 Subject: [PATCH] limex: more general CANNOT_DIE analysis --- src/nfa/limex_compile.cpp | 82 ++++++++++++++++++++++++++++++++++++++- 1 file changed, 81 insertions(+), 1 deletion(-) diff --git a/src/nfa/limex_compile.cpp b/src/nfa/limex_compile.cpp index 92ec4205..7590273c 100644 --- a/src/nfa/limex_compile.cpp +++ b/src/nfa/limex_compile.cpp @@ -66,6 +66,7 @@ #include #include +#include #include using namespace std; @@ -1640,6 +1641,84 @@ u32 findBestNumOfVarShifts(const build_info &args, return bestNumOfVarShifts; } +static +bool cannotDie(const build_info &args, const set &tops) { + const auto &h = args.h; + + // When this top is activated, all of the vertices in 'tops' are switched + // on. If any of those lead to a graph that cannot die, then this top + // cannot die. + + // For each top, we use a depth-first search to traverse the graph from the + // top, looking for a cyclic path consisting of vertices of dot reach. If + // one exists, than the NFA cannot die after this top is triggered. + + vector colours(num_vertices(h)); + auto colour_map = boost::make_iterator_property_map(colours.begin(), + get(vertex_index, h)); + + struct CycleFound {}; + struct CannotDieVisitor : public boost::default_dfs_visitor { + void back_edge(const NFAEdge &e, const NGHolder &g) const { + DEBUG_PRINTF("back-edge %zu,%zu\n", g[source(e, g)].index, + g[target(e, g)].index); + if (g[target(e, g)].char_reach.all()) { + assert(g[source(e, g)].char_reach.all()); + throw CycleFound(); + } + } + }; + + try { + for (const auto &top : tops) { + DEBUG_PRINTF("checking top vertex %zu\n", h[top].index); + + // Constrain the search to the top vertices and any dot vertices it + // can reach. + auto term_func = [&](NFAVertex v, const NGHolder &g) { + if (v == top) { + return false; + } + if (!g[v].char_reach.all()) { + return true; + } + if (contains(args.br_cyclic, v) && + args.br_cyclic.at(v).repeatMax != depth::infinity()) { + // Bounded repeat vertices without inf max can be turned + // off. + return true; + } + return false; + }; + + boost::depth_first_visit(h, top, CannotDieVisitor(), colour_map, + term_func); + } + } catch (const CycleFound &) { + DEBUG_PRINTF("cycle found\n"); + return true; + } + + return false; +} + +/** \brief True if this NFA cannot ever be in no states at all. */ +static +bool cannotDie(const build_info &args) { + const auto &h = args.h; + const auto &state_ids = args.state_ids; + + // If we have a startDs we're actually using, we can't die. + if (state_ids.at(h.startDs) != NO_STATE) { + DEBUG_PRINTF("is using startDs\n"); + return true; + } + + return all_of_in(args.tops | map_values, [&](const set &verts) { + return cannotDie(args, verts); + }); +} + template struct Factory { // typedefs for readability, for types derived from traits @@ -2237,7 +2316,8 @@ struct Factory { limex->shiftCount = shiftCount; writeShiftMasks(args, limex); - if (hasInitDsStates(args.h, args.state_ids)) { + if (cannotDie(args)) { + DEBUG_PRINTF("nfa cannot die\n"); setLimexFlag(limex, LIMEX_FLAG_CANNOT_DIE); }