struct revarc_t
{
+ size_t edge;
size_t dest;
size_t tags;
revarc_t *next;
};
-static void backprop(revarc_t **rdfa,
- Tagpool &tagpool,
- size_t *live,
- size_t tags,
- size_t state)
+static void backprop(revarc_t **rdfa, bool *been, size_t state,
+ size_t *live, size_t tags, Tagpool &tagpool)
{
- const size_t l = live[state];
- live[state] = tagpool.orl(l, tags);
- if (l == live[state]) return;
+ if (been[state]) return;
+ been[state] = true;
for (const revarc_t *a = rdfa[state]; a; a = a->next) {
- backprop(rdfa, tagpool, live,
- tagpool.andlinv(tags, a->tags), a->dest);
+ live[a->edge] = tagpool.orl(live[a->edge], tags);
+ const size_t l = tagpool.andlinv(tags, a->tags);
+ if (l != ZERO_TAGS) {
+ backprop(rdfa, been, a->dest, live, l, tagpool);
+ }
}
}
if (been[state]) return;
been[state] = true;
- live[state] = dfa.tagpool.orl(live[state], need);
-
const dfa_state_t *s = dfa.states[state];
+ size_t *l = &live[state * dfa.nchars];
for (size_t c = 0; c < dfa.nchars; ++c) {
const size_t dest = s->arcs[c];
if (dest != dfa_t::NIL && dfa.states[dest]->fallthru) {
+ l[c] = dfa.tagpool.orl(l[c], need);
forwprop(dfa, been, dest, live, need);
}
}
static void calc_live(const dfa_t &dfa, size_t *live)
{
const size_t nstates = dfa.states.size();
+ bool *been = new bool[nstates];
// backward-propagate rule tags
revarc_t **rdfa = new revarc_t*[nstates]();
for (size_t c = 0; c < dfa.nchars; ++c) {
const size_t j = s->arcs[c];
if (j != dfa_t::NIL) {
+ a->edge = i * dfa.nchars + c;
a->dest = i;
a->tags = s->tags[c];
a->next = rdfa[j];
if (s->rule != Rule::NONE) {
const size_t need = dfa.tagpool.andlinv(
dfa.rules[s->rule].tags, s->rule_tags);
- backprop(rdfa, dfa.tagpool, live, need, i);
+ std::fill(been, been + nstates, false);
+ backprop(rdfa, been, i, live, need, dfa.tagpool);
}
}
delete[] rdfa;
delete[] rarcs;
// forward-propagate fallback tags
- bool *been = new bool[nstates];
for (size_t i = 0; i < nstates; ++i) {
dfa_state_t *s = dfa.states[i];
if (s->fallback) {
const size_t nstates = dfa.states.size();
for (size_t i = 0; i < nstates; ++i) {
dfa_state_t *s = dfa.states[i];
+ const size_t *l = &live[i * dfa.nchars];
for (size_t c = 0; c < dfa.nchars; ++c) {
const size_t j = s->arcs[c];
if (j != dfa_t::NIL) {
- s->tags[c] = dfa.tagpool.andl(s->tags[c],
- live[j]);
+ s->tags[c] = dfa.tagpool.andl(s->tags[c], l[c]);
}
}
if (s->rule != Rule::NONE) {
const size_t ntags = dfa.tags.size();
for (size_t i = 0; i < nstates; ++i) {
const dfa_state_t *s = dfa.states[i];
+ const size_t *l = &livetags[i * dfa.nchars];
for (size_t c = 0; c < dfa.nchars; ++c) {
const size_t j = s->arcs[c];
if (j != dfa_t::NIL) {
- incompatible(incompattbl, dfa.tagpool,
- livetags[j], s->tags[c]);
+ incompatible(incompattbl, dfa.tagpool, l[c], s->tags[c]);
}
}
if (s->rule != Rule::NONE) {
return 0;
}
- const size_t nstates = dfa.states.size();
- size_t *live = new size_t[nstates]();
+ const size_t
+ nstates = dfa.states.size(),
+ nedges = nstates * dfa.nchars;
+ size_t *live = new size_t[nedges]();
calc_live(dfa, live);
mask_dead(dfa, live);
--- /dev/null
+/* Generated by re2c */
+// This test shows that tag liveness should be attributed to DFA edges,
+// not to DFA states. State granularity is too crude: there might be
+// two different paths to the same state, with some tag alive on one
+// path but not on the other. If liveness is attributed to states, then
+// tag liveness on one path implies tag liveness in the join state,
+// which affects the other path. But if liveness is attributed to
+// edges, then liveness of one path won't affect liveness of the other.
+
+// In this example tag 'p' is loop-invariant: it should be moved out
+// of loop and set once in the very end. However, if liveness is
+// attributed to states rather than edges, the accepting state (dispatch
+// on 'yyaccept') will force liveness on the looping path and prevent
+// tag from hoisting out of loop.
+
+
+{
+ YYCTYPE yych;
+ unsigned int yyaccept = 0;
+ long yytag0p;
+ YYCTXMARKER = YYCURSOR;
+ if (YYLIMIT <= YYCURSOR) YYFILL(1);
+ yych = *(YYMARKER = YYCURSOR);
+ switch (yych) {
+ case 'a': goto yy3;
+ default: goto yy2;
+ }
+yy2:
+ { (YYCTXMARKER + yytag0p) }
+yy3:
+ ++YYCURSOR;
+ if (YYLIMIT <= YYCURSOR) YYFILL(1);
+ yych = *YYCURSOR;
+ switch (yych) {
+ case 'b': goto yy5;
+ default: goto yy4;
+ }
+yy4:
+ YYCURSOR = YYMARKER;
+ if (yyaccept == 0) {
+ goto yy2;
+ } else {
+ yytag0p = (YYCURSOR - YYCTXMARKER);
+ goto yy2;
+ }
+yy5:
+ yyaccept = 1;
+ YYMARKER = ++YYCURSOR;
+ if (YYLIMIT <= YYCURSOR) YYFILL(1);
+ yych = *YYCURSOR;
+ switch (yych) {
+ case 'a': goto yy3;
+ default:
+ yytag0p = (YYCURSOR - YYCTXMARKER);
+ goto yy2;
+ }
+}
+
+re2c: warning: line 17: rule matches empty string [-Wmatch-empty-string]
--- /dev/null
+// This test shows that tag liveness should be attributed to DFA edges,
+// not to DFA states. State granularity is too crude: there might be
+// two different paths to the same state, with some tag alive on one
+// path but not on the other. If liveness is attributed to states, then
+// tag liveness on one path implies tag liveness in the join state,
+// which affects the other path. But if liveness is attributed to
+// edges, then liveness of one path won't affect liveness of the other.
+
+// In this example tag 'p' is loop-invariant: it should be moved out
+// of loop and set once in the very end. However, if liveness is
+// attributed to states rather than edges, the accepting state (dispatch
+// on 'yyaccept') will force liveness on the looping path and prevent
+// tag from hoisting out of loop.
+
+/*!re2c
+
+ ("ab" @p)* { @p }
+
+*/