From 37cb93e60f5d423aecb0571cb2e1305f8f2c7599 Mon Sep 17 00:00:00 2001 From: Alex Coyte Date: Thu, 30 Mar 2017 14:34:33 +1100 Subject: [PATCH] rose_build: reduce size/scope of context objects --- src/nfa/castlecompile.cpp | 4 +- src/rose/rose_build_anchored.cpp | 57 ++-- src/rose/rose_build_anchored.h | 9 +- src/rose/rose_build_bytecode.cpp | 501 +++++++++++++++---------------- src/rose/rose_build_compile.cpp | 4 +- src/rose/rose_build_dump.cpp | 91 ++++-- src/rose/rose_build_dump.h | 23 +- src/rose/rose_build_impl.h | 18 -- src/rose/rose_build_matchers.cpp | 194 ++++++------ src/rose/rose_build_matchers.h | 20 ++ src/rose/rose_build_program.cpp | 6 +- src/util/multibit_build.cpp | 7 +- src/util/multibit_build.h | 6 +- unit/internal/multi_bit.cpp | 38 +-- 14 files changed, 520 insertions(+), 458 deletions(-) diff --git a/src/nfa/castlecompile.cpp b/src/nfa/castlecompile.cpp index 3b40ab9a..a7fe1e90 100644 --- a/src/nfa/castlecompile.cpp +++ b/src/nfa/castlecompile.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2015-2016, Intel Corporation + * Copyright (c) 2015-2017, Intel Corporation * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: @@ -560,7 +560,7 @@ buildCastle(const CastleProto &proto, DEBUG_PRINTF("%zu subcastles may go stale\n", may_stale.size()); vector stale_iter; if (!may_stale.empty()) { - mmbBuildSparseIterator(stale_iter, may_stale, numRepeats); + stale_iter = mmbBuildSparseIterator(may_stale, numRepeats); } diff --git a/src/rose/rose_build_anchored.cpp b/src/rose/rose_build_anchored.cpp index d4e08bb3..6c7bb1c1 100644 --- a/src/rose/rose_build_anchored.cpp +++ b/src/rose/rose_build_anchored.cpp @@ -30,6 +30,7 @@ #include "grey.h" #include "rose_build_impl.h" +#include "rose_build_matchers.h" #include "rose_internal.h" #include "ue2common.h" #include "nfa/dfa_min.h" @@ -71,6 +72,8 @@ namespace ue2 { #define INIT_STATE (DEAD_STATE + 1) +#define NO_FRAG_ID (~0U) + // Adds a vertex with the given reach. static NFAVertex add_vertex(NGHolder &h, const CharReach &cr) { @@ -173,7 +176,7 @@ void mergeAnchoredDfas(vector> &dfas, } static -void remapAnchoredReports(raw_dfa &rdfa, const RoseBuildImpl &build) { +void remapAnchoredReports(raw_dfa &rdfa, const vector &frag_map) { for (dstate &ds : rdfa.states) { assert(ds.reports_eod.empty()); // Not used in anchored matcher. if (ds.reports.empty()) { @@ -182,8 +185,8 @@ void remapAnchoredReports(raw_dfa &rdfa, const RoseBuildImpl &build) { flat_set new_reports; for (auto id : ds.reports) { - assert(id < build.literal_info.size()); - new_reports.insert(build.literal_info.at(id).fragment_id); + assert(id < frag_map.size()); + new_reports.insert(frag_map[id]); } ds.reports = std::move(new_reports); } @@ -194,21 +197,37 @@ void remapAnchoredReports(raw_dfa &rdfa, const RoseBuildImpl &build) { * ids) with the fragment id for each literal. */ static -void remapAnchoredReports(RoseBuildImpl &build) { +void remapAnchoredReports(RoseBuildImpl &build, const vector &frag_map) { for (auto &m : build.anchored_nfas) { for (auto &rdfa : m.second) { assert(rdfa); - remapAnchoredReports(*rdfa, build); + remapAnchoredReports(*rdfa, frag_map); } } } +/** + * Returns mapping from literal ids to fragment ids. + */ +static +vector reverseFragMap(const RoseBuildImpl &build, + const vector &fragments) { + vector rev(build.literal_info.size(), NO_FRAG_ID); + for (const auto &f : fragments) { + for (u32 lit_id : f.lit_ids) { + assert(lit_id < rev.size()); + rev[lit_id] = f.fragment_id; + } + } + return rev; +} + /** * \brief Replace the reports (which are literal final_ids) in the given * raw_dfa with program offsets. */ static -void remapIdsToPrograms(const RoseBuildImpl &build, raw_dfa &rdfa) { +void remapIdsToPrograms(const vector &fragments, raw_dfa &rdfa) { for (dstate &ds : rdfa.states) { assert(ds.reports_eod.empty()); // Not used in anchored matcher. if (ds.reports.empty()) { @@ -217,7 +236,7 @@ void remapIdsToPrograms(const RoseBuildImpl &build, raw_dfa &rdfa) { flat_set new_reports; for (auto fragment_id : ds.reports) { - auto &frag = build.fragments.at(fragment_id); + const auto &frag = fragments.at(fragment_id); new_reports.insert(frag.lit_program_offset); } ds.reports = std::move(new_reports); @@ -731,7 +750,7 @@ int addToAnchoredMatcher(RoseBuildImpl &build, const NGHolder &anchored, } static -void buildSimpleDfas(const RoseBuildImpl &build, +void buildSimpleDfas(const RoseBuildImpl &build, const vector &frag_map, vector> *anchored_dfas) { /* we should have determinised all of these before so there should be no * chance of failure. */ @@ -739,7 +758,8 @@ void buildSimpleDfas(const RoseBuildImpl &build, for (const auto &simple : build.anchored_simple) { exit_ids.clear(); for (auto lit_id : simple.second) { - exit_ids.insert(build.literal_info[lit_id].fragment_id); + assert(lit_id < frag_map.size()); + exit_ids.insert(frag_map[lit_id]); } auto h = populate_holder(simple.first, exit_ids); Automaton_Holder autom(*h); @@ -760,7 +780,8 @@ void buildSimpleDfas(const RoseBuildImpl &build, * from RoseBuildImpl. */ static -vector> getAnchoredDfas(RoseBuildImpl &build) { +vector> getAnchoredDfas(RoseBuildImpl &build, + const vector &frag_map) { vector> dfas; // DFAs that already exist as raw_dfas. @@ -773,7 +794,7 @@ vector> getAnchoredDfas(RoseBuildImpl &build) { // DFAs we currently have as simple literals. if (!build.anchored_simple.empty()) { - buildSimpleDfas(build, &dfas); + buildSimpleDfas(build, frag_map, &dfas); build.anchored_simple.clear(); } @@ -825,7 +846,8 @@ size_t buildNfas(vector &anchored_dfas, return total_size; } -vector buildAnchoredDfas(RoseBuildImpl &build) { +vector buildAnchoredDfas(RoseBuildImpl &build, + const vector &fragments) { vector dfas; if (build.anchored_nfas.empty() && build.anchored_simple.empty()) { @@ -833,9 +855,10 @@ vector buildAnchoredDfas(RoseBuildImpl &build) { return dfas; } - remapAnchoredReports(build); + const auto frag_map = reverseFragMap(build, fragments); + remapAnchoredReports(build, frag_map); - auto anch_dfas = getAnchoredDfas(build); + auto anch_dfas = getAnchoredDfas(build, frag_map); mergeAnchoredDfas(anch_dfas, build); dfas.reserve(anch_dfas.size()); @@ -847,8 +870,8 @@ vector buildAnchoredDfas(RoseBuildImpl &build) { } aligned_unique_ptr -buildAnchoredMatcher(RoseBuildImpl &build, vector &dfas, - size_t *asize) { +buildAnchoredMatcher(RoseBuildImpl &build, const vector &fragments, + vector &dfas, size_t *asize) { const CompileContext &cc = build.cc; if (dfas.empty()) { @@ -858,7 +881,7 @@ buildAnchoredMatcher(RoseBuildImpl &build, vector &dfas, } for (auto &rdfa : dfas) { - remapIdsToPrograms(build, rdfa); + remapIdsToPrograms(fragments, rdfa); } vector> nfas; diff --git a/src/rose/rose_build_anchored.h b/src/rose/rose_build_anchored.h index ad89df65..dd59ca32 100644 --- a/src/rose/rose_build_anchored.h +++ b/src/rose/rose_build_anchored.h @@ -44,11 +44,13 @@ namespace ue2 { class RoseBuildImpl; struct raw_dfa; +struct LitFragment; /** * \brief Construct a set of anchored DFAs from our anchored literals/engines. */ -std::vector buildAnchoredDfas(RoseBuildImpl &build); +std::vector buildAnchoredDfas(RoseBuildImpl &build, + const std::vector &fragments); /** * \brief Construct an anchored_matcher_info runtime structure from the given @@ -58,8 +60,9 @@ std::vector buildAnchoredDfas(RoseBuildImpl &build); * given in litPrograms. */ aligned_unique_ptr -buildAnchoredMatcher(RoseBuildImpl &build, std::vector &dfas, - size_t *asize); +buildAnchoredMatcher(RoseBuildImpl &build, + const std::vector &fragments, + std::vector &dfas, size_t *asize); u32 anchoredStateSize(const anchored_matcher_info &atable); diff --git a/src/rose/rose_build_bytecode.cpp b/src/rose/rose_build_bytecode.cpp index a0edc711..41113457 100644 --- a/src/rose/rose_build_bytecode.cpp +++ b/src/rose/rose_build_bytecode.cpp @@ -187,13 +187,6 @@ struct build_context : noncopyable { /** \brief mapping from suffix to queue index. */ map suffixes; - /** \brief Number of roles with a state bit. - * - * This is set by assignStateIndices() and should be constant throughout - * the rest of the compile. - */ - size_t numStates = 0; - /** \brief Simple cache of programs written to engine blob, used for * deduplication. */ ue2::unordered_map roleStateIndices; + /** \brief State indices, for those roles that have them. + * Each vertex present has a unique state index in the range + * [0, roleStateIndices.size()). */ + unordered_map roleStateIndices; /** \brief Mapping from queue index to bytecode offset for built engines * that have already been pushed into the engine_blob. */ @@ -227,9 +222,6 @@ struct build_context : noncopyable { * that need hash table support. */ vector longLiterals; - /** \brief Minimum offset of a match from the floating table. */ - u32 floatingMinLiteralMatchOffset = 0; - /** \brief Long literal length threshold, used in streaming mode. */ size_t longLitLengthThreshold = 0; @@ -251,6 +243,13 @@ struct build_context : noncopyable { /** \brief Data only used during construction of various programs (literal, * anchored, delay, etc). */ struct ProgramBuild : noncopyable { + explicit ProgramBuild(u32 fMinLitOffset) + : floatingMinLiteralMatchOffset(fMinLitOffset) { + } + + /** \brief Minimum offset of a match from the floating table. */ + const u32 floatingMinLiteralMatchOffset; + /** \brief Mapping from vertex to key, for vertices with a * CHECK_NOT_HANDLED instruction. */ ue2::unordered_map handledKeys; @@ -444,21 +443,21 @@ bool isSingleOutfix(const RoseBuildImpl &tbi) { } static -u8 pickRuntimeImpl(const RoseBuildImpl &build, const build_context &bc, +u8 pickRuntimeImpl(const RoseBuildImpl &build, const RoseResources &resources, UNUSED u32 outfixEndQueue) { - DEBUG_PRINTF("has_outfixes=%d\n", bc.resources.has_outfixes); - DEBUG_PRINTF("has_suffixes=%d\n", bc.resources.has_suffixes); - DEBUG_PRINTF("has_leftfixes=%d\n", bc.resources.has_leftfixes); - DEBUG_PRINTF("has_literals=%d\n", bc.resources.has_literals); - DEBUG_PRINTF("has_states=%d\n", bc.resources.has_states); - DEBUG_PRINTF("checks_groups=%d\n", bc.resources.checks_groups); - DEBUG_PRINTF("has_lit_delay=%d\n", bc.resources.has_lit_delay); - DEBUG_PRINTF("has_lit_check=%d\n", bc.resources.has_lit_check); - DEBUG_PRINTF("has_anchored=%d\n", bc.resources.has_anchored); - DEBUG_PRINTF("has_floating=%d\n", bc.resources.has_floating); - DEBUG_PRINTF("has_eod=%d\n", bc.resources.has_eod); + DEBUG_PRINTF("has_outfixes=%d\n", resources.has_outfixes); + DEBUG_PRINTF("has_suffixes=%d\n", resources.has_suffixes); + DEBUG_PRINTF("has_leftfixes=%d\n", resources.has_leftfixes); + DEBUG_PRINTF("has_literals=%d\n", resources.has_literals); + DEBUG_PRINTF("has_states=%d\n", resources.has_states); + DEBUG_PRINTF("checks_groups=%d\n", resources.checks_groups); + DEBUG_PRINTF("has_lit_delay=%d\n", resources.has_lit_delay); + DEBUG_PRINTF("has_lit_check=%d\n", resources.has_lit_check); + DEBUG_PRINTF("has_anchored=%d\n", resources.has_anchored); + DEBUG_PRINTF("has_floating=%d\n", resources.has_floating); + DEBUG_PRINTF("has_eod=%d\n", resources.has_eod); - if (isPureFloating(bc.resources, build.cc)) { + if (isPureFloating(resources, build.cc)) { return ROSE_RUNTIME_PURE_LITERAL; } @@ -1196,7 +1195,6 @@ bool buildLeftfix(RoseBuildImpl &build, build_context &bc, bool prefix, u32 qi, setLeftNfaProperties(*nfa, leftfix); - build.leftfix_queue_map.emplace(leftfix, qi); nfa->queueIndex = qi; if (!prefix && !leftfix.haig() && leftfix.graph() @@ -1374,9 +1372,9 @@ void buildSuffixContainer(RoseGraph &g, build_context &bc, static void updateExclusiveInfixProperties(const RoseBuildImpl &build, - build_context &bc, - const vector &exclusive_info, - set *no_retrigger_queues) { + const vector &exclusive_info, + map &leftfix_info, + set *no_retrigger_queues) { const RoseGraph &g = build.g; for (const auto &info : exclusive_info) { // Set leftfix optimisations, disabled for tamarama subengines @@ -1426,9 +1424,10 @@ void updateExclusiveInfixProperties(const RoseBuildImpl &build, const auto &verts = sub.vertices; for (const auto &v : verts) { u32 lag = g[v].left.lag; - bc.leftfix_info.emplace( - v, left_build_info(qi, lag, max_width, squash_mask, stop, - max_queuelen, cm_count, cm_cr)); + leftfix_info.emplace(v, left_build_info(qi, lag, max_width, + squash_mask, stop, + max_queuelen, cm_count, + cm_cr)); } } } @@ -1490,7 +1489,7 @@ void buildExclusiveInfixes(RoseBuildImpl &build, build_context &bc, info.queue = qif.get_queue(); exclusive_info.push_back(move(info)); } - updateExclusiveInfixProperties(build, bc, exclusive_info, + updateExclusiveInfixProperties(build, exclusive_info, bc.leftfix_info, no_retrigger_queues); buildInfixContainer(g, bc, exclusive_info); } @@ -1843,7 +1842,7 @@ bool prepOutfixes(RoseBuildImpl &tbi, build_context &bc, } static -void assignSuffixQueues(RoseBuildImpl &build, build_context &bc) { +void assignSuffixQueues(RoseBuildImpl &build, map &suffixes) { const RoseGraph &g = build.g; for (auto v : vertices_range(g)) { @@ -1856,14 +1855,13 @@ void assignSuffixQueues(RoseBuildImpl &build, build_context &bc) { DEBUG_PRINTF("vertex %zu triggers suffix %p\n", g[v].index, s.graph()); // We may have already built this NFA. - if (contains(bc.suffixes, s)) { + if (contains(suffixes, s)) { continue; } u32 queue = build.qif.get_queue(); DEBUG_PRINTF("assigning %p to queue %u\n", s.graph(), queue); - bc.suffixes.emplace(s, queue); - build.suffix_queue_map.emplace(s, queue); + suffixes.emplace(s, queue); } } @@ -2096,7 +2094,7 @@ bool buildNfas(RoseBuildImpl &tbi, build_context &bc, QueueIndexFactory &qif, no_retrigger_queues); } - assignSuffixQueues(tbi, bc); + assignSuffixQueues(tbi, bc.suffixes); if (!buildSuffixes(tbi, bc, no_retrigger_queues, suffixTriggers)) { return false; @@ -2268,8 +2266,7 @@ u32 buildLastByteIter(const RoseGraph &g, build_context &bc) { return 0; /* invalid offset */ } - vector iter; - mmbBuildSparseIterator(iter, lb_roles, bc.numStates); + auto iter = mmbBuildSparseIterator(lb_roles, bc.roleStateIndices.size()); return bc.engine_blob.add_iterator(iter); } @@ -2382,8 +2379,7 @@ u32 buildEodNfaIterator(build_context &bc, const u32 activeQueueCount) { DEBUG_PRINTF("building iter for %zu nfas\n", keys.size()); - vector iter; - mmbBuildSparseIterator(iter, keys, activeQueueCount); + auto iter = mmbBuildSparseIterator(keys, activeQueueCount); return bc.engine_blob.add_iterator(iter); } @@ -2512,16 +2508,13 @@ void recordResources(RoseResources &resources, const RoseProgram &program) { static void recordResources(RoseResources &resources, - const RoseBuildImpl &build) { + const RoseBuildImpl &build, + const vector &fragments) { if (!build.outfixes.empty()) { resources.has_outfixes = true; } - resources.has_literals = - any_of(begin(build.literal_info), end(build.literal_info), - [](const rose_literal_info &info) { - return info.fragment_id != MO_INVALID_IDX; - }); + resources.has_literals = !fragments.empty(); const auto &g = build.g; for (const auto &v : vertices_range(g)) { @@ -2537,20 +2530,21 @@ void recordResources(RoseResources &resources, } static -void recordLongLiterals(build_context &bc, const RoseProgram &program) { +void recordLongLiterals(vector &longLiterals, + const RoseProgram &program) { for (const auto &ri : program) { if (const auto *ri_check = dynamic_cast(ri.get())) { DEBUG_PRINTF("found CHECK_LONG_LIT for string '%s'\n", escapeString(ri_check->literal).c_str()); - bc.longLiterals.emplace_back(ri_check->literal, false); + longLiterals.emplace_back(ri_check->literal, false); continue; } if (const auto *ri_check = dynamic_cast(ri.get())) { DEBUG_PRINTF("found CHECK_LONG_LIT_NOCASE for string '%s'\n", escapeString(ri_check->literal).c_str()); - bc.longLiterals.emplace_back(ri_check->literal, true); + longLiterals.emplace_back(ri_check->literal, true); } } } @@ -2569,7 +2563,7 @@ u32 writeProgram(build_context &bc, RoseProgram &&program) { } recordResources(bc.resources, program); - recordLongLiterals(bc, program); + recordLongLiterals(bc.longLiterals, program); u32 len = 0; auto prog_bytecode = writeProgram(bc.engine_blob, program, &len); @@ -2581,7 +2575,7 @@ u32 writeProgram(build_context &bc, RoseProgram &&program) { } static -u32 writeActiveLeftIter(build_context &bc, +u32 writeActiveLeftIter(RoseEngineBlob &engine_blob, const vector &leftInfoTable) { vector keys; for (size_t i = 0; i < leftInfoTable.size(); i++) { @@ -2597,9 +2591,8 @@ u32 writeActiveLeftIter(build_context &bc, return 0; } - vector iter; - mmbBuildSparseIterator(iter, keys, verify_u32(leftInfoTable.size())); - return bc.engine_blob.add_iterator(iter); + auto iter = mmbBuildSparseIterator(keys, verify_u32(leftInfoTable.size())); + return engine_blob.add_iterator(iter); } static @@ -2726,19 +2719,20 @@ void writeLookaroundTables(build_context &bc, RoseEngine &proto) { } static -void writeDkeyInfo(const ReportManager &rm, build_context &bc, +void writeDkeyInfo(const ReportManager &rm, RoseEngineBlob &engine_blob, RoseEngine &proto) { const auto inv_dkeys = rm.getDkeyToReportTable(); - proto.invDkeyOffset = bc.engine_blob.add_range(inv_dkeys); + proto.invDkeyOffset = engine_blob.add_range(inv_dkeys); proto.dkeyCount = rm.numDkeys(); proto.dkeyLogSize = fatbit_size(proto.dkeyCount); } static -void writeLeftInfo(build_context &bc, RoseEngine &proto, +void writeLeftInfo(RoseEngineBlob &engine_blob, RoseEngine &proto, const vector &leftInfoTable) { - proto.leftOffset = bc.engine_blob.add_range(leftInfoTable); - proto.activeLeftIterOffset = writeActiveLeftIter(bc, leftInfoTable); + proto.leftOffset = engine_blob.add_range(leftInfoTable); + proto.activeLeftIterOffset + = writeActiveLeftIter(engine_blob, leftInfoTable); proto.roseCount = verify_u32(leftInfoTable.size()); proto.activeLeftCount = verify_u32(leftInfoTable.size()); proto.rosePrefixCount = countRosePrefixes(leftInfoTable); @@ -3592,10 +3586,11 @@ void makeRoleLookaround(const RoseBuildImpl &build, build_context &bc, } static -void makeRoleCheckLeftfix(const RoseBuildImpl &build, build_context &bc, +void makeRoleCheckLeftfix(const RoseBuildImpl &build, + const map &leftfix_info, RoseVertex v, RoseProgram &program) { - auto it = bc.leftfix_info.find(v); - if (it == end(bc.leftfix_info)) { + auto it = leftfix_info.find(v); + if (it == end(leftfix_info)) { return; } const left_build_info &lni = it->second; @@ -3623,7 +3618,8 @@ void makeRoleCheckLeftfix(const RoseBuildImpl &build, build_context &bc, } static -void makeRoleAnchoredDelay(const RoseBuildImpl &build, build_context &bc, +void makeRoleAnchoredDelay(const RoseBuildImpl &build, + u32 floatingMinLiteralMatchOffset, RoseVertex v, RoseProgram &program) { // Only relevant for roles that can be triggered by the anchored table. if (!build.isAnchored(v)) { @@ -3632,7 +3628,7 @@ void makeRoleAnchoredDelay(const RoseBuildImpl &build, build_context &bc, // If this match cannot occur after floatingMinLiteralMatchOffset, we do // not need this check. - if (build.g[v].max_offset <= bc.floatingMinLiteralMatchOffset) { + if (build.g[v].max_offset <= floatingMinLiteralMatchOffset) { return; } @@ -3662,9 +3658,9 @@ void makeDedupeSom(const RoseBuildImpl &build, const Report &report, } static -void makeCatchup(const RoseBuildImpl &build, const build_context &bc, +void makeCatchup(const RoseBuildImpl &build, bool needs_catchup, const flat_set &reports, RoseProgram &program) { - if (!bc.needs_catchup) { + if (!needs_catchup) { return; } @@ -3685,9 +3681,9 @@ void makeCatchup(const RoseBuildImpl &build, const build_context &bc, } static -void makeCatchupMpv(const RoseBuildImpl &build, const build_context &bc, +void makeCatchupMpv(const RoseBuildImpl &build, bool needs_mpv_catchup, ReportID id, RoseProgram &program) { - if (!bc.needs_mpv_catchup) { + if (!needs_mpv_catchup) { return; } @@ -3931,7 +3927,7 @@ void makeRoleReports(const RoseBuildImpl &build, const build_context &bc, } const auto &reports = g[v].reports; - makeCatchup(build, bc, reports, program); + makeCatchup(build, bc.needs_catchup, reports, program); RoseProgram report_block; for (ReportID id : reports) { @@ -4067,12 +4063,12 @@ void makeRoleInfixTriggers(const RoseBuildImpl &build, const build_context &bc, } static -void makeRoleSetState(const build_context &bc, RoseVertex v, - RoseProgram &program) { +void makeRoleSetState(const unordered_map &roleStateIndices, + RoseVertex v, RoseProgram &program) { // We only need this instruction if a state index has been assigned to this // vertex. - auto it = bc.roleStateIndices.find(v); - if (it == end(bc.roleStateIndices)) { + auto it = roleStateIndices.find(v); + if (it == end(roleStateIndices)) { return; } program.add_before_end(make_unique(it->second)); @@ -4181,7 +4177,8 @@ RoseProgram makeProgram(const RoseBuildImpl &build, build_context &bc, // First, add program instructions that enforce preconditions without // effects. - makeRoleAnchoredDelay(build, bc, v, program); + makeRoleAnchoredDelay(build, prog_build.floatingMinLiteralMatchOffset, v, + program); if (onlyAtEod(build, v)) { DEBUG_PRINTF("only at eod\n"); @@ -4201,7 +4198,7 @@ RoseProgram makeProgram(const RoseBuildImpl &build, build_context &bc, } makeRoleLookaround(build, bc, v, program); - makeRoleCheckLeftfix(build, bc, v, program); + makeRoleCheckLeftfix(build, bc.leftfix_info, v, program); // Next, we can add program instructions that have effects. This must be // done as a series of blocks, as some of them (like reports) are @@ -4228,7 +4225,7 @@ RoseProgram makeProgram(const RoseBuildImpl &build, build_context &bc, effects_block.add_block(move(suffix_block)); RoseProgram state_block; - makeRoleSetState(bc, v, state_block); + makeRoleSetState(bc.roleStateIndices, v, state_block); effects_block.add_block(move(state_block)); // Note: EOD eager reports may generate a CHECK_ONLY_EOD instruction (if @@ -4279,11 +4276,11 @@ void makeBoundaryPrograms(const RoseBuildImpl &build, build_context &bc, } static -void assignStateIndices(const RoseBuildImpl &build, build_context &bc) { +unordered_map assignStateIndices(const RoseBuildImpl &build) { const auto &g = build.g; u32 state = 0; - + unordered_map roleStateIndices; for (auto v : vertices_range(g)) { // Virtual vertices (starts, EOD accept vertices) never need state // indices. @@ -4306,12 +4303,13 @@ void assignStateIndices(const RoseBuildImpl &build, build_context &bc) { } /* TODO: also don't need a state index if all edges are nfa based */ - bc.roleStateIndices.emplace(v, state++); + roleStateIndices.emplace(v, state++); } DEBUG_PRINTF("assigned %u states (from %zu vertices)\n", state, num_vertices(g)); - bc.numStates = state; + + return roleStateIndices; } static @@ -4426,8 +4424,7 @@ void addPredBlockSingle(u32 pred_state, RoseProgram &pred_block, } static -void addPredBlocksAny(const build_context &bc, - map &pred_blocks, +void addPredBlocksAny(map &pred_blocks, u32 num_states, RoseProgram &program) { RoseProgram sparse_program; @@ -4437,7 +4434,7 @@ void addPredBlocksAny(const build_context &bc, } const RoseInstruction *end_inst = sparse_program.end_instruction(); - auto ri = make_unique(bc.numStates, keys, end_inst); + auto ri = make_unique(num_states, keys, end_inst); sparse_program.add_before_end(move(ri)); RoseProgram &block = pred_blocks.begin()->second; @@ -4446,9 +4443,8 @@ void addPredBlocksAny(const build_context &bc, } static -void addPredBlocksMulti(const build_context &bc, - map &pred_blocks, - RoseProgram &program) { +void addPredBlocksMulti(map &pred_blocks, + u32 num_states, RoseProgram &program) { assert(!pred_blocks.empty()); RoseProgram sparse_program; @@ -4456,8 +4452,7 @@ void addPredBlocksMulti(const build_context &bc, vector> jump_table; // BEGIN instruction. - auto ri_begin = - make_unique(bc.numStates, end_inst); + auto ri_begin = make_unique(num_states, end_inst); RoseInstrSparseIterBegin *begin_inst = ri_begin.get(); sparse_program.add_before_end(move(ri_begin)); @@ -4498,7 +4493,7 @@ void addPredBlocksMulti(const build_context &bc, } static -void addPredBlocks(const build_context &bc, map &pred_blocks, +void addPredBlocks(map &pred_blocks, u32 num_states, RoseProgram &program) { // Trim empty blocks, if any exist. for (auto it = pred_blocks.begin(); it != pred_blocks.end();) { @@ -4527,11 +4522,11 @@ void addPredBlocks(const build_context &bc, map &pred_blocks, return RoseProgramEquivalence()(*begin(blocks), block); })) { DEBUG_PRINTF("all blocks equiv\n"); - addPredBlocksAny(bc, pred_blocks, program); + addPredBlocksAny(pred_blocks, num_states, program); return; } - addPredBlocksMulti(bc, pred_blocks, program); + addPredBlocksMulti(pred_blocks, num_states, program); } static @@ -4658,14 +4653,15 @@ u32 findMinOffset(const RoseBuildImpl &build, u32 lit_id) { } static -void makeCheckLitEarlyInstruction(const RoseBuildImpl &build, build_context &bc, - u32 lit_id, const vector &lit_edges, +void makeCheckLitEarlyInstruction(const RoseBuildImpl &build, u32 lit_id, + const vector &lit_edges, + u32 floatingMinLiteralMatchOffset, RoseProgram &program) { if (lit_edges.empty()) { return; } - if (bc.floatingMinLiteralMatchOffset == 0) { + if (floatingMinLiteralMatchOffset == 0) { return; } @@ -4677,18 +4673,17 @@ void makeCheckLitEarlyInstruction(const RoseBuildImpl &build, build_context &bc, const auto &lit = build.literals.right.at(lit_id); size_t min_len = lit.elength(); u32 min_offset = findMinOffset(build, lit_id); - DEBUG_PRINTF("has min_len=%zu, min_offset=%u, " - "global min is %u\n", min_len, min_offset, - bc.floatingMinLiteralMatchOffset); + DEBUG_PRINTF("has min_len=%zu, min_offset=%u, global min is %u\n", min_len, + min_offset, floatingMinLiteralMatchOffset); // If we can't match before the min offset, we don't need the check. - if (min_len >= bc.floatingMinLiteralMatchOffset) { + if (min_len >= floatingMinLiteralMatchOffset) { DEBUG_PRINTF("no need for check, min is %u\n", - bc.floatingMinLiteralMatchOffset); + floatingMinLiteralMatchOffset); return; } - assert(min_offset >= bc.floatingMinLiteralMatchOffset); + assert(min_offset >= floatingMinLiteralMatchOffset); assert(min_offset < UINT32_MAX); DEBUG_PRINTF("adding lit early check, min_offset=%u\n", min_offset); @@ -4698,13 +4693,13 @@ void makeCheckLitEarlyInstruction(const RoseBuildImpl &build, build_context &bc, } static -void makeCheckLiteralInstruction(const RoseBuildImpl &build, - const build_context &bc, u32 lit_id, +void makeCheckLiteralInstruction(const RoseBuildImpl &build, u32 lit_id, + size_t longLitLengthThreshold, RoseProgram &program) { - assert(bc.longLitLengthThreshold > 0); + assert(longLitLengthThreshold > 0); DEBUG_PRINTF("lit_id=%u, long lit threshold %zu\n", lit_id, - bc.longLitLengthThreshold); + longLitLengthThreshold); if (build.isDelayed(lit_id)) { return; @@ -4722,7 +4717,7 @@ void makeCheckLiteralInstruction(const RoseBuildImpl &build, throw ResourceLimitError(); } - if (lit.s.length() <= bc.longLitLengthThreshold) { + if (lit.s.length() <= longLitLengthThreshold) { DEBUG_PRINTF("is a medium-length literal\n"); const auto *end_inst = program.end_instruction(); unique_ptr ri; @@ -4776,7 +4771,8 @@ RoseProgram buildLitInitialProgram(const RoseBuildImpl &build, RoseProgram program; // Check long literal info. - makeCheckLiteralInstruction(build, bc, lit_id, program); + makeCheckLiteralInstruction(build, lit_id, bc.longLitLengthThreshold, + program); // Check lit mask. makeCheckLitMaskInstruction(build, bc, lit_id, program); @@ -4792,7 +4788,9 @@ RoseProgram buildLitInitialProgram(const RoseBuildImpl &build, makePushDelayedInstructions(build, prog_build, lit_id, program); // Add pre-check for early literals in the floating table. - makeCheckLitEarlyInstruction(build, bc, lit_id, lit_edges, program); + makeCheckLitEarlyInstruction(build, lit_id, lit_edges, + prog_build.floatingMinLiteralMatchOffset, + program); return program; } @@ -4827,7 +4825,7 @@ RoseProgram buildLiteralProgram(const RoseBuildImpl &build, build_context &bc, // Add blocks to deal with non-root edges (triggered by sparse iterator or // mmbit_isset checks). - addPredBlocks(bc, pred_blocks, program); + addPredBlocks(pred_blocks, bc.roleStateIndices.size(), program); // Add blocks to handle root roles. for (const auto &e : lit_edges) { @@ -4896,7 +4894,7 @@ RoseProgram assembleProgramBlocks(vector &&blocks) { static u32 writeLiteralProgram(const RoseBuildImpl &build, build_context &bc, - ProgramBuild &prog_build, const flat_set &lit_ids, + ProgramBuild &prog_build, const vector &lit_ids, const map> &lit_edge_map, bool is_anchored_program) { assert(!lit_ids.empty()); @@ -4947,7 +4945,7 @@ u32 writeLiteralProgram(const RoseBuildImpl &build, build_context &bc, static u32 writeDelayRebuildProgram(const RoseBuildImpl &build, build_context &bc, ProgramBuild &prog_build, - const flat_set &lit_ids) { + const vector &lit_ids) { assert(!lit_ids.empty()); if (!build.cc.streaming) { @@ -4964,7 +4962,8 @@ u32 writeDelayRebuildProgram(const RoseBuildImpl &build, build_context &bc, } RoseProgram prog; - makeCheckLiteralInstruction(build, bc, lit_id, prog); + makeCheckLiteralInstruction(build, lit_id, bc.longLitLengthThreshold, + prog); makeCheckLitMaskInstruction(build, bc, lit_id, prog); makePushDelayedInstructions(build, prog_build, lit_id, prog); blocks.push_back(move(prog)); @@ -5046,7 +5045,8 @@ rose_literal_id getFragment(const rose_literal_id &lit) { } static -void groupByFragment(RoseBuildImpl &build) { +vector groupByFragment(const RoseBuildImpl &build) { + vector fragments; u32 frag_id = 0; struct FragmentInfo { @@ -5055,9 +5055,6 @@ void groupByFragment(RoseBuildImpl &build) { }; map frag_info; - map lit_to_frag; - - auto &fragments = build.fragments; for (const auto &m : build.literals.right) { const u32 lit_id = m.first; @@ -5077,8 +5074,7 @@ void groupByFragment(RoseBuildImpl &build) { auto groups = info.group_mask; if (lit.s.length() < ROSE_SHORT_LITERAL_LEN_MAX) { - lit_to_frag.emplace(lit_id, frag_id); - fragments.emplace_back(frag_id, groups); + fragments.emplace_back(frag_id, groups, lit_id); frag_id++; continue; } @@ -5090,53 +5086,38 @@ void groupByFragment(RoseBuildImpl &build) { fi.groups |= groups; } - for (const auto &m : frag_info) { - const auto &fi = m.second; + for (auto &m : frag_info) { + auto &fi = m.second; DEBUG_PRINTF("frag %s -> ids: %s\n", dumpString(m.first.s).c_str(), as_string_list(fi.lit_ids).c_str()); - fragments.emplace_back(frag_id, fi.groups); - for (const auto lit_id : fi.lit_ids) { - assert(!contains(lit_to_frag, lit_id)); - lit_to_frag.emplace(lit_id, frag_id); - } + sort(fi.lit_ids.begin(), fi.lit_ids.end()); /* to match old behaviour */ + fragments.emplace_back(frag_id, fi.groups, move(fi.lit_ids)); frag_id++; + assert(frag_id == fragments.size()); } - // Write the fragment IDs into the literal_info structures. - for (const auto &m : lit_to_frag) { - build.literal_info[m.first].fragment_id = m.second; - } + return fragments; } /** * \brief Build the interpreter programs for each literal. */ static -void buildLiteralPrograms(RoseBuildImpl &build, build_context &bc, +void buildLiteralPrograms(const RoseBuildImpl &build, + vector &fragments, build_context &bc, ProgramBuild &prog_build) { - // Build a reverse mapping from fragment -> {lit_id, lit_id,...} - map> frag_to_lit_map; - for (u32 lit_id = 0; lit_id < verify_u32(build.literal_info.size()); - lit_id++) { - const auto &info = build.literal_info[lit_id]; - if (info.fragment_id == MO_INVALID_IDX) { - continue; - } - frag_to_lit_map[info.fragment_id].insert(lit_id); - } - - DEBUG_PRINTF("%zu fragments\n", build.fragments.size()); + DEBUG_PRINTF("%zu fragments\n", fragments.size()); auto lit_edge_map = findEdgesByLiteral(build); - for (auto &frag : build.fragments) { - const auto &lit_ids = frag_to_lit_map[frag.fragment_id]; + for (auto &frag : fragments) { DEBUG_PRINTF("frag_id=%u, lit_ids=[%s]\n", frag.fragment_id, - as_string_list(lit_ids).c_str()); + as_string_list(frag.lit_ids).c_str()); - frag.lit_program_offset = writeLiteralProgram( - build, bc, prog_build, lit_ids, lit_edge_map, false); - frag.delay_program_offset = - writeDelayRebuildProgram(build, bc, prog_build, lit_ids); + frag.lit_program_offset + = writeLiteralProgram(build, bc, prog_build, frag.lit_ids, + lit_edge_map, false); + frag.delay_program_offset + = writeDelayRebuildProgram(build, bc, prog_build, frag.lit_ids); } } @@ -5147,39 +5128,40 @@ void buildLiteralPrograms(RoseBuildImpl &build, build_context &bc, * programs. */ static -pair writeDelayPrograms(const RoseBuildImpl &build, build_context &bc, +pair writeDelayPrograms(const RoseBuildImpl &build, + const vector &fragments, + build_context &bc, ProgramBuild &prog_build) { auto lit_edge_map = findEdgesByLiteral(build); vector programs; // program offsets indexed by (delayed) lit id unordered_map cache; // program offsets we have already seen - for (const auto &lit_id : build.literals.right | map_keys) { - const auto &info = build.literal_info.at(lit_id); + for (const auto &frag : fragments) { + for (const u32 lit_id : frag.lit_ids) { + const auto &info = build.literal_info.at(lit_id); - if (info.fragment_id == MO_INVALID_IDX) { - continue; // Unused literal. - } + for (const auto &delayed_lit_id : info.delayed_ids) { + DEBUG_PRINTF("lit id %u delay id %u\n", lit_id, delayed_lit_id); + u32 offset = writeLiteralProgram(build, bc, prog_build, + {delayed_lit_id}, lit_edge_map, + false); - for (const auto &delayed_lit_id : info.delayed_ids) { - DEBUG_PRINTF("lit id %u delay id %u\n", lit_id, delayed_lit_id); - u32 offset = writeLiteralProgram( - build, bc, prog_build, {delayed_lit_id}, lit_edge_map, false); - - u32 delay_id; - auto it = cache.find(offset); - if (it != end(cache)) { - delay_id = it->second; - DEBUG_PRINTF("reusing delay_id %u for offset %u\n", delay_id, - offset); - } else { - delay_id = verify_u32(programs.size()); - programs.push_back(offset); - cache.emplace(offset, delay_id); - DEBUG_PRINTF("assigned new delay_id %u for offset %u\n", - delay_id, offset); + u32 delay_id; + auto it = cache.find(offset); + if (it != end(cache)) { + delay_id = it->second; + DEBUG_PRINTF("reusing delay_id %u for offset %u\n", + delay_id, offset); + } else { + delay_id = verify_u32(programs.size()); + programs.push_back(offset); + cache.emplace(offset, delay_id); + DEBUG_PRINTF("assigned new delay_id %u for offset %u\n", + delay_id, offset); + } + prog_build.delay_programs.emplace(delayed_lit_id, delay_id); } - prog_build.delay_programs.emplace(delayed_lit_id, delay_id); } } @@ -5195,6 +5177,7 @@ pair writeDelayPrograms(const RoseBuildImpl &build, build_context &bc, */ static pair writeAnchoredPrograms(const RoseBuildImpl &build, + const vector &fragments, build_context &bc, ProgramBuild &prog_build) { auto lit_edge_map = findEdgesByLiteral(build); @@ -5202,44 +5185,42 @@ pair writeAnchoredPrograms(const RoseBuildImpl &build, vector programs; // program offsets indexed by anchored id unordered_map cache; // program offsets we have already seen - for (const auto &m : build.literals.right) { - u32 lit_id = m.first; - const auto &lit = m.second; + for (const auto &frag : fragments) { + for (const u32 lit_id : frag.lit_ids) { + const auto &lit = build.literals.right.at(lit_id); - if (lit.table != ROSE_ANCHORED) { - continue; + if (lit.table != ROSE_ANCHORED) { + continue; + } + + // If this anchored literal can never match past + // floatingMinLiteralMatchOffset, we will never have to record it. + if (findMaxOffset(build, lit_id) + <= prog_build.floatingMinLiteralMatchOffset) { + DEBUG_PRINTF("can never match after " + "floatingMinLiteralMatchOffset=%u\n", + prog_build.floatingMinLiteralMatchOffset); + continue; + } + + u32 offset = writeLiteralProgram(build, bc, prog_build, {lit_id}, + lit_edge_map, true); + DEBUG_PRINTF("lit_id=%u -> anch prog at %u\n", lit_id, offset); + + u32 anch_id; + auto it = cache.find(offset); + if (it != end(cache)) { + anch_id = it->second; + DEBUG_PRINTF("reusing anch_id %u for offset %u\n", anch_id, offset); + } else { + anch_id = verify_u32(programs.size()); + programs.push_back(offset); + cache.emplace(offset, anch_id); + DEBUG_PRINTF("assigned new anch_id %u for offset %u\n", anch_id, + offset); + } + prog_build.anchored_programs.emplace(lit_id, anch_id); } - - if (build.literal_info.at(lit_id).fragment_id == MO_INVALID_IDX) { - continue; // Unused literal. - } - - // If this anchored literal can never match past - // floatingMinLiteralMatchOffset, we will never have to record it. - if (findMaxOffset(build, lit_id) <= bc.floatingMinLiteralMatchOffset) { - DEBUG_PRINTF("can never match after " - "floatingMinLiteralMatchOffset=%u\n", - bc.floatingMinLiteralMatchOffset); - continue; - } - - u32 offset = writeLiteralProgram(build, bc, prog_build, {lit_id}, - lit_edge_map, true); - DEBUG_PRINTF("lit_id=%u -> anch prog at %u\n", lit_id, offset); - - u32 anch_id; - auto it = cache.find(offset); - if (it != end(cache)) { - anch_id = it->second; - DEBUG_PRINTF("reusing anch_id %u for offset %u\n", anch_id, offset); - } else { - anch_id = verify_u32(programs.size()); - programs.push_back(offset); - cache.emplace(offset, anch_id); - DEBUG_PRINTF("assigned new anch_id %u for offset %u\n", anch_id, - offset); - } - prog_build.anchored_programs.emplace(lit_id, anch_id); } DEBUG_PRINTF("%zu anchored programs\n", programs.size()); @@ -5283,7 +5264,7 @@ pair buildReportPrograms(const RoseBuildImpl &build, for (ReportID id : reports) { RoseProgram program; const bool has_som = false; - makeCatchupMpv(build, bc, id, program); + makeCatchupMpv(build, bc.needs_mpv_catchup, id, program); makeReport(build, id, has_som, program); applyFinalSpecialisation(program); u32 offset = writeProgram(bc, move(program)); @@ -5300,7 +5281,7 @@ pair buildReportPrograms(const RoseBuildImpl &build, static RoseProgram makeEodAnchorProgram(const RoseBuildImpl &build, - const build_context &bc, + bool needs_catchup, ProgramBuild &prog_build, const RoseEdge &e, const bool multiple_preds) { const RoseGraph &g = build.g; @@ -5318,7 +5299,7 @@ RoseProgram makeEodAnchorProgram(const RoseBuildImpl &build, } const auto &reports = g[v].reports; - makeCatchup(build, bc, reports, program); + makeCatchup(build, needs_catchup, reports, program); const bool has_som = false; RoseProgram report_block; @@ -5393,11 +5374,12 @@ void addEodAnchorProgram(const RoseBuildImpl &build, const build_context &bc, assert(contains(bc.roleStateIndices, u)); u32 pred_state = bc.roleStateIndices.at(u); pred_blocks[pred_state].add_block( - makeEodAnchorProgram(build, bc, prog_build, e, multiple_preds)); + makeEodAnchorProgram(build, bc.needs_catchup, prog_build, e, + multiple_preds)); } } - addPredBlocks(bc, pred_blocks, program); + addPredBlocks(pred_blocks, bc.roleStateIndices.size(), program); } static @@ -5588,7 +5570,7 @@ void fillMatcherDistances(const RoseBuildImpl &build, RoseEngine *engine) { static u32 writeEagerQueueIter(const set &eager, u32 leftfixBeginQueue, - u32 queue_count, build_context &bc) { + u32 queue_count, RoseEngineBlob &engine_blob) { if (eager.empty()) { return 0; } @@ -5599,9 +5581,8 @@ u32 writeEagerQueueIter(const set &eager, u32 leftfixBeginQueue, vec.push_back(q - leftfixBeginQueue); } - vector iter; - mmbBuildSparseIterator(iter, vec, queue_count - leftfixBeginQueue); - return bc.engine_blob.add_iterator(iter); + auto iter = mmbBuildSparseIterator(vec, queue_count - leftfixBeginQueue); + return engine_blob.add_iterator(iter); } static @@ -5707,6 +5688,19 @@ size_t calcLongLitThreshold(const RoseBuildImpl &build, return longLitLengthThreshold; } +static +map makeLeftQueueMap(const RoseGraph &g, + const map &leftfix_info) { + map lqm; + for (const auto &e : leftfix_info) { + left_id left(g[e.first].left); + assert(!contains(lqm, left) || lqm[left] == e.second.queue); + lqm[left] = e.second.queue; + } + + return lqm; +} + aligned_unique_ptr RoseBuildImpl::buildFinalEngine(u32 minWidth) { // We keep all our offsets, counts etc. in a prototype RoseEngine which we // will copy into the real one once it is allocated: we can't do this @@ -5730,16 +5724,16 @@ aligned_unique_ptr RoseBuildImpl::buildFinalEngine(u32 minWidth) { historyRequired); DEBUG_PRINTF("longLitLengthThreshold=%zu\n", longLitLengthThreshold); - groupByFragment(*this); + vector fragments = groupByFragment(*this); - auto anchored_dfas = buildAnchoredDfas(*this); + auto anchored_dfas = buildAnchoredDfas(*this, fragments); build_context bc; - bc.floatingMinLiteralMatchOffset = - findMinFloatingLiteralMatch(*this, anchored_dfas); + u32 floatingMinLiteralMatchOffset + = findMinFloatingLiteralMatch(*this, anchored_dfas); bc.longLitLengthThreshold = longLitLengthThreshold; bc.needs_catchup = needsCatchup(*this, anchored_dfas); - recordResources(bc.resources, *this); + recordResources(bc.resources, *this, fragments); if (!anchored_dfas.empty()) { bc.resources.has_anchored = true; } @@ -5777,7 +5771,12 @@ aligned_unique_ptr RoseBuildImpl::buildFinalEngine(u32 minWidth) { throw ResourceLimitError(); } - assignStateIndices(*this, bc); + // Enforce role table resource limit. + if (num_vertices(g) > cc.grey.limitRoseRoleCount) { + throw ResourceLimitError(); + } + + bc.roleStateIndices = assignStateIndices(*this); u32 laggedRoseCount = 0; vector leftInfoTable; @@ -5786,17 +5785,17 @@ aligned_unique_ptr RoseBuildImpl::buildFinalEngine(u32 minWidth) { &laggedRoseCount, &historyRequired); // Information only needed for program construction. - ProgramBuild prog_build; + ProgramBuild prog_build(floatingMinLiteralMatchOffset); prog_build.vertex_group_map = getVertexGroupMap(*this); prog_build.squashable_groups = getSquashableGroups(*this); tie(proto.anchoredProgramOffset, proto.anchored_count) = - writeAnchoredPrograms(*this, bc, prog_build); + writeAnchoredPrograms(*this, fragments, bc, prog_build); tie(proto.delayProgramOffset, proto.delay_count) = - writeDelayPrograms(*this, bc, prog_build); + writeDelayPrograms(*this, fragments, bc, prog_build); - buildLiteralPrograms(*this, bc, prog_build); + buildLiteralPrograms(*this, fragments, bc, prog_build); proto.eodProgramOffset = writeEodProgram(*this, bc, prog_build, eodNfaIterOffset); @@ -5808,22 +5807,17 @@ aligned_unique_ptr RoseBuildImpl::buildFinalEngine(u32 minWidth) { proto.lastByteHistoryIterOffset = buildLastByteIter(g, bc); proto.eagerIterOffset = writeEagerQueueIter( - eager_queues, proto.leftfixBeginQueue, queue_count, bc); + eager_queues, proto.leftfixBeginQueue, queue_count, bc.engine_blob); addSomRevNfas(bc, proto, ssm); writeLookaroundTables(bc, proto); - writeDkeyInfo(rm, bc, proto); - writeLeftInfo(bc, proto, leftInfoTable); - - // Enforce role table resource limit. - if (num_vertices(g) > cc.grey.limitRoseRoleCount) { - throw ResourceLimitError(); - } + writeDkeyInfo(rm, bc.engine_blob, proto); + writeLeftInfo(bc.engine_blob, proto, leftInfoTable); // Build anchored matcher. size_t asize = 0; - auto atable = buildAnchoredMatcher(*this, anchored_dfas, &asize); + auto atable = buildAnchoredMatcher(*this, fragments, anchored_dfas, &asize); if (atable) { proto.amatcherOffset = bc.engine_blob.add(atable.get(), asize, 64); } @@ -5831,7 +5825,8 @@ aligned_unique_ptr RoseBuildImpl::buildFinalEngine(u32 minWidth) { // Build floating HWLM matcher. rose_group fgroups = 0; size_t fsize = 0; - auto ftable = buildFloatingMatcher(*this, bc.longLitLengthThreshold, + auto ftable = buildFloatingMatcher(*this, fragments, + bc.longLitLengthThreshold, &fgroups, &fsize, &historyRequired); if (ftable) { proto.fmatcherOffset = bc.engine_blob.add(ftable.get(), fsize, 64); @@ -5840,22 +5835,22 @@ aligned_unique_ptr RoseBuildImpl::buildFinalEngine(u32 minWidth) { // Build delay rebuild HWLM matcher. size_t drsize = 0; - auto drtable = - buildDelayRebuildMatcher(*this, bc.longLitLengthThreshold, &drsize); + auto drtable = buildDelayRebuildMatcher(*this, fragments, + bc.longLitLengthThreshold, &drsize); if (drtable) { proto.drmatcherOffset = bc.engine_blob.add(drtable.get(), drsize, 64); } // Build EOD-anchored HWLM matcher. size_t esize = 0; - auto etable = buildEodAnchoredMatcher(*this, &esize); + auto etable = buildEodAnchoredMatcher(*this, fragments, &esize); if (etable) { proto.ematcherOffset = bc.engine_blob.add(etable.get(), esize, 64); } // Build small-block HWLM matcher. size_t sbsize = 0; - auto sbtable = buildSmallBlockMatcher(*this, &sbsize); + auto sbtable = buildSmallBlockMatcher(*this, fragments, &sbsize); if (sbtable) { proto.sbmatcherOffset = bc.engine_blob.add(sbtable.get(), sbsize, 64); } @@ -5873,7 +5868,7 @@ aligned_unique_ptr RoseBuildImpl::buildFinalEngine(u32 minWidth) { assert(!cc.streaming || historyRequired <= max(cc.grey.maxHistoryAvailable, cc.grey.somMaxRevNfaLength)); - fillStateOffsets(*this, bc.numStates, proto.anchorStateSize, + fillStateOffsets(*this, bc.roleStateIndices.size(), proto.anchorStateSize, proto.activeArrayCount, proto.activeLeftCount, laggedRoseCount, longLitStreamStateRequired, historyRequired, &proto.stateOffsets); @@ -5883,9 +5878,9 @@ aligned_unique_ptr RoseBuildImpl::buildFinalEngine(u32 minWidth) { writeNfaInfo(*this, bc, proto, no_retrigger_queues); scatter_plan_raw state_scatter = buildStateScatterPlan( - sizeof(u8), bc.numStates, proto.activeLeftCount, proto.rosePrefixCount, - proto.stateOffsets, cc.streaming, proto.activeArrayCount, - proto.outfixBeginQueue, proto.outfixEndQueue); + sizeof(u8), bc.roleStateIndices.size(), proto.activeLeftCount, + proto.rosePrefixCount, proto.stateOffsets, cc.streaming, + proto.activeArrayCount, proto.outfixBeginQueue, proto.outfixEndQueue); u32 currOffset; /* relative to base of RoseEngine */ if (!bc.engine_blob.empty()) { @@ -5910,7 +5905,8 @@ aligned_unique_ptr RoseBuildImpl::buildFinalEngine(u32 minWidth) { proto.needsCatchup = bc.needs_catchup ? 1 : 0; - proto.runtimeImpl = pickRuntimeImpl(*this, bc, proto.outfixEndQueue); + proto.runtimeImpl = pickRuntimeImpl(*this, bc.resources, + proto.outfixEndQueue); proto.mpvTriggeredByLeaf = anyEndfixMpvTriggers(*this); proto.queueCount = queue_count; @@ -5918,10 +5914,10 @@ aligned_unique_ptr RoseBuildImpl::buildFinalEngine(u32 minWidth) { proto.handledKeyCount = prog_build.handledKeys.size(); proto.handledKeyFatbitSize = fatbit_size(proto.handledKeyCount); - proto.rolesWithStateCount = bc.numStates; + proto.rolesWithStateCount = bc.roleStateIndices.size(); proto.initMpvNfa = mpv_as_outfix ? 0 : MO_INVALID_IDX; - proto.stateSize = mmbit_size(bc.numStates); + proto.stateSize = mmbit_size(bc.roleStateIndices.size()); proto.delay_fatbit_size = fatbit_size(proto.delay_count); proto.anchored_fatbit_size = fatbit_size(proto.anchored_count); @@ -5938,7 +5934,7 @@ aligned_unique_ptr RoseBuildImpl::buildFinalEngine(u32 minWidth) { proto.fmatcherMaxBiAnchoredWidth = findMaxBAWidth(*this, ROSE_FLOATING); proto.minWidth = hasBoundaryReports(boundary) ? 0 : minWidth; proto.minWidthExcludingBoundaries = minWidth; - proto.floatingMinLiteralMatchOffset = bc.floatingMinLiteralMatchOffset; + proto.floatingMinLiteralMatchOffset = floatingMinLiteralMatchOffset; proto.maxBiAnchoredWidth = findMaxBAWidth(*this); proto.noFloatingRoots = hasNoFloatingRoots(); @@ -5977,7 +5973,8 @@ aligned_unique_ptr RoseBuildImpl::buildFinalEngine(u32 minWidth) { DEBUG_PRINTF("rose done %p\n", engine.get()); - dumpRose(*this, engine.get()); + dumpRose(*this, fragments, makeLeftQueueMap(g, bc.leftfix_info), + bc.suffixes, engine.get()); return engine; } diff --git a/src/rose/rose_build_compile.cpp b/src/rose/rose_build_compile.cpp index 00586f65..791a68ab 100644 --- a/src/rose/rose_build_compile.cpp +++ b/src/rose/rose_build_compile.cpp @@ -1669,7 +1669,7 @@ bool roleOffsetsAreValid(const RoseGraph &g) { #endif // NDEBUG aligned_unique_ptr RoseBuildImpl::buildRose(u32 minWidth) { - dumpRoseGraph(*this, nullptr, "rose_early.dot"); + dumpRoseGraph(*this, "rose_early.dot"); // Early check for Rose implementability. assert(canImplementGraphs(*this)); @@ -1780,7 +1780,7 @@ aligned_unique_ptr RoseBuildImpl::buildRose(u32 minWidth) { assert(roleOffsetsAreValid(g)); assert(historiesAreValid(g)); - dumpRoseGraph(*this, nullptr, "rose_pre_norm.dot"); + dumpRoseGraph(*this, "rose_pre_norm.dot"); return buildFinalEngine(minWidth); } diff --git a/src/rose/rose_build_dump.cpp b/src/rose/rose_build_dump.cpp index 30dccb1a..a52830b0 100644 --- a/src/rose/rose_build_dump.cpp +++ b/src/rose/rose_build_dump.cpp @@ -112,8 +112,11 @@ string rose_off::str(void) const { class RoseGraphWriter { public: - RoseGraphWriter(const RoseBuildImpl &b_in, const RoseEngine *t_in) : - build(b_in), t(t_in) { + RoseGraphWriter(const RoseBuildImpl &b_in, const map &frag_map_in, + const map &lqm_in, + const map &sqm_in, const RoseEngine *t_in) + : frag_map(frag_map_in), leftfix_queue_map(lqm_in), + suffix_queue_map(sqm_in), build(b_in), t(t_in) { for (const auto &m : build.ghost) { ghost.insert(m.second); } @@ -160,8 +163,8 @@ public: if (g[v].suffix) { suffix_id suff(g[v].suffix); os << "\\n" << render_kind(suff) << " (top " << g[v].suffix.top; - auto it = build.suffix_queue_map.find(suff); - if (it != end(build.suffix_queue_map)) { + auto it = suffix_queue_map.find(suff); + if (it != end(suffix_queue_map)) { os << ", queue " << it->second; } os << ")"; @@ -174,8 +177,8 @@ public: if (g[v].left) { left_id left(g[v].left); os << "\\n" << render_kind(left) << " (queue "; - auto it = build.leftfix_queue_map.find(left); - if (it != end(build.leftfix_queue_map)) { + auto it = leftfix_queue_map.find(left); + if (it != end(leftfix_queue_map)) { os << it->second; } else { os << "??"; @@ -248,8 +251,8 @@ private: // Render the literal associated with a vertex. void writeLiteral(ostream &os, u32 id) const { os << "lit=" << id; - if (id < build.literal_info.size()) { - os << "/" << build.literal_info[id].fragment_id << " "; + if (contains(frag_map, id)) { + os << "/" << frag_map.at(id) << " "; } else { os << "/nofrag "; } @@ -269,13 +272,32 @@ private: } set ghost; + const map &frag_map; + const map &leftfix_queue_map; + const map &suffix_queue_map; const RoseBuildImpl &build; const RoseEngine *t; }; } // namespace +static +map makeFragMap(const vector &fragments) { + map fm; + for (const auto &f : fragments) { + for (u32 id : f.lit_ids) { + fm[id] = f.fragment_id; + } + } + + return fm; +} + +static void dumpRoseGraph(const RoseBuildImpl &build, const RoseEngine *t, + const vector &fragments, + const map &leftfix_queue_map, + const map &suffix_queue_map, const char *filename) { const Grey &grey = build.cc.grey; @@ -293,10 +315,16 @@ void dumpRoseGraph(const RoseBuildImpl &build, const RoseEngine *t, DEBUG_PRINTF("dumping graph to %s\n", ss.str().c_str()); ofstream os(ss.str()); - RoseGraphWriter writer(build, t); + auto frag_map = makeFragMap(fragments); + RoseGraphWriter writer(build, frag_map, leftfix_queue_map, suffix_queue_map, + t); writeGraphviz(os, build.g, writer, get(boost::vertex_index, build.g)); } +void dumpRoseGraph(const RoseBuildImpl &build, const char *filename) { + dumpRoseGraph(build, nullptr, {}, {}, {}, filename); +} + namespace { struct CompareVertexRole { explicit CompareVertexRole(const RoseGraph &g_in) : g(g_in) {} @@ -321,11 +349,14 @@ void lit_graph_info(const RoseBuildImpl &build, const rose_literal_info &li, } static -void dumpRoseLiterals(const RoseBuildImpl &build, const char *filename) { +void dumpRoseLiterals(const RoseBuildImpl &build, + const vector &fragments, + const Grey &grey) { const RoseGraph &g = build.g; + map frag_map = makeFragMap(fragments); DEBUG_PRINTF("dumping literals\n"); - ofstream os(filename); + ofstream os(grey.dumpPath + "rose_literals.txt"); os << "ROSE LITERALS: a total of " << build.literals.right.size() << " literals and " << num_vertices(g) << " roles." << endl << endl; @@ -353,8 +384,11 @@ void dumpRoseLiterals(const RoseBuildImpl &build, const char *filename) { break; } - os << " ID " << id << "/" << lit_info.fragment_id << ": \"" - << escapeString(s.get_string()) << "\"" + os << " ID " << id; + if (contains(frag_map, id)) { + os << "/" << frag_map.at(id); + } + os << ": \"" << escapeString(s.get_string()) << "\"" << " (len " << s.length() << ","; if (s.any_nocase()) { os << " nocase,"; @@ -833,7 +867,7 @@ void dumpMultipathShufti(ofstream &os, u32 len, const u8 *lo, const u8 *hi, #define PROGRAM_CASE(name) \ case ROSE_INSTR_##name: { \ os << " " << std::setw(4) << std::setfill('0') << (pc - pc_base) \ - << ": " #name " (" << (int)ROSE_INSTR_##name << ")" << endl; \ + << ": " #name "\n"; \ const auto *ri = (const struct ROSE_STRUCT_##name *)pc; #define PROGRAM_NEXT_INSTRUCTION \ @@ -1444,13 +1478,13 @@ void dumpProgram(ofstream &os, const RoseEngine *t, const char *pc) { #undef PROGRAM_NEXT_INSTRUCTION static -void dumpRoseLitPrograms(const RoseBuildImpl &build, const RoseEngine *t, - const string &filename) { +void dumpRoseLitPrograms(const vector &fragments, + const RoseEngine *t, const string &filename) { ofstream os(filename); // Collect all programs referenced by a literal fragment. vector programs; - for (const auto &frag : build.fragments) { + for (const auto &frag : fragments) { if (frag.lit_program_offset) { programs.push_back(frag.lit_program_offset); } @@ -2185,18 +2219,21 @@ void roseDumpComponents(const RoseEngine *t, bool dump_raw, } static -void roseDumpPrograms(const RoseBuildImpl &build, const RoseEngine *t, +void roseDumpPrograms(const vector &fragments, const RoseEngine *t, const string &base) { - dumpRoseLitPrograms(build, t, base + "/rose_lit_programs.txt"); + dumpRoseLitPrograms(fragments, t, base + "/rose_lit_programs.txt"); dumpRoseEodPrograms(t, base + "/rose_eod_programs.txt"); dumpRoseReportPrograms(t, base + "/rose_report_programs.txt"); dumpRoseAnchoredPrograms(t, base + "/rose_anchored_programs.txt"); dumpRoseDelayPrograms(t, base + "/rose_delay_programs.txt"); } -void dumpRose(const RoseBuildImpl &build, const RoseEngine *t) { +void dumpRose(const RoseBuildImpl &build, const vector &fragments, + const map &leftfix_queue_map, + const map &suffix_queue_map, + const RoseEngine *t) { const Grey &grey = build.cc.grey; - + if (!grey.dumpFlags) { return; } @@ -2218,16 +2255,14 @@ void dumpRose(const RoseBuildImpl &build, const RoseEngine *t) { fclose(f); roseDumpComponents(t, false, grey.dumpPath); - roseDumpPrograms(build, t, grey.dumpPath); + roseDumpPrograms(fragments, t, grey.dumpPath); // Graph. - dumpRoseGraph(build, t, "rose.dot"); + dumpRoseGraph(build, t, fragments, leftfix_queue_map, suffix_queue_map, + "rose.dot"); - // Literals. - ss.str(""); - ss.clear(); - ss << grey.dumpPath << "rose_literals.txt"; - dumpRoseLiterals(build, ss.str().c_str()); + // Literals + dumpRoseLiterals(build, fragments, grey); f = fopen((grey.dumpPath + "/rose_struct.txt").c_str(), "w"); roseDumpStructRaw(t, f); diff --git a/src/rose/rose_build_dump.h b/src/rose/rose_build_dump.h index 601f5914..d4c620a3 100644 --- a/src/rose/rose_build_dump.h +++ b/src/rose/rose_build_dump.h @@ -29,6 +29,9 @@ #ifndef ROSE_BUILD_DUMP_H #define ROSE_BUILD_DUMP_H +#include "ue2common.h" + +#include #include #include @@ -39,30 +42,40 @@ namespace ue2 { class RoseBuildImpl; struct Grey; struct hwlmLiteral; +struct LitFragment; +struct left_id; +struct suffix_id; #ifdef DUMP_SUPPORT // Dump the Rose graph in graphviz representation. -void dumpRoseGraph(const RoseBuildImpl &build, const RoseEngine *t, - const char *filename); +void dumpRoseGraph(const RoseBuildImpl &build, const char *filename); -void dumpRose(const RoseBuildImpl &build, const RoseEngine *t); +void dumpRose(const RoseBuildImpl &build, + const std::vector &fragments, + const std::map &leftfix_queue_map, + const std::map &suffix_queue_map, + const RoseEngine *t); void dumpMatcherLiterals(const std::vector &lits, const std::string &name, const Grey &grey); + #else static UNUSED -void dumpRoseGraph(const RoseBuildImpl &, const RoseEngine *, const char *) { +void dumpRoseGraph(const RoseBuildImpl &, const char *) { } static UNUSED -void dumpRose(const RoseBuildImpl &, const RoseEngine *) { +void dumpRose(const RoseBuildImpl &, const std::vector &, + const std::map &, const std::map &, + const RoseEngine *) { } static UNUSED void dumpMatcherLiterals(const std::vector &, const std::string &, const Grey &) { } + #endif } // namespace ue2 diff --git a/src/rose/rose_build_impl.h b/src/rose/rose_build_impl.h index cafd0505..b4821b2b 100644 --- a/src/rose/rose_build_impl.h +++ b/src/rose/rose_build_impl.h @@ -264,7 +264,6 @@ struct rose_literal_info { ue2::flat_set vertices; rose_group group_mask = 0; u32 undelayed_id = MO_INVALID_IDX; - u32 fragment_id = MO_INVALID_IDX; //!< ID corresponding to literal prog. bool squash_group = false; bool requires_benefits = false; }; @@ -437,15 +436,6 @@ private: std::set all_reports(const OutfixInfo &outfix); -struct LitFragment { - LitFragment(u32 fragment_id_in, rose_group groups_in) - : fragment_id(fragment_id_in), groups(groups_in) {} - u32 fragment_id; - rose_group groups; - u32 lit_program_offset = ROSE_INVALID_PROG_OFFSET; - u32 delay_program_offset = ROSE_INVALID_PROG_OFFSET; -}; - // Concrete impl class class RoseBuildImpl : public RoseBuild { public: @@ -576,19 +566,11 @@ public: u32 ematcher_region_size; /**< number of bytes the eod table runs over */ - /** \brief Mapping from leftfix to queue ID (used in dump code). */ - unordered_map leftfix_queue_map; - - /** \brief Mapping from suffix to queue ID (used in dump code). */ - unordered_map suffix_queue_map; - /** \brief Mapping from anchored literal ID to the original literal suffix * present when the literal was added to the literal matcher. Used for * overlap calculation in history assignment. */ std::map anchoredLitSuffix; - std::vector fragments; - unordered_set transient; unordered_map rose_squash_masks; diff --git a/src/rose/rose_build_matchers.cpp b/src/rose/rose_build_matchers.cpp index 5625437b..7f1467d7 100644 --- a/src/rose/rose_build_matchers.cpp +++ b/src/rose/rose_build_matchers.cpp @@ -350,9 +350,6 @@ void findMoreLiteralMasks(RoseBuildImpl &build) { const u32 id = e.first; const auto &lit = e.second; - // This pass takes place before fragment IDs are assigned to literals. - assert(build.literal_info.at(id).fragment_id == MO_INVALID_IDX); - if (lit.delay || build.isDelayed(id)) { continue; } @@ -673,6 +670,7 @@ struct MatcherProto { */ static MatcherProto makeMatcherProto(const RoseBuildImpl &build, + const vector &fragments, rose_literal_table table, bool delay_rebuild, size_t max_len, u32 max_offset = ROSE_BOUND_INF) { MatcherProto mp; @@ -682,92 +680,91 @@ MatcherProto makeMatcherProto(const RoseBuildImpl &build, assert(build.cc.streaming); } - for (const auto &e : build.literals.right) { - const u32 id = e.first; - if (build.literal_info.at(id).fragment_id == MO_INVALID_IDX) { - continue; - } + for (const auto &f : fragments) { + for (u32 id : f.lit_ids) { + const rose_literal_id &lit = build.literals.right.at(id); - if (e.second.delay) { - continue; /* delay id's are virtual-ish */ - } + if (lit.table != table) { + continue; /* wrong table */ + } - if (e.second.table != table) { - continue; /* wrong table */ - } + if (lit.delay) { + continue; /* delay id's are virtual-ish */ + } - assert(id < build.literal_info.size()); - const rose_literal_info &info = build.literal_info[id]; - /* Note: requires_benefits are handled in the literal entries */ - const ue2_literal &lit = e.second.s; + assert(id < build.literal_info.size()); + const auto &info = build.literal_info.at(id); - DEBUG_PRINTF("lit='%s' (len %zu)\n", escapeString(lit).c_str(), - lit.length()); + /* Note: requires_benefits are handled in the literal entries */ + const ue2_literal &s = lit.s; - // When building the delay rebuild table, we only want to include - // literals that have delayed variants. - if (delay_rebuild && info.delayed_ids.empty()) { - DEBUG_PRINTF("not needed for delay rebuild\n"); - continue; - } + DEBUG_PRINTF("lit='%s' (len %zu)\n", escapeString(s).c_str(), + s.length()); - if (max_offset != ROSE_BOUND_INF) { - u64a min_report = literalMinReportOffset(build, e.second, info); - if (min_report > max_offset) { - DEBUG_PRINTF("min report offset=%llu exceeds max_offset=%u\n", - min_report, max_offset); + // When building the delay rebuild table, we only want to include + // literals that have delayed variants. + if (delay_rebuild && info.delayed_ids.empty()) { + DEBUG_PRINTF("not needed for delay rebuild\n"); continue; } + + if (max_offset != ROSE_BOUND_INF) { + u64a min_report = literalMinReportOffset(build, lit, info); + if (min_report > max_offset) { + DEBUG_PRINTF("min report offset=%llu exceeds " + "max_offset=%u\n", min_report, max_offset); + continue; + } + } + + const vector &msk = lit.msk; + const vector &cmp = lit.cmp; + bool noruns = isNoRunsLiteral(build, id, info, max_len); + + size_t lit_hist_len = 0; + if (build.cc.streaming) { + lit_hist_len = max(msk.size(), min(s.length(), max_len)); + lit_hist_len = lit_hist_len ? lit_hist_len - 1 : 0; + } + DEBUG_PRINTF("lit requires %zu bytes of history\n", lit_hist_len); + assert(lit_hist_len <= build.cc.grey.maxHistoryAvailable); + + auto lit_final = s; // copy + + if (lit_final.length() > ROSE_SHORT_LITERAL_LEN_MAX) { + DEBUG_PRINTF("truncating to tail of length %zu\n", + size_t{ROSE_SHORT_LITERAL_LEN_MAX}); + lit_final.erase(0, lit_final.length() + - ROSE_SHORT_LITERAL_LEN_MAX); + // We shouldn't have set a threshold below 8 chars. + assert(msk.size() <= ROSE_SHORT_LITERAL_LEN_MAX); + assert(!noruns); + } + + const auto &s_final = lit_final.get_string(); + bool nocase = lit_final.any_nocase(); + + DEBUG_PRINTF("id=%u, s='%s', nocase=%d, noruns=%d, msk=%s, " + "cmp=%s\n", f.fragment_id, + escapeString(s_final).c_str(), (int)nocase, noruns, + dumpMask(msk).c_str(), dumpMask(cmp).c_str()); + + if (!maskIsConsistent(s_final, nocase, msk, cmp)) { + DEBUG_PRINTF("msk/cmp for literal can't match, skipping\n"); + continue; + } + + mp.accel_lits.emplace_back(s.get_string(), s.any_nocase(), msk, cmp, + info.group_mask); + mp.history_required = max(mp.history_required, lit_hist_len); + + u32 prog_offset = delay_rebuild ? f.delay_program_offset + : f.lit_program_offset; + const auto &groups = f.groups; + + mp.lits.emplace_back(move(s_final), nocase, noruns, prog_offset, + groups, msk, cmp); } - - const vector &msk = e.second.msk; - const vector &cmp = e.second.cmp; - bool noruns = isNoRunsLiteral(build, id, info, max_len); - - size_t lit_hist_len = 0; - if (build.cc.streaming) { - lit_hist_len = max(msk.size(), min(lit.length(), max_len)); - lit_hist_len = lit_hist_len ? lit_hist_len - 1 : 0; - } - DEBUG_PRINTF("lit requires %zu bytes of history\n", lit_hist_len); - assert(lit_hist_len <= build.cc.grey.maxHistoryAvailable); - - auto lit_final = lit; // copy - - if (lit_final.length() > ROSE_SHORT_LITERAL_LEN_MAX) { - DEBUG_PRINTF("truncating to tail of length %zu\n", - size_t{ROSE_SHORT_LITERAL_LEN_MAX}); - lit_final.erase(0, lit_final.length() - ROSE_SHORT_LITERAL_LEN_MAX); - // We shouldn't have set a threshold below 8 chars. - assert(msk.size() <= ROSE_SHORT_LITERAL_LEN_MAX); - assert(!noruns); - } - - const auto &s = lit_final.get_string(); - bool nocase = lit_final.any_nocase(); - - DEBUG_PRINTF("id=%u, s='%s', nocase=%d, noruns=%d, msk=%s, " - "cmp=%s\n", - info.fragment_id, escapeString(s).c_str(), (int)nocase, - noruns, dumpMask(msk).c_str(), dumpMask(cmp).c_str()); - - if (!maskIsConsistent(s, nocase, msk, cmp)) { - DEBUG_PRINTF("msk/cmp for literal can't match, skipping\n"); - continue; - } - - mp.accel_lits.emplace_back(lit.get_string(), lit.any_nocase(), msk, cmp, - info.group_mask); - mp.history_required = max(mp.history_required, lit_hist_len); - - assert(info.fragment_id < build.fragments.size()); - const auto &frag = build.fragments.at(info.fragment_id); - u32 prog_offset = - delay_rebuild ? frag.delay_program_offset : frag.lit_program_offset; - const auto &groups = frag.groups; - - mp.lits.emplace_back(move(s), nocase, noruns, prog_offset, groups, msk, - cmp); } sort_and_unique(mp.lits); @@ -809,14 +806,15 @@ void buildAccel(const RoseBuildImpl &build, const MatcherProto &mp, } aligned_unique_ptr -buildFloatingMatcher(const RoseBuildImpl &build, size_t longLitLengthThreshold, - rose_group *fgroups, size_t *fsize, - size_t *historyRequired) { +buildFloatingMatcher(const RoseBuildImpl &build, + const vector &fragments, + size_t longLitLengthThreshold, rose_group *fgroups, + size_t *fsize, size_t *historyRequired) { *fsize = 0; *fgroups = 0; - auto mp = - makeMatcherProto(build, ROSE_FLOATING, false, longLitLengthThreshold); + auto mp = makeMatcherProto(build, fragments, ROSE_FLOATING, false, + longLitLengthThreshold); if (mp.lits.empty()) { DEBUG_PRINTF("empty floating matcher\n"); return nullptr; @@ -847,6 +845,7 @@ buildFloatingMatcher(const RoseBuildImpl &build, size_t longLitLengthThreshold, } aligned_unique_ptr buildDelayRebuildMatcher(const RoseBuildImpl &build, + const vector &fragments, size_t longLitLengthThreshold, size_t *drsize) { *drsize = 0; @@ -856,8 +855,8 @@ aligned_unique_ptr buildDelayRebuildMatcher(const RoseBuildImpl &build, return nullptr; } - auto mp = - makeMatcherProto(build, ROSE_FLOATING, true, longLitLengthThreshold); + auto mp = makeMatcherProto(build, fragments, ROSE_FLOATING, true, + longLitLengthThreshold); if (mp.lits.empty()) { DEBUG_PRINTF("empty delay rebuild matcher\n"); return nullptr; @@ -877,8 +876,9 @@ aligned_unique_ptr buildDelayRebuildMatcher(const RoseBuildImpl &build, return hwlm; } -aligned_unique_ptr buildSmallBlockMatcher(const RoseBuildImpl &build, - size_t *sbsize) { +aligned_unique_ptr +buildSmallBlockMatcher(const RoseBuildImpl &build, + const vector &fragments, size_t *sbsize) { *sbsize = 0; if (build.cc.streaming) { @@ -893,7 +893,7 @@ aligned_unique_ptr buildSmallBlockMatcher(const RoseBuildImpl &build, return nullptr; } - auto mp = makeMatcherProto(build, ROSE_FLOATING, false, + auto mp = makeMatcherProto(build, fragments, ROSE_FLOATING, false, ROSE_SMALL_BLOCK_LEN, ROSE_SMALL_BLOCK_LEN); if (mp.lits.empty()) { DEBUG_PRINTF("no floating table\n"); @@ -903,9 +903,10 @@ aligned_unique_ptr buildSmallBlockMatcher(const RoseBuildImpl &build, return nullptr; } - auto mp_anchored = - makeMatcherProto(build, ROSE_ANCHORED_SMALL_BLOCK, false, - ROSE_SMALL_BLOCK_LEN, ROSE_SMALL_BLOCK_LEN); + auto mp_anchored = makeMatcherProto(build, fragments, + ROSE_ANCHORED_SMALL_BLOCK, false, + ROSE_SMALL_BLOCK_LEN, + ROSE_SMALL_BLOCK_LEN); if (mp_anchored.lits.empty()) { DEBUG_PRINTF("no small-block anchored literals\n"); return nullptr; @@ -937,11 +938,12 @@ aligned_unique_ptr buildSmallBlockMatcher(const RoseBuildImpl &build, return hwlm; } -aligned_unique_ptr buildEodAnchoredMatcher(const RoseBuildImpl &build, - size_t *esize) { +aligned_unique_ptr +buildEodAnchoredMatcher(const RoseBuildImpl &build, + const vector &fragments, size_t *esize) { *esize = 0; - auto mp = makeMatcherProto(build, ROSE_EOD_ANCHORED, false, + auto mp = makeMatcherProto(build, fragments, ROSE_EOD_ANCHORED, false, build.ematcher_region_size); if (mp.lits.empty()) { diff --git a/src/rose/rose_build_matchers.h b/src/rose/rose_build_matchers.h index cb56037d..494a3aeb 100644 --- a/src/rose/rose_build_matchers.h +++ b/src/rose/rose_build_matchers.h @@ -36,25 +36,45 @@ #include "rose_build_impl.h" +#include + struct Grey; struct HWLM; namespace ue2 { +struct LitFragment { + LitFragment(u32 fragment_id_in, rose_group groups_in, u32 lit_id) + : fragment_id(fragment_id_in), groups(groups_in), lit_ids({lit_id}) {} + LitFragment(u32 fragment_id_in, rose_group groups_in, + std::vector lit_ids_in) + : fragment_id(fragment_id_in), groups(groups_in), + lit_ids(std::move(lit_ids_in)) {} + u32 fragment_id; + rose_group groups; + std::vector lit_ids; + u32 lit_program_offset = ROSE_INVALID_PROG_OFFSET; + u32 delay_program_offset = ROSE_INVALID_PROG_OFFSET; +}; + aligned_unique_ptr buildFloatingMatcher(const RoseBuildImpl &build, + const std::vector &fragments, size_t longLitLengthThreshold, rose_group *fgroups, size_t *fsize, size_t *historyRequired); aligned_unique_ptr buildDelayRebuildMatcher(const RoseBuildImpl &build, + const std::vector &fragments, size_t longLitLengthThreshold, size_t *drsize); aligned_unique_ptr buildSmallBlockMatcher(const RoseBuildImpl &build, + const std::vector &fragments, size_t *sbsize); aligned_unique_ptr buildEodAnchoredMatcher(const RoseBuildImpl &build, + const std::vector &fragments, size_t *esize); void findMoreLiteralMasks(RoseBuildImpl &build); diff --git a/src/rose/rose_build_program.cpp b/src/rose/rose_build_program.cpp index 9e030e8e..2fb76c77 100644 --- a/src/rose/rose_build_program.cpp +++ b/src/rose/rose_build_program.cpp @@ -450,8 +450,7 @@ void RoseInstrSparseIterBegin::write(void *dest, RoseEngineBlob &blob, jump_offsets.push_back(offset_map.at(jump.second)); } - vector iter; - mmbBuildSparseIterator(iter, keys, num_keys); + auto iter = mmbBuildSparseIterator(keys, num_keys); assert(!iter.empty()); inst->iter_offset = blob.add_iterator(iter); inst->jump_table = blob.add(jump_offsets.begin(), jump_offsets.end()); @@ -485,8 +484,7 @@ void RoseInstrSparseIterAny::write(void *dest, RoseEngineBlob &blob, inst->fail_jump = calc_jump(offset_map, this, target); // Write the multibit sparse iterator. - vector iter; - mmbBuildSparseIterator(iter, keys, num_keys); + auto iter = mmbBuildSparseIterator(keys, num_keys); assert(!iter.empty()); inst->iter_offset = blob.add_iterator(iter); } diff --git a/src/util/multibit_build.cpp b/src/util/multibit_build.cpp index c726bdf9..ad6a0d6a 100644 --- a/src/util/multibit_build.cpp +++ b/src/util/multibit_build.cpp @@ -155,9 +155,9 @@ void bfs(vector &out, const TreeNode &tree) { /** \brief Construct a sparse iterator over the values in \a bits for a * multibit of size \a total_bits. */ -void mmbBuildSparseIterator(vector &out, - const vector &bits, u32 total_bits) { - assert(out.empty()); +vector mmbBuildSparseIterator(const vector &bits, + u32 total_bits) { + vector out; assert(!bits.empty()); assert(total_bits > 0); assert(total_bits <= MMB_MAX_BITS); @@ -186,6 +186,7 @@ void mmbBuildSparseIterator(vector &out, #endif DEBUG_PRINTF("iter has %zu records\n", out.size()); + return out; } template diff --git a/src/util/multibit_build.h b/src/util/multibit_build.h index 951f1fb4..2d7b5fc2 100644 --- a/src/util/multibit_build.h +++ b/src/util/multibit_build.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2015-2016, Intel Corporation + * Copyright (c) 2015-2017, Intel Corporation * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: @@ -61,8 +61,8 @@ u32 mmbit_size(u32 total_bits); /** \brief Construct a sparse iterator over the values in \a bits for a * multibit of size \a total_bits. */ -void mmbBuildSparseIterator(std::vector &out, - const std::vector &bits, u32 total_bits); +std::vector +mmbBuildSparseIterator(const std::vector &bits, u32 total_bits); struct scatter_plan_raw; diff --git a/unit/internal/multi_bit.cpp b/unit/internal/multi_bit.cpp index 30dce493..2b0c7c79 100644 --- a/unit/internal/multi_bit.cpp +++ b/unit/internal/multi_bit.cpp @@ -782,7 +782,6 @@ TEST_P(MultiBitTest, InitRangePlanChunked) { TEST(MultiBit, SparseIteratorBegin1) { const u32 test_size = 100; - vector it; vector bits; bits.push_back(1); @@ -791,7 +790,7 @@ TEST(MultiBit, SparseIteratorBegin1) { bits.push_back(35); bits.push_back(68); - mmbBuildSparseIterator(it, bits, test_size); + auto it = mmbBuildSparseIterator(bits, test_size); //ASSERT_EQ(4U, it.size()); // Trivial initial test: all bits in 'bits' are on, all others are off @@ -820,7 +819,6 @@ TEST(MultiBit, SparseIteratorBegin1) { TEST(MultiBit, SparseIteratorBegin2) { const u32 test_size = 40000; - vector it; vector bits; bits.push_back(1); @@ -830,7 +828,7 @@ TEST(MultiBit, SparseIteratorBegin2) { bits.push_back(8920); bits.push_back(37000); - mmbBuildSparseIterator(it, bits, test_size); + auto it = mmbBuildSparseIterator(bits, test_size); //ASSERT_EQ(12U, it.size()); // Trivial initial test: all bits in 'bits' are on, all others are off @@ -859,7 +857,6 @@ TEST(MultiBit, SparseIteratorBegin2) { TEST(MultiBit, SparseIteratorNext1) { const u32 test_size = 100; - vector it; vector bits; bits.push_back(1); @@ -868,7 +865,7 @@ TEST(MultiBit, SparseIteratorNext1) { bits.push_back(35); bits.push_back(68); - mmbBuildSparseIterator(it, bits, test_size); + auto it = mmbBuildSparseIterator(bits, test_size); // Trivial initial test: all bits in 'bits' are on, all others are off mmbit_holder ba(test_size); @@ -924,7 +921,6 @@ TEST(MultiBit, SparseIteratorNext1) { TEST(MultiBit, SparseIteratorNext2) { const u32 test_size = 40000; - vector it; vector bits; bits.push_back(1); @@ -939,7 +935,7 @@ TEST(MultiBit, SparseIteratorNext2) { bits.push_back(37000); bits.push_back(39999); - mmbBuildSparseIterator(it, bits, test_size); + auto it = mmbBuildSparseIterator(bits, test_size); // Trivial initial test: all bits in 'bits' are on, all others are off mmbit_holder ba(test_size); @@ -995,7 +991,6 @@ TEST(MultiBit, SparseIteratorNext2) { TEST(MultiBit, SparseIteratorNextSmall) { const u32 test_size = 15; - vector it; vector bits; bits.push_back(1); @@ -1005,7 +1000,7 @@ TEST(MultiBit, SparseIteratorNextSmall) { bits.push_back(12); bits.push_back(14); - mmbBuildSparseIterator(it, bits, test_size); + auto it = mmbBuildSparseIterator(bits, test_size); // Trivial initial test: all bits in 'bits' are on, all others are off mmbit_holder ba(test_size); @@ -1064,13 +1059,12 @@ TEST_P(MultiBitTest, SparseIteratorBeginAll) { ASSERT_TRUE(ba != nullptr); // Put all our bits into the sparse iterator. - vector it; vector bits; bits.reserve(test_size / stride); for (u64a i = 0; i < test_size; i += stride) { bits.push_back(i); } - mmbBuildSparseIterator(it, bits, test_size); + auto it = mmbBuildSparseIterator(bits, test_size); // Switch all bits on in state. mmbit_clear(ba, test_size); @@ -1104,12 +1098,11 @@ TEST_P(MultiBitTest, SparseIteratorBeginThirds) { } // Put all our bits into the sparse iterator - vector it; vector bits(test_size); for (u32 i = 0; i != test_size; i++) { bits[i] = i; } - mmbBuildSparseIterator(it, bits, test_size); + auto it = mmbBuildSparseIterator(bits, test_size); // Switch every third bits on in state mmbit_clear(ba, test_size); @@ -1139,13 +1132,12 @@ TEST_P(MultiBitTest, SparseIteratorNextAll) { ASSERT_TRUE(ba != nullptr); // Put all our bits into the sparse iterator. - vector it; vector bits; bits.reserve(test_size / stride); for (u64a i = 0; i < test_size; i += stride) { bits.push_back(i); } - mmbBuildSparseIterator(it, bits, test_size); + auto it = mmbBuildSparseIterator(bits, test_size); // Switch all bits on in state mmbit_clear(ba, test_size); @@ -1182,14 +1174,13 @@ TEST_P(MultiBitTest, SparseIteratorNextExactStrided) { // Put all our bits into the sparse iterator and switch them on in the // state. mmbit_clear(ba, test_size); - vector it; vector bits; bits.reserve(test_size / stride); for (u64a i = 0; i < test_size; i += stride) { bits.push_back(i); mmbit_set(ba, test_size, i); } - mmbBuildSparseIterator(it, bits, test_size); + auto it = mmbBuildSparseIterator(bits, test_size); // Iterate over all bits. vector state(mmbit_sparse_iter_state_size(test_size)); @@ -1214,13 +1205,12 @@ TEST_P(MultiBitTest, SparseIteratorNextNone) { ASSERT_TRUE(ba != nullptr); // Put all our bits into the sparse iterator. - vector it; vector bits; bits.reserve(test_size / stride); for (u64a i = 0; i < test_size; i += stride) { bits.push_back(i); } - mmbBuildSparseIterator(it, bits, test_size); + auto it = mmbBuildSparseIterator(bits, test_size); // Switch only the first bit on mmbit_clear(ba, test_size); @@ -1243,13 +1233,12 @@ TEST_P(MultiBitTest, SparseIteratorUnsetAll) { ASSERT_TRUE(ba != nullptr); // Put all our bits into the sparse iterator - vector it; vector bits; bits.reserve(test_size / stride); for (u64a i = 0; i < test_size; i += stride) { bits.push_back(i); } - mmbBuildSparseIterator(it, bits, test_size); + auto it = mmbBuildSparseIterator(bits, test_size); // Switch all bits on mmbit_clear(ba, test_size); @@ -1283,9 +1272,8 @@ TEST_P(MultiBitTest, SparseIteratorUnsetHalves) { odd.push_back(i); } - vector it_even, it_odd; - mmbBuildSparseIterator(it_even, even, test_size); - mmbBuildSparseIterator(it_odd, odd, test_size); + auto it_even = mmbBuildSparseIterator(even, test_size); + auto it_odd = mmbBuildSparseIterator(odd, test_size); // Switch all bits on mmbit_clear(ba, test_size);