vrp: Fix up gcc.target/aarch64/pr90838.c [PR97312, PR94801]

> Perhaps another way out of this would be document and enforce that
> __builtin_c[lt]z{,l,ll} etc calls are undefined at zero, but C[TL]Z ifn
> calls are defined there based on *_DEFINED_VALUE_AT_ZERO (*) == 2

The following patch implements that, i.e. __builtin_c?z* now take full
advantage of them being UB at zero, while the ifns are well defined at zero
if *_DEFINED_VALUE_AT_ZERO (*) == 2.  That is what fixes PR94801.

Furthermore, to fix PR97312, if it is well defined at zero and the value at
zero is prec, we don't lower the maximum unless the argument is known to be
non-zero.
For gimple-range.cc I guess we could improve it if needed e.g. by returning
a [0,7][32,32] range for .CTZ of e.g. [0,137], but for now it (roughly)
matches what vr-values.c does.

2020-10-09  Jakub Jelinek  <jakub@redhat.com>

	PR tree-optimization/94801
	PR target/97312
	* vr-values.c (vr_values::extract_range_basic) <CASE_CFN_CLZ,
	CASE_CFN_CTZ>: When stmt is not an internal-fn call or
	C?Z_DEFINED_VALUE_AT_ZERO is not 2, assume argument is not zero
	and thus use [0, prec-1] range unless it can be further improved.
	For CTZ, don't update maxi from upper bound if it was previously prec.
	* gimple-range.cc (gimple_ranger::range_of_builtin_call) <CASE_CFN_CLZ,
	CASE_CFN_CTZ>: Likewise.

	* gcc.dg/tree-ssa/pr94801.c: New test.
This commit is contained in:
Jakub Jelinek 2020-10-09 10:19:16 +02:00
parent 600cf1128e
commit 781634daea
3 changed files with 136 additions and 66 deletions

View File

@ -636,28 +636,38 @@ gimple_ranger::range_of_builtin_call (irange &r, gcall *call)
// __builtin_c[lt]z* return [0, prec-1], except when the // __builtin_c[lt]z* return [0, prec-1], except when the
// argument is 0, but that is undefined behavior. // argument is 0, but that is undefined behavior.
// //
// On many targets where the CLZ RTL or optab value is defined // For __builtin_c[lt]z* consider argument of 0 always undefined
// for 0, the value is prec, so include that in the range by // behavior, for internal fns depending on C?Z_DEFINED_VALUE_AT_ZERO.
// default.
arg = gimple_call_arg (call, 0); arg = gimple_call_arg (call, 0);
prec = TYPE_PRECISION (TREE_TYPE (arg)); prec = TYPE_PRECISION (TREE_TYPE (arg));
mini = 0; mini = 0;
maxi = prec; maxi = prec - 1;
mode = SCALAR_INT_TYPE_MODE (TREE_TYPE (arg)); mode = SCALAR_INT_TYPE_MODE (TREE_TYPE (arg));
if (optab_handler (clz_optab, mode) != CODE_FOR_nothing if (gimple_call_internal_p (call))
&& CLZ_DEFINED_VALUE_AT_ZERO (mode, zerov) {
// Only handle the single common value. if (optab_handler (clz_optab, mode) != CODE_FOR_nothing
&& zerov != prec) && CLZ_DEFINED_VALUE_AT_ZERO (mode, zerov) == 2)
// Magic value to give up, unless we can prove arg is non-zero. {
mini = -2; // Only handle the single common value.
if (zerov == prec)
maxi = prec;
else
// Magic value to give up, unless we can prove arg is non-zero.
mini = -2;
}
}
gcc_assert (range_of_expr (r, arg, call)); gcc_assert (range_of_expr (r, arg, call));
// From clz of minimum we can compute result maximum. // From clz of minimum we can compute result maximum.
if (r.constant_p ()) if (r.constant_p ())
{ {
maxi = prec - 1 - wi::floor_log2 (r.lower_bound ()); int newmaxi = prec - 1 - wi::floor_log2 (r.lower_bound ());
if (maxi != prec) // Argument is unsigned, so do nothing if it is [0, ...] range.
mini = 0; if (newmaxi != prec)
{
mini = 0;
maxi = newmaxi;
}
} }
else if (!range_includes_zero_p (&r)) else if (!range_includes_zero_p (&r))
{ {
@ -669,9 +679,17 @@ gimple_ranger::range_of_builtin_call (irange &r, gcall *call)
// From clz of maximum we can compute result minimum. // From clz of maximum we can compute result minimum.
if (r.constant_p ()) if (r.constant_p ())
{ {
mini = prec - 1 - wi::floor_log2 (r.upper_bound ()); int newmini = prec - 1 - wi::floor_log2 (r.upper_bound ());
if (mini == prec) if (newmini == prec)
break; {
// Argument range is [0, 0]. If CLZ_DEFINED_VALUE_AT_ZERO
// is 2 with VALUE of prec, return [prec, prec], otherwise
// ignore the range.
if (maxi == prec)
mini = prec;
}
else
mini = newmini;
} }
if (mini == -2) if (mini == -2)
break; break;
@ -682,25 +700,27 @@ gimple_ranger::range_of_builtin_call (irange &r, gcall *call)
// __builtin_ctz* return [0, prec-1], except for when the // __builtin_ctz* return [0, prec-1], except for when the
// argument is 0, but that is undefined behavior. // argument is 0, but that is undefined behavior.
// //
// If there is a ctz optab for this mode and // For __builtin_ctz* consider argument of 0 always undefined
// CTZ_DEFINED_VALUE_AT_ZERO, include that in the range, // behavior, for internal fns depending on CTZ_DEFINED_VALUE_AT_ZERO.
// otherwise just assume 0 won't be seen.
arg = gimple_call_arg (call, 0); arg = gimple_call_arg (call, 0);
prec = TYPE_PRECISION (TREE_TYPE (arg)); prec = TYPE_PRECISION (TREE_TYPE (arg));
mini = 0; mini = 0;
maxi = prec - 1; maxi = prec - 1;
mode = SCALAR_INT_TYPE_MODE (TREE_TYPE (arg)); mode = SCALAR_INT_TYPE_MODE (TREE_TYPE (arg));
if (optab_handler (ctz_optab, mode) != CODE_FOR_nothing if (gimple_call_internal_p (call))
&& CTZ_DEFINED_VALUE_AT_ZERO (mode, zerov))
{ {
// Handle only the two common values. if (optab_handler (ctz_optab, mode) != CODE_FOR_nothing
if (zerov == -1) && CTZ_DEFINED_VALUE_AT_ZERO (mode, zerov) == 2)
mini = -1; {
else if (zerov == prec) // Handle only the two common values.
maxi = prec; if (zerov == -1)
else mini = -1;
// Magic value to give up, unless we can prove arg is non-zero. else if (zerov == prec)
mini = -2; maxi = prec;
else
// Magic value to give up, unless we can prove arg is non-zero.
mini = -2;
}
} }
gcc_assert (range_of_expr (r, arg, call)); gcc_assert (range_of_expr (r, arg, call));
if (!r.undefined_p ()) if (!r.undefined_p ())
@ -714,8 +734,20 @@ gimple_ranger::range_of_builtin_call (irange &r, gcall *call)
// the maximum. // the maximum.
wide_int max = r.upper_bound (); wide_int max = r.upper_bound ();
if (max == 0) if (max == 0)
break; {
maxi = wi::floor_log2 (max); // Argument is [0, 0]. If CTZ_DEFINED_VALUE_AT_ZERO
// is 2 with value -1 or prec, return [-1, -1] or [prec, prec].
// Otherwise ignore the range.
if (mini == -1)
maxi = -1;
else if (maxi == prec)
mini = prec;
}
// If value at zero is prec and 0 is in the range, we can't lower
// the upper bound. We could create two separate ranges though,
// [0,floor_log2(max)][prec,prec] though.
else if (maxi != prec)
maxi = wi::floor_log2 (max);
} }
if (mini == -2) if (mini == -2)
break; break;

