* - There is a transition from S to some state S' (maybe equal to S)
* that does not set T and T is alive in S'.
*/
-static void calc_live(const dfa_t &dfa,
- const bool *fallback,
- bool *visited,
- bool *live,
- size_t i)
+static void calc_live(const dfa_t &dfa, const bool *fallback, bool *live)
{
- if (visited[i]) {
- return;
- }
-
- visited[i] = true;
- dfa_state_t *s = dfa.states[i];
+ const size_t nstates = dfa.states.size();
const size_t ntags = dfa.tags.size();
- // add tags before recursing to child states,
- // so that tags propagate into loopbacks to this state
- if (dangling_arcs(s->arcs, dfa.nchars)) {
- if (s->rule != Rule::NONE) {
- // final state, only rule tags are alive
- add_tags_with_mask(&live[i * ntags],
- dfa.rules[s->rule].tags,
- dfa.tagpool[s->rule_tags],
- ntags);
- } else {
- // transition to default state and dispatch on
- // 'yyaccept': all fallback rules are potentially
- // reachable, their tags are alive
- // no mask (no rule implies no rule tags)
- add_tags(&live[i * ntags], fallback, ntags);
+ for (size_t i = 0; i < nstates; ++i) {
+ dfa_state_t *s = dfa.states[i];
+ if (dangling_arcs(s->arcs, dfa.nchars)) {
+ if (s->rule != Rule::NONE) {
+ // final state, only rule tags are alive
+ add_tags_with_mask(&live[i * ntags],
+ dfa.rules[s->rule].tags,
+ dfa.tagpool[s->rule_tags],
+ ntags);
+ } else {
+ // transition to default state and dispatch on
+ // 'yyaccept': all fallback rules are potentially
+ // reachable, their tags are alive
+ // no mask: no rule implies no rule tags
+ add_tags(&live[i * ntags], fallback, ntags);
+ }
}
}
- for (size_t c = 0; c < dfa.nchars; ++c) {
- const size_t j = s->arcs[c];
- if (j != dfa_t::NIL) {
- calc_live(dfa, fallback, visited, live, j);
- add_tags_with_mask(&live[i * ntags],
- &live[j * ntags],
- dfa.tagpool[s->tags[c]],
- ntags);
+ for (bool loop = true; loop;) {
+ loop = false;
+ for (size_t i = 0; i < nstates; ++i) {
+ dfa_state_t *s = dfa.states[i];
+ for (size_t c = 0; c < dfa.nchars; ++c) {
+ const size_t j = s->arcs[c];
+ if (j != dfa_t::NIL) {
+ loop |= addcmp_tags_with_mask(&live[i * ntags],
+ &live[j * ntags],
+ dfa.tagpool[s->tags[c]],
+ ntags);
+ }
+ }
}
}
}
}
const size_t nstates = dfa.states.size();
- bool *visited = new bool[nstates]();
bool *live = new bool[nstates * ntags]();
- calc_live(dfa, fbtags, visited, live, 0);
+ calc_live(dfa, fbtags, live);
mask_dead(dfa, live);
}
delete[] fbtags;
- delete[] visited;
delete[] live;
delete[] incompattbl;
--- /dev/null
+/* Generated by re2c */
+// This test revealed a bug in liveness analyses that takes place
+// during tag deduplication: in loops, live tags added by non-looping
+// child paths failed to propagate into looping paths.
+
+// These two cases differ: in one case looping transition goes first (and tags are lost),
+// in the other case non-looping transition goes first (and tags are not lost).
+
+
+{
+ YYCTYPE yych;
+ long yytag0p;
+ YYCTXMARKER = YYCURSOR;
+ if ((YYLIMIT - YYCURSOR) < 2) YYFILL(2);
+ yych = *YYCURSOR;
+ switch (yych) {
+ case 'a':
+ yytag0p = (YYCURSOR - YYCTXMARKER);
+ goto yy4;
+ case 'b': goto yy5;
+ default: goto yy2;
+ }
+yy2:
+ ++YYCURSOR;
+yy3:
+ {}
+yy4:
+ yych = *(YYMARKER = ++YYCURSOR);
+ switch (yych) {
+ case 'c': goto yy7;
+ default: goto yy3;
+ }
+yy5:
+ ++YYCURSOR;
+ { (YYCTXMARKER + yytag0p) }
+yy7:
+ ++YYCURSOR;
+ if (YYLIMIT <= YYCURSOR) YYFILL(1);
+ yych = *YYCURSOR;
+ switch (yych) {
+ case 'a':
+ yytag0p = (YYCURSOR - YYCTXMARKER);
+ goto yy9;
+ case 'b': goto yy5;
+ default: goto yy8;
+ }
+yy8:
+ YYCURSOR = YYMARKER;
+ goto yy3;
+yy9:
+ ++YYCURSOR;
+ if (YYLIMIT <= YYCURSOR) YYFILL(1);
+ yych = *YYCURSOR;
+ switch (yych) {
+ case 'c': goto yy7;
+ default: goto yy8;
+ }
+}
+
+
+
+{
+ YYCTYPE yych;
+ long yytag0p;
+ YYCTXMARKER = YYCURSOR;
+ if ((YYLIMIT - YYCURSOR) < 2) YYFILL(2);
+ yych = *YYCURSOR;
+ switch (yych) {
+ case 'a': goto yy14;
+ case 'b':
+ yytag0p = (YYCURSOR - YYCTXMARKER);
+ goto yy16;
+ default: goto yy12;
+ }
+yy12:
+ ++YYCURSOR;
+yy13:
+ {}
+yy14:
+ ++YYCURSOR;
+ { (YYCTXMARKER + yytag0p) }
+yy16:
+ yych = *(YYMARKER = ++YYCURSOR);
+ switch (yych) {
+ case 'c': goto yy17;
+ default: goto yy13;
+ }
+yy17:
+ ++YYCURSOR;
+ if (YYLIMIT <= YYCURSOR) YYFILL(1);
+ yych = *YYCURSOR;
+ switch (yych) {
+ case 'a': goto yy14;
+ case 'b':
+ yytag0p = (YYCURSOR - YYCTXMARKER);
+ goto yy19;
+ default: goto yy18;
+ }
+yy18:
+ YYCURSOR = YYMARKER;
+ goto yy13;
+yy19:
+ ++YYCURSOR;
+ if (YYLIMIT <= YYCURSOR) YYFILL(1);
+ yych = *YYCURSOR;
+ switch (yych) {
+ case 'c': goto yy17;
+ default: goto yy18;
+ }
+}
+