re PR middle-end/91450 (__builtin_mul_overflow(A,B,R) wrong code if product < 0, *R is unsigned, and !(A&B))

PR middle-end/91450
	* internal-fn.c (expand_mul_overflow): For s1 * s2 -> ur, if one
	operand is negative and one non-negative, compare the non-negative
	one against 0 rather than comparing s1 & s2 against 0.  Otherwise,
	don't compare (s1 & s2) == 0, but compare separately both s1 == 0
	and s2 == 0, unless one of them is known to be negative.  Remove
	tem2 variable, use tem where tem2 has been used before.

	* gcc.c-torture/execute/pr91450-1.c: New test.
	* gcc.c-torture/execute/pr91450-2.c: New test.

From-SVN: r278437
This commit is contained in:
Jakub Jelinek 2019-11-19 10:15:53 +01:00 committed by Jakub Jelinek
parent 655b60f966
commit beeac4c271
5 changed files with 194 additions and 11 deletions

View file

@ -1,3 +1,13 @@
2019-11-19 Jakub Jelinek <jakub@redhat.com>
PR middle-end/91450
* internal-fn.c (expand_mul_overflow): For s1 * s2 -> ur, if one
operand is negative and one non-negative, compare the non-negative
one against 0 rather than comparing s1 & s2 against 0. Otherwise,
don't compare (s1 & s2) == 0, but compare separately both s1 == 0
and s2 == 0, unless one of them is known to be negative. Remove
tem2 variable, use tem where tem2 has been used before.
2019-11-19 Eric Botcazou <ebotcazou@adacore.com>
* doc/invoke.texi (-gno-internal-reset-location-views): Fix typo.

View file

