re PR tree-optimization/43721 (Failure to optimise (a/b) and (a%b) into single __aeabi_idivmod call)

2016-10-28  Prathamesh Kulkarni  <prathamesh.kulkarni@linaro.org>
	    Kugan Vivekanandarajah  <kuganv@linaro.org>
	    Jim Wilson  <jim.wilson@linaro.org>

	PR tree-optimization/43721
	* target.def: New hook expand_divmod_libfunc.
	* doc/tm.texi.in: Add hook for TARGET_EXPAND_DIVMOD_LIBFUNC
	* doc/tm.texi: Regenerate.
	* internal-fn.def: Add new entry for DIVMOD ifn.
	* internal-fn.c (expand_DIVMOD): New.
	* tree-ssa-math-opts.c: Include optabs-libfuncs.h, tree-eh.h,
	targhooks.h.
	(widen_mul_stats): Add new field divmod_calls_inserted.
	(target_supports_divmod_p): New.
	(divmod_candidate_p): Likewise.
	(convert_to_divmod): Likewise.
	(pass_optimize_widening_mul::execute): Call
	calculate_dominance_info(), renumber_gimple_stmt_uids() at
	beginning of function. Call convert_to_divmod()
	and record stats for divmod.
	* config/arm/arm.c (arm_expand_divmod_libfunc): Override hook
	TARGET_EXPAND_DIVMOD_LIBFUNC.
	* doc/sourcebuild.texi: Add items for arm_divmod_simode, divmod,
	divmod_simode.

testsuite/
	* lib/target-supports.exp (check_effective_target_divmod): New.
	(check_effective_target_divmod_simode): Likewise.
	(check_effective_target_arm_divmod_simode): Likewise.
	* gcc.dg/divmod-1-simode.c: New test.
	* gcc.dg/divmod-1.c: Likewise.
	* gcc.dg/divmod-2-simode.c: Likewise.
	* gcc.dg/divmod-2.c: Likewise.
	* gcc.dg/divmod-3-simode.c: Likewise.
	* gcc.dg/divmod-3.c: Likewise.
	* gcc.dg/divmod-4-simode.c: Likewise.
	* gcc.dg/divmod-4.c: Likewise.
	* gcc.dg/divmod-5.c: Likewise.
	* gcc.dg/divmod-6-simode.c: Likewise.
	* gcc.dg/divmod-6.c: Likewise.
	* gcc.dg/divmod-7.c: Likewise.

Co-Authored-By: Jim Wilson <jim.wilson@linaro.org>
Co-Authored-By: Kugan Vivekanandarajah <kuganv@linaro.org>

From-SVN: r241660
This commit is contained in:
Prathamesh Kulkarni 2016-10-28 19:05:12 +00:00 committed by Prathamesh Kulkarni
parent 0dfeae289c
commit e72531b9cb
23 changed files with 735 additions and 0 deletions

View File

@ -1,3 +1,28 @@
2016-10-28 Prathamesh Kulkarni <prathamesh.kulkarni@linaro.org>
Kugan Vivekanandarajah <kuganv@linaro.org>
Jim Wilson <jim.wilson@linaro.org>
PR tree-optimization/43721
* target.def: New hook expand_divmod_libfunc.
* doc/tm.texi.in: Add hook for TARGET_EXPAND_DIVMOD_LIBFUNC
* doc/tm.texi: Regenerate.
* internal-fn.def: Add new entry for DIVMOD ifn.
* internal-fn.c (expand_DIVMOD): New.
* tree-ssa-math-opts.c: Include optabs-libfuncs.h, tree-eh.h,
targhooks.h.
(widen_mul_stats): Add new field divmod_calls_inserted.
(target_supports_divmod_p): New.
(divmod_candidate_p): Likewise.
(convert_to_divmod): Likewise.
(pass_optimize_widening_mul::execute): Call
calculate_dominance_info(), renumber_gimple_stmt_uids() at
beginning of function. Call convert_to_divmod()
and record stats for divmod.
* config/arm/arm.c (arm_expand_divmod_libfunc): Override hook
TARGET_EXPAND_DIVMOD_LIBFUNC.
* doc/sourcebuild.texi: Add items for arm_divmod_simode, divmod,
divmod_simode.
2016-10-28 Eric Botcazou <ebotcazou@adacore.com>
* dojump.c (do_jump_by_parts_greater_rtx): Invert probability when

