Rose: Move all literal operations into program

Replace the RoseLiteral structure with more program instructions; now,
instead of each literal ID leading to a RoseLiteral, it simply has a
program to run (and a delay rebuild program).

This commit also makes some other improvements:

 * CHECK_STATE instruction, for use instead of a sparse iterator over a
   single element.
 * Elide some checks (CHECK_LIT_EARLY, ANCHORED_DELAY, etc) when not
   needed.
 * Flatten PUSH_DELAYED behaviour to one instruction per delayed
   literal, rather than the mask/index-list approach used before.
 * Simple program cache at compile time for deduplication.
This commit is contained in:
Justin Viiret 2015-12-18 15:24:52 +11:00 committed by Matthew Barr
parent 255d84a83a
commit 10cda4cc33
11 changed files with 843 additions and 628 deletions

View File

@ -1,5 +1,5 @@
/*
* Copyright (c) 2015, Intel Corporation
* Copyright (c) 2015-2016, Intel Corporation
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
@ -114,9 +114,9 @@ int roseEodRunIterator(const struct RoseEngine *t, u64a offset,
DEBUG_PRINTF("running eod program at offset %u\n", t->eodIterProgramOffset);
int work_done = 0;
if (roseRunProgram(t, t->eodIterProgramOffset, offset, &(scratch->tctxt), 0,
&work_done) == HWLM_TERMINATE_MATCHING) {
const size_t match_len = 0;
if (roseRunProgram(t, t->eodIterProgramOffset, offset, match_len,
&(scratch->tctxt), 0) == HWLM_TERMINATE_MATCHING) {
return MO_HALT_MATCHING;
}
@ -233,9 +233,9 @@ int roseRunEodProgram(const struct RoseEngine *t, u64a offset,
// There should be no pending delayed literals.
assert(!scratch->tctxt.filledDelayedSlots);
int work_done = 0;
if (roseRunProgram(t, t->eodProgramOffset, offset, &scratch->tctxt, 0,
&work_done) == HWLM_TERMINATE_MATCHING) {
const size_t match_len = 0;
if (roseRunProgram(t, t->eodProgramOffset, offset, match_len,
&scratch->tctxt, 0) == HWLM_TERMINATE_MATCHING) {
return MO_HALT_MATCHING;
}

View File

@ -1,5 +1,5 @@
/*
* Copyright (c) 2015, Intel Corporation
* Copyright (c) 2015-2016, Intel Corporation
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
@ -71,123 +71,6 @@ void printMatch(const struct core_info *ci, u64a start, u64a end) {
}
#endif
static rose_inline
int roseCheckBenefits(struct RoseContext *tctxt, u64a end, u32 mask_rewind,
const u8 *and_mask, const u8 *exp_mask) {
DEBUG_PRINTF("am offset = %zu, em offset = %zu\n",
and_mask - (const u8 *)tctxt->t,
exp_mask - (const u8 *)tctxt->t);
const u8 *data;
// If the check works over part of the history and part of the buffer, we
// create a temporary copy of the data in here so it's contiguous.
u8 temp[MAX_MASK2_WIDTH];
struct core_info *ci = &tctxtToScratch(tctxt)->core_info;
s64a buffer_offset = (s64a)end - ci->buf_offset;
DEBUG_PRINTF("rel offset %lld\n", buffer_offset);
if (buffer_offset >= mask_rewind) {
data = ci->buf + buffer_offset - mask_rewind;
DEBUG_PRINTF("all in one case data=%p buf=%p rewind=%u\n", data,
ci->buf, mask_rewind);
} else if (buffer_offset <= 0) {
data = ci->hbuf + ci->hlen + buffer_offset - mask_rewind;
DEBUG_PRINTF("all in one case data=%p buf=%p rewind=%u\n", data,
ci->buf, mask_rewind);
} else {
u32 shortfall = mask_rewind - buffer_offset;
DEBUG_PRINTF("shortfall of %u, rewind %u hlen %zu\n", shortfall,
mask_rewind, ci->hlen);
data = temp;
memcpy(temp, ci->hbuf + ci->hlen - shortfall, shortfall);
memcpy(temp + shortfall, ci->buf, mask_rewind - shortfall);
}
#ifdef DEBUG
DEBUG_PRINTF("DATA: ");
for (u32 i = 0; i < mask_rewind; i++) {
printf("%c", ourisprint(data[i]) ? data[i] : '?');
}
printf(" (len=%u)\n", mask_rewind);
#endif
u32 len = mask_rewind;
while (len >= sizeof(u64a)) {
u64a a = unaligned_load_u64a(data);
a &= *(const u64a *)and_mask;
if (a != *(const u64a *)exp_mask) {
DEBUG_PRINTF("argh %016llx %016llx\n", a, *(const u64a *)exp_mask);
return 0;
}
data += sizeof(u64a);
and_mask += sizeof(u64a);
exp_mask += sizeof(u64a);
len -= sizeof(u64a);
}
while (len) {
u8 a = *data;
a &= *and_mask;
if (a != *exp_mask) {
DEBUG_PRINTF("argh d%02hhx =%02hhx am%02hhx em%02hhx\n", a,
*data, *and_mask, *exp_mask);
return 0;
}
data++;
and_mask++;
exp_mask++;
len--;
}
return 1;
}
static
int roseCheckLiteralBenefits(u64a end, size_t mask_rewind, u32 id,
struct RoseContext *tctxt) {
const struct RoseEngine *t = tctxt->t;
const struct lit_benefits *lbi = getLiteralBenefitsTable(t) + id;
return roseCheckBenefits(tctxt, end, mask_rewind, lbi->and_mask.a8,
lbi->expected.e8);
}
static rose_inline
void pushDelayedMatches(const struct RoseLiteral *tl, u64a offset,
struct RoseContext *tctxt) {
u32 delay_mask = tl->delay_mask;
if (!delay_mask) {
return;
}
u32 delay_count = tctxt->t->delay_count;
u8 *delaySlotBase = getDelaySlots(tctxtToScratch(tctxt));
size_t delaySlotSize = tctxt->t->delay_slot_size;
assert(tl->delayIdsOffset != ROSE_OFFSET_INVALID);
const u32 *delayIds = getByOffset(tctxt->t, tl->delayIdsOffset);
assert(ISALIGNED(delayIds));
while (delay_mask) {
u32 src_slot_index = findAndClearLSB_32(&delay_mask);
u32 slot_index = (src_slot_index + offset) & DELAY_MASK;
u8 *slot = delaySlotBase + delaySlotSize * slot_index;
if (offset + src_slot_index <= tctxt->delayLastEndOffset) {
DEBUG_PRINTF("skip too late\n");
goto next;
}
DEBUG_PRINTF("pushing tab %u into slot %u\n", *delayIds, slot_index);
if (!(tctxt->filledDelayedSlots & (1U << slot_index))) {
tctxt->filledDelayedSlots |= 1U << slot_index;
mmbit_clear(slot, delay_count);
}
mmbit_set(slot, delay_count, *delayIds);
next:
delayIds++;
}
}
hwlmcb_rv_t roseDelayRebuildCallback(size_t start, size_t end, u32 id,
void *ctx) {
struct hs_scratch *scratch = ctx;
@ -211,17 +94,17 @@ hwlmcb_rv_t roseDelayRebuildCallback(size_t start, size_t end, u32 id,
return tctx->groups;
}
if (id < t->nonbenefits_base_id
&& !roseCheckLiteralBenefits(real_end, end - start + 1, id, tctx)) {
return tctx->groups;
}
assert(id < t->literalCount);
const struct RoseLiteral *tl = &getLiteralTable(t)[id];
const u32 *delayRebuildPrograms =
getByOffset(t, t->litDelayRebuildProgramOffset);
const u32 programOffset = delayRebuildPrograms[id];
DEBUG_PRINTF("literal id=%u, groups=0x%016llx\n", id, tl->groups);
pushDelayedMatches(tl, real_end, tctx);
if (programOffset) {
const size_t match_len = end - start + 1;
UNUSED hwlmcb_rv_t rv =
roseRunProgram(t, programOffset, real_end, match_len, tctx, 0);
assert(rv != HWLM_TERMINATE_MATCHING);
}
/* we are just repopulating the delay queue, groups should be
* already set from the original scan. */
@ -465,31 +348,28 @@ int roseAnchoredCallback(u64a end, u32 id, void *ctx) {
}
assert(id < t->literalCount);
const struct RoseLiteral *tl = &getLiteralTable(t)[id];
assert(tl->programOffset);
assert(!tl->delay_mask);
const u32 *programs = getByOffset(t, t->litProgramOffset);
const u32 programOffset = programs[id];
assert(programOffset);
DEBUG_PRINTF("literal id=%u, groups=0x%016llx\n", id, tl->groups);
// Anchored literals are never delayed.
assert(!((const u32 *)getByOffset(t, t->litDelayRebuildProgramOffset))[id]);
DEBUG_PRINTF("literal id=%u\n", id);
if (real_end <= t->floatingMinLiteralMatchOffset) {
roseFlushLastByteHistory(t, state, real_end, tctxt);
tctxt->lastEndOffset = real_end;
}
int work_done = 0;
if (roseRunProgram(t, tl->programOffset, real_end, tctxt, 1, &work_done) ==
const size_t match_len = 0;
if (roseRunProgram(t, programOffset, real_end, match_len, tctxt, 1) ==
HWLM_TERMINATE_MATCHING) {
assert(can_stop_matching(tctxtToScratch(tctxt)));
DEBUG_PRINTF("caller requested termination\n");
return MO_HALT_MATCHING;
}
// If we've actually handled any roles, we might need to apply this
// literal's squash mask to our groups as well.
if (work_done && tl->squashesGroup) {
roseSquashGroup(tctxt, tl);
}
DEBUG_PRINTF("DONE groups=0x%016llx\n", tctxt->groups);
if (real_end > t->floatingMinLiteralMatchOffset) {
@ -502,9 +382,10 @@ int roseAnchoredCallback(u64a end, u32 id, void *ctx) {
// Rose match-processing workhorse
/* assumes not in_anchored */
static really_inline
hwlmcb_rv_t roseProcessMatch_i(const struct RoseEngine *t, u64a end, u32 id,
struct RoseContext *tctxt, char do_group_check,
char in_delay_play, char in_anch_playback) {
hwlmcb_rv_t roseProcessMatch_i(const struct RoseEngine *t, u64a end,
size_t match_len, u32 id,
struct RoseContext *tctxt, char in_delay_play,
char in_anch_playback) {
/* assert(!tctxt->in_anchored); */
u8 *state = tctxt->state;
@ -536,63 +417,30 @@ hwlmcb_rv_t roseProcessMatch_i(const struct RoseEngine *t, u64a end, u32 id,
}
assert(id < t->literalCount);
const struct RoseLiteral *tl = &getLiteralTable(t)[id];
DEBUG_PRINTF("lit id=%u, groups=0x%016llx\n", id, tl->groups);
if (do_group_check && !(tl->groups & tctxt->groups)) {
DEBUG_PRINTF("IGNORE: none of this literal's groups are set.\n");
return HWLM_CONTINUE_MATCHING;
}
assert(!in_delay_play || !tl->delay_mask);
if (!in_delay_play) {
pushDelayedMatches(tl, end, tctxt);
}
if (end < t->floatingMinLiteralMatchOffset) {
DEBUG_PRINTF("too soon\n");
assert(!in_delay_play); /* should not have been enqueued */
/* continuing on may result in pushing global time back */
return HWLM_CONTINUE_MATCHING;
}
int work_done = 0;
if (tl->programOffset) {
DEBUG_PRINTF("running program at %u\n", tl->programOffset);
if (roseRunProgram(t, tl->programOffset, end, tctxt, 0, &work_done) ==
HWLM_TERMINATE_MATCHING) {
return HWLM_TERMINATE_MATCHING;
}
}
// If we've actually handled any roles, we might need to apply this
// literal's squash mask to our groups as well.
if (work_done && tl->squashesGroup) {
roseSquashGroup(tctxt, tl);
}
return HWLM_CONTINUE_MATCHING;
}
static never_inline
hwlmcb_rv_t roseProcessDelayedMatch(const struct RoseEngine *t, u64a end, u32 id,
struct RoseContext *tctxt) {
return roseProcessMatch_i(t, end, id, tctxt, 1, 1, 0);
const u32 *programs = getByOffset(t, t->litProgramOffset);
return roseRunProgram(t, programs[id], end, match_len, tctxt, 0);
}
static never_inline
hwlmcb_rv_t roseProcessDelayedAnchoredMatch(const struct RoseEngine *t, u64a end,
u32 id, struct RoseContext *tctxt) {
return roseProcessMatch_i(t, end, id, tctxt, 0, 0, 1);
hwlmcb_rv_t roseProcessDelayedMatch(const struct RoseEngine *t, u64a end,
u32 id, struct RoseContext *tctxt) {
size_t match_len = 0;
return roseProcessMatch_i(t, end, match_len, id, tctxt, 1, 0);
}
static never_inline
hwlmcb_rv_t roseProcessDelayedAnchoredMatch(const struct RoseEngine *t,
u64a end, u32 id,
struct RoseContext *tctxt) {
size_t match_len = 0;
return roseProcessMatch_i(t, end, match_len, id, tctxt, 0, 1);
}
static really_inline
hwlmcb_rv_t roseProcessMainMatch(const struct RoseEngine *t, u64a end, u32 id,
hwlmcb_rv_t roseProcessMainMatch(const struct RoseEngine *t, u64a end,
size_t match_len, u32 id,
struct RoseContext *tctxt) {
return roseProcessMatch_i(t, end, id, tctxt, 1, 0, 0);
return roseProcessMatch_i(t, end, match_len, id, tctxt, 0, 0);
}
static rose_inline
@ -839,11 +687,6 @@ hwlmcb_rv_t roseCallback(size_t start, size_t end, u32 id, void *ctxt) {
return HWLM_TERMINATE_MATCHING;
}
if (id < tctx->t->nonbenefits_base_id
&& !roseCheckLiteralBenefits(real_end, end - start + 1, id, tctx)) {
return tctx->groups;
}
hwlmcb_rv_t rv = flushQueuedLiterals(tctx, real_end);
/* flushDelayed may have advanced tctx->lastEndOffset */
@ -856,7 +699,8 @@ hwlmcb_rv_t roseCallback(size_t start, size_t end, u32 id, void *ctxt) {
return HWLM_TERMINATE_MATCHING;
}
rv = roseProcessMainMatch(tctx->t, real_end, id, tctx);
size_t match_len = end - start + 1;
rv = roseProcessMainMatch(tctx->t, real_end, match_len, id, tctx);
DEBUG_PRINTF("DONE groups=0x%016llx\n", tctx->groups);

View File

@ -1,5 +1,5 @@
/*
* Copyright (c) 2015, Intel Corporation
* Copyright (c) 2015-2016, Intel Corporation
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
@ -41,9 +41,108 @@
#include "runtime.h"
#include "scratch.h"
#include "ue2common.h"
#include "util/compare.h"
#include "util/fatbit.h"
#include "util/multibit.h"
static rose_inline
int roseCheckBenefits(struct RoseContext *tctxt, u64a end, u32 mask_rewind,
const u8 *and_mask, const u8 *exp_mask) {
DEBUG_PRINTF("am offset = %zu, em offset = %zu\n",
and_mask - (const u8 *)tctxt->t,
exp_mask - (const u8 *)tctxt->t);
const u8 *data;
// If the check works over part of the history and part of the buffer, we
// create a temporary copy of the data in here so it's contiguous.
u8 temp[MAX_MASK2_WIDTH];
struct core_info *ci = &tctxtToScratch(tctxt)->core_info;
s64a buffer_offset = (s64a)end - ci->buf_offset;
DEBUG_PRINTF("rel offset %lld\n", buffer_offset);
if (buffer_offset >= mask_rewind) {
data = ci->buf + buffer_offset - mask_rewind;
DEBUG_PRINTF("all in one case data=%p buf=%p rewind=%u\n", data,
ci->buf, mask_rewind);
} else if (buffer_offset <= 0) {
data = ci->hbuf + ci->hlen + buffer_offset - mask_rewind;
DEBUG_PRINTF("all in one case data=%p buf=%p rewind=%u\n", data,
ci->buf, mask_rewind);
} else {
u32 shortfall = mask_rewind - buffer_offset;
DEBUG_PRINTF("shortfall of %u, rewind %u hlen %zu\n", shortfall,
mask_rewind, ci->hlen);
data = temp;
memcpy(temp, ci->hbuf + ci->hlen - shortfall, shortfall);
memcpy(temp + shortfall, ci->buf, mask_rewind - shortfall);
}
#ifdef DEBUG
DEBUG_PRINTF("DATA: ");
for (u32 i = 0; i < mask_rewind; i++) {
printf("%c", ourisprint(data[i]) ? data[i] : '?');
}
printf(" (len=%u)\n", mask_rewind);
#endif
u32 len = mask_rewind;
while (len >= sizeof(u64a)) {
u64a a = unaligned_load_u64a(data);
a &= *(const u64a *)and_mask;
if (a != *(const u64a *)exp_mask) {
DEBUG_PRINTF("argh %016llx %016llx\n", a, *(const u64a *)exp_mask);
return 0;
}
data += sizeof(u64a);
and_mask += sizeof(u64a);
exp_mask += sizeof(u64a);
len -= sizeof(u64a);
}
while (len) {
u8 a = *data;
a &= *and_mask;
if (a != *exp_mask) {
DEBUG_PRINTF("argh d%02hhx =%02hhx am%02hhx em%02hhx\n", a,
*data, *and_mask, *exp_mask);
return 0;
}
data++;
and_mask++;
exp_mask++;
len--;
}
return 1;
}
static rose_inline
void rosePushDelayedMatch(const struct RoseEngine *t, u32 delay,
u32 delay_index, u64a offset,
struct RoseContext *tctxt) {
assert(delay);
const u32 src_slot_index = delay;
u32 slot_index = (src_slot_index + offset) & DELAY_MASK;
if (offset + src_slot_index <= tctxt->delayLastEndOffset) {
DEBUG_PRINTF("skip too late\n");
return;
}
const u32 delay_count = t->delay_count;
u8 *slot = getDelaySlots(tctxtToScratch(tctxt)) +
(t->delay_slot_size * slot_index);
DEBUG_PRINTF("pushing tab %u into slot %u\n", delay_index, slot_index);
if (!(tctxt->filledDelayedSlots & (1U << slot_index))) {
tctxt->filledDelayedSlots |= 1U << slot_index;
mmbit_clear(slot, delay_count);
}
mmbit_set(slot, delay_count, delay_index);
}
static rose_inline
char rosePrefixCheckMiracles(const struct RoseEngine *t,
const struct LeftNfaInfo *left,
@ -782,10 +881,10 @@ char roseCheckRootBounds(u64a end, u32 min_bound, u32 max_bound) {
break; \
}
static really_inline
static rose_inline
hwlmcb_rv_t roseRunProgram(const struct RoseEngine *t, u32 programOffset,
u64a end, struct RoseContext *tctxt,
char in_anchored, int *work_done) {
u64a end, size_t match_len,
struct RoseContext *tctxt, char in_anchored) {
DEBUG_PRINTF("program begins at offset %u\n", programOffset);
assert(programOffset);
@ -800,6 +899,10 @@ hwlmcb_rv_t roseRunProgram(const struct RoseEngine *t, u32 programOffset,
// and SPARSE_ITER_NEXT instructions.
struct mmbit_sparse_state si_state[MAX_SPARSE_ITER_STATES];
// If this program has an effect, work_done will be set to one (which may
// allow the program to squash groups).
int work_done = 0;
assert(*(const u8 *)pc != ROSE_INSTR_END);
for (;;) {
@ -812,7 +915,7 @@ hwlmcb_rv_t roseRunProgram(const struct RoseEngine *t, u32 programOffset,
if (in_anchored && end > t->floatingMinLiteralMatchOffset) {
DEBUG_PRINTF("delay until playback\n");
tctxt->groups |= ri->groups;
*work_done = 1;
work_done = 1;
assert(ri->done_jump); // must progress
pc += ri->done_jump;
continue;
@ -820,6 +923,35 @@ hwlmcb_rv_t roseRunProgram(const struct RoseEngine *t, u32 programOffset,
}
PROGRAM_NEXT_INSTRUCTION
PROGRAM_CASE(CHECK_LIT_MASK) {
assert(match_len);
if (!roseCheckBenefits(tctxt, end, match_len, ri->and_mask.a8,
ri->cmp_mask.a8)) {
DEBUG_PRINTF("halt: failed mask check\n");
return HWLM_CONTINUE_MATCHING;
}
}
PROGRAM_NEXT_INSTRUCTION
PROGRAM_CASE(CHECK_LIT_EARLY) {
if (end < t->floatingMinLiteralMatchOffset) {
DEBUG_PRINTF("halt: too soon, min offset=%u\n",
t->floatingMinLiteralMatchOffset);
return HWLM_CONTINUE_MATCHING;
}
}
PROGRAM_NEXT_INSTRUCTION
PROGRAM_CASE(CHECK_GROUPS) {
DEBUG_PRINTF("groups=0x%llx, checking instr groups=0x%llx\n",
tctxt->groups, ri->groups);
if (!(ri->groups & tctxt->groups)) {
DEBUG_PRINTF("halt: no groups are set\n");
return HWLM_CONTINUE_MATCHING;
}
}
PROGRAM_NEXT_INSTRUCTION
PROGRAM_CASE(CHECK_ONLY_EOD) {
struct core_info *ci = &tctxtToScratch(tctxt)->core_info;
if (end != ci->buf_offset + ci->len) {
@ -874,6 +1006,11 @@ hwlmcb_rv_t roseRunProgram(const struct RoseEngine *t, u32 programOffset,
}
PROGRAM_NEXT_INSTRUCTION
PROGRAM_CASE(PUSH_DELAYED) {
rosePushDelayedMatch(t, ri->delay, ri->index, end, tctxt);
}
PROGRAM_NEXT_INSTRUCTION
PROGRAM_CASE(SOM_ADJUST) {
assert(ri->distance <= end);
som = end - ri->distance;
@ -890,7 +1027,7 @@ hwlmcb_rv_t roseRunProgram(const struct RoseEngine *t, u32 programOffset,
PROGRAM_CASE(TRIGGER_INFIX) {
roseTriggerInfix(t, som, end, ri->queue, ri->event, ri->cancel,
tctxt);
*work_done = 1;
work_done = 1;
}
PROGRAM_NEXT_INSTRUCTION
@ -900,7 +1037,7 @@ hwlmcb_rv_t roseRunProgram(const struct RoseEngine *t, u32 programOffset,
HWLM_TERMINATE_MATCHING) {
return HWLM_TERMINATE_MATCHING;
}
*work_done = 1;
work_done = 1;
}
PROGRAM_NEXT_INSTRUCTION
@ -909,7 +1046,7 @@ hwlmcb_rv_t roseRunProgram(const struct RoseEngine *t, u32 programOffset,
in_anchored) == HWLM_TERMINATE_MATCHING) {
return HWLM_TERMINATE_MATCHING;
}
*work_done = 1;
work_done = 1;
}
PROGRAM_NEXT_INSTRUCTION
@ -919,7 +1056,7 @@ hwlmcb_rv_t roseRunProgram(const struct RoseEngine *t, u32 programOffset,
HWLM_TERMINATE_MATCHING) {
return HWLM_TERMINATE_MATCHING;
}
*work_done = 1;
work_done = 1;
}
PROGRAM_NEXT_INSTRUCTION
@ -928,7 +1065,7 @@ hwlmcb_rv_t roseRunProgram(const struct RoseEngine *t, u32 programOffset,
MO_HALT_MATCHING) {
return HWLM_TERMINATE_MATCHING;
}
*work_done = 1;
work_done = 1;
}
PROGRAM_NEXT_INSTRUCTION
@ -937,7 +1074,7 @@ hwlmcb_rv_t roseRunProgram(const struct RoseEngine *t, u32 programOffset,
in_anchored) == HWLM_TERMINATE_MATCHING) {
return HWLM_TERMINATE_MATCHING;
}
*work_done = 1;
work_done = 1;
}
PROGRAM_NEXT_INSTRUCTION
@ -947,7 +1084,7 @@ hwlmcb_rv_t roseRunProgram(const struct RoseEngine *t, u32 programOffset,
in_anchored) == HWLM_TERMINATE_MATCHING) {
return HWLM_TERMINATE_MATCHING;
}
*work_done = 1;
work_done = 1;
}
PROGRAM_NEXT_INSTRUCTION
@ -957,7 +1094,7 @@ hwlmcb_rv_t roseRunProgram(const struct RoseEngine *t, u32 programOffset,
HWLM_TERMINATE_MATCHING) {
return HWLM_TERMINATE_MATCHING;
}
*work_done = 1;
work_done = 1;
}
PROGRAM_NEXT_INSTRUCTION
@ -965,7 +1102,7 @@ hwlmcb_rv_t roseRunProgram(const struct RoseEngine *t, u32 programOffset,
DEBUG_PRINTF("set state index %u\n", ri->index);
mmbit_set(getRoleState(tctxt->state), t->rolesWithStateCount,
ri->index);
*work_done = 1;
work_done = 1;
}
PROGRAM_NEXT_INSTRUCTION
@ -976,6 +1113,28 @@ hwlmcb_rv_t roseRunProgram(const struct RoseEngine *t, u32 programOffset,
}
PROGRAM_NEXT_INSTRUCTION
PROGRAM_CASE(SQUASH_GROUPS) {
assert(popcount64(ri->groups) == 63); // Squash only one group.
if (work_done) {
tctxt->groups &= ri->groups;
DEBUG_PRINTF("squash groups 0x%llx -> 0x%llx\n", ri->groups,
tctxt->groups);
}
}
PROGRAM_NEXT_INSTRUCTION
PROGRAM_CASE(CHECK_STATE) {
DEBUG_PRINTF("check state %u\n", ri->index);
if (!mmbit_isset(getRoleState(tctxt->state),
t->rolesWithStateCount, ri->index)) {
DEBUG_PRINTF("state not on\n");
assert(ri->fail_jump); // must progress
pc += ri->fail_jump;
continue;
}
}
PROGRAM_NEXT_INSTRUCTION
PROGRAM_CASE(SPARSE_ITER_BEGIN) {
DEBUG_PRINTF("iter_offset=%u\n", ri->iter_offset);
const struct mmbit_sparse_iter *it =
@ -1045,17 +1204,4 @@ hwlmcb_rv_t roseRunProgram(const struct RoseEngine *t, u32 programOffset,
#undef PROGRAM_CASE
#undef PROGRAM_NEXT_INSTRUCTION
static rose_inline
void roseSquashGroup(struct RoseContext *tctxt, const struct RoseLiteral *tl) {
assert(tl->squashesGroup);
// we should be squashing a single group
assert(popcount64(tl->groups) == 1);
DEBUG_PRINTF("apply squash mask 0x%016llx, groups 0x%016llx -> 0x%016llx\n",
~tl->groups, tctxt->groups, tctxt->groups & ~tl->groups);
tctxt->groups &= ~tl->groups;
}
#endif // PROGRAM_RUNTIME_H

View File

@ -1,5 +1,5 @@
/*
* Copyright (c) 2015, Intel Corporation
* Copyright (c) 2015-2016, Intel Corporation
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
@ -170,12 +170,16 @@ public:
const void *get() const {
switch (code()) {
case ROSE_INSTR_CHECK_LIT_MASK: return &u.checkLitMask;
case ROSE_INSTR_CHECK_LIT_EARLY: return &u.checkLitEarly;
case ROSE_INSTR_CHECK_GROUPS: return &u.checkGroups;
case ROSE_INSTR_CHECK_ONLY_EOD: return &u.checkOnlyEod;
case ROSE_INSTR_CHECK_BOUNDS: return &u.checkBounds;
case ROSE_INSTR_CHECK_NOT_HANDLED: return &u.checkNotHandled;
case ROSE_INSTR_CHECK_LOOKAROUND: return &u.checkLookaround;
case ROSE_INSTR_CHECK_LEFTFIX: return &u.checkLeftfix;
case ROSE_INSTR_ANCHORED_DELAY: return &u.anchoredDelay;
case ROSE_INSTR_PUSH_DELAYED: return &u.pushDelayed;
case ROSE_INSTR_SOM_ADJUST: return &u.somAdjust;
case ROSE_INSTR_SOM_LEFTFIX: return &u.somLeftfix;
case ROSE_INSTR_TRIGGER_INFIX: return &u.triggerInfix;
@ -188,6 +192,8 @@ public:
case ROSE_INSTR_REPORT_SOM_KNOWN: return &u.reportSomKnown;
case ROSE_INSTR_SET_STATE: return &u.setState;
case ROSE_INSTR_SET_GROUPS: return &u.setGroups;
case ROSE_INSTR_SQUASH_GROUPS: return &u.squashGroups;
case ROSE_INSTR_CHECK_STATE: return &u.checkState;
case ROSE_INSTR_SPARSE_ITER_BEGIN: return &u.sparseIterBegin;
case ROSE_INSTR_SPARSE_ITER_NEXT: return &u.sparseIterNext;
case ROSE_INSTR_END: return &u.end;
@ -198,12 +204,16 @@ public:
size_t length() const {
switch (code()) {
case ROSE_INSTR_CHECK_LIT_MASK: return sizeof(u.checkLitMask);
case ROSE_INSTR_CHECK_LIT_EARLY: return sizeof(u.checkLitEarly);
case ROSE_INSTR_CHECK_GROUPS: return sizeof(u.checkGroups);
case ROSE_INSTR_CHECK_ONLY_EOD: return sizeof(u.checkOnlyEod);
case ROSE_INSTR_CHECK_BOUNDS: return sizeof(u.checkBounds);
case ROSE_INSTR_CHECK_NOT_HANDLED: return sizeof(u.checkNotHandled);
case ROSE_INSTR_CHECK_LOOKAROUND: return sizeof(u.checkLookaround);
case ROSE_INSTR_CHECK_LEFTFIX: return sizeof(u.checkLeftfix);
case ROSE_INSTR_ANCHORED_DELAY: return sizeof(u.anchoredDelay);
case ROSE_INSTR_PUSH_DELAYED: return sizeof(u.pushDelayed);
case ROSE_INSTR_SOM_ADJUST: return sizeof(u.somAdjust);
case ROSE_INSTR_SOM_LEFTFIX: return sizeof(u.somLeftfix);
case ROSE_INSTR_TRIGGER_INFIX: return sizeof(u.triggerInfix);
@ -216,6 +226,8 @@ public:
case ROSE_INSTR_REPORT_SOM_KNOWN: return sizeof(u.reportSomKnown);
case ROSE_INSTR_SET_STATE: return sizeof(u.setState);
case ROSE_INSTR_SET_GROUPS: return sizeof(u.setGroups);
case ROSE_INSTR_SQUASH_GROUPS: return sizeof(u.squashGroups);
case ROSE_INSTR_CHECK_STATE: return sizeof(u.checkState);
case ROSE_INSTR_SPARSE_ITER_BEGIN: return sizeof(u.sparseIterBegin);
case ROSE_INSTR_SPARSE_ITER_NEXT: return sizeof(u.sparseIterNext);
case ROSE_INSTR_END: return sizeof(u.end);
@ -224,12 +236,16 @@ public:
}
union {
ROSE_STRUCT_CHECK_LIT_MASK checkLitMask;
ROSE_STRUCT_CHECK_LIT_EARLY checkLitEarly;
ROSE_STRUCT_CHECK_GROUPS checkGroups;
ROSE_STRUCT_CHECK_ONLY_EOD checkOnlyEod;
ROSE_STRUCT_CHECK_BOUNDS checkBounds;
ROSE_STRUCT_CHECK_NOT_HANDLED checkNotHandled;
ROSE_STRUCT_CHECK_LOOKAROUND checkLookaround;
ROSE_STRUCT_CHECK_LEFTFIX checkLeftfix;
ROSE_STRUCT_ANCHORED_DELAY anchoredDelay;
ROSE_STRUCT_PUSH_DELAYED pushDelayed;
ROSE_STRUCT_SOM_ADJUST somAdjust;
ROSE_STRUCT_SOM_LEFTFIX somLeftfix;
ROSE_STRUCT_TRIGGER_INFIX triggerInfix;
@ -242,12 +258,25 @@ public:
ROSE_STRUCT_REPORT_SOM_KNOWN reportSomKnown;
ROSE_STRUCT_SET_STATE setState;
ROSE_STRUCT_SET_GROUPS setGroups;
ROSE_STRUCT_SQUASH_GROUPS squashGroups;
ROSE_STRUCT_CHECK_STATE checkState;
ROSE_STRUCT_SPARSE_ITER_BEGIN sparseIterBegin;
ROSE_STRUCT_SPARSE_ITER_NEXT sparseIterNext;
ROSE_STRUCT_END end;
} u;
};
static
size_t hash_value(const RoseInstruction &ri) {
size_t val = 0;
const char *bytes = (const char *)ri.get();
const size_t len = ri.length();
for (size_t i = 0; i < len; i++) {
boost::hash_combine(val, bytes[i]);
}
return val;
}
struct build_context : boost::noncopyable {
/** \brief information about engines to the left of a vertex */
map<RoseVertex, left_build_info> leftfix_info;
@ -270,6 +299,10 @@ struct build_context : boost::noncopyable {
* up iterators in early misc. */
map<vector<mmbit_sparse_iter>, u32> iterCache;
/** \brief Simple cache of programs written to engine blob, used for
* deduplication. */
ue2::unordered_map<vector<RoseInstruction>, u32> program_cache;
/** \brief LookEntry list cache, so that we don't have to go scanning
* through the full list to find cases we've used already. */
ue2::unordered_map<vector<LookEntry>, size_t> lookaround_cache;
@ -284,6 +317,9 @@ struct build_context : boost::noncopyable {
* that have already been pushed into the engine_blob. */
ue2::unordered_map<u32, u32> engineOffsets;
/** \brief Minimum offset of a match from the floating table. */
u32 floatingMinLiteralMatchOffset = 0;
/** \brief Contents of the Rose bytecode immediately following the
* RoseEngine. */
vector<char, AlignedAllocator<char, 64>> engine_blob;
@ -1453,31 +1489,6 @@ void updateNfaState(const build_context &bc, RoseStateOffsets *so,
}
}
static
void buildLitBenefits(const RoseBuildImpl &tbi, RoseEngine *engine,
u32 base_lits_benefits_offset) {
lit_benefits *lba = (lit_benefits *)((char *)engine
+ base_lits_benefits_offset);
DEBUG_PRINTF("base offset %u\n", base_lits_benefits_offset);
for (u32 i = 0; i < tbi.nonbenefits_base_id; i++) {
assert(contains(tbi.final_id_to_literal, i));
assert(tbi.final_id_to_literal.at(i).size() == 1);
u32 lit_id = *tbi.final_id_to_literal.at(i).begin();
const ue2_literal &s = tbi.literals.right.at(lit_id).s;
DEBUG_PRINTF("building mask for lit %u (fid %u) %s\n", lit_id, i,
dumpString(s).c_str());
assert(s.length() <= MAX_MASK2_WIDTH);
u32 j = 0;
for (const auto &e : s) {
lba[i].and_mask.a8[j] = e.nocase ? 0 : CASE_BIT;
lba[i].expected.e8[j] = e.nocase ? 0 : (CASE_BIT & e.c);
DEBUG_PRINTF("a%02hhx e%02hhx\n", lba[i].and_mask.a8[j],
lba[i].expected.e8[j]);
j++;
}
}
}
/* does not include history requirements for outfixes or literal matchers */
u32 RoseBuildImpl::calcHistoryRequired() const {
u32 m = cc.grey.minHistoryAvailable;
@ -2232,11 +2243,11 @@ void enforceEngineSizeLimit(const NFA *n, const size_t nfa_size, const Grey &gre
}
static
u32 findMinFloatingLiteralMatch(const RoseBuildImpl &tbi) {
const RoseGraph &g = tbi.g;
u32 findMinFloatingLiteralMatch(const RoseBuildImpl &build) {
const RoseGraph &g = build.g;
u32 minWidth = ROSE_BOUND_INF;
for (auto v : vertices_range(g)) {
if (tbi.isAnchored(v) || tbi.isVirtualVertex(v)) {
if (build.isAnchored(v) || build.isVirtualVertex(v)) {
DEBUG_PRINTF("skipping %zu anchored or root\n", g[v].idx);
continue;
}
@ -2656,12 +2667,21 @@ flattenProgram(const vector<vector<RoseInstruction>> &programs) {
}
static
u32 writeProgram(build_context &bc, vector<RoseInstruction> &program) {
u32 writeProgram(build_context &bc, const vector<RoseInstruction> &program) {
if (program.empty()) {
DEBUG_PRINTF("no program\n");
return 0;
}
assert(program.back().code() == ROSE_INSTR_END);
assert(program.size() >= 1);
auto it = bc.program_cache.find(program);
if (it != end(bc.program_cache)) {
DEBUG_PRINTF("reusing cached program at %u\n", it->second);
return it->second;
}
DEBUG_PRINTF("writing %zu instructions\n", program.size());
u32 programOffset = 0;
for (const auto &ri : program) {
@ -2674,6 +2694,7 @@ u32 writeProgram(build_context &bc, vector<RoseInstruction> &program) {
}
}
DEBUG_PRINTF("program begins at offset %u\n", programOffset);
bc.program_cache.emplace(program, programOffset);
return programOffset;
}
@ -2764,72 +2785,6 @@ bool hasBoundaryReports(const BoundaryReports &boundary) {
return false;
}
static
void createLiteralEntry(const RoseBuildImpl &tbi, build_context &bc,
vector<RoseLiteral> &literalTable) {
const u32 final_id = verify_u32(literalTable.size());
assert(contains(tbi.final_id_to_literal, final_id));
const UNUSED u32 literalId = *tbi.final_id_to_literal.at(final_id).begin();
/* all literal ids associated with this final id should result in identical
* literal entry */
const auto &lit_infos = getLiteralInfoByFinalId(tbi, final_id);
const rose_literal_info &arb_lit_info = **lit_infos.begin();
literalTable.push_back(RoseLiteral());
RoseLiteral &tl = literalTable.back();
memset(&tl, 0, sizeof(tl));
tl.groups = 0;
for (const auto &li : lit_infos) {
tl.groups |= li->group_mask;
}
assert(tl.groups || tbi.literals.right.at(literalId).table == ROSE_ANCHORED
|| tbi.literals.right.at(literalId).table == ROSE_EVENT);
// If this literal squashes its group behind it, store that data too
tl.squashesGroup = arb_lit_info.squash_group;
// Setup the delay stuff
const auto &children = arb_lit_info.delayed_ids;
if (children.empty()) {
tl.delay_mask = 0;
tl.delayIdsOffset = ROSE_OFFSET_INVALID;
} else {
map<u32, u32> local_delay_map; // delay -> relative child id
for (const auto &int_id : children) {
const rose_literal_id &child_literal = tbi.literals.right.at(int_id);
u32 child_id = tbi.literal_info[int_id].final_id;
u32 delay_index = child_id - tbi.delay_base_id;
tl.delay_mask |= 1U << child_literal.delay;
local_delay_map[child_literal.delay] = delay_index;
}
vector<u32> delayIds;
for (const auto &did : local_delay_map | map_values) {
delayIds.push_back(did);
}
tl.delayIdsOffset = add_to_engine_blob(bc, delayIds.begin(),
delayIds.end());
}
assert(!tbi.literals.right.at(literalId).delay || !tl.delay_mask);
}
// Construct the literal table.
static
void buildLiteralTable(const RoseBuildImpl &tbi, build_context &bc,
vector<RoseLiteral> &literalTable) {
size_t numLiterals = tbi.final_id_to_literal.size();
literalTable.reserve(numLiterals);
for (size_t i = 0; i < numLiterals; ++i) {
createLiteralEntry(tbi, bc, literalTable);
}
}
/**
* \brief True if the given vertex is a role that can only be switched on at
* EOD.
@ -2945,8 +2900,11 @@ void makeRoleAnchoredDelay(RoseBuildImpl &build, UNUSED build_context &bc,
return;
}
// TODO: also limit to matches that can occur after
// floatingMinLiteralMatchOffset.
// If this match cannot occur after floatingMinLiteralMatchOffset, we do
// not need this check.
if (build.g[v].max_offset <= bc.floatingMinLiteralMatchOffset) {
return;
}
auto ri = RoseInstruction(ROSE_INSTR_ANCHORED_DELAY);
ri.u.anchoredDelay.groups = build.g[v].groups;
@ -3112,6 +3070,13 @@ void makeRoleCheckBounds(const RoseBuildImpl &build, RoseVertex v,
const RoseGraph &g = build.g;
const RoseVertex u = source(e, g);
// We know that we can trust the anchored table (DFA) to always deliver us
// literals at the correct offset.
if (build.isAnchored(v)) {
DEBUG_PRINTF("literal in anchored table, skipping bounds check\n");
return;
}
// Use the minimum literal length.
u32 lit_length = g[v].eod_accept ? 0 : verify_u32(build.minLiteralLen(v));
@ -3347,97 +3312,171 @@ vector<RoseInstruction> makePredProgram(RoseBuildImpl &build, build_context &bc,
return program;
}
static
u32 addPredBlocksSingle(
map<u32, vector<vector<RoseInstruction>>> &predProgramLists,
u32 curr_offset, vector<RoseInstruction> &program) {
assert(predProgramLists.size() == 1);
u32 pred_state = predProgramLists.begin()->first;
auto subprog = flattenProgram(predProgramLists.begin()->second);
// Check our pred state.
auto ri = RoseInstruction(ROSE_INSTR_CHECK_STATE);
ri.u.checkState.index = pred_state;
program.push_back(ri);
curr_offset += ROUNDUP_N(program.back().length(), ROSE_INSTR_MIN_ALIGN);
// Add subprogram.
for (const auto &ri : subprog) {
program.push_back(ri);
curr_offset += ROUNDUP_N(ri.length(), ROSE_INSTR_MIN_ALIGN);
}
const u32 end_offset =
curr_offset - ROUNDUP_N(program.back().length(), ROSE_INSTR_MIN_ALIGN);
// Fix up the instruction operands.
curr_offset = 0;
for (size_t i = 0; i < program.size(); i++) {
auto &ri = program[i];
switch (ri.code()) {
case ROSE_INSTR_CHECK_STATE:
ri.u.checkState.fail_jump = end_offset - curr_offset;
break;
default:
break;
}
curr_offset += ROUNDUP_N(ri.length(), ROSE_INSTR_MIN_ALIGN);
}
return 0; // No iterator.
}
static
u32 addPredBlocksMulti(build_context &bc,
map<u32, vector<vector<RoseInstruction>>> &predProgramLists,
u32 curr_offset, vector<RoseInstruction> &program) {
assert(!predProgramLists.empty());
// First, add the iterator itself.
vector<u32> keys;
for (const auto &elem : predProgramLists) {
keys.push_back(elem.first);
}
DEBUG_PRINTF("%zu keys: %s\n", keys.size(), as_string_list(keys).c_str());
vector<mmbit_sparse_iter> iter;
mmbBuildSparseIterator(iter, keys, bc.numStates);
assert(!iter.empty());
u32 iter_offset = addIteratorToTable(bc, iter);
// Construct our program, starting with the SPARSE_ITER_BEGIN
// instruction, keeping track of the jump offset for each sub-program.
vector<u32> jump_table;
program.push_back(RoseInstruction(ROSE_INSTR_SPARSE_ITER_BEGIN));
curr_offset += ROUNDUP_N(program.back().length(), ROSE_INSTR_MIN_ALIGN);
for (const auto &e : predProgramLists) {
DEBUG_PRINTF("subprogram %zu has offset %u\n", jump_table.size(),
curr_offset);
jump_table.push_back(curr_offset);
auto subprog = flattenProgram(e.second);
if (e.first != keys.back()) {
// For all but the last subprogram, replace the END instruction
// with a SPARSE_ITER_NEXT.
assert(!subprog.empty());
assert(subprog.back().code() == ROSE_INSTR_END);
subprog.back() = RoseInstruction(ROSE_INSTR_SPARSE_ITER_NEXT);
}
for (const auto &ri : subprog) {
program.push_back(ri);
curr_offset += ROUNDUP_N(ri.length(), ROSE_INSTR_MIN_ALIGN);
}
}
const u32 end_offset =
curr_offset - ROUNDUP_N(program.back().length(), ROSE_INSTR_MIN_ALIGN);
// Write the jump table into the bytecode.
const u32 jump_table_offset =
add_to_engine_blob(bc, begin(jump_table), end(jump_table));
// Fix up the instruction operands.
auto keys_it = begin(keys);
curr_offset = 0;
for (size_t i = 0; i < program.size(); i++) {
auto &ri = program[i];
switch (ri.code()) {
case ROSE_INSTR_SPARSE_ITER_BEGIN:
ri.u.sparseIterBegin.iter_offset = iter_offset;
ri.u.sparseIterBegin.jump_table = jump_table_offset;
ri.u.sparseIterBegin.fail_jump = end_offset - curr_offset;
break;
case ROSE_INSTR_SPARSE_ITER_NEXT:
ri.u.sparseIterNext.iter_offset = iter_offset;
ri.u.sparseIterNext.jump_table = jump_table_offset;
assert(keys_it != end(keys));
ri.u.sparseIterNext.state = *keys_it++;
ri.u.sparseIterNext.fail_jump = end_offset - curr_offset;
break;
default:
break;
}
curr_offset += ROUNDUP_N(ri.length(), ROSE_INSTR_MIN_ALIGN);
}
return iter_offset;
}
static
u32 addPredBlocks(build_context &bc,
map<u32, vector<vector<RoseInstruction>>> &predProgramLists,
u32 curr_offset, vector<RoseInstruction> &program,
bool force_sparse_iter) {
const size_t num_preds = predProgramLists.size();
if (num_preds == 0) {
program = flattenProgram({program});
return 0; // No iterator.
} else if (!force_sparse_iter && num_preds == 1) {
return addPredBlocksSingle(predProgramLists, curr_offset, program);
} else {
return addPredBlocksMulti(bc, predProgramLists, curr_offset, program);
}
}
/**
* Returns the pair (program offset, sparse iter offset).
*/
static
pair<u32, u32> makeSparseIterProgram(build_context &bc,
map<u32, vector<vector<RoseInstruction>>> &predProgramLists,
const vector<RoseInstruction> &root_program) {
const vector<RoseInstruction> &root_program,
const vector<RoseInstruction> &pre_program) {
vector<RoseInstruction> program;
u32 iter_offset = 0;
u32 curr_offset = 0;
if (!predProgramLists.empty()) {
// First, add the iterator itself.
vector<u32> keys;
for (const auto &elem : predProgramLists) {
keys.push_back(elem.first);
}
DEBUG_PRINTF("%zu keys: %s\n", keys.size(),
as_string_list(keys).c_str());
vector<mmbit_sparse_iter> iter;
mmbBuildSparseIterator(iter, keys, bc.numStates);
assert(!iter.empty());
iter_offset = addIteratorToTable(bc, iter);
// Construct our program, starting with the SPARSE_ITER_BEGIN
// instruction, keeping track of the jump offset for each sub-program.
vector<u32> jump_table;
u32 curr_offset = 0;
program.push_back(RoseInstruction(ROSE_INSTR_SPARSE_ITER_BEGIN));
curr_offset += ROUNDUP_N(program.back().length(), ROSE_INSTR_MIN_ALIGN);
for (const auto &e : predProgramLists) {
DEBUG_PRINTF("subprogram %zu has offset %u\n", jump_table.size(),
curr_offset);
jump_table.push_back(curr_offset);
auto subprog = flattenProgram(e.second);
if (e.first != keys.back()) {
// For all but the last subprogram, replace the END instruction
// with a SPARSE_ITER_NEXT.
assert(!subprog.empty());
assert(subprog.back().code() == ROSE_INSTR_END);
subprog.back() = RoseInstruction(ROSE_INSTR_SPARSE_ITER_NEXT);
}
for (const auto &ri : subprog) {
program.push_back(ri);
curr_offset += ROUNDUP_N(ri.length(), ROSE_INSTR_MIN_ALIGN);
}
}
const u32 end_offset = curr_offset - ROUNDUP_N(program.back().length(),
ROSE_INSTR_MIN_ALIGN);
// Write the jump table into the bytecode.
const u32 jump_table_offset =
add_to_engine_blob(bc, begin(jump_table), end(jump_table));
// Fix up the instruction operands.
auto keys_it = begin(keys);
curr_offset = 0;
for (size_t i = 0; i < program.size(); i++) {
auto &ri = program[i];
switch (ri.code()) {
case ROSE_INSTR_SPARSE_ITER_BEGIN:
ri.u.sparseIterBegin.iter_offset = iter_offset;
ri.u.sparseIterBegin.jump_table = jump_table_offset;
ri.u.sparseIterBegin.fail_jump = end_offset - curr_offset;
break;
case ROSE_INSTR_SPARSE_ITER_NEXT:
ri.u.sparseIterNext.iter_offset = iter_offset;
ri.u.sparseIterNext.jump_table = jump_table_offset;
assert(keys_it != end(keys));
ri.u.sparseIterNext.state = *keys_it++;
ri.u.sparseIterNext.fail_jump = end_offset - curr_offset;
break;
default:
break;
}
curr_offset += ROUNDUP_N(ri.length(), ROSE_INSTR_MIN_ALIGN);
}
// Add pre-program first.
for (const auto &ri : pre_program) {
program.push_back(ri);
curr_offset += ROUNDUP_N(ri.length(), ROSE_INSTR_MIN_ALIGN);
}
// Add blocks to deal with non-root edges (triggered by sparse iterator or
// mmbit_isset checks). This operation will flatten the program up to this
// point.
u32 iter_offset =
addPredBlocks(bc, predProgramLists, curr_offset, program, false);
// If we have a root program, replace the END instruction with it. Note
// that the root program has already been flattened.
assert(!program.empty());
assert(program.back().code() == ROSE_INSTR_END);
if (!root_program.empty()) {
if (!program.empty()) {
assert(program.back().code() == ROSE_INSTR_END);
program.pop_back();
}
program.pop_back();
program.insert(end(program), begin(root_program), end(root_program));
}
@ -3445,15 +3484,182 @@ pair<u32, u32> makeSparseIterProgram(build_context &bc,
}
static
u32 buildLiteralProgram(RoseBuildImpl &build, build_context &bc,
void makePushDelayedInstructions(const RoseBuildImpl &build, u32 final_id,
vector<RoseInstruction> &program) {
const auto &lit_infos = getLiteralInfoByFinalId(build, final_id);
const auto &arb_lit_info = **lit_infos.begin();
if (arb_lit_info.delayed_ids.empty()) {
return;
}
for (const auto &int_id : arb_lit_info.delayed_ids) {
const auto &child_literal = build.literals.right.at(int_id);
u32 child_id = build.literal_info[int_id].final_id;
u32 delay_index = child_id - build.delay_base_id;
DEBUG_PRINTF("final_id=%u delay=%u child_id=%u\n", final_id,
child_literal.delay, child_id);
auto ri = RoseInstruction(ROSE_INSTR_PUSH_DELAYED);
ri.u.pushDelayed.delay = verify_u8(child_literal.delay);
ri.u.pushDelayed.index = delay_index;
program.push_back(move(ri));
}
}
static
void makeGroupCheckInstruction(const RoseBuildImpl &build, u32 final_id,
vector<RoseInstruction> &program) {
assert(contains(build.final_id_to_literal, final_id));
const auto &lit_infos = getLiteralInfoByFinalId(build, final_id);
rose_group groups = 0;
for (const auto &li : lit_infos) {
groups |= li->group_mask;
}
if (!groups) {
return;
}
auto ri = RoseInstruction(ROSE_INSTR_CHECK_GROUPS);
ri.u.checkGroups.groups = groups;
program.push_back(move(ri));
}
static
void makeCheckLitMaskInstruction(const RoseBuildImpl &build, u32 final_id,
vector<RoseInstruction> &program) {
assert(contains(build.final_id_to_literal, final_id));
const auto &lit_infos = getLiteralInfoByFinalId(build, final_id);
assert(!lit_infos.empty());
if (!lit_infos.front()->requires_benefits) {
return;
}
auto ri = RoseInstruction(ROSE_INSTR_CHECK_LIT_MASK);
assert(build.final_id_to_literal.at(final_id).size() == 1);
u32 lit_id = *build.final_id_to_literal.at(final_id).begin();
const ue2_literal &s = build.literals.right.at(lit_id).s;
DEBUG_PRINTF("building mask for lit %u (final id %u) %s\n", lit_id,
final_id, dumpString(s).c_str());
assert(s.length() <= MAX_MASK2_WIDTH);
u32 i = 0;
for (const auto &e : s) {
ri.u.checkLitMask.and_mask.a8[i] = e.nocase ? 0 : CASE_BIT;
ri.u.checkLitMask.cmp_mask.a8[i] = e.nocase ? 0 : (CASE_BIT & e.c);
i++;
}
program.push_back(move(ri));
}
static
void makeGroupSquashInstruction(const RoseBuildImpl &build, u32 final_id,
vector<RoseInstruction> &program) {
assert(contains(build.final_id_to_literal, final_id));
const auto &lit_infos = getLiteralInfoByFinalId(build, final_id);
if (!lit_infos.front()->squash_group) {
return;
}
rose_group groups = 0;
for (const auto &li : lit_infos) {
groups |= li->group_mask;
}
if (!groups) {
return;
}
DEBUG_PRINTF("final_id %u squashes 0x%llx\n", final_id, groups);
auto ri = RoseInstruction(ROSE_INSTR_SQUASH_GROUPS);
ri.u.squashGroups.groups = ~groups; // Negated, so we can just AND it in.
program.push_back(move(ri));
}
static
void makeCheckLitEarlyInstruction(const RoseBuildImpl &build, build_context &bc,
u32 final_id,
const vector<RoseEdge> &lit_edges,
vector<RoseInstruction> &program) {
if (lit_edges.empty()) {
return;
}
if (bc.floatingMinLiteralMatchOffset == 0) {
return;
}
RoseVertex v = target(lit_edges.front(), build.g);
if (!build.isFloating(v)) {
return;
}
const auto &lit_ids = build.final_id_to_literal.at(final_id);
if (lit_ids.empty()) {
return;
}
size_t min_offset = SIZE_MAX;
for (u32 lit_id : lit_ids) {
const auto &lit = build.literals.right.at(lit_id);
min_offset = min(min_offset, lit.elength());
}
DEBUG_PRINTF("%zu lits, min_offset=%zu\n", lit_ids.size(), min_offset);
// If we can't match before the min offset, we don't need the check.
if (min_offset >= bc.floatingMinLiteralMatchOffset) {
DEBUG_PRINTF("no need for check, min is %u\n",
bc.floatingMinLiteralMatchOffset);
return;
}
program.push_back(RoseInstruction(ROSE_INSTR_CHECK_LIT_EARLY));
}
static
vector<RoseInstruction> buildLitInitialProgram(RoseBuildImpl &build,
build_context &bc, u32 final_id,
const vector<RoseEdge> &lit_edges) {
vector<RoseInstruction> pre_program;
// No initial program for EOD.
if (final_id == MO_INVALID_IDX) {
return pre_program;
}
DEBUG_PRINTF("final_id %u\n", final_id);
// Check lit mask.
makeCheckLitMaskInstruction(build, final_id, pre_program);
// Check literal groups.
makeGroupCheckInstruction(build, final_id, pre_program);
// Add instructions for pushing delayed matches, if there are any.
makePushDelayedInstructions(build, final_id, pre_program);
// Add pre-check for early literals in the floating table.
makeCheckLitEarlyInstruction(build, bc, final_id, lit_edges, pre_program);
return pre_program;
}
static
u32 buildLiteralProgram(RoseBuildImpl &build, build_context &bc, u32 final_id,
const vector<RoseEdge> &lit_edges) {
const auto &g = build.g;
DEBUG_PRINTF("%zu lit edges\n", lit_edges.size());
DEBUG_PRINTF("final id %u, %zu lit edges\n", final_id, lit_edges.size());
// pred state id -> list of programs
map<u32, vector<vector<RoseInstruction>>> predProgramLists;
vector<RoseVertex> nonroot_verts;
// Construct sparse iter sub-programs.
for (const auto &e : lit_edges) {
@ -3467,7 +3673,6 @@ u32 buildLiteralProgram(RoseBuildImpl &build, build_context &bc,
u32 pred_state = bc.roleStateIndices.at(u);
auto program = makePredProgram(build, bc, e);
predProgramLists[pred_state].push_back(program);
nonroot_verts.push_back(target(e, g));
}
// Construct sub-program for handling root roles.
@ -3485,13 +3690,39 @@ u32 buildLiteralProgram(RoseBuildImpl &build, build_context &bc,
root_programs.push_back(role_prog);
}
// Literal may squash groups.
if (final_id != MO_INVALID_IDX) {
root_programs.push_back({});
makeGroupSquashInstruction(build, final_id, root_programs.back());
}
vector<RoseInstruction> root_program;
if (!root_programs.empty()) {
root_program = flattenProgram(root_programs);
}
auto pre_program = buildLitInitialProgram(build, bc, final_id, lit_edges);
// Put it all together.
return makeSparseIterProgram(bc, predProgramLists, root_program).first;
return makeSparseIterProgram(bc, predProgramLists, root_program,
pre_program).first;
}
static
u32 buildDelayRebuildProgram(RoseBuildImpl &build, build_context &bc,
u32 final_id) {
const auto &lit_infos = getLiteralInfoByFinalId(build, final_id);
const auto &arb_lit_info = **lit_infos.begin();
if (arb_lit_info.delayed_ids.empty()) {
return 0; // No delayed IDs, no work to do.
}
vector<RoseInstruction> program;
makeCheckLitMaskInstruction(build, final_id, program);
makePushDelayedInstructions(build, final_id, program);
assert(!program.empty());
program = flattenProgram({program});
return writeProgram(bc, program);
}
static
@ -3530,17 +3761,35 @@ map<u32, vector<RoseEdge>> findEdgesByLiteral(const RoseBuildImpl &build) {
return lit_edge_map;
}
/** \brief Build the interpreter program for each literal. */
/**
* \brief Build the interpreter programs for each literal.
*
* Returns the base of the literal program list and the base of the delay
* rebuild program list.
*/
static
void buildLiteralPrograms(RoseBuildImpl &build, build_context &bc,
vector<RoseLiteral> &literalTable) {
pair<u32, u32> buildLiteralPrograms(RoseBuildImpl &build, build_context &bc) {
const u32 num_literals = build.final_id_to_literal.size();
auto lit_edge_map = findEdgesByLiteral(build);
for (u32 finalId = 0; finalId != literalTable.size(); ++finalId) {
vector<u32> litPrograms(num_literals);
vector<u32> delayRebuildPrograms(num_literals);
for (u32 finalId = 0; finalId != num_literals; ++finalId) {
const auto &lit_edges = lit_edge_map[finalId];
u32 offset = buildLiteralProgram(build, bc, lit_edges);
literalTable[finalId].programOffset = offset;
litPrograms[finalId] =
buildLiteralProgram(build, bc, finalId, lit_edges);
delayRebuildPrograms[finalId] =
buildDelayRebuildProgram(build, bc, finalId);
}
u32 litProgramsOffset =
add_to_engine_blob(bc, begin(litPrograms), end(litPrograms));
u32 delayRebuildProgramsOffset = add_to_engine_blob(
bc, begin(delayRebuildPrograms), end(delayRebuildPrograms));
return {litProgramsOffset, delayRebuildProgramsOffset};
}
static
@ -3604,7 +3853,14 @@ pair<u32, u32> buildEodAnchorProgram(RoseBuildImpl &build, build_context &bc) {
return {0, 0};
}
return makeSparseIterProgram(bc, predProgramLists, {});
vector<RoseInstruction> program;
// Note: we force the use of a sparse iterator for the EOD program so we
// can easily guard EOD execution at runtime.
u32 iter_offset = addPredBlocks(bc, predProgramLists, 0, program, true);
assert(program.size() > 1);
return {writeProgram(bc, program), iter_offset};
}
static
@ -3634,7 +3890,7 @@ u32 writeEodProgram(RoseBuildImpl &build, build_context &bc) {
tie(g[source(b, g)].idx, g[target(b, g)].idx);
});
return buildLiteralProgram(build, bc, edge_list);
return buildLiteralProgram(build, bc, MO_INVALID_IDX, edge_list);
}
static
@ -3780,6 +4036,7 @@ aligned_unique_ptr<RoseEngine> RoseBuildImpl::buildFinalEngine(u32 minWidth) {
aligned_unique_ptr<HWLM> sbtable = buildSmallBlockMatcher(*this, &sbsize);
build_context bc;
bc.floatingMinLiteralMatchOffset = findMinFloatingLiteralMatch(*this);
// Build NFAs
set<u32> no_retrigger_queues;
@ -3805,10 +4062,6 @@ aligned_unique_ptr<RoseEngine> RoseBuildImpl::buildFinalEngine(u32 minWidth) {
throw ResourceLimitError();
}
u32 lit_benefits_size =
verify_u32(sizeof(lit_benefits) * nonbenefits_base_id);
assert(ISALIGNED_16(lit_benefits_size));
vector<u32> suffixEkeyLists;
buildSuffixEkeyLists(*this, bc, qif, &suffixEkeyLists);
@ -3820,9 +4073,10 @@ aligned_unique_ptr<RoseEngine> RoseBuildImpl::buildFinalEngine(u32 minWidth) {
queue_count - leftfixBeginQueue, leftInfoTable,
&laggedRoseCount, &historyRequired);
vector<RoseLiteral> literalTable;
buildLiteralTable(*this, bc, literalTable);
buildLiteralPrograms(*this, bc, literalTable);
u32 litProgramOffset;
u32 litDelayRebuildProgramOffset;
tie(litProgramOffset, litDelayRebuildProgramOffset) =
buildLiteralPrograms(*this, bc);
u32 eodProgramOffset = writeEodProgram(*this, bc);
u32 eodIterProgramOffset;
@ -3857,10 +4111,6 @@ aligned_unique_ptr<RoseEngine> RoseBuildImpl::buildFinalEngine(u32 minWidth) {
currOffset = ROUNDUP_CL(currOffset);
DEBUG_PRINTF("currOffset %u\n", currOffset);
/* leave space for the benefits listing */
u32 base_lits_benefits_offset = currOffset;
currOffset += lit_benefits_size;
if (atable) {
currOffset = ROUNDUP_CL(currOffset);
amatcherOffset = currOffset;
@ -3891,10 +4141,6 @@ aligned_unique_ptr<RoseEngine> RoseBuildImpl::buildFinalEngine(u32 minWidth) {
u32 intReportOffset = currOffset;
currOffset += sizeof(internal_report) * int_reports.size();
u32 literalOffset = ROUNDUP_N(currOffset, alignof(RoseLiteral));
u32 literalLen = sizeof(RoseLiteral) * literalTable.size();
currOffset = literalOffset + literalLen;
u32 leftOffset = ROUNDUP_N(currOffset, alignof(LeftNfaInfo));
u32 roseLen = sizeof(LeftNfaInfo) * leftInfoTable.size();
currOffset = leftOffset + roseLen;
@ -4016,8 +4262,9 @@ aligned_unique_ptr<RoseEngine> RoseBuildImpl::buildFinalEngine(u32 minWidth) {
fillInReportInfo(engine.get(), intReportOffset, rm, int_reports);
engine->literalOffset = literalOffset;
engine->literalCount = verify_u32(literalTable.size());
engine->literalCount = verify_u32(final_id_to_literal.size());
engine->litProgramOffset = litProgramOffset;
engine->litDelayRebuildProgramOffset = litDelayRebuildProgramOffset;
engine->runtimeImpl = pickRuntimeImpl(*this, outfixEndQueue);
engine->mpvTriggeredByLeaf = anyEndfixMpvTriggers(*this);
@ -4053,14 +4300,12 @@ aligned_unique_ptr<RoseEngine> RoseBuildImpl::buildFinalEngine(u32 minWidth) {
engine->lastByteHistoryIterOffset = lastByteOffset;
u32 delay_count = verify_u32(literalTable.size() - delay_base_id);
u32 delay_count = verify_u32(final_id_to_literal.size() - delay_base_id);
engine->delay_count = delay_count;
engine->delay_slot_size = mmbit_size(delay_count);
engine->delay_base_id = delay_base_id;
engine->anchored_base_id = anchored_base_id;
engine->anchored_count = delay_base_id - anchored_base_id;
engine->nonbenefits_base_id = nonbenefits_base_id;
engine->literalBenefitsOffsets = base_lits_benefits_offset;
engine->rosePrefixCount = rosePrefixCount;
@ -4094,7 +4339,7 @@ aligned_unique_ptr<RoseEngine> RoseBuildImpl::buildFinalEngine(u32 minWidth) {
engine->minWidth = hasBoundaryReports(boundary) ? 0 : minWidth;
engine->minWidthExcludingBoundaries = minWidth;
engine->maxSafeAnchoredDROffset = findMinWidth(*this, ROSE_FLOATING);
engine->floatingMinLiteralMatchOffset = findMinFloatingLiteralMatch(*this);
engine->floatingMinLiteralMatchOffset = bc.floatingMinLiteralMatchOffset;
engine->maxBiAnchoredWidth = findMaxBAWidth(*this);
engine->noFloatingRoots = hasNoFloatingRoots();
@ -4109,7 +4354,7 @@ aligned_unique_ptr<RoseEngine> RoseBuildImpl::buildFinalEngine(u32 minWidth) {
fillMatcherDistances(*this, engine.get());
engine->initialGroups = getInitialGroups();
engine->totalNumLiterals = verify_u32(literalTable.size());
engine->totalNumLiterals = verify_u32(literal_info.size());
engine->asize = verify_u32(asize);
engine->ematcherRegionSize = ematcher_region_size;
engine->floatingStreamState = verify_u32(floatingStreamStateRequired);
@ -4138,12 +4383,8 @@ aligned_unique_ptr<RoseEngine> RoseBuildImpl::buildFinalEngine(u32 minWidth) {
&engine->scratchStateSize, &engine->nfaStateSize,
&engine->tStateSize);
/* do after update mask */
buildLitBenefits(*this, engine.get(), base_lits_benefits_offset);
// Copy in other tables
copy_bytes(ptr + bc.engine_blob_base, bc.engine_blob);
copy_bytes(ptr + engine->literalOffset, literalTable);
copy_bytes(ptr + engine->leftOffset, leftInfoTable);
fillLookaroundTables(ptr + lookaroundTableOffset,

View File

@ -1,5 +1,5 @@
/*
* Copyright (c) 2015, Intel Corporation
* Copyright (c) 2015-2016, Intel Corporation
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
@ -258,7 +258,6 @@ void allocateFinalLiteralId(RoseBuildImpl &tbi) {
set<u32> anch;
set<u32> norm;
set<u32> norm_benefits;
set<u32> delay;
/* undelayed ids come first */
@ -281,12 +280,8 @@ void allocateFinalLiteralId(RoseBuildImpl &tbi) {
continue;
}
const rose_literal_info &info = tbi.literal_info[i];
if (info.requires_benefits) {
assert(!tbi.isDelayed(i));
norm_benefits.insert(i);
DEBUG_PRINTF("%u has benefits\n", i);
} else if (tbi.isDelayed(i)) {
if (tbi.isDelayed(i)) {
assert(!tbi.literal_info[i].requires_benefits);
delay.insert(i);
} else if (tbi.literals.right.at(i).table == ROSE_ANCHORED) {
anch.insert(i);
@ -295,12 +290,7 @@ void allocateFinalLiteralId(RoseBuildImpl &tbi) {
}
}
/* normal lits first (with benefits confirm)*/
allocateFinalIdToSet(g, norm_benefits, &tbi.literal_info,
&tbi.final_id_to_literal, &next_final_id);
/* other normal lits (without benefits)*/
tbi.nonbenefits_base_id = next_final_id;
/* normal lits */
allocateFinalIdToSet(g, norm, &tbi.literal_info, &tbi.final_id_to_literal,
&next_final_id);

View File

@ -1,5 +1,5 @@
/*
* Copyright (c) 2015, Intel Corporation
* Copyright (c) 2015-2016, Intel Corporation
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
@ -496,7 +496,6 @@ public:
u32 anchored_base_id;
u32 nonbenefits_base_id;
u32 ematcher_region_size; /**< number of bytes the eod table runs over */
/** \brief Mapping from anchored literal ID to the original literal suffix

View File

@ -1,5 +1,5 @@
/*
* Copyright (c) 2015, Intel Corporation
* Copyright (c) 2015-2016, Intel Corporation
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
@ -78,7 +78,6 @@ RoseBuildImpl::RoseBuildImpl(ReportManager &rm_in, SomSlotManager &ssm_in,
group_weak_end(0),
group_end(0),
anchored_base_id(MO_INVALID_IDX),
nonbenefits_base_id(MO_INVALID_IDX),
ematcher_region_size(0),
floating_direct_report(false),
eod_event_literal_id(MO_INVALID_IDX),

View File

@ -1,5 +1,5 @@
/*
* Copyright (c) 2015, Intel Corporation
* Copyright (c) 2015-2016, Intel Corporation
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
@ -36,21 +36,6 @@
namespace ue2 {
// Calculate the minimum depth for the given set of vertices, ignoring those
// with depth 1.
template<class Cont>
static
u8 calcMinDepth(const std::map<RoseVertex, u32> &depths, const Cont &verts) {
u8 d = 255;
for (RoseVertex v : verts) {
u8 vdepth = (u8)std::min((u32)255, depths.at(v));
if (vdepth > 1) {
d = std::min(d, vdepth);
}
}
return d;
}
// Comparator for vertices using their index property.
struct VertexIndexComp {
VertexIndexComp(const RoseGraph &gg) : g(gg) {}

View File

@ -1,5 +1,5 @@
/*
* Copyright (c) 2015, Intel Corporation
* Copyright (c) 2015-2016, Intel Corporation
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
@ -116,31 +116,6 @@ const HWLM *getSmallBlockMatcher(const RoseEngine *t) {
return (const HWLM *)loadFromByteCodeOffset(t, t->sbmatcherOffset);
}
static
u32 literalsWithDirectReports(const RoseEngine *t) {
return t->totalNumLiterals - t->literalCount;
}
template<typename Predicate>
static
size_t literalsWithPredicate(const RoseEngine *t, Predicate pred) {
const RoseLiteral *tl = getLiteralTable(t);
const RoseLiteral *tl_end = tl + t->literalCount;
return count_if(tl, tl_end, pred);
}
static
size_t literalsInGroups(const RoseEngine *t, u32 from, u32 to) {
rose_group mask = ~((1ULL << from) - 1);
if (to < 64) {
mask &= ((1ULL << to) - 1);
}
return literalsWithPredicate(
t, [&mask](const RoseLiteral &l) { return l.groups & mask; });
}
static
CharReach bitvectorToReach(const u8 *reach) {
CharReach cr;
@ -177,6 +152,16 @@ void dumpLookaround(ofstream &os, const RoseEngine *t,
}
}
static
string dumpStrMask(const u8 *mask, size_t len) {
ostringstream oss;
for (size_t i = 0; i < len; i++) {
oss << std::hex << std::setw(2) << std::setfill('0') << u32{mask[i]}
<< " ";
}
return oss.str();
}
#define PROGRAM_CASE(name) \
case ROSE_INSTR_##name: { \
os << " " << std::setw(4) << std::setfill('0') << (pc - pc_base) \
@ -202,14 +187,26 @@ void dumpProgram(ofstream &os, const RoseEngine *t, const char *pc) {
}
PROGRAM_NEXT_INSTRUCTION
PROGRAM_CASE(CHECK_ONLY_EOD) {
os << " fail_jump +" << ri->fail_jump << endl;
PROGRAM_CASE(CHECK_LIT_MASK) {
os << " and_mask "
<< dumpStrMask(ri->and_mask.a8, sizeof(ri->and_mask.a8))
<< endl;
os << " cmp_mask "
<< dumpStrMask(ri->cmp_mask.a8, sizeof(ri->cmp_mask.a8))
<< endl;
}
PROGRAM_NEXT_INSTRUCTION
PROGRAM_CASE(CHECK_BOUNDS) {
os << " min_bound " << ri->min_bound << endl;
os << " max_bound " << ri->max_bound << endl;
PROGRAM_CASE(CHECK_LIT_EARLY) {}
PROGRAM_NEXT_INSTRUCTION
PROGRAM_CASE(CHECK_GROUPS) {
os << " groups 0x" << std::hex << ri->groups << std::dec
<< endl;
}
PROGRAM_NEXT_INSTRUCTION
PROGRAM_CASE(CHECK_ONLY_EOD) {
os << " fail_jump +" << ri->fail_jump << endl;
}
PROGRAM_NEXT_INSTRUCTION
@ -236,6 +233,12 @@ void dumpProgram(ofstream &os, const RoseEngine *t, const char *pc) {
}
PROGRAM_NEXT_INSTRUCTION
PROGRAM_CASE(PUSH_DELAYED) {
os << " delay " << u32{ri->delay} << endl;
os << " index " << ri->index << endl;
}
PROGRAM_NEXT_INSTRUCTION
PROGRAM_CASE(SOM_ADJUST) {
os << " distance " << ri->distance << endl;
}
@ -301,6 +304,18 @@ void dumpProgram(ofstream &os, const RoseEngine *t, const char *pc) {
}
PROGRAM_NEXT_INSTRUCTION
PROGRAM_CASE(SQUASH_GROUPS) {
os << " groups 0x" << std::hex << ri->groups << std::dec
<< endl;
}
PROGRAM_NEXT_INSTRUCTION
PROGRAM_CASE(CHECK_STATE) {
os << " index " << ri->index << endl;
os << " fail_jump +" << ri->fail_jump << endl;
}
PROGRAM_NEXT_INSTRUCTION
PROGRAM_CASE(SPARSE_ITER_BEGIN) {
os << " iter_offset " << ri->iter_offset << endl;
os << " jump_table " << ri->jump_table << endl;
@ -334,21 +349,32 @@ static
void dumpRoseLitPrograms(const RoseEngine *t, const string &filename) {
ofstream os(filename);
const RoseLiteral *lits = getLiteralTable(t);
const char *base = (const char *)t;
const u32 *litPrograms =
(const u32 *)loadFromByteCodeOffset(t, t->litProgramOffset);
const u32 *delayRebuildPrograms =
(const u32 *)loadFromByteCodeOffset(t, t->litDelayRebuildProgramOffset);
for (u32 i = 0; i < t->literalCount; i++) {
const RoseLiteral *lit = &lits[i];
os << "Literal " << i << endl;
os << "---------------" << endl;
if (lit->programOffset) {
os << "Program @ " << lit->programOffset << ":" << endl;
dumpProgram(os, t, base + lit->programOffset);
if (litPrograms[i]) {
os << "Program @ " << litPrograms[i] << ":" << endl;
const char *prog =
(const char *)loadFromByteCodeOffset(t, litPrograms[i]);
dumpProgram(os, t, prog);
} else {
os << "<No Program>" << endl;
}
if (delayRebuildPrograms[i]) {
os << "Delay Rebuild Program @ " << delayRebuildPrograms[i] << ":"
<< endl;
const char *prog = (const char *)loadFromByteCodeOffset(
t, delayRebuildPrograms[i]);
dumpProgram(os, t, prog);
}
os << endl;
}
@ -710,8 +736,6 @@ void roseDumpText(const RoseEngine *t, FILE *f) {
etable ? hwlmSize(etable) : 0, t->ematcherRegionSize);
fprintf(f, " - small-blk matcher : %zu bytes over %u bytes\n",
sbtable ? hwlmSize(sbtable) : 0, t->smallBlockDistance);
fprintf(f, " - literal table : %zu bytes\n",
t->literalCount * sizeof(RoseLiteral));
fprintf(f, " - role state table : %zu bytes\n",
t->rolesWithStateCount * sizeof(u32));
fprintf(f, " - nfa info table : %u bytes\n",
@ -745,22 +769,9 @@ void roseDumpText(const RoseEngine *t, FILE *f) {
fprintf(f, "handled key count : %u\n", t->handledKeyCount);
fprintf(f, "\n");
fprintf(f, "number of literals : %u\n", t->totalNumLiterals);
fprintf(f, " - delayed : %u\n", t->delay_count);
fprintf(f, " - direct report : %u\n",
literalsWithDirectReports(t));
fprintf(f, " - that squash group : %zu\n",
literalsWithPredicate(
t, [](const RoseLiteral &l) { return l.squashesGroup != 0; }));
fprintf(f, " - with benefits : %u\n", t->nonbenefits_base_id);
fprintf(f, " - with program : %zu\n",
literalsWithPredicate(
t, [](const RoseLiteral &l) { return l.programOffset != 0; }));
fprintf(f, " - in groups ::\n");
fprintf(f, " + weak : %zu\n",
literalsInGroups(t, 0, t->group_weak_end));
fprintf(f, " + general : %zu\n",
literalsInGroups(t, t->group_weak_end, sizeof(u64a) * 8));
fprintf(f, "total literal count : %u\n", t->totalNumLiterals);
fprintf(f, " prog table size : %u\n", t->literalCount);
fprintf(f, " delayed literals : %u\n", t->delay_count);
fprintf(f, "\n");
fprintf(f, " minWidth : %u\n", t->minWidth);
@ -839,7 +850,8 @@ void roseDumpStructRaw(const RoseEngine *t, FILE *f) {
DUMP_U32(t, fmatcherMaxBiAnchoredWidth);
DUMP_U32(t, intReportOffset);
DUMP_U32(t, intReportCount);
DUMP_U32(t, literalOffset);
DUMP_U32(t, litProgramOffset);
DUMP_U32(t, litDelayRebuildProgramOffset);
DUMP_U32(t, literalCount);
DUMP_U32(t, multidirectOffset);
DUMP_U32(t, activeArrayCount);
@ -876,7 +888,6 @@ void roseDumpStructRaw(const RoseEngine *t, FILE *f) {
DUMP_U32(t, delay_base_id);
DUMP_U32(t, anchored_count);
DUMP_U32(t, anchored_base_id);
DUMP_U32(t, nonbenefits_base_id);
DUMP_U32(t, maxFloatingDelayedMatch);
DUMP_U32(t, delayRebuildLength);
DUMP_U32(t, stateOffsets.history);
@ -905,7 +916,6 @@ void roseDumpStructRaw(const RoseEngine *t, FILE *f) {
DUMP_U32(t, rosePrefixCount);
DUMP_U32(t, activeLeftIterOffset);
DUMP_U32(t, ematcherRegionSize);
DUMP_U32(t, literalBenefitsOffsets);
DUMP_U32(t, somRevCount);
DUMP_U32(t, somRevOffsetOffset);
DUMP_U32(t, group_weak_end);

View File

@ -1,5 +1,5 @@
/*
* Copyright (c) 2015, Intel Corporation
* Copyright (c) 2015-2016, Intel Corporation
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
@ -73,43 +73,11 @@ ReportID literalToReport(u32 id) {
return id & ~LITERAL_DR_FLAG;
}
/** \brief Structure representing a literal. */
struct RoseLiteral {
/**
* \brief Program to run when this literal is seen.
*
* Offset is relative to RoseEngine, or zero for no program.
*/
u32 programOffset;
/** \brief Bitset of groups that cause this literal to fire. */
rose_group groups;
/**
* \brief True if this literal switches off its group behind it when it
* sets a role.
*/
u8 squashesGroup;
/**
* \brief Bitset which indicates that the literal inserts a delayed
* match at the given offset.
*/
u32 delay_mask;
/** \brief Offset to array of ids to poke in the delay structure. */
u32 delayIdsOffset;
};
/* Allocation of Rose literal ids
*
* The rose literal id space is segmented:
*
* ---- 0
* | | Normal undelayed literals in the e, or f tables which require a
* | | manual benefits confirm on match [a table never requires benefits]
* | |
* ---- nonbenefits_base_id
* | | 'Normal' undelayed literals in either e or f tables
* | |
* | |
@ -127,7 +95,7 @@ struct RoseLiteral {
* ---- LITERAL_DR_FLAG
* | | Direct Report literals: immediately raise an internal report with id
* | | given by (lit_id & ~LITERAL_DR_FLAG). Raised by a or f tables (or e??).
* | | No RoseLiteral structure
* | | No literal programs.
* | |
* | |
* ----
@ -135,14 +103,15 @@ struct RoseLiteral {
/* Rose Literal Sources
*
* Rose currently gets events (mainly roseProcessMatch calls) from 8 sources:
* Rose currently gets events (mainly roseProcessMatch calls) from a number of
* sources:
* 1) The floating table
* 2) The anchored table
* 3) Delayed literals
* 4) suffixes NFAs
* 5) masksv2 (literals with benefits)
* 6) End anchored table
* 7) prefix / infix nfas
* 4) Suffix NFAs
* 5) Literal masks
* 5) End anchored table
* 6) Prefix / Infix nfas
*
* Care is required to ensure that events appear to come into Rose in order
* (or sufficiently ordered for Rose to cope). Generally the progress of the
@ -165,7 +134,7 @@ struct RoseLiteral {
* NFA queues are run to the current point (floating or delayed literal) as
* appropriate.
*
* Maskv2:
* Literal Masks:
* These are triggered from either floating literals or delayed literals and
* inspect the data behind them. Matches are raised at the same location as the
* trigger literal so there are no ordering issues. Masks are always pure
@ -301,12 +270,12 @@ struct RoseStateOffsets {
};
struct RoseBoundaryReports {
u32 reportEodOffset; /**< 0 if no reports lits, otherwise offset of
u32 reportEodOffset; /**< 0 if no reports list, otherwise offset of
* MO_INVALID_IDX terminated list to report at EOD */
u32 reportZeroOffset; /**< 0 if no reports lits, otherwise offset of
u32 reportZeroOffset; /**< 0 if no reports list, otherwise offset of
* MO_INVALID_IDX terminated list to report at offset
* 0 */
u32 reportZeroEodOffset; /**< 0 if no reports lits, otherwise offset of
u32 reportZeroEodOffset; /**< 0 if no reports list, otherwise offset of
* MO_INVALID_IDX terminated list to report if eod
* is at offset 0. Superset of other lists. */
};
@ -338,18 +307,20 @@ struct RoseBoundaryReports {
#define ROSE_RUNTIME_PURE_LITERAL 1
#define ROSE_RUNTIME_SINGLE_OUTFIX 2
// Runtime structure header for Rose.
// In memory, we follow this with:
// 1a. anchored 'literal' matcher table
// 1b. floating literal matcher table
// 1c. eod-anchored literal matcher table
// 1d. small block table
// 2. array of RoseLiteral (literalCount entries)
// 8. array of NFA offsets, one per queue
// 9. array of state offsets, one per queue (+)
// 10. array of role ids for the set of all root roles
// 12. multi-direct report array
/*
/**
* \brief Runtime structure header for Rose.
*
* Runtime structure header for Rose.
* In memory, we follow this with:
* -# the "engine blob"
* -# anchored 'literal' matcher table
* -# floating literal matcher table
* -# eod-anchored literal matcher table
* -# small block table
* -# array of NFA offsets, one per queue
* -# array of state offsets, one per queue (+)
* -# multi-direct report array
*
* (+) stateOffset array note: Offsets in the array are either into the stream
* state (normal case) or into the tstate region of scratch (for transient rose
* nfas). Rose nfa info table can distinguish the cases.
@ -407,8 +378,22 @@ struct RoseEngine {
* with the anchored table. */
u32 intReportOffset; /**< offset of array of internal_report structures */
u32 intReportCount; /**< number of internal_report structures */
u32 literalOffset; // offset of RoseLiteral array (bytes)
u32 literalCount; // number of RoseLiteral entries [NOT number of literals]
/** \brief Offset of u32 array of program offsets for literals. */
u32 litProgramOffset;
/** \brief Offset of u32 array of delay rebuild program offsets for
* literals. */
u32 litDelayRebuildProgramOffset;
/**
* \brief Number of entries in the arrays pointed to by litProgramOffset,
* litDelayRebuildProgramOffset.
*
* Note: NOT the total number of literals.
*/
u32 literalCount;
u32 multidirectOffset; /**< offset of multi-direct report list. */
u32 activeArrayCount; //number of nfas tracked in the active array
u32 activeLeftCount; //number of nfas tracked in the active rose array
@ -468,8 +453,6 @@ struct RoseEngine {
u32 anchored_count; /* number of anchored literal ids */
u32 anchored_base_id; /* literal id of the first literal in the A table.
* anchored literal ids are contiguous */
u32 nonbenefits_base_id; /* first literal id without benefit conf.
* contiguous, blah, blah */
u32 maxFloatingDelayedMatch; /* max offset that a delayed literal can
* usefully be reported */
u32 delayRebuildLength; /* length of the history region which needs to be
@ -486,8 +469,6 @@ struct RoseEngine {
u32 rosePrefixCount; /* number of rose prefixes */
u32 activeLeftIterOffset; /* mmbit_sparse_iter over non-transient roses */
u32 ematcherRegionSize; /* max region size to pass to ematcher */
u32 literalBenefitsOffsets; /* offset to array of benefits indexed by lit
id */
u32 somRevCount; /**< number of som reverse nfas */
u32 somRevOffsetOffset; /**< offset to array of offsets to som rev nfas */
u32 group_weak_end; /* end of weak groups, debugging only */
@ -496,17 +477,6 @@ struct RoseEngine {
struct scatter_full_plan state_init;
};
struct lit_benefits {
union {
u64a a64[MAX_MASK2_WIDTH/sizeof(u64a)];
u8 a8[MAX_MASK2_WIDTH];
} and_mask;
union {
u64a e64[MAX_MASK2_WIDTH/sizeof(u64a)];
u8 e8[MAX_MASK2_WIDTH];
} expected;
};
#if defined(_WIN32)
#pragma pack(push, 1)
#endif
@ -574,14 +544,6 @@ const void *getSBLiteralMatcher(const struct RoseEngine *t) {
return matcher;
}
static really_inline
const struct RoseLiteral *getLiteralTable(const struct RoseEngine *t) {
const struct RoseLiteral *tl
= (const struct RoseLiteral *)((const char *)t + t->literalOffset);
assert(ISALIGNED_N(tl, 4));
return tl;
}
static really_inline
const struct LeftNfaInfo *getLeftTable(const struct RoseEngine *t) {
const struct LeftNfaInfo *r
@ -601,13 +563,6 @@ const struct mmbit_sparse_iter *getActiveLeftIter(const struct RoseEngine *t) {
return it;
}
static really_inline
const struct lit_benefits *getLiteralBenefitsTable(
const struct RoseEngine *t) {
return (const struct lit_benefits *)
((const char *)t + t->literalBenefitsOffsets);
}
static really_inline
const struct NfaInfo *getNfaInfoByQueue(const struct RoseEngine *t, u32 qi) {
const struct NfaInfo *infos

View File

@ -1,5 +1,5 @@
/*
* Copyright (c) 2015, Intel Corporation
* Copyright (c) 2015-2016, Intel Corporation
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
@ -42,11 +42,15 @@
/** \brief Role program instruction opcodes. */
enum RoseInstructionCode {
ROSE_INSTR_ANCHORED_DELAY, //!< Delay until after anchored matcher.
ROSE_INSTR_CHECK_LIT_MASK, //!< Check and/cmp mask.
ROSE_INSTR_CHECK_LIT_EARLY, //!< Skip matches before floating min offset.
ROSE_INSTR_CHECK_GROUPS, //!< Check that literal groups are on.
ROSE_INSTR_CHECK_ONLY_EOD, //!< Role matches only at EOD.
ROSE_INSTR_CHECK_BOUNDS, //!< Bounds on distance from offset 0.
ROSE_INSTR_CHECK_NOT_HANDLED, //!< Test & set role in "handled".
ROSE_INSTR_CHECK_LOOKAROUND, //!< Lookaround check.
ROSE_INSTR_CHECK_LEFTFIX, //!< Leftfix must be in accept state.
ROSE_INSTR_PUSH_DELAYED, //!< Push delayed literal matches.
ROSE_INSTR_SOM_ADJUST, //!< Set SOM from a distance to EOM.
ROSE_INSTR_SOM_LEFTFIX, //!< Acquire SOM from a leftfix engine.
ROSE_INSTR_TRIGGER_INFIX, //!< Trigger an infix engine.
@ -59,6 +63,8 @@ enum RoseInstructionCode {
ROSE_INSTR_REPORT_SOM_KNOWN, //!< Rose role knows its SOM offset.
ROSE_INSTR_SET_STATE, //!< Switch a state index on.
ROSE_INSTR_SET_GROUPS, //!< Set some literal group bits.
ROSE_INSTR_SQUASH_GROUPS, //!< Conditionally turn off some groups.
ROSE_INSTR_CHECK_STATE, //!< Test a single bit in the state multibit.
ROSE_INSTR_SPARSE_ITER_BEGIN, //!< Begin running a sparse iter over states.
ROSE_INSTR_SPARSE_ITER_NEXT, //!< Continue running sparse iter over states.
ROSE_INSTR_END //!< End of program.
@ -70,6 +76,29 @@ struct ROSE_STRUCT_ANCHORED_DELAY {
u32 done_jump; //!< Jump forward this many bytes if successful.
};
union RoseLiteralMask {
u64a a64[MAX_MASK2_WIDTH / sizeof(u64a)];
u8 a8[MAX_MASK2_WIDTH];
};
/** Note: check failure will halt program. */
struct ROSE_STRUCT_CHECK_LIT_MASK {
u8 code; //!< From enum RoseInstructionCode.
union RoseLiteralMask and_mask;
union RoseLiteralMask cmp_mask;
};
/** Note: check failure will halt program. */
struct ROSE_STRUCT_CHECK_LIT_EARLY {
u8 code; //!< From enum RoseInstructionCode.
};
/** Note: check failure will halt program. */
struct ROSE_STRUCT_CHECK_GROUPS {
u8 code; //!< From enum RoseInstructionCode.
rose_group groups; //!< Bitmask.
};
struct ROSE_STRUCT_CHECK_ONLY_EOD {
u8 code; //!< From enum RoseInstructionCode.
u32 fail_jump; //!< Jump forward this many bytes on failure.
@ -103,6 +132,12 @@ struct ROSE_STRUCT_CHECK_LEFTFIX {
u32 fail_jump; //!< Jump forward this many bytes on failure.
};
struct ROSE_STRUCT_PUSH_DELAYED {
u8 code; //!< From enum RoseInstructionCode.
u8 delay; // Number of bytes to delay.
u32 index; // Delay literal index (relative to first delay lit).
};
struct ROSE_STRUCT_SOM_ADJUST {
u8 code; //!< From enum RoseInstructionCode.
u32 distance; //!< Distance to EOM.
@ -164,7 +199,18 @@ struct ROSE_STRUCT_SET_STATE {
struct ROSE_STRUCT_SET_GROUPS {
u8 code; //!< From enum RoseInstructionCode.
rose_group groups; //!< Bitmask.
rose_group groups; //!< Bitmask to OR into groups.
};
struct ROSE_STRUCT_SQUASH_GROUPS {
u8 code; //!< From enum RoseInstructionCode.
rose_group groups; //!< Bitmask to AND into groups.
};
struct ROSE_STRUCT_CHECK_STATE {
u8 code; //!< From enum RoseInstructionCode.
u32 index; //!< State index in the role multibit.
u32 fail_jump; //!< Jump forward this many bytes on failure.
};
/**