| /* SSA Jump Threading | 
 |    Copyright (C) 2005-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/>.  */ | 
 |  | 
 | #include "config.h" | 
 | #include "system.h" | 
 | #include "coretypes.h" | 
 | #include "backend.h" | 
 | #include "predict.h" | 
 | #include "tree.h" | 
 | #include "gimple.h" | 
 | #include "fold-const.h" | 
 | #include "cfgloop.h" | 
 | #include "gimple-iterator.h" | 
 | #include "tree-cfg.h" | 
 | #include "tree-ssa-threadupdate.h" | 
 | #include "tree-ssa-loop.h" | 
 | #include "cfganal.h" | 
 | #include "tree-pass.h" | 
 | #include "gimple-ssa.h" | 
 | #include "tree-phinodes.h" | 
 | #include "tree-inline.h" | 
 | #include "tree-vectorizer.h" | 
 | #include "value-range.h" | 
 | #include "gimple-range.h" | 
 | #include "tree-ssa-threadedge.h" | 
 | #include "gimple-range-path.h" | 
 | #include "ssa.h" | 
 | #include "tree-cfgcleanup.h" | 
 | #include "tree-pretty-print.h" | 
 | #include "cfghooks.h" | 
 | #include "dbgcnt.h" | 
 |  | 
 | // Path registry for the backwards threader.  After all paths have been | 
 | // registered with register_path(), thread_through_all_blocks() is called | 
 | // to modify the CFG. | 
 |  | 
 | class back_threader_registry : public back_jt_path_registry | 
 | { | 
 | public: | 
 |   bool register_path (const vec<basic_block> &, edge taken); | 
 | }; | 
 |  | 
 | // Class to abstract the profitability code for the backwards threader. | 
 |  | 
 | class back_threader_profitability | 
 | { | 
 | public: | 
 |   back_threader_profitability (bool speed_p, gimple *stmt); | 
 |   bool possibly_profitable_path_p (const vec<basic_block> &, bool *); | 
 |   bool profitable_path_p (const vec<basic_block> &, | 
 | 			  edge taken, bool *irreducible_loop); | 
 | private: | 
 |   const bool m_speed_p; | 
 |   int m_exit_jump_benefit; | 
 |   bool m_threaded_multiway_branch; | 
 |   // The following are computed by possibly_profitable_path_p | 
 |   bool m_threaded_through_latch; | 
 |   bool m_multiway_branch_in_path; | 
 |   bool m_contains_hot_bb; | 
 |   int m_n_insns; | 
 | }; | 
 |  | 
 | back_threader_profitability::back_threader_profitability (bool speed_p, | 
 | 							  gimple *last) | 
 |   : m_speed_p (speed_p) | 
 | { | 
 |   m_threaded_multiway_branch = (gimple_code (last) == GIMPLE_SWITCH | 
 | 				|| gimple_code (last) == GIMPLE_GOTO); | 
 |   // The forward threader has estimate_threading_killed_stmts, in | 
 |   // particular it estimates further DCE from eliminating the exit | 
 |   // control stmt. | 
 |   m_exit_jump_benefit = estimate_num_insns (last, &eni_size_weights); | 
 | } | 
 |  | 
 | // Back threader flags. | 
 | #define BT_NONE 0 | 
 | // Generate fast code at the expense of code size. | 
 | #define BT_SPEED 1 | 
 | // Resolve unknown SSAs on entry to a threading path.  If set, use the | 
 | // ranger.  If not, assume all ranges on entry to a path are VARYING. | 
 | #define BT_RESOLVE 2 | 
 |  | 
 | class back_threader | 
 | { | 
 | public: | 
 |   back_threader (function *fun, unsigned flags, bool first); | 
 |   ~back_threader (); | 
 |   unsigned thread_blocks (); | 
 | private: | 
 |   void maybe_thread_block (basic_block bb); | 
 |   bool debug_counter (); | 
 |   edge maybe_register_path (back_threader_profitability &); | 
 |   void maybe_register_path_dump (edge taken_edge); | 
 |   void find_paths_to_names (basic_block bb, bitmap imports, unsigned, | 
 | 			    back_threader_profitability &); | 
 |   edge find_taken_edge (const vec<basic_block> &path); | 
 |   edge find_taken_edge_cond (const vec<basic_block> &path, gcond *); | 
 |   edge find_taken_edge_switch (const vec<basic_block> &path, gswitch *); | 
 |   virtual void debug (); | 
 |   virtual void dump (FILE *out); | 
 |  | 
 |   back_threader_registry m_registry; | 
 |  | 
 |   // Current path being analyzed. | 
 |   auto_vec<basic_block> m_path; | 
 |   // Hash to mark visited BBs while analyzing a path. | 
 |   hash_set<basic_block> m_visited_bbs; | 
 |   // The set of SSA names, any of which could potentially change the | 
 |   // value of the final conditional in a path. | 
 |   auto_bitmap m_imports; | 
 |   // The last statement in the path. | 
 |   gimple *m_last_stmt; | 
 |   // Marker to differentiate unreachable edges. | 
 |   static const edge UNREACHABLE_EDGE; | 
 |   // Set to TRUE if unknown SSA names along a path should be resolved | 
 |   // with the ranger.  Otherwise, unknown SSA names are assumed to be | 
 |   // VARYING.  Setting to true is more precise but slower. | 
 |   function *m_fun; | 
 |   // Ranger for the path solver. | 
 |   gimple_ranger *m_ranger; | 
 |   unsigned m_flags; | 
 |   // Set to TRUE for the first of each thread[12] pass or the first of | 
 |   // each threadfull[12] pass.  This is used to differentiate between | 
 |   // the different threading passes so we can set up debug counters. | 
 |   bool m_first; | 
 | }; | 
 |  | 
 | // Used to differentiate unreachable edges, so we may stop the search | 
 | // in a the given direction. | 
 | const edge back_threader::UNREACHABLE_EDGE = (edge) -1; | 
 |  | 
 | back_threader::back_threader (function *fun, unsigned flags, bool first) | 
 |   : m_first (first) | 
 | { | 
 |   if (flags & BT_SPEED) | 
 |     loop_optimizer_init (LOOPS_HAVE_PREHEADERS | LOOPS_HAVE_SIMPLE_LATCHES); | 
 |   else | 
 |     loop_optimizer_init (AVOID_CFG_MODIFICATIONS); | 
 |  | 
 |   m_fun = fun; | 
 |   m_flags = flags; | 
 |   m_last_stmt = NULL; | 
 |  | 
 |   // The path solver needs EDGE_DFS_BACK in resolving mode. | 
 |   if (flags & BT_RESOLVE) | 
 |     mark_dfs_back_edges (); | 
 |  | 
 |   m_ranger = new gimple_ranger; | 
 | } | 
 |  | 
 | back_threader::~back_threader () | 
 | { | 
 |   delete m_ranger; | 
 |   loop_optimizer_finalize (); | 
 | } | 
 |  | 
 | // A wrapper for the various debug counters for the threading passes. | 
 | // Returns TRUE if it's OK to register the current threading | 
 | // candidate. | 
 |  | 
 | bool | 
 | back_threader::debug_counter () | 
 | { | 
 |   // The ethread pass is mostly harmless ;-). | 
 |   if ((m_flags & BT_SPEED) == 0) | 
 |     return true; | 
 |  | 
 |   if (m_flags & BT_RESOLVE) | 
 |     { | 
 |       if (m_first && !dbg_cnt (back_threadfull1)) | 
 | 	return false; | 
 |  | 
 |       if (!m_first && !dbg_cnt (back_threadfull2)) | 
 | 	return false; | 
 |     } | 
 |   else | 
 |     { | 
 |       if (m_first && !dbg_cnt (back_thread1)) | 
 | 	return false; | 
 |  | 
 |       if (!m_first && !dbg_cnt (back_thread2)) | 
 | 	return false; | 
 |     } | 
 |   return true; | 
 | } | 
 |  | 
 | static void | 
 | dump_path (FILE *dump_file, const vec<basic_block> &path) | 
 | { | 
 |   for (unsigned i = path.length (); i > 0; --i) | 
 |     { | 
 |       basic_block bb = path[i - 1]; | 
 |       fprintf (dump_file, "%d", bb->index); | 
 |       if (i > 1) | 
 | 	fprintf (dump_file, "->"); | 
 |     } | 
 | } | 
 |  | 
 | // Dump details of an attempt to register a path. | 
 |  | 
 | void | 
 | back_threader::maybe_register_path_dump (edge taken) | 
 | { | 
 |   if (m_path.is_empty ()) | 
 |     return; | 
 |  | 
 |   fprintf (dump_file, "path: "); | 
 |   dump_path (dump_file, m_path); | 
 |   fprintf (dump_file, "->"); | 
 |  | 
 |   if (taken == UNREACHABLE_EDGE) | 
 |     fprintf (dump_file, "xx REJECTED (unreachable)\n"); | 
 |   else if (taken) | 
 |     fprintf (dump_file, "%d SUCCESS\n", taken->dest->index); | 
 |   else | 
 |     fprintf (dump_file, "xx REJECTED\n"); | 
 | } | 
 |  | 
 | // If an outgoing edge can be determined out of the current path, | 
 | // register it for jump threading and return the taken edge. | 
 | // | 
 | // Return NULL if it is unprofitable to thread this path, or the | 
 | // outgoing edge is unknown.  Return UNREACHABLE_EDGE if the path is | 
 | // unreachable. | 
 |  | 
 | edge | 
 | back_threader::maybe_register_path (back_threader_profitability &profit) | 
 | { | 
 |   edge taken_edge = find_taken_edge (m_path); | 
 |  | 
 |   if (taken_edge && taken_edge != UNREACHABLE_EDGE) | 
 |     { | 
 |       bool irreducible = false; | 
 |       if (profit.profitable_path_p (m_path, taken_edge, &irreducible) | 
 | 	  && debug_counter () | 
 | 	  && m_registry.register_path (m_path, taken_edge)) | 
 | 	{ | 
 | 	  if (irreducible) | 
 | 	    vect_free_loop_info_assumptions (m_path[0]->loop_father); | 
 | 	} | 
 |       else | 
 | 	taken_edge = NULL; | 
 |     } | 
 |  | 
 |   if (dump_file && (dump_flags & TDF_DETAILS)) | 
 |     maybe_register_path_dump (taken_edge); | 
 |  | 
 |   return taken_edge; | 
 | } | 
 |  | 
 | // Return the known taken edge out of a path.  If the path can be | 
 | // determined to be unreachable, return UNREACHABLE_EDGE.  If no | 
 | // outgoing edge can be calculated, return NULL. | 
 |  | 
 | edge | 
 | back_threader::find_taken_edge (const vec<basic_block> &path) | 
 | { | 
 |   gcc_checking_assert (path.length () > 1); | 
 |   switch (gimple_code (m_last_stmt)) | 
 |     { | 
 |     case GIMPLE_COND: | 
 |       return find_taken_edge_cond (path, as_a<gcond *> (m_last_stmt)); | 
 |  | 
 |     case GIMPLE_SWITCH: | 
 |       return find_taken_edge_switch (path, as_a<gswitch *> (m_last_stmt)); | 
 |  | 
 |     default: | 
 |       return NULL; | 
 |     } | 
 | } | 
 |  | 
 | // Same as find_taken_edge, but for paths ending in a switch. | 
 |  | 
 | edge | 
 | back_threader::find_taken_edge_switch (const vec<basic_block> &path, | 
 | 				       gswitch *sw) | 
 | { | 
 |   tree name = gimple_switch_index (sw); | 
 |   int_range_max r; | 
 |  | 
 |   path_range_query solver (*m_ranger, path, m_imports, m_flags & BT_RESOLVE); | 
 |   solver.range_of_expr (r, name, sw); | 
 |  | 
 |   if (r.undefined_p ()) | 
 |     return UNREACHABLE_EDGE; | 
 |  | 
 |   if (r.varying_p ()) | 
 |     return NULL; | 
 |  | 
 |   tree label = find_case_label_range (sw, &r); | 
 |   if (!label) | 
 |     return NULL; | 
 |  | 
 |   return find_edge (gimple_bb (sw), label_to_block (cfun, CASE_LABEL (label))); | 
 | } | 
 |  | 
 | // Same as find_taken_edge, but for paths ending in a GIMPLE_COND. | 
 |  | 
 | edge | 
 | back_threader::find_taken_edge_cond (const vec<basic_block> &path, | 
 | 				     gcond *cond) | 
 | { | 
 |   int_range_max r; | 
 |  | 
 |   path_range_query solver (*m_ranger, path, m_imports, m_flags & BT_RESOLVE); | 
 |   solver.range_of_stmt (r, cond); | 
 |  | 
 |   if (solver.unreachable_path_p ()) | 
 |     return UNREACHABLE_EDGE; | 
 |  | 
 |   int_range<2> true_range = range_true (); | 
 |   int_range<2> false_range = range_false (); | 
 |  | 
 |   if (r == true_range || r == false_range) | 
 |     { | 
 |       edge e_true, e_false; | 
 |       basic_block bb = gimple_bb (cond); | 
 |       extract_true_false_edges_from_block (bb, &e_true, &e_false); | 
 |       return r == true_range ? e_true : e_false; | 
 |     } | 
 |   return NULL; | 
 | } | 
 |  | 
 | // Find jump threading paths to any of the SSA names in the | 
 | // INTERESTING bitmap, and register any such paths. | 
 | // | 
 | // BB is the current path being processed. | 
 | // | 
 | // OVERALL_PATHS is the search space up to this block | 
 |  | 
 | void | 
 | back_threader::find_paths_to_names (basic_block bb, bitmap interesting, | 
 | 				    unsigned overall_paths, | 
 | 				    back_threader_profitability &profit) | 
 | { | 
 |   if (m_visited_bbs.add (bb)) | 
 |     return; | 
 |  | 
 |   m_path.safe_push (bb); | 
 |  | 
 |   // Try to resolve the path without looking back.  Avoid resolving paths | 
 |   // we know are large but are not (yet) recognized as Finite State Machine. | 
 |   // ???  Ideally we'd explore the cheapest path to the loop backedge here, | 
 |   // avoiding the exponential greedy search and only start that from there. | 
 |   // Precomputing a path-size-to-immediate-dominator-of-successor for each | 
 |   // edge might help here.  Alternatively copying divergent control flow | 
 |   // on the way to the backedge could be worthwhile. | 
 |   bool large_non_fsm; | 
 |   if (m_path.length () > 1 | 
 |       && (!profit.possibly_profitable_path_p (m_path, &large_non_fsm) | 
 | 	  || (!large_non_fsm | 
 | 	      && maybe_register_path (profit)))) | 
 |     ; | 
 |  | 
 |   // The backwards thread copier cannot copy blocks that do not belong | 
 |   // to the same loop, so when the new source of the path entry no | 
 |   // longer belongs to it we don't need to search further. | 
 |   else if (m_path[0]->loop_father != bb->loop_father) | 
 |     ; | 
 |  | 
 |   // Continue looking for ways to extend the path but limit the | 
 |   // search space along a branch | 
 |   else if ((overall_paths = overall_paths * EDGE_COUNT (bb->preds)) | 
 | 	   <= (unsigned)param_max_jump_thread_paths) | 
 |     { | 
 |       // For further greedy searching we want to remove interesting | 
 |       // names defined in BB but add ones on the PHI edges for the | 
 |       // respective edges and adding imports from those stmts. | 
 |       // We do this by starting with all names | 
 |       // not defined in BB as interesting, collecting a list of | 
 |       // interesting PHIs in BB on the fly.  Then we iterate over | 
 |       // predecessor edges, adding interesting PHI edge defs to | 
 |       // the set of interesting names to consider when processing it. | 
 |       auto_bitmap new_interesting; | 
 |       auto_vec<int, 16> new_imports; | 
 |       auto_vec<gphi *, 4> interesting_phis; | 
 |       bitmap_iterator bi; | 
 |       unsigned i; | 
 |       auto_vec<tree, 16> worklist; | 
 |       EXECUTE_IF_SET_IN_BITMAP (interesting, 0, i, bi) | 
 | 	{ | 
 | 	  tree name = ssa_name (i); | 
 | 	  gimple *def_stmt = SSA_NAME_DEF_STMT (name); | 
 | 	  /* Imports remain interesting.  */ | 
 | 	  if (gimple_bb (def_stmt) != bb) | 
 | 	    { | 
 | 	      bitmap_set_bit (new_interesting, i); | 
 | 	      continue; | 
 | 	    } | 
 | 	  worklist.quick_push (name); | 
 | 	  while (!worklist.is_empty ()) | 
 | 	    { | 
 | 	      tree name = worklist.pop (); | 
 | 	      gimple *def_stmt = SSA_NAME_DEF_STMT (name); | 
 | 	      /* Newly discovered imports are interesting.  */ | 
 | 	      if (gimple_bb (def_stmt) != bb) | 
 | 		{ | 
 | 		  bitmap_set_bit (new_interesting, SSA_NAME_VERSION (name)); | 
 | 		  continue; | 
 | 		} | 
 | 	      /* Local PHIs participate in renaming below.  */ | 
 | 	      if (gphi *phi = dyn_cast<gphi *> (def_stmt)) | 
 | 		{ | 
 | 		  tree res = gimple_phi_result (phi); | 
 | 		  if (!SSA_NAME_OCCURS_IN_ABNORMAL_PHI (res)) | 
 | 		    interesting_phis.safe_push (phi); | 
 | 		} | 
 | 	      /* For other local defs process their uses, amending | 
 | 		 imports on the way.  */ | 
 | 	      else | 
 | 		{ | 
 | 		  tree ssa[3]; | 
 | 		  unsigned lim = gimple_range_ssa_names (ssa, 3, def_stmt); | 
 | 		  for (unsigned j = 0; j < lim; ++j) | 
 | 		    { | 
 | 		      tree rhs = ssa[j]; | 
 | 		      if (rhs | 
 | 			  && bitmap_set_bit (m_imports, | 
 | 					     SSA_NAME_VERSION (rhs))) | 
 | 			{ | 
 | 			  new_imports.safe_push (SSA_NAME_VERSION (rhs)); | 
 | 			  worklist.safe_push (rhs); | 
 | 			} | 
 | 		    } | 
 | 		} | 
 | 	    } | 
 | 	} | 
 |       if (!bitmap_empty_p (new_interesting) | 
 | 	  || !interesting_phis.is_empty ()) | 
 | 	{ | 
 | 	  auto_vec<int, 4> unwind (interesting_phis.length ()); | 
 | 	  auto_vec<int, 4> imports_unwind (interesting_phis.length ()); | 
 | 	  edge_iterator iter; | 
 | 	  edge e; | 
 | 	  FOR_EACH_EDGE (e, iter, bb->preds) | 
 | 	    { | 
 | 	      if (e->flags & EDGE_ABNORMAL | 
 | 		  // This is like path_crosses_loops in profitable_path_p but | 
 | 		  // more restrictive to avoid peeling off loop iterations (see | 
 | 		  // tree-ssa/pr14341.c for an example). | 
 | 		  // ???  Note this restriction only applied when visiting an | 
 | 		  // interesting PHI with the former resolve_phi. | 
 | 		  || (!interesting_phis.is_empty () | 
 | 		      && m_path[0]->loop_father != e->src->loop_father)) | 
 | 		continue; | 
 | 	      for (gphi *phi : interesting_phis) | 
 | 		{ | 
 | 		  tree def = PHI_ARG_DEF_FROM_EDGE (phi, e); | 
 | 		  if (TREE_CODE (def) == SSA_NAME) | 
 | 		    { | 
 | 		      int ver = SSA_NAME_VERSION (def); | 
 | 		      if (bitmap_set_bit (new_interesting, ver)) | 
 | 			{ | 
 | 			  if (bitmap_set_bit (m_imports, ver)) | 
 | 			    imports_unwind.quick_push (ver); | 
 | 			  unwind.quick_push (ver); | 
 | 			} | 
 | 		    } | 
 | 		} | 
 | 	      find_paths_to_names (e->src, new_interesting, overall_paths, | 
 | 				   profit); | 
 | 	      // Restore new_interesting. | 
 | 	      for (int def : unwind) | 
 | 		bitmap_clear_bit (new_interesting, def); | 
 | 	      unwind.truncate (0); | 
 | 	      // Restore and m_imports. | 
 | 	      for (int def : imports_unwind) | 
 | 		bitmap_clear_bit (m_imports, def); | 
 | 	      imports_unwind.truncate (0); | 
 | 	    } | 
 | 	} | 
 |       /* m_imports tracks all interesting names on the path, so when | 
 | 	 backtracking we have to restore it.  */ | 
 |       for (int j : new_imports) | 
 | 	bitmap_clear_bit (m_imports, j); | 
 |     } | 
 |   else if (dump_file && (dump_flags & TDF_DETAILS)) | 
 |     fprintf (dump_file, "  FAIL: Search space limit %d reached.\n", | 
 | 	     param_max_jump_thread_paths); | 
 |  | 
 |   // Reset things to their original state. | 
 |   m_path.pop (); | 
 |   m_visited_bbs.remove (bb); | 
 | } | 
 |  | 
 | // Search backwards from BB looking for paths where the final | 
 | // conditional maybe threaded to a successor block.  Record such paths | 
 | // for jump threading. | 
 |  | 
 | void | 
 | back_threader::maybe_thread_block (basic_block bb) | 
 | { | 
 |   if (EDGE_COUNT (bb->succs) <= 1) | 
 |     return; | 
 |  | 
 |   gimple *stmt = *gsi_last_bb (bb); | 
 |   if (!stmt) | 
 |     return; | 
 |  | 
 |   enum gimple_code code = gimple_code (stmt); | 
 |   if (code != GIMPLE_SWITCH | 
 |       && code != GIMPLE_COND) | 
 |     return; | 
 |  | 
 |   m_last_stmt = stmt; | 
 |   m_visited_bbs.empty (); | 
 |   m_path.truncate (0); | 
 |  | 
 |   // We compute imports of the path during discovery starting | 
 |   // just with names used in the conditional. | 
 |   bitmap_clear (m_imports); | 
 |   ssa_op_iter iter; | 
 |   tree name; | 
 |   FOR_EACH_SSA_TREE_OPERAND (name, stmt, iter, SSA_OP_USE) | 
 |     { | 
 |       if (!gimple_range_ssa_p (name)) | 
 | 	return; | 
 |       bitmap_set_bit (m_imports, SSA_NAME_VERSION (name)); | 
 |     } | 
 |  | 
 |   // Interesting is the set of imports we still not have see | 
 |   // the definition of.  So while imports only grow, the | 
 |   // set of interesting defs dwindles and once empty we can | 
 |   // stop searching. | 
 |   auto_bitmap interesting; | 
 |   bitmap_copy (interesting, m_imports); | 
 |   back_threader_profitability profit (m_flags & BT_SPEED, stmt); | 
 |   find_paths_to_names (bb, interesting, 1, profit); | 
 | } | 
 |  | 
 | DEBUG_FUNCTION void | 
 | debug (const vec <basic_block> &path) | 
 | { | 
 |   dump_path (stderr, path); | 
 |   fputc ('\n', stderr); | 
 | } | 
 |  | 
 | void | 
 | back_threader::dump (FILE *out) | 
 | { | 
 |   fprintf (out, "\nCandidates for pre-computation:\n"); | 
 |   fprintf (out, "===================================\n"); | 
 |  | 
 |   bitmap_iterator bi; | 
 |   unsigned i; | 
 |  | 
 |   EXECUTE_IF_SET_IN_BITMAP (m_imports, 0, i, bi) | 
 |     { | 
 |       tree name = ssa_name (i); | 
 |       print_generic_expr (out, name, TDF_NONE); | 
 |       fprintf (out, "\n"); | 
 |     } | 
 | } | 
 |  | 
 | void | 
 | back_threader::debug () | 
 | { | 
 |   dump (stderr); | 
 | } | 
 |  | 
 | /* Examine jump threading path PATH and return TRUE if it is possibly | 
 |    profitable to thread it, otherwise return FALSE.  If this function | 
 |    returns TRUE profitable_path_p might not be satisfied but when | 
 |    the path is extended it might be.  In particular indicate in | 
 |    *LARGE_NON_FSM whether the thread is too large for a non-FSM thread | 
 |    but would be OK if we extend the path to cover the loop backedge. | 
 |  | 
 |    ?? It seems we should be able to loosen some of the restrictions in | 
 |    this function after loop optimizations have run.  */ | 
 |  | 
 | bool | 
 | back_threader_profitability::possibly_profitable_path_p | 
 | 				  (const vec<basic_block> &m_path, | 
 | 				   bool *large_non_fsm) | 
 | { | 
 |   gcc_checking_assert (!m_path.is_empty ()); | 
 |  | 
 |   /* We can an empty path here (excluding the DEF block) when the | 
 |      statement that makes a conditional generate a compile-time | 
 |      constant result is in the same block as the conditional. | 
 |  | 
 |      That's not really a jump threading opportunity, but instead is | 
 |      simple cprop & simplification.  We could handle it here if we | 
 |      wanted by wiring up all the incoming edges.  If we run this | 
 |      early in IPA, that might be worth doing.   For now we just | 
 |      reject that case.  */ | 
 |   if (m_path.length () <= 1) | 
 |       return false; | 
 |  | 
 |   gimple_stmt_iterator gsi; | 
 |   loop_p loop = m_path[0]->loop_father; | 
 |  | 
 |   // We recompute the following, when we rewrite possibly_profitable_path_p | 
 |   // to work incrementally on added BBs we have to unwind them on backtracking | 
 |   m_n_insns = 0; | 
 |   m_threaded_through_latch = false; | 
 |   m_multiway_branch_in_path = false; | 
 |   m_contains_hot_bb = false; | 
 |  | 
 |   if (dump_file && (dump_flags & TDF_DETAILS)) | 
 |     fprintf (dump_file, "Checking profitability of path (backwards): "); | 
 |  | 
 |   /* Count the number of instructions on the path: as these instructions | 
 |      will have to be duplicated, we will not record the path if there | 
 |      are too many instructions on the path.  Also check that all the | 
 |      blocks in the path belong to a single loop.  */ | 
 |   for (unsigned j = 0; j < m_path.length (); j++) | 
 |     { | 
 |       basic_block bb = m_path[j]; | 
 |  | 
 |       if (dump_file && (dump_flags & TDF_DETAILS)) | 
 | 	fprintf (dump_file, " bb:%i", bb->index); | 
 |       /* Remember, blocks in the path are stored in opposite order in | 
 | 	 the PATH array.  The last entry in the array represents the | 
 | 	 block with an outgoing edge that we will redirect to the jump | 
 | 	 threading path.  Thus we don't care how many statements are | 
 | 	 in that block because it will not be copied or whether or not | 
 | 	 it ends in a multiway branch.  */ | 
 |       if (j < m_path.length () - 1) | 
 | 	{ | 
 | 	  int orig_n_insns = m_n_insns; | 
 | 	  if (!m_contains_hot_bb && m_speed_p) | 
 | 	    m_contains_hot_bb |= optimize_bb_for_speed_p (bb); | 
 | 	  for (gsi = gsi_after_labels (bb); | 
 | 	       !gsi_end_p (gsi); | 
 | 	       gsi_next_nondebug (&gsi)) | 
 | 	    { | 
 | 	      /* Do not allow OpenACC loop markers and __builtin_constant_p on | 
 | 		 threading paths.  The latter is disallowed, because an | 
 | 		 expression might be constant on two threading paths, and | 
 | 		 become non-constant (i.e.: phi) when they merge.  */ | 
 | 	      gimple *stmt = gsi_stmt (gsi); | 
 | 	      if (gimple_call_internal_p (stmt, IFN_UNIQUE) | 
 | 		  || gimple_call_builtin_p (stmt, BUILT_IN_CONSTANT_P)) | 
 | 		{ | 
 | 		  if (dump_file && (dump_flags & TDF_DETAILS)) | 
 | 		    fputc ('\n', dump_file); | 
 | 		  return false; | 
 | 		} | 
 | 	      /* Do not count empty statements and labels.  */ | 
 | 	      if (gimple_code (stmt) != GIMPLE_NOP | 
 | 		  && !is_gimple_debug (stmt)) | 
 | 		m_n_insns += estimate_num_insns (stmt, &eni_size_weights); | 
 | 	    } | 
 | 	  if (dump_file && (dump_flags & TDF_DETAILS)) | 
 | 	    fprintf (dump_file, " (%i insns)", m_n_insns-orig_n_insns); | 
 |  | 
 | 	  /* We do not look at the block with the threaded branch | 
 | 	     in this loop.  So if any block with a last statement that | 
 | 	     is a GIMPLE_SWITCH or GIMPLE_GOTO is seen, then we have a | 
 | 	     multiway branch on our path. | 
 |  | 
 | 	     The block in PATH[0] is special, it's the block were we're | 
 | 	     going to be able to eliminate its branch.  */ | 
 | 	  if (j > 0) | 
 | 	    { | 
 | 	      gimple *last = *gsi_last_bb (bb); | 
 | 	      if (last | 
 | 		  && (gimple_code (last) == GIMPLE_SWITCH | 
 | 		      || gimple_code (last) == GIMPLE_GOTO)) | 
 | 		m_multiway_branch_in_path = true; | 
 | 	    } | 
 | 	} | 
 |  | 
 |       /* Note if we thread through the latch, we will want to include | 
 | 	 the last entry in the array when determining if we thread | 
 | 	 through the loop latch.  */ | 
 |       if (loop->latch == bb) | 
 | 	{ | 
 | 	  m_threaded_through_latch = true; | 
 | 	  if (dump_file && (dump_flags & TDF_DETAILS)) | 
 | 	    fprintf (dump_file, " (latch)"); | 
 | 	} | 
 |     } | 
 |  | 
 |   /* We are going to remove the control statement at the end of the | 
 |      last block in the threading path.  So don't count it against our | 
 |      statement count.  */ | 
 |   m_n_insns -= m_exit_jump_benefit; | 
 |  | 
 |   if (dump_file && (dump_flags & TDF_DETAILS)) | 
 |     fprintf (dump_file, "\n  Control statement insns: %i\n" | 
 | 	     "  Overall: %i insns\n", | 
 | 	     m_exit_jump_benefit, m_n_insns); | 
 |  | 
 |   /* Threading is profitable if the path duplicated is hot but also | 
 |      in a case we separate cold path from hot path and permit optimization | 
 |      of the hot path later.  Be on the agressive side here. In some testcases, | 
 |      as in PR 78407 this leads to noticeable improvements.  */ | 
 |   if (m_speed_p) | 
 |     { | 
 |       if (m_n_insns >= param_max_fsm_thread_path_insns) | 
 | 	{ | 
 | 	  if (dump_file && (dump_flags & TDF_DETAILS)) | 
 | 	    fprintf (dump_file, "  FAIL: Jump-thread path not considered: " | 
 | 		     "the number of instructions on the path " | 
 | 		     "exceeds PARAM_MAX_FSM_THREAD_PATH_INSNS.\n"); | 
 | 	  return false; | 
 | 	} | 
 |       edge entry = find_edge (m_path[m_path.length () - 1], | 
 | 			      m_path[m_path.length () - 2]); | 
 |       if (probably_never_executed_edge_p (cfun, entry)) | 
 | 	{ | 
 | 	  if (dump_file && (dump_flags & TDF_DETAILS)) | 
 | 	    fprintf (dump_file, "  FAIL: Jump-thread path not considered: " | 
 | 		     "path entry is probably never executed.\n"); | 
 | 	  return false; | 
 | 	} | 
 |     } | 
 |   else if (m_n_insns > 1) | 
 |     { | 
 |       if (dump_file && (dump_flags & TDF_DETAILS)) | 
 | 	fprintf (dump_file, "  FAIL: Jump-thread path not considered: " | 
 | 		 "duplication of %i insns is needed and optimizing for size.\n", | 
 | 		 m_n_insns); | 
 |       return false; | 
 |     } | 
 |  | 
 |   /* The generic copier used by the backthreader does not re-use an | 
 |      existing threading path to reduce code duplication.  So for that | 
 |      case, drastically reduce the number of statements we are allowed | 
 |      to copy.  We don't know yet whether we will thread through the latch | 
 |      so we have to be permissive and continue threading, but indicate | 
 |      to the caller the thread, if final, wouldn't be profitable.  */ | 
 |   if ((!m_threaded_multiway_branch | 
 |        || !loop->latch | 
 |        || loop->latch->index == EXIT_BLOCK) | 
 |       && (m_n_insns * param_fsm_scale_path_stmts | 
 | 	  >= param_max_jump_thread_duplication_stmts)) | 
 |     { | 
 |       if (dump_file && (dump_flags & TDF_DETAILS)) | 
 | 	fprintf (dump_file, | 
 | 		 "  FAIL: Did not thread around loop and would copy too " | 
 | 		 "many statements.\n"); | 
 |       return false; | 
 |     } | 
 |   *large_non_fsm = (!(m_threaded_through_latch && m_threaded_multiway_branch) | 
 | 		    && (m_n_insns * param_fsm_scale_path_stmts | 
 | 			>= param_max_jump_thread_duplication_stmts)); | 
 |  | 
 |   if (dump_file && (dump_flags & TDF_DETAILS)) | 
 |     fputc ('\n', dump_file); | 
 |   return true; | 
 | } | 
 |  | 
 | /* Examine jump threading path PATH and return TRUE if it is profitable to | 
 |    thread it, otherwise return FALSE. | 
 |  | 
 |    The taken edge out of the path is TAKEN_EDGE. | 
 |  | 
 |    CREATES_IRREDUCIBLE_LOOP is set to TRUE if threading this path | 
 |    would create an irreducible loop. | 
 |  | 
 |    ?? It seems we should be able to loosen some of the restrictions in | 
 |    this function after loop optimizations have run.  */ | 
 |  | 
 | bool | 
 | back_threader_profitability::profitable_path_p (const vec<basic_block> &m_path, | 
 | 						edge taken_edge, | 
 | 						bool *creates_irreducible_loop) | 
 | { | 
 |   // We can assume that possibly_profitable_path_p holds here | 
 |  | 
 |   loop_p loop = m_path[0]->loop_father; | 
 |  | 
 |   if (dump_file && (dump_flags & TDF_DETAILS)) | 
 |     fprintf (dump_file, "Checking profitability of path (backwards): "); | 
 |  | 
 |   /* If this path threaded through the loop latch back into the | 
 |      same loop and the destination does not dominate the loop | 
 |      latch, then this thread would create an irreducible loop.  */ | 
 |   *creates_irreducible_loop = false; | 
 |   if (m_threaded_through_latch | 
 |       && loop == taken_edge->dest->loop_father | 
 |       && (determine_bb_domination_status (loop, taken_edge->dest) | 
 | 	  == DOMST_NONDOMINATING)) | 
 |     *creates_irreducible_loop = true; | 
 |  | 
 |   /* Threading is profitable if the path duplicated is hot but also | 
 |      in a case we separate cold path from hot path and permit optimization | 
 |      of the hot path later.  Be on the agressive side here. In some testcases, | 
 |      as in PR 78407 this leads to noticeable improvements.  */ | 
 |   if (m_speed_p | 
 |       && (optimize_edge_for_speed_p (taken_edge) || m_contains_hot_bb)) | 
 |     { | 
 |       if (probably_never_executed_edge_p (cfun, taken_edge)) | 
 | 	{ | 
 | 	  if (dump_file && (dump_flags & TDF_DETAILS)) | 
 | 	    fprintf (dump_file, "  FAIL: Jump-thread path not considered: " | 
 | 		     "path leads to probably never executed edge.\n"); | 
 | 	  return false; | 
 | 	} | 
 |     } | 
 |   else if (m_n_insns > 1) | 
 |     { | 
 |       if (dump_file && (dump_flags & TDF_DETAILS)) | 
 | 	fprintf (dump_file, "  FAIL: Jump-thread path not considered: " | 
 | 		 "duplication of %i insns is needed and optimizing for size.\n", | 
 | 		 m_n_insns); | 
 |       return false; | 
 |     } | 
 |  | 
 |   /* We avoid creating irreducible inner loops unless we thread through | 
 |      a multiway branch, in which case we have deemed it worth losing | 
 |      other loop optimizations later. | 
 |  | 
 |      We also consider it worth creating an irreducible inner loop after | 
 |      loop optimizations if the number of copied statement is low.  */ | 
 |   if (!m_threaded_multiway_branch | 
 |       && *creates_irreducible_loop | 
 |       && (!(cfun->curr_properties & PROP_loop_opts_done) | 
 | 	  || (m_n_insns * param_fsm_scale_path_stmts | 
 | 	      >= param_max_jump_thread_duplication_stmts))) | 
 |     { | 
 |       if (dump_file && (dump_flags & TDF_DETAILS)) | 
 | 	fprintf (dump_file, | 
 | 		 "  FAIL: Would create irreducible loop early without " | 
 | 		 "threading multiway branch.\n"); | 
 |       /* We compute creates_irreducible_loop only late.  */ | 
 |       return false; | 
 |     } | 
 |  | 
 |   /* The generic copier used by the backthreader does not re-use an | 
 |      existing threading path to reduce code duplication.  So for that | 
 |      case, drastically reduce the number of statements we are allowed | 
 |      to copy.  */ | 
 |   if (!(m_threaded_through_latch && m_threaded_multiway_branch) | 
 |       && (m_n_insns * param_fsm_scale_path_stmts | 
 | 	  >= param_max_jump_thread_duplication_stmts)) | 
 |     { | 
 |       if (dump_file && (dump_flags & TDF_DETAILS)) | 
 | 	fprintf (dump_file, | 
 | 		 "  FAIL: Did not thread around loop and would copy too " | 
 | 		 "many statements.\n"); | 
 |       return false; | 
 |     } | 
 |  | 
 |   /* When there is a multi-way branch on the path, then threading can | 
 |      explode the CFG due to duplicating the edges for that multi-way | 
 |      branch.  So like above, only allow a multi-way branch on the path | 
 |      if we actually thread a multi-way branch.  */ | 
 |   if (!m_threaded_multiway_branch && m_multiway_branch_in_path) | 
 |     { | 
 |       if (dump_file && (dump_flags & TDF_DETAILS)) | 
 | 	fprintf (dump_file, | 
 | 		 "  FAIL: Thread through multiway branch without threading " | 
 | 		 "a multiway branch.\n"); | 
 |       return false; | 
 |     } | 
 |  | 
 |   /* Threading through an empty latch would cause code to be added to | 
 |      the latch.  This could alter the loop form sufficiently to cause | 
 |      loop optimizations to fail.  Disable these threads until after | 
 |      loop optimizations have run.  */ | 
 |   if ((m_threaded_through_latch || taken_edge->dest == loop->latch) | 
 |       && !(cfun->curr_properties & PROP_loop_opts_done) | 
 |       && empty_block_p (loop->latch)) | 
 |     { | 
 |       if (dump_file && (dump_flags & TDF_DETAILS)) | 
 | 	fprintf (dump_file, | 
 | 		 "  FAIL: Thread through latch before loop opts would create " | 
 | 		 "non-empty latch\n"); | 
 |       return false; | 
 |     } | 
 |   if (dump_file && (dump_flags & TDF_DETAILS)) | 
 |     fputc ('\n', dump_file); | 
 |   return true; | 
 | } | 
 |  | 
 |  | 
 | /* The current path PATH is a vector of blocks forming a jump threading | 
 |    path in reverse order.  TAKEN_EDGE is the edge taken from path[0]. | 
 |  | 
 |    Convert the current path into the form used by register_jump_thread and | 
 |    register it. | 
 |  | 
 |    Return TRUE if successful or FALSE otherwise.  */ | 
 |  | 
 | bool | 
 | back_threader_registry::register_path (const vec<basic_block> &m_path, | 
 | 				       edge taken_edge) | 
 | { | 
 |   vec<jump_thread_edge *> *jump_thread_path = allocate_thread_path (); | 
 |  | 
 |   // The generic copier ignores the edge type.  We can build the | 
 |   // thread edges with any type. | 
 |   for (unsigned int j = 0; j + 1 < m_path.length (); j++) | 
 |     { | 
 |       basic_block bb1 = m_path[m_path.length () - j - 1]; | 
 |       basic_block bb2 = m_path[m_path.length () - j - 2]; | 
 |  | 
 |       edge e = find_edge (bb1, bb2); | 
 |       gcc_assert (e); | 
 |       push_edge (jump_thread_path, e, EDGE_COPY_SRC_BLOCK); | 
 |     } | 
 |  | 
 |   push_edge (jump_thread_path, taken_edge, EDGE_NO_COPY_SRC_BLOCK); | 
 |   return register_jump_thread (jump_thread_path); | 
 | } | 
 |  | 
 | // Thread all suitable paths in the current function. | 
 | // | 
 | // Return TODO_flags. | 
 |  | 
 | unsigned int | 
 | back_threader::thread_blocks () | 
 | { | 
 |   basic_block bb; | 
 |   FOR_EACH_BB_FN (bb, m_fun) | 
 |     if (EDGE_COUNT (bb->succs) > 1) | 
 |       maybe_thread_block (bb); | 
 |  | 
 |   bool changed = m_registry.thread_through_all_blocks (true); | 
 |  | 
 |   if (m_flags & BT_SPEED) | 
 |     return changed ? TODO_cleanup_cfg : 0; | 
 |  | 
 |   return false; | 
 | } | 
 |  | 
 | namespace { | 
 |  | 
 | const pass_data pass_data_early_thread_jumps = | 
 | { | 
 |   GIMPLE_PASS, | 
 |   "ethread", | 
 |   OPTGROUP_NONE, | 
 |   TV_TREE_SSA_THREAD_JUMPS, | 
 |   ( PROP_cfg | PROP_ssa ), | 
 |   0, | 
 |   0, | 
 |   0, | 
 |   ( TODO_cleanup_cfg | TODO_update_ssa ), | 
 | }; | 
 |  | 
 | const pass_data pass_data_thread_jumps = | 
 | { | 
 |   GIMPLE_PASS, | 
 |   "thread", | 
 |   OPTGROUP_NONE, | 
 |   TV_TREE_SSA_THREAD_JUMPS, | 
 |   ( PROP_cfg | PROP_ssa ), | 
 |   0, | 
 |   0, | 
 |   0, | 
 |   TODO_update_ssa, | 
 | }; | 
 |  | 
 | const pass_data pass_data_thread_jumps_full = | 
 | { | 
 |   GIMPLE_PASS, | 
 |   "threadfull", | 
 |   OPTGROUP_NONE, | 
 |   TV_TREE_SSA_THREAD_JUMPS, | 
 |   ( PROP_cfg | PROP_ssa ), | 
 |   0, | 
 |   0, | 
 |   0, | 
 |   TODO_update_ssa, | 
 | }; | 
 |  | 
 | // Early jump threading pass optimizing for size. | 
 | class pass_early_thread_jumps : public gimple_opt_pass | 
 | { | 
 | public: | 
 |   pass_early_thread_jumps (gcc::context *ctxt) | 
 |     : gimple_opt_pass (pass_data_early_thread_jumps, ctxt) | 
 |   {} | 
 |  | 
 |   opt_pass * clone () override | 
 |   { | 
 |     return new pass_early_thread_jumps (m_ctxt); | 
 |   } | 
 |   void set_pass_param (unsigned int, bool param) override | 
 |   { | 
 |     m_first = param; | 
 |   } | 
 |   bool gate (function *) override | 
 |   { | 
 |     return flag_thread_jumps; | 
 |   } | 
 |   unsigned int execute (function *fun) override | 
 |   { | 
 |     back_threader threader (fun, BT_NONE, m_first); | 
 |     return threader.thread_blocks (); | 
 |   } | 
 | private: | 
 |   bool m_first; | 
 | }; | 
 |  | 
 | // Jump threading pass without resolving of unknown SSAs. | 
 | class pass_thread_jumps : public gimple_opt_pass | 
 | { | 
 | public: | 
 |   pass_thread_jumps (gcc::context *ctxt) | 
 |     : gimple_opt_pass (pass_data_thread_jumps, ctxt) | 
 |   {} | 
 |   opt_pass * clone (void) override | 
 |   { | 
 |     return new pass_thread_jumps (m_ctxt); | 
 |   } | 
 |   void set_pass_param (unsigned int, bool param) override | 
 |   { | 
 |     m_first = param; | 
 |   } | 
 |   bool gate (function *) override | 
 |   { | 
 |     return flag_thread_jumps && flag_expensive_optimizations; | 
 |   } | 
 |   unsigned int execute (function *fun) override | 
 |   { | 
 |     back_threader threader (fun, BT_SPEED, m_first); | 
 |     return threader.thread_blocks (); | 
 |   } | 
 | private: | 
 |   bool m_first; | 
 | }; | 
 |  | 
 | // Jump threading pass that fully resolves unknown SSAs. | 
 | class pass_thread_jumps_full : public gimple_opt_pass | 
 | { | 
 | public: | 
 |   pass_thread_jumps_full (gcc::context *ctxt) | 
 |     : gimple_opt_pass (pass_data_thread_jumps_full, ctxt) | 
 |   {} | 
 |   opt_pass * clone (void) override | 
 |   { | 
 |     return new pass_thread_jumps_full (m_ctxt); | 
 |   } | 
 |   void set_pass_param (unsigned int, bool param) override | 
 |   { | 
 |     m_first = param; | 
 |   } | 
 |   bool gate (function *) override | 
 |   { | 
 |     return flag_thread_jumps && flag_expensive_optimizations; | 
 |   } | 
 |   unsigned int execute (function *fun) override | 
 |   { | 
 |     back_threader threader (fun, BT_SPEED | BT_RESOLVE, m_first); | 
 |     return threader.thread_blocks (); | 
 |   } | 
 | private: | 
 |   bool m_first; | 
 | }; | 
 |  | 
 | } // namespace { | 
 |  | 
 | gimple_opt_pass * | 
 | make_pass_thread_jumps (gcc::context *ctxt) | 
 | { | 
 |   return new pass_thread_jumps (ctxt); | 
 | } | 
 |  | 
 | gimple_opt_pass * | 
 | make_pass_thread_jumps_full (gcc::context *ctxt) | 
 | { | 
 |   return new pass_thread_jumps_full (ctxt); | 
 | } | 
 |  | 
 | gimple_opt_pass * | 
 | make_pass_early_thread_jumps (gcc::context *ctxt) | 
 | { | 
 |   return new pass_early_thread_jumps (ctxt); | 
 | } |