344 lines
10 KiB
C++
344 lines
10 KiB
C++
/* Analyze RTL for GNU compiler.
|
|
Copyright (C) 2020-2023 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/>. */
|
|
|
|
/* Note that for historical reasons, many rtlanal.cc functions are
|
|
declared in rtl.h rather than here. */
|
|
|
|
#ifndef GCC_RTLANAL_H
|
|
#define GCC_RTLANAL_H
|
|
|
|
/* A dummy register value that represents the whole of variable memory.
|
|
Using ~0U means that arrays that track both registers and memory can
|
|
be indexed by regno + 1. */
|
|
const unsigned int MEM_REGNO = ~0U;
|
|
|
|
/* Bitmasks of flags describing an rtx_obj_reference. See the accessors
|
|
in the class for details. */
|
|
namespace rtx_obj_flags
|
|
{
|
|
const uint16_t IS_READ = 1U << 0;
|
|
const uint16_t IS_WRITE = 1U << 1;
|
|
const uint16_t IS_CLOBBER = 1U << 2;
|
|
const uint16_t IS_PRE_POST_MODIFY = 1U << 3;
|
|
const uint16_t IS_MULTIREG = 1U << 4;
|
|
const uint16_t IN_MEM_LOAD = 1U << 5;
|
|
const uint16_t IN_MEM_STORE = 1U << 6;
|
|
const uint16_t IN_SUBREG = 1U << 7;
|
|
const uint16_t IN_NOTE = 1U << 8;
|
|
|
|
/* Flags that apply to all subrtxes of the rtx they were originally
|
|
added for. */
|
|
static const uint16_t STICKY_FLAGS = IN_NOTE;
|
|
}
|
|
|
|
/* Contains information about a reference to a register or variable memory. */
|
|
class rtx_obj_reference
|
|
{
|
|
public:
|
|
rtx_obj_reference () = default;
|
|
rtx_obj_reference (unsigned int regno, uint16_t flags,
|
|
machine_mode mode, unsigned int multireg_offset = 0);
|
|
|
|
bool is_reg () const { return regno != MEM_REGNO; }
|
|
bool is_mem () const { return regno == MEM_REGNO; }
|
|
|
|
/* True if the reference is a read or a write respectively.
|
|
Both flags are set in a read-modify-write context, such as
|
|
for read_modify_subreg_p. */
|
|
bool is_read () const { return flags & rtx_obj_flags::IS_READ; }
|
|
bool is_write () const { return flags & rtx_obj_flags::IS_WRITE; }
|
|
|
|
/* True if IS_WRITE and if the write is a clobber rather than a set. */
|
|
bool is_clobber () const { return flags & rtx_obj_flags::IS_CLOBBER; }
|
|
|
|
/* True if the reference is updated by an RTX_AUTOINC. Both IS_READ
|
|
and IS_WRITE are also true if so. */
|
|
bool is_pre_post_modify () const
|
|
{
|
|
return flags & rtx_obj_flags::IS_PRE_POST_MODIFY;
|
|
}
|
|
|
|
/* True if the register is part of a multi-register hard REG. */
|
|
bool is_multireg () const { return flags & rtx_obj_flags::IS_MULTIREG; }
|
|
|
|
/* True if the reference occurs in the address of a load MEM. */
|
|
bool in_mem_load () const { return flags & rtx_obj_flags::IN_MEM_LOAD; }
|
|
|
|
/* True if the reference occurs in the address of a store MEM. */
|
|
bool in_mem_store () const { return flags & rtx_obj_flags::IN_MEM_STORE; }
|
|
|
|
/* True if the reference occurs in any kind of MEM address. */
|
|
bool in_address () const { return in_mem_load () || in_mem_store (); }
|
|
|
|
/* True if the reference occurs in a SUBREG. */
|
|
bool in_subreg () const { return flags & rtx_obj_flags::IN_SUBREG; }
|
|
|
|
/* True if the reference occurs in a REG_EQUAL or REG_EQUIV note. */
|
|
bool in_note () const { return flags & rtx_obj_flags::IN_NOTE; }
|
|
|
|
/* The referenced register, or MEM_REGNO for variable memory. */
|
|
unsigned int regno;
|
|
|
|
/* A bitmask of rtx_obj_flags. */
|
|
unsigned int flags : 16;
|
|
|
|
/* The mode of the reference. If IS_MULTIREG, this is the mode of
|
|
REGNO - MULTIREG_OFFSET. */
|
|
machine_mode mode : 8;
|
|
|
|
/* If IS_MULTIREG, the offset of REGNO from the start of the register. */
|
|
unsigned int multireg_offset : 8;
|
|
};
|
|
|
|
/* Construct a reference with the given fields. */
|
|
|
|
inline rtx_obj_reference::rtx_obj_reference (unsigned int regno, uint16_t flags,
|
|
machine_mode mode,
|
|
unsigned int multireg_offset)
|
|
: regno (regno),
|
|
flags (flags),
|
|
mode (mode),
|
|
multireg_offset (multireg_offset)
|
|
{
|
|
}
|
|
|
|
/* Contains information about an rtx or an instruction, including a
|
|
list of rtx_obj_references. The storage backing the list needs
|
|
to be filled in by assigning to REF_BEGIN and REF_END. */
|
|
|
|
class rtx_properties
|
|
{
|
|
public:
|
|
rtx_properties ();
|
|
|
|
void try_to_add_reg (const_rtx x, unsigned int flags = 0);
|
|
void try_to_add_dest (const_rtx x, unsigned int flags = 0);
|
|
void try_to_add_src (const_rtx x, unsigned int flags = 0);
|
|
void try_to_add_pattern (const_rtx pat);
|
|
void try_to_add_note (const_rtx x);
|
|
void try_to_add_insn (const rtx_insn *insn, bool include_notes);
|
|
|
|
iterator_range<rtx_obj_reference *> refs () const;
|
|
|
|
/* Return the number of rtx_obj_references that have been recorded. */
|
|
size_t num_refs () const { return ref_iter - ref_begin; }
|
|
|
|
bool has_side_effects () const;
|
|
|
|
/* [REF_BEGIN, REF_END) is the maximum extent of the memory available
|
|
for recording references. REG_ITER is the first unused entry. */
|
|
rtx_obj_reference *ref_begin;
|
|
rtx_obj_reference *ref_iter;
|
|
rtx_obj_reference *ref_end;
|
|
|
|
/* True if the rtx includes an asm. */
|
|
unsigned int has_asm : 1;
|
|
|
|
/* True if the rtx includes a call. */
|
|
unsigned int has_call : 1;
|
|
|
|
/* True if the rtx includes an RTX_AUTOINC expression. */
|
|
unsigned int has_pre_post_modify : 1;
|
|
|
|
/* True if the rtx contains volatile references, in the sense of
|
|
volatile_refs_p. */
|
|
unsigned int has_volatile_refs : 1;
|
|
|
|
/* For future expansion. */
|
|
unsigned int spare : 28;
|
|
};
|
|
|
|
inline rtx_properties::rtx_properties ()
|
|
: ref_begin (nullptr),
|
|
ref_iter (nullptr),
|
|
ref_end (nullptr),
|
|
has_asm (false),
|
|
has_call (false),
|
|
has_pre_post_modify (false),
|
|
has_volatile_refs (false),
|
|
spare (0)
|
|
{
|
|
}
|
|
|
|
/* Like add_src, but treat X has being part of a REG_EQUAL or
|
|
REG_EQUIV note. */
|
|
|
|
inline void
|
|
rtx_properties::try_to_add_note (const_rtx x)
|
|
{
|
|
try_to_add_src (x, rtx_obj_flags::IN_NOTE);
|
|
}
|
|
|
|
/* Return true if the rtx has side effects, in the sense of
|
|
side_effects_p (except for side_effects_p's special handling
|
|
of combine.cc clobbers). */
|
|
|
|
inline bool
|
|
rtx_properties::has_side_effects () const
|
|
{
|
|
return has_volatile_refs || has_pre_post_modify || has_call;
|
|
}
|
|
|
|
/* Return an iterator range for all the references, suitable for
|
|
range-based for loops. */
|
|
|
|
inline iterator_range<rtx_obj_reference *>
|
|
rtx_properties::refs () const
|
|
{
|
|
return { ref_begin, ref_iter };
|
|
}
|
|
|
|
/* BASE is derived from rtx_properties and provides backing storage
|
|
for REF_BEGIN. It has a grow () method that increases the amount
|
|
of memory available if the initial allocation was too small. */
|
|
|
|
template<typename Base>
|
|
class growing_rtx_properties : public Base
|
|
{
|
|
public:
|
|
template<typename... Args>
|
|
growing_rtx_properties (Args...);
|
|
|
|
template<typename AddFn>
|
|
void repeat (AddFn add);
|
|
|
|
/* Wrappers around the try_to_* functions that always succeed. */
|
|
void add_dest (const_rtx x, unsigned int flags = 0);
|
|
void add_src (const_rtx x, unsigned int flags = 0);
|
|
void add_pattern (const_rtx pat);
|
|
void add_note (const_rtx x);
|
|
void add_insn (const rtx_insn *insn, bool include_notes);
|
|
};
|
|
|
|
template<typename Base>
|
|
template<typename... Args>
|
|
growing_rtx_properties<Base>::growing_rtx_properties (Args... args)
|
|
: Base (std::forward<Args> (args)...)
|
|
{
|
|
}
|
|
|
|
/* Perform ADD until there is enough room to hold the result. */
|
|
|
|
template<typename Base>
|
|
template<typename AddFn>
|
|
inline void
|
|
growing_rtx_properties<Base>::repeat (AddFn add)
|
|
{
|
|
ptrdiff_t count = this->num_refs ();
|
|
for (;;)
|
|
{
|
|
add ();
|
|
/* This retries if the storage happened to be exactly the right size,
|
|
but that's expected to be a rare case and so isn't worth
|
|
optimizing for. */
|
|
if (LIKELY (this->ref_iter != this->ref_end))
|
|
break;
|
|
this->grow (count);
|
|
}
|
|
}
|
|
|
|
template<typename Base>
|
|
inline void
|
|
growing_rtx_properties<Base>::add_dest (const_rtx x, unsigned int flags)
|
|
{
|
|
repeat ([&]() { this->try_to_add_dest (x, flags); });
|
|
}
|
|
|
|
template<typename Base>
|
|
inline void
|
|
growing_rtx_properties<Base>::add_src (const_rtx x, unsigned int flags)
|
|
{
|
|
repeat ([&]() { this->try_to_add_src (x, flags); });
|
|
}
|
|
|
|
template<typename Base>
|
|
inline void
|
|
growing_rtx_properties<Base>::add_pattern (const_rtx pat)
|
|
{
|
|
repeat ([&]() { this->try_to_add_pattern (pat); });
|
|
}
|
|
|
|
template<typename Base>
|
|
inline void
|
|
growing_rtx_properties<Base>::add_note (const_rtx x)
|
|
{
|
|
repeat ([&]() { this->try_to_add_note (x); });
|
|
}
|
|
|
|
template<typename Base>
|
|
inline void
|
|
growing_rtx_properties<Base>::add_insn (const rtx_insn *insn, bool include_notes)
|
|
{
|
|
repeat ([&]() { this->try_to_add_insn (insn, include_notes); });
|
|
}
|
|
|
|
/* A base class for vec_rtx_properties; see there for details. */
|
|
|
|
class vec_rtx_properties_base : public rtx_properties
|
|
{
|
|
static const size_t SIZE = 32;
|
|
|
|
public:
|
|
vec_rtx_properties_base ();
|
|
~vec_rtx_properties_base ();
|
|
|
|
protected:
|
|
void grow (ptrdiff_t);
|
|
|
|
private:
|
|
rtx_obj_reference m_storage[SIZE];
|
|
};
|
|
|
|
inline vec_rtx_properties_base::vec_rtx_properties_base ()
|
|
{
|
|
ref_begin = ref_iter = m_storage;
|
|
ref_end = m_storage + SIZE;
|
|
}
|
|
|
|
inline vec_rtx_properties_base::~vec_rtx_properties_base ()
|
|
{
|
|
if (UNLIKELY (ref_begin != m_storage))
|
|
free (ref_begin);
|
|
}
|
|
|
|
/* A rtx_properties that stores its references in a temporary array.
|
|
Like auto_vec, the array is initially on the stack, but can switch
|
|
to the heap if necessary.
|
|
|
|
The reason for implementing this as a derived class is that the
|
|
default on-stack size should be enough for the vast majority of
|
|
expressions and instructions. It's therefore not worth paying
|
|
the cost of conditionally calling grow code at every site that
|
|
records a new reference. Instead, the rtx_properties code can use
|
|
trivial iterator updates for the common case, and in the rare case
|
|
that the vector needs to be resized, we can pay the cost of
|
|
collecting the references a second time. */
|
|
using vec_rtx_properties = growing_rtx_properties<vec_rtx_properties_base>;
|
|
|
|
bool
|
|
vec_series_highpart_p (machine_mode result_mode, machine_mode op_mode,
|
|
rtx sel);
|
|
|
|
bool
|
|
vec_series_lowpart_p (machine_mode result_mode, machine_mode op_mode, rtx sel);
|
|
|
|
bool
|
|
contains_paradoxical_subreg_p (rtx x);
|
|
#endif
|