[PATCH] RISC-V: Move UNSPEC_SSP_SET and UNSPEC_SSP_TEST to correct enum
[gcc.git] / gcc / gimple-ssa-sccopy.cc
blob9f25fbaff365904a62b76aafbd26e01aabba2c4a
1 /* Strongly-connected copy propagation pass for the GNU compiler.
2 Copyright (C) 2023-2025 Free Software Foundation, Inc.
3 Contributed by Filip Kastl <fkastl@suse.cz>
5 This file is part of GCC.
7 GCC is free software; you can redistribute it and/or modify it under
8 the terms of the GNU General Public License as published by the Free
9 Software Foundation; either version 3, or (at your option) any later
10 version.
12 GCC is distributed in the hope that it will be useful, but WITHOUT ANY
13 WARRANTY; without even the implied warranty of MERCHANTABILITY or
14 FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
15 for more details.
17 You should have received a copy of the GNU General Public License
18 along with GCC; see the file COPYING3. If not see
19 <http://www.gnu.org/licenses/>. */
21 #define INCLUDE_ALGORITHM
22 #include "config.h"
23 #include "system.h"
24 #include "coretypes.h"
25 #include "backend.h"
26 #include "tree.h"
27 #include "gimple.h"
28 #include "tree-pass.h"
29 #include "ssa.h"
30 #include "gimple-iterator.h"
31 #include "vec.h"
32 #include "hash-set.h"
33 #include "ssa-iterators.h"
34 #include "gimple-fold.h"
35 #include "gimplify.h"
36 #include "tree-cfg.h"
37 #include "tree-eh.h"
38 #include "builtins.h"
39 #include "tree-ssa-dce.h"
40 #include "fold-const.h"
42 /* Strongly connected copy propagation pass.
44 This is a lightweight copy propagation pass that is also able to eliminate
45 redundant PHI statements. The pass considers the following types of copy
46 statements:
48 1 An assignment statement with a single argument.
50 _3 = _2;
51 _4 = 5;
53 2 A degenerate PHI statement. A degenerate PHI is a PHI that only refers to
54 itself or one other value.
56 _5 = PHI <_1>;
57 _6 = PHI <_6, _6, _1, _1>;
58 _7 = PHI <16, _7>;
60 3 A set of PHI statements that only refer to each other or to one other
61 value.
63 _8 = PHI <_9, _10>;
64 _9 = PHI <_8, _10>;
65 _10 = PHI <_8, _9, _1>;
67 All of these statements produce copies and can be eliminated from the
68 program. For a copy statement we identify the value it creates a copy of
69 and replace references to the statement with the value -- we propagate the
70 copy.
72 _3 = _2; // Replace all occurences of _3 by _2
74 _8 = PHI <_9, _10>;
75 _9 = PHI <_8, _10>;
76 _10 = PHI <_8, _9, _1>; // Replace all occurences of _8, _9 and _10 by _1
78 To find all three types of copy statements we use an algorithm based on
79 strongly-connected components (SCCs) in dataflow graph. The algorithm was
80 introduced in an article from 2013[1]. We describe the algorithm bellow.
82 To identify SCCs we implement the Robert Tarjan's SCC algorithm. For the
83 SCC computation we wrap potential copy statements in the 'vertex' struct.
84 To each of these statements we also assign a vertex number ('vxnum'). Since
85 the main algorithm has to be able to compute SCCs of subgraphs of the whole
86 dataflow graph we use GIMPLE stmt flags to prevent Tarjan's algorithm from
87 leaving the subgraph.
89 References:
91 [1] Simple and Efficient Construction of Static Single Assignmemnt Form,
92 Braun, Buchwald, Hack, Leissa, Mallon, Zwinkau, 2013, LNCS vol. 7791,
93 Section 3.2. */
95 namespace {
97 /* State of vertex during SCC discovery.
99 unvisited Vertex hasn't yet been popped from worklist.
100 vopen DFS has visited vertex for the first time. Vertex has been put
101 on Tarjan stack.
102 closed DFS has backtracked through vertex. At this point, vertex
103 doesn't have any unvisited neighbors.
104 in_scc Vertex has been popped from Tarjan stack. */
106 enum vstate
108 unvisited,
109 vopen,
110 closed,
111 in_scc
114 /* Information about a vertex. Used by SCC discovery. */
116 struct vertex
118 bool active; /* scc_discovery::compute_sccs () only considers a subgraph of
119 the whole dataflow graph. It uses this flag so that it knows
120 which vertices are part of this subgraph. */
121 vstate state;
122 unsigned index;
123 unsigned lowlink;
126 /* SCC discovery.
128 Used to find SCCs in a dataflow graph. Implements Tarjan's SCC
129 algorithm. */
131 class scc_discovery
133 public:
134 scc_discovery ();
135 ~scc_discovery ();
136 auto_vec<vec<gimple *>> compute_sccs (vec<gimple *> &stmts);
138 private:
139 vertex* vertices; /* Indexed by SSA_NAME_VERSION. */
140 auto_vec<unsigned> worklist; /* DFS stack. */
141 auto_vec<unsigned> stack; /* Tarjan stack. */
143 void visit_neighbor (tree neigh_tree, unsigned parent_vxnum);
146 scc_discovery::scc_discovery ()
148 /* Create vertex struct for each SSA name. */
149 vertices = XNEWVEC (struct vertex, num_ssa_names);
150 unsigned i = 0;
151 for (i = 0; i < num_ssa_names; i++)
152 vertices[i].active = false;
155 scc_discovery::~scc_discovery ()
157 XDELETEVEC (vertices);
160 /* Part of 'scc_discovery::compute_sccs ()'. */
162 void
163 scc_discovery::visit_neighbor (tree neigh_tree, unsigned parent_version)
165 if (TREE_CODE (neigh_tree) != SSA_NAME)
166 return; /* Skip any neighbor that isn't an SSA name. */
167 unsigned neigh_version = SSA_NAME_VERSION (neigh_tree);
169 /* Skip neighbors outside the subgraph that Tarjan currently works
170 with. */
171 if (!vertices[neigh_version].active)
172 return;
174 vstate neigh_state = vertices[neigh_version].state;
175 vstate parent_state = vertices[parent_version].state;
176 if (parent_state == vopen) /* We're currently opening parent. */
178 /* Put unvisited neighbors on worklist. Update lowlink of parent
179 vertex according to indices of neighbors present on stack. */
180 switch (neigh_state)
182 case unvisited:
183 worklist.safe_push (neigh_version);
184 break;
185 case vopen:
186 case closed:
187 vertices[parent_version].lowlink
188 = std::min (vertices[parent_version].lowlink,
189 vertices[neigh_version].index);
190 break;
191 case in_scc:
192 /* Ignore these edges. */
193 break;
196 else if (parent_state == closed) /* We're currently closing parent. */
198 /* Update lowlink of parent vertex according to lowlinks of
199 children of parent (in terms of DFS tree). */
200 if (neigh_state == closed)
202 vertices[parent_version].lowlink
203 = std::min (vertices[parent_version].lowlink,
204 vertices[neigh_version].lowlink);
209 /* Compute SCCs in dataflow graph on given statements 'stmts'. Ignore
210 statements outside 'stmts'. Return the SCCs in a reverse topological
211 order.
213 stmt_may_generate_copy () must be true for all statements from 'stmts'! */
215 auto_vec<vec<gimple *>>
216 scc_discovery::compute_sccs (vec<gimple *> &stmts)
218 auto_vec<vec<gimple *>> sccs;
220 for (gimple *stmt : stmts)
222 unsigned i;
223 switch (gimple_code (stmt))
225 case GIMPLE_ASSIGN:
226 i = SSA_NAME_VERSION (gimple_assign_lhs (stmt));
227 break;
228 case GIMPLE_PHI:
229 i = SSA_NAME_VERSION (gimple_phi_result (stmt));
230 break;
231 default:
232 gcc_unreachable ();
235 vertices[i].index = 0;
236 vertices[i].lowlink = 0;
237 vertices[i].state = unvisited;
238 vertices[i].active = true; /* Mark the subgraph we'll be working on so
239 that we don't leave it. */
241 worklist.safe_push (i);
244 /* Worklist loop. */
245 unsigned curr_index = 0;
246 while (!worklist.is_empty ())
248 unsigned i = worklist.pop ();
249 gimple *stmt = SSA_NAME_DEF_STMT (ssa_name (i));
250 vstate state = vertices[i].state;
252 if (state == unvisited)
254 vertices[i].state = vopen;
256 /* Assign index to this vertex. */
257 vertices[i].index = curr_index;
258 vertices[i].lowlink = curr_index;
259 curr_index++;
261 /* Put vertex on stack and also on worklist to be closed later. */
262 stack.safe_push (i);
263 worklist.safe_push (i);
265 else if (state == vopen)
266 vertices[i].state = closed;
268 /* Visit neighbors of this vertex. */
269 tree op;
270 gphi *phi;
271 switch (gimple_code (stmt))
273 case GIMPLE_PHI:
274 phi = as_a <gphi *> (stmt);
275 unsigned j;
276 for (j = 0; j < gimple_phi_num_args (phi); j++)
278 op = gimple_phi_arg_def (phi, j);
279 visit_neighbor (op, i);
281 break;
282 case GIMPLE_ASSIGN:
283 op = gimple_assign_rhs1 (stmt);
284 visit_neighbor (op, i);
285 break;
286 default:
287 gcc_unreachable ();
290 /* If we've just closed a root vertex of an scc, pop scc from stack. */
291 if (state == vopen && vertices[i].lowlink == vertices[i].index)
293 vec<gimple *> scc = vNULL;
295 unsigned j;
298 j = stack.pop ();
299 scc.safe_push (SSA_NAME_DEF_STMT (ssa_name (j)));
300 vertices[j].state = in_scc;
302 while (j != i);
304 sccs.safe_push (scc);
308 if (!stack.is_empty ())
309 gcc_unreachable ();
311 /* Clear 'active' flags. */
312 for (gimple *stmt : stmts)
314 unsigned i;
315 switch (gimple_code (stmt))
317 case GIMPLE_ASSIGN:
318 i = SSA_NAME_VERSION (gimple_assign_lhs (stmt));
319 break;
320 case GIMPLE_PHI:
321 i = SSA_NAME_VERSION (gimple_phi_result (stmt));
322 break;
323 default:
324 gcc_unreachable ();
327 vertices[i].active = false;
330 return sccs;
333 } // anon namespace
335 /* Could this statement potentially be a copy statement?
337 This pass only considers statements for which this function returns 'true'.
338 Those are basically PHI functions and assignment statements similar to
340 _2 = _1;
342 _2 = 5; */
344 static bool
345 stmt_may_generate_copy (gimple *stmt)
347 /* A PHI may generate a copy. */
348 if (gimple_code (stmt) == GIMPLE_PHI)
350 gphi *phi = as_a <gphi *> (stmt);
352 /* No OCCURS_IN_ABNORMAL_PHI SSA names in lhs nor rhs. */
353 if (SSA_NAME_OCCURS_IN_ABNORMAL_PHI (gimple_phi_result (phi)))
354 return false;
356 unsigned i;
357 for (i = 0; i < gimple_phi_num_args (phi); i++)
359 tree op = gimple_phi_arg_def (phi, i);
360 if (TREE_CODE (op) == SSA_NAME
361 && SSA_NAME_OCCURS_IN_ABNORMAL_PHI (op))
362 return false;
365 /* If PHI has more than one unique non-SSA arguments, it won't generate a
366 copy. */
367 tree const_op = NULL_TREE;
368 for (i = 0; i < gimple_phi_num_args (phi); i++)
370 tree op = gimple_phi_arg_def (phi, i);
371 if (TREE_CODE (op) != SSA_NAME)
373 if (const_op && !operand_equal_p (op, const_op))
374 return false;
375 const_op = op;
379 return true;
382 /* Or a statement of type _2 = _1; OR _2 = 5; may generate a copy. */
384 if (!gimple_assign_single_p (stmt))
385 return false;
387 tree lhs = gimple_assign_lhs (stmt);
388 tree rhs = gimple_assign_rhs1 (stmt);
390 if (TREE_CODE (lhs) != SSA_NAME)
391 return false;
393 /* lhs shouldn't flow through any abnormal edges. */
394 if (SSA_NAME_OCCURS_IN_ABNORMAL_PHI (lhs))
395 return false;
397 if (is_gimple_min_invariant (rhs))
398 return true; /* A statement of type _2 = 5;. */
400 if (TREE_CODE (rhs) != SSA_NAME)
401 return false;
403 /* rhs shouldn't flow through any abnormal edges. */
404 if (SSA_NAME_OCCURS_IN_ABNORMAL_PHI (rhs))
405 return false;
407 /* It is possible that lhs has more alignment or value range information. By
408 propagating we would lose this information. So in the case that alignment
409 or value range information differs, we are conservative and do not
410 propagate.
412 FIXME: Propagate alignment and value range info the same way copy-prop
413 does. */
414 if (POINTER_TYPE_P (TREE_TYPE (lhs))
415 && POINTER_TYPE_P (TREE_TYPE (rhs))
416 && SSA_NAME_PTR_INFO (lhs) != SSA_NAME_PTR_INFO (rhs))
417 return false;
418 if (!POINTER_TYPE_P (TREE_TYPE (lhs))
419 && !POINTER_TYPE_P (TREE_TYPE (rhs))
420 && SSA_NAME_RANGE_INFO (lhs) != SSA_NAME_RANGE_INFO (rhs))
421 return false;
423 return true; /* A statement of type _2 = _1;. */
426 /* Return all statements in cfun that could generate copies. All statements
427 for which stmt_may_generate_copy returns 'true'. */
429 static auto_vec<gimple *>
430 get_all_stmt_may_generate_copy (void)
432 auto_vec<gimple *> result;
434 basic_block bb;
435 FOR_EACH_BB_FN (bb, cfun)
437 gimple_stmt_iterator gsi;
438 for (gsi = gsi_start_bb (bb); !gsi_end_p (gsi); gsi_next (&gsi))
440 gimple *s = gsi_stmt (gsi);
441 if (stmt_may_generate_copy (s))
442 result.safe_push (s);
445 gphi_iterator pi;
446 for (pi = gsi_start_phis (bb); !gsi_end_p (pi); gsi_next (&pi))
448 gimple *s = pi.phi ();
449 if (stmt_may_generate_copy (s))
450 result.safe_push (s);
454 return result;
457 /* SCC copy propagation
459 'scc_copy_prop::propagate ()' is the main function of this pass. */
461 class scc_copy_prop
463 public:
464 scc_copy_prop ();
465 ~scc_copy_prop ();
466 void propagate ();
468 private:
469 /* Bitmap tracking statements which were propagated so that they can be
470 removed at the end of the pass. */
471 bitmap dead_stmts;
473 void visit_op (tree op, hash_set<tree> &outer_ops,
474 hash_set<gimple *> &scc_set, bool &is_inner,
475 tree &last_outer_op);
476 void replace_scc_by_value (vec<gimple *> scc, tree val);
479 /* For each statement from given SCC, replace its usages by value
480 VAL. */
482 void
483 scc_copy_prop::replace_scc_by_value (vec<gimple *> scc, tree val)
485 for (gimple *stmt : scc)
487 tree name = gimple_get_lhs (stmt);
488 replace_uses_by (name, val);
489 bitmap_set_bit (dead_stmts, SSA_NAME_VERSION (name));
492 if (dump_file)
493 fprintf (dump_file, "Replacing SCC of size %d\n", scc.length ());
496 /* Part of 'scc_copy_prop::propagate ()'. */
498 void
499 scc_copy_prop::visit_op (tree op, hash_set<tree> &outer_ops,
500 hash_set<gimple *> &scc_set, bool &is_inner,
501 tree &last_outer_op)
503 bool op_in_scc = false;
505 if (TREE_CODE (op) == SSA_NAME)
507 gimple *op_stmt = SSA_NAME_DEF_STMT (op);
508 if (scc_set.contains (op_stmt))
509 op_in_scc = true;
512 if (!op_in_scc)
514 outer_ops.add (op);
515 last_outer_op = op;
516 is_inner = false;
520 /* Main function of this pass. Find and propagate all three types of copy
521 statements (see pass description above).
523 This is an implementation of an algorithm from the paper Simple and
524 Efficient Construction of Static Single Assignmemnt Form[1]. It is based
525 on strongly-connected components (SCCs) in dataflow graph. The original
526 algorithm only considers PHI statements. We extend it to also consider
527 assignment statements of type _2 = _1;.
529 The algorithm is based on this definition of a set of redundant PHIs[1]:
531 A non-empty set P of PHI functions is redundant iff the PHI functions just
532 reference each other or one other value
534 It uses this lemma[1]:
536 Let P be a redundant set of PHI functions. Then there is a
537 strongly-connected component S subset of P that is also redundant.
539 The algorithm works in this way:
541 1 Find SCCs
542 2 For each SCC S in topological order:
543 3 Construct set 'inner' of statements that only have other statements
544 from S on their right hand side
545 4 Construct set 'outer' of values that originate outside S and appear on
546 right hand side of some statement from S
547 5 If |outer| = 1, outer only contains a value v. Statements in S only
548 refer to each other or to v -- they are redundant. Propagate v.
549 Else, recurse on statements in inner.
551 The implementation is non-recursive.
553 References:
555 [1] Simple and Efficient Construction of Static Single Assignmemnt Form,
556 Braun, Buchwald, Hack, Leissa, Mallon, Zwinkau, 2013, LNCS vol. 7791,
557 Section 3.2. */
559 void
560 scc_copy_prop::propagate ()
562 auto_vec<gimple *> useful_stmts = get_all_stmt_may_generate_copy ();
563 scc_discovery discovery;
565 auto_vec<vec<gimple *>> worklist = discovery.compute_sccs (useful_stmts);
567 while (!worklist.is_empty ())
569 vec<gimple *> scc = worklist.pop ();
571 auto_vec<gimple *> inner;
572 hash_set<tree> outer_ops;
573 tree last_outer_op = NULL_TREE;
575 /* Prepare hash set of PHIs in scc to query later. */
576 hash_set<gimple *> scc_set;
577 for (gimple *stmt : scc)
578 scc_set.add (stmt);
580 for (gimple *stmt : scc)
582 bool is_inner = true;
584 gphi *phi;
585 tree op;
587 switch (gimple_code (stmt))
589 case GIMPLE_PHI:
590 phi = as_a <gphi *> (stmt);
591 unsigned j;
592 for (j = 0; j < gimple_phi_num_args (phi); j++)
594 op = gimple_phi_arg_def (phi, j);
595 visit_op (op, outer_ops, scc_set, is_inner, last_outer_op);
597 break;
598 case GIMPLE_ASSIGN:
599 op = gimple_assign_rhs1 (stmt);
600 visit_op (op, outer_ops, scc_set, is_inner, last_outer_op);
601 break;
602 default:
603 gcc_unreachable ();
606 if (is_inner)
607 inner.safe_push (stmt);
610 if (outer_ops.elements () == 1)
612 /* The only operand in outer_ops. */
613 tree outer_op = last_outer_op;
614 replace_scc_by_value (scc, outer_op);
616 else if (outer_ops.elements () > 1)
618 /* Add inner sccs to worklist. */
619 auto_vec<vec<gimple *>> inner_sccs
620 = discovery.compute_sccs (inner);
621 for (vec<gimple *> inner_scc : inner_sccs)
622 worklist.safe_push (inner_scc);
624 else
625 gcc_unreachable ();
627 scc.release ();
631 scc_copy_prop::scc_copy_prop ()
633 /* For propagated statements. */
634 dead_stmts = BITMAP_ALLOC (NULL);
637 scc_copy_prop::~scc_copy_prop ()
639 /* Remove all propagated statements. */
640 simple_dce_from_worklist (dead_stmts);
641 BITMAP_FREE (dead_stmts);
643 /* Propagating a constant may create dead eh edges. */
644 basic_block bb;
645 FOR_EACH_BB_FN (bb, cfun)
646 gimple_purge_dead_eh_edges (bb);
649 namespace {
651 const pass_data pass_data_sccopy =
653 GIMPLE_PASS, /* type */
654 "sccopy", /* name */
655 OPTGROUP_NONE, /* optinfo_flags */
656 TV_NONE, /* tv_id */
657 ( PROP_cfg | PROP_ssa ), /* properties_required */
658 0, /* properties_provided */
659 0, /* properties_destroyed */
660 0, /* todo_flags_start */
661 TODO_update_ssa | TODO_cleanup_cfg, /* todo_flags_finish */
664 class pass_sccopy : public gimple_opt_pass
666 public:
667 pass_sccopy (gcc::context *ctxt)
668 : gimple_opt_pass (pass_data_sccopy, ctxt)
671 /* opt_pass methods: */
672 virtual bool gate (function *) { return true; }
673 virtual unsigned int execute (function *);
674 opt_pass * clone () final override { return new pass_sccopy (m_ctxt); }
675 }; // class pass_sccopy
677 unsigned
678 pass_sccopy::execute (function *)
680 scc_copy_prop sccopy;
681 sccopy.propagate ();
682 return 0;
685 } // anon namespace
687 gimple_opt_pass *
688 make_pass_sccopy (gcc::context *ctxt)
690 return new pass_sccopy (ctxt);