Hi! As the testcase shows, expand_divmod doesn't handle properly division/remainder in modes larger than HOST_BITS_PER_WIDE_INT like TImode - if op1 is "negative" CONST_INT, it means it has 65 most significant bits set, but EXACT_POWER_OF_2_OR_ZERO_P will happily return true on that and optimize the division into a right shift. Fixed by taking into account that EXACT_POWER_OF_2_OR_ZERO_P works only if either the mode bitsize/precision are smaller or equal than HWI, or if the value doesn't have MSB bit set.
Bootstrapped/regtested on x86_64-linux and i686-linux, ok for trunk? 2016-11-18 Jakub Jelinek <ja...@redhat.com> PR middle-end/78416 * expmed.c (expand_divmod): For modes wider than HWI, take into account implicit 1 bits above msb for EXACT_POWER_OF_2_OR_ZERO_P. Formatting fixes. Use size <= HOST_BITS_PER_WIDE_INT instead of HOST_BITS_PER_WIDE_INT >= size. * gcc.dg/torture/pr78416.c: New test. --- gcc/expmed.c.jj 2016-10-31 13:28:12.000000000 +0100 +++ gcc/expmed.c 2016-11-18 16:57:57.608703605 +0100 @@ -3998,7 +3998,15 @@ expand_divmod (int rem_flag, enum tree_c if (unsignedp) ext_op1 &= GET_MODE_MASK (mode); op1_is_pow2 = ((EXACT_POWER_OF_2_OR_ZERO_P (ext_op1) - || (! unsignedp && EXACT_POWER_OF_2_OR_ZERO_P (-ext_op1)))); + /* If mode is wider than HWI and op1 has msb set, + then it has there extra implicit 1 bits above it. */ + && (GET_MODE_PRECISION (mode) <= HOST_BITS_PER_WIDE_INT + || INTVAL (op1) >= 0)) + || (! unsignedp + && EXACT_POWER_OF_2_OR_ZERO_P (-ext_op1) + && (GET_MODE_PRECISION (mode) + <= HOST_BITS_PER_WIDE_INT + || INTVAL (op1) < 0))); } /* @@ -4141,8 +4149,17 @@ expand_divmod (int rem_flag, enum tree_c op1_is_constant = CONST_INT_P (op1); op1_is_pow2 = (op1_is_constant && ((EXACT_POWER_OF_2_OR_ZERO_P (INTVAL (op1)) - || (! unsignedp - && EXACT_POWER_OF_2_OR_ZERO_P (-UINTVAL (op1)))))); + /* If mode is wider than HWI and op1 has msb set, + then it has there extra implicit 1 bits above + it. */ + && (GET_MODE_PRECISION (compute_mode) + <= HOST_BITS_PER_WIDE_INT + || INTVAL (op1) >= 0)) + || (! unsignedp + && EXACT_POWER_OF_2_OR_ZERO_P (-UINTVAL (op1)) + && (GET_MODE_PRECISION (compute_mode) + <= HOST_BITS_PER_WIDE_INT + || INTVAL (op1) < 0)))); } /* If one of the operands is a volatile MEM, copy it into a register. */ @@ -4185,7 +4202,8 @@ expand_divmod (int rem_flag, enum tree_c unsigned HOST_WIDE_INT d = (INTVAL (op1) & GET_MODE_MASK (compute_mode)); - if (EXACT_POWER_OF_2_OR_ZERO_P (d)) + if (EXACT_POWER_OF_2_OR_ZERO_P (d) + && (INTVAL (op1) >= 0 || size <= HOST_BITS_PER_WIDE_INT)) { pre_shift = floor_log2 (d); if (rem_flag) @@ -4325,7 +4343,7 @@ expand_divmod (int rem_flag, enum tree_c else if (d == -1) quotient = expand_unop (compute_mode, neg_optab, op0, tquotient, 0); - else if (HOST_BITS_PER_WIDE_INT >= size + else if (size <= HOST_BITS_PER_WIDE_INT && abs_d == HOST_WIDE_INT_1U << (size - 1)) { /* This case is not handled correctly below. */ @@ -4335,6 +4353,7 @@ expand_divmod (int rem_flag, enum tree_c goto fail1; } else if (EXACT_POWER_OF_2_OR_ZERO_P (d) + && (size <= HOST_BITS_PER_WIDE_INT || d >= 0) && (rem_flag ? smod_pow2_cheap (speed, compute_mode) : sdiv_pow2_cheap (speed, compute_mode)) @@ -4348,7 +4367,9 @@ expand_divmod (int rem_flag, enum tree_c compute_mode) != CODE_FOR_nothing))) ; - else if (EXACT_POWER_OF_2_OR_ZERO_P (abs_d)) + else if (EXACT_POWER_OF_2_OR_ZERO_P (abs_d) + && (size <= HOST_BITS_PER_WIDE_INT + || abs_d != (unsigned HOST_WIDE_INT) d)) { if (rem_flag) { @@ -4483,7 +4504,7 @@ expand_divmod (int rem_flag, enum tree_c case FLOOR_DIV_EXPR: case FLOOR_MOD_EXPR: /* We will come here only for signed operations. */ - if (op1_is_constant && HOST_BITS_PER_WIDE_INT >= size) + if (op1_is_constant && size <= HOST_BITS_PER_WIDE_INT) { unsigned HOST_WIDE_INT mh, ml; int pre_shift, lgup, post_shift; @@ -4552,9 +4573,8 @@ expand_divmod (int rem_flag, enum tree_c op0, constm1_rtx), NULL_RTX); t2 = expand_binop (compute_mode, ior_optab, op0, t1, NULL_RTX, 0, OPTAB_WIDEN); - nsign = expand_shift - (RSHIFT_EXPR, compute_mode, t2, - size - 1, NULL_RTX, 0); + nsign = expand_shift (RSHIFT_EXPR, compute_mode, t2, + size - 1, NULL_RTX, 0); t3 = force_operand (gen_rtx_MINUS (compute_mode, t1, nsign), NULL_RTX); t4 = expand_divmod (0, TRUNC_DIV_EXPR, compute_mode, t3, op1, @@ -4663,7 +4683,10 @@ expand_divmod (int rem_flag, enum tree_c case CEIL_MOD_EXPR: if (unsignedp) { - if (op1_is_constant && EXACT_POWER_OF_2_OR_ZERO_P (INTVAL (op1))) + if (op1_is_constant + && EXACT_POWER_OF_2_OR_ZERO_P (INTVAL (op1)) + && (size <= HOST_BITS_PER_WIDE_INT + || INTVAL (op1) >= 0)) { rtx t1, t2, t3; unsigned HOST_WIDE_INT d = INTVAL (op1); @@ -4876,7 +4899,7 @@ expand_divmod (int rem_flag, enum tree_c break; case EXACT_DIV_EXPR: - if (op1_is_constant && HOST_BITS_PER_WIDE_INT >= size) + if (op1_is_constant && size <= HOST_BITS_PER_WIDE_INT) { HOST_WIDE_INT d = INTVAL (op1); unsigned HOST_WIDE_INT ml; --- gcc/testsuite/gcc.dg/torture/pr78416.c.jj 2016-11-18 16:43:35.010448325 +0100 +++ gcc/testsuite/gcc.dg/torture/pr78416.c 2016-11-18 16:44:40.388634204 +0100 @@ -0,0 +1,17 @@ +/* PR middle-end/78416 */ +/* { dg-do run { target int128 } } */ + +int +main () +{ + unsigned __int128 x; + x = 0xFFFFFFFFFFFFFFFFULL; + x /= ~0x7FFFFFFFFFFFFFFFLL; + if (x != 0) + __builtin_abort (); + x = ~0x7FFFFFFFFFFFFFFELL; + x /= ~0x7FFFFFFFFFFFFFFFLL; + if (x != 1) + __builtin_abort (); + return 0; +} Jakub