View File

@ -62,6 +62,7 @@
#include "builtins.h"
#include "tm-constrs.h"
#include "rtl-iter.h"
#include "optabs-libfuncs.h"
/* This file should be included last. */
#include "target-def.h"
@ -304,6 +305,7 @@ static section *arm_function_section (tree, enum node_frequency, bool, bool);
static bool arm_asm_elf_flags_numeric (unsigned int flags, unsigned int *num);
static unsigned int arm_elf_section_type_flags (tree decl, const char *name,
int reloc);
static void arm_expand_divmod_libfunc (rtx, machine_mode, rtx, rtx, rtx *, rtx *);
/* Table of machine attributes. */
static const struct attribute_spec arm_attribute_table[] =
@ -739,6 +741,9 @@ static const struct attribute_spec arm_attribute_table[] =
#undef TARGET_SECTION_TYPE_FLAGS
#define TARGET_SECTION_TYPE_FLAGS arm_elf_section_type_flags
#undef TARGET_EXPAND_DIVMOD_LIBFUNC
#define TARGET_EXPAND_DIVMOD_LIBFUNC arm_expand_divmod_libfunc
struct gcc_target targetm = TARGET_INITIALIZER;
/* Obstack for minipool constant handling. */
@ -30845,4 +30850,33 @@ arm_elf_section_type_flags (tree decl, const char *name, int reloc)
return flags;
}
/* Generate call to __aeabi_[mode]divmod (op0, op1). */
static void
arm_expand_divmod_libfunc (rtx libfunc, machine_mode mode,
rtx op0, rtx op1,
rtx *quot_p, rtx *rem_p)
{
if (mode == SImode)
gcc_assert (!TARGET_IDIV);
machine_mode libval_mode = smallest_mode_for_size (2 * GET_MODE_BITSIZE (mode),
MODE_INT);
rtx libval = emit_library_call_value (libfunc, NULL_RTX, LCT_CONST,
libval_mode, 2,
op0, GET_MODE (op0),
op1, GET_MODE (op1));
rtx quotient = simplify_gen_subreg (mode, libval, libval_mode, 0);
rtx remainder = simplify_gen_subreg (mode, libval, libval_mode,
GET_MODE_SIZE (mode));
gcc_assert (quotient);
gcc_assert (remainder);
*quot_p = quotient;
*rem_p = remainder;
}
#include "gt-arm.h"

View File

@ -1675,6 +1675,10 @@ and @code{MOVT} instructions available.
ARM target generates Thumb-1 code for @code{-mthumb} with
@code{CBZ} and @code{CBNZ} instructions available.
@item arm_divmod_simode
ARM target for which divmod transform is disabled, if it supports hardware
div instruction.
@end table
@subsubsection AArch64-specific attributes
@ -1848,6 +1852,13 @@ Target requires a command line argument to enable a SIMD instruction set.
@item pie_copyreloc
The x86-64 target linker supports PIE with copy reloc.
@item divmod
Target supporting hardware divmod insn or divmod libcall.
@item divmod_simode
Target supporting hardware divmod insn or divmod libcall for SImode.
@end table
@subsubsection Environment attributes

View File

@ -7096,6 +7096,11 @@ This is firstly introduced on ARM/AArch64 targets, please refer to
the hook implementation for how different fusion types are supported.
@end deftypefn
@deftypefn {Target Hook} void TARGET_EXPAND_DIVMOD_LIBFUNC (rtx @var{libfunc}, machine_mode @var{mode}, rtx @var{op0}, rtx @var{op1}, rtx *@var{quot}, rtx *@var{rem})
Define this hook for enabling divmod transform if the port does not have
hardware divmod insn but defines target-specific divmod libfuncs.
@end deftypefn
@node Sections
@section Dividing the Output into Sections (Texts, Data, @dots{})
@c the above section title is WAY too long. maybe cut the part between

View File

@ -4892,6 +4892,8 @@ them: try the first ones in this list first.
@hook TARGET_SCHED_FUSION_PRIORITY
@hook TARGET_EXPAND_DIVMOD_LIBFUNC
@node Sections
@section Dividing the Output into Sections (Texts, Data, @dots{})
@c the above section title is WAY too long. maybe cut the part between

View File