View File

@ -0,0 +1,16 @@
/* PR tree-optimization/94801 */
/* { dg-do compile } */
/* { dg-options "-O2 -fdump-tree-optimized" } */
/* { dg-final { scan-tree-dump-times "return 0;" 2 "optimized" } } */
int
foo (int a)
{
return __builtin_clz (a) >> 5;
}
int
bar (int a)
{
return __builtin_ctz (a) >> 5;
}

View File

@ -1208,34 +1208,42 @@ vr_values::extract_range_basic (value_range_equiv *vr, gimple *stmt)
mini = 0; mini = 0;
maxi = 1; maxi = 1;
goto bitop_builtin; goto bitop_builtin;
/* __builtin_c[lt]z* return [0, prec-1], except for /* __builtin_clz* return [0, prec-1], except for
when the argument is 0, but that is undefined behavior. when the argument is 0, but that is undefined behavior.
On many targets where the CLZ RTL or optab value is defined Always handle __builtin_clz* which can be only written
for 0 the value is prec, so include that in the range by user as UB on 0 and so [0, prec-1] range, and the internal-fn
by default. */ calls depending on how CLZ_DEFINED_VALUE_AT_ZERO is defined. */
CASE_CFN_CLZ: CASE_CFN_CLZ:
arg = gimple_call_arg (stmt, 0); arg = gimple_call_arg (stmt, 0);
prec = TYPE_PRECISION (TREE_TYPE (arg)); prec = TYPE_PRECISION (TREE_TYPE (arg));
mini = 0; mini = 0;
maxi = prec; maxi = prec - 1;
mode = SCALAR_INT_TYPE_MODE (TREE_TYPE (arg)); mode = SCALAR_INT_TYPE_MODE (TREE_TYPE (arg));
if (optab_handler (clz_optab, mode) != CODE_FOR_nothing if (gimple_call_internal_p (stmt))
&& CLZ_DEFINED_VALUE_AT_ZERO (mode, zerov) {
/* Handle only the single common value. */ if (optab_handler (clz_optab, mode) != CODE_FOR_nothing
&& zerov != prec) && CLZ_DEFINED_VALUE_AT_ZERO (mode, zerov) == 2)
/* Magic value to give up, unless vr0 proves {
arg is non-zero. */ /* Handle only the single common value. */
mini = -2; if (zerov == prec)
maxi = prec;
/* Magic value to give up, unless vr0 proves
arg is non-zero. */
else
mini = -2;
}
}
if (TREE_CODE (arg) == SSA_NAME) if (TREE_CODE (arg) == SSA_NAME)
{ {
const value_range_equiv *vr0 = get_value_range (arg); const value_range_equiv *vr0 = get_value_range (arg);
/* From clz of VR_RANGE minimum we can compute /* From clz of VR_RANGE minimum we can compute
result maximum. */ result maximum. */
if (vr0->kind () == VR_RANGE if (vr0->kind () == VR_RANGE
&& TREE_CODE (vr0->min ()) == INTEGER_CST) && TREE_CODE (vr0->min ()) == INTEGER_CST
&& integer_nonzerop (vr0->min ()))
{ {
maxi = prec - 1 - tree_floor_log2 (vr0->min ()); maxi = prec - 1 - tree_floor_log2 (vr0->min ());
if (maxi != prec) if (mini == -2)
mini = 0; mini = 0;
} }
else if (vr0->kind () == VR_ANTI_RANGE else if (vr0->kind () == VR_ANTI_RANGE
@ -1251,9 +1259,14 @@ vr_values::extract_range_basic (value_range_equiv *vr, gimple *stmt)
if (vr0->kind () == VR_RANGE if (vr0->kind () == VR_RANGE
&& TREE_CODE (vr0->max ()) == INTEGER_CST) && TREE_CODE (vr0->max ()) == INTEGER_CST)
{ {
mini = prec - 1 - tree_floor_log2 (vr0->max ()); int newmini = prec - 1 - tree_floor_log2 (vr0->max ());
if (mini == prec) if (newmini == prec)
break; {
if (maxi == prec)
mini = prec;
}
else
mini = newmini;
} }
} }
if (mini == -2) if (mini == -2)
@ -1261,27 +1274,30 @@ vr_values::extract_range_basic (value_range_equiv *vr, gimple *stmt)
goto bitop_builtin; goto bitop_builtin;
/* __builtin_ctz* return [0, prec-1], except for /* __builtin_ctz* return [0, prec-1], except for
when the argument is 0, but that is undefined behavior. when the argument is 0, but that is undefined behavior.
If there is a ctz optab for this mode and Always handle __builtin_ctz* which can be only written
CTZ_DEFINED_VALUE_AT_ZERO, include that in the range, by user as UB on 0 and so [0, prec-1] range, and the internal-fn
otherwise just assume 0 won't be seen. */ calls depending on how CTZ_DEFINED_VALUE_AT_ZERO is defined. */
CASE_CFN_CTZ: CASE_CFN_CTZ:
arg = gimple_call_arg (stmt, 0); arg = gimple_call_arg (stmt, 0);
prec = TYPE_PRECISION (TREE_TYPE (arg)); prec = TYPE_PRECISION (TREE_TYPE (arg));
mini = 0; mini = 0;
maxi = prec - 1; maxi = prec - 1;
mode = SCALAR_INT_TYPE_MODE (TREE_TYPE (arg)); mode = SCALAR_INT_TYPE_MODE (TREE_TYPE (arg));
if (optab_handler (ctz_optab, mode) != CODE_FOR_nothing if (gimple_call_internal_p (stmt))
&& CTZ_DEFINED_VALUE_AT_ZERO (mode, zerov))
{ {
/* Handle only the two common values. */ if (optab_handler (ctz_optab, mode) != CODE_FOR_nothing
if (zerov == -1) && CTZ_DEFINED_VALUE_AT_ZERO (mode, zerov) == 2)
mini = -1; {
else if (zerov == prec) /* Handle only the two common values. */
maxi = prec; if (zerov == -1)
else mini = -1;
/* Magic value to give up, unless vr0 proves else if (zerov == prec)
arg is non-zero. */ maxi = prec;
mini = -2; else
/* Magic value to give up, unless vr0 proves
arg is non-zero. */
mini = -2;
}
} }
if (TREE_CODE (arg) == SSA_NAME) if (TREE_CODE (arg) == SSA_NAME)
{ {
@ -1300,10 +1316,16 @@ vr_values::extract_range_basic (value_range_equiv *vr, gimple *stmt)
if (vr0->kind () == VR_RANGE if (vr0->kind () == VR_RANGE
&& TREE_CODE (vr0->max ()) == INTEGER_CST) && TREE_CODE (vr0->max ()) == INTEGER_CST)
{ {
maxi = tree_floor_log2 (vr0->max ()); int newmaxi = tree_floor_log2 (vr0->max ());
/* For vr0 [0, 0] give up. */ if (newmaxi == -1)
if (maxi == -1) {
break; if (mini == -1)
maxi = -1;
else if (maxi == prec)
mini = prec;
}
else if (maxi != prec)
maxi = newmaxi;
} }
} }
if (mini == -2) if (mini == -2)