| // Implementation of access-related functions for RTL SSA -*- C++ -*- |
| // Copyright (C) 2020-2021 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.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. WILL_BE_DEBUG_USE is true if the |
| // definition will be used only in debug instructions. |
| // |
| // 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, |
| bool will_be_debug_use) |
| { |
| 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 () || will_be_debug_use) |
| 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_temp<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, |
| bool will_be_debug_uses) |
| { |
| 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, will_be_debug_uses); |
| 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); } |