@@ -61,6 +61,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"
@@ -300,6 +301,7 @@ static unsigned HOST_WIDE_INT arm_asan_shadow_offset (void);
static void arm_sched_fusion_priority (rtx_insn *, int, int *, int*);
static bool arm_can_output_mi_thunk (const_tree, HOST_WIDE_INT, HOST_WIDE_INT,
const_tree);
+static void arm_expand_divmod_libfunc (bool, machine_mode, rtx, rtx, rtx *, rtx *);
/* Table of machine attributes. */
@@ -730,6 +732,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_EXPAND_DIVMOD_LIBFUNC
+#define TARGET_EXPAND_DIVMOD_LIBFUNC arm_expand_divmod_libfunc
+
struct gcc_target targetm = TARGET_INITIALIZER;
/* Obstack for minipool constant handling. */
@@ -30354,6 +30359,37 @@ arm_sched_fusion_priority (rtx_insn *insn, int max_pri,
return;
}
+/* Expand call to __aeabi_[mode]divmod (op0, op1). */
+
+static void
+arm_expand_divmod_libfunc (bool unsignedp, machine_mode mode,
+ rtx op0, rtx op1,
+ rtx *quot_p, rtx *rem_p)
+{
+ if (mode == SImode)
+ gcc_assert (!TARGET_IDIV);
+
+ optab tab = (unsignedp) ? udivmod_optab : sdivmod_optab;
+ rtx libfunc = optab_libfunc (tab, mode);
+ gcc_assert (libfunc);
+
+ 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;
+}
/* Construct and return a PARALLEL RTX vector with elements numbering the
lanes of either the high (HIGH == TRUE) or low (HIGH == FALSE) half of
new file mode 100644
@@ -0,0 +1,22 @@
+/* { dg-require-effective-target divmod_simode } */
+/* { dg-options "-O2 -fdump-tree-widening_mul-details" } */
+/* div dominates mod. */
+
+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(int, int, 1)
+FOO(int, unsigned, 2)
+FOO(unsigned, unsigned, 5)
+
+/* { dg-final { scan-tree-dump-times "DIVMOD" 3 "widening_mul" } } */
new file mode 100644
@@ -0,0 +1,26 @@
+/* { dg-require-effective-target divmod } */
+/* { dg-options "-O2 -fdump-tree-widening_mul-details" } */
+/* div dominates mod. */
+
+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(int, long long, 3)
+FOO(int, unsigned long long, 4)
+FOO(unsigned, long long, 6)
+FOO(unsigned, unsigned long long, 7)
+FOO(long long, long long, 8)
+FOO(long long, unsigned long long, 9)
+FOO(unsigned long long, unsigned long long, 10)
+
+/* { dg-final { scan-tree-dump-times "DIVMOD" 7 "widening_mul" } } */
new file mode 100644
@@ -0,0 +1,22 @@
+/* { dg-require-effective-target divmod_simode } */
+/* { dg-options "-O2 -fdump-tree-widening_mul-details" } */
+/* mod dominates div. */
+
+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(int, int, 1)
+FOO(int, unsigned, 2)
+FOO(unsigned, unsigned, 5)
+
+/* { dg-final { scan-tree-dump-times "DIVMOD" 3 "widening_mul" } } */
new file mode 100644
@@ -0,0 +1,26 @@
+/* { dg-require-effective-target divmod } */
+/* { dg-options "-O2 -fdump-tree-widening_mul-details" } */
+/* mod dominates div. */
+
+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(int, long long, 3)
+FOO(int, unsigned long long, 4)
+FOO(unsigned, long long, 6)
+FOO(unsigned, unsigned long long, 7)
+FOO(long long, long long, 8)
+FOO(long long, unsigned long long, 9)
+FOO(unsigned long long, unsigned long long, 10)
+
+/* { dg-final { scan-tree-dump-times "DIVMOD" 7 "widening_mul" } } */
new file mode 100644
@@ -0,0 +1,20 @@
+/* { dg-require-effective-target divmod_simode } */
+/* { dg-options "-O2 -fdump-tree-widening_mul-details" } */
+/* div comes before mod in same bb. */
+
+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(int, int, 1)
+FOO(int, unsigned, 2)
+FOO(unsigned, unsigned, 5)
+
+/* { dg-final { scan-tree-dump-times "DIVMOD" 3 "widening_mul" } } */
new file mode 100644
@@ -0,0 +1,24 @@
+/* { dg-require-effective-target divmod } */
+/* { dg-options "-O2 -fdump-tree-widening_mul-details" } */
+/* div comes before mod in same bb. */
+
+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(int, long long, 3)
+FOO(int, unsigned long long, 4)
+FOO(unsigned, long long, 6)
+FOO(unsigned, unsigned long long, 7)
+FOO(long long, long long, 8)
+FOO(long long, unsigned long long, 9)
+FOO(unsigned long long, unsigned long long, 10)
+
+/* { dg-final { scan-tree-dump-times "DIVMOD" 7 "widening_mul" } } */
new file mode 100644
@@ -0,0 +1,20 @@
+/* { dg-require-effective-target divmod_simode } */
+/* { dg-options "-O2 -fdump-tree-widening_mul-details" } */
+/* mod comes before div in same bb. */
+
+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(int, int, 1)
+FOO(int, unsigned, 2)
+FOO(unsigned, unsigned, 5)
+
+/* { dg-final { scan-tree-dump-times "DIVMOD" 3 "widening_mul" } } */
new file mode 100644
@@ -0,0 +1,24 @@
+/* { dg-require-effective-target divmod } */
+/* { dg-options "-O2 -fdump-tree-widening_mul-details" } */
+/* mod comes before div in same bb. */
+
+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(int, long long, 3)
+FOO(int, unsigned long long, 4)
+FOO(unsigned, long long, 6)
+FOO(unsigned, unsigned long long, 7)
+FOO(long long, long long, 8)
+FOO(long long, unsigned long long, 9)
+FOO(unsigned long long, unsigned long long, 10)
+
+/* { dg-final { scan-tree-dump-times "DIVMOD" 7 "widening_mul" } } */
new file mode 100644
@@ -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" } } */
new file mode 100644
@@ -0,0 +1,24 @@
+/* { dg-require-effective-target divmod_simode } */
+/* { dg-options "-O2 -fdump-tree-widening_mul-details" } */
+
+
+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(int, int, 1)
+FOO(int, unsigned, 2)
+FOO(unsigned, unsigned, 5)
+
+/* { dg-final { scan-tree-dump-times "DIVMOD" 3 "widening_mul" } } */
new file mode 100644
@@ -0,0 +1,27 @@
+/* { dg-require-effective-target divmod } */
+/* { dg-options "-O2 -fdump-tree-widening_mul-details" } */
+
+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(int, long long, 3)
+FOO(int, unsigned long long, 4)
+FOO(unsigned, long long, 6)
+FOO(unsigned, unsigned long long, 7)
+FOO(long long, long long, 8)
+FOO(long long, unsigned long long, 9)
+FOO(unsigned long long, unsigned long long, 10)
+
+/* { dg-final { scan-tree-dump-times "DIVMOD" 7 "widening_mul" } } */
new file mode 100644
@@ -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" 0 "widening_mul" } } */
@@ -6986,3 +6986,32 @@ proc check_effective_target_offload_hsa { } {
int main () {return 0;}
} "-foffload=hsa" ]
}
+
+# For ARM configs defining __ARM_ARCH_EXT_IDIV__, disable divmod_simode test-cases.
+
+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;
+ }]
+}
+
+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
+}
+
+proc check_effective_target_divmod_simode { } {
+ if { [istarget arm*-*-*] } {
+ return [check_effective_target_arm_divmod_simode]
+ }
+
+ return [check_effective_target_divmod]
+}