bool somMayGoBackwards(NFAVertex u, const NGHolder &g, const ue2::unordered_map<NFAVertex, u32> ®ion_map, smgb_cache &cache) { /* Need to ensure all matches of the graph g up to u contain no infixes * which are also matches of the graph to u. * * This is basically the same as firstMatchIsFirst except we g is not * always a dag. As we haven't gotten around to writing an execute_graph * that operates on general graphs, we take some (hopefully) conservative * short cuts. * * Note: if the u can be jumped we will take jump edges * into account as a possibility of som going backwards * * TODO: write a generalised ng_execute_graph/make this less hacky */ assert(&g == &cache.g); if (contains(cache.smgb, u)) { return cache.smgb[u]; } DEBUG_PRINTF("checking if som can go backwards on %u\n", g[u].index); set<NFAEdge> be; BackEdges<set<NFAEdge>> backEdgeVisitor(be); depth_first_search( g.g, visitor(backEdgeVisitor) .root_vertex(g.start) .vertex_index_map(get(&NFAGraphVertexProps::index, g.g))); bool rv; if (0) { exit: DEBUG_PRINTF("using cached result\n"); cache.smgb[u] = rv; return rv; } assert(contains(region_map, u)); const u32 u_region = region_map.at(u); for (const auto &e : be) { NFAVertex s = source(e, g); NFAVertex t = target(e, g); /* only need to worry about big cycles including/before u */ DEBUG_PRINTF("back edge %u %u\n", g[s].index, g[t].index); if (s != t && region_map.at(s) <= u_region) { DEBUG_PRINTF("eek big cycle\n"); rv = true; /* big cycle -> eek */ goto exit; } } ue2::unordered_map<NFAVertex, NFAVertex> orig_to_copy; NGHolder c_g; cloneHolder(c_g, g, &orig_to_copy); for (NFAVertex v : vertices_range(g)) { if (!is_virtual_start(v, g)) { continue; } NFAVertex c_v = orig_to_copy[v]; orig_to_copy[v] = c_g.startDs; for (NFAVertex c_w : adjacent_vertices_range(c_v, c_g)) { add_edge_if_not_present(c_g.startDs, c_w, c_g); } clear_vertex(c_v, c_g); } NFAVertex c_u = orig_to_copy[u]; clear_in_edges(c_g.acceptEod, c_g); add_edge(c_g.accept, c_g.acceptEod, c_g); clear_in_edges(c_g.accept, c_g); clear_out_edges(c_u, c_g); if (hasSelfLoop(u, g)) { add_edge(c_u, c_u, c_g); } add_edge(c_u, c_g.accept, c_g); set<NFAVertex> u_succ; insert(&u_succ, adjacent_vertices(u, g)); u_succ.erase(u); for (auto t : inv_adjacent_vertices_range(u, g)) { if (t == u) { continue; } for (auto v : adjacent_vertices_range(t, g)) { if (contains(u_succ, v)) { add_edge(orig_to_copy[t], c_g.accept, c_g); break; } } } pruneUseless(c_g); be.clear(); depth_first_search(c_g.g, visitor(backEdgeVisitor).root_vertex(c_g.start). vertex_index_map(get(&NFAGraphVertexProps::index, c_g.g))); for (const auto &e : be) { NFAVertex s = source(e, c_g); NFAVertex t = target(e, c_g); DEBUG_PRINTF("back edge %u %u\n", c_g[s].index, c_g[t].index); if (s != t) { assert(0); DEBUG_PRINTF("eek big cycle\n"); rv = true; /* big cycle -> eek */ goto exit; } } DEBUG_PRINTF("checking acyclic+selfloop graph\n"); rv = !firstMatchIsFirst(c_g); DEBUG_PRINTF("som may regress? %d\n", (int)rv); goto exit; }
static void wireSuccessorsToStart(NGHolder &g, NFAVertex u) { for (auto v : adjacent_vertices_range(u, g)) { add_edge_if_not_present(g.start, v, g); } }
static void mergeClass(ptr_vector<VertexInfo> &infos, NGHolder &g, unsigned eq_class, VertexInfoSet &cur_class_vertices, set<NFAVertex> *toRemove) { DEBUG_PRINTF("Replacing %zd vertices from equivalence class %u with a " "single vertex.\n", cur_class_vertices.size(), eq_class); // replace equivalence class with a single vertex: // 1. create new vertex with matching properties // 2. wire all predecessors to new vertex // 2a. update info for new vertex with new predecessors // 2b. update each predecessor's successor list // 3. wire all successors to new vertex // 3a. update info for new vertex with new successors // 3b. update each successor's predecessor list // 4. remove old vertex // any differences between vertex properties were resolved during // initial partitioning, so we assume that every vertex in equivalence // class has the same CharReach et al. // so, we find the first vertex in our class and get all its properties /* For left equivalence, if the members have different reporting behaviour * we sometimes require two vertices to be created (one connected to accept * and one to accepteod) */ NFAVertex old_v = (*cur_class_vertices.begin())->v; NFAVertex new_v = clone_vertex(g, old_v); /* set up new vertex with same * props */ g[new_v].reports.clear(); /* populated as we pull in succs */ VertexInfo *new_vertex_info = new VertexInfo(new_v, g); // store this vertex in our global vertex list infos.push_back(new_vertex_info); NFAVertex new_v_eod = NGHolder::null_vertex(); VertexInfo *new_vertex_info_eod = nullptr; if (require_separate_eod_vertex(cur_class_vertices, g)) { new_v_eod = clone_vertex(g, old_v); g[new_v_eod].reports.clear(); new_vertex_info_eod = new VertexInfo(new_v_eod, g); infos.push_back(new_vertex_info_eod); } const unsigned edgetop = (*cur_class_vertices.begin())->edge_top; for (VertexInfo *old_vertex_info : cur_class_vertices) { assert(old_vertex_info->equivalence_class == eq_class); // mark this vertex for removal toRemove->insert(old_vertex_info->v); // for each predecessor, add edge to new vertex and update info for (VertexInfo *pred_info : old_vertex_info->pred) { // update info for new vertex new_vertex_info->pred.insert(pred_info); if (new_vertex_info_eod) { new_vertex_info_eod->pred.insert(pred_info); } // update info for predecessor pred_info->succ.erase(old_vertex_info); // if edge doesn't exist, create it NFAEdge e = add_edge_if_not_present(pred_info->v, new_v, g).first; // put edge top, if applicable if (edgetop != (unsigned) -1) { g[e].top = edgetop; } pred_info->succ.insert(new_vertex_info); if (new_v_eod) { NFAEdge ee = add_edge_if_not_present(pred_info->v, new_v_eod, g).first; // put edge top, if applicable if (edgetop != (unsigned) -1) { g[ee].top = edgetop; } pred_info->succ.insert(new_vertex_info_eod); } } // for each successor, add edge from new vertex and update info for (VertexInfo *succ_info : old_vertex_info->succ) { NFAVertex succ_v = succ_info->v; // update info for successor succ_info->pred.erase(old_vertex_info); if (new_v_eod && succ_v == g.acceptEod) { // update info for new vertex new_vertex_info_eod->succ.insert(succ_info); insert(&g[new_v_eod].reports, g[old_vertex_info->v].reports); add_edge_if_not_present(new_v_eod, succ_v, g); succ_info->pred.insert(new_vertex_info_eod); } else { // update info for new vertex new_vertex_info->succ.insert(succ_info); // if edge doesn't exist, create it add_edge_if_not_present(new_v, succ_v, g); succ_info->pred.insert(new_vertex_info); if (is_any_accept(succ_v, g)) { insert(&g[new_v].reports, g[old_vertex_info->v].reports); } } } } // update classmap new_vertex_info->equivalence_class = eq_class; cur_class_vertices.insert(new_vertex_info); }