[PATCH] Selectively trap if ranger and vr-values disagree on range builtins.
Aldy Hernandez
aldyh@redhat.com
Tue Oct 27 15:58:43 GMT 2020
For the record, this is what I envision the follow-up patch to be (untested).
Aldy
diff --git a/gcc/vr-values.c b/gcc/vr-values.c
index 9f5943a1ab6..3db72a360a6 100644
--- a/gcc/vr-values.c
+++ b/gcc/vr-values.c
@@ -1159,188 +1159,16 @@ check_for_binary_op_overflow (range_query *query,
successful. */
bool
-vr_values::extract_range_builtin (value_range_equiv *vr, gimple *stmt)
+vr_values::extract_range_from_ubsan_builtin (value_range_equiv *vr,
gimple *stmt)
{
gcc_assert (is_gimple_call (stmt));
tree type = gimple_expr_type (stmt);
- tree arg;
- int mini, maxi, zerov = 0, prec;
enum tree_code subcode = ERROR_MARK;
combined_fn cfn = gimple_call_combined_fn (stmt);
scalar_int_mode mode;
switch (cfn)
{
- case CFN_BUILT_IN_CONSTANT_P:
- /* Resolve calls to __builtin_constant_p after inlining. */
- if (cfun->after_inlining)
- {
- vr->set_zero (type);
- vr->equiv_clear ();
- return true;
- }
- break;
- /* Both __builtin_ffs* and __builtin_popcount return
- [0, prec]. */
- CASE_CFN_FFS:
- CASE_CFN_POPCOUNT:
- arg = gimple_call_arg (stmt, 0);
- prec = TYPE_PRECISION (TREE_TYPE (arg));
- mini = 0;
- maxi = prec;
- if (TREE_CODE (arg) == SSA_NAME)
- {
- const value_range_equiv *vr0 = get_value_range (arg);
- /* If arg is non-zero, then ffs or popcount are non-zero. */
- if (range_includes_zero_p (vr0) == 0)
- mini = 1;
- /* If some high bits are known to be zero,
- we can decrease the maximum. */
- if (vr0->kind () == VR_RANGE
- && TREE_CODE (vr0->max ()) == INTEGER_CST
- && !operand_less_p (vr0->min (),
- build_zero_cst (TREE_TYPE (vr0->min ()))))
- maxi = tree_floor_log2 (vr0->max ()) + 1;
- }
- goto bitop_builtin;
- /* __builtin_parity* returns [0, 1]. */
- CASE_CFN_PARITY:
- mini = 0;
- maxi = 1;
- goto bitop_builtin;
- /* __builtin_clz* return [0, prec-1], except for
- when the argument is 0, but that is undefined behavior.
- Always handle __builtin_clz* which can be only written
- by user as UB on 0 and so [0, prec-1] range, and the internal-fn
- calls depending on how CLZ_DEFINED_VALUE_AT_ZERO is defined. */
- CASE_CFN_CLZ:
- arg = gimple_call_arg (stmt, 0);
- prec = TYPE_PRECISION (TREE_TYPE (arg));
- mini = 0;
- maxi = prec - 1;
- mode = SCALAR_INT_TYPE_MODE (TREE_TYPE (arg));
- if (gimple_call_internal_p (stmt))
- {
- if (optab_handler (clz_optab, mode) != CODE_FOR_nothing
- && CLZ_DEFINED_VALUE_AT_ZERO (mode, zerov) == 2)
- {
- /* Handle only the single common value. */
- 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)
- {
- const value_range_equiv *vr0 = get_value_range (arg);
- /* From clz of VR_RANGE minimum we can compute
- result maximum. */
- if (vr0->kind () == VR_RANGE
- && TREE_CODE (vr0->min ()) == INTEGER_CST
- && integer_nonzerop (vr0->min ()))
- {
- maxi = prec - 1 - tree_floor_log2 (vr0->min ());
- if (mini == -2)
- mini = 0;
- }
- else if (vr0->kind () == VR_ANTI_RANGE
- && integer_zerop (vr0->min ()))
- {
- maxi = prec - 1;
- mini = 0;
- }
- if (mini == -2)
- break;
- /* From clz of VR_RANGE maximum we can compute
- result minimum. */
- if (vr0->kind () == VR_RANGE
- && TREE_CODE (vr0->max ()) == INTEGER_CST)
- {
- int newmini = prec - 1 - tree_floor_log2 (vr0->max ());
- if (newmini == prec)
- {
- if (maxi == prec)
- mini = prec;
- }
- else
- mini = newmini;
- }
- }
- if (mini == -2)
- break;
- goto bitop_builtin;
- /* __builtin_ctz* return [0, prec-1], except for
- when the argument is 0, but that is undefined behavior.
- Always handle __builtin_ctz* which can be only written
- by user as UB on 0 and so [0, prec-1] range, and the internal-fn
- calls depending on how CTZ_DEFINED_VALUE_AT_ZERO is defined. */
- CASE_CFN_CTZ:
- arg = gimple_call_arg (stmt, 0);
- prec = TYPE_PRECISION (TREE_TYPE (arg));
- mini = 0;
- maxi = prec - 1;
- mode = SCALAR_INT_TYPE_MODE (TREE_TYPE (arg));
- if (gimple_call_internal_p (stmt))
- {
- if (optab_handler (ctz_optab, mode) != CODE_FOR_nothing
- && CTZ_DEFINED_VALUE_AT_ZERO (mode, zerov) == 2)
- {
- /* Handle only the two common values. */
- if (zerov == -1)
- mini = -1;
- else if (zerov == prec)
- maxi = prec;
- else
- /* Magic value to give up, unless vr0 proves
- arg is non-zero. */
- mini = -2;
- }
- }
- if (TREE_CODE (arg) == SSA_NAME)
- {
- const value_range_equiv *vr0 = get_value_range (arg);
- /* If arg is non-zero, then use [0, prec - 1]. */
- if ((vr0->kind () == VR_RANGE
- && integer_nonzerop (vr0->min ()))
- || (vr0->kind () == VR_ANTI_RANGE
- && integer_zerop (vr0->min ())))
- {
- mini = 0;
- maxi = prec - 1;
- }
- /* If some high bits are known to be zero,
- we can decrease the result maximum. */
- if (vr0->kind () == VR_RANGE
- && TREE_CODE (vr0->max ()) == INTEGER_CST)
- {
- int newmaxi = tree_floor_log2 (vr0->max ());
- if (newmaxi == -1)
- {
- if (mini == -1)
- maxi = -1;
- else if (maxi == prec)
- mini = prec;
- }
- else if (maxi != prec)
- maxi = newmaxi;
- }
- }
- if (mini == -2)
- break;
- goto bitop_builtin;
- /* __builtin_clrsb* returns [0, prec-1]. */
- CASE_CFN_CLRSB:
- arg = gimple_call_arg (stmt, 0);
- prec = TYPE_PRECISION (TREE_TYPE (arg));
- mini = 0;
- maxi = prec - 1;
- goto bitop_builtin;
- bitop_builtin:
- vr->set (build_int_cst (type, mini), build_int_cst (type, maxi));
- return true;
case CFN_UBSAN_CHECK_ADD:
subcode = PLUS_EXPR;
break;
@@ -1350,47 +1178,6 @@ vr_values::extract_range_builtin
(value_range_equiv *vr, gimple *stmt)
case CFN_UBSAN_CHECK_MUL:
subcode = MULT_EXPR;
break;
- case CFN_GOACC_DIM_SIZE:
- case CFN_GOACC_DIM_POS:
- /* Optimizing these two internal functions helps the loop
- optimizer eliminate outer comparisons. Size is [1,N]
- and pos is [0,N-1]. */
- {
- bool is_pos = cfn == CFN_GOACC_DIM_POS;
- int axis = oacc_get_ifn_dim_arg (stmt);
- int size = oacc_get_fn_dim_size (current_function_decl, axis);
-
- if (!size)
- /* If it's dynamic, the backend might know a hardware
- limitation. */
- size = targetm.goacc.dim_limit (axis);
-
- tree type = TREE_TYPE (gimple_call_lhs (stmt));
- vr->set(build_int_cst (type, is_pos ? 0 : 1),
- size
- ? build_int_cst (type, size - is_pos) : vrp_val_max (type));
- }
- return true;
- case CFN_BUILT_IN_STRLEN:
- if (tree lhs = gimple_call_lhs (stmt))
- if (ptrdiff_type_node
- && (TYPE_PRECISION (ptrdiff_type_node)
- == TYPE_PRECISION (TREE_TYPE (lhs))))
- {
- tree type = TREE_TYPE (lhs);
- tree max = vrp_val_max (ptrdiff_type_node);
- wide_int wmax = wi::to_wide (max, TYPE_PRECISION (TREE_TYPE (max)));
- tree range_min = build_zero_cst (type);
- /* To account for the terminating NUL, the maximum length
- is one less than the maximum array size, which in turn
- is one less than PTRDIFF_MAX (or SIZE_MAX where it's
- smaller than the former type).
- FIXME: Use max_object_size() - 1 here. */
- tree range_max = wide_int_to_tree (type, wmax - 2);
- vr->set (range_min, range_max);
- return true;
- }
- break;
default:
break;
}
@@ -1430,20 +1217,27 @@ vr_values::extract_range_basic
(value_range_equiv *vr, gimple *stmt)
bool sop;
tree type = gimple_expr_type (stmt);
- if (is_gimple_call (stmt) && extract_range_builtin (vr, stmt))
+ if (is_gimple_call (stmt))
{
combined_fn cfn = gimple_call_combined_fn (stmt);
- if (cfn == CFN_UBSAN_CHECK_ADD
- || cfn == CFN_UBSAN_CHECK_SUB
- || cfn == CFN_UBSAN_CHECK_MUL)
- return;
-
- value_range_equiv tmp;
- /* Assert that any ranges vr_values::extract_range_builtin gets
- are also handled by the ranger counterpart. */
- gcc_assert (range_of_builtin_call (*this, tmp, as_a<gcall *> (stmt)));
- gcc_assert (tmp.equal_p (*vr, /*ignore_equivs=*/false));
- return;
+ switch (cfn)
+ {
+ case CFN_UBSAN_CHECK_ADD:
+ case CFN_UBSAN_CHECK_SUB:
+ case CFN_UBSAN_CHECK_MUL:
+ if (extract_range_from_ubsan_builtin (vr, stmt))
+ return;
+ break;
+ default:
+ if (range_of_builtin_call (*this, *vr, as_a<gcall *> (stmt)))
+ {
+ /* The original code nuked equivalences every time a
+ range was found, so do the same here. */
+ vr->equiv_clear ();
+ return;
+ }
+ break;
+ }
}
/* Handle extraction of the two results (result of arithmetics and
a flag whether arithmetics overflowed) from {ADD,SUB,MUL}_OVERFLOW
diff --git a/gcc/vr-values.h b/gcc/vr-values.h
index 59fac0c4b1e..712d029909f 100644
--- a/gcc/vr-values.h
+++ b/gcc/vr-values.h
@@ -148,7 +148,7 @@ class vr_values : public range_query
void extract_range_from_comparison (value_range_equiv *, gimple *);
void vrp_visit_assignment_or_call (gimple*, tree *, value_range_equiv *);
void vrp_visit_switch_stmt (gswitch *, edge *);
- bool extract_range_builtin (value_range_equiv *, gimple *);
+ bool extract_range_from_ubsan_builtin (value_range_equiv *, gimple *);
/* This probably belongs in the lattice rather than in here. */
bool values_propagated;
On Tue, Oct 27, 2020 at 4:29 PM Aldy Hernandez <aldyh@redhat.com> wrote:
>
> The UBSAN builtins degrade into PLUS/MINUS/MULT and call
> extract_range_from_binary_expr, which as the PR shows, can special
> case some symbolics which the ranger doesn't currently handle.
>
> Looking at vr_values::extract_range_builtin(), I see that every single
> place where we ask for a range, we bail on non-integers (symbolics,
> etc). That is, with the exception of the UBSAN builtins.
>
> Since this seems to be particular to UBSAN, we could still go with the
> original plan of removing the duplicity in ranger vs vr-values, but
> leave in the UBSAN builtin handling. This isn't ideal, as we'd like
> to remove all the common code, but I'd be willing to put up with UBSAN
> duplication for the time being.
>
> This patch disables the assert on the UBSAN builtins, while still
> trapping if any other differences are found between the vr_values and
> the ranger versions of builtin range handling.
>
> As a follow-up, once Fedora can test this approach, I'll remove all
> the builtin code from extract_range_builtin, with the exception of the
> UBSAN stuff (renaming it to extract_range_ubsan_builtin).
>
> Since the builtin code has proven fickle across architectures, I've
> tested this with {-m32,-m64,-fsanitize=signed-integer-overflow} on
> x86, ppc64le, and aarch64. I think this should be enough. If it
> isn't, we can revert the patch, and leave the duplicate code until
> the next release cycle when hopefully vr_values, evrp, and friends
> will all be overhauled.
>
> Andrew, do you have any thoughts on this?
>
> Aldy
>
> gcc/ChangeLog:
>
> PR tree-optimization/97505
> * vr-values.c (vr_values::extract_range_basic): Enable
> trap again for everything except UBSAN builtins.
> ---
> gcc/vr-values.c | 9 ++++++---
> 1 file changed, 6 insertions(+), 3 deletions(-)
>
> diff --git a/gcc/vr-values.c b/gcc/vr-values.c
> index 7a0e70eab64..9f5943a1ab6 100644
> --- a/gcc/vr-values.c
> +++ b/gcc/vr-values.c
> @@ -1432,14 +1432,17 @@ vr_values::extract_range_basic (value_range_equiv *vr, gimple *stmt)
>
> if (is_gimple_call (stmt) && extract_range_builtin (vr, stmt))
> {
> + combined_fn cfn = gimple_call_combined_fn (stmt);
> + if (cfn == CFN_UBSAN_CHECK_ADD
> + || cfn == CFN_UBSAN_CHECK_SUB
> + || cfn == CFN_UBSAN_CHECK_MUL)
> + return;
> +
> value_range_equiv tmp;
> /* Assert that any ranges vr_values::extract_range_builtin gets
> are also handled by the ranger counterpart. */
> gcc_assert (range_of_builtin_call (*this, tmp, as_a<gcall *> (stmt)));
> -#if 0
> - /* Disable this while PR97505 is resolved. */
> gcc_assert (tmp.equal_p (*vr, /*ignore_equivs=*/false));
> -#endif
> return;
> }
> /* Handle extraction of the two results (result of arithmetics and
> --
> 2.26.2
>
More information about the Gcc-patches
mailing list