diff --git a/gcc/Makefile.in b/gcc/Makefile.in index 0aabc6ea3f2859e17b21b7ef84d3fd2eb43bc0e8..d115879ed9a05683712c85f21e638802efaa77ae 100644 --- a/gcc/Makefile.in +++ b/gcc/Makefile.in @@ -1454,6 +1454,7 @@ OBJS = \ ipa-cp.o \ ipa-sra.o \ ipa-devirt.o \ + ipa-bbr.o \ ipa-fnsummary.o \ ipa-polymorphic-call.o \ ipa-split.o \ diff --git a/gcc/cgraph.h b/gcc/cgraph.h index d96690326d14c45e4dde42e43fb5e8eaa0f33d50..0d0ad3b4c8c80277d24c75d9da197b941499d2af 100644 --- a/gcc/cgraph.h +++ b/gcc/cgraph.h @@ -892,7 +892,8 @@ struct GTY((tag ("SYMTAB_FUNCTION"))) cgraph_node : public symtab_node versionable (false), can_change_signature (false), redefined_extern_inline (false), tm_may_enter_irr (false), ipcp_clone (false), declare_variant_alt (false), - calls_declare_variant_alt (false), m_uid (uid), m_summary_id (-1) + calls_declare_variant_alt (false), m_uid (uid), m_summary_id (-1), + side_effect_state (3), side_effect_aux (0), bbr_cost (0) {} /* Remove the node from cgraph and all inline clones inlined into it. @@ -1491,6 +1492,14 @@ struct GTY((tag ("SYMTAB_FUNCTION"))) cgraph_node : public symtab_node unsigned declare_variant_alt : 1; /* True if the function calls declare_variant_alt functions. */ unsigned calls_declare_variant_alt : 1; + /*0:no side effect; 1:have local side effect; 2:have global side effect; + * 3:pending */ + unsigned side_effect_state : 2; + /*used to describle the indexs of parameter which result in local side + * effect, initial value:0*/ + uint64_t side_effect_aux; + /*cgraph_node bbr cost. initial value:0*/ + uint64_t bbr_cost; private: /* Unique id of the node. */ diff --git a/gcc/common.opt b/gcc/common.opt index 30f979870f61bdb2583401f51d230f87a841b23a..1cc920c22b18a00ef6dcc493a95f5e6a4057876f 100644 --- a/gcc/common.opt +++ b/gcc/common.opt @@ -1117,10 +1117,14 @@ Common Var(flag_asynchronous_unwind_tables) Optimization Generate unwind tables that are exact at each instruction boundary. farray-widen-compare -Common Report Var(flag_array_widen_compare) Optimization +Common Var(flag_array_widen_compare) Optimization Extends types for pointers to arrays to improve array comparsion performance. In some extreme situations this may result in unsafe behavior. +fipa-bbr +Common Var(flag_ipa_bbr) Optimization +Execute the reordering of blocks which end with 'gcond'. + fauto-inc-dec Common Var(flag_auto_inc_dec) Init(1) Optimization Generate auto-inc/dec instructions. diff --git a/gcc/ipa-bbr.cc b/gcc/ipa-bbr.cc new file mode 100644 index 0000000000000000000000000000000000000000..477ccdbda1d4f7940363e68eed64597e5fa9b5d5 --- /dev/null +++ b/gcc/ipa-bbr.cc @@ -0,0 +1,1795 @@ +/*this file implement the feature of basic block reorder; it will find the find +the chain of consecutive basic block ends with "gcond", and then reorder the +chain by the cost; +==----------------------------------------------------------------------===// + \author: hedian + \emial: hedian@hygon.cn + \version: hygonGCC1.2 + \date 2023/09/08 +==----------------------------------------------------------------------===// +*/ +#include +#include +#include +#include "config.h" +#include "system.h" +#include "coretypes.h" +#include "backend.h" +#include +#include "input.h" +#include "target.h" +#include "tree.h" +#include "gimple.h" +#include "tree-cfg.h" +#include "tree-pass.h" +#include "tree-pretty-print.h" +#include "tree-ssa-loop-manip.h" +#include "print-tree.h" +#include "gimple-iterator.h" +#include "gimple-ssa.h" +#include "gimple-fold.h" +#include "gimple-pretty-print.h" +#include "gimplify.h" +#include "tree-ssa-loop.h" +#include "tree-into-ssa.h" +#include "ssa.h" +#include "ssa-iterators.h" +#include "vec.h" +#include "dominance.h" +#include "fold-const.h" +#include "cgraph.h" +#include "ipa-utils.h" +#include "basic-block.h" +#include "cfghooks.h" + +using namespace std; + +int new_chain_no = -1; +// the declaration of basic block reorder struct +struct bb_info +{ + basic_block bb; // the basic block which is to be processed + unsigned HOST_WIDE_INT cost; // the cost of basic block + + bb_info () : bb (bb), cost (0) {} + bb_info (basic_block bb) : bb (bb), cost (0) {} +}; + +#define UNKNOWN_FUNCTION_BODY_COST 100 +#define GIMPLE_CALL_INIT_COST 5 + +// key is the code of builtin function; +// value is a pair data, first is the boolean which means have global +// side_effect, second means the indexes of args which result in the local side +// effect +unordered_map > > + side_effect_builtin_funs = { + // write + { BUILT_IN_PUTC, { true, { 0 } } }, + { BUILT_IN_PUTC_UNLOCKED, { true, { 0 } } }, + { BUILT_IN_FPUTC, { true, { 0 } } }, + { BUILT_IN_FPUTC_UNLOCKED, { true, { 0 } } }, + { BUILT_IN_FPUTS, { true, { 0 } } }, + { BUILT_IN_FPUTS_UNLOCKED, { true, { 0 } } }, + { BUILT_IN_PUTCHAR, { true, { 0 } } }, + { BUILT_IN_PUTCHAR_UNLOCKED, { true, { 0 } } }, + { BUILT_IN_PUTS, { true, { 0 } } }, + { BUILT_IN_PUTS_UNLOCKED, { true, { 0 } } }, + { BUILT_IN_FWRITE, { true, { 0 } } }, + { BUILT_IN_FWRITE_UNLOCKED, { true, { 0 } } }, + { BUILT_IN_FPRINTF, { true, { 0 } } }, + { BUILT_IN_FPRINTF_UNLOCKED, { true, { 0 } } }, + { BUILT_IN_PRINTF, { true, { 0 } } }, + { BUILT_IN_PRINTF_UNLOCKED, { true, { 0 } } }, + { BUILT_IN_SNPRINTF, { true, { 0 } } }, + { BUILT_IN_SPRINTF, { true, { 0 } } }, + { BUILT_IN_VFPRINTF, { true, { 0 } } }, + { BUILT_IN_VPRINTF, { true, { 0 } } }, + { BUILT_IN_VSNPRINTF, { true, { 0 } } }, + { BUILT_IN_VSPRINTF, { true, { 0 } } }, + { BUILT_IN_ISPRINT, { true, { 0 } } }, + { BUILT_IN_ISWPRINT, { true, { 0 } } }, + { BUILT_IN_SNPRINTF_CHK, { true, { 0 } } }, + { BUILT_IN_SPRINTF_CHK, { true, { 0 } } }, + { BUILT_IN_VSNPRINTF_CHK, { true, { 0 } } }, + { BUILT_IN_VSPRINTF_CHK, { true, { 0 } } }, + { BUILT_IN_FPRINTF_CHK, { true, { 0 } } }, + { BUILT_IN_PRINTF_CHK, { true, { 0 } } }, + { BUILT_IN_VFPRINTF_CHK, { true, { 0 } } }, + { BUILT_IN_VPRINTF_CHK, { true, { 0 } } }, + + // read + { BUILT_IN_SCANF, { false, { 1, 63 } } }, + { BUILT_IN_FSCANF, { false, { 2, 63 } } }, + { BUILT_IN_SSCANF, { false, { 2, 63 } } }, + { BUILT_IN_VFSCANF, { false, { 2, 63 } } }, + { BUILT_IN_VSCANF, { false, { 1, 63 } } }, + { BUILT_IN_VSSCANF, { false, { 2, 63 } } }, + + // exception + { BUILT_IN_UNWIND_RESUME, { false, { 0 } } }, + { BUILT_IN_CXA_END_CLEANUP, { false, { 0 } } }, + { BUILT_IN_EH_POINTER, { false, { 0 } } }, + { BUILT_IN_EH_FILTER, { false, { 0 } } }, + { BUILT_IN_EH_COPY_VALUES, { false, { 0 } } }, + + // mem str op + { BUILT_IN_MEMSET, { false, { 0 } } }, + { BUILT_IN_MEMSET_CHK, { false, { 0 } } }, + { BUILT_IN_MEMMOVE, { false, { 0 } } }, + { BUILT_IN_MEMMOVE_CHK, { false, { 0 } } }, + { BUILT_IN_MEMCPY, { false, { 0 } } }, + { BUILT_IN_MEMCPY_CHK, { false, { 0 } } }, + { BUILT_IN_STRCPY, { false, { 0 } } }, + { BUILT_IN_STRCPY_CHK, { false, { 0 } } }, + { BUILT_IN_STRNCPY, { false, { 0 } } }, + { BUILT_IN_STRNCPY_CHK, { false, { 0 } } }, + { BUILT_IN_STRCAT, { false, { 0 } } }, + { BUILT_IN_STRCAT_CHK, { false, { 0 } } }, + { BUILT_IN_STRNCAT, { false, { 0 } } }, + { BUILT_IN_STRNCAT_CHK, { false, { 0 } } }, + { BUILT_IN_INIT_TRAMPOLINE, { false, { 0 } } }, + { BUILT_IN_TM_MALLOC, { false, { 0 } } }, + { BUILT_IN_FREE, { false, { 0 } } }, + }; + +// key is the name string of function; +// value is a pair data, first is the boolean which means have global +// side_effect, second means the indexes of args which result in the local side +// effect +unordered_map > > + side_effect_customized_funs = { + { "fstat", { false, { 1 } } }, + { "oeprator delete", { false, { 0 } } }, + { "oeprator delete []", { false, { { 0 } } } }, + { "oeprator new", { false, { { 0 } } } }, + { "oeprator new []", { false, { 0 } } }, + { "malloc", { false, { 0 } } }, + { "calloc", { false, { 0 } } }, + { "free", { false, { 0 } } }, + { "pthread_once", { false, { 0 } } }, + { "regexec", { false, { 3 } } }, + { "putback", { false, { 0 } } }, + { "__fxstat", { false, { 2 } } }, + }; + +// information about basic block reorder. +struct aggr_bbr +{ + function *f; + // the processing basic blocks array for reorder + auto_vec br_reorder_chain; + auto_vec ori_br_reorder_chain; + // the list for the basic blocks which has been processed + auto_vec processed_bbs; + // return true if the basic_block bb is available for reorder + unsigned HOST_WIDE_INT num_chain; + + // return true if the basic_block bb is available for reorder + bool one_bbr_avail_p (basic_block bb, int mode); + // return true if the basic_block chain_cur_bb and chain_next_bb are both + // available for reorder + bool two_bbr_avail_p (basic_block chain_cur_bb, basic_block chain_next_bb, + basic_block chain_same_succ, int mode); + // return true if the basic_block chain_cur_bb and chain_next_bb has no + // use-def relationships between each other + bool two_bbr_use_def_independence_p (basic_block chain_cur_bb, + basic_block chain_next_bb); + // return true if the basic_block chain_cur_bb and chain_next_bb has no data + // dependent relationships between each other + bool two_bbr_mem_independence_p (basic_block chain_cur_bb, + basic_block chain_next_bb); + // get the cost for basic block reorder + void get_bbr_cost (); + // set the positions for basic block reorder + bool set_bbr_position (); + // main transform for basic block reorder + bool bbr_transform_1 (basic_block chain_pred, basic_block chain_same_succ, + basic_block chain_diff_succ); + // sink transform for basic block reorder + void bbr_sink_transform (basic_block chain_pred, basic_block chain_same_succ, + basic_block chain_diff_succ); + // switch gimple transform for basic block reorder + basic_block switch_transform (basic_block chain_last_bb, + basic_block chain_same_succ); + // max numbers of basic_blocks for reorder + unsigned HOST_WIDE_INT chain_max_num_bb = 20; + // aggr basic_block reorder + aggr_bbr (function *fun) : f (fun), num_chain (0) {} + // return true if basic_block reorder successfully + bool bbr_successfully_p (basic_block bb); + + void + dump_chain (FILE *dump_file, auto_vec &v) + { + for (auto i = 0; i < v.length (); ++i) + { + fprintf (dump_file, "#%d bb cost: %lu\n", i, v[i]->cost); + dump_bb (dump_file, v[i]->bb, 0, dump_flags); + } + } +}; + +static bool +bb_has_gphi_p (basic_block bb) +{ + if (!bb) + return false; + for (auto gsi = gsi_start_phis (bb); !gsi_end_p (gsi); gsi_next (&gsi)) + { + auto g = gsi_stmt (gsi); + if (g && TREE_CODE (gimple_phi_result (g)) == SSA_NAME) + return true; + } + return false; +} + +static unsigned HOST_WIDE_INT +get_function_gimple_count (function *fun) +{ + if (!fun) + return 0; + basic_block bb; + unsigned HOST_WIDE_INT res = 0; + FOR_EACH_BB_FN (bb, fun) + { + for (auto gsi = gsi_start_nondebug_bb (bb); !gsi_end_p (gsi); + gsi_next_nondebug (&gsi)) + ++res; + } + return res; +} + +static int +bb_cost_cmp (const void *p0, const void *p1) +{ + auto bb_info0 = *(bb_info **)p0; + auto bb_info1 = *(bb_info **)p1; + auto cost0 = bb_info0->cost; + auto cost1 = bb_info1->cost; + if (cost0 > cost1) + return 1; + if (cost0 == cost1) + return 0; + if (cost0 < cost1) + return -1; +} + +static unsigned HOST_WIDE_INT +get_bb_num_nonedebug_gimple (basic_block bb) +{ + if (!bb) + return -1; + unsigned HOST_WIDE_INT res = 0; + for (auto gsi = gsi_start_nondebug_bb (bb); !gsi_end_p (gsi); + gsi_next_nondebug (&gsi)) + { + ++res; + } + return res; +} + +static tree +find_mem_ref_in_tree (tree t) +{ + if (!t || CONSTANT_CLASS_P (t) || VAR_P (t) || DECL_P (t)) + return NULL_TREE; + auto t_code = TREE_CODE (t); + switch (t_code) + { + case MEM_REF: + return t; + case SSA_NAME: + break; + default: + return find_mem_ref_in_tree (TREE_OPERAND (t, 0)); + } + return NULL_TREE; +} + +static bool +gimple_has_side_effect_p (gimple *g) +{ + if (!g) + return false; + auto lhs = gimple_get_lhs (g); + + // we conservatively think !SSA_NAME lhs is store gimple, Todo:clear the what + // gimple has side effect to discover more optimze chances + if (lhs && TREE_CODE (lhs) != SSA_NAME) + return true; + + if (!is_gimple_call (g)) + return false; + auto g_call = dyn_cast (g); + auto callee_fn = gimple_call_fn (g_call); + tree callee_fndecl = gimple_call_fndecl (g_call); + if (callee_fndecl) + { + cgraph_node *n = cgraph_node::get (callee_fndecl); + if (n) + { + n = n->function_symbol (); + if (n->side_effect_state == 0) + return false; + } + } + else + { // gimple_call_fndecl(g) null + if (!callee_fn) + return true; + if (virtual_method_call_p (callee_fn)) + { + auto final = false, all_targets_have_no_side_effect = true; + auto targets + = possible_polymorphic_call_targets (callee_fn, g, &final, 0); + for (auto i = 0; i < targets.length (); ++i) + { + auto target = targets[i]; + auto rtarget = target->function_symbol (); + if (target->side_effect_state != 0) + all_targets_have_no_side_effect = false; + } + if (all_targets_have_no_side_effect) + return false; + } + } + return true; +} + +static bool +bb_succs_have_nonvirtual_gphi_p (basic_block bb) +{ + if (!bb) + return false; + for (auto i = 0; i < EDGE_COUNT (bb->succs); ++i) + { + auto succ = EDGE_SUCC (bb, i)->dest; + for (auto gsi = gsi_start_nonvirtual_phis (succ); !gsi_end_p (gsi); + gsi_next (&gsi)) + { + auto g = gsi_stmt (gsi); + if (g) + return true; + } + } + return false; +} + +static bool +bb_has_side_effect_p (basic_block bb) +{ + if (!bb) + return false; + for (auto gsi = gsi_start_nondebug_bb (bb); !gsi_end_p (gsi); + gsi_next_nondebug (&gsi)) + { + auto g = gsi_stmt (gsi); + if (gimple_has_side_effect_p (g)) + return true; + } + return false; +} + +static bool +compare_tree_code_p (tree_code t) +{ + if (!t) + return false; + if (t == NE_EXPR || t == EQ_EXPR || t == GE_EXPR || t == GT_EXPR + || t == LE_EXPR || t == LT_EXPR) + return true; + return false; +} + +static bool +get_data_from_vtable (tree rhs1) +{ + if (!rhs1) + return false; + for (auto type = TREE_TYPE (rhs1); type; type = TREE_TYPE (type)) + { + auto type_name = TYPE_NAME (type); + if (!type_name) + continue; + if (TREE_CODE (type_name) != IDENTIFIER_NODE) + continue; + auto type_name_str = IDENTIFIER_POINTER (type_name); + if (!strcmp (type_name_str, "__vtbl_ptr_type")) + return true; + } + return false; +} + +bool +aggr_bbr::one_bbr_avail_p (basic_block bb, int mode) +{ + if (!bb) + return true; + auto gsi = gsi_last_nondebug_bb (bb); + auto g = gsi_stmt (gsi); + if (!g) + return false; + auto g_code = gimple_code (g); + if (g_code != GIMPLE_COND && g_code != GIMPLE_SWITCH) + return false; + if (g_code == GIMPLE_COND && !(mode == 0 && EDGE_COUNT (bb->succs) == 2)) + return false; + if (g_code == GIMPLE_SWITCH) + { + auto g_swtich = dyn_cast (g); + auto num_labels = gimple_switch_num_labels (g_swtich); + if (!(mode == 1 && num_labels == 3)) + return false; + } + if (!single_pred_p (bb)) + return false; + if (bb_has_gphi_p (bb)) + return false; + if (bb_succs_have_nonvirtual_gphi_p (bb)) + return false; + if (EDGE_COUNT (bb->succs) == 2 + && EDGE_SUCC (bb, 0)->dest + == EDGE_SUCC (bb, 1) + ->dest) // should not have the same successor basic block. + return false; + if (bb_has_side_effect_p (bb)) + return false; + for (gsi = gsi_start_nondebug_bb (bb); !gsi_end_p (gsi); + gsi_next_nondebug (&gsi)) + { + g = gsi_stmt (gsi); + g_code = gimple_code (g); + auto lhs = gimple_get_lhs (g); + if (!lhs || TREE_CODE (lhs) != SSA_NAME) + continue; + gimple *g_user; + imm_use_iterator imm_iter; + FOR_EACH_IMM_USE_STMT (g_user, imm_iter, lhs) + { + // ensure the other bb does not use operand in this bb + if (gimple_bb (g_user) != bb) + return false; + // ensure no overflow compare + if (is_gimple_assign (g) && gimple_assign_rhs_code (g) == PLUS_EXPR) + { + auto rhs1 = gimple_assign_rhs1 (g); + auto rhs2 = gimple_assign_rhs2 (g); + if (TREE_CODE (rhs1) != SSA_NAME || TREE_CODE (rhs2) != INTEGER_CST + || !tree_fits_uhwi_p (rhs2)) + continue; + auto rhs2_uhwi = tree_to_uhwi (rhs2); + auto user_code = gimple_code (g_user); + if (user_code != GIMPLE_ASSIGN && user_code != GIMPLE_COND) + continue; + auto assign_p = is_gimple_assign (g_user); + auto cmp_lhs = assign_p ? gimple_assign_rhs1 (g_user) + : gimple_cond_lhs (g_user); + auto cmp_rhs = assign_p ? gimple_assign_rhs2 (g_user) + : gimple_cond_rhs (g_user); + auto cmp_code = assign_p ? gimple_assign_rhs_code (g_user) + : gimple_cond_code (g_user); + if (cmp_lhs != lhs || !compare_tree_code_p (cmp_code) + || !tree_fits_uhwi_p (cmp_rhs)) + continue; + auto cmp_rhs_uhwi = tree_to_uhwi (cmp_rhs); + if (cmp_rhs_uhwi < rhs2_uhwi) + return false; + } + // should deference the pointer lhs in same bb but to get data by vptr + auto type_code = TREE_CODE (TREE_TYPE (lhs)); + if (type_code == POINTER_TYPE && gimple_code (g_user) == GIMPLE_ASSIGN) + { + auto rhs1 = gimple_assign_rhs1 (g_user); + auto mem_ref = find_mem_ref_in_tree (rhs1); + if (!mem_ref) + continue; + auto op0 = TREE_OPERAND (mem_ref, 0); + if (op0 == lhs && !get_data_from_vtable (rhs1)) + return false; + } + } // FOR_EACH_IMM_USE_STMT end + } // for end + return true; +} + +static tree +find_virtual_operand_in_bb (basic_block bb) +{ + if (!bb) + return NULL_TREE; + // look for the last lhs virtual operand of gimple except gphi + for (auto gsi0 = gsi_last_nondebug_bb (bb); !gsi_end_p (gsi0); + gsi_prev (&gsi0)) + { + auto g0 = gsi_stmt (gsi0); + auto vdef0 = gimple_vdef (g0); + auto vuse0 = gimple_vuse (g0); + if (!vdef0 && !vuse0) + continue; + // find vdef or vuse of a gimple + return vdef0 ? vdef0 : vuse0; + } + + // look for the last virtual phi result + auto vdef = NULL_TREE; + for (auto gsi = gsi_start_phis (bb); !gsi_end_p (gsi); gsi_next (&gsi)) + { + auto g = gsi_stmt (gsi); + if (!virtual_operand_p (gimple_get_lhs (g))) + continue; + vdef = gimple_phi_result (g); + } + + return vdef; +} + +static void +fixup_bb_virtual_phis (basic_block bb, basic_block chain_pred) +{ + if (!bb) + return; + for (auto gsi = gsi_start_phis (bb); !gsi_end_p (gsi); gsi_next (&gsi)) + { + auto g = gsi_stmt (gsi); + gcc_assert (virtual_operand_p (gimple_phi_result (g))); + auto g_phi = dyn_cast (g); + if (gimple_phi_num_args (g_phi) <= 0) + return; + for (auto i = 0; i < gimple_phi_num_args (g_phi); ++i) + { + auto arg_def = gimple_phi_arg_def (g_phi, i); + if (arg_def) + continue; + // when arg_def is null, look for the vdef + tree vdef = NULL_TREE; + auto e = gimple_phi_arg_edge (g_phi, i); + auto pred = e->src; + for (auto bb0 = pred; bb0; + bb0 = get_immediate_dominator (CDI_DOMINATORS, bb0)) + { + vdef = find_virtual_operand_in_bb (bb0); + if (vdef) + goto set_vir_val; + } + // no virtual operand which want, look for the oldest .MEM operand + basic_block bb0; + FOR_EACH_BB_FN (bb0, cfun) + { + for (auto gsi = gsi_start_phis (bb0); !gsi_end_p (gsi); + gsi_next (&gsi)) + { + auto g0 = gsi_stmt (gsi); + auto lhs = gimple_get_lhs (g0); + if (virtual_operand_p (lhs)) + { + vdef = lhs; + goto set_vir_val; + } + } + for (auto gsi = gsi_start_nondebug_bb (bb0); !gsi_end_p (gsi); + gsi_next (&gsi)) + { + auto g0 = gsi_stmt (gsi); + auto vdef0 = gimple_vdef (g0); + auto vuse0 = gimple_vuse (g0); + if (!vdef0 && !vuse0) + continue; + vdef = vdef0 ? vdef0 : vuse0; + goto set_vir_val; + } + } + set_vir_val: + gcc_assert (vdef); + // set null def phi arg + location_t l = gimple_phi_arg_location_from_edge (g_phi, e); + SET_PHI_ARG_DEF (g_phi, i, vdef); + gimple_phi_arg_set_location (g_phi, i, l); + } + } +} + +// main real transform for basic block reorder +bool +aggr_bbr::bbr_transform_1 (basic_block chain_pred, basic_block chain_same_succ, + basic_block chain_diff_succ) +{ + unsigned HOST_WIDE_INT n = br_reorder_chain.length (); + gcc_assert (n > 1 && "n > 1"); + // step1 : switch_transform + auto last_bb_info = br_reorder_chain[n - 1]; + auto gsi = gsi_last_nondebug_bb (last_bb_info->bb); + auto g = gsi_stmt (gsi); + auto g_code = gimple_code (g); + if (g_code == GIMPLE_SWITCH) + { + chain_diff_succ = switch_transform (last_bb_info->bb, chain_same_succ); + fixup_bb_virtual_phis (chain_same_succ, chain_pred); + fixup_bb_virtual_phis (chain_diff_succ, chain_pred); + } + for (auto i = 0; i < br_reorder_chain.length (); ++i) + { + auto bb = br_reorder_chain[i]->bb; + for (gsi = gsi_start_nondebug_bb (bb); !gsi_end_p (gsi); + gsi_next_nondebug (&gsi)) + { + g = gsi_stmt (gsi); + gimple *g_user; + imm_use_iterator imm_iter; + tree lhs = gimple_get_lhs (g); + if (!(lhs && TREE_CODE (lhs) == SSA_NAME)) + continue; + FOR_EACH_IMM_USE_STMT (g_user, imm_iter, lhs) + { + if (gimple_bb (g_user) == chain_same_succ) + return false; + } // for each gimple user end + } // for each gimple end + } // for each block end + + // step2 : get_bbr_cost + get_bbr_cost (); + if (dump_file) + { + fprintf (dump_file, + "=== after switch transform and getting cost, chain:\n"); + dump_chain (dump_file, br_reorder_chain); + } + + // step3 : set_bbr_position + if (!set_bbr_position ()) + { + if (dump_file) + fprintf (dump_file, "=== reordering not change chain\n"); + return false; + } + + // step4 : bbr_sink_transform + ++new_chain_no; + bbr_sink_transform (chain_pred, chain_same_succ, chain_diff_succ); + if (dump_file) + { + fprintf (dump_file, "=== after reordering, %s's #%d new chain: \n", + dump_file_name, new_chain_no); + dump_chain (dump_file, br_reorder_chain); + fprintf (dump_file, "\n\n"); + } + + return true; +} + +static unsigned HOST_WIDE_INT +gimple_get_bbr_cost (gimple *g) +{ + if (!g) + return 0; + if (!is_gimple_call (g)) + return 1; + auto fun_exec_divisor = 3; + auto callee_fn = gimple_call_fn (g); + auto callee_fndecl = gimple_call_fndecl (g); + if (callee_fndecl) + { + auto n = cgraph_node::get (callee_fndecl); + if (n && n->bbr_cost != 0) + return n->bbr_cost / fun_exec_divisor; + if (n->alias) + n = n->get_alias_target (); + if (n && n->bbr_cost) + return n->bbr_cost / fun_exec_divisor; + } + else + { // gimple_call_fndecl(g) null + if (!callee_fn) + return UNKNOWN_FUNCTION_BODY_COST; + if (virtual_method_call_p (callee_fn)) + { + bool final = false; + auto targets + = possible_polymorphic_call_targets (callee_fn, g, &final, 0); + unsigned HOST_WIDE_INT polymorphic_call_cost_sum = 0; + auto n = targets.length (); + if (n <= 0) + return UNKNOWN_FUNCTION_BODY_COST; + for (auto i = 0; i < n; ++i) + { + auto target = targets[i]; + polymorphic_call_cost_sum += target->bbr_cost != 0 + ? target->bbr_cost + : UNKNOWN_FUNCTION_BODY_COST; + } + auto avg_polymorphic_call_cost = polymorphic_call_cost_sum / n; + return avg_polymorphic_call_cost / fun_exec_divisor; + } + // no virtual method + return UNKNOWN_FUNCTION_BODY_COST; + } + return UNKNOWN_FUNCTION_BODY_COST; +} + +static unsigned HOST_WIDE_INT +bb_get_bbr_cost (basic_block bb) +{ + if (!bb) + return 0; + unsigned HOST_WIDE_INT cost = 0; + for (auto gsi = gsi_start_nondebug_bb (bb); !gsi_end_p (gsi); + gsi_next_nondebug (&gsi)) + { + auto g = gsi_stmt (gsi); + cost += gimple_get_bbr_cost (g); + } + for (auto gsi = gsi_start_phis (bb); !gsi_end_p (gsi); gsi_next (&gsi)) + { + auto g = gsi_stmt (gsi); + cost += gimple_get_bbr_cost (g); + } + return cost; +} + +void +aggr_bbr::get_bbr_cost () +{ + for (auto i = 0; i < br_reorder_chain.length (); ++i) + { + auto bb_info = br_reorder_chain[i]; + auto bb = bb_info->bb; + bb_info->cost = bb_get_bbr_cost (bb); + } +} + +// set the positions for basic block reorder +bool +aggr_bbr::set_bbr_position () +{ + // reorder the basic blocks by the cost from small to large + auto n = br_reorder_chain.length (); + auto cost = br_reorder_chain[0]->cost; + for (auto i = 0; i < n; ++i) + { + if (cost != br_reorder_chain[i]->cost) + { + cost = 0; + break; + } + } + if (cost == 0) // have different bb cost + br_reorder_chain.qsort (bb_cost_cmp); + for (auto i = 0; i < n; ++i) + { + if (br_reorder_chain[i]->bb != ori_br_reorder_chain[i]->bb) + return true; + } + return false; +} + +// sink transform for basic block reorder +void +aggr_bbr::bbr_sink_transform (basic_block chain_pred, + basic_block chain_same_succ, + basic_block chain_diff_succ) +{ + edge e; + edge_iterator ei; + + // redirect the edge of br_reorder chain + for (auto i = 0; i < br_reorder_chain.length (); ++i) + { + auto bb = br_reorder_chain[i]->bb; + e = EDGE_SUCC (bb, 0)->dest == chain_same_succ ? EDGE_SUCC (bb, 1) + : EDGE_SUCC (bb, 0); + auto new_dest = i == br_reorder_chain.length () - 1 + ? chain_diff_succ + : br_reorder_chain[i + 1]->bb; + redirect_edge_and_branch (e, new_dest); + } + e = EDGE_PRED (ori_br_reorder_chain[0]->bb, 0); + auto new_dest = br_reorder_chain[0]->bb; + redirect_edge_and_branch (e, new_dest); + + // fixup the function dominator tree + basic_block bb; + auto_vec all_blocks_for_fn; + FOR_EACH_BB_FN (bb, cfun) + all_blocks_for_fn.safe_push (bb); + iterate_fix_dominators (CDI_DOMINATORS, all_blocks_for_fn, false); + + fixup_bb_virtual_phis (chain_same_succ, chain_pred); + fixup_bb_virtual_phis (chain_diff_succ, chain_pred); +} + +// split the bb ends with 'gswitch' into two bbs; +basic_block +aggr_bbr::switch_transform (basic_block chain_last_bb, + basic_block chain_same_succ) +{ + auto gsi = gsi_last_nondebug_bb (chain_last_bb); + auto g = gsi_stmt (gsi); + auto g_code = gimple_code (g); + auto g_switch = dyn_cast (g); + auto num_nondebug_gimple = get_bb_num_nonedebug_gimple (chain_last_bb); + gsi_prev_nondebug (&gsi); + g = gsi_stmt (gsi); + auto e = split_block (chain_last_bb, g); + auto splited_switch_bb = e->dest; + // remove_edge(e); + // auto splited_switch_gimple_seq=gsi_split_seq_after(gsi); + gimple_seq new_if_gimple_seq = nullptr; + unsigned case_num = gimple_switch_num_labels (g_switch); + auto g_switch_index = gimple_switch_index (g_switch); + tree t_cond = NULL_TREE; + gimple *g_cond = nullptr; + for (auto i = 1; i < case_num; ++i) + { // ignore the default label + auto case_label = gimple_switch_label (g_switch, i); + auto case_value = CASE_LOW (case_label); + auto t_cond_i = make_ssa_name (boolean_type_node); + auto g_cond_i = gimple_build_assign (t_cond_i, EQ_EXPR, g_switch_index, + case_value); + gimple_seq_add_stmt (&new_if_gimple_seq, g_cond_i); + if (i == 1) + { + t_cond = t_cond_i; + } + else if (i > 1) + { + g_cond = gimple_build_assign (make_ssa_name (boolean_type_node), + TRUTH_OR_EXPR, t_cond, t_cond_i); + gimple_seq_add_stmt (&new_if_gimple_seq, g_cond); + t_cond = gimple_assign_lhs (g_cond); + } + } + g_cond = gimple_build_cond_from_tree (t_cond, NULL_TREE, NULL_TREE); + gimple_seq_add_stmt (&new_if_gimple_seq, g_cond); + gsi = gsi_last_nondebug_bb (chain_last_bb); + gsi_insert_seq_after (&gsi, new_if_gimple_seq, GSI_LAST_NEW_STMT); + + remove_edge (e); + make_edge (chain_last_bb, splited_switch_bb, EDGE_TRUE_VALUE); + make_edge (chain_last_bb, chain_same_succ, EDGE_FALSE_VALUE); + + return splited_switch_bb; +} + +// analysis the two basic blocks whether has no use-def relationships between +// each other +bool +aggr_bbr::two_bbr_use_def_independence_p (basic_block chain_cur_bb, + basic_block chain_next_bb) +{ + if (!chain_cur_bb || !chain_next_bb) + return false; + if (chain_cur_bb == chain_next_bb) + return false; + + for (auto gsi = gsi_start_nondebug_bb (chain_cur_bb); !gsi_end_p (gsi); + gsi_next_nondebug (&gsi)) + { + auto g = gsi_stmt (gsi); + imm_use_iterator imm_iter; + gimple *g_user; + auto lhs = gimple_get_lhs (g); + if (lhs && TREE_CODE (lhs) == SSA_NAME) + { + FOR_EACH_IMM_USE_STMT (g_user, imm_iter, lhs) + { + if (gimple_bb (g_user) == chain_next_bb) + return false; + } + } + tree var; + ssa_op_iter op_iter; + FOR_EACH_SSA_TREE_OPERAND (var, g, op_iter, SSA_OP_USE | SSA_OP_DEF) + { + auto g0 = SSA_NAME_DEF_STMT (var); + if (g0 && gimple_bb (g0) == chain_next_bb) + return false; + } + } + return true; +} + +// analysis whether the basic_block chain_cur_bb and chain_next_bb has no data +// dependent relationships between each other +bool +aggr_bbr::two_bbr_mem_independence_p (basic_block chain_bb, + basic_block chain_next_bb) +{ + for (auto gsi = gsi_start_nondebug_bb (chain_next_bb); !gsi_end_p (gsi); + gsi_next_nondebug (&gsi)) + { + auto g = gsi_stmt (gsi); + auto g_code = gimple_code (g); + if (g_code != GIMPLE_ASSIGN) + continue; + auto rhs1 = gimple_assign_rhs1 (g); + auto mem_ref = find_mem_ref_in_tree (rhs1); + if (!mem_ref) + continue; + auto op0 = TREE_OPERAND (mem_ref, 0); + if (TREE_CODE (op0) != SSA_NAME + || TREE_CODE (TREE_TYPE (op0)) != POINTER_TYPE) + continue; + // if this pointer has been judged in the previous chain bbs, we cannot + // put chain_next_bb into the chain + gimple *g_user; + imm_use_iterator imm_iter; + FOR_EACH_IMM_USE_STMT (g_user, imm_iter, op0) + { + if (gimple_bb (g_user) != chain_bb) + continue; + auto user_code = gimple_code (g_user); + if (user_code != GIMPLE_ASSIGN && user_code != GIMPLE_COND) + continue; + auto assign_p = is_gimple_assign (g_user); + auto cmp_lhs = assign_p ? gimple_assign_rhs1 (g_user) + : gimple_cond_lhs (g_user); + auto cmp_rhs = assign_p ? gimple_assign_rhs2 (g_user) + : gimple_cond_rhs (g_user); + auto cmp_code = assign_p ? gimple_assign_rhs_code (g_user) + : gimple_cond_code (g_user); + if (cmp_lhs == op0 && tree_fits_uhwi_p (cmp_rhs) + && integer_zerop (cmp_rhs)) + return false; + } + } + return true; +} + +static unsigned HOST_WIDE_INT +get_max_val (unsigned int size) +{ + if (size == 0 || size == 1) + return size; + unsigned HOST_WIDE_INT ret = 1; + for (auto i = 0; i < size - 1; ++i) + ret |= ret << 1; + return ret; +} + +// analysis whether the basic_block chain_cur_bb and chain_next_bb are both +// available for reorder +bool +aggr_bbr::two_bbr_avail_p (basic_block chain_cur_bb, basic_block chain_next_bb, + basic_block chain_same_succ, int mode) +{ + if (chain_cur_bb == chain_next_bb) + return false; + if (!one_bbr_avail_p (chain_cur_bb, 0)) + return false; + if (!one_bbr_avail_p (chain_next_bb, mode)) + return false; + if (!two_bbr_use_def_independence_p (chain_cur_bb, chain_next_bb)) + return false; + if (!two_bbr_mem_independence_p (chain_cur_bb, chain_next_bb)) + return false; + edge e0, e1; + edge_iterator ei0, ei1; + FOR_EACH_EDGE (e0, ei0, chain_cur_bb->succs) + { + FOR_EACH_EDGE (e1, ei1, chain_next_bb->succs) + { + auto chain_cur_bb_succ = e0->dest, chain_next_bb_succ = e1->dest; + if (chain_cur_bb_succ == chain_next_bb_succ + && chain_cur_bb_succ == chain_same_succ) + return true; + } + } + return false; +} + +bool +aggr_bbr::bbr_successfully_p (basic_block chain_cur_bb) +{ + if (!chain_cur_bb) + return false; + gswitch *g_switch = nullptr; + auto transform_successfully = false; + unsigned HOST_WIDE_INT n = 0; + + // clear the processing branch reorder chain + for (auto i = 0; i < br_reorder_chain.length (); ++i) + delete br_reorder_chain[i]; + br_reorder_chain.release (); + + if (processed_bbs.contains (chain_cur_bb)) + return false; + auto chain_pred = single_pred (chain_cur_bb); + auto g = gsi_stmt (gsi_last_nondebug_bb (chain_cur_bb)); + + // chain_cur_bb must ends with 'gcond' + if (!one_bbr_avail_p (chain_cur_bb, 0)) + return false; + + br_reorder_chain.safe_push (new bb_info (chain_cur_bb)); + + // find the chain_next_bb and chain_same_succ + basic_block chain_next_bb = nullptr, chain_same_succ = nullptr; + for (auto i = 0; i < 2; ++i) + { + auto chain_cur_bb_succ0 = EDGE_SUCC (chain_cur_bb, i)->dest; + auto chain_cur_bb_succ1 = EDGE_SUCC (chain_cur_bb, i == 0 ? 1 : 0)->dest; + for (auto j = 0; j < EDGE_COUNT (chain_cur_bb_succ0->succs); ++j) + { + auto succ = EDGE_SUCC (chain_cur_bb_succ0, j)->dest; + if (succ == chain_cur_bb_succ1) + { + chain_next_bb = chain_cur_bb_succ0; + chain_same_succ = chain_cur_bb_succ1; + break; + } + } + } + if (!(chain_next_bb && chain_same_succ)) + return false; + + // 1.find the the chain bb which ends with 'gcond' as much as possible +find_longest_chain_ends_with_gcond: + while (br_reorder_chain.length () < chain_max_num_bb) + { + if (!one_bbr_avail_p (chain_next_bb, 0)) + goto analyze_gswitch_bb; + for (auto i = 0; i < br_reorder_chain.length (); ++i) + { + auto bb + = br_reorder_chain[i]->bb; // bb0 is the processing basic block + if (!two_bbr_avail_p (bb, chain_next_bb, chain_same_succ, 0)) + goto analyze_gswitch_bb; + } + br_reorder_chain.safe_push (new bb_info (chain_next_bb)); + chain_next_bb = EDGE_SUCC (chain_next_bb, 0)->dest == chain_same_succ + ? EDGE_SUCC (chain_next_bb, 1)->dest + : EDGE_SUCC (chain_next_bb, 0)->dest; + for (auto j = 0; j < br_reorder_chain.length (); j++) + { + if (chain_next_bb == br_reorder_chain[j]->bb) + goto analyze_gswitch_bb; + } + } + + // 2.the analysis for the basic block endswith 'gswitch' +analyze_gswitch_bb: + g = gsi_stmt (gsi_last_nondebug_bb (chain_next_bb)); + if (!g || gimple_code (g) != GIMPLE_SWITCH + || !one_bbr_avail_p (chain_next_bb, 1)) + goto transform; + for (auto i = 0; i != br_reorder_chain.length (); ++i) + { + auto bb = br_reorder_chain[i]->bb; + if (!two_bbr_avail_p (bb, chain_next_bb, chain_same_succ, 1)) + goto transform; + } + g_switch + = dyn_cast (gsi_stmt (gsi_last_nondebug_bb (chain_next_bb))); + if (gimple_switch_default_bb (f, g_switch) + != chain_same_succ) // this switch default case bb must be chain_same_bb + goto transform; + // bb of cases except default must have a single succ==chain_same_bb + for (unsigned i = 1; i < gimple_switch_num_labels (g_switch); ++i) + { + auto case_bb = gimple_switch_label_bb (f, g_switch, i); + if (!(single_succ_p (case_bb) + && single_succ (case_bb) == chain_same_succ)) + { + goto transform; + } + } + // we find the chain last bb ends with 'gswtich' + br_reorder_chain.safe_push (new bb_info (chain_next_bb)); + + // 3.transform +transform: + n = br_reorder_chain.length (); + for (auto i = 0; i < n; ++i) + processed_bbs.safe_push (br_reorder_chain[i]->bb); + if (n < 2) + return false; + ori_br_reorder_chain = br_reorder_chain.copy (); + ++num_chain; + if (dump_file) + { + fprintf ( + dump_file, + "\n======= find #%d ori branch-reorder-chain of %d basic blocks:\n", + num_chain - 1, n); + dump_chain (dump_file, ori_br_reorder_chain); + } + return bbr_transform_1 (chain_pred, chain_same_succ, chain_next_bb); +} + +static int +bbr_transform () +{ + auto abbr = aggr_bbr (cfun); + basic_block bb; + auto cfg_change = false; + auto ret = 0; + // for debug + if (dump_file) + { + fprintf (dump_file, "=== ori function body:\n"); + dump_function_to_file (current_function_decl, dump_file, dump_flags); + } + + do + { + cfg_change = false; + FOR_EACH_BB_FN (bb, cfun) + { + if (abbr.bbr_successfully_p (bb)) + { + cfg_change = true; + ret = 1; + break; + } + } + } + while (cfg_change); + return ret; +} + +static bool +cn_bbr_data_has_been_set_p (cgraph_node *n) +{ + if (!n) + return false; + if (n->side_effect_state != 3) + return true; + return false; +} + +static void +set_cn_side_effect_state (cgraph_node *n, int state) +{ + auto s = n->side_effect_state; + if (s == 0 || state == 3) + return; + n->side_effect_state = s == 3 ? state : s; +} + +static void +add_cn_bbr_cost (cgraph_node *n, unsigned HOST_WIDE_INT cost) +{ + if (!n) + return; + n->bbr_cost += cost ? cost : UNKNOWN_FUNCTION_BODY_COST; +} + +static bool +tree_local_p (tree t) +{ + gcc_assert (t); + auto lds = cfun->local_decls; + if (lds && lds->contains (t)) + return true; + return false; +} + +static void +set_cn_side_effect_aux (cgraph_node *n, int i) +{ + if (!n || i < 0) + return; + auto tmp = n->side_effect_aux; + auto bitwidth = sizeof (tmp) * 8; + if (i >= bitwidth) + return; + tmp = 1; + n->side_effect_aux |= (tmp << i); +} + +static int +find_cfun_arg_index (tree t) +{ + if (!t) + return -1; + auto args = DECL_ARGUMENTS (current_function_decl); + int i = 0; + for (tree arg = args; arg; arg = TREE_CHAIN (arg), ++i) + { + if (t == arg) + return i; + } + return -1; +} + +static tree +find_decl_in_tree (tree t, gimple *g = nullptr) +{ + if (!t) + return NULL_TREE; + auto t_code = TREE_CODE (t); + switch (t_code) + { + case INTEGER_CST: + case RESULT_DECL: + case STRING_CST: + case CONST_DECL: + return NULL_TREE; + + case VAR_DECL: + case PARM_DECL: + return t; + case SSA_NAME: + { + auto def_stmt = SSA_NAME_DEF_STMT (t); + auto type_code = TREE_CODE (TREE_TYPE (t)); + if (def_stmt && type_code == POINTER_TYPE + && is_gimple_assign (def_stmt) && def_stmt != g) + return find_decl_in_tree (gimple_assign_rhs1 (def_stmt), g); + auto var = SSA_NAME_VAR (t); + if (var) + return find_decl_in_tree (var, g); + break; + } + default: + return find_decl_in_tree (TREE_OPERAND (t, 0), g); + } // switch end + return NULL_TREE; +} + +static void +analyze_gassign_side_effect (gimple *g, cgraph_node *n) +{ + if (!g) + return; + auto lhs = gimple_get_lhs (g); + auto decl = find_decl_in_tree (lhs, g); + auto in_mem_ref = find_mem_ref_in_tree (lhs) ? true : false; + if (!decl) + return; + auto t_code = TREE_CODE (decl); + switch (t_code) + { + case VAR_DECL: + { + if (!tree_local_p (decl)) + set_cn_side_effect_state (n, 2); + break; + } + case PARM_DECL: + { + auto type_code = TREE_CODE (TREE_TYPE (decl)); + if (in_mem_ref + && (type_code == POINTER_TYPE || type_code == REFERENCE_TYPE)) + { + set_cn_side_effect_state (n, 1); + auto idx = find_cfun_arg_index (decl); + if (idx >= 0) + set_cn_side_effect_aux (n, idx); + } + break; + } + } +} + +auto_vec +get_cn_side_effect_aux_arr (cgraph_node *n, gimple *g = nullptr) +{ + auto_vec v; + if (!n) + return v; + auto tmp = n->side_effect_aux; + tmp = 1; + auto bitwidth = sizeof (tmp) * 8; + auto num_args = g ? gimple_call_num_args (g) : bitwidth - 1; + auto max_num_args = bitwidth - 1 > num_args ? num_args : (bitwidth - 1); + + for (auto i = 0; i < max_num_args; ++i) + { + if ((n->side_effect_aux & (tmp << i))) + { + v.safe_push (i); + } + } + if (v.length () == 0) + return v; + if (n->side_effect_aux & (tmp << bitwidth - 1)) + { + for (auto i = v[v.length () - 1] + 1; i < max_num_args; i++) + v.safe_push (i); + } + return v; +} + +static void +analyze_bbr_cost_for_cgraph_edge (cgraph_node *caller, cgraph_node *callee) +{ + if (!callee) + { + add_cn_bbr_cost (caller, UNKNOWN_FUNCTION_BODY_COST); + return; + } + callee = callee->function_symbol (); + if (!callee->bbr_cost) + { + add_cn_bbr_cost (caller, UNKNOWN_FUNCTION_BODY_COST); + return; + } + add_cn_bbr_cost (caller, callee->bbr_cost); +} + +static void +analyze_side_effect_for_cgraph_edge (cgraph_node *caller, cgraph_node *callee, + gimple *g) +{ + if (!caller || caller->side_effect_state == 2 + || caller->side_effect_state == 0 || caller == callee) + return; + if (!callee) + { + set_cn_side_effect_state (caller, 2); + return; + } + // have non-null callee + callee = callee->function_symbol (); + auto state = callee->side_effect_state; + switch (state) + { + case 1: + { + auto_vec side_effect_arg_indexs + = get_cn_side_effect_aux_arr (callee, g); + for (auto i = 0; i < side_effect_arg_indexs.length (); ++i) + { + auto idx = side_effect_arg_indexs[i]; + auto arg = gimple_call_arg (g, idx); + auto decl = find_decl_in_tree (arg); + if (!decl) + continue; + auto t_code = TREE_CODE (decl); + switch (t_code) + { + case VAR_DECL: + { + if (!tree_local_p (decl)) + set_cn_side_effect_state (caller, 2); + break; + } + case PARM_DECL: + { + auto type_code = TREE_CODE (TREE_TYPE (decl)); + if (type_code == POINTER_TYPE || type_code == REFERENCE_TYPE) + { + set_cn_side_effect_state (caller, 1); + auto idx = find_cfun_arg_index (decl); + if (idx >= 0) + set_cn_side_effect_aux (caller, idx); + } + break; + } + } // switch code end + } // for end + break; + } // case 1 end + case 2: + set_cn_side_effect_state (caller, 2); + break; + } // swtich end; +} + +static void +analyze_local_side_effect_parm_indexes (cgraph_node *n, vector &v) +{ + if (!n) + return; + auto tmp = n->side_effect_aux; + tmp = 1; + for (auto i = 0; i < v.size (); ++i) + n->side_effect_aux |= (tmp << v[i]); +} + +static void +analyze_builtin_fun_side_effect (cgraph_node *n) +{ + if (!n || !n->decl) + return; + auto dfc = DECL_FUNCTION_CODE (n->decl); + if (dfc == BUILT_IN_NONE) + return; + if (dump_file) + fprintf (dump_file, "this cgraph_node is a builtin function\n"); + if (side_effect_builtin_funs.find (dfc) != side_effect_builtin_funs.end ()) + { + auto side_effect_info = side_effect_builtin_funs[dfc]; + if (side_effect_info.first) + set_cn_side_effect_state (n, 2); + else + { + set_cn_side_effect_state (n, 1); + analyze_local_side_effect_parm_indexes (n, side_effect_info.second); + } + } +} + +static void +analyze_customized_fun_side_effect (cgraph_node *n) +{ + if (!n || !n->decl) + return; + auto node_name = n->name (); + if (side_effect_customized_funs.find (node_name) + != side_effect_customized_funs.end ()) + { + auto side_effect_info = side_effect_customized_funs[node_name]; + if (side_effect_info.first) + set_cn_side_effect_state (n, 2); + else + { + set_cn_side_effect_state (n, 1); + analyze_local_side_effect_parm_indexes (n, side_effect_info.second); + } + } +} + +static void +set_cn_bbr_data_with_fun_body (cgraph_node *n) +{ + // walk the gimple of function body + basic_block bb; + FOR_EACH_BB_FN (bb, cfun) + { + for (auto gsi = gsi_start_nondebug_bb (bb); !gsi_end_p (gsi); + gsi_next_nondebug (&gsi)) + { + auto g = gsi_stmt (gsi); + auto g_code = gimple_code (g); + if (g_code == GIMPLE_LABEL) + continue; + n->bbr_cost += 1; + auto lhs = gimple_get_lhs (g); + switch (g_code) + { + case GIMPLE_ASSIGN: + { + auto in_mem_ref = false; + analyze_gassign_side_effect (g, n); + break; + } // GIMPLE_ASSIGN end + case GIMPLE_CALL: + { + add_cn_bbr_cost (n, GIMPLE_CALL_INIT_COST); + auto g_call = dyn_cast (g); + auto callee_fn = gimple_call_fn (g_call); + auto callee_fndecl = gimple_call_fndecl (g_call); + if (callee_fndecl) + { + auto n0 = cgraph_node::get (callee_fndecl); + analyze_side_effect_for_cgraph_edge (n, n0, g); + analyze_bbr_cost_for_cgraph_edge (n, n0); + } + else + { // gimple_call_fndecl(g) is null + if (!callee_fn) + { + set_cn_side_effect_state (n, 2); + add_cn_bbr_cost (n, UNKNOWN_FUNCTION_BODY_COST); + continue; + } + // gimple_call_fn(g) is non-null + if (virtual_method_call_p (callee_fn)) + { + bool final = false; + auto targets = possible_polymorphic_call_targets ( + callee_fn, g, &final, 0); + auto len = targets.length (); + // polymorphic_call_targets num is 0 + if (len == 0) + { + set_cn_side_effect_state (n, 2); + add_cn_bbr_cost (n, UNKNOWN_FUNCTION_BODY_COST); + continue; + } + // polymorphic_call_targets num > 0 + unsigned HOST_WIDE_INT polymorphic_call_cost_sum = 0; + for (auto i = 0; i < len; ++i) + { + auto target = targets[i]; + auto rtarget = target->function_symbol (); + analyze_side_effect_for_cgraph_edge (n, rtarget, g); + polymorphic_call_cost_sum + += rtarget->bbr_cost != 0 + ? rtarget->bbr_cost + : UNKNOWN_FUNCTION_BODY_COST; + } + unsigned HOST_WIDE_INT avg_polymorphic_call_cost + = polymorphic_call_cost_sum / len; + add_cn_bbr_cost (n, avg_polymorphic_call_cost); + continue; + } + // no virtual method + set_cn_side_effect_state (n, 2); + add_cn_bbr_cost (n, UNKNOWN_FUNCTION_BODY_COST); + continue; + } + } // GIMPLE_CALL + } // switch + } // for each gimple + + for (auto gsi = gsi_start_nonvirtual_phis (bb); !gsi_end_p (gsi); + gsi_next (&gsi)) + n->bbr_cost += 1; + } // for bb end +} + +static void +dump_cn_bbr_data (cgraph_node *n) +{ + if (!n) + return; + fprintf (dump_file, "side effect state: "); + switch (n->side_effect_state) + { + case 0: + fprintf (dump_file, "NO_SIDE_EFFECT\n"); + break; + case 1: + { + fprintf (dump_file, "LOCAL_SIDE_EFFECT\n"); + fprintf (dump_file, + "indexes of parameters which resulted in local side effect:"); + auto_vec local_side_effect_parm_indexes + = get_cn_side_effect_aux_arr (n); + for (auto i = 0; i < local_side_effect_parm_indexes.length (); ++i) + fprintf (dump_file, " %d", local_side_effect_parm_indexes[i]); + fprintf (dump_file, "\n"); + break; + } + case 2: + fprintf (dump_file, "GLOBAL_SIDE_EFFECT\n"); + break; + case 3: + fprintf (dump_file, "PENDING_SIDE_EFFECT\n"); + break; + } + fprintf (dump_file, "bbr_cost: %lu\n", n->bbr_cost); +} + +static void +set_cn_bbr_data (cgraph_node *n) +{ + if (!n || !n->decl || n->alias || n->thunk) + return; + if (dump_file) + fprintf (dump_file, + "\n\n\n====================== start to set bbr data of " + "cgraph_node(name==%s, cgraph_uid==%d)\n", + n->name (), n->get_uid ()); + if (cn_bbr_data_has_been_set_p (n)) + { + if (dump_file) + fprintf (dump_file, "cgraph_node bbr data has been set\n"); + return; + } + analyze_builtin_fun_side_effect (n); + analyze_customized_fun_side_effect (n); + if (!n->definition) + { // no definition + if (dump_file) + fprintf (dump_file, "this cgraph_node has no definiton\n"); + set_cn_side_effect_state (n, 0); + add_cn_bbr_cost (n, UNKNOWN_FUNCTION_BODY_COST); + } + else + { // have definition + n->get_untransformed_body (); + auto fun = DECL_STRUCT_FUNCTION (n->decl); + gcc_assert (fun); + push_cfun (fun); + if (dump_file) + { + dump_function_header (dump_file, n->decl, dump_flags); + dump_function_to_file (n->decl, dump_file, dump_flags); + } + set_cn_bbr_data_with_fun_body (n); + pop_cfun (); + } + if (dump_file) + { + fprintf (dump_file, "=== set cgraph node \"%s(%s)\" bbr info:\n", + n->name (), n->asm_name ()); + dump_cn_bbr_data (n); + } +} + +static void +fixup_cn_bbr_data_with_fun_body (cgraph_node *n) +{ + basic_block bb; + FOR_EACH_BB_FN (bb, cfun) + { + for (auto gsi = gsi_start_nondebug_bb (bb); !gsi_end_p (gsi); + gsi_next_nondebug (&gsi)) + { + auto g = gsi_stmt (gsi); + if (!is_gimple_call (g)) + continue; + auto callee_fn = gimple_call_fn (g); + auto callee_fndecl = gimple_call_fndecl (g); + if (callee_fndecl) + { + auto n0 = cgraph_node::get (callee_fndecl); + analyze_side_effect_for_cgraph_edge (n, n0, g); + } + else + { // gimple_call_fndecl(g) is null + if (!callee_fn) + { + set_cn_side_effect_state (n, 2); + continue; + } + // gimple_call_fn(g) is non-null + if (virtual_method_call_p (callee_fn)) + { + bool final = false; + auto targets = possible_polymorphic_call_targets (callee_fn, g, + &final, 0); + auto len = targets.length (); + for (auto i = 0; i < len; ++i) + analyze_side_effect_for_cgraph_edge (n, targets[i], g); + continue; + } + // no virtual method + set_cn_side_effect_state (n, 2); + } + } + } +} + +// used to fixup the cgraph_node side_effect info because some cgraph_node +// side_effect info cannot be decided in the first time while its callee is +// decided after it +static void +fixup_cn_bbr_data (cgraph_node *n) +{ + if (!n) + return; + if (!n || !n->decl || n->alias || n->thunk) + return; + if (dump_file) + fprintf (dump_file, + "\n\n\n====================== start to fixup bbr data of " + "cgraph_node(name==%s, cgraph_uid==%d)\n", + n->name (), n->get_uid ()); + if (cn_bbr_data_has_been_set_p (n)) + { + if (dump_file) + fprintf (dump_file, "cgraph_node bbr data has been set\n"); + return; + } + gcc_assert (n->definition); + auto fndecl = n->decl; + n->get_untransformed_body (); + // definitely have non-null struct function and cfg info + auto fun = DECL_STRUCT_FUNCTION (fndecl); + push_cfun (fun); + if (dump_file) + { + dump_function_header (dump_file, fndecl, dump_flags); + dump_function_to_file (fndecl, dump_file, dump_flags); + } + fixup_cn_bbr_data_with_fun_body (n); + pop_cfun (); + if (!cn_bbr_data_has_been_set_p (n)) + set_cn_side_effect_state (n, 0); + if (dump_file) + { + fprintf (dump_file, "=== fixup cgraph node \"%s(%s)\" bbr info:\n", + n->name (), n->asm_name ()); + dump_cn_bbr_data (n); + } +} + +static unsigned int +ipa_bbr () +{ + cgraph_node **order = XCNEWVEC (cgraph_node *, symtab->cgraph_count); + auto order_pos = ipa_reverse_postorder (order); + + for (auto i = order_pos - 1; i >= 0; i--) + set_cn_bbr_data (order[i]); + for (auto i = order_pos - 1; i >= 0; i--) + fixup_cn_bbr_data (order[i]); + free (order); + return 0; +} + +namespace +{ + +const pass_data pass_data_ipa_bbr = { + IPA_PASS, /* type */ + "bbr", /* name */ + OPTGROUP_OTHER, /* optinfo_flags */ + TV_IPA_BBR, /* tv_id */ + (PROP_ssa | PROP_cfg), /* properties_required */ + 0, /* properties_provided */ + 0, /* properties_destroyed */ + 0, /* todo_flags_start */ + 0, /* todo_flags_finish */ +}; + +class pass_ipa_bbr : public ipa_opt_pass_d +{ +public: + pass_ipa_bbr (gcc::context *ctxt) + : ipa_opt_pass_d (pass_data_ipa_bbr, ctxt, NULL, /* generate_summary */ + NULL, /* write_summary */ + NULL, /* read_summary */ + NULL, /* write_optimization_summary */ + NULL, /* read_optimization_summary */ + NULL, /* stmt_fixup */ + 0, /* function_transform_todo_flags_start */ + NULL, /* function_transform */ + NULL) /* variable_transform */ + { + } + + virtual bool + gate (function *) + { + return flag_ipa_bbr && flag_tree_ter && optimize && in_lto_p; + } + + virtual unsigned int + execute (function *) + { + return ipa_bbr (); + } +}; // class pass_ipa_bbr + +const pass_data pass_data_bbr = { + GIMPLE_PASS, /* type */ + "bbr", /* name */ + OPTGROUP_OTHER, /* optinfo_flags */ + TV_TREE_BBR, /* tv_id */ + /* PROP_no_crit_edges is ensured by running split_edges_for_insertion in + pass_data_bbr::execute (). */ + (PROP_cfg | PROP_ssa), /* properties_required */ + 0, /* properties_provided */ + 0, /* properties_destroyed */ + 0, /* todo_flags_start */ + TODO_update_ssa, /* todo_flags_finish */ +}; + +class pass_bbr : public gimple_opt_pass +{ +public: + pass_bbr (gcc::context *ctxt) : gimple_opt_pass (pass_data_bbr, ctxt) {} + + /* opt_pass methods: */ + opt_pass * + clone () + { + return new pass_bbr (m_ctxt); + } + + virtual bool + gate (function *) + { + return flag_ipa_bbr && flag_tree_ter && optimize && in_lto_p; + } + + virtual unsigned int + execute (function *) + { + unsigned int todo = 0; + todo = bbr_transform (); + todo |= execute_fixup_cfg (); + return todo; + } +}; + +} // anon namespace + +ipa_opt_pass_d * +make_pass_ipa_bbr (gcc::context *ctxt) +{ + return new pass_ipa_bbr (ctxt); +} + +gimple_opt_pass * +make_pass_bbr (gcc::context *ctxt) +{ + return new pass_bbr (ctxt); +} diff --git a/gcc/lto-cgraph.cc b/gcc/lto-cgraph.cc index 237743ef0ba5b083b5973a987d7eb530f97b7cf3..49eb2a768f9c87e92f53f76f11b26519123229fc 100644 --- a/gcc/lto-cgraph.cc +++ b/gcc/lto-cgraph.cc @@ -503,6 +503,8 @@ lto_output_node (struct lto_simple_output_block *ob, struct cgraph_node *node, section = ""; streamer_write_hwi_stream (ob->main_stream, node->tp_first_run); + streamer_write_uhwi_stream (ob->main_stream, node->side_effect_aux); + streamer_write_uhwi_stream (ob->main_stream, node->bbr_cost); bp = bitpack_create (ob->main_stream); bp_pack_value (&bp, node->local, 1); @@ -557,6 +559,7 @@ lto_output_node (struct lto_simple_output_block *ob, struct cgraph_node *node, info next time we process the file. */ flag_incremental_link ? LDPR_UNKNOWN : node->resolution); bp_pack_value (&bp, node->split_part, 1); + bp_pack_value (&bp, node->side_effect_state, 2); streamer_write_bitpack (&bp); streamer_write_data_stream (ob->main_stream, section, strlen (section) + 1); @@ -1211,6 +1214,7 @@ input_overwrite_node (struct lto_file_decl_data *file_data, node->resolution = bp_unpack_enum (bp, ld_plugin_symbol_resolution, LDPR_NUM_KNOWN); node->split_part = bp_unpack_value (bp, 1); + node->side_effect_state = bp_unpack_value (bp, 2); verify_node_partition (node); } @@ -1302,7 +1306,8 @@ input_node (struct lto_file_decl_data *file_data, "node with uid %d", node->get_uid ()); node->tp_first_run = streamer_read_uhwi (ib); - + node->side_effect_aux=streamer_read_uhwi(ib); + node->bbr_cost=streamer_read_uhwi(ib); bp = streamer_read_bitpack (ib); input_overwrite_node (file_data, node, tag, &bp, &has_thunk_info); diff --git a/gcc/opt-functions.awk b/gcc/opt-functions.awk index 0288fb68adc0a60b77dffc061ab9fa5bc1b5f5c3..a832eed0bf70ef3035fa7d2f29f1c0652e5579e4 100644 --- a/gcc/opt-functions.awk +++ b/gcc/opt-functions.awk @@ -179,7 +179,7 @@ function switch_bit_fields (flags) flag_init("ToLower", flags) \ byte_size_flag - if (var_name(flags) != "flag_array_widen_compare" && flag_set_p("Report", flags)) + if (flag_set_p("Report", flags)) print "#error Report option property is dropped" sub(", $", "", result) diff --git a/gcc/opt-functions.awk.ori b/gcc/opt-functions.awk.ori new file mode 100644 index 0000000000000000000000000000000000000000..0288fb68adc0a60b77dffc061ab9fa5bc1b5f5c3 --- /dev/null +++ b/gcc/opt-functions.awk.ori @@ -0,0 +1,389 @@ +# Copyright (C) 2003-2022 Free Software Foundation, Inc. +# Contributed by Kelley Cook, June 2004. +# Original code from Neil Booth, May 2003. +# +# This program 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. +# +# This program 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 this program; see the file COPYING3. If not see +# . + +# Some common subroutines for use by opt[ch]-gen.awk. + +# Define some helpful character classes, for portability. +BEGIN { + lower = "abcdefghijklmnopqrstuvwxyz" + upper = "ABCDEFGHIJKLMNOPQRSTUVWXYZ" + digit = "0123456789" + alnum = lower "" upper "" digit +} + +# Return nonzero if FLAGS contains a flag matching REGEX. +function flag_set_p(regex, flags) +{ + # Ignore the arguments of flags with arguments. + gsub ("\\([^)]+\\)", "", flags); + return (" " flags " ") ~ (" " regex " ") +} + +# Return STRING if FLAGS contains a flag matching regexp REGEX, +# otherwise return the empty string. +function test_flag(regex, flags, string) +{ + if (flag_set_p(regex, flags)) + return string + return "" +} + +# Return a field initializer, with trailing comma, for a field that is +# 1 if FLAGS contains a flag matching REGEX and 0 otherwise. +function flag_init(regex, flags) +{ + if (flag_set_p(regex, flags)) + return "1 /* " regex " */, " + else + return "0, " +} + +# If FLAGS contains a "NAME(...argument...)" flag, return the value +# of the argument. Return the empty string otherwise. +function opt_args(name, flags) +{ + flags = " " flags + if (flags !~ " " name "\\(") + return "" + sub(".* " name "\\(", "", flags) + if (flags ~ "^[{]") + { + sub ("^[{]", "", flags) + sub ("}\\).*", "", flags) + } + else + sub("\\).*", "", flags) + + return flags +} + +# If FLAGS contains a "NAME(...argument...)" flag, return the value +# of the argument. Print error message otherwise. +function opt_args_non_empty(name, flags, description) +{ + args = opt_args(name, flags) + if (args == "") + print "#error Empty option argument '" name "' during parsing of: " flags + return args +} + +# Return the number of comma-separated element of S. +function n_args(s) +{ + n = 1 + while (s ~ ",") { + n++ + sub("[^,]*, *", "", s) + } + return n +} + +# Return the Nth comma-separated element of S. Return the empty string +# if S does not contain N elements. +function nth_arg(n, s) +{ + while (n-- > 0) { + if (s !~ ",") + return "" + sub("[^,]*, *", "", s) + } + sub(",.*", "", s) + return s +} + +# Return a bitmask of CL_* values for option flags FLAGS. +function switch_flags (flags) +{ + result = "0" + for (j = 0; j < n_langs; j++) { + regex = langs[j] + gsub ( "\\+", "\\+", regex ) + result = result test_flag(regex, flags, " | " macros[j]) + } + result = result \ + test_flag("Common", flags, " | CL_COMMON") \ + test_flag("Target", flags, " | CL_TARGET") \ + test_flag("PchIgnore", flags, " | CL_PCH_IGNORE") \ + test_flag("Driver", flags, " | CL_DRIVER") \ + test_flag("Joined", flags, " | CL_JOINED") \ + test_flag("JoinedOrMissing", flags, " | CL_JOINED") \ + test_flag("Separate", flags, " | CL_SEPARATE") \ + test_flag("Undocumented", flags, " | CL_UNDOCUMENTED") \ + test_flag("NoDWARFRecord", flags, " | CL_NO_DWARF_RECORD") \ + test_flag("Warning", flags, " | CL_WARNING") \ + test_flag("(Optimization|PerFunction)", flags, " | CL_OPTIMIZATION") \ + test_flag("Param", flags, " | CL_PARAMS") + sub( "^0 \\| ", "", result ) + return result +} + +# Return bit-field initializers for option flags FLAGS. +function switch_bit_fields (flags) +{ + uinteger_flag = "" + vn = var_name(flags); + if (host_wide_int[vn] == "yes") + hwi = "Host_Wide_Int" + else if (flag_set_p("Host_Wide_Int", flags)) { + hwi = "Host_Wide_Int" + uinteger_flag = flag_init("UInteger", flags) + } + else + hwi = "" + result = "" + sep_args = opt_args("Args", flags) + if (sep_args == "") + sep_args = 0 + else + sep_args-- + result = result sep_args ", " + + if (uinteger_flag == "") + uinteger_flag = flag_init("UInteger", flags) + + hwi_flag = flag_init("Host_Wide_Int", hwi) + byte_size_flag = flag_init("ByteSize", flags) + + if (substr(byte_size_flag, 1, 1) != "0" \ + && substr(uinteger_flag, 1, 1) == "0" \ + && substr(hwi_flag, 1, 1) == "0") + print "#error only UInteger amd Host_Wide_Int options can specify a ByteSize suffix" + + # The following flags need to be in the same order as + # the corresponding members of struct cl_option defined + # in gcc/opts.h. + result = result \ + flag_init("SeparateAlias", flags) \ + flag_init("NegativeAlias", flags) \ + flag_init("NoDriverArg", flags) \ + flag_init("RejectDriver", flags) \ + flag_init("RejectNegative", flags) \ + flag_init("JoinedOrMissing", flags) \ + uinteger_flag \ + hwi_flag \ + flag_init("ToLower", flags) \ + byte_size_flag + + if (var_name(flags) != "flag_array_widen_compare" && flag_set_p("Report", flags)) + print "#error Report option property is dropped" + + sub(", $", "", result) + return result +} + +# If FLAGS includes a Var flag, return the name of the variable it specifies. +# Return the empty string otherwise. +function var_name(flags) +{ + return nth_arg(0, opt_args("Var", flags)) +} + +# Return the name of the variable if FLAGS has a HOST_WIDE_INT variable. +# Return the empty string otherwise. +function host_wide_int_var_name(flags) +{ + split (flags, array, "[ \t]+") + if (array[1] == "HOST_WIDE_INT") + return array[2] + else + return "" +} + +# Return true if the option described by FLAGS has a globally-visible state. +function global_state_p(flags) +{ + return (var_name(flags) != "" \ + || opt_args("Mask", flags) != "" \ + || opt_args("InverseMask", flags) != "") +} + +# Return true if the option described by FLAGS must have some state +# associated with it. +function needs_state_p(flags) +{ + return (flag_set_p("Target", flags) \ + && !flag_set_p("Alias.*", flags) \ + && !flag_set_p("Ignore", flags)) +} + +# If FLAGS describes an option that needs state without a public +# variable name, return the name of that field, minus the initial +# "x_", otherwise return "". NAME is the name of the option. +function static_var(name, flags) +{ + if (global_state_p(flags) || !needs_state_p(flags)) + return "" + gsub ("[^" alnum "]", "_", name) + return "VAR_" name +} + +# Return the type of variable that should be associated with the given flags. +function var_type(flags) +{ + if (flag_set_p("Defer", flags)) + return "void *" + else if (flag_set_p("Enum.*", flags)) { + en = opt_args("Enum", flags); + return enum_type[en] " " + } + else if (!flag_set_p("Joined.*", flags) && !flag_set_p("Separate", flags)) + return "int " + else if (flag_set_p("Host_Wide_Int", flags)) + return "HOST_WIDE_INT " + else if (flag_set_p("UInteger", flags)) + return "int " + else + return "const char *" +} + +# Return the type of variable that should be associated with the given flags +# for use within a structure. Simple variables are changed to signed char +# type instead of int to save space. +function var_type_struct(flags) +{ + if (flag_set_p("UInteger", flags)) { + if (host_wide_int[var_name(flags)] == "yes") + return "HOST_WIDE_INT "; + if (flag_set_p("ByteSize", flags)) + return "HOST_WIDE_INT " + return "int " + } + else if (flag_set_p("Enum.*", flags)) { + en = opt_args("Enum", flags); + return enum_type[en] " " + } + else if (!flag_set_p("Joined.*", flags) && !flag_set_p("Separate", flags)) { + if (flag_set_p(".*Mask.*", flags)) { + if (host_wide_int[var_name(flags)] == "yes") + return "HOST_WIDE_INT " + else + return "/* - */ int " + } + else + return "signed char " + } + else + return "const char *" +} + +# Given that an option has flags FLAGS, return an initializer for the +# "var_enum", "var_type" and "var_value" fields of its cl_options[] entry. +function var_set(flags) +{ + if (flag_set_p("Defer", flags)) + return "0, CLVC_DEFER, 0" + s = nth_arg(1, opt_args("Var", flags)) + if (s != "") + return "0, CLVC_EQUAL, " s + s = opt_args("Mask", flags); + if (s != "") { + vn = var_name(flags); + if (vn) + return "0, CLVC_BIT_SET, OPTION_MASK_" s + else + return "0, CLVC_BIT_SET, MASK_" s + } + s = nth_arg(0, opt_args("InverseMask", flags)); + if (s != "") { + vn = var_name(flags); + if (vn) + return "0, CLVC_BIT_CLEAR, OPTION_MASK_" s + else + return "0, CLVC_BIT_CLEAR, MASK_" s + } + if (flag_set_p("Enum.*", flags)) { + en = opt_args("Enum", flags); + if (flag_set_p("EnumSet", flags)) + return enum_index[en] ", CLVC_ENUM, CLEV_SET" + else if (flag_set_p("EnumBitSet", flags)) + return enum_index[en] ", CLVC_ENUM, CLEV_BITSET" + else + return enum_index[en] ", CLVC_ENUM, CLEV_NORMAL" + } + if (var_type(flags) == "const char *") + return "0, CLVC_STRING, 0" + if (flag_set_p("ByteSize", flags)) + return "0, CLVC_SIZE, 0" + return "0, CLVC_INTEGER, 0" +} + +# Given that an option called NAME has flags FLAGS, return an initializer +# for the "flag_var" field of its cl_options[] entry. +function var_ref(name, flags) +{ + name = var_name(flags) static_var(name, flags) + if (name != "") + return "offsetof (struct gcc_options, x_" name ")" + if (opt_args("Mask", flags) != "") + return "offsetof (struct gcc_options, x_target_flags)" + if (opt_args("InverseMask", flags) != "") + return "offsetof (struct gcc_options, x_target_flags)" + return "(unsigned short) -1" +} + +# Given the option called NAME return a sanitized version of its name. +function opt_sanitized_name(name) +{ + gsub ("[^" alnum "]", "_", name) + return name +} + +# Given the option called NAME return the appropriate enum for it. +function opt_enum(name) +{ + return "OPT_" opt_sanitized_name(name) +} + +# Given the language called NAME return a sanitized version of its name. +function lang_sanitized_name(name) +{ + gsub( "[^" alnum "_]", "X", name ) + return name +} + +# Search for a valid var_name among all OPTS equal to option NAME. +# If not found, return "". +function search_var_name(name, opt_numbers, opts, flags, n_opts) +{ + opt_var_name = var_name(flags[opt_numbers[name]]); + if (opt_var_name != "") { + return opt_var_name; + } + for (k = 0; k < n_opts; k++) { + if (opts[k] == name && var_name(flags[k]) != "") { + return var_name(flags[k]); + } + } + return "" +} + +function integer_range_info(range_option, init, option, uinteger_used) +{ + if (range_option != "") { + ival = init + 0; + start = nth_arg(0, range_option) + 0; + end = nth_arg(1, range_option) + 0; + if (init != "" && init != "-1" && (ival < start || ival > end)) + print "#error initial value " init " of '" option "' must be in range [" start "," end "]" + if (uinteger_used && start < 0) + print "#error '" option"': negative IntegerRange (" start ", " end ") cannot be combined with UInteger" + return start ", " end + } + else + return "-1, -1" +} diff --git a/gcc/passes.def b/gcc/passes.def index 8dbb7983e3e2227bc8ea0f72082f86e82cc24234..8597be472d8e196a386988f613e560a0d1b23988 100644 --- a/gcc/passes.def +++ b/gcc/passes.def @@ -156,6 +156,7 @@ along with GCC; see the file COPYING3. If not see NEXT_PASS (pass_ipa_profile); NEXT_PASS (pass_ipa_icf); NEXT_PASS (pass_ipa_devirt); + NEXT_PASS (pass_ipa_bbr); NEXT_PASS (pass_ipa_cp); NEXT_PASS (pass_ipa_sra); NEXT_PASS (pass_ipa_cdtor_merge); @@ -260,6 +261,7 @@ along with GCC; see the file COPYING3. If not see NEXT_PASS (pass_lim); NEXT_PASS (pass_walloca, false); NEXT_PASS (pass_pre); + NEXT_PASS (pass_bbr); NEXT_PASS (pass_sink_code, false /* unsplit edges */); NEXT_PASS (pass_sancov); NEXT_PASS (pass_asan); diff --git a/gcc/testsuite/g++.dg/tree-ssa/bbr-0.C b/gcc/testsuite/g++.dg/tree-ssa/bbr-0.C new file mode 100644 index 0000000000000000000000000000000000000000..c3a2c57287764775de34890df69015a74e304b1f --- /dev/null +++ b/gcc/testsuite/g++.dg/tree-ssa/bbr-0.C @@ -0,0 +1,27 @@ +// we should reorder the f0 and rand() + +/* { dg-options "-O3 -flto -fipa-bbr -fdump-ipa-bbr-details -fdump-tree-bbr-details" } */ +/* { dg-do link } */ + +#include +#include + +__attribute__ ((noinline)) bool +f0 () +{ + int a = rand (); + a += 1; + if (a > 123456) + return true; + return false; +} + +int +main () +{ + if (f0 () && rand ()) + printf ("%d\n", rand ()); + return 0; +} + +/* { dg-final { scan-ltrans-tree-dump "find #" "bbr"} } */ diff --git a/gcc/testsuite/g++.dg/tree-ssa/bbr-1.C b/gcc/testsuite/g++.dg/tree-ssa/bbr-1.C new file mode 100644 index 0000000000000000000000000000000000000000..6db0a935c28ece2e9098cff1aa9da58eb36d004c --- /dev/null +++ b/gcc/testsuite/g++.dg/tree-ssa/bbr-1.C @@ -0,0 +1,28 @@ +// f0() have local side effect, cannot reorder + +/* { dg-options "-O3 -flto -fipa-bbr -fdump-ipa-bbr-details -fdump-tree-bbr-details" } */ +/* { dg-do link } */ + +#include +#include + +__attribute__ ((noinline)) bool +f0 (int *ap) +{ + *ap = rand (); + *ap += 1; + if ((*ap) > 123456) + return true; + return false; +} + +int +main () +{ + int a = 0; + if (f0 (&a) && rand ()) + printf ("%d\n", rand ()); + return 0; +} + +/* { dg-final { scan-ltrans-tree-dump-not "find #" "bbr"} } */ diff --git a/gcc/testsuite/g++.dg/tree-ssa/bbr-10.C b/gcc/testsuite/g++.dg/tree-ssa/bbr-10.C new file mode 100644 index 0000000000000000000000000000000000000000..bf7b3896d5b12de5400dad42da87ed2d03c5f3e3 --- /dev/null +++ b/gcc/testsuite/g++.dg/tree-ssa/bbr-10.C @@ -0,0 +1,27 @@ +// f0() have global side effect while amending the global variable by its +// reference + +/* { dg-options "-O3 -flto -fipa-bbr -fdump-ipa-bbr-details -fdump-tree-bbr-details" } */ +/* { dg-do link } */ +#include +#include + +int a = 1; + +__attribute__ ((noinline)) bool +f0 (int &a) +{ + a = rand (); + return a > 0 ? true : false; +} + +int +main () +{ + if (f0 (a) && rand ()) + { + printf ("%d\n", a); + } +} + +/* { dg-final { scan-ltrans-tree-dump-not "find #" "bbr"} } */ diff --git a/gcc/testsuite/g++.dg/tree-ssa/bbr-11.C b/gcc/testsuite/g++.dg/tree-ssa/bbr-11.C new file mode 100644 index 0000000000000000000000000000000000000000..e7bfb1af096cfca56f1682df57aeb2a1014b13a2 --- /dev/null +++ b/gcc/testsuite/g++.dg/tree-ssa/bbr-11.C @@ -0,0 +1,29 @@ +// f0() have local side effect, and the local side effect parameter index is +// not 0; cannot reorder + +/* { dg-options "-O3 -flto -fipa-bbr -fdump-ipa-bbr-details -fdump-tree-bbr-details" } */ +/* { dg-do link } */ + +#include +#include + +__attribute__ ((noinline)) bool +f0 (int a, int *ap) +{ + *ap = rand (); + *ap += a; + if ((*ap) > 123456) + return true; + return false; +} + +int +main () +{ + int a = 0; + if (f0 (rand (), &a) && rand ()) + printf ("%d\n", rand ()); + return 0; +} + +/* { dg-final { scan-ltrans-tree-dump-not "find #" "bbr"} } */ diff --git a/gcc/testsuite/g++.dg/tree-ssa/bbr-12.C b/gcc/testsuite/g++.dg/tree-ssa/bbr-12.C new file mode 100644 index 0000000000000000000000000000000000000000..17d5bc23b556d31eed01a14e723e920e37d389ea --- /dev/null +++ b/gcc/testsuite/g++.dg/tree-ssa/bbr-12.C @@ -0,0 +1,36 @@ +// f0() have no side effect but f1() have local side effect while amend the +// local variable of f0 by pointer parameter, and its index is not 0, can +// reorder +// +/* { dg-options "-O3 -flto -fipa-bbr -fdump-ipa-bbr-details -fdump-tree-bbr-details" } */ +/* { dg-do link } */ + +#include +#include + +__attribute__ ((noinline)) void +f1 (int a, int *ap) +{ + *ap = rand (); + *ap += a; +} +__attribute__ ((noinline)) bool +f0 () +{ + int a; + f1 (rand (), &a); + if (a > 123) + return true; + return false; +} + +int +main () +{ + int a = 0; + if (f0 () && rand ()) + printf ("%d\n", rand ()); + return 0; +} + +/* { dg-final { scan-ltrans-tree-dump "find #" "bbr"} } */ diff --git a/gcc/testsuite/g++.dg/tree-ssa/bbr-13.C b/gcc/testsuite/g++.dg/tree-ssa/bbr-13.C new file mode 100644 index 0000000000000000000000000000000000000000..7473e9a7e6d53e9b03d43d992bca4417ed7b3906 --- /dev/null +++ b/gcc/testsuite/g++.dg/tree-ssa/bbr-13.C @@ -0,0 +1,35 @@ +// f0() and f1() have no side effect by transmit the pointer parameter, and the +// index is different; cannot reorder + +// +/* { dg-options "-O3 -flto -fipa-bbr -fdump-ipa-bbr-details -fdump-tree-bbr-details" } */ +/* { dg-do link } */ + +#include +#include + +__attribute__ ((noinline)) void +f1 (int a, int *ap) +{ + *ap = rand (); + *ap += a; +} +__attribute__ ((noinline)) bool +f0 (int *ap) +{ + f1 (rand (), ap); + if (*ap > 123) + return true; + return false; +} + +int +main () +{ + int a = 0; + if (f0 (&a) && rand ()) + printf ("%d\n", rand ()); + return 0; +} + +/* { dg-final { scan-ltrans-tree-dump-not "find #" "bbr"} } */ diff --git a/gcc/testsuite/g++.dg/tree-ssa/bbr-14.C b/gcc/testsuite/g++.dg/tree-ssa/bbr-14.C new file mode 100644 index 0000000000000000000000000000000000000000..d35a8a2ca0e2e0d55be5a9ded74290ca6b186cd8 --- /dev/null +++ b/gcc/testsuite/g++.dg/tree-ssa/bbr-14.C @@ -0,0 +1,30 @@ +// f0() have local side effect by calling the function of the unkown function +// body, ie scanf + +/* { dg-options "-O3 -flto -fipa-bbr -fdump-ipa-bbr-details -fdump-tree-bbr-details" } */ +/* { dg-do link } */ + +#include +#include + +__attribute__ ((noinline)) bool +f0 (int *ap) +{ + *ap = rand (); + *ap += 1; + scanf ("%d\n", ap); + if ((*ap) > 123456) + return true; + return false; +} + +int +main () +{ + int a = 0; + if (f0 (&a) && rand ()) + printf ("%d\n", rand ()); + return 0; +} + +/* { dg-final { scan-ltrans-tree-dump-not "find #" "bbr"} } */ diff --git a/gcc/testsuite/g++.dg/tree-ssa/bbr-15.C b/gcc/testsuite/g++.dg/tree-ssa/bbr-15.C new file mode 100644 index 0000000000000000000000000000000000000000..843ef876a8c02292a8a43ad276b944498da0fedd --- /dev/null +++ b/gcc/testsuite/g++.dg/tree-ssa/bbr-15.C @@ -0,0 +1,30 @@ +// f0() have local side effect by amend more than 2 variable; + +/* { dg-options "-O3 -flto -fipa-bbr -fdump-ipa-bbr-details -fdump-tree-bbr-details" } */ +/* { dg-do link } */ + +#include +#include + +int b = 0; + +__attribute__ ((noinline)) bool +f0 (int *ap, int *bp) +{ + *ap = rand (); + *bp = rand (); + if ((*ap) > 123456 && (*bp) < 123) + return true; + return false; +} + +int +main () +{ + int a = 0; + if (f0 (&a, &b) && rand ()) + printf ("%d\n", rand ()); + return 0; +} + +/* { dg-final { scan-ltrans-tree-dump-not "find #" "bbr"} } */ diff --git a/gcc/testsuite/g++.dg/tree-ssa/bbr-16.C b/gcc/testsuite/g++.dg/tree-ssa/bbr-16.C new file mode 100644 index 0000000000000000000000000000000000000000..fab09db3cd6249a8b1512febd697319b3c5e3e84 --- /dev/null +++ b/gcc/testsuite/g++.dg/tree-ssa/bbr-16.C @@ -0,0 +1,37 @@ +// f0() and f1() have no side effect by transmit the pointer parameter, and the +// index is different; cannot reorder + +// +/* { dg-options "-O3 -flto -fipa-bbr -fdump-ipa-bbr-details -fdump-tree-bbr-details" } */ +/* { dg-do link } */ + +#include +#include + +__attribute__ ((noinline)) void +f1 (int *ap, int a, int *bp) +{ + *ap = rand (); + *bp = rand (); + *ap += *bp + a; +} +__attribute__ ((noinline)) bool +f0 (int *ap) +{ + int b = 0; + f1 (ap, rand (), &b); + if (*ap > 123) + return true; + return false; +} + +int +main () +{ + int a = 0; + if (f0 (&a) && rand ()) + printf ("%d\n", rand ()); + return 0; +} + +/* { dg-final { scan-ltrans-tree-dump-not "find #" "bbr"} } */ diff --git a/gcc/testsuite/g++.dg/tree-ssa/bbr-2.C b/gcc/testsuite/g++.dg/tree-ssa/bbr-2.C new file mode 100644 index 0000000000000000000000000000000000000000..1404d543c15453e51614898d54f6d91f06d898b9 --- /dev/null +++ b/gcc/testsuite/g++.dg/tree-ssa/bbr-2.C @@ -0,0 +1,34 @@ +// f0() have no side effect but f1() have local side effect while amend the +// local variable of f0 by pointer parameter, can reorder +// +/* { dg-options "-O3 -flto -fipa-bbr -fdump-ipa-bbr-details -fdump-tree-bbr-details" } */ +/* { dg-do link } */ + +#include +#include + +__attribute__ ((noinline)) void +f1 (int *ap) +{ + *ap = rand (); +} +__attribute__ ((noinline)) bool +f0 () +{ + int a; + f1 (&a); + if (a > 123) + return true; + return false; +} + +int +main () +{ + int a = 0; + if (f0 () && rand ()) + printf ("%d\n", rand ()); + return 0; +} + +/* { dg-final { scan-ltrans-tree-dump "find #" "bbr"} } */ diff --git a/gcc/testsuite/g++.dg/tree-ssa/bbr-3.C b/gcc/testsuite/g++.dg/tree-ssa/bbr-3.C new file mode 100644 index 0000000000000000000000000000000000000000..e4b93c964f06b6f8f2bcf8454586cbea3cfc27ed --- /dev/null +++ b/gcc/testsuite/g++.dg/tree-ssa/bbr-3.C @@ -0,0 +1,26 @@ +// f0() have global side effect by amend the global value, cannot reorder +/* { dg-options "-O3 -flto -fipa-bbr -fdump-ipa-bbr-details -fdump-tree-bbr-details" } */ +/* { dg-do link } */ + +#include +#include + +int a = 0; + +__attribute__ ((noinline)) bool +f0 () +{ + a = rand (); + return a > 123 ? true : false; +} + +int +main () +{ + int a = 0; + if (f0 () && rand ()) + printf ("%d\n", rand ()); + return 0; +} + +/* { dg-final { scan-ltrans-tree-dump-not "find #" "bbr"} } */ diff --git a/gcc/testsuite/g++.dg/tree-ssa/bbr-4.C b/gcc/testsuite/g++.dg/tree-ssa/bbr-4.C new file mode 100644 index 0000000000000000000000000000000000000000..64c976a547eb31e9bb4ae7286ef70b0ac9d1960e --- /dev/null +++ b/gcc/testsuite/g++.dg/tree-ssa/bbr-4.C @@ -0,0 +1,27 @@ +// f0() have side effect while amend the global variable by deferencing its +// pointer; +// +/* { dg-options "-O3 -flto -fipa-bbr -fdump-ipa-bbr-details -fdump-tree-bbr-details" } */ +/* { dg-do link } */ + +#include +#include + +int a = 0; + +__attribute__ ((noinline)) bool +f0 (int *ap) +{ + *ap = rand (); + return (*ap) > 123 ? true : false; +} + +int +main () +{ + if (f0 (&a) && rand ()) + printf ("%d\n", rand ()); + return 0; +} + +/* { dg-final { scan-ltrans-tree-dump-not "find #" "bbr"} } */ diff --git a/gcc/testsuite/g++.dg/tree-ssa/bbr-5.C b/gcc/testsuite/g++.dg/tree-ssa/bbr-5.C new file mode 100644 index 0000000000000000000000000000000000000000..2d8b383991d203683c12cf0e715e15328b940761 --- /dev/null +++ b/gcc/testsuite/g++.dg/tree-ssa/bbr-5.C @@ -0,0 +1,29 @@ +// gimple in the chain bb deference a pointer which are judged in the previous +// chain bb, this bb cannot be add to chain, cannot reorder; + +/* { dg-options "-O3 -flto -fipa-bbr -fdump-ipa-bbr-details -fdump-tree-bbr-details" } */ +/* { dg-do link } */ + +#include +#include +#include + +__attribute__ ((noinline)) int * +f0 () +{ + if (rand () > 123) + return new int (); + return 0; +} + +int +main () +{ + int *p = f0 (); + if (rand () && p && *p > 123) + { + printf ("%p %d\n", p, *p); + } +} + +/* { dg-final { scan-ltrans-tree-dump-not "find #" "bbr"} } */ diff --git a/gcc/testsuite/g++.dg/tree-ssa/bbr-6.C b/gcc/testsuite/g++.dg/tree-ssa/bbr-6.C new file mode 100644 index 0000000000000000000000000000000000000000..fb792e6dd6349985ac2d6122ef5455e1f4e16c08 --- /dev/null +++ b/gcc/testsuite/g++.dg/tree-ssa/bbr-6.C @@ -0,0 +1,32 @@ +// next gimples deference the POINTER_TYPE gimple lhs in same bb and not to get +// data from vtable + +/* { dg-options "-O3 -flto -fipa-bbr -fdump-ipa-bbr-details -fdump-tree-bbr-details" } */ +/* { dg-do link } */ +#include +#include + +struct ac +{ + int *ip; +}; + +__attribute__ ((noinline)) void +f0 (ac *acp) +{ + int *p = acp->ip; + if (rand () && *(acp->ip)) + { + printf ("%d\n", *(acp->ip)); + } +} + +int +main () +{ + ac *acp = new ac (); + acp->ip = rand () ? 0 : (new int ()); + f0 (acp); +} + +/* { dg-final { scan-ltrans-tree-dump-not "find #" "bbr"} } */ diff --git a/gcc/testsuite/g++.dg/tree-ssa/bbr-7.C b/gcc/testsuite/g++.dg/tree-ssa/bbr-7.C new file mode 100644 index 0000000000000000000000000000000000000000..c7d45c868805ea64135f6517e20347f87117ca1f --- /dev/null +++ b/gcc/testsuite/g++.dg/tree-ssa/bbr-7.C @@ -0,0 +1,38 @@ +// next gimples deference the POINTER_TYPE gimple lhs in same bb but to get +// data from vtable + +/* { dg-options "-O3 -flto -fipa-bbr -fdump-ipa-bbr-details -fdump-tree-bbr-details" } */ +/* { dg-do link } */ +#include +#include + +struct ac +{ + int *ap; + __attribute__ ((noinline)) virtual bool + f () + { + return rand () ? (rand () ? true : false) : false; + } +}; + +struct bc : public ac +{ + __attribute__ ((noinline)) bool + f () + { + return rand () ? true : false; + } +}; + +int +main () +{ + ac *acp = rand () ? (new ac ()) : (new bc ()); + if (rand () && acp->f () && rand ()) + { + printf ("%p\n", acp); + } +} + +/* { dg-final { scan-ltrans-tree-dump "find #" "bbr"} } */ diff --git a/gcc/testsuite/g++.dg/tree-ssa/bbr-8.C b/gcc/testsuite/g++.dg/tree-ssa/bbr-8.C new file mode 100644 index 0000000000000000000000000000000000000000..941355369dc10a206e8b8a96bb3a6e33412f5437 --- /dev/null +++ b/gcc/testsuite/g++.dg/tree-ssa/bbr-8.C @@ -0,0 +1,46 @@ +// use a POINTER_TYPE SSA_NAME to get a function POINTER_TYPE parameter, and +// then write value by deferecing this SSA_NAME; f0() f1() have local side +// effect, cannot reorder + +/* { dg-options "-O3 -flto -fipa-bbr -fdump-ipa-bbr-details -fdump-tree-bbr-details" } */ +/* { dg-do link } */ +#include +#include + +struct bc +{ + int c; + int b; +}; + +struct ac +{ + int a; + bc bcm; +}; + +__attribute__ ((noinline)) void +f1 (int *ap) +{ + *ap = rand (); +} + +__attribute__ ((noinline)) bool +f0 (ac *acp) +{ + int *p = &(acp->bcm.b); + f1 (p); + return *p > 1 ? true : false; +} + +int +main () +{ + ac *acp = new ac (); + if (rand () && f0 (acp) && rand ()) + { + printf ("%d\n", acp->bcm.b); + } +} + +/* { dg-final { scan-ltrans-tree-dump-not "find #" "bbr"} } */ diff --git a/gcc/testsuite/g++.dg/tree-ssa/bbr-9.C b/gcc/testsuite/g++.dg/tree-ssa/bbr-9.C new file mode 100644 index 0000000000000000000000000000000000000000..36a17824c248128bd7508f5968cc616430914ff5 --- /dev/null +++ b/gcc/testsuite/g++.dg/tree-ssa/bbr-9.C @@ -0,0 +1,26 @@ +// f0() have local side effect while amending the local variable by its +// reference + +/* { dg-options "-O3 -flto -fipa-bbr -fdump-ipa-bbr-details -fdump-tree-bbr-details" } */ +/* { dg-do link } */ +#include +#include + +__attribute__ ((noinline)) bool +f0 (int &a) +{ + a = rand (); + return a > 0 ? true : false; +} + +int +main () +{ + int a = 1; + if (f0 (a) && rand ()) + { + printf ("%d\n", a); + } +} + +/* { dg-final { scan-ltrans-tree-dump-not "find #" "bbr"} } */ diff --git a/gcc/timevar.def b/gcc/timevar.def index 794b8017d18168888d07455b99fe938fc692ef09..59f03ac947d291cf2b61547718f85a7b234c1d35 100644 --- a/gcc/timevar.def +++ b/gcc/timevar.def @@ -80,6 +80,7 @@ DEFTIMEVAR (TV_IPA_CONSTANT_PROP , "ipa cp") DEFTIMEVAR (TV_IPA_INLINING , "ipa inlining heuristics") DEFTIMEVAR (TV_IPA_FNSPLIT , "ipa function splitting") DEFTIMEVAR (TV_IPA_COMDATS , "ipa comdats") +DEFTIMEVAR (TV_IPA_BBR , "ipa bbr") DEFTIMEVAR (TV_IPA_OPT , "ipa various optimizations") DEFTIMEVAR (TV_IPA_LTO_DECOMPRESS , "lto stream decompression") DEFTIMEVAR (TV_IPA_LTO_COMPRESS , "lto stream compression") @@ -176,6 +177,7 @@ DEFTIMEVAR (TV_TREE_SPLIT_EDGES , "tree split crit edges") DEFTIMEVAR (TV_TREE_REASSOC , "tree reassociation") DEFTIMEVAR (TV_TREE_PRE , "tree PRE") DEFTIMEVAR (TV_TREE_FRE , "tree FRE") +DEFTIMEVAR (TV_TREE_BBR , "tree bbr") DEFTIMEVAR (TV_TREE_SINK , "tree code sinking") DEFTIMEVAR (TV_TREE_PHIOPT , "tree linearize phis") DEFTIMEVAR (TV_TREE_BACKPROP , "tree backward propagate") diff --git a/gcc/tree-pass.h b/gcc/tree-pass.h index 55ee2fe7f9e89f00e35cfd61d602bbaec3a1906a..2eda6e31965fd0a026a2964c83ebd6d420969f7e 100644 --- a/gcc/tree-pass.h +++ b/gcc/tree-pass.h @@ -458,6 +458,7 @@ extern gimple_opt_pass *make_pass_tree_ifcombine (gcc::context *ctxt); extern gimple_opt_pass *make_pass_dse (gcc::context *ctxt); extern gimple_opt_pass *make_pass_nrv (gcc::context *ctxt); extern gimple_opt_pass *make_pass_rename_ssa_copies (gcc::context *ctxt); +extern gimple_opt_pass *make_pass_bbr (gcc::context *ctxt); extern gimple_opt_pass *make_pass_sink_code (gcc::context *ctxt); extern gimple_opt_pass *make_pass_fre (gcc::context *ctxt); extern gimple_opt_pass *make_pass_check_data_deps (gcc::context *ctxt); @@ -521,6 +522,7 @@ extern ipa_opt_pass_d *make_pass_ipa_inline (gcc::context *ctxt); extern simple_ipa_opt_pass *make_pass_ipa_free_lang_data (gcc::context *ctxt); extern simple_ipa_opt_pass *make_pass_ipa_free_fn_summary (gcc::context *ctxt); extern ipa_opt_pass_d *make_pass_ipa_cp (gcc::context *ctxt); +extern ipa_opt_pass_d *make_pass_ipa_bbr (gcc::context *ctxt); extern ipa_opt_pass_d *make_pass_ipa_sra (gcc::context *ctxt); extern ipa_opt_pass_d *make_pass_ipa_icf (gcc::context *ctxt); extern ipa_opt_pass_d *make_pass_ipa_devirt (gcc::context *ctxt); diff --git a/gcc/tree-ssa-loop-array-widen-compare.c b/gcc/tree-ssa-loop-array-widen-compare.cc similarity index 100% rename from gcc/tree-ssa-loop-array-widen-compare.c rename to gcc/tree-ssa-loop-array-widen-compare.cc