summaryrefslogtreecommitdiff
path: root/gcc/tree-ssa-threadupdate.c
blob: b4b30fa985bcee7efc2a2a178560dc23ee1d5e40 (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
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
/* Thread edges through blocks and update the control flow and SSA graphs.
   Copyright (C) 2004 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 2, 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 COPYING.  If not, write to
the Free Software Foundation, 59 Temple Place - Suite 330,
Boston, MA 02111-1307, USA.  */

#include "config.h"
#include "system.h"
#include "coretypes.h"
#include "tm.h"
#include "tree.h"
#include "flags.h"
#include "rtl.h"
#include "tm_p.h"
#include "ggc.h"
#include "basic-block.h"
#include "output.h"
#include "errors.h"
#include "expr.h"
#include "function.h"
#include "diagnostic.h"
#include "tree-flow.h"
#include "tree-dump.h"
#include "tree-pass.h"

/* Given a block B, update the CFG and SSA graph to reflect redirecting
   one or more in-edges to B to instead reach the destination of an
   out-edge from B while preserving any side effects in B.

   i.e., given A->B and B->C, change A->B to be A->C yet still preserve the
   side effects of executing B.

     1. Make a copy of B (including its outgoing edges and statements).  Call
	the copy B'.  Note B' has no incoming edges or PHIs at this time.

     2. Remove the control statement at the end of B' and all outgoing edges
	except B'->C.

     3. Add a new argument to each PHI in C with the same value as the existing
	argument associated with edge B->C.  Associate the new PHI arguments
	with the edge B'->C.

     4. For each PHI in B, find or create a PHI in B' with an identical
	PHI_RESULT.  Add an argument to the PHI in B' which has the same
	value as the PHI in B associated with the edge A->B.  Associate
	the new argument in the PHI in B' with the edge A->B.

     5. Change the edge A->B to A->B'.

	5a. This automatically deletes any PHI arguments associated with the
	    edge A->B in B.

	5b. This automatically associates each new argument added in step 4
	    with the edge A->B'.

     6. Repeat for other incoming edges into B.

     7. Put the duplicated resources in B and all the B' blocks into SSA form.

   Note that block duplication can be minimized by first collecting the
   the set of unique destination blocks that the incoming edges should
   be threaded to.  Block duplication can be further minimized by using 
   B instead of creating B' for one destination if all edges into B are
   going to be threaded to a successor of B.  */


/* Main data structure recording information regarding B's duplicate
   blocks.  */

struct redirection_data
{
  /* A duplicate of B with the trailing control statement removed and which
     targets a single successor of B.  */
  basic_block dup_block;

  /* An outgoing edge from B.  DUP_BLOCK will have OUTGOING_EDGE->dest as
     its single successor.  */
  edge outgoing_edge;
};

/* Main data structure to hold information for duplicates of BB.  */
static varray_type redirection_data;

/* Remove the last statement in block BB if it is a control statement
   Also remove all outgoing edges except the edge which reaches DEST_BB.
   If DEST_BB is NULL, then remove all outgoing edges.  */

static void
remove_ctrl_stmt_and_useless_edges (basic_block bb, basic_block dest_bb)
{
  block_stmt_iterator bsi;
  edge e;
  edge_iterator ei;

  bsi = bsi_last (bb);

  /* If the duplicate ends with a control statement, then remove it.

     Note that if we are duplicating the template block rather than the
     original basic block, then the duplicate might not have any real
     statements in it.  */
  if (!bsi_end_p (bsi)
      && bsi_stmt (bsi)
      && (TREE_CODE (bsi_stmt (bsi)) == COND_EXPR
	  || TREE_CODE (bsi_stmt (bsi)) == SWITCH_EXPR))
    bsi_remove (&bsi);

  for (ei = ei_start (bb->succs); (e = ei_safe_edge (ei)); )
    {
      if (e->dest != dest_bb)
	ssa_remove_edge (e);
      else
	ei_next (&ei);
    }
}

/* Create a duplicate of BB which only reaches the destination of the edge
   stored in RD.  Record the duplicate block in RD.  */

static void
create_block_for_threading (basic_block bb, struct redirection_data *rd)
{
  /* We can use the generic block duplication code and simply remove
     the stuff we do not need.  */
  rd->dup_block = duplicate_block (bb, NULL);

  /* Zero out the profile, since the block is unreachable for now.  */
  rd->dup_block->frequency = 0;
  rd->dup_block->count = 0;

  /* The call to duplicate_block will copy everything, including the
     useless COND_EXPR or SWITCH_EXPR at the end of BB.  We just remove
     the useless COND_EXPR or SWITCH_EXPR here rather than having a
     specialized block copier.  We also remove all outgoing edges
     from the duplicate block.  The appropriate edge will be created
     later.  */
  remove_ctrl_stmt_and_useless_edges (rd->dup_block, NULL);
}

/* BB is a block which ends with a COND_EXPR or SWITCH_EXPR and when BB
   is reached via one or more specific incoming edges, we know which
   outgoing edge from BB will be traversed.

   We want to redirect those incoming edges to the target of the 
   appropriate outgoing edge.  Doing so avoids a conditional branch
   and may expose new optimization opportunities.  Note that we have
   to update dominator tree and SSA graph after such changes.

   The key to keeping the SSA graph update manageable is to duplicate
   the side effects occurring in BB so that those side effects still
   occur on the paths which bypass BB after redirecting edges.

   We accomplish this by creating duplicates of BB and arranging for
   the duplicates to unconditionally pass control to one specific
   successor of BB.  We then revector the incoming edges into BB to
   the appropriate duplicate of BB.

   BB and its duplicates will have assignments to the same set of
   SSA_NAMEs.  Right now, we just call into rewrite_ssa_into_ssa
   to update the SSA graph for those names.

   We are also going to experiment with a true incremental update
   scheme for the duplicated resources.  One of the interesting
   properties we can exploit here is that all the resources set
   in BB will have the same IDFS, so we have one IDFS computation
   per block with incoming threaded edges, which can lower the
   cost of the true incremental update algorithm.  */

static void
thread_block (basic_block bb)
{
  /* E is an incoming edge into BB that we may or may not want to
     redirect to a duplicate of BB.  */
  edge e;
  edge_iterator ei;
  basic_block template_block;

  /* ALL indicates whether or not all incoming edges into BB should
     be threaded to a duplicate of BB.  */
  bool all = true;

  unsigned int i;

  VARRAY_GENERIC_PTR_INIT (redirection_data, 2, "redirection data");

  /* Look at each incoming edge into BB.  Record each unique outgoing
     edge that we want to thread an incoming edge to.  Also note if
     all incoming edges are threaded or not.  */
  FOR_EACH_EDGE (e, ei, bb->preds)
    {
      if (!e->aux)
	{
	  all = false;
	}
      else
	{
	  unsigned int i;

	  /* See if we can find an entry for the destination of this
	     threaded edge that has already been recorded.  */
	  for (i = 0; i < VARRAY_ACTIVE_SIZE (redirection_data); i++)
	    {
	      struct redirection_data *rd;
	      edge e2;

	      rd = VARRAY_GENERIC_PTR (redirection_data, i);
	      e2 = e->aux;

	      if (e2->dest == rd->outgoing_edge->dest)
		break;
	    }

	  /* If the loop did not terminate early, then we have a new
	     destination for the incoming threaded edges.  Record it.  */
	  if (i == VARRAY_ACTIVE_SIZE (redirection_data))
	    {
	      struct redirection_data *rd;

	      rd = ggc_alloc_cleared (sizeof (struct redirection_data));
	      rd->outgoing_edge = e->aux;
	      VARRAY_PUSH_GENERIC_PTR (redirection_data, rd);
	    }
	}
    }

  /* Now create duplicates of BB.  Note that if all incoming edges are
     threaded, then BB is going to become unreachable.  In that case
     we use BB for one of the duplicates rather than wasting memory
     duplicating BB.  Thus the odd starting condition for the loop.

     Note that for a block with a high outgoing degree we can waste
     a lot of time and memory creating and destroying useless edges.

     So we first duplicate BB and remove the control structure at the
     tail of the duplicate as well as all outgoing edges from the
     duplicate.  We then use that duplicate block as a template for
     the rest of the duplicates.  */
  template_block = NULL;
  for (i = (all ? 1 : 0); i < VARRAY_ACTIVE_SIZE (redirection_data); i++)
    {
      struct redirection_data *rd = VARRAY_GENERIC_PTR (redirection_data, i);

      if (template_block == NULL)
	{
	  create_block_for_threading (bb, rd);
	  template_block = rd->dup_block;
	}
      else
	{
	  create_block_for_threading (template_block, rd);
	}
    }

  /* Now created up edges from the duplicate blocks to their new
     destinations.  Doing this as a separate loop after block creation
     allows us to avoid creating lots of useless edges.  */
  for (i = (all ? 1 : 0); i < VARRAY_ACTIVE_SIZE (redirection_data); i++)
    {
      struct redirection_data *rd = VARRAY_GENERIC_PTR (redirection_data, i);
      tree phi;
      edge e;

      e = make_edge (rd->dup_block, rd->outgoing_edge->dest, EDGE_FALLTHRU);

      /* If there are any PHI nodes at the destination of the outgoing edge
	 from the duplicate block, then we will need to add a new argument
	 to them.  The argument should have the same value as the argument
	 associated with the outgoing edge stored in RD.  */
      for (phi = phi_nodes (e->dest); phi; phi = PHI_CHAIN (phi))
	{
	  int indx = phi_arg_from_edge (phi, rd->outgoing_edge);
	  add_phi_arg (&phi, PHI_ARG_DEF_TREE (phi, indx), e);
	}
    }

  /* The loop above created the duplicate blocks (and the statements
     within the duplicate blocks).  This loop creates PHI nodes for the
     duplicated blocks and redirects the incoming edges into BB to reach
     the duplicates of BB.

     Note that redirecting the edge will change e->pred_next, so we have
     to hold e->pred_next in a temporary. 

     If this turns out to be a performance problem, then we could create
     a list of incoming edges associated with each entry in 
     REDIRECTION_DATA and walk over that list of edges instead.  */
  for (ei = ei_start (bb->preds); (e = ei_safe_edge (ei)); )
    {
      edge new_dest = e->aux;

      /* E was not threaded, then there is nothing to do.  */
      if (!new_dest)
	{
	  ei_next (&ei);
	  continue;
	}

      /* Go ahead and clear E->aux.  It's not needed anymore and failure
         to clear it will cause all kinds of unpleasant problems later.  */
      e->aux = NULL;

      /* We know E is an edge we want to thread.  Find the entry associated
         with E's new destination in the REDIRECTION_DATA array.  */
      for (i = 0; i < VARRAY_ACTIVE_SIZE (redirection_data); i++)
	{
	  struct redirection_data *rd;

	  rd = VARRAY_GENERIC_PTR (redirection_data, i);

	  /* We have found the right entry if the outgoing edge in this
	     entry matches E's new destination.  Note that if we have not
	     created a duplicate block (rd->dup_block is NULL), then we
	     are going to re-use BB as a duplicate and we do not need
	     to create PHI nodes or redirect the edge.  */
	  if (rd->outgoing_edge == new_dest && rd->dup_block)
	    {
	      edge e2;

	      if (dump_file && (dump_flags & TDF_DETAILS))
		fprintf (dump_file, "  Threaded jump %d --> %d to %d\n",
			 e->src->index, e->dest->index, rd->dup_block->index);

	      e2 = redirect_edge_and_branch (e, rd->dup_block);
	      flush_pending_stmts (e2);

	      if ((dump_file && (dump_flags & TDF_DETAILS))
		  && e->src != e2->src)
	      fprintf (dump_file, "    basic block %d created\n",
		       e2->src->index);
	      break;
	    }
	}
    }

  /* If all the incoming edges where threaded, then we used BB as one
     of the duplicate blocks.  We need to fixup BB in that case so that
     it no longer has a COND_EXPR or SWITCH_EXPR and reaches one destination
     unconditionally.  */
  if (all)
    {
      struct redirection_data *rd;

      rd = VARRAY_GENERIC_PTR (redirection_data, 0);

      if (dump_file && (dump_flags & TDF_DETAILS))
	fprintf (dump_file, "  Threaded jump %d --> %d to %d\n",
		 EDGE_PRED (bb, 0)->src->index, bb->index,
		 EDGE_SUCC (bb, 0)->dest->index);

      remove_ctrl_stmt_and_useless_edges (bb, rd->outgoing_edge->dest);
      EDGE_SUCC (bb, 0)->flags &= ~(EDGE_TRUE_VALUE | EDGE_FALSE_VALUE);
      EDGE_SUCC (bb, 0)->flags |= EDGE_FALLTHRU;
    }

  /* Done with this block.  Clear REDIRECTION_DATA.  */
  VARRAY_CLEAR (redirection_data);
}

/* Walk through all blocks and thread incoming edges to the block's 
   destinations as requested.  This is the only entry point into this
   file.

   Blocks which have one or more incoming edges have INCOMING_EDGE_THREADED
   set in the block's annotation.
   this routine.

   Each edge that should be threaded has the new destination edge stored in
   the original edge's AUX field.

   This routine (or one of its callees) will clear INCOMING_EDGE_THREADED
   in the block annotations and the AUX field in the edges.

   It is the caller's responsibility to fix the dominance information
   and rewrite duplicated SSA_NAMEs back into SSA form.

   Returns true if one or more edges were threaded, false otherwise.  */

bool
thread_through_all_blocks (void)
{
  basic_block bb;
  bool retval = false;

  FOR_EACH_BB (bb)
    {
      if (bb_ann (bb)->incoming_edge_threaded)
	{
	  thread_block (bb);
	  retval = true;
	  bb_ann (bb)->incoming_edge_threaded = false;
	}
    }
  return retval;
}