@ -2232,6 +2232,53 @@ expand_LAUNDER (internal_fn, gcall *call)
expand_assignment (lhs, gimple_call_arg (call, 0), false);
}
/* Expand DIVMOD() using:
a) optab handler for udivmod/sdivmod if it is available.
b) If optab_handler doesn't exist, generate call to
target-specific divmod libfunc. */
static void
expand_DIVMOD (internal_fn, gcall *call_stmt)
{
tree lhs = gimple_call_lhs (call_stmt);
tree arg0 = gimple_call_arg (call_stmt, 0);
tree arg1 = gimple_call_arg (call_stmt, 1);
gcc_assert (TREE_CODE (TREE_TYPE (lhs)) == COMPLEX_TYPE);
tree type = TREE_TYPE (TREE_TYPE (lhs));
machine_mode mode = TYPE_MODE (type);
bool unsignedp = TYPE_UNSIGNED (type);
optab tab = (unsignedp) ? udivmod_optab : sdivmod_optab;
rtx op0 = expand_normal (arg0);
rtx op1 = expand_normal (arg1);
rtx target = expand_expr (lhs, NULL_RTX, VOIDmode, EXPAND_WRITE);
rtx quotient, remainder, libfunc;
/* Check if optab_handler exists for divmod_optab for given mode. */
if (optab_handler (tab, mode) != CODE_FOR_nothing)
{
quotient = gen_reg_rtx (mode);
remainder = gen_reg_rtx (mode);
expand_twoval_binop (tab, op0, op1, quotient, remainder, unsignedp);
}
/* Generate call to divmod libfunc if it exists. */
else if ((libfunc = optab_libfunc (tab, mode)) != NULL_RTX)
targetm.expand_divmod_libfunc (libfunc, mode, op0, op1,
&quotient, &remainder);
else
gcc_unreachable ();
/* Wrap the return value (quotient, remainder) within COMPLEX_EXPR. */
expand_expr (build2 (COMPLEX_EXPR, TREE_TYPE (lhs),
make_tree (TREE_TYPE (arg0), quotient),
make_tree (TREE_TYPE (arg1), remainder)),
target, VOIDmode, EXPAND_NORMAL);
}
/* Expand a call to FN using the operands in STMT. FN has a single
output operand and NARGS input operands. */

View File

@ -201,6 +201,9 @@ DEF_INTERNAL_FN (FALLTHROUGH, ECF_LEAF | ECF_NOTHROW, NULL)
/* To implement __builtin_launder. */
DEF_INTERNAL_FN (LAUNDER, ECF_LEAF | ECF_NOTHROW | ECF_NOVOPS, NULL)
/* Divmod function. */
DEF_INTERNAL_FN (DIVMOD, ECF_CONST | ECF_LEAF, NULL)
#undef DEF_INTERNAL_INT_FN
#undef DEF_INTERNAL_FLT_FN
#undef DEF_INTERNAL_OPTAB_FN

View File

