1 /* Strongly-connected copy propagation pass for the GNU compiler. 2 Copyright (C) 2023-2024 Free Software Foundation, Inc. 3 Contributed by Filip Kastl <fkastl (at) suse.cz> 4 5 This file is part of GCC. 6 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. 11 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. 16 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/>. */ 20 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" 41 42 /* Strongly connected copy propagation pass. 43 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: 47 48 1 An assignment statement with a single argument. 49 50 _3 = _2; 51 _4 = 5; 52 53 2 A degenerate PHI statement. A degenerate PHI is a PHI that only refers to 54 itself or one other value. 55 56 _5 = PHI <_1>; 57 _6 = PHI <_6, _6, _1, _1>; 58 _7 = PHI <16, _7>; 59 60 3 A set of PHI statements that only refer to each other or to one other 61 value. 62 63 _8 = PHI <_9, _10>; 64 _9 = PHI <_8, _10>; 65 _10 = PHI <_8, _9, _1>; 66 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. 71 72 _3 = _2; // Replace all occurences of _3 by _2 73 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 77 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. 81 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. 88 89 References: 90 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. */ 94 95 /* Bitmap tracking statements which were propagated to be removed at the end of 96 the pass. */ 97 98 namespace { 99 static bitmap dead_stmts; 100 101 /* State of vertex during SCC discovery. 102 103 unvisited Vertex hasn't yet been popped from worklist. 104 vopen DFS has visited vertex for the first time. Vertex has been put 105 on Tarjan stack. 106 closed DFS has backtracked through vertex. At this point, vertex 107 doesn't have any unvisited neighbors. 108 in_scc Vertex has been popped from Tarjan stack. */ 109 110 enum vstate 111 { 112 unvisited, 113 vopen, 114 closed, 115 in_scc 116 }; 117 118 /* Information about a vertex. Used by SCC discovery. */ 119 120 struct vertex 121 { 122 bool active; /* scc_discovery::compute_sccs () only considers a subgraph of 123 the whole dataflow graph. It uses this flag so that it knows 124 which vertices are part of this subgraph. */ 125 vstate state; 126 unsigned index; 127 unsigned lowlink; 128 }; 129 130 /* SCC discovery. 131 132 Used to find SCCs in a dataflow graph. Implements Tarjan's SCC 133 algorithm. */ 134 135 class scc_discovery 136 { 137 public: 138 scc_discovery (); 139 ~scc_discovery (); 140 auto_vec<vec<gimple *>> compute_sccs (vec<gimple *> &stmts); 141 142 private: 143 vertex* vertices; /* Indexed by SSA_NAME_VERSION. */ 144 auto_vec<unsigned> worklist; /* DFS stack. */ 145 auto_vec<unsigned> stack; /* Tarjan stack. */ 146 147 void visit_neighbor (tree neigh_tree, unsigned parent_vxnum); 148 }; 149 150 scc_discovery::scc_discovery () 151 { 152 /* Create vertex struct for each SSA name. */ 153 vertices = XNEWVEC (struct vertex, num_ssa_names); 154 unsigned i = 0; 155 for (i = 0; i < num_ssa_names; i++) 156 vertices[i].active = false; 157 } 158 159 scc_discovery::~scc_discovery () 160 { 161 XDELETEVEC (vertices); 162 } 163 164 /* Part of 'scc_discovery::compute_sccs ()'. */ 165 166 void 167 scc_discovery::visit_neighbor (tree neigh_tree, unsigned parent_version) 168 { 169 if (TREE_CODE (neigh_tree) != SSA_NAME) 170 return; /* Skip any neighbor that isn't an SSA name. */ 171 unsigned neigh_version = SSA_NAME_VERSION (neigh_tree); 172 173 /* Skip neighbors outside the subgraph that Tarjan currently works 174 with. */ 175 if (!vertices[neigh_version].active) 176 return; 177 178 vstate neigh_state = vertices[neigh_version].state; 179 vstate parent_state = vertices[parent_version].state; 180 if (parent_state == vopen) /* We're currently opening parent. */ 181 { 182 /* Put unvisited neighbors on worklist. Update lowlink of parent 183 vertex according to indices of neighbors present on stack. */ 184 switch (neigh_state) 185 { 186 case unvisited: 187 worklist.safe_push (neigh_version); 188 break; 189 case vopen: 190 case closed: 191 vertices[parent_version].lowlink 192 = std::min (vertices[parent_version].lowlink, 193 vertices[neigh_version].index); 194 break; 195 case in_scc: 196 /* Ignore these edges. */ 197 break; 198 } 199 } 200 else if (parent_state == closed) /* We're currently closing parent. */ 201 { 202 /* Update lowlink of parent vertex according to lowlinks of 203 children of parent (in terms of DFS tree). */ 204 if (neigh_state == closed) 205 { 206 vertices[parent_version].lowlink 207 = std::min (vertices[parent_version].lowlink, 208 vertices[neigh_version].lowlink); 209 } 210 } 211 } 212 213 /* Compute SCCs in dataflow graph on given statements 'stmts'. Ignore 214 statements outside 'stmts'. Return the SCCs in a reverse topological 215 order. 216 217 stmt_may_generate_copy () must be true for all statements from 'stmts'! */ 218 219 auto_vec<vec<gimple *>> 220 scc_discovery::compute_sccs (vec<gimple *> &stmts) 221 { 222 auto_vec<vec<gimple *>> sccs; 223 224 for (gimple *stmt : stmts) 225 { 226 unsigned i; 227 switch (gimple_code (stmt)) 228 { 229 case GIMPLE_ASSIGN: 230 i = SSA_NAME_VERSION (gimple_assign_lhs (stmt)); 231 break; 232 case GIMPLE_PHI: 233 i = SSA_NAME_VERSION (gimple_phi_result (stmt)); 234 break; 235 default: 236 gcc_unreachable (); 237 } 238 239 vertices[i].index = 0; 240 vertices[i].lowlink = 0; 241 vertices[i].state = unvisited; 242 vertices[i].active = true; /* Mark the subgraph we'll be working on so 243 that we don't leave it. */ 244 245 worklist.safe_push (i); 246 } 247 248 /* Worklist loop. */ 249 unsigned curr_index = 0; 250 while (!worklist.is_empty ()) 251 { 252 unsigned i = worklist.pop (); 253 gimple *stmt = SSA_NAME_DEF_STMT (ssa_name (i)); 254 vstate state = vertices[i].state; 255 256 if (state == unvisited) 257 { 258 vertices[i].state = vopen; 259 260 /* Assign index to this vertex. */ 261 vertices[i].index = curr_index; 262 vertices[i].lowlink = curr_index; 263 curr_index++; 264 265 /* Put vertex on stack and also on worklist to be closed later. */ 266 stack.safe_push (i); 267 worklist.safe_push (i); 268 } 269 else if (state == vopen) 270 vertices[i].state = closed; 271 272 /* Visit neighbors of this vertex. */ 273 tree op; 274 gphi *phi; 275 switch (gimple_code (stmt)) 276 { 277 case GIMPLE_PHI: 278 phi = as_a <gphi *> (stmt); 279 unsigned j; 280 for (j = 0; j < gimple_phi_num_args (phi); j++) 281 { 282 op = gimple_phi_arg_def (phi, j); 283 visit_neighbor (op, i); 284 } 285 break; 286 case GIMPLE_ASSIGN: 287 op = gimple_assign_rhs1 (stmt); 288 visit_neighbor (op, i); 289 break; 290 default: 291 gcc_unreachable (); 292 } 293 294 /* If we've just closed a root vertex of an scc, pop scc from stack. */ 295 if (state == vopen && vertices[i].lowlink == vertices[i].index) 296 { 297 vec<gimple *> scc = vNULL; 298 299 unsigned j; 300 do 301 { 302 j = stack.pop (); 303 scc.safe_push (SSA_NAME_DEF_STMT (ssa_name (j))); 304 vertices[j].state = in_scc; 305 } 306 while (j != i); 307 308 sccs.safe_push (scc); 309 } 310 } 311 312 if (!stack.is_empty ()) 313 gcc_unreachable (); 314 315 /* Clear 'active' flags. */ 316 for (gimple *stmt : stmts) 317 { 318 unsigned i; 319 switch (gimple_code (stmt)) 320 { 321 case GIMPLE_ASSIGN: 322 i = SSA_NAME_VERSION (gimple_assign_lhs (stmt)); 323 break; 324 case GIMPLE_PHI: 325 i = SSA_NAME_VERSION (gimple_phi_result (stmt)); 326 break; 327 default: 328 gcc_unreachable (); 329 } 330 331 vertices[i].active = false; 332 } 333 334 return sccs; 335 } 336 337 } // anon namespace 338 339 /* Could this statement potentially be a copy statement? 340 341 This pass only considers statements for which this function returns 'true'. 342 Those are basically PHI functions and assignment statements similar to 343 344 _2 = _1; 345 or 346 _2 = 5; */ 347 348 static bool 349 stmt_may_generate_copy (gimple *stmt) 350 { 351 /* A PHI may generate a copy. */ 352 if (gimple_code (stmt) == GIMPLE_PHI) 353 { 354 gphi *phi = as_a <gphi *> (stmt); 355 356 /* No OCCURS_IN_ABNORMAL_PHI SSA names in lhs nor rhs. */ 357 if (SSA_NAME_OCCURS_IN_ABNORMAL_PHI (gimple_phi_result (phi))) 358 return false; 359 360 unsigned i; 361 for (i = 0; i < gimple_phi_num_args (phi); i++) 362 { 363 tree op = gimple_phi_arg_def (phi, i); 364 if (TREE_CODE (op) == SSA_NAME 365 && SSA_NAME_OCCURS_IN_ABNORMAL_PHI (op)) 366 return false; 367 } 368 369 /* If PHI has more than one unique non-SSA arguments, it won't generate a 370 copy. */ 371 tree const_op = NULL_TREE; 372 for (i = 0; i < gimple_phi_num_args (phi); i++) 373 { 374 tree op = gimple_phi_arg_def (phi, i); 375 if (TREE_CODE (op) != SSA_NAME) 376 { 377 if (const_op && !operand_equal_p (op, const_op)) 378 return false; 379 const_op = op; 380 } 381 } 382 383 return true; 384 } 385 386 /* Or a statement of type _2 = _1; OR _2 = 5; may generate a copy. */ 387 388 if (!gimple_assign_single_p (stmt)) 389 return false; 390 391 tree lhs = gimple_assign_lhs (stmt); 392 tree rhs = gimple_assign_rhs1 (stmt); 393 394 if (TREE_CODE (lhs) != SSA_NAME) 395 return false; 396 397 /* lhs shouldn't flow through any abnormal edges. */ 398 if (SSA_NAME_OCCURS_IN_ABNORMAL_PHI (lhs)) 399 return false; 400 401 if (is_gimple_min_invariant (rhs)) 402 return true; /* A statement of type _2 = 5;. */ 403 404 if (TREE_CODE (rhs) != SSA_NAME) 405 return false; 406 407 /* rhs shouldn't flow through any abnormal edges. */ 408 if (SSA_NAME_OCCURS_IN_ABNORMAL_PHI (rhs)) 409 return false; 410 411 /* It is possible that lhs has more alignment or value range information. By 412 propagating we would lose this information. So in the case that alignment 413 or value range information differs, we are conservative and do not 414 propagate. 415 416 FIXME: Propagate alignment and value range info the same way copy-prop 417 does. */ 418 if (POINTER_TYPE_P (TREE_TYPE (lhs)) 419 && POINTER_TYPE_P (TREE_TYPE (rhs)) 420 && SSA_NAME_PTR_INFO (lhs) != SSA_NAME_PTR_INFO (rhs)) 421 return false; 422 if (!POINTER_TYPE_P (TREE_TYPE (lhs)) 423 && !POINTER_TYPE_P (TREE_TYPE (rhs)) 424 && SSA_NAME_RANGE_INFO (lhs) != SSA_NAME_RANGE_INFO (rhs)) 425 return false; 426 427 return true; /* A statement of type _2 = _1;. */ 428 } 429 430 /* Return all statements in cfun that could generate copies. All statements 431 for which stmt_may_generate_copy returns 'true'. */ 432 433 static auto_vec<gimple *> 434 get_all_stmt_may_generate_copy (void) 435 { 436 auto_vec<gimple *> result; 437 438 basic_block bb; 439 FOR_EACH_BB_FN (bb, cfun) 440 { 441 gimple_stmt_iterator gsi; 442 for (gsi = gsi_start_bb (bb); !gsi_end_p (gsi); gsi_next (&gsi)) 443 { 444 gimple *s = gsi_stmt (gsi); 445 if (stmt_may_generate_copy (s)) 446 result.safe_push (s); 447 } 448 449 gphi_iterator pi; 450 for (pi = gsi_start_phis (bb); !gsi_end_p (pi); gsi_next (&pi)) 451 { 452 gimple *s = pi.phi (); 453 if (stmt_may_generate_copy (s)) 454 result.safe_push (s); 455 } 456 } 457 458 return result; 459 } 460 461 /* For each statement from given SCC, replace its usages by value 462 VAL. */ 463 464 static void 465 replace_scc_by_value (vec<gimple *> scc, tree val) 466 { 467 for (gimple *stmt : scc) 468 { 469 tree name = gimple_get_lhs (stmt); 470 replace_uses_by (name, val); 471 bitmap_set_bit (dead_stmts, SSA_NAME_VERSION (name)); 472 } 473 474 if (dump_file) 475 fprintf (dump_file, "Replacing SCC of size %d\n", scc.length ()); 476 } 477 478 /* Part of 'sccopy_propagate ()'. */ 479 480 static void 481 sccopy_visit_op (tree op, hash_set<tree> &outer_ops, 482 hash_set<gimple *> &scc_set, bool &is_inner, 483 tree &last_outer_op) 484 { 485 bool op_in_scc = false; 486 487 if (TREE_CODE (op) == SSA_NAME) 488 { 489 gimple *op_stmt = SSA_NAME_DEF_STMT (op); 490 if (scc_set.contains (op_stmt)) 491 op_in_scc = true; 492 } 493 494 if (!op_in_scc) 495 { 496 outer_ops.add (op); 497 last_outer_op = op; 498 is_inner = false; 499 } 500 } 501 502 /* Main function of this pass. Find and propagate all three types of copy 503 statements (see pass description above). 504 505 This is an implementation of an algorithm from the paper Simple and 506 Efficient Construction of Static Single Assignmemnt Form[1]. It is based 507 on strongly-connected components (SCCs) in dataflow graph. The original 508 algorithm only considers PHI statements. We extend it to also consider 509 assignment statements of type _2 = _1;. 510 511 The algorithm is based on this definition of a set of redundant PHIs[1]: 512 513 A non-empty set P of PHI functions is redundant iff the PHI functions just 514 reference each other or one other value 515 516 It uses this lemma[1]: 517 518 Let P be a redundant set of PHI functions. Then there is a 519 strongly-connected component S subset of P that is also redundant. 520 521 The algorithm works in this way: 522 523 1 Find SCCs 524 2 For each SCC S in topological order: 525 3 Construct set 'inner' of statements that only have other statements 526 from S on their right hand side 527 4 Construct set 'outer' of values that originate outside S and appear on 528 right hand side of some statement from S 529 5 If |outer| = 1, outer only contains a value v. Statements in S only 530 refer to each other or to v -- they are redundant. Propagate v. 531 Else, recurse on statements in inner. 532 533 The implementation is non-recursive. 534 535 References: 536 537 [1] Simple and Efficient Construction of Static Single Assignmemnt Form, 538 Braun, Buchwald, Hack, Leissa, Mallon, Zwinkau, 2013, LNCS vol. 7791, 539 Section 3.2. */ 540 541 static void 542 sccopy_propagate () 543 { 544 auto_vec<gimple *> useful_stmts = get_all_stmt_may_generate_copy (); 545 scc_discovery discovery; 546 547 auto_vec<vec<gimple *>> worklist = discovery.compute_sccs (useful_stmts); 548 549 while (!worklist.is_empty ()) 550 { 551 vec<gimple *> scc = worklist.pop (); 552 553 /* When we do 'replace_scc_by_value' it may happen that some EH edges 554 get removed. That means parts of CFG get removed. Those may 555 contain copy statements. For that reason we prune SCCs here. */ 556 unsigned i; 557 for (i = 0; i < scc.length ();) 558 if (gimple_bb (scc[i]) == NULL) 559 scc.unordered_remove (i); 560 else 561 i++; 562 if (scc.is_empty ()) 563 { 564 scc.release (); 565 continue; 566 } 567 568 auto_vec<gimple *> inner; 569 hash_set<tree> outer_ops; 570 tree last_outer_op = NULL_TREE; 571 572 /* Prepare hash set of PHIs in scc to query later. */ 573 hash_set<gimple *> scc_set; 574 for (gimple *stmt : scc) 575 scc_set.add (stmt); 576 577 for (gimple *stmt : scc) 578 { 579 bool is_inner = true; 580 581 gphi *phi; 582 tree op; 583 584 switch (gimple_code (stmt)) 585 { 586 case GIMPLE_PHI: 587 phi = as_a <gphi *> (stmt); 588 unsigned j; 589 for (j = 0; j < gimple_phi_num_args (phi); j++) 590 { 591 op = gimple_phi_arg_def (phi, j); 592 sccopy_visit_op (op, outer_ops, scc_set, is_inner, 593 last_outer_op); 594 } 595 break; 596 case GIMPLE_ASSIGN: 597 op = gimple_assign_rhs1 (stmt); 598 sccopy_visit_op (op, outer_ops, scc_set, is_inner, 599 last_outer_op); 600 break; 601 default: 602 gcc_unreachable (); 603 } 604 605 if (is_inner) 606 inner.safe_push (stmt); 607 } 608 609 if (outer_ops.elements () == 1) 610 { 611 /* The only operand in outer_ops. */ 612 tree outer_op = last_outer_op; 613 replace_scc_by_value (scc, outer_op); 614 } 615 else if (outer_ops.elements () > 1) 616 { 617 /* Add inner sccs to worklist. */ 618 auto_vec<vec<gimple *>> inner_sccs 619 = discovery.compute_sccs (inner); 620 for (vec<gimple *> inner_scc : inner_sccs) 621 worklist.safe_push (inner_scc); 622 } 623 else 624 gcc_unreachable (); 625 626 scc.release (); 627 } 628 } 629 630 /* Called when pass execution starts. */ 631 632 static void 633 init_sccopy (void) 634 { 635 /* For propagated statements. */ 636 dead_stmts = BITMAP_ALLOC (NULL); 637 } 638 639 /* Called before pass execution ends. */ 640 641 static void 642 finalize_sccopy (void) 643 { 644 /* Remove all propagated statements. */ 645 simple_dce_from_worklist (dead_stmts); 646 BITMAP_FREE (dead_stmts); 647 648 /* Propagating a constant may create dead eh edges. */ 649 basic_block bb; 650 FOR_EACH_BB_FN (bb, cfun) 651 gimple_purge_dead_eh_edges (bb); 652 } 653 654 namespace { 655 656 const pass_data pass_data_sccopy = 657 { 658 GIMPLE_PASS, /* type */ 659 "sccopy", /* name */ 660 OPTGROUP_NONE, /* optinfo_flags */ 661 TV_NONE, /* tv_id */ 662 ( PROP_cfg | PROP_ssa ), /* properties_required */ 663 0, /* properties_provided */ 664 0, /* properties_destroyed */ 665 0, /* todo_flags_start */ 666 TODO_update_ssa | TODO_cleanup_cfg, /* todo_flags_finish */ 667 }; 668 669 class pass_sccopy : public gimple_opt_pass 670 { 671 public: 672 pass_sccopy (gcc::context *ctxt) 673 : gimple_opt_pass (pass_data_sccopy, ctxt) 674 {} 675 676 /* opt_pass methods: */ 677 virtual bool gate (function *) { return true; } 678 virtual unsigned int execute (function *); 679 opt_pass * clone () final override { return new pass_sccopy (m_ctxt); } 680 }; // class pass_sccopy 681 682 unsigned 683 pass_sccopy::execute (function *) 684 { 685 init_sccopy (); 686 sccopy_propagate (); 687 finalize_sccopy (); 688 return 0; 689 } 690 691 } // anon namespace 692 693 gimple_opt_pass * 694 make_pass_sccopy (gcc::context *ctxt) 695 { 696 return new pass_sccopy (ctxt); 697 } 698