/* Counterexample Generation Search Nodes
Copyright (C) 2020-2021 Free Software Foundation, Inc.
This file is part of Bison, the GNU Compiler Compiler.
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see . */
#include
#include "state-item.h"
#include
#include
#include
#include
#include
#include "closure.h"
#include "getargs.h"
#include "nullable.h"
size_t nstate_items;
state_item_number *state_item_map;
state_item *state_items;
// Hash functions for index -> bitset hash maps.
typedef struct
{
int key;
bitset l;
} hash_pair;
static size_t
hash_pair_hasher (const hash_pair *sl, size_t max)
{
return sl->key % max;
}
static bool
hash_pair_comparator (const hash_pair *l, const hash_pair *r)
{
return l->key == r->key;
}
static void
hash_pair_free (hash_pair *hp)
{
bitset_free (hp->l);
free (hp);
}
static Hash_table *
hash_pair_table_create (int size)
{
return hash_xinitialize (size,
NULL,
(Hash_hasher) hash_pair_hasher,
(Hash_comparator) hash_pair_comparator,
(Hash_data_freer) hash_pair_free);
}
static bitset
hash_pair_lookup (Hash_table *tab, int key)
{
hash_pair probe;
probe.key = key;
hash_pair *hp = hash_lookup (tab, &probe);
return hp ? hp->l : NULL;
}
static void
hash_pair_insert (Hash_table *tab, int key, bitset val)
{
hash_pair *hp = xmalloc (sizeof *hp);
hp->key = key;
hp->l = val;
hash_pair *res = hash_xinsert (tab, hp);
// This must be the first insertion.
(void) res;
assert (res == hp);
}
/* A state_item from a state's id and the offset of the item within
the state. */
state_item *
state_item_lookup (state_number s, state_item_number off)
{
return &state_items[state_item_index_lookup (s, off)];
}
static inline void
state_item_set (state_item_number sidx, const state *s, item_number off)
{
state_items[sidx].state = s;
state_items[sidx].item = &ritem[off];
state_items[sidx].lookahead = NULL;
state_items[sidx].trans = -1;
state_items[sidx].prods = NULL;
state_items[sidx].revs = bitset_create (nstate_items, BITSET_SPARSE);
}
/**
* Initialize state_items set
*/
static void
init_state_items (void)
{
nstate_items = 0;
bitsetv production_items = bitsetv_create (nstates, nritems, BITSET_SPARSE);
for (int i = 0; i < nstates; ++i)
{
const state *s = states[i];
nstate_items += s->nitems;
closure (s->items, s->nitems);
for (size_t j = 0; j < nitemset; ++j)
if (0 < itemset[j]
&& item_number_is_rule_number (ritem[itemset[j] - 1]))
{
bitset_set (production_items[i], itemset[j]);
++nstate_items;
}
}
state_item_map = xnmalloc (nstates + 1, sizeof (state_item_number));
state_items = xnmalloc (nstate_items, sizeof (state_item));
state_item_number sidx = 0;
for (int i = 0; i < nstates; ++i)
{
state_item_map[i] = sidx;
int rule_search_idx = 0;
const state *s = states[i];
const reductions *red = s->reductions;
for (int j = 0; j < s->nitems; ++j)
{
state_item_set (sidx, s, s->items[j]);
state_item *si = &state_items[sidx];
const rule *r = item_rule (si->item);
if (rule_search_idx < red->num && red->rules[rule_search_idx] < r)
++rule_search_idx;
if (rule_search_idx < red->num && r == red->rules[rule_search_idx])
{
bitsetv lookahead = red->lookaheads;
if (lookahead)
si->lookahead = lookahead[rule_search_idx];
}
++sidx;
}
bitset_iterator biter;
item_number off;
BITSET_FOR_EACH (biter, production_items[i], off, 0)
{
state_item_set (sidx, s, off);
if (item_number_is_rule_number (ritem[off]))
{
bitsetv lookahead = red->lookaheads;
if (lookahead)
state_items[sidx].lookahead = lookahead[rule_search_idx];
++rule_search_idx;
}
++sidx;
}
}
state_item_map[nstates] = nstate_items;
bitsetv_free (production_items);
}
static size_t
state_sym_hasher (const void *st, size_t max)
{
return ((state *) st)->accessing_symbol % max;
}
static bool
state_sym_comparator (const void *s1, const void *s2)
{
return ((state *) s1)->accessing_symbol == ((state *) s2)->accessing_symbol;
}
static state *
state_sym_lookup (symbol_number sym, Hash_table *h)
{
state probe;
probe.accessing_symbol = sym;
return hash_lookup (h, &probe);
}
static void
init_trans (void)
{
for (state_number i = 0; i < nstates; ++i)
{
// Generate a hash set that maps from accepting symbols to the states
// this state transitions to.
state *s = states[i];
transitions *t = s->transitions;
Hash_table *transition_set
= hash_xinitialize (t->num, NULL, (Hash_hasher) state_sym_hasher,
(Hash_comparator) state_sym_comparator, NULL);
for (int j = 0; j < t->num; ++j)
if (!TRANSITION_IS_DISABLED (t, j))
hash_xinsert (transition_set, t->states[j]);
for (state_item_number j = state_item_map[i]; j < state_item_map[i + 1]; ++j)
{
item_number *item = state_items[j].item;
if (item_number_is_rule_number (*item))
continue;
state *dst = state_sym_lookup (*item, transition_set);
if (!dst)
continue;
// find the item in the destination state that corresponds
// to the transition of item
for (int k = 0; k < dst->nitems; ++k)
if (item + 1 == ritem + dst->items[k])
{
state_item_number dstSI =
state_item_index_lookup (dst->number, k);
state_items[j].trans = dstSI;
bitset_set (state_items[dstSI].revs, j);
break;
}
}
hash_free (transition_set);
}
}
static void
init_prods (void)
{
for (int i = 0; i < nstates; ++i)
{
state *s = states[i];
// closure_map is a hash map from nonterminals to a set
// of the items that produce those nonterminals
Hash_table *closure_map = hash_pair_table_create (nsyms - ntokens);
// Add the nitems of state to skip to the production portion
// of that state's state_items
for (state_item_number j = state_item_map[i] + s->nitems;
j < state_item_map[i + 1]; ++j)
{
state_item *src = &state_items[j];
item_number *item = src->item;
symbol_number lhs = item_rule (item)->lhs->number;
bitset itms = hash_pair_lookup (closure_map, lhs);
if (!itms)
{
itms = bitset_create (nstate_items, BITSET_SPARSE);
hash_pair_insert (closure_map, lhs, itms);
}
bitset_set (itms, j);
}
// For each item with a dot followed by a nonterminal,
// try to create a production edge.
for (state_item_number j = state_item_map[i]; j < state_item_map[i + 1]; ++j)
{
state_item *src = &state_items[j];
item_number item = *(src->item);
// Skip reduce items and items with terminals after the dot
if (item_number_is_rule_number (item) || ISTOKEN (item))
continue;
symbol_number sym = item_number_as_symbol_number (item);
bitset lb = hash_pair_lookup (closure_map, sym);
if (lb)
{
bitset copy = bitset_create (nstate_items, BITSET_SPARSE);
bitset_copy (copy, lb);
// update prods.
state_items[j].prods = copy;
// update revs.
bitset_iterator biter;
state_item_number prod;
BITSET_FOR_EACH (biter, copy, prod, 0)
bitset_set (state_items[prod].revs, j);
}
}
hash_free (closure_map);
}
}
/* Since lookaheads are only generated for reductions, we need to
propagate lookahead sets backwards as the searches require each
state_item to have a lookahead. */
static inline void
gen_lookaheads (void)
{
for (state_item_number i = 0; i < nstate_items; ++i)
{
state_item *si = &state_items[i];
if (item_number_is_symbol_number (*(si->item)) || !si->lookahead)
continue;
bitset lookahead = si->lookahead;
state_item_list queue =
gl_list_create (GL_LINKED_LIST, NULL, NULL, NULL, true, 1,
(const void **) &si);
// For each reduction item, traverse through all state_items
// accessible through reverse transition steps, and set their
// lookaheads to the reduction items lookahead
while (gl_list_size (queue) > 0)
{
state_item *prev = (state_item *) gl_list_get_at (queue, 0);
gl_list_remove_at (queue, 0);
prev->lookahead = lookahead;
if (SI_TRANSITION (prev))
{
bitset rsi = state_items[prev - state_items].revs;
bitset_iterator biter;
state_item_number sin;
BITSET_FOR_EACH (biter, rsi, sin, 0)
gl_list_add_first (queue, &state_items[sin]);
}
}
gl_list_free (queue);
}
}
bitsetv firsts = NULL;
static void
init_firsts (void)
{
firsts = bitsetv_create (nnterms, nsyms, BITSET_FIXED);
for (rule_number i = 0; i < nrules; ++i)
{
rule *r = &rules[i];
item_number *n = r->rhs;
// Iterate through nullable nonterminals to try to find a terminal.
while (item_number_is_symbol_number (*n) && ISVAR (*n)
&& nullable[*n - ntokens])
++n;
if (item_number_is_rule_number (*n) || ISVAR (*n))
continue;
symbol_number lhs = r->lhs->number;
bitset_set (FIRSTS (lhs), *n);
}
bool change = true;
while (change)
{
change = false;
for (rule_number i = 0; i < nrules; ++i)
{
rule *r = &rules[i];
symbol_number lhs = r->lhs->number;
bitset f_lhs = FIRSTS (lhs);
for (item_number *n = r->rhs;
item_number_is_symbol_number (*n) && ISVAR (*n);
++n)
{
bitset f = FIRSTS (*n);
if (!bitset_subset_p (f_lhs, f))
{
change = true;
bitset_union (f_lhs, f_lhs, f);
}
if (!nullable[*n - ntokens])
break;
}
}
}
}
static inline void
disable_state_item (state_item *si)
{
si->trans = -2;
bitset_free (si->revs);
if (si->prods)
bitset_free (si->prods);
}
/* Disable all state_item paths that lead to/from SI and nowhere
else. */
static void
prune_state_item (const state_item *si)
{
state_item_list queue =
gl_list_create (GL_LINKED_LIST, NULL, NULL, NULL, true, 1,
(const void **) &si);
while (gl_list_size (queue) > 0)
{
state_item *dsi = (state_item *) gl_list_get_at (queue, 0);
gl_list_remove_at (queue, 0);
if (SI_DISABLED (dsi - state_items))
continue;
if (dsi->trans >= 0 && !SI_DISABLED (dsi->trans))
{
const state_item *trans = &state_items[dsi->trans];
bitset_reset (trans->revs, dsi - state_items);
if (bitset_empty_p (trans->revs))
gl_list_add_last (queue, trans);
}
if (dsi->prods)
{
bitset_iterator biter;
state_item_number sin;
BITSET_FOR_EACH (biter, dsi->prods, sin, 0)
{
if (SI_DISABLED (sin))
continue;
const state_item *prod = &state_items[sin];
bitset_reset (prod->revs, dsi - state_items);
if (bitset_empty_p (prod->revs))
gl_list_add_last (queue, prod);
}
}
bitset_iterator biter;
state_item_number sin;
BITSET_FOR_EACH (biter, dsi->revs, sin, 0)
{
if (SI_DISABLED (sin))
continue;
state_item *rev = &state_items[sin];
if (&state_items[rev->trans] == dsi)
gl_list_add_last (queue, rev);
else if (rev->prods)
{
bitset_reset (rev->prods, dsi - state_items);
if (bitset_empty_p (rev->prods))
gl_list_add_last (queue, rev);
}
else
gl_list_add_last (queue, rev);
}
disable_state_item (dsi);
}
gl_list_free (queue);
}
/* To make searches more efficient, prune away paths that are caused
by disabled transitions. */
static void
prune_disabled_paths (void)
{
for (int i = nstate_items - 1; i >= 0; --i)
{
state_item *si = &state_items[i];
if (si->trans == -1 && item_number_is_symbol_number (*si->item))
prune_state_item (si);
}
}
void
state_item_print (const state_item *si, FILE *out, const char *prefix)
{
fputs (prefix, out);
item_print (si->item, NULL, out);
putc ('\n', out);
}
const rule*
state_item_rule (const state_item *si)
{
return item_rule (si->item);
}
/**
* Report the state_item graph
*/
static void
state_items_report (FILE *out)
{
fprintf (out, "# state items: %zu\n", nstate_items);
for (state_number i = 0; i < nstates; ++i)
{
fprintf (out, "State %d:\n", i);
for (state_item_number j = state_item_map[i]; j < state_item_map[i + 1]; ++j)
{
const state_item *si = &state_items[j];
item_print (si->item, NULL, out);
if (SI_DISABLED (j))
fputs (" DISABLED\n", out);
else
{
putc ('\n', out);
if (si->trans >= 0)
{
fputs (" -> ", out);
state_item_print (&state_items[si->trans], out, "");
}
bitset sets[2] = { si->prods, si->revs };
const char *txt[2] = { " => ", " <- " };
for (int seti = 0; seti < 2; ++seti)
{
bitset b = sets[seti];
if (b)
{
bitset_iterator biter;
state_item_number sin;
BITSET_FOR_EACH (biter, b, sin, 0)
{
fputs (txt[seti], out);
state_item_print (&state_items[sin], out, "");
}
}
}
}
putc ('\n', out);
}
}
fprintf (out, "FIRSTS\n");
for (symbol_number i = ntokens; i < nsyms; ++i)
{
fprintf (out, " %s firsts\n", symbols[i]->tag);
bitset_iterator iter;
symbol_number j;
BITSET_FOR_EACH (iter, FIRSTS (i), j, 0)
fprintf (out, " %s\n", symbols[j]->tag);
}
fputs ("\n\n", out);
}
void
state_items_init (void)
{
time_t start = time (NULL);
init_state_items ();
init_trans ();
init_prods ();
gen_lookaheads ();
init_firsts ();
prune_disabled_paths ();
if (trace_flag & trace_cex)
{
fprintf (stderr, "init: %f\n", difftime (time (NULL), start));
state_items_report (stderr);
}
}
void
state_items_free (void)
{
for (state_item_number i = 0; i < nstate_items; ++i)
if (!SI_DISABLED (i))
{
state_item *si = &state_items[i];
if (si->prods)
bitset_free (si->prods);
bitset_free (si->revs);
}
free (state_items);
bitsetv_free (firsts);
}
/**
* Determine, using precedence and associativity, whether the next
* production is allowed from the current production.
*/
bool
production_allowed (const state_item *si, const state_item *next)
{
sym_content *s1 = item_rule (si->item)->lhs;
sym_content *s2 = item_rule (next->item)->lhs;
int prec1 = s1->prec;
int prec2 = s2->prec;
if (prec1 >= 0 && prec2 >= 0)
{
// Do not expand if lower precedence.
if (prec1 > prec2)
return false;
// Do not expand if same precedence, but left-associative.
if (prec1 == prec2 && s1->assoc == left_assoc)
return false;
}
return true;
}