@ -5055,6 +5055,15 @@ Normally, this is not needed.",
bool, (const_tree field, machine_mode mode),
default_member_type_forces_blk)
/* See tree-ssa-math-opts.c:divmod_candidate_p for conditions
that gate the divod transform. */
DEFHOOK
(expand_divmod_libfunc,
"Define this hook for enabling divmod transform if the port does not have\n\
hardware divmod insn but defines target-specific divmod libfuncs.",
void, (rtx libfunc, machine_mode mode, rtx op0, rtx op1, rtx *quot, rtx *rem),
NULL)
/* Return the class for a secondary reload, and fill in extra information. */
DEFHOOK
(secondary_reload,

View File

@ -1,3 +1,24 @@
2016-10-28 Prathamesh Kulkarni <prathamesh.kulkarni@linaro.org>
Kugan Vivekanandarajah <kuganv@linaro.org>
Jim Wilson <jim.wilson@linaro.org>
PR tree-optimization/43721
* lib/target-supports.exp (check_effective_target_divmod): New.
(check_effective_target_divmod_simode): Likewise.
(check_effective_target_arm_divmod_simode): Likewise.
* gcc.dg/divmod-1-simode.c: New test.
* gcc.dg/divmod-1.c: Likewise.
* gcc.dg/divmod-2-simode.c: Likewise.
* gcc.dg/divmod-2.c: Likewise.
* gcc.dg/divmod-3-simode.c: Likewise.
* gcc.dg/divmod-3.c: Likewise.
* gcc.dg/divmod-4-simode.c: Likewise.
* gcc.dg/divmod-4.c: Likewise.
* gcc.dg/divmod-5.c: Likewise.
* gcc.dg/divmod-6-simode.c: Likewise.
* gcc.dg/divmod-6.c: Likewise.
* gcc.dg/divmod-7.c: Likewise.
2016-10-28 Kyrylo Tkachov <kyrylo.tkachov@arm.com>
Jakub Jelinek <jakub@redhat.com>
Andrew Pinski <pinskia@gmail.com>

View File

@ -0,0 +1,25 @@
/* { dg-require-effective-target divmod_simode } */
/* { dg-options "-O2 -fdump-tree-widening_mul-details" } */
/* div dominates mod. */
typedef int SImode __attribute__((mode(SI)));
typedef unsigned USImode __attribute__((mode(SI)));
extern int cond;
void foo(void);
#define FOO(smalltype, bigtype, no) \
bigtype f_##no(smalltype x, bigtype y) \
{ \
bigtype q = x / y; \
if (cond) \
foo (); \
bigtype r = x % y; \
return q + r; \
}
FOO(SImode, SImode, 1)
FOO(SImode, USImode, 2)
FOO(USImode, USImode, 3)
/* { dg-final { scan-tree-dump-times "DIVMOD" 3 "widening_mul" } } */

View File

@ -0,0 +1,32 @@
/* { dg-require-effective-target divmod } */
/* { dg-options "-O2 -fdump-tree-widening_mul-details" } */
/* div dominates mod. */
typedef int SImode __attribute__((mode(SI)));
typedef unsigned USImode __attribute__((mode(SI)));
typedef int DImode __attribute__((mode(DI)));
typedef unsigned UDImode __attribute__((mode(DI)));
extern int cond;
void foo(void);
#define FOO(smalltype, bigtype, no) \
bigtype f_##no(smalltype x, bigtype y) \
{ \
bigtype q = x / y; \
if (cond) \
foo (); \
bigtype r = x % y; \
return q + r; \
}
FOO(SImode, DImode, 1)
FOO(SImode, UDImode, 2)
FOO(USImode, DImode, 3)
FOO(USImode, UDImode, 4)
FOO(DImode, DImode, 5)
FOO(DImode, UDImode, 6)
FOO(UDImode, UDImode, 7)
/* { dg-final { scan-tree-dump-times "DIVMOD" 7 "widening_mul" } } */

View File

@ -0,0 +1,25 @@
/* { dg-require-effective-target divmod_simode } */
/* { dg-options "-O2 -fdump-tree-widening_mul-details" } */
/* mod dominates div. */
typedef int SImode __attribute__((mode(SI)));
typedef unsigned USImode __attribute__((mode(SI)));
extern int cond;
void foo(void);
#define FOO(smalltype, bigtype, no) \
bigtype f_##no(smalltype x, bigtype y) \
{ \
bigtype r = x % y; \
if (cond) \
foo (); \
bigtype q = x / y; \
return q + r; \
}
FOO(SImode, SImode, 1)
FOO(SImode, USImode, 2)
FOO(USImode, USImode, 3)
/* { dg-final { scan-tree-dump-times "DIVMOD" 3 "widening_mul" } } */

View File

@ -0,0 +1,32 @@
/* { dg-require-effective-target divmod } */
/* { dg-options "-O2 -fdump-tree-widening_mul-details" } */
/* mod dominates div. */
typedef int SImode __attribute__((mode(SI)));
typedef unsigned USImode __attribute__((mode(SI)));
typedef int DImode __attribute__((mode(DI)));
typedef unsigned UDImode __attribute__((mode(DI)));
extern int cond;
void foo(void);
#define FOO(smalltype, bigtype, no) \
bigtype f_##no(smalltype x, bigtype y) \
{ \
bigtype r = x % y; \
if (cond) \
foo (); \
bigtype q = x / y; \
return q + r; \
}
FOO(SImode, DImode, 1)
FOO(SImode, UDImode, 2)
FOO(USImode, DImode, 3)
FOO(USImode, UDImode, 4)
FOO(DImode, DImode, 5)
FOO(DImode, UDImode, 6)
FOO(UDImode, UDImode, 7)
/* { dg-final { scan-tree-dump-times "DIVMOD" 7 "widening_mul" } } */

View File

@ -0,0 +1,23 @@
/* { dg-require-effective-target divmod_simode } */
/* { dg-options "-O2 -fdump-tree-widening_mul-details" } */
/* div comes before mod in same bb. */
typedef int SImode __attribute__((mode(SI)));
typedef unsigned USImode __attribute__((mode(SI)));
extern int cond;
void foo(void);
#define FOO(smalltype, bigtype, no) \
bigtype f_##no(smalltype x, bigtype y) \
{ \
bigtype q = x / y; \
bigtype r = x % y; \
return q + r; \
}
FOO(SImode, SImode, 1)
FOO(SImode, USImode, 2)
FOO(USImode, USImode, 3)
/* { dg-final { scan-tree-dump-times "DIVMOD" 3 "widening_mul" } } */

View File

@ -0,0 +1,30 @@
/* { dg-require-effective-target divmod } */
/* { dg-options "-O2 -fdump-tree-widening_mul-details" } */
/* div comes before mod in same bb. */
typedef int SImode __attribute__((mode(SI)));
typedef unsigned USImode __attribute__((mode(SI)));
typedef int DImode __attribute__((mode(DI)));
typedef unsigned UDImode __attribute__((mode(DI)));
extern int cond;
void foo(void);
#define FOO(smalltype, bigtype, no) \
bigtype f_##no(smalltype x, bigtype y) \
{ \
bigtype q = x / y; \
bigtype r = x % y; \
return q + r; \
}
FOO(SImode, DImode, 1)
FOO(SImode, UDImode, 2)
FOO(USImode, DImode, 3)
FOO(USImode, UDImode, 4)
FOO(DImode, DImode, 5)
FOO(DImode, UDImode, 6)
FOO(UDImode, UDImode, 7)
/* { dg-final { scan-tree-dump-times "DIVMOD" 7 "widening_mul" } } */

View File

@ -0,0 +1,23 @@
/* { dg-require-effective-target divmod_simode } */
/* { dg-options "-O2 -fdump-tree-widening_mul-details" } */
/* mod comes before div in same bb. */
typedef int SImode __attribute__((mode(SI)));
typedef unsigned USImode __attribute__((mode(SI)));
extern int cond;
void foo(void);
#define FOO(smalltype, bigtype, no) \
bigtype f_##no(smalltype x, bigtype y) \
{ \
bigtype r = x % y; \
bigtype q = x / y; \
return q + r; \
}
FOO(SImode, SImode, 1)
FOO(SImode, USImode, 2)
FOO(USImode, USImode, 3)
/* { dg-final { scan-tree-dump-times "DIVMOD" 3 "widening_mul" } } */

View File

@ -0,0 +1,30 @@
/* { dg-require-effective-target divmod } */
/* { dg-options "-O2 -fdump-tree-widening_mul-details" } */
/* mod comes before div in same bb. */
typedef int SImode __attribute__((mode(SI)));
typedef unsigned USImode __attribute__((mode(SI)));
typedef int DImode __attribute__((mode(DI)));
typedef unsigned UDImode __attribute__((mode(DI)));
extern int cond;
void foo(void);
#define FOO(smalltype, bigtype, no) \
bigtype f_##no(smalltype x, bigtype y) \
{ \
bigtype r = x % y; \
bigtype q = x / y; \
return q + r; \
}
FOO(SImode, DImode, 3)
FOO(SImode, UDImode, 4)
FOO(USImode, DImode, 6)
FOO(USImode, UDImode, 7)
FOO(DImode, DImode, 8)
FOO(DImode, UDImode, 9)
FOO(UDImode, UDImode, 10)
/* { dg-final { scan-tree-dump-times "DIVMOD" 7 "widening_mul" } } */

View File

@ -0,0 +1,19 @@
/* { dg-require-effective-target divmod_simode } */
/* { dg-options "-O2 -fdump-tree-widening_mul-details" } */
/* div and mod are not in same bb and
bb's containing div and mod don't dominate each other. */
int f(int x, int y)
{
int q = 0;
int r = 0;
extern int cond;
if (cond)
q = x / y;
r = x % y;
return q + r;
}
/* { dg-final { scan-tree-dump-times "DIVMOD" 0 "widening_mul" } } */

View File

@ -0,0 +1,26 @@
/* { dg-require-effective-target divmod_simode } */
/* { dg-options "-O2 -fdump-tree-widening_mul-details" } */
typedef int SImode __attribute__((mode(SI)));
typedef unsigned USImode __attribute__((mode(SI)));
extern int cond;
void foo(void);
#define FOO(smalltype, bigtype, no) \
bigtype f_##no(smalltype x, bigtype y) \
{ \
bigtype q = x / y; \
bigtype r1 = 0, r2 = 0; \
if (cond) \
r1 = x % y; \
else \
r2 = x % y; \
return q + r1 + r2; \
}
FOO(SImode, SImode, 1)
FOO(SImode, USImode, 2)
FOO(USImode, USImode, 3)
/* { dg-final { scan-tree-dump-times "DIVMOD" 3 "widening_mul" } } */

View File

@ -0,0 +1,33 @@
/* { dg-require-effective-target divmod } */
/* { dg-options "-O2 -fdump-tree-widening_mul-details" } */
typedef int SImode __attribute__((mode(SI)));
typedef unsigned USImode __attribute__((mode(SI)));
typedef int DImode __attribute__((mode(DI)));
typedef unsigned UDImode __attribute__((mode(DI)));
extern int cond;
void foo(void);
#define FOO(smalltype, bigtype, no) \
bigtype f_##no(smalltype x, bigtype y) \
{ \
bigtype q = x / y; \
bigtype r1 = 0, r2 = 0; \
if (cond) \
r1 = x % y; \
else \
r2 = x % y; \
return q + r1 + r2; \
}
FOO(SImode, DImode, 3)
FOO(SImode, UDImode, 4)
FOO(USImode, DImode, 6)
FOO(USImode, UDImode, 7)
FOO(DImode, DImode, 8)
FOO(DImode, UDImode, 9)
FOO(UDImode, UDImode, 10)
/* { dg-final { scan-tree-dump-times "DIVMOD" 7 "widening_mul" } } */

View File

@ -0,0 +1,21 @@
/* { dg-require-effective-target divmod_simode } */
/* { dg-options "-O2 -fdump-tree-widening_mul-details" } */
int f(int x, int y)
{
int q = 0, r1 = 0, r2 = 0;
extern int cond;
if (cond)
q = x / y;
else
{
r1 = x % y;
return q + r1;
}
r2 = x % y;
return q + r2;
}
/* { dg-final { scan-tree-dump-times "DIVMOD" 1 "widening_mul" } } */

View File

@ -8091,3 +8091,41 @@ proc check_effective_target_profile_update_atomic {} {
int main (void) { return 0; }
} "-fprofile-update=atomic -fprofile-generate"]
}
#For versions of ARM architectures that have hardware div insn,
#disable the divmod transform
proc check_effective_target_arm_divmod_simode { } {
return [check_no_compiler_messages arm_divmod assembly {
#ifdef __ARM_ARCH_EXT_IDIV__
#error has div insn
#endif
int i;
}]
}
# Return 1 if target supports divmod hardware insn or divmod libcall.
proc check_effective_target_divmod { } {
#TODO: Add checks for all targets that have either hardware divmod insn
# or define libfunc for divmod.
if { [istarget arm*-*-*]
|| [istarget x86_64-*-*] } {
return 1
}
return 0
}
# Return 1 if target supports divmod for SImode. The reason for
# separating this from check_effective_target_divmod is that
# some versions of ARM architecture define div instruction
# only for simode, and for these archs, we do not want to enable
# divmod transform for simode.
proc check_effective_target_divmod_simode { } {
if { [istarget arm*-*-*] } {
return [check_effective_target_arm_divmod_simode]
}
return [check_effective_target_divmod]
}

