]> git.ipfire.org Git - thirdparty/vectorscan.git/commitdiff
limex: rework accept handling
authorJustin Viiret <justin.viiret@intel.com>
Tue, 30 Aug 2016 04:24:23 +0000 (14:24 +1000)
committerMatthew Barr <matthew.barr@intel.com>
Fri, 28 Oct 2016 03:46:19 +0000 (14:46 +1100)
Rather that iterating over NFAAccept structures and testing individual
bits in the state structure, iterate over the state vector and index
into accept structures.

Adds report list support to this path, unified with the report lists
used for exception handling.

src/nfa/limex_common_impl.h
src/nfa/limex_compile.cpp
src/nfa/limex_dump.cpp
src/nfa/limex_exceptional.h
src/nfa/limex_internal.h
src/nfa/limex_native.c
src/nfa/limex_runtime.h
src/nfa/limex_runtime_impl.h

index ae0d1da88bdee4e1f74213ec81444ba4252b44d4..5bd5187b10bdbd7845c14d5986a78960d8709d0c 100644 (file)
@@ -46,6 +46,7 @@
 #define INITIAL_FN          JOIN(moNfaInitial, SIZE)
 #define TOP_FN              JOIN(moNfaTop, SIZE)
 #define TOPN_FN             JOIN(moNfaTopN, SIZE)
+#define PROCESS_ACCEPTS_IMPL_FN  JOIN(moProcessAcceptsImpl, SIZE)
 #define PROCESS_ACCEPTS_FN  JOIN(moProcessAccepts, SIZE)
 #define PROCESS_ACCEPTS_NOSQUASH_FN  JOIN(moProcessAcceptsNoSquash, SIZE)
 #define CONTEXT_T           JOIN(NFAContext, SIZE)
 #define SQUASH_UNTUG_BR_FN  JOIN(lazyTug, SIZE)
 #define GET_NFA_REPEAT_INFO_FN JOIN(getNfaRepeatInfo, SIZE)
 
+#if defined(ARCH_64_BIT) && (SIZE >= 64)
+#define CHUNK_T u64a
+#define FIND_AND_CLEAR_FN findAndClearLSB_64
+#define POPCOUNT_FN popcount64
+#define RANK_IN_MASK_FN rank_in_mask64
+#else
+#define CHUNK_T u32
+#define FIND_AND_CLEAR_FN findAndClearLSB_32
+#define POPCOUNT_FN popcount32
+#define RANK_IN_MASK_FN rank_in_mask32
+#endif
+
+#define NUM_STATE_CHUNKS (sizeof(STATE_T) / sizeof(CHUNK_T))
+
 static really_inline
 void SQUASH_UNTUG_BR_FN(const IMPL_NFA_T *limex,
                         const union RepeatControl *repeat_ctrl,
@@ -98,63 +113,84 @@ void SQUASH_UNTUG_BR_FN(const IMPL_NFA_T *limex,
     }
 }
 
