Advertisement
Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- diff --git a/gcc/config/arm/arm.c b/gcc/config/arm/arm.c
- index fa4e083..27511bc 100644
- --- a/gcc/config/arm/arm.c
- +++ b/gcc/config/arm/arm.c
- @@ -309,6 +309,9 @@ static void arm_canonicalize_comparison (int *code, rtx *op0, rtx *op1,
- static unsigned HOST_WIDE_INT arm_asan_shadow_offset (void);
- static void arm_sched_fusion_priority (rtx_insn *, int, int *, int*);
- +
- +static bool arm_combine_divmod(enum machine_mode mode);
- +
- /* Table of machine attributes. */
- static const struct attribute_spec arm_attribute_table[] =
- @@ -735,6 +738,9 @@ static const struct attribute_spec arm_attribute_table[] =
- #undef TARGET_SCHED_FUSION_PRIORITY
- #define TARGET_SCHED_FUSION_PRIORITY arm_sched_fusion_priority
- +#undef TARGET_COMBINE_DIVMOD
- +#define TARGET_COMBINE_DIVMOD arm_combine_divmod
- +
- struct gcc_target targetm = TARGET_INITIALIZER;
- /* Obstack for minipool constant handling. */
- @@ -29781,4 +29787,14 @@ arm_sched_fusion_priority (rtx_insn *insn, int max_pri,
- *pri = tmp;
- return;
- }
- +
- +/* Implement TARGET_COMBINE_DIVMOD hook. */
- +
- +static bool
- +arm_combine_divmod (enum machine_mode mode ATTRIBUTE_UNUSED)
- +{
- + /// ??? return true for all modes ?
- + return true;
- +}
- +
- #include "gt-arm.h"
- diff --git a/gcc/doc/tm.texi b/gcc/doc/tm.texi
- index d548d96..8cd0500 100644
- --- a/gcc/doc/tm.texi
- +++ b/gcc/doc/tm.texi
- @@ -11541,6 +11541,10 @@ The support includes the assembler, linker and dynamic linker.
- The default value of this hook is based on target's libc.
- @end deftypefn
- +@deftypefn {Target Hook} bool TARGET_COMBINE_DIVMOD (enum machine_mode @var{mode})
- +This target hook returns @code{true} if the target provides divmod libcall operation for the machine mode @var{mode} and must be used to combine integer division and modulus operations. Return @code{false} otherwise.
- +@end deftypefn
- +
- @deftypefn {Target Hook} {unsigned int} TARGET_ATOMIC_ALIGN_FOR_MODE (machine_mode @var{mode})
- If defined, this function returns an appropriate alignment in bits for an atomic object of machine_mode @var{mode}. If 0 is returned then the default alignment for the specified mode is used.
- @end deftypefn
- diff --git a/gcc/doc/tm.texi.in b/gcc/doc/tm.texi.in
- index 9bef4a5..ea207ef 100644
- --- a/gcc/doc/tm.texi.in
- +++ b/gcc/doc/tm.texi.in
- @@ -8193,6 +8193,8 @@ and the associated definitions of those functions.
- @hook TARGET_ATOMIC_TEST_AND_SET_TRUEVAL
- +@hook TARGET_COMBINE_DIVMOD
- +
- @hook TARGET_HAS_IFUNC_P
- @hook TARGET_ATOMIC_ALIGN_FOR_MODE
- diff --git a/gcc/internal-fn.c b/gcc/internal-fn.c
- index e785946..cf28011 100644
- --- a/gcc/internal-fn.c
- +++ b/gcc/internal-fn.c
- @@ -1942,6 +1942,56 @@ expand_VA_ARG (gcall *stmt ATTRIBUTE_UNUSED)
- gcc_unreachable ();
- }
- +static void
- +expand_DIVMOD (gcall *stmt)
- +{
- + tree type, lhs, arg0, arg1;
- + rtx op0, op1, res0, res1, target;
- + enum machine_mode mode, compute_mode;
- + rtx libval;
- + rtx libfunc = NULL_RTX;
- + bool is_unsigned;
- +
- + lhs = gimple_call_lhs (stmt);
- + arg0 = gimple_call_arg (stmt, 0);
- + arg1 = gimple_call_arg (stmt, 1);
- + mode = TYPE_MODE (TREE_TYPE (arg0));
- + is_unsigned = TYPE_UNSIGNED (TREE_TYPE (arg0));
- +
- + op0 = expand_expr (arg0, NULL_RTX, VOIDmode, EXPAND_NORMAL);
- + op1 = expand_expr (arg1, NULL_RTX, VOIDmode, EXPAND_NORMAL);
- + target = expand_expr (lhs, NULL_RTX, VOIDmode, EXPAND_WRITE);
- +
- + for (compute_mode = mode; compute_mode != VOIDmode; compute_mode = GET_MODE_WIDER_MODE (compute_mode))
- + if (optab_libfunc (is_unsigned ? udivmod_optab : sdivmod_optab, compute_mode))
- + break;
- +
- + if (compute_mode != mode)
- + {
- + op0 = convert_modes (compute_mode, mode, op0, is_unsigned);
- + op1 = convert_modes (compute_mode, mode, op1, is_unsigned);
- + }
- +
- + /* Get the libcall */
- + libfunc = optab_libfunc (is_unsigned ? udivmod_optab : sdivmod_optab, compute_mode);
- + gcc_assert (libfunc);
- +
- + machine_mode libval_mode = smallest_mode_for_size (2 * GET_MODE_BITSIZE (compute_mode), MODE_INT);
- + libval = emit_library_call_value (libfunc, NULL_RTX, LCT_CONST, libval_mode, 2,
- + op0, compute_mode, op1, compute_mode);
- +
- + /* Get quotient and remainder into two registers. */
- + res0 = simplify_gen_subreg (mode, libval, libval_mode, 0);
- + res1 = simplify_gen_subreg (mode, libval, libval_mode, GET_MODE_SIZE (compute_mode));
- +
- + /* Now build the complex integer target. */
- + expand_expr (build2 (COMPLEX_EXPR, TREE_TYPE (lhs),
- + make_tree (TREE_TYPE (arg0), res0),
- + make_tree (TREE_TYPE (arg1), res1)),
- + target, VOIDmode, EXPAND_NORMAL);
- +}
- +
- +
- /* Routines to expand each internal function, indexed by function number.
- Each routine has the prototype:
- diff --git a/gcc/internal-fn.def b/gcc/internal-fn.def
- index ba5c2c1..c9363cc 100644
- --- a/gcc/internal-fn.def
- +++ b/gcc/internal-fn.def
- @@ -63,3 +63,4 @@ DEF_INTERNAL_FN (SUB_OVERFLOW, ECF_CONST | ECF_LEAF | ECF_NOTHROW, NULL)
- DEF_INTERNAL_FN (MUL_OVERFLOW, ECF_CONST | ECF_LEAF | ECF_NOTHROW, NULL)
- DEF_INTERNAL_FN (TSAN_FUNC_EXIT, ECF_NOVOPS | ECF_LEAF | ECF_NOTHROW, NULL)
- DEF_INTERNAL_FN (VA_ARG, ECF_NOTHROW | ECF_LEAF, NULL)
- +DEF_INTERNAL_FN (DIVMOD, ECF_CONST | ECF_LEAF, NULL)
- diff --git a/gcc/passes.def b/gcc/passes.def
- index 64fc4d9..315484d 100644
- --- a/gcc/passes.def
- +++ b/gcc/passes.def
- @@ -271,6 +271,7 @@ along with GCC; see the file COPYING3. If not see
- NEXT_PASS (pass_simduid_cleanup);
- NEXT_PASS (pass_lower_vector_ssa);
- NEXT_PASS (pass_cse_reciprocals);
- + NEXT_PASS (pass_cse_divmod);
- NEXT_PASS (pass_reassoc);
- NEXT_PASS (pass_strength_reduction);
- NEXT_PASS (pass_tracer);
- diff --git a/gcc/target.def b/gcc/target.def
- index aa5a1f1..ead638d 100644
- --- a/gcc/target.def
- +++ b/gcc/target.def
- @@ -4875,6 +4875,16 @@ Normally, this is not needed.",
- bool, (const_tree field, machine_mode mode),
- default_member_type_forces_blk)
- +/* True if div and mod operations for MODE should be combined. */
- +DEFHOOK
- +(combine_divmod,
- + "This target hook returns @code{true} if the target provides divmod libcall\
- + operation for the machine mode @var{mode} and must be used to combine\
- + integer division and modulus operations. Return @code{false} otherwise.",
- + bool, (enum machine_mode mode),
- + default_combine_divmod)
- +
- +
- /* Return the class for a secondary reload, and fill in extra information. */
- DEFHOOK
- (secondary_reload,
- diff --git a/gcc/targhooks.c b/gcc/targhooks.c
- index 7238c8f..907338e 100644
- --- a/gcc/targhooks.c
- +++ b/gcc/targhooks.c
- @@ -1922,4 +1922,9 @@ can_use_doloop_if_innermost (const widest_int &, const widest_int &,
- return loop_depth == 1;
- }
- +bool default_combine_divmod (enum machine_mode mode ATTRIBUTE_UNUSED)
- +{
- + return false;
- +}
- +
- #include "gt-targhooks.h"
- diff --git a/gcc/targhooks.h b/gcc/targhooks.h
- index 5ae991d..8275e0e 100644
- --- a/gcc/targhooks.h
- +++ b/gcc/targhooks.h
- @@ -240,4 +240,7 @@ extern void default_setup_incoming_vararg_bounds (cumulative_args_t ca ATTRIBUTE
- tree type ATTRIBUTE_UNUSED,
- int *pretend_arg_size ATTRIBUTE_UNUSED,
- int second_time ATTRIBUTE_UNUSED);
- +
- +extern bool default_combine_divmod (enum machine_mode);
- +
- #endif /* GCC_TARGHOOKS_H */
- diff --git a/gcc/tree-pass.h b/gcc/tree-pass.h
- index 7b66a1c..8e14950 100644
- --- a/gcc/tree-pass.h
- +++ b/gcc/tree-pass.h
- @@ -414,6 +414,7 @@ extern gimple_opt_pass *make_pass_early_warn_uninitialized (gcc::context *ctxt);
- extern gimple_opt_pass *make_pass_late_warn_uninitialized (gcc::context *ctxt);
- extern gimple_opt_pass *make_pass_cse_reciprocals (gcc::context *ctxt);
- extern gimple_opt_pass *make_pass_cse_sincos (gcc::context *ctxt);
- +extern gimple_opt_pass *make_pass_cse_divmod (gcc::context *ctxt);
- extern gimple_opt_pass *make_pass_optimize_bswap (gcc::context *ctxt);
- extern gimple_opt_pass *make_pass_optimize_widening_mul (gcc::context *ctxt);
- extern gimple_opt_pass *make_pass_warn_function_return (gcc::context *ctxt);
- diff --git a/gcc/tree-ssa-math-opts.c b/gcc/tree-ssa-math-opts.c
- index eae5358..095b836 100644
- --- a/gcc/tree-ssa-math-opts.c
- +++ b/gcc/tree-ssa-math-opts.c
- @@ -3643,3 +3643,254 @@ make_pass_optimize_widening_mul (gcc::context *ctxt)
- {
- return new pass_optimize_widening_mul (ctxt);
- }
- +
- +namespace {
- +
- +static struct
- +{
- + int divmod_calls_inserted;
- + int divmod_result_used;
- +} divmod_stats;
- +
- +
- +const pass_data pass_data_combine_divmod =
- +{
- + GIMPLE_PASS, /* type */
- + "divmod", /* name */
- + OPTGROUP_NONE, /* optinfo_flags */
- + TV_NONE, /* tv_id */
- + PROP_ssa, /* properties_required */
- + 0, /* properties_provided */
- + 0, /* properties_destroyed */
- + 0, /* todo_flags_start */
- + TODO_update_ssa, /* todo_flags_finish */
- +};
- +
- +class pass_cse_divmod : public gimple_opt_pass
- +{
- +public:
- + pass_cse_divmod (gcc::context *ctxt)
- + : gimple_opt_pass (pass_data_combine_divmod, ctxt)
- + {}
- +
- + /* opt_pass methods: */
- + virtual bool gate (function *)
- + {
- + return optimize;
- + }
- +
- + virtual unsigned int execute (function *);
- +
- +private:
- + static bool execute_cse_divmod_1 (gimple);
- + static bool maybe_record_stmt (vec<gimple> *stmts, basic_block *top_bb, gimple use_stmt)
- + {
- + stmts->safe_push (use_stmt);
- + basic_block use_bb = gimple_bb (use_stmt);
- + return true;
- + }
- +
- +}; // class pass_cse_divmod
- +
- +/* Look for div and mod statements with the same operands and
- + create a library call if needed. We first walk over all
- + immediate uses of one of the operand looking for matched statement
- + that we can combine. In a second pass replace the statement with
- + a library calls and statements with the results from library call. */
- +
- +bool
- +pass_cse_divmod::execute_cse_divmod_1 (gimple stmt)
- +{
- + gimple_stmt_iterator gsi;
- + imm_use_iterator use_iter;
- + gimple use_stmt;
- + bool found = false;
- + vec<gimple> stmts = vNULL;
- + basic_block top_bb = NULL;
- + bool cfg_changed = false;
- + int i;
- + tree type, op1, op2;
- +
- + op1 = gimple_assign_rhs1 (stmt);
- + op2 = gimple_assign_rhs2 (stmt);
- + type = TREE_TYPE (op1);
- +
- + /* Skip if both the operands and constant. */
- + if (TREE_CODE (op1) == INTEGER_CST && TREE_CODE (op2) == INTEGER_CST)
- + return false;
- +
- + /* Skip if the target doesnt support or require it. */
- + if (!targetm.combine_divmod (TYPE_MODE (type)))
- + return false;
- +
- + stmts.safe_push (stmt);
- + top_bb = gimple_bb (stmt);
- +
- + /* look fpr a TRUNC_MOD_EXPR coresponding to stmt
- + TRUNC_DIV_EXPR s to combine. */
- + FOR_EACH_IMM_USE_STMT (use_stmt, use_iter, (TREE_CODE (op2) == SSA_NAME) ? op2 : op1)
- + {
- + if (is_gimple_assign (use_stmt)
- + && (gimple_assign_rhs_code (use_stmt) == TRUNC_MOD_EXPR))
- + {
- + tree rhs1 = gimple_assign_rhs1 (use_stmt);
- + tree rhs2 = gimple_assign_rhs2 (use_stmt);
- +
- + if ((rhs1 == op1 && rhs2 == op2)
- + ||(rhs1 == op1 && rhs2 == op2))
- + found |= maybe_record_stmt (&stmts, &top_bb, use_stmt);
- + }
- + }
- +
- + /* If we have matched instructions to combine, try creatiing Library
- + call and use the results. */
- + if (found)
- + {
- + gimple def_stmt1 = NULL, def_stmt2 = NULL;
- + bool insert_after_op1_def, insert_after_op2_def;
- + tree res, rhs;
- + gimple assign_stmt, call_stmt;
- + tree return_type = build_complex_type (type);
- +
- + if (TREE_CODE (op1) == SSA_NAME)
- + def_stmt1 = SSA_NAME_DEF_STMT (op1);
- + if (TREE_CODE (op2) == SSA_NAME)
- + def_stmt2 = SSA_NAME_DEF_STMT (op2);
- +
- + /* Is the call to be insterted adter op1 define stmt. */
- + insert_after_op1_def = TREE_CODE (op1) == SSA_NAME
- + && !SSA_NAME_IS_DEFAULT_DEF (op1)
- + && gimple_code (def_stmt1) != GIMPLE_PHI
- + && gimple_bb (def_stmt1) == top_bb;
- +
- + /* Is the call to be insterted adter op2 define stmt. */
- + insert_after_op2_def = TREE_CODE (op2) == SSA_NAME
- + && !SSA_NAME_IS_DEFAULT_DEF (op2)
- + && gimple_code (def_stmt2) != GIMPLE_PHI
- + && gimple_bb (def_stmt2) == top_bb;
- +
- + /* Create a library call and instert it at the place idenified. */
- + call_stmt = gimple_build_call_internal (IFN_DIVMOD, 2, op1, op2);
- + res = make_temp_ssa_name (return_type,
- + call_stmt, "divmod");
- + gimple_call_set_lhs (call_stmt, res);
- + divmod_stats.divmod_calls_inserted++;
- +
- + /* Insert call at the beginning of top_bb but not earlier
- + than the name def statement. */
- + if (insert_after_op1_def || insert_after_op2_def)
- + {
- + if (insert_after_op1_def && insert_after_op2_def)
- + {
- + for (gsi = gsi_start_bb (top_bb);
- + !gsi_end_p (gsi); gsi_next (&gsi))
- + {
- + gimple g = gsi_stmt (gsi);
- + if (g == def_stmt1)
- + {
- + gsi = gsi_for_stmt (def_stmt2);
- + break;
- + }
- + else if (g == def_stmt2)
- + {
- + gsi = gsi_for_stmt (def_stmt1);
- + break;
- + }
- + }
- + }
- +
- + /* Only one of the definition is in the basic block, place after
- + that. */
- + else if (insert_after_op1_def)
- + gsi = gsi_for_stmt (def_stmt1);
- + else
- + gsi = gsi_for_stmt (def_stmt2);
- +
- + gsi_insert_after (&gsi, call_stmt, GSI_SAME_STMT);
- + }
- + else
- + {
- + /* op1 and op2 are defined before the top_bb. Insert as first
- + non label instruction. */
- + gsi = gsi_after_labels (top_bb);
- + gsi_insert_before (&gsi, call_stmt, GSI_SAME_STMT);
- + }
- +
- + /* Adjust the recorded old statements to use the results. */
- + for (i = 0; stmts.iterate (i, &use_stmt); ++i)
- + {
- + gsi = gsi_for_stmt (use_stmt);
- + divmod_stats.divmod_result_used++;
- +
- + switch (gimple_assign_rhs_code (use_stmt))
- + {
- + case TRUNC_MOD_EXPR:
- + /* Get the top 32 bit from result and assign it. */
- + rhs = fold_build1 (IMAGPART_EXPR, type, res);
- + break;
- +
- + case TRUNC_DIV_EXPR:
- + /* Get the bottom 32 bit from result and assign it. */
- + rhs = fold_build1 (REALPART_EXPR, type, res);
- + break;
- +
- + default:
- + gcc_unreachable ();
- + }
- +
- + /* Replace the statement with a copy. */
- + assign_stmt = gimple_build_assign (gimple_assign_lhs (use_stmt), rhs);
- + gsi_replace (&gsi, assign_stmt, true);
- +
- + if (gimple_purge_dead_eh_edges (gimple_bb (assign_stmt)))
- + cfg_changed = true;
- + }
- + }
- +
- + stmts.release ();
- + return cfg_changed;
- +}
- +
- +/* Go through all the floating-point SSA_NAMEs, and call
- + execute_cse_reciprocals_1 on each of them. */
- +unsigned
- +pass_cse_divmod::execute (function *fun)
- +{
- + basic_block bb;
- +
- + memset (&divmod_stats, 0, sizeof (divmod_stats));
- + calculate_dominance_info (CDI_DOMINATORS);
- + calculate_dominance_info (CDI_POST_DOMINATORS);
- + FOR_EACH_BB_FN (bb, fun)
- + {
- + gimple_stmt_iterator gsi;
- + for (gsi = gsi_after_labels (bb); !gsi_end_p (gsi); gsi_next (&gsi))
- + {
- + gimple stmt = gsi_stmt (gsi);
- +
- + if (is_gimple_assign (stmt)
- + && (TREE_CODE_CLASS (gimple_assign_rhs_code (stmt)) == tcc_binary)
- + && (gimple_assign_rhs_code (stmt) == TRUNC_DIV_EXPR))
- + {
- + pass_cse_divmod::execute_cse_divmod_1 (stmt);
- + }
- + }
- + }
- +
- + statistics_counter_event (cfun, "number of combined divmod calls inserted",
- + divmod_stats.divmod_calls_inserted);
- + statistics_counter_event (cfun, "number of instructions uses divmod result",
- + divmod_stats.divmod_result_used);
- +
- + free_dominance_info (CDI_DOMINATORS);
- + free_dominance_info (CDI_POST_DOMINATORS);
- + return 0;
- +}
- +
- +} // anon namespace
- +
- +gimple_opt_pass *
- +make_pass_cse_divmod (gcc::context *ctxt)
- +{
- + return new pass_cse_divmod (ctxt);
- +}
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement