From: Ulya Trofimovich Date: Fri, 9 Dec 2016 12:15:56 +0000 (+0000) Subject: Respect injective mappings when optimizing save(X), copy(Y,X) to save(Y). X-Git-Tag: 1.0~39^2~198 X-Git-Url: https://granicus.if.org/sourcecode?a=commitdiff_plain;h=790e1acdbcdf3e0051c3832bcf0392550915c8a7;p=re2c Respect injective mappings when optimizing save(X), copy(Y,X) to save(Y). If mapping is injective, there may be multiple copy commands per one save command: save(X), copy(Y,X), copy(Z,X). In this case save command must be replicated for each copy command: save(Y), save(Z). --- diff --git a/re2c/src/ir/dfa/find_state.cc b/re2c/src/ir/dfa/find_state.cc index 9de2f6b3..221b9598 100644 --- a/re2c/src/ir/dfa/find_state.cc +++ b/re2c/src/ir/dfa/find_state.cc @@ -242,54 +242,68 @@ kernels_t::result_t kernels_t::insert(const closure_t &clos, tagver_t maxver) /* note [save(X), copy(Y,X) optimization] * - * 'Save' command 'X <- ...' followed by a 'copy' command 'Y <- X' - * can be optimized to 'save' command 'Y <- ...'. This way we end - * up with less commands ans less tag versions (new version X is - * gone), but more importantly, we can safely put 'copy' commands - * in front of 'save' commands. This order is necessary when it - * comes to fallback commands. - * This optimization is applied after checking priorities, so it - * cannot affect them. + * save(X) command followed by a copy(Y,X) command can be optimized to + * save(Y). This helps reduce the number commands and versions (new version + * X is gone), but what is more important, it allows to put copy commands + * in front of save commands. This order is necessary when it comes to + * fallback commands. + * + * Note that in case of injective mapping there may be more than one copy + * command matching the same save command: save(X), copy(Y,X), copy(Z,X). + * In this case save command must be replicated for each copy command: + * save(Y), save(Z). + * + * For each save(X) command there must be at least one copy(Y,X) command + * (exactly one case of bijective mapping). This is because X version in + * save(X) command must be a new version which cannot occur in the older + * DFA state. Thus all save commands are transformed (maybe replicated) by + * copy commands, and some copy commands are erased by save commands. + * + * This optimization is applied after checking priority violation, so it + * cannot affect the check. */ static tcmd_t commands(const closure_t &closure, const Tagpool &tagpool, - tcpool_t &tcpool, mapping_t *mapping) + tcpool_t &tcpool, mapping_t *map) { tagsave_t *save = NULL; tagcopy_t *copy = NULL; + tagver_t *cur = tagpool.buffer1, *bot = tagpool.buffer2; cclositer_t c1 = closure.begin(), c2 = closure.end(), c; + // at most one new cursor and one new bottom version per tag for (size_t t = 0; t < tagpool.ntags; ++t) { for (c = c1; c != c2 && tagpool[c->ttran][t] != TAGVER_CURSOR; ++c); - if (c != c2) save = tcpool.make_save(save, tagpool[c->tvers][t], false); + cur[t] = c == c2 ? TAGVER_ZERO : tagpool[c->tvers][t]; for (c = c1; c != c2 && tagpool[c->ttran][t] != TAGVER_BOTTOM; ++c); - if (c != c2) save = tcpool.make_save(save, -tagpool[c->tvers][t], true); + bot[t] = c == c2 ? TAGVER_ZERO : tagpool[c->tvers][t]; } - if (mapping) { - tagver_t max = mapping->max, - *x2y = mapping->x2y, - *y2x = mapping->y2x; - - // see note [save(X), copy(Y,X) optimization] - for (tagsave_t *s = save; s; s = s->next) { - const tagver_t - y = s->bottom ? -s->ver : s->ver, - x = y2x[y]; - if (x != TAGVER_ZERO) { - y2x[y] = x2y[x] = TAGVER_ZERO; - s->ver = abs(x); - } + if (!map) { + // no mapping => only save commands, no copy commands + for (size_t t = 0; t < tagpool.ntags; ++t) { + const tagver_t x = cur[t], y = bot[t]; + if (x != TAGVER_ZERO) save = tcpool.make_save(save, x, false); + if (y != TAGVER_ZERO) save = tcpool.make_save(save, -y, true); } - for (tagver_t x = -max; x < max; ++x) { - const tagver_t y = x2y[x]; - if (y != TAGVER_ZERO && y != x) { + } else { + // mapping: see note [save(X), copy(Y,X) optimization] + for (tagver_t x = -map->max; x < map->max; ++x) { + const tagver_t y = map->x2y[x]; + if (y == TAGVER_ZERO || y == x) continue; + + const size_t t = map->x2t[x]; + if (cur[t] == y) { + save = tcpool.make_save(save, abs(x), false); + } else if (bot[t] == y) { + save = tcpool.make_save(save, abs(x), true); + } else { copy = tcpool.make_copy(copy, abs(x), abs(y)); } } // see note [topological ordering of copy commands] - tagcopy_t::topsort(©, mapping->indeg); + tagcopy_t::topsort(©, map->indeg); } return tcmd_t(save, copy); diff --git a/re2c/test/tags/mapping1.i--tags--dfa-mapping(injective).c b/re2c/test/tags/mapping1.i--tags--dfa-mapping(injective).c index 0f256104..c06a105d 100644 --- a/re2c/test/tags/mapping1.i--tags--dfa-mapping(injective).c +++ b/re2c/test/tags/mapping1.i--tags--dfa-mapping(injective).c @@ -8,7 +8,7 @@ case 'a': yyt2 = YYCURSOR; goto yy5; - case 'b': goto yy3; + case 'b': goto yy7; default: yyt1 = YYCURSOR; goto yy3; @@ -20,11 +20,11 @@ yy3: switch (yych) { case 'a': yyt2 = NULL; - goto yy8; + goto yy9; case 'b': goto yy4; default: yyt2 = NULL; - goto yy7; + goto yy8; } yy4: YYCURSOR = YYMARKER; @@ -32,7 +32,7 @@ yy4: yy5: ++YYCURSOR; switch ((yych = *YYCURSOR)) { - case 'a': goto yy9; + case 'a': goto yy10; default: goto yy6; } yy6: @@ -41,18 +41,29 @@ yy6: yy7: yych = *++YYCURSOR; switch (yych) { - case 'a': goto yy9; - default: goto yy4; + case 'a': + yyt1 = yyt2 = NULL; + goto yy9; + case 'b': goto yy4; + default: + yyt2 = NULL; + goto yy8; } yy8: yych = *++YYCURSOR; switch (yych) { - case 'a': goto yy9; + case 'a': goto yy10; + default: goto yy4; + } +yy9: + yych = *++YYCURSOR; + switch (yych) { + case 'a': goto yy10; default: yyt2 = yyt1; goto yy6; } -yy9: +yy10: ++YYCURSOR; yych = *YYCURSOR; goto yy6; diff --git a/re2c/test/tags/mapping2.i--tags--dfa-mapping(injective).c b/re2c/test/tags/mapping2.i--tags--dfa-mapping(injective).c index 6d8f777f..a898ee40 100644 --- a/re2c/test/tags/mapping2.i--tags--dfa-mapping(injective).c +++ b/re2c/test/tags/mapping2.i--tags--dfa-mapping(injective).c @@ -14,6 +14,7 @@ yy2: ++YYCURSOR; if (YYLIMIT <= YYCURSOR) YYFILL(1); yych = *YYCURSOR; +yy3: switch (yych) { case 'b': goto yy4; default: goto yy2; @@ -23,15 +24,9 @@ yy4: {} yy5: ++YYCURSOR; - switch ((yych = *YYCURSOR)) { - case 'a': goto yy2; - case 'b': - yyt1 = NULL; - goto yy4; - default: - yyt1 = NULL; - goto yy2; - } + yyt1 = NULL; + yych = *YYCURSOR; + goto yy3; } re2c: warning: line 3: rule matches empty string [-Wmatch-empty-string]