-static never_inline
-char PROCESS_ACCEPTS_FN(const IMPL_NFA_T *limex, STATE_T *s,
-                        const struct NFAAccept *acceptTable, u32 acceptCount,
-                        u64a offset, NfaCallback callback, void *context) {
+static really_inline
+char PROCESS_ACCEPTS_IMPL_FN(const IMPL_NFA_T *limex, const STATE_T *s,
+                             STATE_T *squash, const ENG_STATE_T *squashMasks,
+                             const STATE_T *acceptMask,
+                             const struct NFAAccept *acceptTable, u64a offset,
+                             NfaCallback callback, void *context) {
     assert(s);
     assert(limex);
     assert(callback);
-    assert(acceptCount);
-
-    // We have squash masks we might have to apply after firing reports.
-    STATE_T squash = ONES_STATE;
-    const ENG_STATE_T *squashMasks = (const ENG_STATE_T *)
-        ((const char *)limex + limex->squashOffset);
 
-    for (u32 i = 0; i < acceptCount; i++) {
-        const struct NFAAccept *a = &acceptTable[i];
-        if (TESTBIT_STATE(*s, a->state)) {
-            DEBUG_PRINTF("state %u is on, firing report id=%u, offset=%llu\n",
-                         a->state, a->externalId, offset);
-            int rv = callback(0, offset, a->externalId, context);
+    const STATE_T accept_mask = *acceptMask;
+    STATE_T accepts = AND_STATE(*s, accept_mask);
+
+    // Caller must ensure that we have at least one accept state on.
+    assert(ISNONZERO_STATE(accepts));
+
+    CHUNK_T chunks[NUM_STATE_CHUNKS];
+    memcpy(chunks, &accepts, sizeof(accepts));
+
+    CHUNK_T mask_chunks[NUM_STATE_CHUNKS];
+    memcpy(mask_chunks, &accept_mask, sizeof(accept_mask));
+
+    u32 base_index = 0; // Cumulative sum of mask popcount up to current chunk.
+    for (u32 i = 0; i < NUM_STATE_CHUNKS; i++) {
+        CHUNK_T chunk = chunks[i];
+        while (chunk != 0) {
+            u32 bit = FIND_AND_CLEAR_FN(&chunk);
+            u32 local_idx = RANK_IN_MASK_FN(mask_chunks[i], bit);
+            u32 idx = local_idx + base_index;
+            const struct NFAAccept *a = &acceptTable[idx];
+            DEBUG_PRINTF("state %u: firing report list=%u, offset=%llu\n",
+                         bit + i * (u32)sizeof(chunk) * 8, a->reports, offset);
+            int rv = limexRunAccept((const char *)limex, a, callback, context,
+                                    offset);
             if (unlikely(rv == MO_HALT_MATCHING)) {
                 return 1;
             }
-            if (a->squash != MO_INVALID_IDX) {
+            if (squash != NULL && a->squash != MO_INVALID_IDX) {
+                assert(squashMasks);
                 assert(a->squash < limex->squashCount);
                 const ENG_STATE_T *sq = &squashMasks[a->squash];
                 DEBUG_PRINTF("squash mask %u @ %p\n", a->squash, sq);
-                squash = AND_STATE(squash, LOAD_FROM_ENG(sq));
+                *squash = AND_STATE(*squash, LOAD_FROM_ENG(sq));
             }
         }
+        base_index += POPCOUNT_FN(mask_chunks[i]);
     }
 
-    *s = AND_STATE(*s, squash);
     return 0;
 }
 
 static never_inline
-char PROCESS_ACCEPTS_NOSQUASH_FN(const STATE_T *s,
+char PROCESS_ACCEPTS_FN(const IMPL_NFA_T *limex, STATE_T *s,
+                        const STATE_T *acceptMask,
+                        const struct NFAAccept *acceptTable, u64a offset,
+                        NfaCallback callback, void *context) {
+    // We have squash masks we might have to apply after firing reports.
+    STATE_T squash = ONES_STATE;
+    const ENG_STATE_T *squashMasks = (const ENG_STATE_T *)
+        ((const char *)limex + limex->squashOffset);
+
+    return PROCESS_ACCEPTS_IMPL_FN(limex, s, &squash, squashMasks, acceptMask,
+                                   acceptTable, offset, callback, context);
+
+    *s = AND_STATE(*s, squash);
+}
+
+static never_inline
+char PROCESS_ACCEPTS_NOSQUASH_FN(const IMPL_NFA_T *limex, const STATE_T *s,
+                                 const STATE_T *acceptMask,
                                  const struct NFAAccept *acceptTable,
-                                 u32 acceptCount, u64a offset,
-                                 NfaCallback callback, void *context) {
-    assert(s);
-    assert(callback);
-    assert(acceptCount);
-
-    for (u32 i = 0; i < acceptCount; i++) {
-        const struct NFAAccept *a = &acceptTable[i];
-        if (TESTBIT_STATE(*s, a->state)) {
-            DEBUG_PRINTF("state %u is on, firing report id=%u, offset=%llu\n",
-                         a->state, a->externalId, offset);
-            int rv = callback(0, offset, a->externalId, context);
-            if (unlikely(rv == MO_HALT_MATCHING)) {
-                return 1;
-            }
-        }
-    }
-    return 0;
+                                 u64a offset, NfaCallback callback,
+                                 void *context) {
+    STATE_T *squash = NULL;
+    const ENG_STATE_T *squashMasks = NULL;
+
+    return PROCESS_ACCEPTS_IMPL_FN(limex, s, squash, squashMasks, acceptMask,
+                                   acceptTable, offset, callback, context);
 }
 
 // Run EOD accepts. Note that repeat_ctrl and repeat_state may be NULL if this
@@ -179,8 +215,8 @@ char TESTEOD_FN(const IMPL_NFA_T *limex, const STATE_T *s,
 
     if (unlikely(ISNONZERO_STATE(foundAccepts))) {
         const struct NFAAccept *acceptEodTable = getAcceptEodTable(limex);
-        if (PROCESS_ACCEPTS_NOSQUASH_FN(&foundAccepts, acceptEodTable,
-                                        limex->acceptEodCount, offset, callback,
+        if (PROCESS_ACCEPTS_NOSQUASH_FN(limex, &foundAccepts, &acceptEodMask,
+                                        acceptEodTable, offset, callback,
                                         context)) {
             return MO_HALT_MATCHING;
         }
@@ -206,8 +242,8 @@ char REPORTCURRENT_FN(const IMPL_NFA_T *limex, const struct mq *q) {
         const struct NFAAccept *acceptTable = getAcceptTable(limex);
         u64a offset = q_cur_offset(q);
 
-        if (PROCESS_ACCEPTS_NOSQUASH_FN(&foundAccepts, acceptTable,
-                                        limex->acceptCount, offset, q->cb,
+        if (PROCESS_ACCEPTS_NOSQUASH_FN(limex, &foundAccepts, &acceptMask,
+                                        acceptTable, offset, q->cb,
                                         q->context)) {
             return MO_HALT_MATCHING;
         }
@@ -307,37 +343,45 @@ char LIMEX_INACCEPT_FN(const IMPL_NFA_T *limex, STATE_T state,
                        u64a offset, ReportID report) {
     assert(limex);
 
-    const STATE_T acceptMask = LOAD_FROM_ENG(&limex->accept);
-    STATE_T accstate = AND_STATE(state, acceptMask);
+    const STATE_T accept_mask = LOAD_FROM_ENG(&limex->accept);
+    STATE_T accepts = AND_STATE(state, accept_mask);
 
     // Are we in an accept state?
-    if (ISZERO_STATE(accstate)) {
+    if (ISZERO_STATE(accepts)) {
         DEBUG_PRINTF("no accept states are on\n");
         return 0;
     }
 
-    SQUASH_UNTUG_BR_FN(limex, repeat_ctrl, repeat_state, offset, &accstate);
+    SQUASH_UNTUG_BR_FN(limex, repeat_ctrl, repeat_state, offset, &accepts);
 
     DEBUG_PRINTF("looking for report %u\n", report);
 
-#ifdef DEBUG
-    DEBUG_PRINTF("accept states that are on: ");
-    for (u32 i = 0; i < sizeof(STATE_T) * 8; i++) {
-        if (TESTBIT_STATE(accstate, i)) printf("%u ", i);
-    }
-    printf("\n");
-#endif
-
-    // Does one of our states match the given report ID?
     const struct NFAAccept *acceptTable = getAcceptTable(limex);
-    for (u32 i = 0; i < limex->acceptCount; i++) {
-        const struct NFAAccept *a = &acceptTable[i];
-        DEBUG_PRINTF("checking idx=%u, externalId=%u\n", a->state,
-                     a->externalId);
-        if (a->externalId == report && TESTBIT_STATE(accstate, a->state)) {
-            DEBUG_PRINTF("report is on!\n");
-            return 1;
+
+    CHUNK_T chunks[NUM_STATE_CHUNKS];
+    memcpy(chunks, &accepts, sizeof(accepts));
+
+    CHUNK_T mask_chunks[NUM_STATE_CHUNKS];
+    memcpy(mask_chunks, &accept_mask, sizeof(accept_mask));
+
+    u32 base_index = 0; // Cumulative sum of mask popcount up to current chunk.
+    for (u32 i = 0; i < NUM_STATE_CHUNKS; i++) {
+        CHUNK_T chunk = chunks[i];
+        while (chunk != 0) {
+            u32 bit = FIND_AND_CLEAR_FN(&chunk);
+            u32 local_idx = RANK_IN_MASK_FN(mask_chunks[i], bit);
+            u32 idx = local_idx + base_index;
+            assert(idx < limex->acceptCount);
+            const struct NFAAccept *a = &acceptTable[idx];
+            DEBUG_PRINTF("state %u is on, report list at %u\n",
+                         bit + i * (u32)sizeof(chunk) * 8, a->reports);
+
+            if (limexAcceptHasReport((const char *)limex, a, report)) {
+                DEBUG_PRINTF("report %u is on\n", report);
+                return 1;
+            }
         }
+        base_index += POPCOUNT_FN(mask_chunks[i]);
     }
 
     return 0;
@@ -381,7 +425,14 @@ char LIMEX_INANYACCEPT_FN(const IMPL_NFA_T *limex, STATE_T state,
 #undef TESTBIT_STATE
 #undef ISNONZERO_STATE
 #undef ISZERO_STATE
+#undef PROCESS_ACCEPTS_IMPL_FN
 #undef PROCESS_ACCEPTS_FN
 #undef PROCESS_ACCEPTS_NOSQUASH_FN
 #undef SQUASH_UNTUG_BR_FN
 #undef GET_NFA_REPEAT_INFO_FN
+
+#undef CHUNK_T
+#undef FIND_AND_CLEAR_FN
+#undef POPCOUNT_FN
+#undef RANK_IN_MASK_FN
+#undef NUM_STATE_CHUNKS
index 78b9729f3a63ac13718234d93532916b9be50a9c..89eaf10afa5986d8534e469b776e81eb874a9315 100644 (file)
@@ -992,14 +992,105 @@ void buildAccel(const build_info &args, NFAStateSet &accelMask,
 }
 
 static
-void buildAccepts(const build_info &args, NFAStateSet &acceptMask,
-                  NFAStateSet &acceptEodMask, vector<NFAAccept> &accepts,
-                  vector<NFAAccept> &acceptsEod, vector<NFAStateSet> &squash) {
+u32 addSquashMask(const build_info &args, const NFAVertex &v,
+                  vector<NFAStateSet> &squash) {
+    auto sit = args.reportSquashMap.find(v);
+    if (sit == args.reportSquashMap.end()) {
+        return MO_INVALID_IDX;
+    }
+
+    // This state has a squash mask. Paw through the existing vector to
+    // see if we've already seen it, otherwise add a new one.
+    auto it = find(squash.begin(), squash.end(), sit->second);
+    if (it != squash.end()) {
+        return verify_u32(distance(squash.begin(), it));
+    }
+    u32 idx = verify_u32(squash.size());
+    squash.push_back(sit->second);
+    return idx;
+}
+
+static
+u32 addReports(const flat_set<ReportID> &r, vector<ReportID> &reports,
+               unordered_map<vector<ReportID>, u32> &reportListCache) {
+    assert(!r.empty());
+
+    vector<ReportID> my_reports(begin(r), end(r));
+    my_reports.push_back(MO_INVALID_IDX); // sentinel
+
+    auto cache_it = reportListCache.find(my_reports);
+    if (cache_it != end(reportListCache)) {
+        u32 offset = cache_it->second;
+        DEBUG_PRINTF("reusing cached report list at %u\n", offset);
+        return offset;
+    }
+
+    auto it = search(begin(reports), end(reports), begin(my_reports),
+                     end(my_reports));
+    if (it != end(reports)) {
+        u32 offset = verify_u32(distance(begin(reports), it));
+        DEBUG_PRINTF("reusing found report list at %u\n", offset);
+        return offset;
+    }
+
+    u32 offset = verify_u32(reports.size());
+    insert(&reports, reports.end(), my_reports);
+    reportListCache.emplace(move(my_reports), offset);
+    return offset;
+}
+
+static
+void buildAcceptsList(const build_info &args,
+                      unordered_map<vector<ReportID>, u32> &reports_cache,
+                      vector<NFAVertex> &verts, vector<NFAAccept> &accepts,
+                      vector<ReportID> &reports, vector<NFAStateSet> &squash) {
+    if (verts.empty()) {
+        return;
+    }
+
+    DEBUG_PRINTF("building accept lists for %zu states\n", verts.size());
+
+    auto cmp_state_id = [&args](NFAVertex a, NFAVertex b) {
+        u32 a_state = args.state_ids.at(a);
+        u32 b_state = args.state_ids.at(b);
+        assert(a_state != b_state || a == b);
+        return a_state < b_state;
+    };
+
+    sort(begin(verts), end(verts), cmp_state_id);
+
+    const NGHolder &h = args.h;
+    for (const auto &v : verts) {
+        DEBUG_PRINTF("state=%u, reports: [%s]\n", args.state_ids.at(v),
+                     as_string_list(h[v].reports).c_str());
+        NFAAccept a;
+        memset(&a, 0, sizeof(a));
+        assert(!h[v].reports.empty());
+        if (h[v].reports.size() == 1) {
+            a.single_report = 1;
+            a.reports = *h[v].reports.begin();
+        } else {
+            a.single_report = 0;
+            a.reports = addReports(h[v].reports, reports, reports_cache);
+        }
+        a.squash = addSquashMask(args, v, squash);
+        accepts.push_back(move(a));
+    }
+}
+
+static
+void buildAccepts(const build_info &args,
+                  unordered_map<vector<ReportID>, u32> &reports_cache,
+                  NFAStateSet &acceptMask, NFAStateSet &acceptEodMask,
+                  vector<NFAAccept> &accepts, vector<NFAAccept> &acceptsEod,
+                  vector<ReportID> &reports, vector<NFAStateSet> &squash) {
     const NGHolder &h = args.h;
 
     acceptMask.resize(args.num_states);
     acceptEodMask.resize(args.num_states);
 
+    vector<NFAVertex> verts_accept, verts_accept_eod;
+
     for (auto v : vertices_range(h)) {
         u32 state_id = args.state_ids.at(v);
 
@@ -1007,41 +1098,20 @@ void buildAccepts(const build_info &args, NFAStateSet &acceptMask,
             continue;
         }
 
-        u32 squashMaskOffset = MO_INVALID_IDX;
-        auto sit = args.reportSquashMap.find(v);
-        if (sit != args.reportSquashMap.end()) {
-            // This state has a squash mask. Paw through the existing vector to
-            // see if we've already seen it, otherwise add a new one.
-            auto it = find(squash.begin(), squash.end(), sit->second);
-            if (it != squash.end()) {
-                squashMaskOffset = verify_u32(distance(squash.begin(), it));
-            } else {
-                squashMaskOffset = verify_u32(squash.size());
-                squash.push_back(sit->second);
-            }
-        }
-
-        // Add an accept (or acceptEod) per report ID.
-
-        vector<NFAAccept> *accepts_out;
         if (edge(v, h.accept, h).second) {
             acceptMask.set(state_id);
-            accepts_out = &accepts;
+            verts_accept.push_back(v);
         } else {
             assert(edge(v, h.acceptEod, h).second);
             acceptEodMask.set(state_id);
-            accepts_out = &acceptsEod;
-        }
-
-        for (auto report : h[v].reports) {
-            accepts_out->push_back(NFAAccept());
-            NFAAccept &a = accepts_out->back();
-            a.state = state_id;
-            a.externalId = report;
-            a.squash = squashMaskOffset;
-            DEBUG_PRINTF("Accept: state=%u, externalId=%u\n", state_id, report);
+            verts_accept_eod.push_back(v);
         }
     }
+
+    buildAcceptsList(args, reports_cache, verts_accept, accepts, reports,
+                     squash);
+    buildAcceptsList(args, reports_cache, verts_accept_eod, acceptsEod, reports,
+                     squash);
 }
 
 static
@@ -1314,36 +1384,12 @@ struct ExceptionProto {
     }
 };
 
-static
-u32 getReportListIndex(const flat_set<ReportID> &reports,
-                       vector<ReportID> &exceptionReports,
-                       map<vector<ReportID>, u32> &reportListCache) {
-    if (reports.empty()) {
-        return MO_INVALID_IDX;
-    }
-
-    const vector<ReportID> r(reports.begin(), reports.end());
-
-    auto it = reportListCache.find(r);
-    if (it != reportListCache.end()) {
-        u32 idx = it->second;
-        assert(idx < exceptionReports.size());
-        assert(equal(r.begin(), r.end(), exceptionReports.begin() + idx));
-        return idx;
-    }
-
-    u32 idx = verify_u32(exceptionReports.size());
-    reportListCache[r] = idx;
-    exceptionReports.insert(exceptionReports.end(), r.begin(), r.end());
-    exceptionReports.push_back(MO_INVALID_IDX); // terminator
-    return idx;
-}
-
 static
 u32 buildExceptionMap(const build_info &args,
+                      unordered_map<vector<ReportID>, u32> &reports_cache,
                       const ue2::unordered_set<NFAEdge> &exceptional,
-                      map<ExceptionProto, vector<u32> > &exceptionMap,
-                      vector<ReportID> &exceptionReports) {
+                      map<ExceptionProto, vector<u32>> &exceptionMap,
+                      vector<ReportID> &reportList) {
     const NGHolder &h = args.h;
     const u32 num_states = args.num_states;
     u32 exceptionCount = 0;
@@ -1361,10 +1407,6 @@ u32 buildExceptionMap(const build_info &args,
         }
     }
 
-    // We track report lists that have already been written into the global
-    // list in case we can reuse them.
-    map<vector<ReportID>, u32> reportListCache;
-
     for (auto v : vertices_range(h)) {
         const u32 i = args.state_ids.at(v);
 
@@ -1383,8 +1425,12 @@ u32 buildExceptionMap(const build_info &args,
             DEBUG_PRINTF("state %u is exceptional due to accept "
                          "(%zu reports)\n", i, reports.size());
 
-            e.reports_index =
-                getReportListIndex(reports, exceptionReports, reportListCache);
+            if (reports.empty()) {
+                e.reports_index = MO_INVALID_IDX;
+            } else {
+                e.reports_index =
+                    addReports(reports, reportList, reports_cache);
+            }
 
             // We may be applying a report squash too.
             auto mi = args.reportSquashMap.find(v);
@@ -1810,9 +1856,10 @@ struct Factory {
     }
 
     static
-    void writeExceptions(const map<ExceptionProto, vector<u32> > &exceptionMap,
-                         const vector<u32> &repeatOffsets,
-                         implNFA_t *limex, const u32 exceptionsOffset) {
+    void writeExceptions(const map<ExceptionProto, vector<u32>> &exceptionMap,
+                         const vector<u32> &repeatOffsets, implNFA_t *limex,
+                         const u32 exceptionsOffset,
+                         const u32 reportListOffset) {
         DEBUG_PRINTF("exceptionsOffset=%u\n", exceptionsOffset);
 
         exception_t *etable = (exception_t *)((char *)limex + exceptionsOffset);
@@ -1839,7 +1886,12 @@ struct Factory {
             exception_t &e = etable[ecount];
             maskSetBits(e.squash, proto.squash_states);
             maskSetBits(e.successors, proto.succ_states);
-            e.reports = proto.reports_index;
+            if (proto.reports_index == MO_INVALID_IDX) {
+                e.reports = MO_INVALID_IDX;
+            } else {
+                e.reports = reportListOffset +
+                            proto.reports_index * sizeof(ReportID);
+            }
             e.hasSquash = verify_u8(proto.squash);
             e.trigger = verify_u8(proto.trigger);
             u32 repeat_offset = proto.repeat_index == MO_INVALID_IDX
@@ -1958,7 +2010,9 @@ struct Factory {
                       const vector<NFAAccept> &acceptsEod,
                       const vector<NFAStateSet> &squash, implNFA_t *limex,
                       const u32 acceptsOffset, const u32 acceptsEodOffset,
-                      const u32 squashOffset) {
+                      const u32 squashOffset, const u32 reportListOffset) {
+        char *limex_base = (char *)limex;
+
         DEBUG_PRINTF("acceptsOffset=%u, acceptsEodOffset=%u, squashOffset=%u\n",
                      acceptsOffset, acceptsEodOffset, squashOffset);
 
@@ -1966,27 +2020,38 @@ struct Factory {
         maskSetBits(limex->accept, acceptMask);
         maskSetBits(limex->acceptAtEOD, acceptEodMask);
 
+        // Transforms the index into the report list into an offset relative to
+        // the base of the limex.
+        auto report_offset_fn = [&](NFAAccept a) {
+            if (!a.single_report) {
+                a.reports = reportListOffset + a.reports * sizeof(ReportID);
+            }
+            return a;
+        };
+
         // Write accept table.
         limex->acceptOffset = acceptsOffset;
         limex->acceptCount = verify_u32(accepts.size());
         DEBUG_PRINTF("NFA has %zu accepts\n", accepts.size());
-        NFAAccept *acceptsTable = (NFAAccept *)((char *)limex + acceptsOffset);
+        NFAAccept *acceptsTable = (NFAAccept *)(limex_base + acceptsOffset);
         assert(ISALIGNED(acceptsTable));
-        copy(accepts.begin(), accepts.end(), acceptsTable);
+        transform(accepts.begin(), accepts.end(), acceptsTable,
+                  report_offset_fn);
 
         // Write eod accept table.
         limex->acceptEodOffset = acceptsEodOffset;
         limex->acceptEodCount = verify_u32(acceptsEod.size());
         DEBUG_PRINTF("NFA has %zu EOD accepts\n", acceptsEod.size());
-        NFAAccept *acceptsEodTable = (NFAAccept *)((char *)limex + acceptsEodOffset);
+        NFAAccept *acceptsEodTable = (NFAAccept *)(limex_base + acceptsEodOffset);
         assert(ISALIGNED(acceptsEodTable));
-        copy(acceptsEod.begin(), acceptsEod.end(), acceptsEodTable);
+        transform(acceptsEod.begin(), acceptsEod.end(), acceptsEodTable,
+                  report_offset_fn);
 
         // Write squash mask table.
         limex->squashCount = verify_u32(squash.size());
         limex->squashOffset = squashOffset;
         DEBUG_PRINTF("NFA has %zu report squash masks\n", squash.size());
-        tableRow_t *mask = (tableRow_t *)((char *)limex + squashOffset);
+        tableRow_t *mask = (tableRow_t *)(limex_base + squashOffset);
         assert(ISALIGNED(mask));
         for (size_t i = 0, end = squash.size(); i < end; i++) {
             maskSetBits(mask[i], squash[i]);
@@ -2023,15 +2088,12 @@ struct Factory {
     }
 
     static
-    void writeExceptionReports(const vector<ReportID> &reports,
-                               implNFA_t *limex,
-                               const u32 exceptionReportsOffset) {
-        DEBUG_PRINTF("exceptionReportsOffset=%u\n", exceptionReportsOffset);
-
-        limex->exReportOffset = exceptionReportsOffset;
-        assert(ISALIGNED_N((char *)limex + exceptionReportsOffset,
+    void writeReportList(const vector<ReportID> &reports, implNFA_t *limex,
+                         const u32 reportListOffset) {
+        DEBUG_PRINTF("reportListOffset=%u\n", reportListOffset);
+        assert(ISALIGNED_N((char *)limex + reportListOffset,
                            alignof(ReportID)));
-        copy_bytes((char *)limex + exceptionReportsOffset, reports);
+        copy_bytes((char *)limex + reportListOffset, reports);
     }
 
     static
@@ -2050,6 +2112,10 @@ struct Factory {
             repeatSize += repeats[i].second;
         }
 
+        // We track report lists that have already been written into the global
+        // list in case we can reuse them.
+        unordered_map<vector<ReportID>, u32> reports_cache;
+
         ue2::unordered_set<NFAEdge> exceptional;
         u32 shiftCount = findBestNumOfVarShifts(args);
         assert(shiftCount);
@@ -2057,9 +2123,10 @@ struct Factory {
         findExceptionalTransitions(args, exceptional, maxShift);
 
         map<ExceptionProto, vector<u32> > exceptionMap;
-        vector<ReportID> exceptionReports;
-        u32 exceptionCount = buildExceptionMap(args, exceptional, exceptionMap,
-                                               exceptionReports);
+        vector<ReportID> reportList;
+
+        u32 exceptionCount = buildExceptionMap(args, reports_cache, exceptional,
+                                               exceptionMap, reportList);
 
         assert(exceptionCount <= args.num_states);
 
@@ -2076,8 +2143,8 @@ struct Factory {
         NFAStateSet acceptMask, acceptEodMask;
         vector<NFAAccept> accepts, acceptsEod;
         vector<NFAStateSet> squash;
-        buildAccepts(args, acceptMask, acceptEodMask, accepts, acceptsEod,
-                     squash);
+        buildAccepts(args, reports_cache, acceptMask, acceptEodMask, accepts,
+                     acceptsEod, reportList, squash);
 
         // Build all our accel info.
         NFAStateSet accelMask, accelFriendsMask;
@@ -2118,8 +2185,8 @@ struct Factory {
         const u32 exceptionsOffset = offset;
         offset += sizeof(exception_t) * exceptionCount;
 
-        const u32 exceptionReportsOffset = offset;
-        offset += sizeof(ReportID) * exceptionReports.size();
+        const u32 reportListOffset = offset;
+        offset += sizeof(ReportID) * reportList.size();
 
         const u32 repeatOffsetsOffset = offset;
         offset += sizeof(u32) * args.repeats.size();
@@ -2146,7 +2213,8 @@ struct Factory {
                    limex, accelTableOffset, accelAuxOffset);
 
         writeAccepts(acceptMask, acceptEodMask, accepts, acceptsEod, squash,
-                     limex, acceptsOffset, acceptsEodOffset, squashOffset);
+                     limex, acceptsOffset, acceptsEodOffset, squashOffset,
+                     reportListOffset);
 
         limex->shiftCount = shiftCount;
         writeShiftMasks(args, limex);
@@ -2154,14 +2222,15 @@ struct Factory {
         // Determine the state required for our state vector.
         findStateSize(args, limex);
 
-        writeExceptionReports(exceptionReports, limex, exceptionReportsOffset);
+        writeReportList(reportList, limex, reportListOffset);
 
         // Repeat structures and offset table.
         vector<u32> repeatOffsets;
         writeRepeats(repeats, repeatOffsets, limex, repeatOffsetsOffset,
                      repeatsOffset);
 
-        writeExceptions(exceptionMap, repeatOffsets, limex, exceptionsOffset);
+        writeExceptions(exceptionMap, repeatOffsets, limex, exceptionsOffset,
+                        reportListOffset);
 
         writeLimexMasks(args, limex);
 
index 181951dc31c2e76344af4b7b87c68482dbbff2fd..149e81077b268dbf621c9bca0ff544a17a2406db 100644 (file)
@@ -180,26 +180,40 @@ void dumpAccel(const limex_type *limex, FILE *f) {
     }
 }
 
+static
+void dumpAcceptList(const char *limex_base, const struct NFAAccept *accepts,
+                    u32 acceptCount, FILE *f) {
+    for (u32 i = 0; i < acceptCount; i++) {
+        const NFAAccept &a = accepts[i];
+        if (a.single_report) {
+            fprintf(f, "  idx %u fires single report %u\n", i, a.reports);
+            continue;
+        }
+        fprintf(f, "  idx %u fires report list %u:", i, a.reports);
+        const ReportID *report = (const ReportID *)(limex_base + a.reports);
+        for (; *report != MO_INVALID_IDX; report++) {
+            fprintf(f, " %u", *report);
+        }
+        fprintf(f, "\n");
+    }
+}
+
 template<typename limex_type>
 static
 void dumpAccepts(const limex_type *limex, FILE *f) {
-    u32 acceptCount = limex->acceptCount;
-    u32 acceptEodCount = limex->acceptEodCount;
+    const char *limex_base = (const char *)limex;
+
+    const u32 acceptCount = limex->acceptCount;
+    const u32 acceptEodCount = limex->acceptEodCount;
 
     fprintf(f, "\n%u accepts.\n", acceptCount);
-    const struct NFAAccept *accepts
-        = (const struct NFAAccept *)((const char *)limex + limex->acceptOffset);
-    for (u32 i = 0; i < acceptCount; i++) {
-        fprintf(f, "  state %u fires report %u\n", accepts[i].state,
-                accepts[i].externalId);
-    }
+    const auto *accepts =
+        (const struct NFAAccept *)(limex_base + limex->acceptOffset);
+    dumpAcceptList(limex_base, accepts, acceptCount, f);
     fprintf(f, "\n%u accepts at EOD.\n", acceptEodCount);
-    accepts = (const struct NFAAccept *)((const char *)limex
-                                         + limex->acceptEodOffset);
-    for (u32 i = 0; i < acceptEodCount; i++) {
-        fprintf(f, "  state %u fires report %u\n", accepts[i].state,
-                accepts[i].externalId);
-    }
+    const auto *accepts_eod =
+        (const struct NFAAccept *)(limex_base + limex->acceptEodOffset);
+    dumpAcceptList(limex_base, accepts_eod, acceptEodCount, f);
     fprintf(f, "\n");
 }
 
@@ -226,20 +240,15 @@ getExceptionTable(const limex_type *limex) {
         ((const char *)limex + limex->exceptionOffset);
 }
 
-template<typename limex_type>
-static
-const ReportID *getReportList(const limex_type *limex) {
-    return (const ReportID *)((const char *)limex + limex->exReportOffset);
-}
-
 template<typename limex_type>
 static
 void dumpLimexExceptions(const limex_type *limex, FILE *f) {
     const typename limex_traits<limex_type>::exception_type *e =
                 getExceptionTable(limex);
-    const ReportID *reports = getReportList(limex);
     const u32 size = limex_traits<limex_type>::size;
 
+    const char *limex_base = (const char *)limex;
+
     fprintf(f, "\n");
     for (u32 i = 0; i < limex->exceptionCount; i++) {
         fprintf(f, "exception %u: hasSquash=%u, reports offset=%u\n",
@@ -255,7 +264,7 @@ void dumpLimexExceptions(const limex_type *limex, FILE *f) {
         if (e[i].reports == MO_INVALID_IDX) {
             fprintf(f, " <none>\n");
         } else {
-            const ReportID *r = reports + e[i].reports;
+            const ReportID *r = (const ReportID *)(limex_base + e[i].reports);
             while (*r != MO_INVALID_IDX) {
                 fprintf(f, " %u", *r++);
             }
index c8296f918dda5a3bc77eda349faa4049275fa08f..e770c3278138312341380a75da858409effc0df7 100644 (file)
@@ -95,7 +95,6 @@ int RUN_EXCEPTION_FN(const EXCEPTION_T *e, STATE_ARG,
                      STATE_T *local_succ,
 #endif
                      const struct IMPL_NFA_T *limex,
-                     const ReportID *exReports,
                      u64a offset,
                      struct CONTEXT_T *ctx,
                      struct proto_cache *new_cache,
@@ -161,7 +160,8 @@ int RUN_EXCEPTION_FN(const EXCEPTION_T *e, STATE_ARG,
     // Some exceptions fire accepts.
     if (e->reports != MO_INVALID_IDX) {
         if (flags & CALLBACK_OUTPUT) {
-            const ReportID *reports = exReports + e->reports;
+            const ReportID *reports =
+                (const ReportID *)((const char *)limex + e->reports);
             if (unlikely(limexRunReports(reports, ctx->callback,
                             ctx->context, offset)
                         == MO_HALT_MATCHING)) {
@@ -210,8 +210,7 @@ int RUN_EXCEPTION_FN(const EXCEPTION_T *e, STATE_ARG,
 static really_inline
 int PE_FN(STATE_ARG, ESTATE_ARG, u32 diffmask, STATE_T *succ,
           const struct IMPL_NFA_T *limex, const EXCEPTION_T *exceptions,
-          const ReportID *exReports, u64a offset, struct CONTEXT_T *ctx,
-          char in_rev, char flags) {
+          u64a offset, struct CONTEXT_T *ctx, char in_rev, char flags) {
     assert(diffmask > 0); // guaranteed by caller macro
 
     if (EQ_STATE(estate, ctx->cached_estate)) {
@@ -271,8 +270,8 @@ int PE_FN(STATE_ARG, ESTATE_ARG, u32 diffmask, STATE_T *succ,
 #ifndef BIG_MODEL
                                   &local_succ,
 #endif
-                                  limex, exReports, offset, ctx, &new_cache,
-                                  &cacheable, in_rev, flags)) {
+                                  limex, offset, ctx, &new_cache, &cacheable,
+                                  in_rev, flags)) {
                 return PE_RV_HALT;
             }
         } while (word);
@@ -326,7 +325,9 @@ int PE_FN(STATE_ARG, ESTATE_ARG, u32 diffmask, STATE_T *succ,
 #undef STATE_ARG_NAME
 #undef STATE_ARG_P
 
+#undef IMPL_NFA_T
+
 #undef CHUNK_T
 #undef FIND_AND_CLEAR_FN
-#undef IMPL_NFA_T
-#undef GET_NFA_REPEAT_INFO_FN
+#undef POPCOUNT_FN
+#undef RANK_IN_MASK_FN
index 03ebb3844a7ec4784a901f93a2882609473e90c7..0d46732f85fb7777d48039f35b1de160c2e880bb 100644 (file)
@@ -132,7 +132,6 @@ struct LimExNFA##size {                                                     \
     u32 acceptEodOffset; /* rel. to start of LimExNFA */                    \
     u32 exceptionCount;                                                     \
     u32 exceptionOffset; /* rel. to start of LimExNFA */                    \
-    u32 exReportOffset; /* rel. to start of LimExNFA */                     \
     u32 repeatCount;                                                        \
     u32 repeatOffset;                                                       \
     u32 squashOffset; /* rel. to start of LimExNFA; for accept squashing */ \
@@ -184,9 +183,16 @@ struct NFARepeatInfo {
 };
 
 struct NFAAccept {
-    u32 state;           //!< state ID of triggering state
-    ReportID externalId; //!< report ID to raise
-    u32 squash;          //!< offset into masks, or MO_INVALID_IDX
+    u8 single_report; //!< If true, 'reports' is report id.
+
+    /**
+     * \brief If single report is true, this is the report id to fire.
+     * Otherwise, it is the offset (relative to the start of the LimExNFA
+     * structure) of a list of reports, terminated with MO_INVALID_IDX.
+     */
+    u32 reports;
+
+    u32 squash;  //!< Offset into squash masks, or MO_INVALID_IDX.
 };
 
 #endif
index c9949836d3aae8786d4b311b3ef1597173701cf3..f6f5809c36395c80a1b41a000bfea3e985388dbd 100644 (file)
@@ -73,8 +73,7 @@
 static really_inline
 int processExceptional32(u32 s, u32 estate, UNUSED u32 diffmask, u32 *succ,
                          const struct LimExNFA32 *limex,
-                         const struct NFAException32 *exceptions,
-                         const ReportID *exReports, u64a offset,
+                         const struct NFAException32 *exceptions, u64a offset,
                          struct NFAContext32 *ctx, char in_rev, char flags) {
     assert(estate != 0); // guaranteed by calling macro
 
@@ -104,8 +103,8 @@ int processExceptional32(u32 s, u32 estate, UNUSED u32 diffmask, u32 *succ,
         u32 bit = findAndClearLSB_32(&estate);
         u32 idx = rank_in_mask32(limex->exceptionMask, bit);
         const struct NFAException32 *e = &exceptions[idx];
-        if (!runException32(e, s, succ, &local_succ, limex, exReports, offset,
-                            ctx, &new_cache, &cacheable, in_rev, flags)) {
+        if (!runException32(e, s, succ, &local_succ, limex, offset, ctx,
+                            &new_cache, &cacheable, in_rev, flags)) {
             return PE_RV_HALT;
         }
     } while (estate != 0);
index 75094ef6e2a48be0a1afe437f7788a101a89914b..6109d382d8da4556d2e45954aad4417003a5fe16 100644 (file)
@@ -103,14 +103,42 @@ int limexRunReports(const ReportID *reports, NfaCallback callback,
     return MO_CONTINUE_MATCHING; // continue
 }
 
+static really_inline
+int limexRunAccept(const char *limex_base, const struct NFAAccept *accept,
+                   NfaCallback callback, void *context, u64a offset) {
+    if (accept->single_report) {
+        const ReportID report = accept->reports;
+        DEBUG_PRINTF("firing single report for id %u at offset %llu\n", report,
+                     offset);
+        return callback(0, offset, report, context);
+    }
+    const ReportID *reports = (const ReportID *)(limex_base + accept->reports);
+    return limexRunReports(reports, callback, context, offset);
+}
+
+static really_inline
+int limexAcceptHasReport(const char *limex_base, const struct NFAAccept *accept,
+                         ReportID report) {
+    if (accept->single_report) {
+        return accept->reports == report;
+    }
+
+    const ReportID *reports = (const ReportID *)(limex_base + accept->reports);
+    assert(*reports != MO_INVALID_IDX);
+    do {
+        if (*reports == report) {
+            return 1;
+        }
+        reports++;
+    } while (*reports != MO_INVALID_IDX);
+
+    return 0;
+}
+
 /** \brief Return a (correctly typed) pointer to the exception table. */
 #define getExceptionTable(exc_type, lim)                                       \
     ((const exc_type *)((const char *)(lim) + (lim)->exceptionOffset))
 
-/** \brief Return a pointer to the exceptional reports list. */
-#define getExReports(lim)                                                      \
-    ((const ReportID *)((const char *)(lim) + (lim)->exReportOffset))
-
 /** \brief Return a pointer to the ordinary accepts table. */
 #define getAcceptTable(lim)                                                    \
     ((const struct NFAAccept *)((const char *)(lim) + (lim)->acceptOffset))
index 5bc79c24d4a0e996cc66a65e6fbb0d70655600c3..45ceb2b5ef2e9ba4372bbb76685d2a1cf5f660a2 100644 (file)
 // continue, 1 if an accept was fired and the user instructed us to halt.
 static really_inline
 char RUN_EXCEPTIONS_FN(const IMPL_NFA_T *limex, const EXCEPTION_T *exceptions,
-                       const ReportID *exReports, STATE_T s,
-                       const STATE_T emask, size_t i, u64a offset,
+                       STATE_T s, const STATE_T emask, size_t i, u64a offset,
                        STATE_T *succ, u64a *final_loc, struct CONTEXT_T *ctx,
                        const char flags, const char in_rev,
                        const char first_match) {
@@ -131,7 +130,7 @@ char RUN_EXCEPTIONS_FN(const IMPL_NFA_T *limex, const EXCEPTION_T *exceptions,
     char localflags = (!i && !in_rev) ? NO_OUTPUT | FIRST_BYTE : flags;
 
     int rv = JOIN(processExceptional, SIZE)(
-        pass_state, pass_estate, diffmask, succ, limex, exceptions, exReports,
+        pass_state, pass_estate, diffmask, succ, limex, exceptions,
         callback_offset, ctx, in_rev, localflags);
     if (rv == PE_RV_HALT) {
         return 1; // Halt matching.
@@ -207,7 +206,6 @@ char STREAM_FN(const IMPL_NFA_T *limex, const u8 *input, size_t length,
     const union AccelAux *accelAux =
         (const union AccelAux *)((const char *)limex + limex->accelAuxOffset);
     const EXCEPTION_T *exceptions = getExceptionTable(EXCEPTION_T, limex);
-    const ReportID *exReports = getExReports(limex);
     STATE_T s = ctx->s;
 
     /* assert(ISALIGNED_16(exceptions)); */
@@ -235,9 +233,8 @@ without_accel:
         STATE_T succ;
         NFA_EXEC_GET_LIM_SUCC(limex, s, succ);
 
-        if (RUN_EXCEPTIONS_FN(limex, exceptions, exReports, s, EXCEPTION_MASK,
-                              i, offset, &succ, final_loc, ctx, flags, 0,
-                              first_match)) {
+        if (RUN_EXCEPTIONS_FN(limex, exceptions, s, EXCEPTION_MASK, i, offset,
+                              &succ, final_loc, ctx, flags, 0, first_match)) {
             return MO_HALT_MATCHING;
         }
 
@@ -286,9 +283,8 @@ with_accel:
         STATE_T succ;
         NFA_EXEC_GET_LIM_SUCC(limex, s, succ);
 
-        if (RUN_EXCEPTIONS_FN(limex, exceptions, exReports, s,  EXCEPTION_MASK,
-                              i, offset, &succ, final_loc, ctx, flags, 0,
-                              first_match)) {
+        if (RUN_EXCEPTIONS_FN(limex, exceptions, s, EXCEPTION_MASK, i, offset,
+                              &succ, final_loc, ctx, flags, 0, first_match)) {
             return MO_HALT_MATCHING;
         }
 
@@ -300,8 +296,6 @@ with_accel:
     if ((first_match || (flags & CALLBACK_OUTPUT)) && limex->acceptCount) {
         STATE_T acceptMask = LOAD_FROM_ENG(&limex->accept);
         const struct NFAAccept *acceptTable = getAcceptTable(limex);
-        const u32 acceptCount = limex->acceptCount;
-
         STATE_T foundAccepts = AND_STATE(s, acceptMask);
         if (unlikely(ISNONZERO_STATE(foundAccepts))) {
             if (first_match) {
@@ -309,8 +303,8 @@ with_accel:
                 assert(final_loc);
                 *final_loc = length;
                 return MO_HALT_MATCHING;
-            } else if (PROCESS_ACCEPTS_FN(limex, &ctx->s, acceptTable,
-                                          acceptCount, offset + length,
+            } else if (PROCESS_ACCEPTS_FN(limex, &ctx->s, &acceptMask,
+                                          acceptTable, offset + length,
                                           ctx->callback, ctx->context)) {
                 return MO_HALT_MATCHING;
             }
@@ -331,7 +325,6 @@ char REV_STREAM_FN(const IMPL_NFA_T *limex, const u8 *input, size_t length,
     const STATE_T exceptionMask = LOAD_FROM_ENG(&limex->exceptionMask);
 #endif
     const EXCEPTION_T *exceptions = getExceptionTable(EXCEPTION_T, limex);
-    const ReportID *exReports = getExReports(limex);
     STATE_T s = ctx->s;
 
     /* assert(ISALIGNED_16(exceptions)); */
@@ -351,9 +344,8 @@ char REV_STREAM_FN(const IMPL_NFA_T *limex, const u8 *input, size_t length,
         STATE_T succ;
         NFA_EXEC_GET_LIM_SUCC(limex, s, succ);
 
-        if (RUN_EXCEPTIONS_FN(limex, exceptions, exReports, s,
-                              EXCEPTION_MASK, i, offset, &succ, final_loc, ctx,
-                              flags, 1, 0)) {
+        if (RUN_EXCEPTIONS_FN(limex, exceptions, s, EXCEPTION_MASK, i, offset,
+                              &succ, final_loc, ctx, flags, 1, 0)) {
             return MO_HALT_MATCHING;
         }
 
@@ -369,8 +361,8 @@ char REV_STREAM_FN(const IMPL_NFA_T *limex, const u8 *input, size_t length,
     if (acceptCount) {
         STATE_T foundAccepts = AND_STATE(s, acceptMask);
         if (unlikely(ISNONZERO_STATE(foundAccepts))) {
-            if (PROCESS_ACCEPTS_NOSQUASH_FN(&ctx->s, acceptTable, acceptCount,
-                                            offset, ctx->callback,
+            if (PROCESS_ACCEPTS_NOSQUASH_FN(limex, &ctx->s, &acceptMask,
+                                            acceptTable, offset, ctx->callback,
                                             ctx->context)) {
                 return MO_HALT_MATCHING;
             }