| /* Analyze RTL for GNU compiler. |
| 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/>. */ |
| |
| /* 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 (__builtin_expect (this->ref_iter != this->ref_end, 1)) |
| 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 (__builtin_expect (ref_begin != m_storage, 0)) |
| 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); |
| |
| #endif |