@ -1409,7 +1409,7 @@ expand_mul_overflow (location_t loc, tree lhs, tree arg0, tree arg1,
/* s1 * s2 -> ur */
if (!uns0_p && !uns1_p && unsr_p)
{
rtx tem, tem2;
rtx tem;
switch (pos_neg0 | pos_neg1)
{
case 1: /* Both operands known to be non-negative. */
@ -1439,10 +1439,8 @@ expand_mul_overflow (location_t loc, tree lhs, tree arg0, tree arg1,
ops.op2 = NULL_TREE;
ops.location = loc;
res = expand_expr_real_2 (&ops, NULL_RTX, mode, EXPAND_NORMAL);
tem = expand_binop (mode, and_optab, op0, op1, NULL_RTX, false,
OPTAB_LIB_WIDEN);
do_compare_rtx_and_jump (tem, const0_rtx, EQ, true, mode,
NULL_RTX, NULL, done_label,
do_compare_rtx_and_jump (pos_neg0 == 1 ? op0 : op1, const0_rtx, EQ,
true, mode, NULL_RTX, NULL, done_label,
profile_probability::very_likely ());
goto do_error_label;
}
@ -1473,16 +1471,23 @@ expand_mul_overflow (location_t loc, tree lhs, tree arg0, tree arg1,
arg1 = error_mark_node;
emit_jump (do_main_label);
emit_label (after_negate_label);
tem2 = expand_binop (mode, xor_optab, op0, op1, NULL_RTX, false,
OPTAB_LIB_WIDEN);
do_compare_rtx_and_jump (tem2, const0_rtx, GE, false, mode, NULL_RTX,
NULL, do_main_label, profile_probability::very_likely ());
tem = expand_binop (mode, xor_optab, op0, op1, NULL_RTX, false,
OPTAB_LIB_WIDEN);
do_compare_rtx_and_jump (tem, const0_rtx, GE, false, mode, NULL_RTX,
NULL, do_main_label,
profile_probability::very_likely ());
/* One argument is negative here, the other positive. This
overflows always, unless one of the arguments is 0. But
if e.g. s2 is 0, (U) s1 * 0 doesn't overflow, whatever s1
is, thus we can keep do_main code oring in overflow as is. */
do_compare_rtx_and_jump (tem, const0_rtx, EQ, true, mode, NULL_RTX,
NULL, do_main_label, profile_probability::very_likely ());
if (pos_neg0 != 2)
do_compare_rtx_and_jump (op0, const0_rtx, EQ, true, mode, NULL_RTX,
NULL, do_main_label,
profile_probability::very_unlikely ());
if (pos_neg1 != 2)
do_compare_rtx_and_jump (op1, const0_rtx, EQ, true, mode, NULL_RTX,
NULL, do_main_label,
profile_probability::very_unlikely ());
expand_arith_set_overflow (lhs, target);
emit_label (do_main_label);
goto do_main;

View file

@ -1,5 +1,9 @@
2019-11-19 Jakub Jelinek <jakub@redhat.com>
PR middle-end/91450
* gcc.c-torture/execute/pr91450-1.c: New test.
* gcc.c-torture/execute/pr91450-2.c: New test.
PR c++/92504
* g++.dg/gomp/pr92504.C: New test.

View file

@ -0,0 +1,88 @@
/* PR middle-end/91450 */
__attribute__((noipa)) unsigned long long
foo (int a, int b)
{
unsigned long long r;
if (!__builtin_mul_overflow (a, b, &r))
__builtin_abort ();
return r;
}
__attribute__((noipa)) unsigned long long
bar (int a, int b)
{
unsigned long long r;
if (a >= 0)
return 0;
if (!__builtin_mul_overflow (a, b, &r))
__builtin_abort ();
return r;
}
__attribute__((noipa)) unsigned long long
baz (int a, int b)
{
unsigned long long r;
if (b >= 0)
return 0;
if (!__builtin_mul_overflow (a, b, &r))
__builtin_abort ();
return r;
}
__attribute__((noipa)) unsigned long long
qux (int a, int b)
{
unsigned long long r;
if (a >= 0)
return 0;
if (b < 0)
return 0;
if (!__builtin_mul_overflow (a, b, &r))
__builtin_abort ();
return r;
}
__attribute__((noipa)) unsigned long long
quux (int a, int b)
{
unsigned long long r;
if (a < 0)
return 0;
if (b >= 0)
return 0;
if (!__builtin_mul_overflow (a, b, &r))
__builtin_abort ();
return r;
}
int
main ()
{
if (foo (-4, 2) != -8ULL)
__builtin_abort ();
if (foo (2, -4) != -8ULL)
__builtin_abort ();
if (bar (-4, 2) != -8ULL)
__builtin_abort ();
if (baz (2, -4) != -8ULL)
__builtin_abort ();
if (qux (-4, 2) != -8ULL)
__builtin_abort ();
if (quux (2, -4) != -8ULL)
__builtin_abort ();
if (foo (-2, 1) != -2ULL)
__builtin_abort ();
if (foo (1, -2) != -2ULL)
__builtin_abort ();
if (bar (-2, 1) != -2ULL)
__builtin_abort ();
if (baz (1, -2) != -2ULL)
__builtin_abort ();
if (qux (-2, 1) != -2ULL)
__builtin_abort ();
if (quux (1, -2) != -2ULL)
__builtin_abort ();
return 0;
}

View file

@ -0,0 +1,76 @@
/* PR middle-end/91450 */
__attribute__((noipa)) void
foo (int a, int b)
{
unsigned long long r;
if (__builtin_mul_overflow (a, b, &r))
__builtin_abort ();
if (r != 0)
__builtin_abort ();
}
__attribute__((noipa)) void
bar (int a, int b)
{
unsigned long long r;
if (a >= 0)
return;
if (__builtin_mul_overflow (a, b, &r))
__builtin_abort ();
if (r != 0)
__builtin_abort ();
}
__attribute__((noipa)) void
baz (int a, int b)
{
unsigned long long r;
if (b >= 0)
return;
if (__builtin_mul_overflow (a, b, &r))
__builtin_abort ();
if (r != 0)
__builtin_abort ();
}
__attribute__((noipa)) void
qux (int a, int b)
{
unsigned long long r;
if (a >= 0)
return;
if (b < 0)
return;
if (__builtin_mul_overflow (a, b, &r))
__builtin_abort ();
if (r != 0)
__builtin_abort ();
}
__attribute__((noipa)) void
quux (int a, int b)
{
unsigned long long r;
if (a < 0)
return;
if (b >= 0)
return;
if (__builtin_mul_overflow (a, b, &r))
__builtin_abort ();
if (r != 0)
__builtin_abort ();
}
int
main ()
{
foo (-4, 0);
foo (0, -4);
foo (0, 0);
bar (-4, 0);
baz (0, -4);
qux (-4, 0);
quux (0, -4);
return 0;
}