aboutsummaryrefslogtreecommitdiff
path: root/gcc/rtl-ssa/functions.cc
diff options
context:
space:
mode:
Diffstat (limited to 'gcc/rtl-ssa/functions.cc')
-rw-r--r--gcc/rtl-ssa/functions.cc325
1 files changed, 325 insertions, 0 deletions
diff --git a/gcc/rtl-ssa/functions.cc b/gcc/rtl-ssa/functions.cc
new file mode 100644
index 0000000..50595ac
--- /dev/null
+++ b/gcc/rtl-ssa/functions.cc
@@ -0,0 +1,325 @@
+// Implementation of function-related RTL SSA functions -*- 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;
+
+function_info::function_info (function *fn)
+ : m_fn (fn)
+{
+ // Force the alignment to be obstack_alignment. Everything else is normal.
+ obstack_specify_allocation (&m_obstack, OBSTACK_CHUNK_SIZE,
+ obstack_alignment, obstack_chunk_alloc,
+ obstack_chunk_free);
+ obstack_specify_allocation (&m_temp_obstack, OBSTACK_CHUNK_SIZE,
+ obstack_alignment, obstack_chunk_alloc,
+ obstack_chunk_free);
+
+ // Record the start of the obstacks.
+ m_obstack_start = XOBNEWVAR (&m_obstack, char, 0);
+ m_temp_obstack_start = XOBNEWVAR (&m_temp_obstack, char, 0);
+
+ init_function_data ();
+ process_all_blocks ();
+ simplify_phis ();
+}
+
+function_info::~function_info ()
+{
+ // Anything using the temporary obstack should free it afterwards,
+ // preferably via temp_watermark ().
+ gcc_assert (XOBNEWVAR (&m_temp_obstack, char, 0) == m_temp_obstack_start);
+
+ obstack_free (&m_temp_obstack, nullptr);
+ obstack_free (&m_obstack, nullptr);
+}
+
+// See the comment above the declaration.
+void
+function_info::print (pretty_printer *pp) const
+{
+ pp_string (pp, "Function: ");
+ pp_string (pp, function_name (m_fn));
+ for (ebb_info *ebb : ebbs ())
+ {
+ pp_newline (pp);
+ pp_newline_and_indent (pp, 0);
+ pp_ebb (pp, ebb);
+ }
+}
+
+// Calculate m_potential_phi_regs.
+void
+function_info::calculate_potential_phi_regs ()
+{
+ auto *lr_info = DF_LR_BB_INFO (ENTRY_BLOCK_PTR_FOR_FN (m_fn));
+ for (unsigned int regno = 0; regno < m_num_regs; ++regno)
+ if (regno >= DF_REG_SIZE (DF)
+ // Exclude registers that have a single definition that dominates
+ // all uses. If the definition does not dominate all uses,
+ // the register will be exposed upwards to the entry block but
+ // will not be defined by the entry block.
+ || DF_REG_DEF_COUNT (regno) > 1
+ || (!bitmap_bit_p (&lr_info->def, regno)
+ && bitmap_bit_p (&lr_info->out, regno)))
+ bitmap_set_bit (m_potential_phi_regs, regno);
+}
+
+// Initialize all member variables in preparation for (re)building
+// SSA form from scratch.
+void
+function_info::init_function_data ()
+{
+ m_next_artificial_uid = -1;
+ m_next_phi_uid = 0;
+ m_num_regs = max_reg_num ();
+ m_defs.safe_grow_cleared (m_num_regs + 1);
+ m_bbs.safe_grow_cleared (last_basic_block_for_fn (m_fn));
+ m_first_bb = nullptr;
+ m_last_bb = nullptr;
+ m_first_insn = nullptr;
+ m_last_insn = nullptr;
+ m_last_nondebug_insn = nullptr;
+ m_free_phis = nullptr;
+
+ calculate_potential_phi_regs ();
+}
+
+// The initial phase of the phi simplification process. The cumulative
+// effect of the initial phase is to set up ASSUMED_VALUES such that,
+// for a phi P with uid ID:
+//
+// - if we think all inputs to P have the same value, ASSUMED_VALUES[ID]
+// is that value
+//
+// - otherwise, ASSUMED_VALUES[ID] is P.
+//
+// This has already been done for phis with a lower uid than PHI,
+// initially making optimistic assumptions about backedge inputs.
+// Now do the same for PHI. If this might invalidate any assumptions
+// made for earlier phis, add the uids of those phis to WORKLIST.
+void
+function_info::simplify_phi_setup (phi_info *phi, set_info **assumed_values,
+ bitmap worklist)
+{
+ // If all non-backedge inputs have the same value, set NEW_VALUE
+ // to that value. Otherwise set NEW_VALUE to PHI, to indicate
+ // that PHI cannot be simplified.
+ unsigned int phi_uid = phi->uid ();
+ bool is_first_input = true;
+ set_info *new_value = nullptr;
+ machine_mode phi_mode = phi->mode ();
+ for (use_info *input : phi->inputs ())
+ {
+ set_info *def = input->def ();
+
+ if (auto *input_phi = safe_dyn_cast<phi_info *> (def))
+ {
+ // Ignore backedges for now.
+ unsigned int input_phi_uid = input_phi->uid ();
+ if (phi_uid <= input_phi_uid)
+ continue;
+
+ def = assumed_values[input_phi_uid];
+ }
+
+ // Compare this definition with previous ones.
+ if (is_first_input)
+ {
+ new_value = def;
+ is_first_input = false;
+ }
+ else if (new_value != def)
+ new_value = phi;
+
+ // If the input has a known mode (i.e. not BLKmode), make sure
+ // that the phi's mode is at least as large.
+ if (def)
+ phi_mode = combine_modes (phi_mode, def->mode ());
+ }
+ if (phi->mode () != phi_mode)
+ phi->set_mode (phi_mode);
+
+ // Since we use a reverse postorder traversal, no phi can consist
+ // entirely of backedges.
+ gcc_checking_assert (!is_first_input);
+ assumed_values[phi_uid] = new_value;
+
+ // See whether any assumptions for earlier phis are now invalid.
+ simplify_phi_propagate (phi, assumed_values, nullptr, worklist);
+}
+
+// The propagation phase of the phi simplification process, with
+// ASSUMED_VALUES as described above simplify_phi_setup. Iteratively
+// update the phis that use PHI based on PHI's entry in ASSUMED_VALUES.
+// If CURR_WORKLIST is null, consider only phi uses with a lower uid
+// than PHI, otherwise consider all phi uses.
+//
+// If a phi with a higher uid than PHI needs updating, add its uid to
+// CURR_WORKLIST; if a phi with a lower uid than PHI needs updating,
+// add its uid to NEXT_WORKLIST.
+void
+function_info::simplify_phi_propagate (phi_info *phi,
+ set_info **assumed_values,
+ bitmap curr_worklist,
+ bitmap next_worklist)
+{
+ // Go through each phi user of PHI to see whether it needs updating.
+ unsigned int phi_uid = phi->uid ();
+ machine_mode phi_mode = phi->mode ();
+ set_info *phi_value = assumed_values[phi_uid];
+ for (use_info *use : phi->phi_uses ())
+ {
+ phi_info *user_phi = use->phi ();
+
+ // Propagate the phi's new mode to all phi users. Insn uses should
+ // not be updated, since their modes reflect a property of the insns
+ // rather than the phi.
+ if (use->mode () != phi_mode)
+ use->set_mode (phi_mode);
+
+ if (user_phi == phi)
+ continue;
+
+ // If this is a phi we should be looking at, see whether it needs
+ // an update.
+ unsigned int user_phi_uid = user_phi->uid ();
+ if (user_phi_uid < phi_uid || curr_worklist)
+ {
+ bool needs_update = false;
+
+ // Make sure that USER_PHI's mode is at least as big as PHI_MODE.
+ machine_mode user_phi_mode = user_phi->mode ();
+ machine_mode new_mode = combine_modes (user_phi_mode, phi_mode);
+ if (user_phi_mode != new_mode)
+ {
+ user_phi->set_mode (new_mode);
+ needs_update = true;
+ }
+
+ // If USER_PHI optimistically assumed an incorrect value,
+ // adjust it now.
+ if (assumed_values[user_phi_uid] != user_phi
+ && assumed_values[user_phi_uid] != phi_value)
+ {
+ assumed_values[user_phi_uid] = user_phi;
+ needs_update = true;
+ }
+
+ if (needs_update)
+ {
+ if (user_phi_uid < phi_uid)
+ bitmap_set_bit (next_worklist, user_phi_uid);
+ else
+ bitmap_set_bit (curr_worklist, user_phi_uid);
+ }
+ }
+ }
+}
+
+// Update the modes of all phis so that they are at least as big as
+// all inputs. Remove any non-degenerate phis whose inputs are all equal.
+void
+function_info::simplify_phis ()
+{
+ auto temps = temp_watermark ();
+
+ // See the comment above simplify_phi_setup for details about this array.
+ auto *assumed_values = XOBNEWVEC (&m_temp_obstack, set_info *,
+ m_next_phi_uid);
+
+ // An array of all phis, indexed by uid.
+ auto *phis = XOBNEWVEC (&m_temp_obstack, phi_info *, m_next_phi_uid);
+
+ // Which phi uids are actually in use.
+ auto_sbitmap valid_phi_uids (m_next_phi_uid);
+ bitmap_clear (valid_phi_uids);
+
+ // Bitmaps used for the main double-queue propagation phase.
+ auto_bitmap worklist1;
+ auto_bitmap worklist2;
+ bitmap curr_worklist = worklist1;
+ bitmap next_worklist = worklist2;
+
+ // Perform the set-up phase; see simplify_phi_setup for details.
+ for (ebb_info *ebb : ebbs ())
+ for (phi_info *phi : ebb->phis ())
+ {
+ bitmap_set_bit (valid_phi_uids, phi->uid ());
+ phis[phi->uid ()] = phi;
+ simplify_phi_setup (phi, assumed_values, curr_worklist);
+ }
+
+ // Iteratively process any phis that need updating; see
+ // simplify_phi_propagate for details. Using a double queue
+ // should reduce the number of times that any given phi node
+ // needs to be revisited.
+ while (!bitmap_empty_p (curr_worklist))
+ {
+ do
+ {
+ unsigned int uid = bitmap_first_set_bit (curr_worklist);
+ bitmap_clear_bit (curr_worklist, uid);
+ simplify_phi_propagate (phis[uid], assumed_values,
+ curr_worklist, next_worklist);
+ }
+ while (!bitmap_empty_p (curr_worklist));
+ std::swap (next_worklist, curr_worklist);
+ }
+
+ // Make sure that assumed_values is a transitive closure. This ensures
+ // that each use_info is only updated once.
+ if (flag_checking)
+ for (unsigned int i = 0; i < m_next_phi_uid; ++i)
+ if (bitmap_bit_p (valid_phi_uids, i))
+ if (auto *new_phi = safe_dyn_cast<phi_info *> (assumed_values[i]))
+ gcc_assert (assumed_values[new_phi->uid ()] == new_phi);
+
+ // Update any phis that turned out to be equivalent to a single input.
+ for (unsigned int i = 0; i < m_next_phi_uid; ++i)
+ if (bitmap_bit_p (valid_phi_uids, i) && phis[i] != assumed_values[i])
+ replace_phi (phis[i], assumed_values[i]);
+}
+
+// Print a description of FUNCTION to PP.
+void
+rtl_ssa::pp_function (pretty_printer *pp, const function_info *function)
+{
+ function->print (pp);
+}
+
+// Print a description of FUNCTION to FILE.
+void
+dump (FILE *file, const function_info *function)
+{
+ dump_using (file, pp_function, function);
+}
+
+// Debug interface to the dump routine above.
+void debug (const function_info *x) { dump (stderr, x); }