/* IELR's inadequacy annotation list. Copyright (C) 2009-2015, 2018-2020 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 "AnnotationList.h" #include "system.h" #include "ielr.h" #include "lalr.h" /** * \pre * - annotations_obstackp != NULL. * \post * - \c result is a new \c AnnotationList with one node whose: * - \c inadequacyNode member is \c NULL. * - \c contributions member is allocated with \c contribution_count * uninitialized elements. * - All memory was allocated on \c annotations_obstackp. */ static AnnotationList* AnnotationList__alloc_on_obstack (ContributionIndex contribution_count, struct obstack *annotations_obstackp) { AnnotationList *res; size_t contributions_size = contribution_count * sizeof res->contributions[0]; res = obstack_alloc (annotations_obstackp, offsetof (AnnotationList, contributions) + contributions_size); res->next = NULL; res->inadequacyNode = NULL; return res; } /** * \pre * - self != NULL. * - 0 <= ci < self->inadequacyNode->contributionCount. * \post * - \c result = true iff contribution \c ci in \c self represents an * "always" contribution. */ static bool AnnotationList__isContributionAlways (AnnotationList const *self, ContributionIndex ci) { aver (0 <= ci && ci < self->inadequacyNode->contributionCount); return self->contributions[ci] == NULL; } /** * \pre * - \c self is a single node. * - \c self annotates the same state as every other node in \c list, and * that state has \c nitems kernel items. * \post * - If the list \c list already contains an identical annotation to \c self, * \c self was discarded, \c result is false, and the caller is responsible * for the memory of \c self. * - Otherwise, \c list now contains the node \c self, \c result is true, and * \c list assumes responsibility for the memory of \c self. * - The sort in \c list is: * - Sort in reverse order on the unique ID of the associated * inadequacy node. Because these IDs are assigned in ascending * order, this should mean that the insertion position within an * annotation list is usually near the beginning with other * annotations associated with the same inadequacy. * - Next, sort on the first contribution that is different as follows: * - Sort an always-contribution before a never-contribution before a * potential-contribution. * - Two always-contributions are identical. * - Two never-contributions are identical. * - For two potential-contributions, sort on the contributions' kernel * item bitsets interpreted as binary numbers. * - The sorting has a few effects: * - It accelerates elimination of identical annotations during insertion. * - It determines how the output of \c AnnotationList__debug is sorted. * - Other than that, it's probably not important. */ static bool AnnotationList__insertInto (AnnotationList *self, AnnotationList **list, size_t nitems) { AnnotationList **node; for (node = list; *node; node = &(*node)->next) { int cmp = 0; if (self->inadequacyNode->id < (*node)->inadequacyNode->id) cmp = 1; else if ((*node)->inadequacyNode->id < self->inadequacyNode->id) cmp = -1; else for (ContributionIndex ci = 0; cmp == 0 && ci < self->inadequacyNode->contributionCount; ++ci) { if (AnnotationList__isContributionAlways (self, ci)) { if (!AnnotationList__isContributionAlways (*node, ci)) cmp = -1; } else if (AnnotationList__isContributionAlways (*node, ci)) cmp = 1; else for (size_t item = 0; cmp == 0 && item < nitems; ++item) { if (!Sbitset__test (self->contributions[ci], item)) { if (Sbitset__test ((*node)->contributions[ci], item)) cmp = -1; } else if (!Sbitset__test ((*node)->contributions[ci], item)) cmp = 1; } } if (cmp < 0) { self->next = *node; *node = self; break; } else if (cmp == 0) { self = NULL; break; } } if (!*node) *node = self; return self != NULL; } static bitset AnnotationList__compute_shift_tokens (transitions *trans) { bitset shift_tokens = bitset_create (ntokens, BITSET_FIXED); int i; FOR_EACH_SHIFT (trans, i) bitset_set (shift_tokens, TRANSITION_SYMBOL (trans, i)); return shift_tokens; } static bitset AnnotationList__compute_conflicted_tokens (bitset shift_tokens, reductions *reds) { bitset conflicted_tokens = bitset_create (ntokens, BITSET_FIXED); bitset conflicted_tokens_rule = bitset_create (ntokens, BITSET_FIXED); bitset tokens = bitset_create (ntokens, BITSET_FIXED); bitset_copy (tokens, shift_tokens); for (int i = 0; i < reds->num; ++i) { bitset_and (conflicted_tokens_rule, tokens, reds->lookahead_tokens[i]); bitset_or (conflicted_tokens, conflicted_tokens, conflicted_tokens_rule); bitset_or (tokens, tokens, reds->lookahead_tokens[i]); /* Check that rules are sorted on rule number or the next step in AnnotationList__compute_from_inadequacies will misbehave. */ aver (i == 0 || reds->rules[i-1] < reds->rules[i]); } bitset_free (tokens); bitset_free (conflicted_tokens_rule); return conflicted_tokens; } static bool AnnotationList__compute_lhs_contributions (state *s, const rule *the_rule, symbol_number conflicted_token, bitsetv follow_kernel_items, bitsetv always_follows, state ***predecessors, bitset **item_lookahead_sets, Sbitset *items, struct obstack *annotations_obstackp) { goto_number lhs_goto = map_goto (s->number, the_rule->lhs->number); if (bitset_test (always_follows[lhs_goto], conflicted_token)) return true; *items = Sbitset__new_on_obstack (s->nitems, annotations_obstackp); { bitset_iterator biter_item; bitset_bindex item; BITSET_FOR_EACH (biter_item, follow_kernel_items[lhs_goto], item, 0) if (ielr_item_has_lookahead (s, 0, item, conflicted_token, predecessors, item_lookahead_sets)) Sbitset__set (*items, item); } return false; } static void AnnotationList__computePredecessorAnnotations ( AnnotationList *self, state *s, bitsetv follow_kernel_items, bitsetv always_follows, state ***predecessors, bitset **item_lookahead_sets, AnnotationList **annotation_lists, AnnotationIndex *annotation_counts, struct obstack *annotations_obstackp) { for (state **predecessor = predecessors[s->number]; *predecessor; ++predecessor) { AnnotationList *annotation_node = AnnotationList__alloc_on_obstack ( self->inadequacyNode->contributionCount, annotations_obstackp); annotation_node->inadequacyNode = self->inadequacyNode; bool potential_contribution = false; bitset *lookaheads = NULL; for (ContributionIndex ci = 0; ci < self->inadequacyNode->contributionCount; ++ci) { symbol_number contribution_token = InadequacyList__getContributionToken (self->inadequacyNode, ci) ->content->number; if (AnnotationList__isContributionAlways (self, ci)) { annotation_node->contributions[ci] = NULL; continue; } annotation_node->contributions[ci] = Sbitset__new_on_obstack ((*predecessor)->nitems, annotations_obstackp); { size_t predecessor_item = 0; Sbitset sbiter_item; Sbitset__Index self_item; SBITSET__FOR_EACH (self->contributions[ci], s->nitems, sbiter_item, self_item) { /* If this kernel item is the beginning of a RHS, it must be the kernel item in the start state, and so it has an empty lookahead set. Thus, it can't contribute to inadequacies, and so it should never have been identified as a contribution. If, instead, this kernel item is the successor of the start state's kernel item, the lookahead set is still empty, and so it also should never have been identified as a contribution. This situation is fortunate because we want to avoid the - 2 below in both cases. */ aver (s->items[self_item] > 1); /* If this kernel item is next to the beginning of the RHS, then check all of the predecessor's goto follows for the LHS. */ if (item_number_is_rule_number (ritem[s->items[self_item] - 2])) { Sbitset items; if (AnnotationList__compute_lhs_contributions ( *predecessor, item_rule (&ritem[s->items[self_item]]), contribution_token, follow_kernel_items, always_follows, predecessors, item_lookahead_sets, &items, annotations_obstackp)) { obstack_free (annotations_obstackp, annotation_node->contributions[ci]); annotation_node->contributions[ci] = NULL; // "Break" out of SBITSET__FOR_EACH. goto after_sbitset__for_each; } else { Sbitset__or (annotation_node->contributions[ci], annotation_node->contributions[ci], items, (*predecessor)->nitems); obstack_free (annotations_obstackp, items); } } /* If this kernel item is later in the RHS, then check the predecessor item's lookahead set. */ else { /* We don't have to start the predecessor item search at the beginning every time because items from both states are sorted by their indices in ritem. */ for (; predecessor_item < (*predecessor)->nitems; ++predecessor_item) if ((*predecessor)->items[predecessor_item] == s->items[self_item] - 1) break; aver (predecessor_item != (*predecessor)->nitems); if (ielr_item_has_lookahead (*predecessor, 0, predecessor_item, contribution_token, predecessors, item_lookahead_sets)) Sbitset__set (annotation_node->contributions[ci], predecessor_item); } } after_sbitset__for_each:; } if (annotation_node->contributions[ci]) { Sbitset biter; Sbitset__Index i; SBITSET__FOR_EACH (annotation_node->contributions[ci], (*predecessor)->nitems, biter, i) { potential_contribution = true; if (!lookaheads) { lookaheads = xnmalloc ((*predecessor)->nitems, sizeof *lookaheads); for (size_t j = 0; j < (*predecessor)->nitems; ++j) lookaheads[j] = NULL; } if (!lookaheads[i]) lookaheads[i] = bitset_create (ntokens, BITSET_FIXED); bitset_set (lookaheads[i], contribution_token); } } } /* If the predecessor has any contributions besides just "always" and "never" contributions: - If the dominant contribution is split-stable, the annotation could not affect merging on this predecessor state or its eventual predecessor states. Moreover, all contributions that affect whether the dominant contribution remains dominant must be "always" or "never" contributions in order for the dominant contribution to be split-stable. Thus, the dominant contribution computation result in eventual successor states will not be affected by lookaheads tracked for this predecessor state. (Also, as in the isocore compatibility test, we depend on the fact that isocores with equal dominant contributions will have the same dominant contribution when merged. Otherwise, we might have to worry that the presence of a potential contribution might somehow be the culprit of that behavior and thus need to be tracked regardless of the split stability of the dominant contribution.) Thus, go ahead and discard the annotation to save space now plus time during state splitting. - Otherwise, record the annotation, and compute any resulting annotations needed on predecessor states. */ if (potential_contribution) { if (ContributionIndex__none != AnnotationList__computeDominantContribution ( annotation_node, (*predecessor)->nitems, lookaheads, true)) { obstack_free (annotations_obstackp, annotation_node); annotation_node = NULL; } { for (size_t i = 0; i < (*predecessor)->nitems; ++i) if (lookaheads[i]) bitset_free (lookaheads[i]); free (lookaheads); } if (annotation_node) { if (AnnotationList__insertInto (annotation_node, &annotation_lists[(*predecessor) ->number], (*predecessor)->nitems)) { ++annotation_counts[(*predecessor)->number]; AnnotationList__computePredecessorAnnotations ( annotation_node, *predecessor, follow_kernel_items, always_follows, predecessors, item_lookahead_sets, annotation_lists, annotation_counts, annotations_obstackp); } else obstack_free (annotations_obstackp, annotation_node); } } else obstack_free (annotations_obstackp, annotation_node); } } void AnnotationList__compute_from_inadequacies ( state *s, bitsetv follow_kernel_items, bitsetv always_follows, state ***predecessors, bitset **item_lookahead_sets, InadequacyList **inadequacy_lists, AnnotationList **annotation_lists, AnnotationIndex *annotation_counts, ContributionIndex *max_contributionsp, struct obstack *annotations_obstackp, InadequacyListNodeCount *inadequacy_list_node_count) { /* Return an empty list if s->lookahead_tokens = NULL. */ if (s->consistent) return; bitsetv all_lookaheads = bitsetv_create (s->nitems, ntokens, BITSET_FIXED); bitsetv_ones (all_lookaheads); bitset shift_tokens = AnnotationList__compute_shift_tokens (s->transitions); bitset conflicted_tokens = AnnotationList__compute_conflicted_tokens (shift_tokens, s->reductions); /* Add an inadequacy annotation for each conflicted_token. */ bitset_iterator biter_conflict; bitset_bindex conflicted_token; BITSET_FOR_EACH (biter_conflict, conflicted_tokens, conflicted_token, 0) { AnnotationList *annotation_node; ContributionIndex contribution_count = 0; /* Allocate the annotation node. */ { for (int rule_i = 0; rule_i < s->reductions->num; ++rule_i) if (bitset_test (s->reductions->lookahead_tokens[rule_i], conflicted_token)) ++contribution_count; if (bitset_test (shift_tokens, conflicted_token)) ++contribution_count; annotation_node = AnnotationList__alloc_on_obstack (contribution_count, annotations_obstackp); } /* FIXME: Would a BITSET_FRUGAL or BITEST_SPARSE be more efficient? Now or convert it inside InadequacyList__new_conflict? */ bitset actions = bitset_create (s->reductions->num + 1, BITSET_FIXED); bool potential_contribution = false; /* Add a contribution for each reduction that has conflicted_token as a lookahead. */ { ContributionIndex ci = 0; int item_i = 0; for (int rule_i = 0; rule_i < s->reductions->num; ++rule_i) { rule *the_rule = s->reductions->rules[rule_i]; if (bitset_test (s->reductions->lookahead_tokens[rule_i], conflicted_token)) { bitset_set (actions, rule_i); /* If this reduction is on a kernel item, just add it. */ if (!item_number_is_rule_number (the_rule->rhs[0])) { annotation_node->contributions[ci] = Sbitset__new_on_obstack (s->nitems, annotations_obstackp); /* Catch item_i up to rule_i. This works because both are sorted on rule number. */ while (!item_number_is_rule_number (ritem[s->items[item_i]]) || item_number_as_rule_number (ritem[s->items[item_i]]) != the_rule->number) { ++item_i; aver (item_i < s->nitems); } Sbitset__set (annotation_node->contributions[ci], item_i); } /* Otherwise, add the kernel items whose lookahead sets contribute the conflicted token to this reduction's lookahead set. */ else if (AnnotationList__compute_lhs_contributions ( s, the_rule, conflicted_token, follow_kernel_items, always_follows, predecessors, item_lookahead_sets, &annotation_node->contributions[ci], annotations_obstackp)) { annotation_node->contributions[ci++] = NULL; continue; } /* The lookahead token has to come from somewhere. */ aver (!Sbitset__isEmpty (annotation_node->contributions[ci], s->nitems)); ++ci; potential_contribution = true; } } } /* If there are any contributions besides just "always" contributions: - If there's also a shift contribution, record it. - If the dominant contribution is split-stable, then the annotation could not affect merging, so go ahead and discard the annotation and the inadequacy to save space now plus time during state splitting. - Otherwise, record the annotation and the inadequacy, and compute any resulting annotations needed on predecessor states. */ if (potential_contribution) { if (bitset_test (shift_tokens, conflicted_token)) { bitset_set (actions, s->reductions->num); annotation_node->contributions[contribution_count - 1] = NULL; } { InadequacyList *conflict_node = InadequacyList__new_conflict ( s, symbols[conflicted_token], actions, inadequacy_list_node_count); actions = NULL; annotation_node->inadequacyNode = conflict_node; if (ContributionIndex__none != AnnotationList__computeDominantContribution ( annotation_node, s->nitems, all_lookaheads, true)) { obstack_free (annotations_obstackp, annotation_node); InadequacyList__delete (conflict_node); } else { InadequacyList__prependTo (conflict_node, &inadequacy_lists[s->number]); { bool b = AnnotationList__insertInto (annotation_node, &annotation_lists[s->number], s->nitems); aver (b); (void) b; } /* This aver makes sure the AnnotationList__computeDominantContribution check above does discard annotations in the simplest case of a S/R conflict with no token precedence. */ aver (!bitset_test (shift_tokens, conflicted_token) || symbols[conflicted_token]->content->prec); ++annotation_counts[s->number]; if (contribution_count > *max_contributionsp) *max_contributionsp = contribution_count; AnnotationList__computePredecessorAnnotations ( annotation_node, s, follow_kernel_items, always_follows, predecessors, item_lookahead_sets, annotation_lists, annotation_counts, annotations_obstackp); } } } else { bitset_free (actions); obstack_free (annotations_obstackp, annotation_node); } } bitsetv_free (all_lookaheads); bitset_free (shift_tokens); bitset_free (conflicted_tokens); } void AnnotationList__debug (AnnotationList const *self, size_t nitems, int spaces) { AnnotationList const *a; AnnotationIndex ai; for (a = self, ai = 0; a; a = a->next, ++ai) { fprintf (stderr, "%*sAnnotation %d (manifesting state %d):\n", spaces, "", ai, a->inadequacyNode->manifestingState->number); bitset_bindex rulei = bitset_first (a->inadequacyNode->inadequacy.conflict.actions); for (ContributionIndex ci = 0; ci < a->inadequacyNode->contributionCount; ++ci) { symbol_number token = InadequacyList__getContributionToken (a->inadequacyNode, ci) ->content->number; fprintf (stderr, "%*s", spaces+2, ""); if (ci == InadequacyList__getShiftContributionIndex ( a->inadequacyNode)) fprintf (stderr, "Contributes shift of token %d.\n", token); else { fprintf (stderr, "Contributes token %d", token); aver (rulei != BITSET_BINDEX_MAX); fprintf (stderr, " as lookahead, rule number %d", a->inadequacyNode->manifestingState ->reductions->rules[rulei]->number); rulei = bitset_next (a->inadequacyNode->inadequacy.conflict.actions, rulei+1); if (AnnotationList__isContributionAlways (a, ci)) fprintf (stderr, " always."); else { fprintf (stderr, ", items: "); Sbitset__fprint (a->contributions[ci], nitems, stderr); } fprintf (stderr, "\n"); } } } } void AnnotationList__computeLookaheadFilter (AnnotationList const *self, size_t nitems, bitsetv lookahead_filter) { bitsetv_zero (lookahead_filter); for (; self; self = self->next) for (ContributionIndex ci = 0; ci < self->inadequacyNode->contributionCount; ++ci) if (!AnnotationList__isContributionAlways (self, ci)) { symbol_number token = InadequacyList__getContributionToken (self->inadequacyNode, ci) ->content->number; Sbitset__Index item; Sbitset biter; SBITSET__FOR_EACH (self->contributions[ci], nitems, biter, item) bitset_set (lookahead_filter[item], token); } } /** * \pre * - self != NULL. * - \c nitems is the number of kernel items in the LR(0) state that \c self * annotates. * - \c lookaheads describes the lookahead sets on the kernel items of some * isocore of the LR(0) state that \c self annotates. Either: * - lookaheads = NULL only if the lookahead set on every kernel * item is empty. * - For any 0 <= i < nitems, lookaheads[i] is either: * - \c NULL only if the lookahead set on kernel item \c i is empty. * - The (possibly empty) lookahead set on kernel item \c i. * - 0 <= ci < self->inadequacyNode->contributionCount. * \post * - \c result = true iff contribution \c ci in \c self is made by the state * described by \c lookaheads. */ static bool AnnotationList__stateMakesContribution (AnnotationList const *self, size_t nitems, ContributionIndex ci, bitset *lookaheads) { if (AnnotationList__isContributionAlways (self, ci)) return true; if (!lookaheads) return false; { symbol_number token = InadequacyList__getContributionToken (self->inadequacyNode, ci) ->content->number; Sbitset__Index item; Sbitset biter; SBITSET__FOR_EACH (self->contributions[ci], nitems, biter, item) if (lookaheads[item] && bitset_test (lookaheads[item], token)) return true; } return false; } ContributionIndex AnnotationList__computeDominantContribution (AnnotationList const *self, size_t nitems, bitset *lookaheads, bool require_split_stable) { ContributionIndex const ci_shift = InadequacyList__getShiftContributionIndex (self->inadequacyNode); symbol *token = self->inadequacyNode->inadequacy.conflict.token; /* S/R conflict. */ if (ci_shift != ContributionIndex__none) { bool find_stable_domination_over_shift = false; bool find_stable_error_action_domination = false; { int shift_precedence = token->content->prec; /* If the token has no precedence set, shift is always chosen. */ if (!shift_precedence) return ci_shift; /* Figure out which reductions contribute, which of those would dominate in a R/R comparison, and whether any reduction dominates the shift so that the R/R comparison is actually needed. */ ContributionIndex ci_rr_dominator = ContributionIndex__none; int actioni; ContributionIndex ci; for (ci = 0, actioni = bitset_first (self->inadequacyNode->inadequacy .conflict.actions); ci < self->inadequacyNode->contributionCount; ++ci, actioni = bitset_next (self->inadequacyNode->inadequacy .conflict.actions, actioni+1)) { int reduce_precedence = 0; if (ci == ci_shift) continue; { rule *r = self->inadequacyNode->manifestingState ->reductions->rules[actioni]; if (r->prec) reduce_precedence = r->prec->prec; } /* If there's no need to check whether this reduction actually contributes because the shift eliminates it from the R/R comparison anyway, continue to the next reduction. */ if (reduce_precedence && (reduce_precedence < shift_precedence || (reduce_precedence == shift_precedence && token->content->assoc == right_assoc))) continue; if (!AnnotationList__stateMakesContribution (self, nitems, ci, lookaheads)) continue; /* This uneliminated reduction contributes, so see if it can cause an error action. */ if (reduce_precedence == shift_precedence && token->content->assoc == non_assoc) { /* It's not possible to find split-stable domination over shift after a potential %nonassoc. */ if (find_stable_domination_over_shift) return ContributionIndex__none; if (!require_split_stable || AnnotationList__isContributionAlways (self, ci)) return ContributionIndex__error_action; find_stable_error_action_domination = true; } /* Consider this uneliminated contributing reduction in the R/R comparison. */ if (ci_rr_dominator == ContributionIndex__none) ci_rr_dominator = ci; /* If precedence is set for this uneliminated contributing reduction, it dominates the shift, so try to figure out which reduction dominates the R/R comparison. */ if (reduce_precedence) { /* It's not possible to find split-stable error action domination after a potential reduction. */ if (find_stable_error_action_domination) return ContributionIndex__none; if (!require_split_stable) return ci_rr_dominator; if (!AnnotationList__isContributionAlways (self, ci_rr_dominator)) return ContributionIndex__none; if (AnnotationList__isContributionAlways (self, ci)) return ci_rr_dominator; find_stable_domination_over_shift = true; } } } if (find_stable_domination_over_shift || find_stable_error_action_domination) return ContributionIndex__none; /* No reduce or error action domination found, so shift dominates. */ return ci_shift; } /* R/R conflict, so the reduction with the lowest rule number dominates. Fortunately, contributions are sorted by rule number. */ for (ContributionIndex ci = 0; ci < self->inadequacyNode->contributionCount; ++ci) if (AnnotationList__stateMakesContribution (self, nitems, ci, lookaheads)) { if (require_split_stable && !AnnotationList__isContributionAlways (self, ci)) return ContributionIndex__none; return ci; } return ContributionIndex__none; }