1/* Analyze RTL for GNU compiler.
2   Copyright (C) 2020-2022 Free Software Foundation, Inc.
3
4This file is part of GCC.
5
6GCC is free software; you can redistribute it and/or modify it under
7the terms of the GNU General Public License as published by the Free
8Software Foundation; either version 3, or (at your option) any later
9version.
10
11GCC is distributed in the hope that it will be useful, but WITHOUT ANY
12WARRANTY; without even the implied warranty of MERCHANTABILITY or
13FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
14for more details.
15
16You should have received a copy of the GNU General Public License
17along with GCC; see the file COPYING3.  If not see
18<http://www.gnu.org/licenses/>.  */
19
20/* Note that for historical reasons, many rtlanal.cc functions are
21   declared in rtl.h rather than here.  */
22
23#ifndef GCC_RTLANAL_H
24#define GCC_RTLANAL_H
25
26/* A dummy register value that represents the whole of variable memory.
27   Using ~0U means that arrays that track both registers and memory can
28   be indexed by regno + 1.  */
29const unsigned int MEM_REGNO = ~0U;
30
31/* Bitmasks of flags describing an rtx_obj_reference.  See the accessors
32   in the class for details.  */
33namespace rtx_obj_flags
34{
35  const uint16_t IS_READ = 1U << 0;
36  const uint16_t IS_WRITE = 1U << 1;
37  const uint16_t IS_CLOBBER = 1U << 2;
38  const uint16_t IS_PRE_POST_MODIFY = 1U << 3;
39  const uint16_t IS_MULTIREG = 1U << 4;
40  const uint16_t IN_MEM_LOAD = 1U << 5;
41  const uint16_t IN_MEM_STORE = 1U << 6;
42  const uint16_t IN_SUBREG = 1U << 7;
43  const uint16_t IN_NOTE = 1U << 8;
44
45  /* Flags that apply to all subrtxes of the rtx they were originally
46     added for.  */
47  static const uint16_t STICKY_FLAGS = IN_NOTE;
48}
49
50/* Contains information about a reference to a register or variable memory.  */
51class rtx_obj_reference
52{
53public:
54  rtx_obj_reference () = default;
55  rtx_obj_reference (unsigned int regno, uint16_t flags,
56		     machine_mode mode, unsigned int multireg_offset = 0);
57
58  bool is_reg () const { return regno != MEM_REGNO; }
59  bool is_mem () const { return regno == MEM_REGNO; }
60
61  /* True if the reference is a read or a write respectively.
62     Both flags are set in a read-modify-write context, such as
63     for read_modify_subreg_p.  */
64  bool is_read () const { return flags & rtx_obj_flags::IS_READ; }
65  bool is_write () const { return flags & rtx_obj_flags::IS_WRITE; }
66
67  /* True if IS_WRITE and if the write is a clobber rather than a set.  */
68  bool is_clobber () const { return flags & rtx_obj_flags::IS_CLOBBER; }
69
70  /* True if the reference is updated by an RTX_AUTOINC.  Both IS_READ
71     and IS_WRITE are also true if so.  */
72  bool is_pre_post_modify () const
73  {
74    return flags & rtx_obj_flags::IS_PRE_POST_MODIFY;
75  }
76
77  /* True if the register is part of a multi-register hard REG.  */
78  bool is_multireg () const { return flags & rtx_obj_flags::IS_MULTIREG; }
79
80  /* True if the reference occurs in the address of a load MEM.  */
81  bool in_mem_load () const { return flags & rtx_obj_flags::IN_MEM_LOAD; }
82
83  /* True if the reference occurs in the address of a store MEM.  */
84  bool in_mem_store () const { return flags & rtx_obj_flags::IN_MEM_STORE; }
85
86  /* True if the reference occurs in any kind of MEM address.  */
87  bool in_address () const { return in_mem_load () || in_mem_store (); }
88
89  /* True if the reference occurs in a SUBREG.  */
90  bool in_subreg () const { return flags & rtx_obj_flags::IN_SUBREG; }
91
92  /* True if the reference occurs in a REG_EQUAL or REG_EQUIV note.  */
93  bool in_note () const { return flags & rtx_obj_flags::IN_NOTE; }
94
95  /* The referenced register, or MEM_REGNO for variable memory.  */
96  unsigned int regno;
97
98  /* A bitmask of rtx_obj_flags.  */
99  unsigned int flags : 16;
100
101  /* The mode of the reference.  If IS_MULTIREG, this is the mode of
102     REGNO - MULTIREG_OFFSET.  */
103  machine_mode mode : 8;
104
105  /* If IS_MULTIREG, the offset of REGNO from the start of the register.  */
106  unsigned int multireg_offset : 8;
107};
108
109/* Construct a reference with the given fields.  */
110
111inline rtx_obj_reference::rtx_obj_reference (unsigned int regno, uint16_t flags,
112					     machine_mode mode,
113					     unsigned int multireg_offset)
114  : regno (regno),
115    flags (flags),
116    mode (mode),
117    multireg_offset (multireg_offset)
118{
119}
120
121/* Contains information about an rtx or an instruction, including a
122   list of rtx_obj_references.  The storage backing the list needs
123   to be filled in by assigning to REF_BEGIN and REF_END.  */
124
125class rtx_properties
126{
127public:
128  rtx_properties ();
129
130  void try_to_add_reg (const_rtx x, unsigned int flags = 0);
131  void try_to_add_dest (const_rtx x, unsigned int flags = 0);
132  void try_to_add_src (const_rtx x, unsigned int flags = 0);
133  void try_to_add_pattern (const_rtx pat);
134  void try_to_add_note (const_rtx x);
135  void try_to_add_insn (const rtx_insn *insn, bool include_notes);
136
137  iterator_range<rtx_obj_reference *> refs () const;
138
139  /* Return the number of rtx_obj_references that have been recorded.  */
140  size_t num_refs () const { return ref_iter - ref_begin; }
141
142  bool has_side_effects () const;
143
144  /* [REF_BEGIN, REF_END) is the maximum extent of the memory available
145     for recording references.  REG_ITER is the first unused entry.  */
146  rtx_obj_reference *ref_begin;
147  rtx_obj_reference *ref_iter;
148  rtx_obj_reference *ref_end;
149
150  /* True if the rtx includes an asm.  */
151  unsigned int has_asm : 1;
152
153  /* True if the rtx includes a call.  */
154  unsigned int has_call : 1;
155
156  /* True if the rtx includes an RTX_AUTOINC expression.  */
157  unsigned int has_pre_post_modify : 1;
158
159  /* True if the rtx contains volatile references, in the sense of
160     volatile_refs_p.  */
161  unsigned int has_volatile_refs : 1;
162
163  /* For future expansion.  */
164  unsigned int spare : 28;
165};
166
167inline rtx_properties::rtx_properties ()
168  : ref_begin (nullptr),
169    ref_iter (nullptr),
170    ref_end (nullptr),
171    has_asm (false),
172    has_call (false),
173    has_pre_post_modify (false),
174    has_volatile_refs (false),
175    spare (0)
176{
177}
178
179/* Like add_src, but treat X has being part of a REG_EQUAL or
180   REG_EQUIV note.  */
181
182inline void
183rtx_properties::try_to_add_note (const_rtx x)
184{
185  try_to_add_src (x, rtx_obj_flags::IN_NOTE);
186}
187
188/* Return true if the rtx has side effects, in the sense of
189   side_effects_p (except for side_effects_p's special handling
190   of combine.cc clobbers).  */
191
192inline bool
193rtx_properties::has_side_effects () const
194{
195  return has_volatile_refs || has_pre_post_modify || has_call;
196}
197
198/* Return an iterator range for all the references, suitable for
199   range-based for loops.  */
200
201inline iterator_range<rtx_obj_reference *>
202rtx_properties::refs () const
203{
204  return { ref_begin, ref_iter };
205}
206
207/* BASE is derived from rtx_properties and provides backing storage
208   for REF_BEGIN.  It has a grow () method that increases the amount
209   of memory available if the initial allocation was too small.  */
210
211template<typename Base>
212class growing_rtx_properties : public Base
213{
214public:
215  template<typename... Args>
216  growing_rtx_properties (Args...);
217
218  template<typename AddFn>
219  void repeat (AddFn add);
220
221  /* Wrappers around the try_to_* functions that always succeed.  */
222  void add_dest (const_rtx x, unsigned int flags = 0);
223  void add_src (const_rtx x, unsigned int flags = 0);
224  void add_pattern (const_rtx pat);
225  void add_note (const_rtx x);
226  void add_insn (const rtx_insn *insn, bool include_notes);
227};
228
229template<typename Base>
230template<typename... Args>
231growing_rtx_properties<Base>::growing_rtx_properties (Args... args)
232  : Base (std::forward<Args> (args)...)
233{
234}
235
236/* Perform ADD until there is enough room to hold the result.  */
237
238template<typename Base>
239template<typename AddFn>
240inline void
241growing_rtx_properties<Base>::repeat (AddFn add)
242{
243  ptrdiff_t count = this->num_refs ();
244  for (;;)
245    {
246      add ();
247      /* This retries if the storage happened to be exactly the right size,
248	 but that's expected to be a rare case and so isn't worth
249	 optimizing for.  */
250      if (__builtin_expect (this->ref_iter != this->ref_end, 1))
251	break;
252      this->grow (count);
253    }
254}
255
256template<typename Base>
257inline void
258growing_rtx_properties<Base>::add_dest (const_rtx x, unsigned int flags)
259{
260  repeat ([&]() { this->try_to_add_dest (x, flags); });
261}
262
263template<typename Base>
264inline void
265growing_rtx_properties<Base>::add_src (const_rtx x, unsigned int flags)
266{
267  repeat ([&]() { this->try_to_add_src (x, flags); });
268}
269
270template<typename Base>
271inline void
272growing_rtx_properties<Base>::add_pattern (const_rtx pat)
273{
274  repeat ([&]() { this->try_to_add_pattern (pat); });
275}
276
277template<typename Base>
278inline void
279growing_rtx_properties<Base>::add_note (const_rtx x)
280{
281  repeat ([&]() { this->try_to_add_note (x); });
282}
283
284template<typename Base>
285inline void
286growing_rtx_properties<Base>::add_insn (const rtx_insn *insn, bool include_notes)
287{
288  repeat ([&]() { this->try_to_add_insn (insn, include_notes); });
289}
290
291/* A base class for vec_rtx_properties; see there for details.  */
292
293class vec_rtx_properties_base : public rtx_properties
294{
295  static const size_t SIZE = 32;
296
297public:
298  vec_rtx_properties_base ();
299  ~vec_rtx_properties_base ();
300
301protected:
302  void grow (ptrdiff_t);
303
304private:
305  rtx_obj_reference m_storage[SIZE];
306};
307
308inline vec_rtx_properties_base::vec_rtx_properties_base ()
309{
310  ref_begin = ref_iter = m_storage;
311  ref_end = m_storage + SIZE;
312}
313
314inline vec_rtx_properties_base::~vec_rtx_properties_base ()
315{
316  if (__builtin_expect (ref_begin != m_storage, 0))
317    free (ref_begin);
318}
319
320/* A rtx_properties that stores its references in a temporary array.
321   Like auto_vec, the array is initially on the stack, but can switch
322   to the heap if necessary.
323
324   The reason for implementing this as a derived class is that the
325   default on-stack size should be enough for the vast majority of
326   expressions and instructions.  It's therefore not worth paying
327   the cost of conditionally calling grow code at every site that
328   records a new reference.  Instead, the rtx_properties code can use
329   trivial iterator updates for the common case, and in the rare case
330   that the vector needs to be resized, we can pay the cost of
331   collecting the references a second time.  */
332using vec_rtx_properties = growing_rtx_properties<vec_rtx_properties_base>;
333
334bool
335vec_series_highpart_p (machine_mode result_mode, machine_mode op_mode,
336		       rtx sel);
337
338bool
339vec_series_lowpart_p (machine_mode result_mode, machine_mode op_mode, rtx sel);
340
341#endif
342