diff options
Diffstat (limited to 'gcc/rtl-ssa/accesses.cc')
-rw-r--r-- | gcc/rtl-ssa/accesses.cc | 1594 |
1 files changed, 1594 insertions, 0 deletions
diff --git a/gcc/rtl-ssa/accesses.cc b/gcc/rtl-ssa/accesses.cc new file mode 100644 index 0000000..6a28007 --- /dev/null +++ b/gcc/rtl-ssa/accesses.cc @@ -0,0 +1,1594 @@ +// Implementation of access-related functions for RTL SSA -*- C++ -*- +// Copyright (C) 2020 Free Software Foundation, Inc. +// +// This file is part of GCC. +// +// GCC 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, or (at your option) any later +// version. +// +// GCC 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 GCC; see the file COPYING3. If not see +// <http://www.gnu.org/licenses/>. + +#define INCLUDE_ALGORITHM +#define INCLUDE_FUNCTIONAL +#include "config.h" +#include "system.h" +#include "coretypes.h" +#include "backend.h" +#include "rtl.h" +#include "df.h" +#include "rtl-ssa.h" +#include "rtl-ssa/internals.inl" + +using namespace rtl_ssa; + +// This clobber belongs to a clobber_group but m_group appears to be +// out of date. Update it and return the new (correct) value. +clobber_group * +clobber_info::recompute_group () +{ + using splay_tree = clobber_info::splay_tree; + + // Splay this clobber to the root of the tree while searching for a node + // that has the correct group. The root always has the correct group, + // so the search always breaks early and does not install this clobber + // as the root. + clobber_info *cursor = m_parent; + auto find_group = [](clobber_info *node, unsigned int) + { + return node->m_group->has_been_superceded () ? nullptr : node->m_group; + }; + clobber_group *group = splay_tree::splay_and_search (this, nullptr, + find_group); + gcc_checking_assert (m_parent); + + // If the previous splay operation did anything, this clobber is now an + // ancestor of CURSOR, and all the nodes inbetween have a stale group. + // Since we have visited the nodes, we might as well update them too. + // + // If the previous splay operation did nothing, start the update from + // this clobber instead. In that case we change at most two clobbers: + // this clobber and possibly its parent. + if (cursor == m_parent) + cursor = this; + + // Walk up the tree from CURSOR updating clobbers that need it. + // This walk always includes this clobber. + while (cursor->m_group != group) + { + cursor->m_group = group; + cursor = cursor->m_parent; + } + + gcc_checking_assert (m_group == group); + return group; +} + +// See the comment above the declaration. +void +resource_info::print_identifier (pretty_printer *pp) const +{ + if (is_mem ()) + pp_string (pp, "mem"); + else + { + char tmp[3 * sizeof (regno) + 2]; + snprintf (tmp, sizeof (tmp), "r%d", regno); + pp_string (pp, tmp); + } +} + +// See the comment above the declaration. +void +resource_info::print_context (pretty_printer *pp) const +{ + if (HARD_REGISTER_NUM_P (regno)) + { + if (const char *name = reg_names[regno]) + { + pp_space (pp); + pp_left_paren (pp); + pp_string (pp, name); + if (mode != E_BLKmode) + { + pp_colon (pp); + pp_string (pp, GET_MODE_NAME (mode)); + } + pp_right_paren (pp); + } + } + else if (is_reg ()) + { + pp_space (pp); + pp_left_paren (pp); + if (mode != E_BLKmode) + { + pp_string (pp, GET_MODE_NAME (mode)); + pp_space (pp); + } + pp_string (pp, "pseudo"); + pp_right_paren (pp); + } +} + +// See the comment above the declaration. +void +resource_info::print (pretty_printer *pp) const +{ + print_identifier (pp); + print_context (pp); +} + +// Some properties can naturally be described using adjectives that attach +// to nouns like "use" or "definition". Print such adjectives to PP. +void +access_info::print_prefix_flags (pretty_printer *pp) const +{ + if (m_is_temp) + pp_string (pp, "temporary "); + if (m_has_been_superceded) + pp_string (pp, "superceded "); +} + +// Print properties not handled by print_prefix_flags to PP, putting +// each property on a new line indented by two extra spaces. +void +access_info::print_properties_on_new_lines (pretty_printer *pp) const +{ + if (m_is_pre_post_modify) + { + pp_newline_and_indent (pp, 2); + pp_string (pp, "set by a pre/post-modify"); + pp_indentation (pp) -= 2; + } + if (m_includes_address_uses) + { + pp_newline_and_indent (pp, 2); + pp_string (pp, "appears inside an address"); + pp_indentation (pp) -= 2; + } + if (m_includes_read_writes) + { + pp_newline_and_indent (pp, 2); + pp_string (pp, "appears in a read/write context"); + pp_indentation (pp) -= 2; + } + if (m_includes_subregs) + { + pp_newline_and_indent (pp, 2); + pp_string (pp, "appears inside a subreg"); + pp_indentation (pp) -= 2; + } +} + +// Return true if there are no known issues with the integrity of the +// link information. +inline bool +use_info::check_integrity () +{ + auto subsequence_id = [](use_info *use) + { + if (use->is_in_nondebug_insn ()) + return 1; + if (use->is_in_debug_insn ()) + return 2; + return 3; + }; + + use_info *prev = prev_use (); + use_info *next = next_use (); + + if (prev && subsequence_id (prev) > subsequence_id (this)) + return false; + if (next && subsequence_id (next) < subsequence_id (this)) + return false; + if (m_is_last_nondebug_insn_use != calculate_is_last_nondebug_insn_use ()) + return false; + + if (!prev && last_use ()->next_use ()) + return false; + if (!next) + if (use_info *use = last_nondebug_insn_use ()) + if (!use->m_is_last_nondebug_insn_use) + return false; + + return true; +} + +// See the comment above the declaration. +void +use_info::print_location (pretty_printer *pp) const +{ + if (is_in_phi ()) + pp_access (pp, phi (), PP_ACCESS_INCLUDE_LOCATION); + else + insn ()->print_identifier_and_location (pp); +} + +// See the comment above the declaration. +void +use_info::print_def (pretty_printer *pp) const +{ + if (const set_info *set = def ()) + pp_access (pp, set, 0); + else + { + pp_string (pp, "undefined "); + resource ().print (pp); + } +} + +// See the comment above the declaration. +void +use_info::print (pretty_printer *pp, unsigned int flags) const +{ + print_prefix_flags (pp); + + const set_info *set = def (); + if (set && set->mode () != mode ()) + { + pp_string (pp, GET_MODE_NAME (mode ())); + pp_space (pp); + } + + pp_string (pp, "use of "); + print_def (pp); + if (flags & PP_ACCESS_INCLUDE_LOCATION) + { + pp_string (pp, " by "); + print_location (pp); + } + if (set && (flags & PP_ACCESS_INCLUDE_LINKS)) + { + pp_newline_and_indent (pp, 2); + pp_string (pp, "defined in "); + set->insn ()->print_location (pp); + pp_indentation (pp) -= 2; + } + if (flags & PP_ACCESS_INCLUDE_PROPERTIES) + print_properties_on_new_lines (pp); +} + +// See the comment above the declaration. +void +def_info::print_identifier (pretty_printer *pp) const +{ + resource ().print_identifier (pp); + pp_colon (pp); + insn ()->print_identifier (pp); + resource ().print_context (pp); +} + +// See the comment above the declaration. +void +def_info::print_location (pretty_printer *pp) const +{ + insn ()->print_identifier_and_location (pp); +} + +// See the comment above the declaration. +void +clobber_info::print (pretty_printer *pp, unsigned int flags) const +{ + print_prefix_flags (pp); + if (is_call_clobber ()) + pp_string (pp, "call "); + pp_string (pp, "clobber "); + print_identifier (pp); + if (flags & PP_ACCESS_INCLUDE_LOCATION) + { + pp_string (pp, " in "); + insn ()->print_location (pp); + } + if (flags & PP_ACCESS_INCLUDE_PROPERTIES) + print_properties_on_new_lines (pp); +} + +// See the comment above the declaration. +void +set_info::print_uses_on_new_lines (pretty_printer *pp) const +{ + for (const use_info *use : all_uses ()) + { + pp_newline_and_indent (pp, 2); + if (use->is_live_out_use ()) + { + pp_string (pp, "live out from "); + use->insn ()->print_location (pp); + } + else + { + pp_string (pp, "used by "); + use->print_location (pp); + } + pp_indentation (pp) -= 2; + } + if (m_use_tree) + { + pp_newline_and_indent (pp, 2); + pp_string (pp, "splay tree:"); + pp_newline_and_indent (pp, 2); + auto print_use = [](pretty_printer *pp, + splay_tree_node<use_info *> *node) + { + pp_string (pp, "use by "); + node->value ()->print_location (pp); + }; + m_use_tree.print (pp, m_use_tree.root (), print_use); + pp_indentation (pp) -= 4; + } +} + +// See the comment above the declaration. +void +set_info::print (pretty_printer *pp, unsigned int flags) const +{ + print_prefix_flags (pp); + pp_string (pp, "set "); + print_identifier (pp); + if (flags & PP_ACCESS_INCLUDE_LOCATION) + { + pp_string (pp, " in "); + insn ()->print_location (pp); + } + if (flags & PP_ACCESS_INCLUDE_PROPERTIES) + print_properties_on_new_lines (pp); + if (flags & PP_ACCESS_INCLUDE_LINKS) + print_uses_on_new_lines (pp); +} + +// See the comment above the declaration. +void +phi_info::print (pretty_printer *pp, unsigned int flags) const +{ + print_prefix_flags (pp); + pp_string (pp, "phi node "); + print_identifier (pp); + if (flags & PP_ACCESS_INCLUDE_LOCATION) + { + pp_string (pp, " in "); + insn ()->print_location (pp); + } + + if (flags & PP_ACCESS_INCLUDE_PROPERTIES) + print_properties_on_new_lines (pp); + + if (flags & PP_ACCESS_INCLUDE_LINKS) + { + basic_block cfg_bb = bb ()->cfg_bb (); + pp_newline_and_indent (pp, 2); + pp_string (pp, "inputs:"); + unsigned int i = 0; + for (const use_info *input : inputs ()) + { + basic_block pred_cfg_bb = EDGE_PRED (cfg_bb, i)->src; + pp_newline_and_indent (pp, 2); + pp_string (pp, "bb"); + pp_decimal_int (pp, pred_cfg_bb->index); + pp_colon (pp); + pp_space (pp); + input->print_def (pp); + pp_indentation (pp) -= 2; + i += 1; + } + pp_indentation (pp) -= 2; + + print_uses_on_new_lines (pp); + } +} + +// See the comment above the declaration. +void +set_node::print (pretty_printer *pp) const +{ + pp_access (pp, first_def ()); +} + +// See the comment above the declaration. +void +clobber_group::print (pretty_printer *pp) const +{ + auto print_clobber = [](pretty_printer *pp, const def_info *clobber) + { + pp_access (pp, clobber); + }; + pp_string (pp, "grouped clobber"); + for (const def_info *clobber : clobbers ()) + { + pp_newline_and_indent (pp, 2); + print_clobber (pp, clobber); + pp_indentation (pp) -= 2; + } + pp_newline_and_indent (pp, 2); + pp_string (pp, "splay tree"); + pp_newline_and_indent (pp, 2); + m_clobber_tree.print (pp, print_clobber); + pp_indentation (pp) -= 4; +} + +// Return a clobber_group for CLOBBER, creating one if CLOBBER doesn't +// already belong to a group. +clobber_group * +function_info::need_clobber_group (clobber_info *clobber) +{ + if (clobber->is_in_group ()) + return clobber->group (); + return allocate<clobber_group> (clobber); +} + +// Return a def_node for inserting DEF into the associated resource's +// splay tree. Use a clobber_group if DEF is a clobber and a set_node +// otherwise. +def_node * +function_info::need_def_node (def_info *def) +{ + if (auto *clobber = dyn_cast<clobber_info *> (def)) + return need_clobber_group (clobber); + return allocate<set_node> (as_a<set_info *> (def)); +} + +// LAST is the last thing to define LAST->resource (), and is where any +// splay tree root for LAST->resource () is stored. Require such a splay tree +// to exist, creating a new one if necessary. Return the root of the tree. +// +// The caller must call LAST->set_splay_root after it has finished with +// the splay tree. +def_splay_tree +function_info::need_def_splay_tree (def_info *last) +{ + if (def_node *root = last->splay_root ()) + return root; + + // Use a left-spine rooted at the last node. + def_node *root = need_def_node (last); + def_node *parent = root; + while (def_info *prev = first_def (parent)->prev_def ()) + { + def_node *node = need_def_node (prev); + def_splay_tree::insert_child (parent, 0, node); + parent = node; + } + return root; +} + +// Search TREE for either: +// +// - a set_info at INSN or +// - a clobber_group whose range includes INSN +// +// If such a node exists, install it as the root of TREE and return 0. +// Otherwise arbitrarily choose between: +// +// (1) Installing the closest preceding node as the root and returning 1. +// (2) Installing the closest following node as the root and returning -1. +// +// Note that this routine should not be used to check whether INSN +// itself defines a resource; that can be checked more cheaply using +// find_access_index. +int +rtl_ssa::lookup_def (def_splay_tree &tree, insn_info *insn) +{ + auto go_left = [&](def_node *node) + { + return *insn < *first_def (node)->insn (); + }; + auto go_right = [&](def_node *node) + { + return *insn > *last_def (node)->insn (); + }; + return tree.lookup (go_left, go_right); +} + +// Search TREE for a clobber in INSN. If such a clobber exists, install +// it as the root of TREE and return 0. Otherwise arbitrarily choose between: +// +// (1) Installing the closest preceding clobber as the root and returning 1. +// (2) Installing the closest following clobber as the root and returning -1. +int +rtl_ssa::lookup_clobber (clobber_tree &tree, insn_info *insn) +{ + auto compare = [&](clobber_info *clobber) + { + return insn->compare_with (clobber->insn ()); + }; + return tree.lookup (compare); +} + +// Search for a definition of RESOURCE at INSN and return the result of +// the search as a def_lookup. See the comment above the class for more +// details. +def_lookup +function_info::find_def (resource_info resource, insn_info *insn) +{ + def_info *first = m_defs[resource.regno + 1]; + if (!first) + // There are no nodes. The comparison result is pretty meaningless + // in this case. + return { nullptr, -1 }; + + // See whether the first node matches. + auto first_result = clobber_group_or_single_def (first); + if (*insn <= *last_def (first_result)->insn ()) + { + int comparison = (*insn >= *first->insn () ? 0 : -1); + return { first_result, comparison }; + } + + // See whether the last node matches. + def_info *last = first->last_def (); + auto last_result = clobber_group_or_single_def (last); + if (*insn >= *first_def (last_result)->insn ()) + { + int comparison = (*insn <= *last->insn () ? 0 : 1); + return { last_result, comparison }; + } + + // Resort to using a splay tree to search for the result. + def_splay_tree tree = need_def_splay_tree (last); + int comparison = lookup_def (tree, insn); + last->set_splay_root (tree.root ()); + return { tree.root (), comparison }; +} + +// Add DEF to the function's list of definitions of DEF->resource (), +// inserting DEF immediately before BEFORE. DEF is not currently in the list. +void +function_info::insert_def_before (def_info *def, def_info *before) +{ + gcc_checking_assert (!def->has_def_links () + && *before->insn () > *def->insn ()); + + def->copy_prev_from (before); + if (def_info *prev = def->prev_def ()) + { + gcc_checking_assert (*prev->insn () < *def->insn ()); + prev->set_next_def (def); + } + else + m_defs[def->regno () + 1] = def; + + def->set_next_def (before); + before->set_prev_def (def); +} + +// Add DEF to the function's list of definitions of DEF->resource (), +// inserting DEF immediately after AFTER. DEF is not currently in the list. +void +function_info::insert_def_after (def_info *def, def_info *after) +{ + gcc_checking_assert (!def->has_def_links () + && *after->insn () < *def->insn ()); + + def->copy_next_from (after); + if (def_info *next = def->next_def ()) + { + gcc_checking_assert (*next->insn () > *def->insn ()); + next->set_prev_def (def); + } + else + m_defs[def->regno () + 1]->set_last_def (def); + + def->set_prev_def (after); + after->set_next_def (def); +} + +// Remove DEF from the function's list of definitions of DEF->resource (). +void +function_info::remove_def_from_list (def_info *def) +{ + def_info *prev = def->prev_def (); + def_info *next = def->next_def (); + + if (next) + next->copy_prev_from (def); + else + m_defs[def->regno () + 1]->set_last_def (prev); + + if (prev) + prev->copy_next_from (def); + else + m_defs[def->regno () + 1] = next; + + def->clear_def_links (); +} + +// Add CLOBBER to GROUP and insert it into the function's list of +// accesses to CLOBBER->resource (). CLOBBER is not currently part +// of an active group and is not currently in the list. +void +function_info::add_clobber (clobber_info *clobber, clobber_group *group) +{ + // Search for either the previous or next clobber in the group. + // The result is less than zero if CLOBBER should come before NEIGHBOR + // or greater than zero if CLOBBER should come after NEIGHBOR. + int comparison = lookup_clobber (group->m_clobber_tree, clobber->insn ()); + gcc_checking_assert (comparison != 0); + clobber_info *neighbor = group->m_clobber_tree.root (); + + // Since HEIGHBOR is now the root of the splay tree, its group needs + // to be up-to-date. + neighbor->update_group (group); + + // If CLOBBER comes before NEIGHBOR, insert CLOBBER to NEIGHBOR's left, + // otherwise insert CLOBBER to NEIGHBOR's right. + clobber_info::splay_tree::insert_child (neighbor, comparison > 0, clobber); + clobber->set_group (group); + + // Insert the clobber into the function-wide list and update the + // bounds of the group. + if (comparison > 0) + { + insert_def_after (clobber, neighbor); + if (neighbor == group->last_clobber ()) + group->set_last_clobber (clobber); + } + else + { + insert_def_before (clobber, neighbor); + if (neighbor == group->first_clobber ()) + group->set_first_clobber (clobber); + } +} + +// Remove CLOBBER from GROUP, given that GROUP contains other clobbers too. +// Also remove CLOBBER from the function's list of accesses to +// CLOBBER->resource (). +void +function_info::remove_clobber (clobber_info *clobber, clobber_group *group) +{ + if (clobber == group->first_clobber ()) + { + auto *new_first = as_a<clobber_info *> (clobber->next_def ()); + group->set_first_clobber (new_first); + new_first->update_group (group); + } + else if (clobber == group->last_clobber ()) + { + auto *new_last = as_a<clobber_info *> (clobber->prev_def ()); + group->set_last_clobber (new_last); + new_last->update_group (group); + } + + clobber_info *replacement = clobber_info::splay_tree::remove_node (clobber); + if (clobber == group->m_clobber_tree.root ()) + { + group->m_clobber_tree = replacement; + replacement->update_group (group); + } + clobber->set_group (nullptr); + + remove_def_from_list (clobber); +} + +// Add CLOBBER immediately before the first clobber in GROUP, given that +// CLOBBER is not currently part of any group. +void +function_info::prepend_clobber_to_group (clobber_info *clobber, + clobber_group *group) +{ + clobber_info *next = group->first_clobber (); + clobber_info::splay_tree::insert_child (next, 0, clobber); + group->set_first_clobber (clobber); + clobber->set_group (group); +} + +// Add CLOBBER immediately after the last clobber in GROUP, given that +// CLOBBER is not currently part of any group. +void +function_info::append_clobber_to_group (clobber_info *clobber, + clobber_group *group) +{ + clobber_info *prev = group->last_clobber (); + clobber_info::splay_tree::insert_child (prev, 1, clobber); + group->set_last_clobber (clobber); + clobber->set_group (group); +} + +// Put CLOBBER1 and CLOBBER2 into the same clobber_group, given that +// CLOBBER1 occurs immediately before CLOBBER2 and that the two clobbers +// are not currently in the same group. LAST is the last definition of +// the associated resource, and is where any splay tree is stored. +void +function_info::merge_clobber_groups (clobber_info *clobber1, + clobber_info *clobber2, + def_info *last) +{ + if (clobber1->is_in_group () && clobber2->is_in_group ()) + { + clobber_group *group1 = clobber1->group (); + clobber_group *group2 = clobber2->group (); + gcc_checking_assert (clobber1 == group1->last_clobber () + && clobber2 == group2->first_clobber ()); + + if (def_splay_tree tree = last->splay_root ()) + { + // Remove GROUP2 from the splay tree. + int comparison = lookup_def (tree, clobber2->insn ()); + gcc_checking_assert (comparison == 0); + tree.remove_root (); + last->set_splay_root (tree.root ()); + } + + // Splice the trees together. + group1->m_clobber_tree.splice_next_tree (group2->m_clobber_tree); + + // Bring the two extremes of GROUP2 under GROUP1. Any other + // clobbers in the group are updated lazily on demand. + clobber2->set_group (group1); + group2->last_clobber ()->set_group (group1); + group1->set_last_clobber (group2->last_clobber ()); + + // Record that GROUP2 is no more. + group2->set_first_clobber (nullptr); + group2->set_last_clobber (nullptr); + group2->m_clobber_tree = nullptr; + } + else + { + // In this case there can be no active splay tree. + gcc_assert (!last->splay_root ()); + if (clobber2->is_in_group ()) + prepend_clobber_to_group (clobber1, clobber2->group ()); + else + append_clobber_to_group (clobber2, need_clobber_group (clobber1)); + } +} + +// GROUP spans INSN, and INSN now sets the resource that GROUP clobbers. +// Split GROUP around INSN and return the clobber that comes immediately +// before INSN. +clobber_info * +function_info::split_clobber_group (clobber_group *group, insn_info *insn) +{ + // Search for either the previous or next clobber in the group. + // The result is less than zero if CLOBBER should come before NEIGHBOR + // or greater than zero if CLOBBER should come after NEIGHBOR. + int comparison = lookup_clobber (group->m_clobber_tree, insn); + gcc_checking_assert (comparison != 0); + clobber_info *neighbor = group->m_clobber_tree.root (); + + clobber_tree tree1, tree2; + clobber_info *prev; + clobber_info *next; + if (comparison > 0) + { + // NEIGHBOR is the last clobber in what will become the first group. + tree1 = neighbor; + tree2 = tree1.split_after_root (); + prev = neighbor; + next = as_a<clobber_info *> (prev->next_def ()); + } + else + { + // NEIGHBOR is the first clobber in what will become the second group. + tree2 = neighbor; + tree1 = tree2.split_before_root (); + next = neighbor; + prev = as_a<clobber_info *> (next->prev_def ()); + } + + // Use GROUP to hold PREV and earlier clobbers. Create a new group for + // NEXT onwards. + clobber_info *last_clobber = group->last_clobber (); + clobber_group *group1 = group; + clobber_group *group2 = allocate<clobber_group> (next); + + // Finish setting up GROUP1, making sure that the roots and extremities + // have a correct group pointer. Leave the rest to be updated lazily. + group1->set_last_clobber (prev); + tree1->set_group (group1); + prev->set_group (group1); + + // Finish setting up GROUP2, with the same approach as for GROUP1. + group2->set_first_clobber (next); + group2->set_last_clobber (last_clobber); + next->set_group (group2); + tree2->set_group (group2); + last_clobber->set_group (group2); + + return prev; +} + +// Add DEF to the end of the function's list of definitions of +// DEF->resource (). There is known to be no associated splay tree yet. +void +function_info::append_def (def_info *def) +{ + gcc_checking_assert (!def->has_def_links ()); + def_info **head = &m_defs[def->regno () + 1]; + def_info *first = *head; + if (!first) + { + // This is the only definition of the resource. + def->set_last_def (def); + *head = def; + return; + } + + def_info *prev = first->last_def (); + gcc_checking_assert (!prev->splay_root ()); + + // Maintain the invariant that two clobbers must not appear in + // neighboring nodes of the splay tree. + auto *clobber = dyn_cast<clobber_info *> (def); + auto *prev_clobber = dyn_cast<clobber_info *> (prev); + if (clobber && prev_clobber) + append_clobber_to_group (clobber, need_clobber_group (prev_clobber)); + + prev->set_next_def (def); + def->set_prev_def (prev); + first->set_last_def (def); +} + +// Add DEF to the function's list of definitions of DEF->resource (). +// Also insert it into the associated splay tree, if there is one. +// DEF is not currently part of the list and is not in the splay tree. +void +function_info::add_def (def_info *def) +{ + gcc_checking_assert (!def->has_def_links () + && !def->m_is_temp + && !def->m_has_been_superceded); + def_info **head = &m_defs[def->regno () + 1]; + def_info *first = *head; + if (!first) + { + // This is the only definition of the resource. + def->set_last_def (def); + *head = def; + return; + } + + def_info *last = first->last_def (); + insn_info *insn = def->insn (); + + int comparison; + def_node *root = nullptr; + def_info *prev = nullptr; + def_info *next = nullptr; + if (*insn > *last->insn ()) + { + // This definition comes after all other definitions. + comparison = 1; + if (def_splay_tree tree = last->splay_root ()) + { + tree.splay_max_node (); + root = tree.root (); + last->set_splay_root (root); + } + prev = last; + } + else if (*insn < *first->insn ()) + { + // This definition comes before all other definitions. + comparison = -1; + if (def_splay_tree tree = last->splay_root ()) + { + tree.splay_min_node (); + root = tree.root (); + last->set_splay_root (root); + } + next = first; + } + else + { + // Search the splay tree for an insertion point. + def_splay_tree tree = need_def_splay_tree (last); + comparison = lookup_def (tree, insn); + root = tree.root (); + last->set_splay_root (root); + + // Deal with cases in which we found an overlapping live range. + if (comparison == 0) + { + auto *group = as_a<clobber_group *> (tree.root ()); + if (auto *clobber = dyn_cast<clobber_info *> (def)) + { + add_clobber (clobber, group); + return; + } + prev = split_clobber_group (group, insn); + next = prev->next_def (); + } + // COMPARISON is < 0 if DEF comes before ROOT or > 0 if DEF comes + // after ROOT. + else if (comparison < 0) + { + next = first_def (root); + prev = next->prev_def (); + } + else + { + prev = last_def (root); + next = prev->next_def (); + } + } + + // See if we should merge CLOBBER with a neighboring clobber. + auto *clobber = dyn_cast<clobber_info *> (def); + auto *prev_clobber = safe_dyn_cast<clobber_info *> (prev); + auto *next_clobber = safe_dyn_cast<clobber_info *> (next); + // We shouldn't have consecutive clobber_groups. + gcc_checking_assert (!(clobber && prev_clobber && next_clobber)); + if (clobber && prev_clobber) + append_clobber_to_group (clobber, need_clobber_group (prev_clobber)); + else if (clobber && next_clobber) + prepend_clobber_to_group (clobber, need_clobber_group (next_clobber)); + else if (root) + { + // If DEF comes before ROOT, insert DEF to ROOT's left, + // otherwise insert DEF to ROOT's right. + def_node *node = need_def_node (def); + def_splay_tree::insert_child (root, comparison >= 0, node); + } + if (prev) + insert_def_after (def, prev); + else + insert_def_before (def, next); +} + +// Remove DEF from the function's list of definitions of DEF->resource (). +// Also remove DEF from the associated splay tree, if there is one. +void +function_info::remove_def (def_info *def) +{ + def_info **head = &m_defs[def->regno () + 1]; + def_info *first = *head; + gcc_checking_assert (first); + if (first->is_last_def ()) + { + // DEF is the only definition of the resource. + gcc_checking_assert (first == def); + *head = nullptr; + def->clear_def_links (); + return; + } + + // If CLOBBER belongs to a clobber_group that contains other clobbers + // too, then we need to update the clobber_group and the list, but any + // splay tree that contains the clobber_group is unaffected. + if (auto *clobber = dyn_cast<clobber_info *> (def)) + if (clobber->is_in_group ()) + { + clobber_group *group = clobber->group (); + if (group->first_clobber () != group->last_clobber ()) + { + remove_clobber (clobber, group); + return; + } + } + + // If we've created a splay tree for this resource, remove the entry + // for DEF. + def_info *last = first->last_def (); + if (def_splay_tree tree = last->splay_root ()) + { + int comparison = lookup_def (tree, def->insn ()); + gcc_checking_assert (comparison == 0); + tree.remove_root (); + last->set_splay_root (tree.root ()); + } + + // If the definition came between two clobbers, merge them into a single + // group. + auto *prev_clobber = safe_dyn_cast<clobber_info *> (def->prev_def ()); + auto *next_clobber = safe_dyn_cast<clobber_info *> (def->next_def ()); + if (prev_clobber && next_clobber) + merge_clobber_groups (prev_clobber, next_clobber, last); + + remove_def_from_list (def); +} + +// Require DEF to have a splay tree that contains all non-phi uses. +void +function_info::need_use_splay_tree (set_info *def) +{ + if (!def->m_use_tree) + for (use_info *use : def->all_insn_uses ()) + { + auto *use_node = allocate<splay_tree_node<use_info *>> (use); + def->m_use_tree.insert_max_node (use_node); + } +} + +// Compare two instructions by their position in a use splay tree. Return >0 +// if INSN1 comes after INSN2, <0 if INSN1 comes before INSN2, or 0 if they are +// the same instruction. +static inline int +compare_use_insns (insn_info *insn1, insn_info *insn2) +{ + // Debug instructions go after nondebug instructions. + int diff = insn1->is_debug_insn () - insn2->is_debug_insn (); + if (diff != 0) + return diff; + return insn1->compare_with (insn2); +} + +// Search TREE for a use in INSN. If such a use exists, install it as +// the root of TREE and return 0. Otherwise arbitrarily choose between: +// +// (1) Installing the closest preceding use as the root and returning 1. +// (2) Installing the closest following use as the root and returning -1. +int +rtl_ssa::lookup_use (splay_tree<use_info *> &tree, insn_info *insn) +{ + auto compare = [&](splay_tree_node<use_info *> *node) + { + return compare_use_insns (insn, node->value ()->insn ()); + }; + return tree.lookup (compare); +} + +// Add USE to USE->def ()'s list of uses. inserting USE immediately before +// BEFORE. USE is not currently in the list. +// +// This routine should not be used for inserting phi uses. +void +function_info::insert_use_before (use_info *use, use_info *before) +{ + gcc_checking_assert (!use->has_use_links () && use->is_in_any_insn ()); + + set_info *def = use->def (); + + use->copy_prev_from (before); + use->set_next_use (before); + + if (use_info *prev = use->prev_use ()) + prev->set_next_use (use); + else + use->def ()->set_first_use (use); + + before->set_prev_use (use); + if (use->is_in_nondebug_insn () && before->is_in_debug_insn_or_phi ()) + def->last_use ()->set_last_nondebug_insn_use (use); + + gcc_checking_assert (use->check_integrity () && before->check_integrity ()); +} + +// Add USE to USE->def ()'s list of uses. inserting USE immediately after +// AFTER. USE is not currently in the list. +// +// This routine should not be used for inserting phi uses. +void +function_info::insert_use_after (use_info *use, use_info *after) +{ + set_info *def = use->def (); + gcc_checking_assert (after->is_in_any_insn () + && !use->has_use_links () + && use->is_in_any_insn ()); + + use->set_prev_use (after); + use->copy_next_from (after); + + after->set_next_use (use); + + if (use_info *next = use->next_use ()) + { + // The last node doesn't change, but we might need to update its + // last_nondebug_insn_use record. + if (use->is_in_nondebug_insn () && next->is_in_debug_insn_or_phi ()) + def->last_use ()->set_last_nondebug_insn_use (use); + next->set_prev_use (use); + } + else + { + // USE is now the last node. + if (use->is_in_nondebug_insn ()) + use->set_last_nondebug_insn_use (use); + def->first_use ()->set_last_use (use); + } + + gcc_checking_assert (use->check_integrity () && after->check_integrity ()); +} + +// If USE has a known definition, add USE to that definition's list of uses. +// Also update the associated splay tree, if any. +void +function_info::add_use (use_info *use) +{ + gcc_checking_assert (!use->has_use_links () + && !use->m_is_temp + && !use->m_has_been_superceded); + + set_info *def = use->def (); + if (!def) + return; + + use_info *first = def->first_use (); + if (!first) + { + // This is the only use of the definition. + use->set_last_use (use); + if (use->is_in_nondebug_insn ()) + use->set_last_nondebug_insn_use (use); + + def->set_first_use (use); + + gcc_checking_assert (use->check_integrity ()); + return; + } + + if (use->is_in_phi ()) + { + // Add USE at the end of the list, as the new first phi. + use_info *last = first->last_use (); + + use->set_prev_use (last); + use->copy_next_from (last); + + last->set_next_use (use); + first->set_last_use (use); + + gcc_checking_assert (use->check_integrity ()); + return; + } + + // If there is currently no splay tree for this definition, see if can + // get away with a pure list-based update. + insn_info *insn = use->insn (); + auto quick_path = [&]() + { + // Check if USE should come before all current uses. + if (first->is_in_phi () || compare_use_insns (insn, first->insn ()) < 0) + { + insert_use_before (use, first); + return true; + } + + // Check if USE should come after all current uses in the same + // subsequence (i.e. the list of nondebug insn uses or the list + // of debug insn uses). + use_info *last = first->last_use (); + if (use->is_in_debug_insn ()) + { + if (last->is_in_phi ()) + return false; + } + else + last = last->last_nondebug_insn_use (); + + if (compare_use_insns (insn, last->insn ()) > 0) + { + insert_use_after (use, last); + return true; + } + + return false; + }; + if (!def->m_use_tree && quick_path ()) + return; + + // Search the splay tree for an insertion point. COMPARISON is less + // than zero if USE should come before NEIGHBOR, or greater than zero + // if USE should come after NEIGHBOR. + need_use_splay_tree (def); + int comparison = lookup_use (def->m_use_tree, insn); + gcc_checking_assert (comparison != 0); + splay_tree_node<use_info *> *neighbor = def->m_use_tree.root (); + + // If USE comes before NEIGHBOR, insert USE to NEIGHBOR's left, + // otherwise insert USE to NEIGHBOR's right. + auto *use_node = allocate<splay_tree_node<use_info *>> (use); + def->m_use_tree.insert_child (neighbor, comparison > 0, use_node); + if (comparison > 0) + insert_use_after (use, neighbor->value ()); + else + insert_use_before (use, neighbor->value ()); +} + +// If USE has a known definition, remove USE from that definition's list +// of uses. Also remove if it from the associated splay tree, if any. +void +function_info::remove_use (use_info *use) +{ + set_info *def = use->def (); + if (!def) + return; + + // Remove USE from the splay tree. + if (def->m_use_tree && use->is_in_any_insn ()) + { + int comparison = lookup_use (def->m_use_tree, use->insn ()); + gcc_checking_assert (comparison == 0); + def->m_use_tree.remove_root (); + } + + use_info *prev = use->prev_use (); + use_info *next = use->next_use (); + + use_info *first = def->first_use (); + use_info *last = first->last_use (); + if (last->last_nondebug_insn_use () == use) + last->set_last_nondebug_insn_use (prev); + + if (next) + next->copy_prev_from (use); + else + first->set_last_use (prev); + + if (prev) + prev->copy_next_from (use); + else + def->set_first_use (next); + + use->clear_use_links (); + gcc_checking_assert ((!prev || prev->check_integrity ()) + && (!next || next->check_integrity ())); +} + +// Allocate a temporary clobber_info for register REGNO in insn INSN, +// including it in the region of the obstack governed by WATERMARK. +// Return a new def_array that contains OLD_DEFS and the new clobber. +// +// OLD_DEFS is known not to define REGNO. +def_array +function_info::insert_temp_clobber (obstack_watermark &watermark, + insn_info *insn, unsigned int regno, + def_array old_defs) +{ + gcc_checking_assert (watermark == &m_temp_obstack); + auto *clobber = allocate_temp<clobber_info> (insn, regno); + clobber->m_is_temp = true; + return insert_access (watermark, clobber, old_defs); +} + +// A subroutine of make_uses_available. Try to make USE's definition +// available at the head of BB. On success: +// +// - If the use would have the same def () as USE, return USE. +// +// - If BB already has a degenerate phi for the same definition, +// return a temporary use of that phi. +// +// - Otherwise, the use would need a new degenerate phi. Allocate a +// temporary phi and return a temporary use of it. +// +// Return null on failure. +use_info * +function_info::make_use_available (use_info *use, bb_info *bb) +{ + set_info *def = use->def (); + if (!def) + return use; + + if (is_single_dominating_def (def)) + return use; + + // FIXME: Deliberately limited for fwprop compatibility testing. + basic_block cfg_bb = bb->cfg_bb (); + bb_info *use_bb = use->bb (); + if (single_pred_p (cfg_bb) + && single_pred (cfg_bb) == use_bb->cfg_bb () + && remains_available_on_exit (def, use_bb)) + { + if (def->ebb () == bb->ebb ()) + return use; + + resource_info resource = use->resource (); + set_info *ultimate_def = look_through_degenerate_phi (def); + + // See if there is already a (degenerate) phi for DEF. + insn_info *phi_insn = bb->ebb ()->phi_insn (); + phi_info *phi; + def_lookup dl = find_def (resource, phi_insn); + if (set_info *set = dl.matching_set ()) + { + // There is an existing phi. + phi = as_a<phi_info *> (set); + gcc_checking_assert (phi->input_value (0) == ultimate_def); + } + else + { + // Create a temporary placeholder phi. This will become + // permanent if the change is later committed. + phi = allocate_temp<phi_info> (phi_insn, resource, 0); + auto *input = allocate<use_info> (phi, resource, ultimate_def); + input->m_is_temp = true; + phi->m_is_temp = true; + phi->make_degenerate (input); + if (def_info *prev = dl.prev_def ()) + phi->set_prev_def (prev); + if (def_info *next = dl.next_def ()) + phi->set_next_def (next); + } + + // Create a temporary use of the phi at the head of the first + // block, since we know for sure that it's available there. + insn_info *use_insn = bb->ebb ()->first_bb ()->head_insn (); + auto *new_use = allocate_temp<use_info> (use_insn, resource, phi); + new_use->m_is_temp = true; + return new_use; + } + return nullptr; +} + +// See the comment above the declaration. +use_array +function_info::make_uses_available (obstack_watermark &watermark, + use_array uses, bb_info *bb) +{ + unsigned int num_uses = uses.size (); + if (num_uses == 0) + return uses; + + auto **new_uses = XOBNEWVEC (watermark, access_info *, num_uses); + for (unsigned int i = 0; i < num_uses; ++i) + { + use_info *use = make_use_available (uses[i], bb); + if (!use) + return use_array (access_array::invalid ()); + new_uses[i] = use; + } + return use_array (new_uses, num_uses); +} + +// Return true if ACCESS1 can represent ACCESS2 and if ACCESS2 can +// represent ACCESS1. +static bool +can_merge_accesses (access_info *access1, access_info *access2) +{ + if (access1 == access2) + return true; + + auto *use1 = dyn_cast<use_info *> (access1); + auto *use2 = dyn_cast<use_info *> (access2); + return use1 && use2 && use1->def () == use2->def (); +} + +// See the comment above the declaration. +access_array +rtl_ssa::merge_access_arrays_base (obstack_watermark &watermark, + access_array accesses1, + access_array accesses2) +{ + if (accesses1.empty ()) + return accesses2; + if (accesses2.empty ()) + return accesses1; + + auto i1 = accesses1.begin (); + auto end1 = accesses1.end (); + auto i2 = accesses2.begin (); + auto end2 = accesses2.end (); + + access_array_builder builder (watermark); + builder.reserve (accesses1.size () + accesses2.size ()); + + while (i1 != end1 && i2 != end2) + { + access_info *access1 = *i1; + access_info *access2 = *i2; + + unsigned int regno1 = access1->regno (); + unsigned int regno2 = access2->regno (); + if (regno1 == regno2) + { + if (!can_merge_accesses (access1, access2)) + return access_array::invalid (); + + builder.quick_push (access1); + ++i1; + ++i2; + } + else if (regno1 < regno2) + { + builder.quick_push (access1); + ++i1; + } + else + { + builder.quick_push (access2); + ++i2; + } + } + for (; i1 != end1; ++i1) + builder.quick_push (*i1); + for (; i2 != end2; ++i2) + builder.quick_push (*i2); + + return builder.finish (); +} + +// See the comment above the declaration. +access_array +rtl_ssa::insert_access_base (obstack_watermark &watermark, + access_info *access1, access_array accesses2) +{ + access_array_builder builder (watermark); + builder.reserve (1 + accesses2.size ()); + + unsigned int regno1 = access1->regno (); + auto i2 = accesses2.begin (); + auto end2 = accesses2.end (); + while (i2 != end2) + { + access_info *access2 = *i2; + + unsigned int regno2 = access2->regno (); + if (regno1 == regno2) + { + if (!can_merge_accesses (access1, access2)) + return access_array::invalid (); + + builder.quick_push (access1); + access1 = nullptr; + ++i2; + break; + } + else if (regno1 < regno2) + { + builder.quick_push (access1); + access1 = nullptr; + break; + } + else + { + builder.quick_push (access2); + ++i2; + } + } + if (access1) + builder.quick_push (access1); + for (; i2 != end2; ++i2) + builder.quick_push (*i2); + + return builder.finish (); +} + +// See the comment above the declaration. +access_array +rtl_ssa::remove_note_accesses_base (obstack_watermark &watermark, + access_array accesses) +{ + for (access_info *access : accesses) + if (access->only_occurs_in_notes ()) + { + access_array_builder builder (watermark); + builder.reserve (accesses.size ()); + for (access_info *access2 : accesses) + if (!access2->only_occurs_in_notes ()) + builder.quick_push (access2); + return builder.finish (); + } + return accesses; +} + +// Print RESOURCE to PP. +void +rtl_ssa::pp_resource (pretty_printer *pp, resource_info resource) +{ + resource.print (pp); +} + +// Print ACCESS to PP. FLAGS is a bitmask of PP_ACCESS_* flags. +void +rtl_ssa::pp_access (pretty_printer *pp, const access_info *access, + unsigned int flags) +{ + if (!access) + pp_string (pp, "<null>"); + else if (auto *phi = dyn_cast<const phi_info *> (access)) + phi->print (pp, flags); + else if (auto *set = dyn_cast<const set_info *> (access)) + set->print (pp, flags); + else if (auto *clobber = dyn_cast<const clobber_info *> (access)) + clobber->print (pp, flags); + else if (auto *use = dyn_cast<const use_info *> (access)) + use->print (pp, flags); + else + pp_string (pp, "??? Unknown access"); +} + +// Print ACCESSES to PP. FLAGS is a bitmask of PP_ACCESS_* flags. +void +rtl_ssa::pp_accesses (pretty_printer *pp, access_array accesses, + unsigned int flags) +{ + if (accesses.empty ()) + pp_string (pp, "none"); + else + { + bool is_first = true; + for (access_info *access : accesses) + { + if (is_first) + is_first = false; + else + pp_newline_and_indent (pp, 0); + pp_access (pp, access, flags); + } + } +} + +// Print NODE to PP. +void +rtl_ssa::pp_def_node (pretty_printer *pp, const def_node *node) +{ + if (!node) + pp_string (pp, "<null>"); + else if (auto *group = dyn_cast<const clobber_group *> (node)) + group->print (pp); + else if (auto *set = dyn_cast<const set_node *> (node)) + set->print (pp); + else + pp_string (pp, "??? Unknown def node"); +} + +// Print MUX to PP. +void +rtl_ssa::pp_def_mux (pretty_printer *pp, def_mux mux) +{ + if (auto *node = mux.dyn_cast<def_node *> ()) + pp_def_node (pp, node); + else + pp_access (pp, mux.as_a<def_info *> ()); +} + +// Print DL to PP. +void +rtl_ssa::pp_def_lookup (pretty_printer *pp, def_lookup dl) +{ + pp_string (pp, "comparison result of "); + pp_decimal_int (pp, dl.comparison); + pp_string (pp, " for "); + pp_newline_and_indent (pp, 0); + pp_def_mux (pp, dl.mux); +} + +// Dump RESOURCE to FILE. +void +dump (FILE *file, resource_info resource) +{ + dump_using (file, pp_resource, resource); +} + +// Dump ACCESS to FILE. FLAGS is a bitmask of PP_ACCESS_* flags. +void +dump (FILE *file, const access_info *access, unsigned int flags) +{ + dump_using (file, pp_access, access, flags); +} + +// Dump ACCESSES to FILE. FLAGS is a bitmask of PP_ACCESS_* flags. +void +dump (FILE *file, access_array accesses, unsigned int flags) +{ + dump_using (file, pp_accesses, accesses, flags); +} + +// Print NODE to FILE. +void +dump (FILE *file, const def_node *node) +{ + dump_using (file, pp_def_node, node); +} + +// Print MUX to FILE. +void +dump (FILE *file, def_mux mux) +{ + dump_using (file, pp_def_mux, mux); +} + +// Print RESULT to FILE. +void +dump (FILE *file, def_lookup result) +{ + dump_using (file, pp_def_lookup, result); +} + +// Debug interfaces to the dump routines above. +void debug (const resource_info &x) { dump (stderr, x); } +void debug (const access_info *x) { dump (stderr, x); } +void debug (const access_array &x) { dump (stderr, x); } +void debug (const def_node *x) { dump (stderr, x); } +void debug (const def_mux &x) { dump (stderr, x); } +void debug (const def_lookup &x) { dump (stderr, x); } |