diff options
author | Jakub Jelinek <jakub@redhat.com> | 2017-11-15 10:01:42 +0100 |
---|---|---|
committer | Jakub Jelinek <jakub@gcc.gnu.org> | 2017-11-15 10:01:42 +0100 |
commit | 0b99f25372431148e637b98f02e7cbe03feef7f9 (patch) | |
tree | 63d30374d3cf9bdc2b71e04db9d013a8c15c009c | |
parent | 65205a116a8d1c076f0709d92acad2453caa1efb (diff) | |
download | gcc-0b99f25372431148e637b98f02e7cbe03feef7f9.tar.gz |
re PR target/82981 (unnecessary __multi3 call for mips64r6 linux kernel)
PR target/82981
* internal-fn.c: Include gimple-ssa.h, tree-phinodes.h and
ssa-iterators.h.
(can_widen_mult_without_libcall): New function.
(expand_mul_overflow): If only checking unsigned mul overflow,
not result, and can do efficiently MULT_HIGHPART_EXPR, emit that.
Don't use WIDEN_MULT_EXPR if it would involve a libcall, unless
no other way works. Add MULT_HIGHPART_EXPR + MULT_EXPR support.
(expand_DIVMOD): Formatting fix.
* expmed.h (expand_mult): Add NO_LIBCALL argument.
* expmed.c (expand_mult): Likewise. Use OPTAB_WIDEN rather
than OPTAB_LIB_WIDEN if NO_LIBCALL is true, and allow it to fail.
* gcc.target/mips/pr82981.c: New test.
From-SVN: r254758
-rw-r--r-- | gcc/ChangeLog | 13 | ||||
-rw-r--r-- | gcc/expmed.c | 10 | ||||
-rw-r--r-- | gcc/expmed.h | 2 | ||||
-rw-r--r-- | gcc/internal-fn.c | 90 | ||||
-rw-r--r-- | gcc/testsuite/ChangeLog | 5 | ||||
-rw-r--r-- | gcc/testsuite/gcc.target/mips/pr82981.c | 13 |
6 files changed, 126 insertions, 7 deletions
diff --git a/gcc/ChangeLog b/gcc/ChangeLog index fd72410e186..277c5578707 100644 --- a/gcc/ChangeLog +++ b/gcc/ChangeLog @@ -1,5 +1,18 @@ 2017-11-15 Jakub Jelinek <jakub@redhat.com> + PR target/82981 + * internal-fn.c: Include gimple-ssa.h, tree-phinodes.h and + ssa-iterators.h. + (can_widen_mult_without_libcall): New function. + (expand_mul_overflow): If only checking unsigned mul overflow, + not result, and can do efficiently MULT_HIGHPART_EXPR, emit that. + Don't use WIDEN_MULT_EXPR if it would involve a libcall, unless + no other way works. Add MULT_HIGHPART_EXPR + MULT_EXPR support. + (expand_DIVMOD): Formatting fix. + * expmed.h (expand_mult): Add NO_LIBCALL argument. + * expmed.c (expand_mult): Likewise. Use OPTAB_WIDEN rather + than OPTAB_LIB_WIDEN if NO_LIBCALL is true, and allow it to fail. + PR tree-optimization/82977 * tree-ssa-strlen.c (strlen_optimize_stmt): Pass a reference to a copy constructed temporary to strlen_to_stridx.put. diff --git a/gcc/expmed.c b/gcc/expmed.c index 8e9f15d6a65..dacb2b9771a 100644 --- a/gcc/expmed.c +++ b/gcc/expmed.c @@ -3284,7 +3284,7 @@ expand_mult_const (machine_mode mode, rtx op0, HOST_WIDE_INT val, rtx expand_mult (machine_mode mode, rtx op0, rtx op1, rtx target, - int unsignedp) + int unsignedp, bool no_libcall) { enum mult_variant variant; struct algorithm algorithm; @@ -3420,14 +3420,16 @@ expand_mult (machine_mode mode, rtx op0, rtx op1, rtx target, { op0 = force_reg (GET_MODE (op0), op0); return expand_binop (mode, add_optab, op0, op0, - target, unsignedp, OPTAB_LIB_WIDEN); + target, unsignedp, + no_libcall ? OPTAB_WIDEN : OPTAB_LIB_WIDEN); } /* This used to use umul_optab if unsigned, but for non-widening multiply there is no difference between signed and unsigned. */ op0 = expand_binop (mode, do_trapv ? smulv_optab : smul_optab, - op0, op1, target, unsignedp, OPTAB_LIB_WIDEN); - gcc_assert (op0); + op0, op1, target, unsignedp, + no_libcall ? OPTAB_WIDEN : OPTAB_LIB_WIDEN); + gcc_assert (op0 || no_libcall); return op0; } diff --git a/gcc/expmed.h b/gcc/expmed.h index 712c7715ec8..6b57ec634d5 100644 --- a/gcc/expmed.h +++ b/gcc/expmed.h @@ -727,7 +727,7 @@ extern rtx extract_bit_field (rtx, unsigned HOST_WIDE_INT, unsigned HOST_WIDE_INT, int, rtx, machine_mode, machine_mode, bool, rtx *); extern rtx extract_low_bits (machine_mode, machine_mode, rtx); -extern rtx expand_mult (machine_mode, rtx, rtx, rtx, int); +extern rtx expand_mult (machine_mode, rtx, rtx, rtx, int, bool = false); extern rtx expand_mult_highpart_adjust (scalar_int_mode, rtx, rtx, rtx, rtx, int); diff --git a/gcc/internal-fn.c b/gcc/internal-fn.c index 94873c2948a..1c00792fec7 100644 --- a/gcc/internal-fn.c +++ b/gcc/internal-fn.c @@ -46,6 +46,9 @@ along with GCC; see the file COPYING3. If not see #include "recog.h" #include "builtins.h" #include "optabs-tree.h" +#include "gimple-ssa.h" +#include "tree-phinodes.h" +#include "ssa-iterators.h" /* The names of each internal function, indexed by function number. */ const char *const internal_fn_name_array[] = { @@ -1172,6 +1175,35 @@ expand_neg_overflow (location_t loc, tree lhs, tree arg1, bool is_ubsan, } } +/* Return true if UNS WIDEN_MULT_EXPR with result mode WMODE and operand + mode MODE can be expanded without using a libcall. */ + +static bool +can_widen_mult_without_libcall (scalar_int_mode wmode, scalar_int_mode mode, + rtx op0, rtx op1, bool uns) +{ + if (find_widening_optab_handler (umul_widen_optab, wmode, mode) + != CODE_FOR_nothing) + return true; + + if (find_widening_optab_handler (smul_widen_optab, wmode, mode) + != CODE_FOR_nothing) + return true; + + rtx_insn *last = get_last_insn (); + if (CONSTANT_P (op0)) + op0 = convert_modes (wmode, mode, op0, uns); + else + op0 = gen_raw_REG (wmode, LAST_VIRTUAL_REGISTER + 1); + if (CONSTANT_P (op1)) + op1 = convert_modes (wmode, mode, op1, uns); + else + op1 = gen_raw_REG (wmode, LAST_VIRTUAL_REGISTER + 2); + rtx ret = expand_mult (wmode, op0, op1, NULL_RTX, uns, true); + delete_insns_since (last); + return ret != NULL_RTX; +} + /* Add mul overflow checking to the statement STMT. */ static void @@ -1465,9 +1497,29 @@ expand_mul_overflow (location_t loc, tree lhs, tree arg0, tree arg1, ops.op1 = make_tree (type, op1); ops.op2 = NULL_TREE; ops.location = loc; + + /* Optimize unsigned overflow check where we don't use the + multiplication result, just whether overflow happened. + If we can do MULT_HIGHPART_EXPR, that followed by + comparison of the result against zero is cheapest. + We'll still compute res, but it should be DCEd later. */ + use_operand_p use; + gimple *use_stmt; + if (!is_ubsan + && lhs + && uns + && !(uns0_p && uns1_p && !unsr_p) + && can_mult_highpart_p (mode, uns) == 1 + && single_imm_use (lhs, &use, &use_stmt) + && is_gimple_assign (use_stmt) + && gimple_assign_rhs_code (use_stmt) == IMAGPART_EXPR) + goto highpart; + if (GET_MODE_2XWIDER_MODE (mode).exists (&wmode) - && targetm.scalar_mode_supported_p (wmode)) + && targetm.scalar_mode_supported_p (wmode) + && can_widen_mult_without_libcall (wmode, mode, op0, op1, uns)) { + twoxwider: ops.code = WIDEN_MULT_EXPR; ops.type = build_nonstandard_integer_type (GET_MODE_PRECISION (wmode), uns); @@ -1495,6 +1547,35 @@ expand_mul_overflow (location_t loc, tree lhs, tree arg0, tree arg1, profile_probability::very_likely ()); } } + else if (can_mult_highpart_p (mode, uns) == 1) + { + highpart: + ops.code = MULT_HIGHPART_EXPR; + ops.type = type; + + rtx hipart = expand_expr_real_2 (&ops, NULL_RTX, mode, + EXPAND_NORMAL); + ops.code = MULT_EXPR; + res = expand_expr_real_2 (&ops, NULL_RTX, mode, EXPAND_NORMAL); + if (uns) + /* For the unsigned multiplication, there was overflow if + HIPART is non-zero. */ + do_compare_rtx_and_jump (hipart, const0_rtx, EQ, true, mode, + NULL_RTX, NULL, done_label, + profile_probability::very_likely ()); + else + { + rtx signbit = expand_shift (RSHIFT_EXPR, mode, res, prec - 1, + NULL_RTX, 0); + /* RES is low half of the double width result, HIPART + the high half. There was overflow if + HIPART is different from RES < 0 ? -1 : 0. */ + do_compare_rtx_and_jump (signbit, hipart, EQ, true, mode, + NULL_RTX, NULL, done_label, + profile_probability::very_likely ()); + } + + } else if (int_mode_for_size (prec / 2, 1).exists (&hmode) && 2 * GET_MODE_PRECISION (hmode) == prec) { @@ -1800,6 +1881,11 @@ expand_mul_overflow (location_t loc, tree lhs, tree arg0, tree arg1, tem = expand_expr_real_2 (&ops, NULL_RTX, mode, EXPAND_NORMAL); emit_move_insn (res, tem); } + else if (GET_MODE_2XWIDER_MODE (mode).exists (&wmode) + && targetm.scalar_mode_supported_p (wmode)) + /* Even emitting a libcall is better than not detecting overflow + at all. */ + goto twoxwider; else { gcc_assert (!is_ubsan); @@ -2588,7 +2674,7 @@ expand_DIVMOD (internal_fn, gcall *call_stmt) expand_expr (build2 (COMPLEX_EXPR, TREE_TYPE (lhs), make_tree (TREE_TYPE (arg0), quotient), make_tree (TREE_TYPE (arg1), remainder)), - target, VOIDmode, EXPAND_NORMAL); + target, VOIDmode, EXPAND_NORMAL); } /* Expand a call to FN using the operands in STMT. FN has a single diff --git a/gcc/testsuite/ChangeLog b/gcc/testsuite/ChangeLog index fd2b5d049ea..e006115e8fc 100644 --- a/gcc/testsuite/ChangeLog +++ b/gcc/testsuite/ChangeLog @@ -1,3 +1,8 @@ +2017-11-15 Jakub Jelinek <jakub@redhat.com> + + PR target/82981 + * gcc.target/mips/pr82981.c: New test. + 2017-11-15 Martin Liska <mliska@suse.cz> * g++.dg/ubsan/vptr-12.C: New test. diff --git a/gcc/testsuite/gcc.target/mips/pr82981.c b/gcc/testsuite/gcc.target/mips/pr82981.c new file mode 100644 index 00000000000..677e4cc01e9 --- /dev/null +++ b/gcc/testsuite/gcc.target/mips/pr82981.c @@ -0,0 +1,13 @@ +/* PR target/82981 */ +/* { dg-do compile } */ +/* { dg-skip-if "" { *-*-* } { "*" } { "-O2" } } */ +/* { dg-options "-march=mips64r6 -mabi=64 -mexplicit-relocs" } */ + +unsigned long +func (unsigned long a, unsigned long b) +{ + return a > (~0UL) / b; +} + +/* { dg-final { scan-assembler-not "__multi3" } } */ +/* { dg-final { scan-assembler "\tdmuhu" } } */ |