#include <limits>
+#include <string.h>
#include "src/ir/dfa/dfa.h"
{
static void forwprop(const dfa_t &dfa, bool *been, size_t state,
- size_t *live, size_t need)
+ bool *live, const bool *need)
{
if (been[state]) return;
been[state] = true;
+ const size_t
+ nsym = dfa.nchars,
+ ntag = dfa.tags.size();
const dfa_state_t *s = dfa.states[state];
- size_t *l = &live[state * dfa.nchars];
- for (size_t c = 0; c < dfa.nchars; ++c) {
+
+ for (size_t c = 0; c < nsym; ++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);
+ bool *l = &live[(state * nsym + c) * ntag];
+ for (size_t t = 0; t < ntag; ++t) {
+ l[t] |= need[t];
+ }
forwprop(dfa, been, dest, live, need);
}
}
}
-static void calc_live(const dfa_t &dfa, size_t *live)
+static void liveness(const dfa_t &dfa, bool *live)
{
const size_t
nstate = dfa.states.size(),
nsym = dfa.nchars,
- narc = nstate * nsym;
+ narc = nstate * nsym,
+ ntag = dfa.tags.size();
+ bool *l0 = dfa.tagpool.buffer1;
/* note [control flow equations for tag liveness]
*
* - all outgoing arcs from state s2
* - if state s2 is final, corresponding rule action
*/
- std::fill(live, live + narc, ZERO_TAGS);
+ memset(live, 0, narc * ntag * sizeof(bool));
for (bool loop = true; loop;) {
loop = false;
if (i == dfa_t::NIL) continue;
const dfa_state_t *s = dfa.states[i];
- size_t l = s->rule == Rule::NONE
- ? ZERO_TAGS
- : dfa.tagpool.andlinv(dfa.rules[s->rule].tags, s->rule_tags.set);
+ bool *l = &live[a * ntag];
+ memcpy(l0, l, ntag * sizeof(bool));
+ memset(l, 0, ntag * sizeof(bool));
- const size_t *live_i = &live[i * nsym];
- for (size_t c = 0; c < nsym; ++c) {
- l = dfa.tagpool.orl(l,
- dfa.tagpool.andlinv(live_i[c], s->tags[c].set));
+ if (s->rule != Rule::NONE) {
+ const bool
+ *use = dfa.tagpool[dfa.rules[s->rule].tags],
+ *def = dfa.tagpool[s->rule_tags.set];
+ for (size_t t = 0; t < ntag; ++t) {
+ l[t] |= use[t] && !def[t];
+ }
}
- size_t *pl = &live[a];
- if (*pl != l) {
- *pl = l;
- loop = true;
+ for (size_t c = 0; c < nsym; ++c) {
+ const bool
+ *use = &live[(i * nsym + c) * ntag],
+ *def = dfa.tagpool[s->tags[c].set];
+ for (size_t t = 0; t < ntag; ++t) {
+ l[t] |= use[t] && !def[t];
+ }
}
+
+ loop |= memcmp(l, l0, ntag * sizeof(bool)) != 0;
}
}
*/
bool *been = new bool[nstate];
for (size_t i = 0; i < nstate; ++i) {
- dfa_state_t *s = dfa.states[i];
+ const dfa_state_t *s = dfa.states[i];
if (s->fallback) {
- const size_t need = dfa.tagpool.andlinv(
- dfa.rules[s->rule].tags, s->rule_tags.set);
+ const bool
+ *use = dfa.tagpool[dfa.rules[s->rule].tags],
+ *def = dfa.tagpool[s->rule_tags.set];
+ for (size_t t = 0; t < ntag; ++t) {
+ l0[t] = use[t] && !def[t];
+ }
std::fill(been, been + nstate, false);
- forwprop(dfa, been, i, live, need);
+ forwprop(dfa, been, i, live, l0);
}
}
delete[] been;
}
-static void mask_dead(dfa_t &dfa, const size_t *live)
+static void mask_dead(dfa_t &dfa, const bool *live)
{
- 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].set = dfa.tagpool.andl(s->tags[c].set, l[c]);
+ const size_t
+ nsym = dfa.nchars,
+ narc = dfa.states.size() * nsym,
+ ntag = dfa.tags.size();
+
+ for (size_t a = 0; a < narc; ++a) {
+ const size_t
+ c = a % nsym,
+ i = a / nsym;
+ const dfa_state_t *s = dfa.states[i];
+ // rule tags can't be dead by construction
+
+ size_t *p = &s->tags[c].set;
+ if (*p != ZERO_TAGS) {
+ const bool
+ *liv = &live[a * ntag],
+ *set = dfa.tagpool[*p];
+ bool *set_liv = dfa.tagpool.buffer1;
+ for (size_t t = 0; t < ntag; ++t) {
+ set_liv[t] = set[t] && liv[t];
}
- }
- if (s->rule != Rule::NONE) {
- s->rule_tags.set = dfa.tagpool.andl(s->rule_tags.set,
- dfa.rules[s->rule].tags);
+ *p = dfa.tagpool.insert(set_liv);
}
}
}
// tags that are updated here are pairwise incompatible
// with all tags that are alive, but not updated here
-static void incompatible(bool *tbl,
- Tagpool &tagpool, size_t l, size_t t)
+static void interfere(bool *intrf, size_t ntag,
+ const bool *live, const bool *tags)
{
- const size_t ntags = tagpool.ntags;
- const bool *live = tagpool[l];
- const bool *tags = tagpool[t];
- for (size_t i = 0; i < ntags; ++i) {
+ for (size_t i = 0; i < ntag; ++i) {
if (live[i] && !tags[i]) {
- for (size_t j = 0; j < ntags; ++j) {
+ for (size_t j = 0; j < ntag; ++j) {
if (tags[j]) {
- tbl[i * ntags + j] = tbl[j * ntags + i] = true;
+ intrf[i * ntag + j] = intrf[j * ntag + i] = true;
}
}
}
}
}
-static void incompatibility_table(const dfa_t &dfa,
- const size_t *livetags, bool *incompattbl)
+static void interference(const dfa_t &dfa,
+ const bool *live, bool *intrf)
{
- const size_t nstates = dfa.states.size();
- const size_t ntags = dfa.tags.size();
- for (size_t i = 0; i < nstates; ++i) {
+ const size_t
+ nstate = dfa.states.size(),
+ ntag = dfa.tags.size(),
+ nsym = dfa.nchars;
+
+ memset(intrf, 0, ntag * ntag * sizeof(bool));
+ for (size_t i = 0; i < nstate; ++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, l[c], s->tags[c].set);
- }
- }
+
if (s->rule != Rule::NONE) {
- incompatible(incompattbl, dfa.tagpool,
- dfa.rules[s->rule].tags, s->rule_tags.set);
+ const bool
+ *liv = dfa.tagpool[dfa.rules[s->rule].tags],
+ *set = dfa.tagpool[s->rule_tags.set];
+ interfere(intrf, ntag, liv, set);
+ }
+
+ for (size_t c = 0; c < nsym; ++c) {
+ const size_t x = s->tags[c].set;
+ if (x != ZERO_TAGS) {
+ const bool
+ *liv = &live[(i * nsym + c) * ntag],
+ *set = dfa.tagpool[x];
+ interfere(intrf, ntag, liv, set);
+ }
}
}
// fixed tags should not participate in deduplication, so
// each fixed tag is incompatible with all other tags
- for (size_t i = 0; i < ntags; ++i) {
+ for (size_t i = 0; i < ntag; ++i) {
if (dfa.tags[i].type == Tag::FIX) {
- for (size_t j = 0; j < ntags; ++j) {
- incompattbl[i * ntags + j]
- = incompattbl[j * ntags + i]
+ for (size_t j = 0; j < ntag; ++j) {
+ intrf[i * ntag + j]
+ = intrf[j * ntag + i]
= j != i;
}
}
* We build just some cover (not necessarily minimal).
* The algorithm takes quadratic (in the number of tags) time.
*/
-static void equivalence_classes(const bool *incompattbl,
- size_t ntags, size_t *represent)
+static size_t equivalence_classes(const dfa_t &dfa,
+ const bool *intrf, size_t *represent)
{
static const size_t END = std::numeric_limits<size_t>::max();
+ const size_t ntags = dfa.tags.size();
std::vector<size_t>
head(ntags, END), // list of representatives
next(ntags, END); // list of tags mapped to the same representative
// skip the 1st tag, it maps to itself
+ memset(represent, 0, ntags * sizeof(size_t));
for (size_t c = 1; c < ntags; ++c) {
size_t h;
for (h = 0; h != END; h = head[h]) {
size_t n;
for (n = h; n != END; n = next[n]) {
- if (incompattbl[c * ntags + n]) {
+ if (intrf[c * ntags + n]) {
break;
}
}
head[0] = c;
}
}
+
+ size_t nreps = 0;
+ for (size_t i = 0; i < ntags; ++i) {
+ if (dfa.tags[i].type == Tag::VAR && represent[i] == i) {
+ ++nreps;
+ }
+ }
+ return nreps;
}
-static void patch_tags(dfa_t &dfa, const size_t *represent)
+static void subst(Tagpool &tagpool, size_t *ptags, const size_t *represent)
{
- const size_t nstates = dfa.states.size();
+ const size_t ntag = tagpool.ntags;
+ const bool *tags = tagpool[*ptags];
+ bool *subs = tagpool.buffer1;
+ memset(subs, 0, ntag * sizeof(bool));
+ for (size_t t = 0; t < ntag; ++t) {
+ if (tags[t]) {
+ subs[represent[t]] = true;
+ }
+ }
+ *ptags = tagpool.insert(subs);
+}
+
+static void substitute(dfa_t &dfa, const size_t *represent)
+{
+ const size_t
+ nstates = dfa.states.size(),
+ ntags = dfa.tags.size();
+
for (size_t i = 0; i < nstates; ++i) {
dfa_state_t *s = dfa.states[i];
for (size_t c = 0; c < dfa.nchars; ++c) {
- s->tags[c].set = dfa.tagpool.subst(s->tags[c].set, represent);
- s->tags[c].copy = dfa.tagpool.subst(s->tags[c].copy, represent);
+ subst(dfa.tagpool, &s->tags[c].set, represent);
+ subst(dfa.tagpool, &s->tags[c].copy, represent);
}
- s->rule_tags.set = dfa.tagpool.subst(s->rule_tags.set, represent);
- s->rule_tags.copy = dfa.tagpool.subst(s->rule_tags.copy, represent);
+ subst(dfa.tagpool, &s->rule_tags.set, represent);
+ subst(dfa.tagpool, &s->rule_tags.copy, represent);
}
- dfa.copy_tags = dfa.tagpool.subst(dfa.copy_tags, represent);
+ subst(dfa.tagpool, &dfa.copy_tags, represent);
- const size_t ntags = dfa.tags.size();
for (size_t i = 0; i < ntags; ++i) {
Tag &t = dfa.tags[i];
if (t.type == Tag::VAR) {
size_t deduplicate_tags(dfa_t &dfa)
{
- const size_t ntags = dfa.tags.size();
- if (ntags == 0) {
- return 0;
- }
+ const size_t ntag = dfa.tags.size();
+ if (ntag == 0) return 0;
- const size_t
- nstates = dfa.states.size(),
- nedges = nstates * dfa.nchars;
- size_t *live = new size_t[nedges]();
- calc_live(dfa, live);
+ bool *live = new bool[dfa.states.size() * dfa.nchars * ntag];
+ bool *interfere = new bool[ntag * ntag];
+ size_t *represent = new size_t[ntag];
+ liveness(dfa, live);
mask_dead(dfa, live);
-
- bool *incompattbl = new bool[ntags * ntags]();
- incompatibility_table(dfa, live, incompattbl);
-
- size_t *represent = new size_t[ntags]();
- equivalence_classes(incompattbl, ntags, represent);
-
- size_t nreps = 0;
- for (size_t i = 0; i < ntags; ++i) {
- if (dfa.tags[i].type == Tag::VAR && represent[i] == i) {
- ++nreps;
- }
- }
-
- if (nreps < ntags) {
- patch_tags(dfa, represent);
+ interference(dfa, live, interfere);
+ const size_t nrep = equivalence_classes(dfa, interfere, represent);
+ if (nrep < ntag) {
+ substitute(dfa, represent);
}
delete[] live;
- delete[] incompattbl;
+ delete[] interfere;
delete[] represent;
- return nreps;
+ return nrep;
}
} // namespace re2c