View File

@ -112,6 +112,9 @@ along with GCC; see the file COPYING3. If not see
#include "params.h"
#include "internal-fn.h"
#include "case-cfn-macros.h"
#include "optabs-libfuncs.h"
#include "tree-eh.h"
#include "targhooks.h"
/* This structure represents one basic block that either computes a
division, or is a common dominator for basic block that compute a
@ -184,6 +187,9 @@ static struct
/* Number of fp fused multiply-add ops inserted. */
int fmas_inserted;
/* Number of divmod calls inserted. */
int divmod_calls_inserted;
} widen_mul_stats;
/* The instance of "struct occurrence" representing the highest
@ -3793,6 +3799,213 @@ match_uaddsub_overflow (gimple_stmt_iterator *gsi, gimple *stmt,
return true;
}
/* Return true if target has support for divmod. */
static bool
target_supports_divmod_p (optab divmod_optab, optab div_optab, machine_mode mode)
{
/* If target supports hardware divmod insn, use it for divmod. */
if (optab_handler (divmod_optab, mode) != CODE_FOR_nothing)
return true;
/* Check if libfunc for divmod is available. */
rtx libfunc = optab_libfunc (divmod_optab, mode);
if (libfunc != NULL_RTX)
{
/* If optab_handler exists for div_optab, perhaps in a wider mode,
we don't want to use the libfunc even if it exists for given mode. */
for (machine_mode div_mode = mode;
div_mode != VOIDmode;
div_mode = GET_MODE_WIDER_MODE (div_mode))
if (optab_handler (div_optab, div_mode) != CODE_FOR_nothing)
return false;
return targetm.expand_divmod_libfunc != NULL;
}
return false;
}
/* Check if stmt is candidate for divmod transform. */
static bool
divmod_candidate_p (gassign *stmt)
{
tree type = TREE_TYPE (gimple_assign_lhs (stmt));
enum machine_mode mode = TYPE_MODE (type);
optab divmod_optab, div_optab;
if (TYPE_UNSIGNED (type))
{
divmod_optab = udivmod_optab;
div_optab = udiv_optab;
}
else
{
divmod_optab = sdivmod_optab;
div_optab = sdiv_optab;
}
tree op1 = gimple_assign_rhs1 (stmt);
tree op2 = gimple_assign_rhs2 (stmt);
/* Disable the transform if either is a constant, since division-by-constant
may have specialized expansion. */
if (CONSTANT_CLASS_P (op1) || CONSTANT_CLASS_P (op2))
return false;
/* Exclude the case where TYPE_OVERFLOW_TRAPS (type) as that should
expand using the [su]divv optabs. */
if (TYPE_OVERFLOW_TRAPS (type))
return false;
if (!target_supports_divmod_p (divmod_optab, div_optab, mode))
return false;
return true;
}
/* This function looks for:
t1 = a TRUNC_DIV_EXPR b;
t2 = a TRUNC_MOD_EXPR b;
and transforms it to the following sequence:
complex_tmp = DIVMOD (a, b);
t1 = REALPART_EXPR(a);
t2 = IMAGPART_EXPR(b);
For conditions enabling the transform see divmod_candidate_p().
The pass has three parts:
1) Find top_stmt which is trunc_div or trunc_mod stmt and dominates all
other trunc_div_expr and trunc_mod_expr stmts.
2) Add top_stmt and all trunc_div and trunc_mod stmts dominated by top_stmt
to stmts vector.
3) Insert DIVMOD call just before top_stmt and update entries in
stmts vector to use return value of DIMOVD (REALEXPR_PART for div,
IMAGPART_EXPR for mod). */
static bool
convert_to_divmod (gassign *stmt)
{
if (stmt_can_throw_internal (stmt)
|| !divmod_candidate_p (stmt))
return false;
tree op1 = gimple_assign_rhs1 (stmt);
tree op2 = gimple_assign_rhs2 (stmt);
imm_use_iterator use_iter;
gimple *use_stmt;
auto_vec<gimple *> stmts;
gimple *top_stmt = stmt;
basic_block top_bb = gimple_bb (stmt);
/* Part 1: Try to set top_stmt to "topmost" stmt that dominates
at-least stmt and possibly other trunc_div/trunc_mod stmts
having same operands as stmt. */
FOR_EACH_IMM_USE_STMT (use_stmt, use_iter, op1)
{
if (is_gimple_assign (use_stmt)
&& (gimple_assign_rhs_code (use_stmt) == TRUNC_DIV_EXPR
|| gimple_assign_rhs_code (use_stmt) == TRUNC_MOD_EXPR)
&& operand_equal_p (op1, gimple_assign_rhs1 (use_stmt), 0)
&& operand_equal_p (op2, gimple_assign_rhs2 (use_stmt), 0))
{
if (stmt_can_throw_internal (use_stmt))
continue;
basic_block bb = gimple_bb (use_stmt);
if (bb == top_bb)
{
if (gimple_uid (use_stmt) < gimple_uid (top_stmt))
top_stmt = use_stmt;
}
else if (dominated_by_p (CDI_DOMINATORS, top_bb, bb))
{
top_bb = bb;
top_stmt = use_stmt;
}
}
}
tree top_op1 = gimple_assign_rhs1 (top_stmt);
tree top_op2 = gimple_assign_rhs2 (top_stmt);
stmts.safe_push (top_stmt);
bool div_seen = (gimple_assign_rhs_code (top_stmt) == TRUNC_DIV_EXPR);
/* Part 2: Add all trunc_div/trunc_mod statements domianted by top_bb
to stmts vector. The 2nd loop will always add stmt to stmts vector, since
gimple_bb (top_stmt) dominates gimple_bb (stmt), so the
2nd loop ends up adding at-least single trunc_mod_expr stmt. */
FOR_EACH_IMM_USE_STMT (use_stmt, use_iter, top_op1)
{
if (is_gimple_assign (use_stmt)
&& (gimple_assign_rhs_code (use_stmt) == TRUNC_DIV_EXPR
|| gimple_assign_rhs_code (use_stmt) == TRUNC_MOD_EXPR)
&& operand_equal_p (top_op1, gimple_assign_rhs1 (use_stmt), 0)
&& operand_equal_p (top_op2, gimple_assign_rhs2 (use_stmt), 0))
{
if (use_stmt == top_stmt
|| stmt_can_throw_internal (use_stmt)
|| !dominated_by_p (CDI_DOMINATORS, gimple_bb (use_stmt), top_bb))
continue;
stmts.safe_push (use_stmt);
if (gimple_assign_rhs_code (use_stmt) == TRUNC_DIV_EXPR)
div_seen = true;
}
}
if (!div_seen)
return false;
/* Part 3: Create libcall to internal fn DIVMOD:
divmod_tmp = DIVMOD (op1, op2). */
gcall *call_stmt = gimple_build_call_internal (IFN_DIVMOD, 2, op1, op2);
tree res = make_temp_ssa_name (build_complex_type (TREE_TYPE (op1)),
call_stmt, "divmod_tmp");
gimple_call_set_lhs (call_stmt, res);
/* Insert the call before top_stmt. */
gimple_stmt_iterator top_stmt_gsi = gsi_for_stmt (top_stmt);
gsi_insert_before (&top_stmt_gsi, call_stmt, GSI_SAME_STMT);
widen_mul_stats.divmod_calls_inserted++;
/* Update all statements in stmts vector:
lhs = op1 TRUNC_DIV_EXPR op2 -> lhs = REALPART_EXPR<divmod_tmp>
lhs = op1 TRUNC_MOD_EXPR op2 -> lhs = IMAGPART_EXPR<divmod_tmp>. */
for (unsigned i = 0; stmts.iterate (i, &use_stmt); ++i)
{
tree new_rhs;
switch (gimple_assign_rhs_code (use_stmt))
{
case TRUNC_DIV_EXPR:
new_rhs = fold_build1 (REALPART_EXPR, TREE_TYPE (op1), res);
break;
case TRUNC_MOD_EXPR:
new_rhs = fold_build1 (IMAGPART_EXPR, TREE_TYPE (op1), res);
break;
default:
gcc_unreachable ();
}
gimple_stmt_iterator gsi = gsi_for_stmt (use_stmt);
gimple_assign_set_rhs_from_tree (&gsi, new_rhs);
update_stmt (use_stmt);
}
return true;
}
/* Find integer multiplications where the operands are extended from
smaller types, and replace the MULT_EXPR with a WIDEN_MULT_EXPR
@ -3837,6 +4050,8 @@ pass_optimize_widening_mul::execute (function *fun)
bool cfg_changed = false;
memset (&widen_mul_stats, 0, sizeof (widen_mul_stats));
calculate_dominance_info (CDI_DOMINATORS);
renumber_gimple_stmt_uids ();
FOR_EACH_BB_FN (bb, fun)
{
@ -3870,6 +4085,10 @@ pass_optimize_widening_mul::execute (function *fun)
match_uaddsub_overflow (&gsi, stmt, code);
break;
case TRUNC_MOD_EXPR:
convert_to_divmod (as_a<gassign *> (stmt));
break;
default:;
}
}
@ -3916,6 +4135,8 @@ pass_optimize_widening_mul::execute (function *fun)
widen_mul_stats.maccs_inserted);
statistics_counter_event (fun, "fused multiply-adds inserted",
widen_mul_stats.fmas_inserted);
statistics_counter_event (fun, "divmod calls inserted",
widen_mul_stats.divmod_calls_inserted);
return cfg_changed ? TODO_cleanup_cfg : 0;
}