diff options
Diffstat (limited to '0013-LoopElim-Redundant-loop-elimination-optimization.patch')
-rw-r--r-- | 0013-LoopElim-Redundant-loop-elimination-optimization.patch | 499 |
1 files changed, 499 insertions, 0 deletions
diff --git a/0013-LoopElim-Redundant-loop-elimination-optimization.patch b/0013-LoopElim-Redundant-loop-elimination-optimization.patch new file mode 100644 index 0000000..d50107e --- /dev/null +++ b/0013-LoopElim-Redundant-loop-elimination-optimization.patch @@ -0,0 +1,499 @@ +From 0d14a2b7a3defc82ed16c99a18c2bc2e6be9f5b1 Mon Sep 17 00:00:00 2001 +From: xiezhiheng <xiezhiheng@huawei.com> +Date: Fri, 16 Jul 2021 23:21:38 -0400 +Subject: [PATCH 13/13] [LoopElim] Redundant loop elimination optimization + +Introduce redundant loop elimination optimization controlled +by -floop-elim. And it's often used with -ffinite-loops. + +diff --git a/gcc/common.opt b/gcc/common.opt +index 79c9ef6615b..b2b0aac7fdf 100644 +--- a/gcc/common.opt ++++ b/gcc/common.opt +@@ -1169,6 +1169,10 @@ fcompare-elim + Common Report Var(flag_compare_elim_after_reload) Optimization + Perform comparison elimination after register allocation has finished. + ++floop-elim ++Common Report Var(flag_loop_elim) Init(0) Optimization ++Perform redundant loop elimination. ++ + fconserve-stack + Common Var(flag_conserve_stack) Optimization + Do not perform optimizations increasing noticeably stack usage. +diff --git a/gcc/tree-ssa-phiopt.c b/gcc/tree-ssa-phiopt.c +index 3b5b6907679..591b6435f78 100644 +--- a/gcc/tree-ssa-phiopt.c ++++ b/gcc/tree-ssa-phiopt.c +@@ -69,6 +69,7 @@ static hash_set<tree> * get_non_trapping (); + static void replace_phi_edge_with_variable (basic_block, edge, gimple *, tree); + static void hoist_adjacent_loads (basic_block, basic_block, + basic_block, basic_block); ++static bool do_phiopt_pattern (basic_block, basic_block, basic_block); + static bool gate_hoist_loads (void); + + /* This pass tries to transform conditional stores into unconditional +@@ -257,6 +258,10 @@ tree_ssa_phiopt_worker (bool do_store_elim, bool do_hoist_loads, bool early_p) + hoist_adjacent_loads (bb, bb1, bb2, bb3); + continue; + } ++ else if (flag_loop_elim && do_phiopt_pattern (bb, bb1, bb2)) ++ { ++ continue; ++ } + else + continue; + +@@ -2819,6 +2824,449 @@ hoist_adjacent_loads (basic_block bb0, basic_block bb1, + } + } + ++static bool check_uses (tree, hash_set<tree> *); ++ ++/* Check SSA_NAME is used in ++ if (SSA_NAME == 0) ++ ... ++ or ++ if (SSA_NAME != 0) ++ ... ++*/ ++static bool ++check_uses_cond (const_tree ssa_name, gimple *stmt, ++ hash_set<tree> *hset ATTRIBUTE_UNUSED) ++{ ++ tree_code code = gimple_cond_code (stmt); ++ if (code != EQ_EXPR && code != NE_EXPR) ++ { ++ return false; ++ } ++ ++ tree lhs = gimple_cond_lhs (stmt); ++ tree rhs = gimple_cond_rhs (stmt); ++ if ((lhs == ssa_name && integer_zerop (rhs)) ++ || (rhs == ssa_name && integer_zerop (lhs))) ++ { ++ return true; ++ } ++ ++ return false; ++} ++ ++/* Check SSA_NAME is used in ++ _tmp = SSA_NAME == 0; ++ or ++ _tmp = SSA_NAME != 0; ++ or ++ _tmp = SSA_NAME | _tmp2; ++*/ ++static bool ++check_uses_assign (const_tree ssa_name, gimple *stmt, hash_set<tree> *hset) ++{ ++ tree_code code = gimple_assign_rhs_code (stmt); ++ tree lhs, rhs1, rhs2; ++ ++ switch (code) ++ { ++ case EQ_EXPR: ++ case NE_EXPR: ++ rhs1 = gimple_assign_rhs1 (stmt); ++ rhs2 = gimple_assign_rhs2 (stmt); ++ if ((rhs1 == ssa_name && integer_zerop (rhs2)) ++ || (rhs2 == ssa_name && integer_zerop (rhs1))) ++ { ++ return true; ++ } ++ break; ++ ++ case BIT_IOR_EXPR: ++ lhs = gimple_assign_lhs (stmt); ++ if (hset->contains (lhs)) ++ { ++ return false; ++ } ++ /* We should check the use of _tmp further. */ ++ return check_uses (lhs, hset); ++ ++ default: ++ break; ++ } ++ return false; ++} ++ ++/* Check SSA_NAME is used in ++ # result = PHI <SSA_NAME (bb1), 0 (bb2), 0 (bb3)> ++*/ ++static bool ++check_uses_phi (const_tree ssa_name, gimple *stmt, hash_set<tree> *hset) ++{ ++ for (unsigned i = 0; i < gimple_phi_num_args (stmt); i++) ++ { ++ tree arg = gimple_phi_arg_def (stmt, i); ++ if (!integer_zerop (arg) && arg != ssa_name) ++ { ++ return false; ++ } ++ } ++ ++ tree result = gimple_phi_result (stmt); ++ ++ /* It is used to avoid infinite recursion, ++ <bb 1> ++ if (cond) ++ goto <bb 2> ++ else ++ goto <bb 3> ++ ++ <bb 2> ++ # _tmp2 = PHI <0 (bb 1), _tmp3 (bb 3)> ++ {BODY} ++ if (cond) ++ goto <bb 3> ++ else ++ goto <bb 4> ++ ++ <bb 3> ++ # _tmp3 = PHI <0 (bb 1), _tmp2 (bb 2)> ++ {BODY} ++ if (cond) ++ goto <bb 2> ++ else ++ goto <bb 4> ++ ++ <bb 4> ++ ... ++ */ ++ if (hset->contains (result)) ++ { ++ return false; ++ } ++ ++ return check_uses (result, hset); ++} ++ ++/* Check the use of SSA_NAME, it should only be used in comparison ++ operation and PHI node. HSET is used to record the ssa_names ++ that have been already checked. */ ++static bool ++check_uses (tree ssa_name, hash_set<tree> *hset) ++{ ++ imm_use_iterator imm_iter; ++ use_operand_p use_p; ++ ++ if (TREE_CODE (ssa_name) != SSA_NAME) ++ { ++ return false; ++ } ++ ++ if (SSA_NAME_VAR (ssa_name) ++ && is_global_var (SSA_NAME_VAR (ssa_name))) ++ { ++ return false; ++ } ++ ++ hset->add (ssa_name); ++ ++ FOR_EACH_IMM_USE_FAST (use_p, imm_iter, ssa_name) ++ { ++ gimple *stmt = USE_STMT (use_p); ++ ++ /* Ignore debug gimple statements. */ ++ if (is_gimple_debug (stmt)) ++ { ++ continue; ++ } ++ ++ switch (gimple_code (stmt)) ++ { ++ case GIMPLE_COND: ++ if (!check_uses_cond (ssa_name, stmt, hset)) ++ { ++ return false; ++ } ++ break; ++ ++ case GIMPLE_ASSIGN: ++ if (!check_uses_assign (ssa_name, stmt, hset)) ++ { ++ return false; ++ } ++ break; ++ ++ case GIMPLE_PHI: ++ if (!check_uses_phi (ssa_name, stmt, hset)) ++ { ++ return false; ++ } ++ break; ++ ++ default: ++ return false; ++ } ++ } ++ return true; ++} ++ ++static bool ++check_def_gimple (gimple *def1, gimple *def2, const_tree result) ++{ ++ /* def1 and def2 should be POINTER_PLUS_EXPR. */ ++ if (!is_gimple_assign (def1) || !is_gimple_assign (def2) ++ || gimple_assign_rhs_code (def1) != POINTER_PLUS_EXPR ++ || gimple_assign_rhs_code (def2) != POINTER_PLUS_EXPR) ++ { ++ return false; ++ } ++ ++ tree rhs12 = gimple_assign_rhs2 (def1); ++ ++ tree rhs21 = gimple_assign_rhs1 (def2); ++ tree rhs22 = gimple_assign_rhs2 (def2); ++ ++ if (rhs21 != result) ++ { ++ return false; ++ } ++ ++ /* We should have a positive pointer-plus constant to ensure ++ that the pointer value is continuously increasing. */ ++ if (TREE_CODE (rhs12) != INTEGER_CST || TREE_CODE (rhs22) != INTEGER_CST ++ || compare_tree_int (rhs12, 0) <= 0 || compare_tree_int (rhs22, 0) <= 0) ++ { ++ return false; ++ } ++ ++ return true; ++} ++ ++static bool ++check_loop_body (basic_block bb0, basic_block bb2, const_tree result) ++{ ++ gimple *g01 = first_stmt (bb0); ++ if (!g01 || !is_gimple_assign (g01) ++ || gimple_assign_rhs_code (g01) != MEM_REF ++ || TREE_OPERAND (gimple_assign_rhs1 (g01), 0) != result) ++ { ++ return false; ++ } ++ ++ gimple *g02 = g01->next; ++ /* GIMPLE_COND would be the last gimple in a basic block, ++ and have no other side effects on RESULT. */ ++ if (!g02 || gimple_code (g02) != GIMPLE_COND) ++ { ++ return false; ++ } ++ ++ if (first_stmt (bb2) != last_stmt (bb2)) ++ { ++ return false; ++ } ++ ++ return true; ++} ++ ++/* Pattern is like ++ <pre bb> ++ arg1 = base (rhs11) + cst (rhs12); [def1] ++ goto <bb 0> ++ ++ <bb 2> ++ arg2 = result (rhs21) + cst (rhs22); [def2] ++ ++ <bb 0> ++ # result = PHI <arg1 (pre bb), arg2 (bb 2)> ++ _v = *result; [g01] ++ if (_v == 0) [g02] ++ goto <bb 1> ++ else ++ goto <bb 2> ++ ++ <bb 1> ++ _1 = result - base; [g1] ++ _2 = _1 /[ex] cst; [g2] ++ _3 = (unsigned int) _2; [g3] ++ if (_3 == 0) ++ ... ++*/ ++static bool ++check_bb_order (basic_block bb0, basic_block &bb1, basic_block &bb2, ++ gphi *phi_stmt, gimple *&output) ++{ ++ /* Start check from PHI node in BB0. */ ++ if (gimple_phi_num_args (phi_stmt) != 2 ++ || virtual_operand_p (gimple_phi_result (phi_stmt))) ++ { ++ return false; ++ } ++ ++ tree result = gimple_phi_result (phi_stmt); ++ tree arg1 = gimple_phi_arg_def (phi_stmt, 0); ++ tree arg2 = gimple_phi_arg_def (phi_stmt, 1); ++ ++ if (TREE_CODE (arg1) != SSA_NAME ++ || TREE_CODE (arg2) != SSA_NAME ++ || SSA_NAME_IS_DEFAULT_DEF (arg1) ++ || SSA_NAME_IS_DEFAULT_DEF (arg2)) ++ { ++ return false; ++ } ++ ++ gimple *def1 = SSA_NAME_DEF_STMT (arg1); ++ gimple *def2 = SSA_NAME_DEF_STMT (arg2); ++ ++ /* Swap bb1 and bb2 if pattern is like ++ if (_v != 0) ++ goto <bb 2> ++ else ++ goto <bb 1> ++ */ ++ if (gimple_bb (def2) == bb1 && EDGE_SUCC (bb1, 0)->dest == bb0) ++ { ++ std::swap (bb1, bb2); ++ } ++ ++ /* prebb[def1] --> bb0 <-- bb2[def2] */ ++ if (!gimple_bb (def1) ++ || EDGE_SUCC (gimple_bb (def1), 0)->dest != bb0 ++ || gimple_bb (def2) != bb2 || EDGE_SUCC (bb2, 0)->dest != bb0) ++ { ++ return false; ++ } ++ ++ /* Check whether define gimple meets the pattern requirements. */ ++ if (!check_def_gimple (def1, def2, result)) ++ { ++ return false; ++ } ++ ++ if (!check_loop_body (bb0, bb2, result)) ++ { ++ return false; ++ } ++ ++ output = def1; ++ return true; ++} ++ ++/* Check pattern ++ <bb 1> ++ _1 = result - base; [g1] ++ _2 = _1 /[ex] cst; [g2] ++ _3 = (unsigned int) _2; [g3] ++ if (_3 == 0) ++ ... ++*/ ++static bool ++check_gimple_order (basic_block bb1, const_tree base, const_tree cst, ++ const_tree result, gimple *&output) ++{ ++ gimple *g1 = first_stmt (bb1); ++ if (!g1 || !is_gimple_assign (g1) ++ || gimple_assign_rhs_code (g1) != POINTER_DIFF_EXPR ++ || gimple_assign_rhs1 (g1) != result ++ || gimple_assign_rhs2 (g1) != base) ++ { ++ return false; ++ } ++ ++ gimple *g2 = g1->next; ++ if (!g2 || !is_gimple_assign (g2) ++ || gimple_assign_rhs_code (g2) != EXACT_DIV_EXPR ++ || gimple_assign_lhs (g1) != gimple_assign_rhs1 (g2) ++ || TREE_CODE (gimple_assign_rhs2 (g2)) != INTEGER_CST) ++ { ++ return false; ++ } ++ ++ /* INTEGER_CST cst in gimple def1. */ ++ HOST_WIDE_INT num1 = TREE_INT_CST_LOW (cst); ++ /* INTEGER_CST cst in gimple g2. */ ++ HOST_WIDE_INT num2 = TREE_INT_CST_LOW (gimple_assign_rhs2 (g2)); ++ /* _2 must be at least a positive number. */ ++ if (num2 == 0 || num1 / num2 <= 0) ++ { ++ return false; ++ } ++ ++ gimple *g3 = g2->next; ++ if (!g3 || !is_gimple_assign (g3) ++ || gimple_assign_rhs_code (g3) != NOP_EXPR ++ || gimple_assign_lhs (g2) != gimple_assign_rhs1 (g3) ++ || TREE_CODE (gimple_assign_lhs (g3)) != SSA_NAME) ++ { ++ return false; ++ } ++ ++ /* _3 should only be used in comparison operation or PHI node. */ ++ hash_set<tree> *hset = new hash_set<tree>; ++ if (!check_uses (gimple_assign_lhs (g3), hset)) ++ { ++ delete hset; ++ return false; ++ } ++ delete hset; ++ ++ output = g3; ++ return true; ++} ++ ++static bool ++do_phiopt_pattern (basic_block bb0, basic_block bb1, basic_block bb2) ++{ ++ gphi_iterator gsi; ++ ++ for (gsi = gsi_start_phis (bb0); !gsi_end_p (gsi); gsi_next (&gsi)) ++ { ++ gphi *phi_stmt = gsi.phi (); ++ gimple *def1 = NULL; ++ tree base, cst, result; ++ ++ if (!check_bb_order (bb0, bb1, bb2, phi_stmt, def1)) ++ { ++ continue; ++ } ++ ++ base = gimple_assign_rhs1 (def1); ++ cst = gimple_assign_rhs2 (def1); ++ result = gimple_phi_result (phi_stmt); ++ ++ gimple *stmt = NULL; ++ if (!check_gimple_order (bb1, base, cst, result, stmt)) ++ { ++ continue; ++ } ++ ++ gcc_assert (stmt); ++ ++ if (dump_file && (dump_flags & TDF_DETAILS)) ++ { ++ fprintf (dump_file, "PHIOPT pattern optimization (1) - Rewrite:\n"); ++ print_gimple_stmt (dump_file, stmt, 0); ++ fprintf (dump_file, "to\n"); ++ } ++ ++ /* Rewrite statement ++ _3 = (unsigned int) _2; ++ to ++ _3 = (unsigned int) 1; ++ */ ++ tree type = TREE_TYPE (gimple_assign_rhs1 (stmt)); ++ gimple_assign_set_rhs1 (stmt, build_int_cst (type, 1)); ++ update_stmt (stmt); ++ ++ if (dump_file && (dump_flags & TDF_DETAILS)) ++ { ++ print_gimple_stmt (dump_file, stmt, 0); ++ fprintf (dump_file, "\n"); ++ } ++ ++ return true; ++ } ++ return false; ++} ++ + /* Determine whether we should attempt to hoist adjacent loads out of + diamond patterns in pass_phiopt. Always hoist loads if + -fhoist-adjacent-loads is specified and the target machine has +-- +2.21.0.windows.1 + |