aboutsummaryrefslogtreecommitdiff
path: root/gcc/rtlanal.h
blob: dedc2d70613a9fea6dc2bcd1b22f572cd183d63d (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
/* Analyze RTL for GNU compiler.
   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/>.  */

/* Note that for historical reasons, many rtlanal.c 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.c 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>;

#endif