aboutsummaryrefslogtreecommitdiff
path: root/gcc/rtl-ssa/functions.cc
blob: 8313cd6f78355843a34f699c1d0cabdd69683aa5 (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
// Implementation of function-related RTL SSA functions             -*- C++ -*-
// Copyright (C) 2020-2024 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/>.

#define INCLUDE_ALGORITHM
#define INCLUDE_FUNCTIONAL
#include "config.h"
#include "system.h"
#include "coretypes.h"
#include "backend.h"
#include "rtl.h"
#include "df.h"
#include "rtl-ssa.h"
#include "rtl-ssa/internals.h"
#include "rtl-ssa/internals.inl"

using namespace rtl_ssa;

function_info::function_info (function *fn)
  : m_fn (fn), m_clobbered_by_calls ()
{
  // Force the alignment to be obstack_alignment.  Everything else is normal.
  obstack_specify_allocation (&m_obstack, OBSTACK_CHUNK_SIZE,
			      obstack_alignment, obstack_chunk_alloc,
			      obstack_chunk_free);
  obstack_specify_allocation (&m_temp_obstack, OBSTACK_CHUNK_SIZE,
			      obstack_alignment, obstack_chunk_alloc,
			      obstack_chunk_free);

  // Record the start of the obstacks.
  m_obstack_start = XOBNEWVAR (&m_obstack, char, 0);
  m_temp_obstack_start = XOBNEWVAR (&m_temp_obstack, char, 0);

  init_function_data ();
  process_all_blocks ();
  simplify_phis ();
}

function_info::~function_info ()
{
  // Anything using the temporary obstack should free it afterwards,
  // preferably via temp_watermark ().
  gcc_assert (XOBNEWVAR (&m_temp_obstack, char, 0) == m_temp_obstack_start);

  obstack_free (&m_temp_obstack, nullptr);
  obstack_free (&m_obstack, nullptr);
}

// See the comment above the declaration.
void
function_info::print (pretty_printer *pp) const
{
  pp_string (pp, "Function: ");
  pp_string (pp, function_name (m_fn));
  for (ebb_info *ebb : ebbs ())
    {
      pp_newline (pp);
      pp_newline_and_indent (pp, 0);
      pp_ebb (pp, ebb);
    }
}

// Initialize all member variables in preparation for (re)building
// SSA form from scratch.
void
function_info::init_function_data ()
{
  m_next_artificial_uid = -1;
  m_next_phi_uid = 0;
  m_num_regs = max_reg_num ();
  m_defs.safe_grow_cleared (m_num_regs + 1);
  m_bbs.safe_grow_cleared (last_basic_block_for_fn (m_fn));
  m_first_bb = nullptr;
  m_last_bb = nullptr;
  m_first_insn = nullptr;
  m_last_insn = nullptr;
  m_last_nondebug_insn = nullptr;
  m_free_phis = nullptr;
}

// The initial phase of the phi simplification process.  The cumulative
// effect of the initial phase is to set up ASSUMED_VALUES such that,
// for a phi P with uid ID:
//
// - if we think all inputs to P have the same value, ASSUMED_VALUES[ID]
//   is that value
//
// - otherwise, ASSUMED_VALUES[ID] is P.
//
// This has already been done for phis with a lower uid than PHI,
// initially making optimistic assumptions about backedge inputs.
// Now do the same for PHI.  If this might invalidate any assumptions
// made for earlier phis, add the uids of those phis to WORKLIST.
void
function_info::simplify_phi_setup (phi_info *phi, set_info **assumed_values,
				   bitmap worklist)
{
  // If all non-backedge inputs have the same value, set NEW_VALUE
  // to that value.  Otherwise set NEW_VALUE to PHI, to indicate
  // that PHI cannot be simplified.
  unsigned int phi_uid = phi->uid ();
  bool is_first_input = true;
  set_info *new_value = nullptr;
  machine_mode phi_mode = phi->mode ();
  for (use_info *input : phi->inputs ())
    {
      set_info *def = input->def ();

      if (auto *input_phi = safe_dyn_cast<phi_info *> (def))
	{
	  // Ignore backedges for now.
	  unsigned int input_phi_uid = input_phi->uid ();
	  if (phi_uid <= input_phi_uid)
	    continue;

	  def = assumed_values[input_phi_uid];
	}

      // Compare this definition with previous ones.
      if (is_first_input)
	{
	  new_value = def;
	  is_first_input = false;
	}
      else if (new_value != def)
	new_value = phi;

      // If the input has a known mode (i.e. not BLKmode), make sure
      // that the phi's mode is at least as large.
      if (def)
	phi_mode = combine_modes (phi_mode, def->mode ());
    }
  if (phi->mode () != phi_mode)
    phi->set_mode (phi_mode);

  // Since we use a reverse postorder traversal, no phi can consist
  // entirely of backedges.
  gcc_checking_assert (!is_first_input);
  assumed_values[phi_uid] = new_value;

  // See whether any assumptions for earlier phis are now invalid.
  simplify_phi_propagate (phi, assumed_values, nullptr, worklist);
}

// The propagation phase of the phi simplification process, with
// ASSUMED_VALUES as described above simplify_phi_setup.  Iteratively
// update the phis that use PHI based on PHI's entry in ASSUMED_VALUES.
// If CURR_WORKLIST is null, consider only phi uses with a lower uid
// than PHI, otherwise consider all phi uses.
//
// If a phi with a higher uid than PHI needs updating, add its uid to
// CURR_WORKLIST; if a phi with a lower uid than PHI needs updating,
// add its uid to NEXT_WORKLIST.
void
function_info::simplify_phi_propagate (phi_info *phi,
				       set_info **assumed_values,
				       bitmap curr_worklist,
				       bitmap next_worklist)
{
  // Go through each phi user of PHI to see whether it needs updating.
  unsigned int phi_uid = phi->uid ();
  machine_mode phi_mode = phi->mode ();
  set_info *phi_value = assumed_values[phi_uid];
  for (use_info *use : phi->phi_uses ())
    {
      phi_info *user_phi = use->phi ();

      // Propagate the phi's new mode to all phi users.  Insn uses should
      // not be updated, since their modes reflect a property of the insns
      // rather than the phi.
      if (use->mode () != phi_mode)
	use->set_mode (phi_mode);

      if (user_phi == phi)
	continue;

      // If this is a phi we should be looking at, see whether it needs
      // an update.
      unsigned int user_phi_uid = user_phi->uid ();
      if (user_phi_uid < phi_uid || curr_worklist)
	{
	  bool needs_update = false;

	  // Make sure that USER_PHI's mode is at least as big as PHI_MODE.
	  machine_mode user_phi_mode = user_phi->mode ();
	  machine_mode new_mode = combine_modes (user_phi_mode, phi_mode);
	  if (user_phi_mode != new_mode)
	    {
	      user_phi->set_mode (new_mode);
	      needs_update = true;
	    }

	  // If USER_PHI optimistically assumed an incorrect value,
	  // adjust it now.
	  if (assumed_values[user_phi_uid] != user_phi
	      && assumed_values[user_phi_uid] != phi_value)
	    {
	      assumed_values[user_phi_uid] = user_phi;
	      needs_update = true;
	    }

	  if (needs_update)
	    {
	      if (user_phi_uid < phi_uid)
		bitmap_set_bit (next_worklist, user_phi_uid);
	      else
		bitmap_set_bit (curr_worklist, user_phi_uid);
	    }
	}
    }
}

// Update the modes of all phis so that they are at least as big as
// all inputs.  Remove any non-degenerate phis whose inputs are all equal.
void
function_info::simplify_phis ()
{
  auto temps = temp_watermark ();

  // See the comment above simplify_phi_setup for details about this array.
  auto *assumed_values = XOBNEWVEC (&m_temp_obstack, set_info *,
				    m_next_phi_uid);

  // An array of all phis, indexed by uid.
  auto *phis = XOBNEWVEC (&m_temp_obstack, phi_info *, m_next_phi_uid);

  // Which phi uids are actually in use.
  auto_sbitmap valid_phi_uids (m_next_phi_uid);
  bitmap_clear (valid_phi_uids);

  // Bitmaps used for the main double-queue propagation phase.
  auto_bitmap worklist1;
  auto_bitmap worklist2;
  bitmap curr_worklist = worklist1;
  bitmap next_worklist = worklist2;

  // Perform the set-up phase; see simplify_phi_setup for details.
  for (ebb_info *ebb : ebbs ())
    for (phi_info *phi : ebb->phis ())
      {
	bitmap_set_bit (valid_phi_uids, phi->uid ());
	phis[phi->uid ()] = phi;
	simplify_phi_setup (phi, assumed_values, curr_worklist);
      }

  // Iteratively process any phis that need updating; see
  // simplify_phi_propagate for details.  Using a double queue
  // should reduce the number of times that any given phi node
  // needs to be revisited.
  while (!bitmap_empty_p (curr_worklist))
    {
      do
	{
	  unsigned int uid = bitmap_first_set_bit (curr_worklist);
	  bitmap_clear_bit (curr_worklist, uid);
	  simplify_phi_propagate (phis[uid], assumed_values,
				  curr_worklist, next_worklist);
	}
      while (!bitmap_empty_p (curr_worklist));
      std::swap (next_worklist, curr_worklist);
    }

  // Make sure that assumed_values is a transitive closure.  This ensures
  // that each use_info is only updated once.
  if (flag_checking)
    for (unsigned int i = 0; i < m_next_phi_uid; ++i)
      if (bitmap_bit_p (valid_phi_uids, i))
	if (auto *new_phi = safe_dyn_cast<phi_info *> (assumed_values[i]))
	  gcc_assert (assumed_values[new_phi->uid ()] == new_phi);

  // Update any phis that turned out to be equivalent to a single input.
  for (unsigned int i = 0; i < m_next_phi_uid; ++i)
    if (bitmap_bit_p (valid_phi_uids, i) && phis[i] != assumed_values[i])
      replace_phi (phis[i], assumed_values[i]);
}

// Print a description of FUNCTION to PP.
void
rtl_ssa::pp_function (pretty_printer *pp, const function_info *function)
{
  function->print (pp);
}

// Print a description of FUNCTION to FILE.
void
dump (FILE *file, const function_info *function)
{
  dump_using (file, pp_function, function);
}

// Debug interface to the dump routine above.
void debug (const function_info *x) { dump (stderr, x); }