limex: rework accept handling

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.
This commit is contained in:
Justin Viiret 2016-08-30 14:24:23 +10:00 committed by Matthew Barr
parent 3dcfea19e0
commit 77fe1ef6e5
8 changed files with 374 additions and 219 deletions

View 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)
@ -60,6 +61,20 @@
#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);
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 (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));
}
}
base_index += POPCOUNT_FN(mask_chunks[i]);
}
return 0;
}
static never_inline
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);
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;
}
if (a->squash != MO_INVALID_IDX) {
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));
}
}
}
return PROCESS_ACCEPTS_IMPL_FN(limex, s, &squash, squashMasks, acceptMask,
acceptTable, offset, callback, context);
*s = AND_STATE(*s, squash);
return 0;
}
static never_inline
char PROCESS_ACCEPTS_NOSQUASH_FN(const STATE_T *s,
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);
u64a offset, NfaCallback callback,
void *context) {
STATE_T *squash = NULL;
const ENG_STATE_T *squashMasks = NULL;
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;
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

View 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);

View 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++);
}

View 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

View 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

View 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);

View 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))

View File

@ -103,8 +103,7 @@
// 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;
}