]> granicus.if.org Git - re2c/commitdiff
Respect injective mappings when optimizing save(X), copy(Y,X) to save(Y).
authorUlya Trofimovich <skvadrik@gmail.com>
Fri, 9 Dec 2016 12:15:56 +0000 (12:15 +0000)
committerUlya Trofimovich <skvadrik@gmail.com>
Fri, 9 Dec 2016 12:15:56 +0000 (12:15 +0000)
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).

re2c/src/ir/dfa/find_state.cc
re2c/test/tags/mapping1.i--tags--dfa-mapping(injective).c
re2c/test/tags/mapping2.i--tags--dfa-mapping(injective).c

index 9de2f6b3b5fa8a57ed8dac3008732a62da0e476a..221b9598a90ddc27609c02b1b9cc0bcff155ad5d 100644 (file)
@@ -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(&copy, mapping->indeg);
+               tagcopy_t::topsort(&copy, map->indeg);
        }
 
        return tcmd_t(save, copy);
index 0f2561045f6d38bd711dd4dcd2f0fd1ce9761e72..c06a105d486f1a1378f652d2052ecda32236721d 100644 (file)
@@ -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;
index 6d8f777f179572d36f4c568f50265a555f8d22f1..a898ee401ffcbb8b83d063b64204343d029d3a84 100644 (file)
@@ -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]