blob: a629c7c09cf53a1e6813b2823e98cdb9cf6c7f94 [file] [log] [blame]
// Implementation of private inline member functions for RTL SSA -*- C++ -*-
// Copyright (C) 2020-2022 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/>.
namespace rtl_ssa {
// Construct a new access with the given resource () and kind () values.
inline access_info::access_info (resource_info resource, access_kind kind)
: m_regno (resource.regno),
m_kind (kind),
m_is_artificial (false),
m_is_set_with_nondebug_insn_uses (false),
m_is_pre_post_modify (false),
m_is_call_clobber (false),
m_is_live_out_use (false),
m_includes_address_uses (false),
m_includes_read_writes (false),
m_includes_subregs (false),
m_includes_multiregs (false),
m_only_occurs_in_notes (false),
m_is_last_nondebug_insn_use (false),
m_is_in_debug_insn_or_phi (false),
m_has_been_superceded (false),
m_is_temp (false),
m_spare (0),
m_mode (resource.mode)
{
}
// Construct a use of RESOURCE in LOCATION. The resource's value is provided
// by DEF, or is completely undefined if DEF is null.
inline use_info::use_info (insn_or_phi location, resource_info resource,
set_info *definition)
: access_info (resource, access_kind::USE),
m_insn_or_phi (location),
m_last_use_or_prev_use (nullptr),
m_last_nondebug_insn_use_or_next_use (nullptr),
m_def (definition)
{
if (m_insn_or_phi.is_second ())
{
m_is_in_debug_insn_or_phi = true;
m_is_artificial = true;
}
else
{
insn_info *insn = m_insn_or_phi.known_first ();
m_is_in_debug_insn_or_phi = insn->is_debug_insn ();
m_is_artificial = insn->is_artificial ();
}
}
// Return the correct (uncached) value of m_is_last_nondebug_insn_use.
inline bool
use_info::calculate_is_last_nondebug_insn_use () const
{
use_info *next = next_use ();
return is_in_nondebug_insn () && (!next || next->is_in_debug_insn_or_phi ());
}
// Accumulate any properties about REF that are also stored in use_infos.
// IS_FIRST is true if REF is the first access to resource () that we have
// recorded in this way, false if we have already recorded previous
// references.
inline void
use_info::record_reference (rtx_obj_reference ref, bool is_first)
{
if (is_first)
{
m_includes_address_uses = ref.in_address ();
m_includes_read_writes = ref.is_write ();
m_includes_subregs = ref.in_subreg ();
m_includes_multiregs = ref.is_multireg ();
m_only_occurs_in_notes = ref.in_note ();
}
else
{
m_includes_address_uses |= ref.in_address ();
m_includes_read_writes |= ref.is_write ();
m_includes_subregs |= ref.in_subreg ();
m_includes_multiregs |= ref.is_multireg ();
m_only_occurs_in_notes &= ref.in_note ();
}
}
// Change the value of insn () to INSN.
inline void
use_info::set_insn (insn_info *insn)
{
m_insn_or_phi = insn;
m_is_artificial = insn->is_artificial ();
}
// Copy the overloaded prev link from OTHER.
inline void
use_info::copy_prev_from (use_info *other)
{
m_last_use_or_prev_use = other->m_last_use_or_prev_use;
}
// Copy the overloaded next link from OTHER.
inline void
use_info::copy_next_from (use_info *other)
{
m_last_nondebug_insn_use_or_next_use
= other->m_last_nondebug_insn_use_or_next_use;
m_is_last_nondebug_insn_use = calculate_is_last_nondebug_insn_use ();
}
// Record that this use is the first in the list and that the last use is LAST.
inline void
use_info::set_last_use (use_info *last_use)
{
m_last_use_or_prev_use.set_first (last_use);
}
// Record that this use is not the first in the list and that the previous
// use is PREV.
inline void
use_info::set_prev_use (use_info *prev_use)
{
m_last_use_or_prev_use.set_second (prev_use);
}
// Record that this use is the last use in the list. If USE is nonnull,
// record that USE is the last use in the list by a nondebug instruction,
// otherwise record that there are no uses by nondebug instructions
// in the list.
inline void
use_info::set_last_nondebug_insn_use (use_info *use)
{
m_last_nondebug_insn_use_or_next_use.set_first (use);
m_is_last_nondebug_insn_use = (use == this);
}
// Record that this use is not the last in the list and that the next
// use is NEXT_USE.
inline void
use_info::set_next_use (use_info *next_use)
{
m_last_nondebug_insn_use_or_next_use.set_second (next_use);
m_is_last_nondebug_insn_use = calculate_is_last_nondebug_insn_use ();
}
// Clear any information relating to the position of the use in its
// definition's list.
inline void
use_info::clear_use_links ()
{
m_last_use_or_prev_use = nullptr;
m_last_nondebug_insn_use_or_next_use = nullptr;
m_is_last_nondebug_insn_use = false;
}
// Return true if the use has any links to other uses. This is mostly
// for assert checking.
inline bool
use_info::has_use_links ()
{
return (m_last_use_or_prev_use
|| m_last_nondebug_insn_use_or_next_use
|| m_is_last_nondebug_insn_use);
}
// Construct a definition of RESOURCE in INSN, giving it kind KIND.
inline def_info::def_info (insn_info *insn, resource_info resource,
access_kind kind)
: access_info (resource, kind),
m_insn (insn),
m_last_def_or_prev_def (nullptr),
m_splay_root_or_next_def (nullptr)
{
m_is_artificial = insn->is_artificial ();
}
// Record any properties about REF that are also stored in def_infos.
// IS_FIRST is true if REF is the first access to resource () that we have
// recorded in this way, false if we have already recorded previous
// references.
inline void
def_info::record_reference (rtx_obj_reference ref, bool is_first)
{
if (is_first)
{
m_is_pre_post_modify = ref.is_pre_post_modify ();
m_includes_read_writes = ref.is_read ();
m_includes_subregs = ref.in_subreg ();
m_includes_multiregs = ref.is_multireg ();
}
else
{
m_is_pre_post_modify |= ref.is_pre_post_modify ();
m_includes_read_writes |= ref.is_read ();
m_includes_subregs |= ref.in_subreg ();
m_includes_multiregs |= ref.is_multireg ();
}
}
// Return the last definition in the list. Only valid when is_first ()
// is true.
inline def_info *
def_info::last_def () const
{
return m_last_def_or_prev_def.known_first ();
}
// Return the root of the splay tree of definitions of resource (),
// or null if no splay tree has been created for this resource.
// Only valid when is_last () is true.
inline def_node *
def_info::splay_root () const
{
return m_splay_root_or_next_def.known_first ();
}
// Copy the overloaded prev link from OTHER.
inline void
def_info::copy_prev_from (def_info *other)
{
m_last_def_or_prev_def
= other->m_last_def_or_prev_def;
}
// Copy the overloaded next link from OTHER.
inline void
def_info::copy_next_from (def_info *other)
{
m_splay_root_or_next_def = other->m_splay_root_or_next_def;
}
// Record that this definition is the first in the list and that the last
// definition is LAST.
inline void
def_info::set_last_def (def_info *last_def)
{
m_last_def_or_prev_def.set_first (last_def);
}
// Record that this definition is not the first in the list and that the
// previous definition is PREV.
inline void
def_info::set_prev_def (def_info *prev_def)
{
m_last_def_or_prev_def.set_second (prev_def);
}
// Record that this definition is the last in the list and that the root
// of the splay tree associated with resource () is ROOT.
inline void
def_info::set_splay_root (def_node *root)
{
m_splay_root_or_next_def = root;
}
// Record that this definition is not the last in the list and that the
// next definition is NEXT.
inline void
def_info::set_next_def (def_info *next_def)
{
m_splay_root_or_next_def = next_def;
}
// Clear the prev and next links
inline void
def_info::clear_def_links ()
{
m_last_def_or_prev_def = nullptr;
m_splay_root_or_next_def = nullptr;
}
// Return true if the definition has any links to other definitions.
// This is mostly for assert checking.
inline bool
def_info::has_def_links ()
{
return m_last_def_or_prev_def || m_splay_root_or_next_def;
}
// Construct a clobber of register REGNO in insn INSN.
inline clobber_info::clobber_info (insn_info *insn, unsigned int regno)
: def_info (insn, { E_BLKmode, regno }, access_kind::CLOBBER),
m_children (),
m_parent (nullptr),
m_group (nullptr)
{
}
// Set the containing group to GROUP, if it isn't already. The main
// use of this function is to update the new root of GROUP's splay tree.
inline void
clobber_info::update_group (clobber_group *group)
{
if (__builtin_expect (m_group != group, 0))
m_group = group;
}
// Cconstruct a set_info for a store to RESOURCE in INSN, giving it
// kind KIND.
inline set_info::set_info (insn_info *insn, resource_info resource,
access_kind kind)
: def_info (insn, resource, kind),
m_first_use (nullptr)
{
}
// Cconstruct a set_info for a store to RESOURCE in INSN.
inline set_info::set_info (insn_info *insn, resource_info resource)
: set_info (insn, resource, access_kind::SET)
{
}
// Record that USE is the first use of this definition.
inline void
set_info::set_first_use (use_info *first_use)
{
m_first_use = first_use;
m_is_set_with_nondebug_insn_uses
= (first_use && first_use->is_in_nondebug_insn ());
}
// Construct a phi for RESOURCE in INSN, giving it identifier UID.
inline phi_info::phi_info (insn_info *insn, resource_info resource,
unsigned int uid)
: set_info (insn, resource, access_kind::PHI),
m_uid (uid),
m_num_inputs (0),
m_prev_phi (nullptr),
m_next_phi (nullptr)
{
}
// Turn the phi into a degenerate phi, with INPUT representing the
// value of the resource on all incoming edges.
inline void
phi_info::make_degenerate (use_info *input)
{
m_num_inputs = 1;
m_single_input = input;
}
// Set the inputs of the phi to INPUTS.
inline void
phi_info::set_inputs (use_array inputs)
{
m_num_inputs = inputs.size ();
if (inputs.size () == 1)
m_single_input = inputs[0];
else
m_inputs = access_array (inputs).begin ();
}
// Construct a definition splay tree node for FIRST_DEF, which is either
// the first clobber_info in a group or a standalone set_info.
inline def_node::def_node (clobber_or_set first_def)
: m_clobber_or_set (first_def),
m_children ()
{
}
// Construct a new group of clobber_infos that initially contains just CLOBBER.
inline clobber_group::clobber_group (clobber_info *clobber)
: def_node (clobber),
m_last_clobber (clobber),
m_clobber_tree (clobber)
{
clobber->m_group = this;
}
// Construct a node for the instruction with uid UID.
inline insn_info::order_node::order_node (int uid)
: insn_note (kind),
m_children (),
m_parent (nullptr)
{
m_data32 = uid;
}
// Construct a note for instruction INSN, giving it abi_id () value ABI_ID.
inline insn_call_clobbers_note::insn_call_clobbers_note (unsigned int abi_id,
insn_info *insn)
: insn_note (kind),
m_children (),
m_insn (insn)
{
m_data32 = abi_id;
}
// Construct an instruction with the given bb () and rtl () values.
// If the instruction is real, COST_OR_UID is the value of cost (),
// otherwise it is the value of uid ().
inline insn_info::insn_info (bb_info *bb, rtx_insn *rtl, int cost_or_uid)
: m_prev_insn_or_last_debug_insn (nullptr),
m_next_nondebug_or_debug_insn (nullptr),
m_bb (bb),
m_rtl (rtl),
m_accesses (nullptr),
m_num_uses (0),
m_num_defs (0),
m_is_debug_insn (rtl && DEBUG_INSN_P (rtl)),
m_can_be_optimized (false),
m_is_asm (false),
m_has_pre_post_modify (false),
m_has_volatile_refs (false),
m_spare (0),
m_point (0),
m_cost_or_uid (cost_or_uid),
m_first_note (nullptr)
{
}
// Copy any insn properties from PROPERTIES that are also stored in an
// insn_info.
inline void
insn_info::set_properties (const rtx_properties &properties)
{
m_is_asm = properties.has_asm;
m_has_pre_post_modify = properties.has_pre_post_modify;
m_has_volatile_refs = properties.has_volatile_refs;
// Not strictly related to the properties we've been given, but it's
// a convenient location to do this.
m_can_be_optimized = (NONDEBUG_INSN_P (m_rtl)
& (GET_CODE (PATTERN (m_rtl)) != USE)
& (GET_CODE (PATTERN (m_rtl)) != CLOBBER));
}
// Change the list of instruction accesses to ACCESSES, which contains
// NUM_DEFS definitions followed by NUM_USES uses.
inline void
insn_info::set_accesses (access_info **accesses,
unsigned int num_defs, unsigned int num_uses)
{
m_accesses = accesses;
m_num_defs = num_defs;
gcc_assert (num_defs == m_num_defs);
m_num_uses = num_uses;
}
// Change defs () and uses () to DEFS and USES respectively, given that
// the existing m_accesses array has enough room for them.
inline void
insn_info::copy_accesses (access_array defs, access_array uses)
{
gcc_assert (defs.size () + uses.size () <= m_num_defs + m_num_uses);
memcpy (m_accesses, defs.begin (), defs.size_bytes ());
memcpy (m_accesses + defs.size (), uses.begin (), uses.size_bytes ());
m_num_defs = defs.size ();
gcc_assert (m_num_defs == defs.size ());
m_num_uses = uses.size ();
}
// If the instruction has an insn_info::order_node, return the node,
// otherwise return null.
inline insn_info::order_node *
insn_info::get_order_node () const
{
// The order_node always comes first.
if (insn_note *note = first_note ())
return note->dyn_cast<insn_info::order_node *> ();
return nullptr;
}
// Like get_order_node (), but the node is known to exist.
inline insn_info::order_node *
insn_info::get_known_order_node () const
{
// The order_node always comes first.
return first_note ()->as_a<insn_info::order_node *> ();
}
// Copy the overloaded prev link from OTHER.
inline void
insn_info::copy_prev_from (insn_info *other)
{
m_prev_insn_or_last_debug_insn = other->m_prev_insn_or_last_debug_insn;
}
// Copy the overloaded next link from OTHER.
inline void
insn_info::copy_next_from (insn_info *other)
{
m_next_nondebug_or_debug_insn = other->m_next_nondebug_or_debug_insn;
}
// If this is a nondebug instruction, record that the previous nondebug
// instruction is PREV. (There might be intervening debug instructions.)
//
// If this is a debug instruction, record that the previous instruction
// is debug instruction PREV.
inline void
insn_info::set_prev_sametype_insn (insn_info *prev)
{
m_prev_insn_or_last_debug_insn.set_first (prev);
}
// Only valid for debug instructions. Record that this instruction starts
// a subsequence of debug instructions that ends with LAST.
inline void
insn_info::set_last_debug_insn (insn_info *last)
{
m_prev_insn_or_last_debug_insn.set_second (last);
}
// Record that the next instruction of any kind is NEXT.
inline void
insn_info::set_next_any_insn (insn_info *next)
{
if (next && next->is_debug_insn ())
m_next_nondebug_or_debug_insn.set_second (next);
else
m_next_nondebug_or_debug_insn.set_first (next);
}
// Clear the list links and point number for this instruction.
inline void
insn_info::clear_insn_links ()
{
m_prev_insn_or_last_debug_insn = nullptr;
m_next_nondebug_or_debug_insn = nullptr;
m_point = 0;
}
// Return true if the instruction contains any list information.
// This is used by assert checking.
inline bool
insn_info::has_insn_links ()
{
return (m_prev_insn_or_last_debug_insn
|| m_next_nondebug_or_debug_insn
|| m_point);
}
// Construct a representation of basic block CFG_BB.
inline bb_info::bb_info (basic_block cfg_bb)
: m_prev_bb (nullptr),
m_next_bb (nullptr),
m_cfg_bb (cfg_bb),
m_ebb (nullptr),
m_head_insn (nullptr),
m_end_insn (nullptr)
{
}
// Construct a tree of call clobbers for the given ABI.
inline ebb_call_clobbers_info::
ebb_call_clobbers_info (const predefined_function_abi *abi)
: m_next (nullptr),
m_abi (abi)
{
}
// Construct an EBB whose first block is FIRST_BB and whose last block
// is LAST_BB.
inline ebb_info::ebb_info (bb_info *first_bb, bb_info *last_bb)
: m_first_phi (nullptr),
m_phi_insn (nullptr),
m_first_bb (first_bb),
m_last_bb (last_bb),
m_first_call_clobbers (nullptr)
{
}
// Record register definition DEF in last_access, pushing a definition
// to def_stack where appropriate.
inline void
function_info::build_info::record_reg_def (def_info *def)
{
unsigned int regno = def->regno ();
auto *prev_dominating_def = safe_as_a<def_info *> (last_access[regno + 1]);
if (!prev_dominating_def)
// Indicate that DEF is the first dominating definition of REGNO.
def_stack.safe_push (def);
else if (prev_dominating_def->bb () != def->bb ())
// Record that PREV_DOMINATING_DEF was the dominating definition
// of REGNO on entry to the current block.
def_stack.safe_push (prev_dominating_def);
last_access[regno + 1] = def;
}
// Set the contents of last_access for memory to DEF.
inline void
function_info::build_info::record_mem_def (def_info *def)
{
last_access[0] = def;
}
// Return the current value of live register REGNO, or null if the register's
// value is completedly undefined.
inline set_info *
function_info::build_info::current_reg_value (unsigned int regno) const
{
return safe_dyn_cast<set_info *> (last_access[regno + 1]);
}
// Return the current value of memory.
inline set_info *
function_info::build_info::current_mem_value () const
{
return as_a<set_info *> (last_access[0]);
}
// Allocate a T on the function's main obstack, passing ARGS
// to its constructor.
template<typename T, typename... Ts>
inline T *
function_info::allocate (Ts... args)
{
static_assert (std::is_trivially_destructible<T>::value,
"destructor won't be called");
static_assert (alignof (T) <= obstack_alignment,
"too much alignment required");
void *addr = obstack_alloc (&m_obstack, sizeof (T));
return new (addr) T (std::forward<Ts> (args)...);
}
// Allocate a T on the function's temporary obstack, passing ARGS
// to its constructor.
template<typename T, typename... Ts>
inline T *
function_info::allocate_temp (Ts... args)
{
static_assert (std::is_trivially_destructible<T>::value,
"destructor won't be called");
static_assert (alignof (T) <= obstack_alignment,
"too much alignment required");
void *addr = obstack_alloc (&m_temp_obstack, sizeof (T));
return new (addr) T (std::forward<Ts> (args)...);
}
// Add INSN to the end of the function's list of instructions.
inline void
function_info::append_insn (insn_info *insn)
{
gcc_checking_assert (!insn->has_insn_links ());
if (insn_info *after = m_last_insn)
add_insn_after (insn, after);
else
// The first instruction is for the entry block and is always a nondebug
// insn
m_first_insn = m_last_insn = m_last_nondebug_insn = insn;
}
// Start building a new list of uses and definitions for an instruction.
inline void
function_info::start_insn_accesses ()
{
gcc_checking_assert (m_temp_defs.is_empty ()
&& m_temp_uses.is_empty ());
}
// Return a mode that encapsulates two distinct references to a register,
// one with mode MODE1 and one with mode MODE2. Treat BLKmode as a
// "don't know" wildcard.
inline machine_mode
combine_modes (machine_mode mode1, machine_mode mode2)
{
if (mode1 == E_BLKmode)
return mode2;
if (mode2 == E_BLKmode)
return mode1;
return wider_subreg_mode (mode1, mode2);
}
// PRINTER (PP, ARGS...) prints ARGS... to a pretty_printer PP. Use it
// to print ARGS... to FILE.
template<typename Printer, typename... Args>
inline void
dump_using (FILE *file, Printer printer, Args... args)
{
pretty_printer pp;
printer (&pp, args...);
pp_newline (&pp);
fprintf (file, "%s", pp_formatted_text (&pp));
}
}