[PATCH] expansion: Further improve double-word modulo, division and divmod [PR97459]
Richard Biener
rguenther@suse.de
Wed Dec 2 07:52:15 GMT 2020
On Tue, 1 Dec 2020, Jakub Jelinek wrote:
> Hi!
>
> The following patch implements what Thomas wrote about, in particular
> that we can handle also double-word divison by the constants for which
> the earlier patch optimized modulo (if it would be otherwise a library
> call) and that we can also easily handle such constants shifted to the left.
> Unfortunately, seems CSE isn't able to optimize away the two almost
> identical sequences (one to compute remainder, one to compute quotient),
> probably because of the ADD_OVERFLOW introduced jumps, so the patch also
> adjusts expand_DIVMOD.
>
> Bootstrapped/regtested on x86_64-linux and i686-linux, ok for trunk?
OK.
Richard.
> 2020-12-01 Jakub Jelinek <jakub@redhat.com>
>
> PR rtl-optimization/97459
> * optabs.h (expand_doubleword_divmod): Declare.
> * optabs.c (expand_doubleword_divmod): New function.
> (expand_binop): Use it.
> * internal-fn.c (expand_DIVMOD): Likewise.
>
> * gcc.target/i386/pr97282.c (foo): Use 123456 divisor instead of
> 10.
> * gcc.dg/pr97459-1.c (TESTS): Add tests for 10, 12 and
> 6144.
> * gcc.dg/pr97459-2.c (TESTS): Likewise.
> * gcc.dg/pr97459-3.c: New test.
> * gcc.dg/pr97459-4.c: New test.
> * gcc.dg/pr97459-5.c: New test.
> * gcc.dg/pr97459-6.c: New test.
>
> --- gcc/optabs.h.jj 2020-12-01 13:19:12.831127718 +0100
> +++ gcc/optabs.h 2020-12-01 17:40:08.783137431 +0100
> @@ -183,6 +183,8 @@ extern bool force_expand_binop (machine_
> enum optab_methods);
> extern rtx expand_vector_broadcast (machine_mode, rtx);
>
> +extern rtx expand_doubleword_divmod (machine_mode, rtx, rtx, rtx *, bool);
> +
> /* Generate code for a simple binary or unary operation. "Simple" in
> this case means "can be unambiguously described by a (mode, code)
> pair and mapped to a single optab." */
> --- gcc/optabs.c.jj 2020-12-01 16:25:01.567733333 +0100
> +++ gcc/optabs.c 2020-12-01 18:11:12.878230572 +0100
> @@ -1118,6 +1118,99 @@ expand_doubleword_mod (machine_mode mode
> }
> return NULL_RTX;
> }
> +
> +/* Similarly to the above function, but compute both quotient and remainder.
> + Quotient can be computed from the remainder as:
> + rem = op0 % op1; // Handled using expand_doubleword_mod
> + quot = (op0 - rem) * inv; // inv is multiplicative inverse of op1 modulo
> + // 2 * BITS_PER_WORD
> +
> + We can also handle cases where op1 is a multiple of power of two constant
> + and constant handled by expand_doubleword_mod.
> + op11 = 1 << __builtin_ctz (op1);
> + op12 = op1 / op11;
> + rem1 = op0 % op12; // Handled using expand_doubleword_mod
> + quot1 = (op0 - rem1) * inv; // inv is multiplicative inverse of op12 modulo
> + // 2 * BITS_PER_WORD
> + rem = (quot1 % op11) * op12 + rem1;
> + quot = quot1 / op11; */
> +
> +rtx
> +expand_doubleword_divmod (machine_mode mode, rtx op0, rtx op1, rtx *rem,
> + bool unsignedp)
> +{
> + *rem = NULL_RTX;
> +
> + /* Negative dividend should have been optimized into positive,
> + similarly modulo by 1 and modulo by power of two is optimized
> + differently too. */
> + if (INTVAL (op1) <= 1 || pow2p_hwi (INTVAL (op1)))
> + return NULL_RTX;
> +
> + rtx op11 = const1_rtx;
> + rtx op12 = op1;
> + if ((INTVAL (op1) & 1) == 0)
> + {
> + int bit = ctz_hwi (INTVAL (op1));
> + op11 = GEN_INT (HOST_WIDE_INT_1 << bit);
> + op12 = GEN_INT (INTVAL (op1) >> bit);
> + }
> +
> + rtx rem1 = expand_doubleword_mod (mode, op0, op12, unsignedp);
> + if (rem1 == NULL_RTX)
> + return NULL_RTX;
> +
> + int prec = 2 * BITS_PER_WORD;
> + wide_int a = wide_int::from (INTVAL (op12), prec + 1, UNSIGNED);
> + wide_int b = wi::shifted_mask (prec, 1, false, prec + 1);
> + wide_int m = wide_int::from (wi::mod_inv (a, b), prec, UNSIGNED);
> + rtx inv = immed_wide_int_const (m, mode);
> +
> + rtx_insn *last = get_last_insn ();
> + rtx quot1 = expand_simple_binop (mode, MINUS, op0, rem1,
> + NULL_RTX, unsignedp, OPTAB_DIRECT);
> + if (quot1 == NULL_RTX)
> + return NULL_RTX;
> +
> + quot1 = expand_simple_binop (mode, MULT, quot1, inv,
> + NULL_RTX, unsignedp, OPTAB_DIRECT);
> + if (quot1 == NULL_RTX)
> + return NULL_RTX;
> +
> + if (op11 != const1_rtx)
> + {
> + rtx rem2 = expand_divmod (1, TRUNC_MOD_EXPR, mode, quot1, op11,
> + NULL_RTX, unsignedp);
> + if (rem2 == NULL_RTX)
> + return NULL_RTX;
> +
> + rem2 = expand_simple_binop (mode, MULT, rem2, op12, NULL_RTX,
> + unsignedp, OPTAB_DIRECT);
> + if (rem2 == NULL_RTX)
> + return NULL_RTX;
> +
> + rem2 = expand_simple_binop (mode, PLUS, rem2, rem1, NULL_RTX,
> + unsignedp, OPTAB_DIRECT);
> + if (rem2 == NULL_RTX)
> + return NULL_RTX;
> +
> + rtx quot2 = expand_divmod (0, TRUNC_DIV_EXPR, mode, quot1, op11,
> + NULL_RTX, unsignedp);
> + if (quot2 == NULL_RTX)
> + return NULL_RTX;
> +
> + rem1 = rem2;
> + quot1 = quot2;
> + }
> +
> + /* Punt if we need any library calls. */
> + for (; last; last = NEXT_INSN (last))
> + if (CALL_P (last))
> + return NULL_RTX;
> +
> + *rem = rem1;
> + return quot1;
> +}
>
> /* Wrapper around expand_binop which takes an rtx code to specify
> the operation to perform, not an optab pointer. All other
> @@ -1999,7 +2092,10 @@ expand_binop (machine_mode mode, optab b
> }
>
> /* Attempt to synthetize double word modulo by constant divisor. */
> - if ((binoptab == umod_optab || binoptab == smod_optab)
> + if ((binoptab == umod_optab
> + || binoptab == smod_optab
> + || binoptab == udiv_optab
> + || binoptab == sdiv_optab)
> && optimize
> && CONST_INT_P (op1)
> && is_int_mode (mode, &int_mode)
> @@ -2008,21 +2104,33 @@ expand_binop (machine_mode mode, optab b
> && optab_handler (add_optab, word_mode) != CODE_FOR_nothing
> && optimize_insn_for_speed_p ())
> {
> - rtx remainder = expand_doubleword_mod (int_mode, op0, op1,
> - binoptab == umod_optab);
> - if (remainder != NULL_RTX)
> + rtx res = NULL_RTX;
> + if ((binoptab == umod_optab || binoptab == smod_optab)
> + && (INTVAL (op1) & 1) == 0)
> + res = expand_doubleword_mod (int_mode, op0, op1,
> + binoptab == umod_optab);
> + else
> + {
> + rtx quot = expand_doubleword_divmod (int_mode, op0, op1, &res,
> + binoptab == umod_optab
> + || binoptab == udiv_optab);
> + if (quot == NULL_RTX)
> + res = NULL_RTX;
> + else if (binoptab == udiv_optab || binoptab == sdiv_optab)
> + res = quot;
> + }
> + if (res != NULL_RTX)
> {
> if (optab_handler (mov_optab, int_mode) != CODE_FOR_nothing)
> {
> - rtx_insn *move = emit_move_insn (target ? target : remainder,
> - remainder);
> - set_dst_reg_note (move,
> - REG_EQUAL,
> - gen_rtx_fmt_ee (UMOD, int_mode,
> - copy_rtx (op0), op1),
> - target ? target : remainder);
> + rtx_insn *move = emit_move_insn (target ? target : res,
> + res);
> + set_dst_reg_note (move, REG_EQUAL,
> + gen_rtx_fmt_ee (optab_to_code (binoptab),
> + int_mode, copy_rtx (op0), op1),
> + target ? target : res);
> }
> - return remainder;
> + return res;
> }
> else
> delete_insns_since (last);
> --- gcc/internal-fn.c.jj 2020-11-30 10:55:33.134963320 +0100
> +++ gcc/internal-fn.c 2020-12-01 18:18:20.964436077 +0100
> @@ -3230,27 +3230,68 @@ expand_DIVMOD (internal_fn, gcall *call_
> the division and modulo and if it emits any library calls or any
> {,U}{DIV,MOD} rtxes throw it away and use a divmod optab or
> divmod libcall. */
> - struct separate_ops ops;
> - ops.code = TRUNC_DIV_EXPR;
> - ops.type = type;
> - ops.op0 = make_tree (ops.type, op0);
> - ops.op1 = arg1;
> - ops.op2 = NULL_TREE;
> - ops.location = gimple_location (call_stmt);
> - start_sequence ();
> - quotient = expand_expr_real_2 (&ops, NULL_RTX, mode, EXPAND_NORMAL);
> - if (contains_call_div_mod (get_insns ()))
> - quotient = NULL_RTX;
> - else
> + scalar_int_mode int_mode;
> + if (remainder == NULL_RTX
> + && optimize
> + && CONST_INT_P (op1)
> + && !pow2p_hwi (INTVAL (op1))
> + && is_int_mode (TYPE_MODE (type), &int_mode)
> + && GET_MODE_SIZE (int_mode) == 2 * UNITS_PER_WORD
> + && optab_handler (and_optab, word_mode) != CODE_FOR_nothing
> + && optab_handler (add_optab, word_mode) != CODE_FOR_nothing
> + && optimize_insn_for_speed_p ())
> {
> - ops.code = TRUNC_MOD_EXPR;
> - remainder = expand_expr_real_2 (&ops, NULL_RTX, mode, EXPAND_NORMAL);
> + rtx_insn *last = get_last_insn ();
> + remainder = NULL_RTX;
> + quotient = expand_doubleword_divmod (int_mode, op0, op1, &remainder,
> + TYPE_UNSIGNED (type));
> + if (quotient != NULL_RTX)
> + {
> + if (optab_handler (mov_optab, int_mode) != CODE_FOR_nothing)
> + {
> + rtx_insn *move = emit_move_insn (quotient, quotient);
> + set_dst_reg_note (move, REG_EQUAL,
> + gen_rtx_fmt_ee (TYPE_UNSIGNED (type)
> + ? UDIV : DIV, int_mode,
> + copy_rtx (op0), op1),
> + quotient);
> + move = emit_move_insn (remainder, remainder);
> + set_dst_reg_note (move, REG_EQUAL,
> + gen_rtx_fmt_ee (TYPE_UNSIGNED (type)
> + ? UMOD : MOD, int_mode,
> + copy_rtx (op0), op1),
> + quotient);
> + }
> + }
> + else
> + delete_insns_since (last);
> + }
> +
> + if (remainder == NULL_RTX)
> + {
> + struct separate_ops ops;
> + ops.code = TRUNC_DIV_EXPR;
> + ops.type = type;
> + ops.op0 = make_tree (ops.type, op0);
> + ops.op1 = arg1;
> + ops.op2 = NULL_TREE;
> + ops.location = gimple_location (call_stmt);
> + start_sequence ();
> + quotient = expand_expr_real_2 (&ops, NULL_RTX, mode, EXPAND_NORMAL);
> if (contains_call_div_mod (get_insns ()))
> - remainder = NULL_RTX;
> + quotient = NULL_RTX;
> + else
> + {
> + ops.code = TRUNC_MOD_EXPR;
> + remainder = expand_expr_real_2 (&ops, NULL_RTX, mode,
> + EXPAND_NORMAL);
> + if (contains_call_div_mod (get_insns ()))
> + remainder = NULL_RTX;
> + }
> + if (remainder)
> + insns = get_insns ();
> + end_sequence ();
> }
> - if (remainder)
> - insns = get_insns ();
> - end_sequence ();
> }
>
> if (remainder)
> --- gcc/testsuite/gcc.target/i386/pr97282.c.jj 2020-10-06 10:32:14.769771587 +0200
> +++ gcc/testsuite/gcc.target/i386/pr97282.c 2020-12-01 21:52:57.901708048 +0100
> @@ -18,8 +18,8 @@ foo (T x)
> unsigned long ret = 0;
> while (x > 0)
> {
> - ret = ret + x % 10;
> - x = x / 10;
> + ret = ret + x % 123456;
> + x = x / 123456;
> }
> return ret;
> }
> --- gcc/testsuite/gcc.dg/pr97459-1.c.jj 2020-11-30 10:55:33.135963309 +0100
> +++ gcc/testsuite/gcc.dg/pr97459-1.c 2020-12-01 18:23:25.243031163 +0100
> @@ -24,7 +24,7 @@ T __attribute__((noipa)) foo (T x, T n)
> #define C3(n) C2(n##0) C2(n##4) C2(n##9)
> #define C4(n) C3(n##0) C3(n##3) C3(n##7)
> #endif
> -#define TESTS C4(1)
> +#define TESTS C4(1) C1(10010) C1(10012) C1(16144)
>
> TESTS
>
> --- gcc/testsuite/gcc.dg/pr97459-2.c.jj 2020-11-30 10:55:33.136963298 +0100
> +++ gcc/testsuite/gcc.dg/pr97459-2.c 2020-12-01 18:23:51.423738268 +0100
> @@ -26,7 +26,7 @@ T __attribute__((noipa)) foo (T x, T n)
> #define C3(n) C2(n##0) C2(n##4) C2(n##9)
> #define C4(n) C3(n##0) C3(n##3) C3(n##7)
> #endif
> -#define TESTS C4(1)
> +#define TESTS C4(1) C1(10010) C1(10012) C1(16144)
>
> TESTS
>
> --- gcc/testsuite/gcc.dg/pr97459-3.c.jj 2020-12-01 18:25:38.452540896 +0100
> +++ gcc/testsuite/gcc.dg/pr97459-3.c 2020-12-01 18:26:00.662292428 +0100
> @@ -0,0 +1,54 @@
> +/* PR rtl-optimization/97459 */
> +/* { dg-do run } */
> +/* { dg-options "-O2" } */
> +/* { dg-additional-options "-DEXPENSIVE" { target run_expensive_tests } } */
> +
> +#ifdef __SIZEOF_INT128__
> +typedef __uint128_t T;
> +#else
> +typedef unsigned long long T;
> +#endif
> +
> +T __attribute__((noipa)) foo (T x, T n) { return x / n; }
> +#define C(n) T __attribute__((noipa)) foo##n (T x) { return x / (n - 10000); }
> +
> +#define C1(n) C(n##1) C(n##3) C(n##5) C(n##7) C(n##9)
> +#define C2(n) C1(n##0) C1(n##1) C1(n##2) C1(n##3) C1(n##4) \
> + C1(n##5) C1(n##6) C1(n##7) C1(n##8) C1(n##9)
> +#ifdef EXPENSIVE
> +#define C3(n) C2(n##0) C2(n##1) C2(n##2) C2(n##3) C2(n##4) \
> + C2(n##5) C2(n##6) C2(n##7) C2(n##8) C2(n##9)
> +#define C4(n) C3(n##0) C3(n##1) C3(n##2) C3(n##3) C3(n##4) \
> + C3(n##5) C3(n##6) C3(n##7) C3(n##8) C3(n##9)
> +#else
> +#define C3(n) C2(n##0) C2(n##4) C2(n##9)
> +#define C4(n) C3(n##0) C3(n##3) C3(n##7)
> +#endif
> +#define TESTS C4(1) C1(10010) C1(10012) C1(16144)
> +
> +TESTS
> +
> +struct S { T x; T (*foo) (T); };
> +
> +#undef C
> +#define C(n) { n - 10000, foo##n },
> +
> +struct S tests[] = {
> +TESTS
> + { 0, 0 }
> +};
> +
> +int
> +main ()
> +{
> + int i, j, k;
> + for (k = 0; tests[k].x; k++)
> + for (i = 0; i < sizeof (T) * __CHAR_BIT__; i++)
> + for (j = -5; j <= 5; j++)
> + {
> + T x = ((T) 1 << i) + j;
> + if (foo (x, tests[k].x) != tests[k].foo (x))
> + __builtin_abort ();
> + }
> + return 0;
> +}
> --- gcc/testsuite/gcc.dg/pr97459-4.c.jj 2020-12-01 18:26:10.272184915 +0100
> +++ gcc/testsuite/gcc.dg/pr97459-4.c 2020-12-01 18:26:27.345993905 +0100
> @@ -0,0 +1,57 @@
> +/* PR rtl-optimization/97459 */
> +/* { dg-do run } */
> +/* { dg-options "-O2" } */
> +/* { dg-additional-options "-DEXPENSIVE" { target run_expensive_tests } } */
> +
> +#ifdef __SIZEOF_INT128__
> +typedef __int128_t T;
> +typedef __uint128_t U;
> +#else
> +typedef long long T;
> +typedef unsigned long long U;
> +#endif
> +
> +T __attribute__((noipa)) foo (T x, T n) { return x / n; }
> +#define C(n) T __attribute__((noipa)) foo##n (T x) { return x / (n - 10000); }
> +
> +#define C1(n) C(n##1) C(n##3) C(n##5) C(n##7) C(n##9)
> +#define C2(n) C1(n##0) C1(n##1) C1(n##2) C1(n##3) C1(n##4) \
> + C1(n##5) C1(n##6) C1(n##7) C1(n##8) C1(n##9)
> +#ifdef EXPENSIVE
> +#define C3(n) C2(n##0) C2(n##1) C2(n##2) C2(n##3) C2(n##4) \
> + C2(n##5) C2(n##6) C2(n##7) C2(n##8) C2(n##9)
> +#define C4(n) C3(n##0) C3(n##1) C3(n##2) C3(n##3) C3(n##4) \
> + C3(n##5) C3(n##6) C3(n##7) C3(n##8) C3(n##9)
> +#else
> +#define C3(n) C2(n##0) C2(n##4) C2(n##9)
> +#define C4(n) C3(n##0) C3(n##3) C3(n##7)
> +#endif
> +#define TESTS C4(1) C1(10010) C1(10012) C1(16144)
> +
> +TESTS
> +
> +struct S { T x; T (*foo) (T); };
> +
> +#undef C
> +#define C(n) { n - 10000, foo##n },
> +
> +struct S tests[] = {
> +TESTS
> + { 0, 0 }
> +};
> +
> +int
> +main ()
> +{
> + int i, j, k;
> + for (k = 0; tests[k].x; k++)
> + for (i = 0; i < sizeof (T) * __CHAR_BIT__; i++)
> + for (j = -5; j <= 5; j++)
> + {
> + U x = ((U) 1 << i) + j;
> + if (foo ((T) x, tests[k].x) != tests[k].foo ((T) x)
> + || foo ((T) -x, tests[k].x) != tests[k].foo ((T) -x))
> + __builtin_abort ();
> + }
> + return 0;
> +}
> --- gcc/testsuite/gcc.dg/pr97459-5.c.jj 2020-12-01 18:27:03.386590701 +0100
> +++ gcc/testsuite/gcc.dg/pr97459-5.c 2020-12-01 18:28:30.324618095 +0100
> @@ -0,0 +1,56 @@
> +/* PR rtl-optimization/97459 */
> +/* { dg-do run } */
> +/* { dg-options "-O2" } */
> +/* { dg-additional-options "-DEXPENSIVE" { target run_expensive_tests } } */
> +
> +#ifdef __SIZEOF_INT128__
> +typedef __uint128_t T;
> +#else
> +typedef unsigned long long T;
> +#endif
> +
> +T __attribute__((noipa)) foo (T x, T n, T *r) { *r = x % n; return x / n; }
> +#define C(n) T __attribute__((noipa)) foo##n (T x, T *r) { *r = x % (n - 10000); return x / (n - 10000); }
> +
> +#define C1(n) C(n##1) C(n##3) C(n##5) C(n##7) C(n##9)
> +#define C2(n) C1(n##0) C1(n##1) C1(n##2) C1(n##3) C1(n##4) \
> + C1(n##5) C1(n##6) C1(n##7) C1(n##8) C1(n##9)
> +#ifdef EXPENSIVE
> +#define C3(n) C2(n##0) C2(n##1) C2(n##2) C2(n##3) C2(n##4) \
> + C2(n##5) C2(n##6) C2(n##7) C2(n##8) C2(n##9)
> +#define C4(n) C3(n##0) C3(n##1) C3(n##2) C3(n##3) C3(n##4) \
> + C3(n##5) C3(n##6) C3(n##7) C3(n##8) C3(n##9)
> +#else
> +#define C3(n) C2(n##0) C2(n##4) C2(n##9)
> +#define C4(n) C3(n##0) C3(n##3) C3(n##7)
> +#endif
> +#define TESTS C4(1) C1(10010) C1(10012) C1(16144)
> +
> +TESTS
> +
> +struct S { T x; T (*foo) (T, T *); };
> +
> +#undef C
> +#define C(n) { n - 10000, foo##n },
> +
> +struct S tests[] = {
> +TESTS
> + { 0, 0 }
> +};
> +
> +int
> +main ()
> +{
> + int i, j, k;
> + for (k = 0; tests[k].x; k++)
> + for (i = 0; i < sizeof (T) * __CHAR_BIT__; i++)
> + for (j = -5; j <= 5; j++)
> + {
> + T x = ((T) 1 << i) + j;
> + T r1, r2;
> + if (foo (x, tests[k].x, &r1) != tests[k].foo (x, &r2)
> + || r1 != r2)
> + __builtin_abort ();
> + }
> + return 0;
> +}
> --- gcc/testsuite/gcc.dg/pr97459-6.c.jj 2020-12-01 18:28:55.452336978 +0100
> +++ gcc/testsuite/gcc.dg/pr97459-6.c 2020-12-01 18:31:50.667376785 +0100
> @@ -0,0 +1,62 @@
> +/* PR rtl-optimization/97459 */
> +/* { dg-do run } */
> +/* { dg-options "-O2" } */
> +/* { dg-additional-options "-DEXPENSIVE" { target run_expensive_tests } } */
> +
> +#ifdef __SIZEOF_INT128__
> +typedef __int128_t T;
> +typedef __uint128_t U;
> +#else
> +typedef long long T;
> +typedef unsigned long long U;
> +#endif
> +
> +T __attribute__((noipa)) foo (T x, T n, T *r) { *r = x % n; return x / n; }
> +#define C(n) T __attribute__((noipa)) foo##n (T x, T *r) { *r = x % (n - 10000); return x / (n - 10000); }
> +
> +#define C1(n) C(n##1) C(n##3) C(n##5) C(n##7) C(n##9)
> +#define C2(n) C1(n##0) C1(n##1) C1(n##2) C1(n##3) C1(n##4) \
> + C1(n##5) C1(n##6) C1(n##7) C1(n##8) C1(n##9)
> +#ifdef EXPENSIVE
> +#define C3(n) C2(n##0) C2(n##1) C2(n##2) C2(n##3) C2(n##4) \
> + C2(n##5) C2(n##6) C2(n##7) C2(n##8) C2(n##9)
> +#define C4(n) C3(n##0) C3(n##1) C3(n##2) C3(n##3) C3(n##4) \
> + C3(n##5) C3(n##6) C3(n##7) C3(n##8) C3(n##9)
> +#else
> +#define C3(n) C2(n##0) C2(n##4) C2(n##9)
> +#define C4(n) C3(n##0) C3(n##3) C3(n##7)
> +#endif
> +#define TESTS C4(1) C1(10010) C1(10012) C1(16144)
> +
> +TESTS
> +
> +struct S { T x; T (*foo) (T, T *); };
> +
> +#undef C
> +#define C(n) { n - 10000, foo##n },
> +
> +struct S tests[] = {
> +TESTS
> + { 0, 0 }
> +};
> +
> +int
> +main ()
> +{
> + int i, j, k;
> + for (k = 0; tests[k].x; k++)
> + for (i = 0; i < sizeof (T) * __CHAR_BIT__; i++)
> + for (j = -5; j <= 5; j++)
> + {
> + U x = ((U) 1 << i) + j;
> + T r1 = 0, r2 = 0;
> + if (foo ((T) x, tests[k].x, &r1) != tests[k].foo ((T) x, &r2)
> + || r1 != r2)
> + __builtin_abort ();
> + r1 = 0; r2 = 0;
> + if (foo ((T) -x, tests[k].x, &r1) != tests[k].foo ((T) -x, &r2)
> + || r1 != r2)
> + __builtin_abort ();
> + }
> + return 0;
> +}
>
> Jakub
>
>
--
Richard Biener <rguenther@suse.de>
SUSE Software Solutions Germany GmbH, Maxfeldstrasse 5, 90409 Nuernberg,
Germany; GF: Felix Imendörffer; HRB 36809 (AG Nuernberg)
More information about the Gcc-patches
mailing list