506 lines
18 KiB
C++
506 lines
18 KiB
C++
// Instruction-related RTL SSA classes -*- 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/>.
|
|
|
|
namespace rtl_ssa {
|
|
|
|
// A fake cost for instructions that we haven't costed yet.
|
|
const int UNKNOWN_COST = INT_MAX;
|
|
|
|
// Enumerates the kinds of note that can be added to an instruction.
|
|
// See the comment above insn_info for details.
|
|
enum class insn_note_kind : uint8_t
|
|
{
|
|
ORDER_NODE,
|
|
CALL_CLOBBERS
|
|
};
|
|
|
|
// The base class for notes that can be added to an instruction.
|
|
// See the comment above insn_info for details.
|
|
class insn_note
|
|
{
|
|
// Size: 2 LP64 words.
|
|
friend class insn_info;
|
|
friend class function_info;
|
|
|
|
public:
|
|
// Return what kind of note this is.
|
|
insn_note_kind kind () const { return m_kind; }
|
|
|
|
// Return the next note in the list, or null if none.
|
|
insn_note *next_note () const { return m_next_note; }
|
|
|
|
// Used with T = Derived *, where Derived is derived from insn_note.
|
|
// Convert the note to Derived, asserting that it has the right kind.
|
|
template<typename T>
|
|
T as_a ();
|
|
|
|
// Used with T = Derived *, where Derived is derived from insn_note.
|
|
// If the note is a Derived note, return it in that form, otherwise
|
|
// return null.
|
|
template<typename T>
|
|
T dyn_cast ();
|
|
|
|
protected:
|
|
// Construct a note with the given kind.
|
|
insn_note (insn_note_kind);
|
|
|
|
private:
|
|
// The next note in the list, or null if none.
|
|
insn_note *m_next_note;
|
|
|
|
// The kind of note this is.
|
|
insn_note_kind m_kind : 8;
|
|
|
|
protected:
|
|
// Fill in the remaining LP64 word with data that derived classes can use.
|
|
unsigned int m_data8 : 8;
|
|
unsigned int m_data16 : 16;
|
|
unsigned int m_data32 : 32;
|
|
};
|
|
|
|
// Instructions have one of these notes if insn_info::has_call_clobbers ()
|
|
// is true. All such instructions in an EBB are first grouped together
|
|
// by the predefined_function_abis of the functions that they call.
|
|
// Then, for each such predefined ABI, the call_clobbers notes are put
|
|
// into a splay tree whose nodes follow execution order.
|
|
class insn_call_clobbers_note : public insn_note
|
|
{
|
|
friend class function_info;
|
|
friend class default_splay_tree_accessors<insn_call_clobbers_note *>;
|
|
|
|
public:
|
|
static const insn_note_kind kind = insn_note_kind::CALL_CLOBBERS;
|
|
|
|
// Return the identifier of the predefined_function_abi.
|
|
unsigned int abi_id () const { return m_data32; }
|
|
|
|
// Return the instruction to which the note is attached.
|
|
insn_info *insn () const { return m_insn; }
|
|
|
|
protected:
|
|
insn_call_clobbers_note (unsigned int abi_id, insn_info *insn);
|
|
|
|
// The splay tree pointers.
|
|
insn_call_clobbers_note *m_children[2];
|
|
|
|
// The value returned by insn ().
|
|
insn_info *m_insn;
|
|
};
|
|
|
|
// A splay tree of insn_call_clobbers_notes.
|
|
using insn_call_clobbers_tree = default_splay_tree<insn_call_clobbers_note *>;
|
|
|
|
// SSA-related information about an instruction. It also represents
|
|
// artificial instructions that are added to make the dataflow correct;
|
|
// these artificial instructions fall into three categories:
|
|
//
|
|
// - Instructions that hold the phi nodes for an extended basic block (is_phi).
|
|
//
|
|
// - Instructions that represent the head of a basic block and that hold
|
|
// all the associated artificial uses and definitions.
|
|
//
|
|
// - Instructions that represent the end of a basic block and that again
|
|
// hold all the associated artificial uses and definitions.
|
|
//
|
|
// Dataflow-wise, each instruction goes through three stages:
|
|
//
|
|
// (1) Use all the values in uses ().
|
|
//
|
|
// (2) If has_call_clobbers (), clobber the registers indicated by
|
|
// insn_callee_abi.
|
|
//
|
|
// (3) Define all the values in defs ().
|
|
//
|
|
// Having stage (2) is a trade-off: it makes processing the instructions
|
|
// more complicated, but it saves having to allocate memory for every
|
|
// individual call clobber. Without it, clobbers for calls would often
|
|
// make up a large proportion of the total definitions in a function.
|
|
//
|
|
// All the instructions in a function are chained together in a list
|
|
// that follows a reverse postorder traversal of the CFG. The list
|
|
// contains both debug and nondebug instructions, but it is possible
|
|
// to hop from one nondebug instruction to the next with constant complexity.
|
|
//
|
|
// Instructions can have supplemental information attached in the form
|
|
// of "notes", a bit like REG_NOTES for the underlying RTL insns.
|
|
class insn_info
|
|
{
|
|
// Size: 8 LP64 words.
|
|
friend class ebb_info;
|
|
friend class function_info;
|
|
|
|
public:
|
|
// Compare instructions by their positions in the function list described
|
|
// above. Thus for two instructions in the same basic block, I1 < I2 if
|
|
// I1 comes before I2 in the block.
|
|
bool operator< (const insn_info &) const;
|
|
bool operator<= (const insn_info &) const;
|
|
bool operator>= (const insn_info &) const;
|
|
bool operator> (const insn_info &) const;
|
|
|
|
// Return -1 if this instruction comes before INSN in the reverse
|
|
// postorder, 0 if this instruction is INSN, or 1 if this instruction
|
|
// comes after INSN in the reverse postorder.
|
|
int compare_with (const insn_info *insn) const;
|
|
|
|
// Return the previous and next instructions in the list described above,
|
|
// or null if there are no such instructions.
|
|
insn_info *prev_any_insn () const;
|
|
insn_info *next_any_insn () const;
|
|
|
|
// Only valid if !is_debug_insn (). Return the previous and next
|
|
// nondebug instructions in the list described above, skipping over
|
|
// any intervening debug instructions. These are constant-time operations.
|
|
insn_info *prev_nondebug_insn () const;
|
|
insn_info *next_nondebug_insn () const;
|
|
|
|
// Return the underlying RTL insn. This instruction is null if is_phi ()
|
|
// or is_bb_end () are true. The instruction is a basic block note if
|
|
// is_bb_head () is true.
|
|
rtx_insn *rtl () const { return m_rtl; }
|
|
|
|
// Return true if the instruction is a real insn with an rtl pattern.
|
|
// Return false if it is an artificial instruction that represents the
|
|
// phi nodes in an extended basic block or the head or end of a basic block.
|
|
bool is_real () const { return m_cost_or_uid >= 0; }
|
|
|
|
// Return the opposite of is_real ().
|
|
bool is_artificial () const { return m_cost_or_uid < 0; }
|
|
|
|
// Return true if the instruction was a real instruction but has now
|
|
// been deleted. In this case the instruction is no longer part of
|
|
// the SSA information.
|
|
bool has_been_deleted () const { return m_rtl && !INSN_P (m_rtl); }
|
|
|
|
// Return true if the instruction is a debug instruction (and thus
|
|
// also a real instruction).
|
|
bool is_debug_insn () const { return m_is_debug_insn; }
|
|
|
|
// Return true if the instruction is something that we can optimize.
|
|
// This implies that it is a real instruction that contains an asm
|
|
// or that contains something that matches an .md define_insn pattern.
|
|
bool can_be_optimized () const { return m_can_be_optimized; }
|
|
|
|
// Return true if the instruction is a call instruction.
|
|
//
|
|
// ??? We could cache this information, but since most callers would
|
|
// go on to access PATTERN (rtl ()), a cache might not be helpful and
|
|
// could even be counterproductive.
|
|
bool is_call () const { return CALL_P (m_rtl); }
|
|
|
|
// Return true if the instruction is a jump instruction.
|
|
//
|
|
// ??? See is_call for the reason we don't cache this.
|
|
bool is_jump () const { return JUMP_P (m_rtl); }
|
|
|
|
// Return true if the instruction is real and contains an inline asm.
|
|
bool is_asm () const { return m_is_asm; }
|
|
|
|
// Return true if the instruction is real and includes an RTX_AUTOINC
|
|
// operation.
|
|
bool has_pre_post_modify () const { return m_has_pre_post_modify; }
|
|
|
|
// Return true if the instruction is real and has volatile references,
|
|
// in the sense of volatile_refs_p. This includes volatile memory,
|
|
// volatile asms and UNSPEC_VOLATILEs.
|
|
bool has_volatile_refs () const { return m_has_volatile_refs; }
|
|
|
|
// Return true if the instruction is aritificial and if its (sole)
|
|
// purpose is to hold the phi nodes in an extended basic block.
|
|
bool is_phi () const;
|
|
|
|
// Return true if the instruction is artificial and if it represents
|
|
// the head of a basic block. If so, the instruction conceptually
|
|
// executes before the real instructions in the block. The uses
|
|
// and definitions represent the df_get_artificial_uses and
|
|
// df_get_artificial_defs entries for the head of the block.
|
|
bool is_bb_head () const;
|
|
|
|
// Return true if the instruction is artificial and if it represents
|
|
// the end of a basic block. The uses and definitions represent the
|
|
// the df_get_artificial_uses and df_get_artificial_defs entries for
|
|
// the end of the block.
|
|
bool is_bb_end () const;
|
|
|
|
// Return the basic block that the instruction is in.
|
|
bb_info *bb () const { return m_bb; }
|
|
|
|
// Return the extended basic block that the instruction is in;
|
|
// see bb_info for details.
|
|
ebb_info *ebb () const;
|
|
|
|
// If the instruction is real, return the unique identifier of the
|
|
// underlying RTL insn. If the instruction is artificial, return
|
|
// a unique negative identifier for the instructions.
|
|
//
|
|
// Note that the identifiers are not linear: it can be the case that
|
|
// an instruction with a higher uid comes earlier in a block than an
|
|
// instruction with a lower uid. The identifiers are however persistent;
|
|
// the identifier remains the same after the instruction has been moved
|
|
// or changed.
|
|
int uid () const;
|
|
|
|
// Return the list of things that this instruction uses. Registers
|
|
// come first, in register number order, followed by memory.
|
|
use_array uses () const;
|
|
|
|
// Return true if the instruction is a call and if the clobbers
|
|
// described by insn_callee_abi have been omitted from the list
|
|
// of definitions.
|
|
bool has_call_clobbers () const;
|
|
|
|
// Return the list of things that this instruction sets or clobbers.
|
|
// Registers come first, in register number order, followed by memory.
|
|
//
|
|
// If has_call_clobbers () is true, the list omits both the full and
|
|
// partial register clobbers described by insn_callee_abi.
|
|
def_array defs () const;
|
|
|
|
// The number of entries in uses ().
|
|
unsigned int num_uses () const { return m_num_uses; }
|
|
|
|
// The number of entries in defs ().
|
|
unsigned int num_defs () const { return m_num_defs; }
|
|
|
|
// Return the cost of the instruction, as calculated by the target.
|
|
// For performance reasons, the cost is evaluated lazily on first use.
|
|
//
|
|
// Artificial instructions have a cost of 0.
|
|
unsigned int cost () const;
|
|
|
|
// Return the first insn_note attached to the instruction, or null
|
|
// if none.
|
|
insn_note *first_note () const { return m_first_note; }
|
|
|
|
// See if a note of type T is attached to the instruction. Return it
|
|
// if so, otherwise return null.
|
|
template<typename T>
|
|
const T *find_note () const;
|
|
|
|
// Print "i" + uid () for real instructions and "a" + -uid () for
|
|
// artificial instructions.
|
|
void print_identifier (pretty_printer *) const;
|
|
|
|
// Print a short(ish) description of where the instruction is.
|
|
void print_location (pretty_printer *) const;
|
|
|
|
// Combine print_identifier and print_location.
|
|
void print_identifier_and_location (pretty_printer *) const;
|
|
|
|
// Print a full description of the instruction.
|
|
void print_full (pretty_printer *) const;
|
|
|
|
private:
|
|
// The first-order way of representing the order between instructions
|
|
// is to assign "program points", with higher point numbers coming
|
|
// later in the reverse postorder than lower point numbers. However,
|
|
// after a sequence of instruction movements, we may end up in a situation
|
|
// that adjacent instructions have the same program point.
|
|
//
|
|
// When that happens, we put the instructions into a splay tree that
|
|
// records their relative order. Each node of the splay tree is an
|
|
// order_node note that is attached to its respective instruction.
|
|
// The root of the splay tree is not stored, since the only thing
|
|
// we need the tree for is to compare two nodes.
|
|
class order_node : public insn_note
|
|
{
|
|
public:
|
|
static const insn_note_kind kind = insn_note_kind::ORDER_NODE;
|
|
|
|
order_node (int uid);
|
|
|
|
// Return the uid of the instruction that this node describes.
|
|
int uid () const { return m_data32; }
|
|
|
|
// The splay tree pointers.
|
|
order_node *m_children[2];
|
|
order_node *m_parent;
|
|
};
|
|
using order_splay_tree = default_rootless_splay_tree<order_node *>;
|
|
|
|
// prev_insn_or_last_debug_insn represents a choice between two things:
|
|
//
|
|
// (1) A pointer to the previous instruction in the list that has the
|
|
// same is_debug_insn () value, or null if no such instruction exists.
|
|
//
|
|
// (2) A pointer to the end of a sublist of debug instructions.
|
|
//
|
|
// (2) is used if this instruction is a debug instruction and the
|
|
// previous instruction is not. (1) is used otherwise.
|
|
//
|
|
// next_nondebug_or_debug_insn points to the next instruction but also
|
|
// records whether that next instruction is a debug instruction or a
|
|
// nondebug instruction.
|
|
//
|
|
// Thus the list is chained as follows:
|
|
//
|
|
// ----> ----> ----> ----> ---->
|
|
// NONDEBUG NONDEBUG DEBUG DEBUG DEBUG NONDEBUG ...
|
|
// <---- ^ +-- <---- <---- ^ +--
|
|
// | | | |
|
|
// | +------------------------+ |
|
|
// | |
|
|
// +-----------------------------------+
|
|
using prev_insn_or_last_debug_insn = pointer_mux<insn_info>;
|
|
using next_nondebug_or_debug_insn = pointer_mux<insn_info>;
|
|
|
|
insn_info (bb_info *bb, rtx_insn *rtl, int cost_or_uid);
|
|
|
|
static void print_uid (pretty_printer *, int);
|
|
|
|
void calculate_cost () const;
|
|
void set_properties (const rtx_properties &);
|
|
void set_accesses (access_info **, unsigned int, unsigned int);
|
|
void copy_accesses (access_array, access_array);
|
|
void set_cost (unsigned int cost) { m_cost_or_uid = cost; }
|
|
void set_bb (bb_info *bb) { m_bb = bb; }
|
|
|
|
void add_note (insn_note *note);
|
|
|
|
order_node *get_order_node () const;
|
|
order_node *get_known_order_node () const;
|
|
int slow_compare_with (const insn_info &) const;
|
|
|
|
insn_info *last_debug_insn () const;
|
|
|
|
unsigned int point () const { return m_point; }
|
|
void copy_prev_from (insn_info *);
|
|
void copy_next_from (insn_info *);
|
|
void set_prev_sametype_insn (insn_info *);
|
|
void set_last_debug_insn (insn_info *);
|
|
void set_next_any_insn (insn_info *);
|
|
void set_point (unsigned int point) { m_point = point; }
|
|
void clear_insn_links ();
|
|
bool has_insn_links ();
|
|
|
|
// The values returned by the accessors above.
|
|
prev_insn_or_last_debug_insn m_prev_insn_or_last_debug_insn;
|
|
next_nondebug_or_debug_insn m_next_nondebug_or_debug_insn;
|
|
bb_info *m_bb;
|
|
rtx_insn *m_rtl;
|
|
|
|
// The list of definitions followed by the list of uses.
|
|
access_info **m_accesses;
|
|
|
|
// The number of definitions and the number uses. FIRST_PSEUDO_REGISTER + 1
|
|
// is the maximum number of accesses to hard registers and memory, and
|
|
// MAX_RECOG_OPERANDS is the maximum number of pseudos that can be
|
|
// defined by an instruction, so the number of definitions should fit
|
|
// easily in 16 bits.
|
|
unsigned int m_num_uses;
|
|
unsigned int m_num_defs : 16;
|
|
|
|
// Flags returned by the accessors above.
|
|
unsigned int m_is_debug_insn : 1;
|
|
unsigned int m_can_be_optimized : 1;
|
|
unsigned int m_is_asm : 1;
|
|
unsigned int m_has_pre_post_modify : 1;
|
|
unsigned int m_has_volatile_refs : 1;
|
|
|
|
// For future expansion.
|
|
unsigned int m_spare : 11;
|
|
|
|
// The program point at which the instruction occurs.
|
|
//
|
|
// Note that the values of the program points are influenced by -g
|
|
// and so should not used to make codegen decisions.
|
|
unsigned int m_point;
|
|
|
|
// Negative if the instruction is artificial, nonnegative if it is real.
|
|
//
|
|
// For real instructions: the cost of the instruction, or UNKNOWN_COST
|
|
// if we haven't measured it yet.
|
|
//
|
|
// For artificial instructions: the (negative) unique identifier of the
|
|
// instruction.
|
|
mutable int m_cost_or_uid;
|
|
|
|
// The list of notes that have been attached to the instruction.
|
|
insn_note *m_first_note;
|
|
};
|
|
|
|
// Iterators for unfiltered lists of instructions.
|
|
using any_insn_iterator = list_iterator<insn_info, &insn_info::next_any_insn>;
|
|
using reverse_any_insn_iterator
|
|
= list_iterator<insn_info, &insn_info::prev_any_insn>;
|
|
|
|
// Iterators for nondebug instructions only.
|
|
using nondebug_insn_iterator
|
|
= list_iterator<insn_info, &insn_info::next_nondebug_insn>;
|
|
using reverse_nondebug_insn_iterator
|
|
= list_iterator<insn_info, &insn_info::prev_nondebug_insn>;
|
|
|
|
// A class that describes an inclusive range of instructions.
|
|
class insn_range_info
|
|
{
|
|
public:
|
|
insn_range_info () = default;
|
|
|
|
// Create a range that contains a singleton instruction.
|
|
insn_range_info (insn_info *insn) : first (insn), last (insn) {}
|
|
|
|
// Create a range [FIRST, LAST], given that *FIRST <= *LAST.
|
|
insn_range_info (insn_info *first, insn_info *last);
|
|
|
|
// Return true if the range contains at least one instruction.
|
|
explicit operator bool () const { return *first <= *last; }
|
|
|
|
bool operator== (const insn_range_info &) const;
|
|
bool operator!= (const insn_range_info &) const;
|
|
|
|
// If the range contains a single instruction, return that instruction,
|
|
// otherwise return null.
|
|
insn_info *singleton () const;
|
|
|
|
// Return true if the range includes INSN.
|
|
bool includes (insn_info *insn) const;
|
|
|
|
// If INSN is inside the range, return INSN, otherwise return the
|
|
// nearest in-range instruction.
|
|
insn_info *clamp_insn_to_range (insn_info *insn) const;
|
|
|
|
// Return true if this range is a subrange of OTHER, i.e. if OTHER
|
|
// includes every instruction that this range does.
|
|
bool is_subrange_of (const insn_range_info &other) const;
|
|
|
|
// The lower and upper bounds of the range.
|
|
insn_info *first;
|
|
insn_info *last;
|
|
};
|
|
|
|
// A class that represents a closure of operator== for instructions.
|
|
// This is used by insn_is; see there for details.
|
|
class insn_is_closure
|
|
{
|
|
public:
|
|
insn_is_closure (const insn_info *insn) : m_insn (insn) {}
|
|
bool operator() (const insn_info *other) const { return m_insn == other; }
|
|
|
|
private:
|
|
const insn_info *m_insn;
|
|
};
|
|
|
|
void pp_insn (pretty_printer *, const insn_info *);
|
|
|
|
}
|
|
|
|
void dump (FILE *, const rtl_ssa::insn_info *);
|
|
|
|
void DEBUG_FUNCTION debug (const rtl_ssa::insn_info *);
|