Makefile.in (OBJS): Add range.o and range-op.o.
* Makefile.in (OBJS): Add range.o and range-op.o. Remove wide-int-range.o. * function-tests.c (test_ranges): New. (function_tests_c_tests): Call test_ranges. * ipa-cp.c (ipa_vr_operation_and_type_effects): Call range_fold_unary_expr instead of extract_range_from_unary_expr. * ipa-prop.c (ipa_compute_jump_functions_for_edge): Same. * range-op.cc: New file. * range-op.h: New file. * range.cc: New file. * range.h: New file. * selftest.h (range_tests): New prototype. * ssa.h: Include range.h. * tree-vrp.c (value_range_base::value_range_base): New constructors. (value_range_base::singleton_p): Do not call ranges_from_anti_range until sure we will need to. (value_range_base::type): Rename gcc_assert to gcc_checking_assert. (vrp_val_is_max): New argument. (vrp_val_is_min): Same. (wide_int_range_set_zero_nonzero_bits): Move from wide-int-range.cc. (extract_range_into_wide_ints): Remove. (extract_range_from_multiplicative_op): Remove. (extract_range_from_pointer_plus_expr): Abstract POINTER_PLUS code from extract_range_from_binary_expr. (extract_range_from_plus_minus_expr): Abstract PLUS/MINUS code from extract_range_from_binary_expr. (extract_range_from_binary_expr): Remove. (normalize_for_range_ops): New. (range_fold_binary_expr): New. (range_fold_unary_expr): New. (value_range_base::num_pairs): New. (value_range_base::lower_bound): New. (value_range_base::upper_bound): New. (value_range_base::upper_bound): New. (value_range_base::contains_p): New. (value_range_base::invert): New. (value_range_base::union_): New. (value_range_base::intersect): New. (range_compatible_p): New. (value_range_base::operator==): New. (determine_value_range_1): Call range_fold_*expr instead of extract_range_from_*expr. * tree-vrp.h (class value_range_base): Add new constructors. Add methods for union_, intersect, operator==, contains_p, num_pairs, lower_bound, upper_bound, invert. (vrp_val_is_min): Add handle_pointers argument. (vrp_val_is_max): Same. (extract_range_from_unary_expr): Remove. (extract_range_from_binary_expr): Remove. (range_fold_unary_expr): New. (range_fold_binary_expr): New. * vr-values.c (vr_values::extract_range_from_binary_expr): Call range_fold_binary_expr instead of extract_range_from_binary_expr. (vr_values::extract_range_basic): Same. (vr_values::extract_range_from_unary_expr): Call range_fold_unary_expr instead of extract_range_from_unary_expr. * wide-int-range.cc: Remove. * wide-int-range.h: Remove. From-SVN: r276504
This commit is contained in:
parent
0a8c8f4d65
commit
38a734350f
16 changed files with 4121 additions and 1781 deletions
|
@ -1,3 +1,67 @@
|
|||
2019-10-03 Aldy Hernandez <aldyh@redhat.com>
|
||||
|
||||
* Makefile.in (OBJS): Add range.o and range-op.o.
|
||||
Remove wide-int-range.o.
|
||||
* function-tests.c (test_ranges): New.
|
||||
(function_tests_c_tests): Call test_ranges.
|
||||
* ipa-cp.c (ipa_vr_operation_and_type_effects): Call
|
||||
range_fold_unary_expr instead of extract_range_from_unary_expr.
|
||||
* ipa-prop.c (ipa_compute_jump_functions_for_edge): Same.
|
||||
* range-op.cc: New file.
|
||||
* range-op.h: New file.
|
||||
* range.cc: New file.
|
||||
* range.h: New file.
|
||||
* selftest.h (range_tests): New prototype.
|
||||
* ssa.h: Include range.h.
|
||||
* tree-vrp.c (value_range_base::value_range_base): New
|
||||
constructors.
|
||||
(value_range_base::singleton_p): Do not call
|
||||
ranges_from_anti_range until sure we will need to.
|
||||
(value_range_base::type): Rename gcc_assert to
|
||||
gcc_checking_assert.
|
||||
(vrp_val_is_max): New argument.
|
||||
(vrp_val_is_min): Same.
|
||||
(wide_int_range_set_zero_nonzero_bits): Move from
|
||||
wide-int-range.cc.
|
||||
(extract_range_into_wide_ints): Remove.
|
||||
(extract_range_from_multiplicative_op): Remove.
|
||||
(extract_range_from_pointer_plus_expr): Abstract POINTER_PLUS code
|
||||
from extract_range_from_binary_expr.
|
||||
(extract_range_from_plus_minus_expr): Abstract PLUS/MINUS code
|
||||
from extract_range_from_binary_expr.
|
||||
(extract_range_from_binary_expr): Remove.
|
||||
(normalize_for_range_ops): New.
|
||||
(range_fold_binary_expr): New.
|
||||
(range_fold_unary_expr): New.
|
||||
(value_range_base::num_pairs): New.
|
||||
(value_range_base::lower_bound): New.
|
||||
(value_range_base::upper_bound): New.
|
||||
(value_range_base::upper_bound): New.
|
||||
(value_range_base::contains_p): New.
|
||||
(value_range_base::invert): New.
|
||||
(value_range_base::union_): New.
|
||||
(value_range_base::intersect): New.
|
||||
(range_compatible_p): New.
|
||||
(value_range_base::operator==): New.
|
||||
(determine_value_range_1): Call range_fold_*expr instead of
|
||||
extract_range_from_*expr.
|
||||
* tree-vrp.h (class value_range_base): Add new constructors.
|
||||
Add methods for union_, intersect, operator==, contains_p,
|
||||
num_pairs, lower_bound, upper_bound, invert.
|
||||
(vrp_val_is_min): Add handle_pointers argument.
|
||||
(vrp_val_is_max): Same.
|
||||
(extract_range_from_unary_expr): Remove.
|
||||
(extract_range_from_binary_expr): Remove.
|
||||
(range_fold_unary_expr): New.
|
||||
(range_fold_binary_expr): New.
|
||||
* vr-values.c (vr_values::extract_range_from_binary_expr): Call
|
||||
range_fold_binary_expr instead of extract_range_from_binary_expr.
|
||||
(vr_values::extract_range_basic): Same.
|
||||
(vr_values::extract_range_from_unary_expr): Call
|
||||
range_fold_unary_expr instead of extract_range_from_unary_expr.
|
||||
* wide-int-range.cc: Remove.
|
||||
* wide-int-range.h: Remove.
|
||||
|
||||
2019-10-02 Michael Meissner <meissner@linux.ibm.com>
|
||||
|
||||
* config/rs6000/rs6000.c (mem_operand_gpr): Use
|
||||
|
|
|
@ -1453,6 +1453,8 @@ OBJS = \
|
|||
print-tree.o \
|
||||
profile.o \
|
||||
profile-count.o \
|
||||
range.o \
|
||||
range-op.o \
|
||||
read-md.o \
|
||||
read-rtl.o \
|
||||
read-rtl-function.o \
|
||||
|
@ -1611,7 +1613,6 @@ OBJS = \
|
|||
web.o \
|
||||
wide-int.o \
|
||||
wide-int-print.o \
|
||||
wide-int-range.o \
|
||||
xcoffout.o \
|
||||
$(out_object_file) \
|
||||
$(EXTRA_OBJS) \
|
||||
|
|
|
@ -570,6 +570,19 @@ test_conversion_to_ssa ()
|
|||
ASSERT_EQ (SSA_NAME, TREE_CODE (gimple_return_retval (return_stmt)));
|
||||
}
|
||||
|
||||
/* Test range folding. We must start this here because we need cfun
|
||||
set. */
|
||||
|
||||
static void
|
||||
test_ranges ()
|
||||
{
|
||||
tree fndecl = build_trivial_high_gimple_function ();
|
||||
function *fun = DECL_STRUCT_FUNCTION (fndecl);
|
||||
push_cfun (fun);
|
||||
range_tests ();
|
||||
pop_cfun ();
|
||||
}
|
||||
|
||||
/* Test of expansion from gimple-ssa to RTL. */
|
||||
|
||||
static void
|
||||
|
@ -674,6 +687,7 @@ function_tests_c_tests ()
|
|||
test_gimplification ();
|
||||
test_building_cfg ();
|
||||
test_conversion_to_ssa ();
|
||||
test_ranges ();
|
||||
test_expansion_to_rtl ();
|
||||
}
|
||||
|
||||
|
|
|
@ -1944,8 +1944,7 @@ ipa_vr_operation_and_type_effects (value_range_base *dst_vr,
|
|||
enum tree_code operation,
|
||||
tree dst_type, tree src_type)
|
||||
{
|
||||
extract_range_from_unary_expr (dst_vr, operation, dst_type,
|
||||
src_vr, src_type);
|
||||
range_fold_unary_expr (dst_vr, operation, dst_type, src_vr, src_type);
|
||||
if (dst_vr->varying_p () || dst_vr->undefined_p ())
|
||||
return false;
|
||||
return true;
|
||||
|
|
|
@ -1921,8 +1921,8 @@ ipa_compute_jump_functions_for_edge (struct ipa_func_body_info *fbi,
|
|||
value_range_base tmpvr (type,
|
||||
wide_int_to_tree (TREE_TYPE (arg), min),
|
||||
wide_int_to_tree (TREE_TYPE (arg), max));
|
||||
extract_range_from_unary_expr (&resvr, NOP_EXPR, param_type,
|
||||
&tmpvr, TREE_TYPE (arg));
|
||||
range_fold_unary_expr (&resvr, NOP_EXPR, param_type,
|
||||
&tmpvr, TREE_TYPE (arg));
|
||||
if (!resvr.undefined_p () && !resvr.varying_p ())
|
||||
ipa_set_jfunc_vr (jfunc, &resvr);
|
||||
else
|
||||
|
|
3260
gcc/range-op.cc
Normal file
3260
gcc/range-op.cc
Normal file
File diff suppressed because it is too large
Load diff
88
gcc/range-op.h
Normal file
88
gcc/range-op.h
Normal file
|
@ -0,0 +1,88 @@
|
|||
/* Header file for range operator class.
|
||||
Copyright (C) 2017-2019 Free Software Foundation, Inc.
|
||||
Contributed by Andrew MacLeod <amacleod@redhat.com>
|
||||
and Aldy Hernandez <aldyh@redhat.com>.
|
||||
|
||||
This file is part of GCC.
|
||||
|
||||
GCC is free software; you can redistribute it and/or modify it under
|
||||
the terms of the GNU General Public License as published by the Free
|
||||
Software Foundation; either version 3, or (at your option) any later
|
||||
version.
|
||||
|
||||
GCC is distributed in the hope that it will be useful, but WITHOUT ANY
|
||||
WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||
for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with GCC; see the file COPYING3. If not see
|
||||
<http://www.gnu.org/licenses/>. */
|
||||
|
||||
#ifndef GCC_RANGE_OP_H
|
||||
#define GCC_RANGE_OP_H
|
||||
|
||||
// This class is implemented for each kind of operator supported by
|
||||
// the range generator. It serves various purposes.
|
||||
//
|
||||
// 1 - Generates range information for the specific operation between
|
||||
// two ranges. This provides the ability to fold ranges for an
|
||||
// expression.
|
||||
//
|
||||
// 2 - Performs range algebra on the expression such that a range can be
|
||||
// adjusted in terms of one of the operands:
|
||||
//
|
||||
// def = op1 + op2
|
||||
//
|
||||
// Given a range for def, we can adjust the range so that it is in
|
||||
// terms of either operand.
|
||||
//
|
||||
// op1_range (def_range, op2) will adjust the range in place so it
|
||||
// is in terms of op1. Since op1 = def - op2, it will subtract
|
||||
// op2 from each element of the range.
|
||||
//
|
||||
// 3 - Creates a range for an operand based on whether the result is 0 or
|
||||
// non-zero. This is mostly for logical true false, but can serve other
|
||||
// purposes.
|
||||
// ie 0 = op1 - op2 implies op2 has the same range as op1.
|
||||
|
||||
class range_operator
|
||||
{
|
||||
public:
|
||||
// Perform an operation between 2 ranges and return it.
|
||||
virtual value_range_base fold_range (tree type,
|
||||
const value_range_base &lh,
|
||||
const value_range_base &rh) const;
|
||||
|
||||
// Return the range for op[12] in the general case. LHS is the range for
|
||||
// the LHS of the expression, OP[12]is the range for the other
|
||||
//
|
||||
// The operand and the result is returned in R.
|
||||
//
|
||||
// TYPE is the expected type of the range.
|
||||
//
|
||||
// Return TRUE if the operation is performed and a valid range is available.
|
||||
//
|
||||
// i.e. [LHS] = ??? + OP2
|
||||
// is re-formed as R = [LHS] - OP2.
|
||||
virtual bool op1_range (value_range_base &r, tree type,
|
||||
const value_range_base &lhs,
|
||||
const value_range_base &op2) const;
|
||||
virtual bool op2_range (value_range_base &r, tree type,
|
||||
const value_range_base &lhs,
|
||||
const value_range_base &op1) const;
|
||||
|
||||
protected:
|
||||
// Perform an operation between 2 sub-ranges and return it.
|
||||
virtual value_range_base wi_fold (tree type,
|
||||
const wide_int &lh_lb,
|
||||
const wide_int &lh_ub,
|
||||
const wide_int &rh_lb,
|
||||
const wide_int &rh_ub) const;
|
||||
};
|
||||
|
||||
extern range_operator *range_op_handler (enum tree_code code, tree type);
|
||||
|
||||
extern void range_cast (value_range_base &, tree type);
|
||||
|
||||
#endif // GCC_RANGE_OP_H
|
89
gcc/range.cc
Normal file
89
gcc/range.cc
Normal file
|
@ -0,0 +1,89 @@
|
|||
/* Misc range functions.
|
||||
Copyright (C) 2017-2019 Free Software Foundation, Inc.
|
||||
Contributed by Aldy Hernandez <aldyh@redhat.com>.
|
||||
|
||||
This file is part of GCC.
|
||||
|
||||
GCC is free software; you can redistribute it and/or modify it under
|
||||
the terms of the GNU General Public License as published by the Free
|
||||
Software Foundation; either version 3, or (at your option) any later
|
||||
version.
|
||||
|
||||
GCC is distributed in the hope that it will be useful, but WITHOUT ANY
|
||||
WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||
for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with GCC; see the file COPYING3. If not see
|
||||
<http://www.gnu.org/licenses/>. */
|
||||
|
||||
#include "config.h"
|
||||
#include "system.h"
|
||||
#include "coretypes.h"
|
||||
#include "backend.h"
|
||||
#include "tree.h"
|
||||
#include "gimple.h"
|
||||
#include "gimple-pretty-print.h"
|
||||
#include "fold-const.h"
|
||||
#include "ssa.h"
|
||||
#include "range.h"
|
||||
|
||||
value_range_base
|
||||
range_intersect (const value_range_base &r1, const value_range_base &r2)
|
||||
{
|
||||
value_range_base tmp (r1);
|
||||
tmp.intersect (r2);
|
||||
return tmp;
|
||||
}
|
||||
|
||||
value_range_base
|
||||
range_invert (const value_range_base &r1)
|
||||
{
|
||||
value_range_base tmp (r1);
|
||||
tmp.invert ();
|
||||
return tmp;
|
||||
}
|
||||
|
||||
value_range_base
|
||||
range_union (const value_range_base &r1, const value_range_base &r2)
|
||||
{
|
||||
value_range_base tmp (r1);
|
||||
tmp.union_ (r2);
|
||||
return tmp;
|
||||
}
|
||||
|
||||
value_range_base
|
||||
range_zero (tree type)
|
||||
{
|
||||
return value_range_base (build_zero_cst (type), build_zero_cst (type));
|
||||
}
|
||||
|
||||
value_range_base
|
||||
range_nonzero (tree type)
|
||||
{
|
||||
return value_range_base (VR_ANTI_RANGE,
|
||||
build_zero_cst (type), build_zero_cst (type));
|
||||
}
|
||||
|
||||
value_range_base
|
||||
range_positives (tree type)
|
||||
{
|
||||
unsigned prec = TYPE_PRECISION (type);
|
||||
signop sign = TYPE_SIGN (type);
|
||||
return value_range_base (type, wi::zero (prec), wi::max_value (prec, sign));
|
||||
}
|
||||
|
||||
value_range_base
|
||||
range_negatives (tree type)
|
||||
{
|
||||
unsigned prec = TYPE_PRECISION (type);
|
||||
signop sign = TYPE_SIGN (type);
|
||||
value_range_base r;
|
||||
if (sign == UNSIGNED)
|
||||
r.set_undefined ();
|
||||
else
|
||||
r = value_range_base (type, wi::min_value (prec, sign),
|
||||
wi::minus_one (prec));
|
||||
return r;
|
||||
}
|
33
gcc/range.h
Normal file
33
gcc/range.h
Normal file
|
@ -0,0 +1,33 @@
|
|||
/* Header file for misc range functions. -*- C++ -*-
|
||||
Copyright (C) 2017-2019 Free Software Foundation, Inc.
|
||||
Contributed by Aldy Hernandez <aldyh@redhat.com>.
|
||||
|
||||
This file is part of GCC.
|
||||
|
||||
GCC is free software; you can redistribute it and/or modify it under
|
||||
the terms of the GNU General Public License as published by the Free
|
||||
Software Foundation; either version 3, or (at your option) any later
|
||||
version.
|
||||
|
||||
GCC is distributed in the hope that it will be useful, but WITHOUT ANY
|
||||
WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||
for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with GCC; see the file COPYING3. If not see
|
||||
<http://www.gnu.org/licenses/>. */
|
||||
|
||||
#ifndef GCC_RANGE_H
|
||||
#define GCC_RANGE_H
|
||||
|
||||
value_range_base range_zero (tree type);
|
||||
value_range_base range_nonzero (tree type);
|
||||
value_range_base range_intersect (const value_range_base &,
|
||||
const value_range_base &);
|
||||
value_range_base range_union (const value_range_base &,
|
||||
const value_range_base &);
|
||||
value_range_base range_invert (const value_range_base &);
|
||||
value_range_base range_positives (tree type);
|
||||
value_range_base range_negatives (tree type);
|
||||
#endif // GCC_RANGE_H
|
|
@ -259,6 +259,10 @@ extern int num_passes;
|
|||
|
||||
} /* end of namespace selftest. */
|
||||
|
||||
/* This is outside of the selftest namespace because it's a friend of
|
||||
value_range_base. */
|
||||
extern void range_tests ();
|
||||
|
||||
/* Macros for writing tests. */
|
||||
|
||||
/* Evaluate EXPR and coerce to bool, calling
|
||||
|
|
|
@ -26,6 +26,7 @@ along with GCC; see the file COPYING3. If not see
|
|||
#include "stringpool.h"
|
||||
#include "gimple-ssa.h"
|
||||
#include "tree-vrp.h"
|
||||
#include "range.h"
|
||||
#include "tree-ssanames.h"
|
||||
#include "tree-phinodes.h"
|
||||
#include "ssa-iterators.h"
|
||||
|
|
1236
gcc/tree-vrp.c
1236
gcc/tree-vrp.c
File diff suppressed because it is too large
Load diff
|
@ -35,14 +35,19 @@ enum value_range_kind
|
|||
VR_LAST
|
||||
};
|
||||
|
||||
|
||||
/* Range of values that can be associated with an SSA_NAME after VRP
|
||||
has executed. */
|
||||
class GTY((for_user)) value_range_base
|
||||
{
|
||||
friend void range_tests ();
|
||||
public:
|
||||
value_range_base ();
|
||||
value_range_base (value_range_kind, tree, tree);
|
||||
value_range_base (tree, tree);
|
||||
value_range_base (value_range_kind,
|
||||
tree type, const wide_int &, const wide_int &);
|
||||
value_range_base (tree type, const wide_int &, const wide_int &);
|
||||
value_range_base (tree type);
|
||||
|
||||
void set (value_range_kind, tree, tree);
|
||||
void set (tree);
|
||||
|
@ -63,8 +68,10 @@ public:
|
|||
|
||||
void union_ (const value_range_base *);
|
||||
void intersect (const value_range_base *);
|
||||
void union_ (const value_range_base &);
|
||||
void intersect (const value_range_base &);
|
||||
|
||||
bool operator== (const value_range_base &) const /* = delete */;
|
||||
bool operator== (const value_range_base &) const;
|
||||
bool operator!= (const value_range_base &) const /* = delete */;
|
||||
bool equal_p (const value_range_base &) const;
|
||||
|
||||
|
@ -80,6 +87,14 @@ public:
|
|||
static bool supports_type_p (tree);
|
||||
value_range_base normalize_symbolics () const;
|
||||
|
||||
static const unsigned int m_max_pairs = 2;
|
||||
bool contains_p (tree) const;
|
||||
unsigned num_pairs () const;
|
||||
wide_int lower_bound (unsigned = 0) const;
|
||||
wide_int upper_bound (unsigned) const;
|
||||
wide_int upper_bound () const;
|
||||
void invert ();
|
||||
|
||||
protected:
|
||||
void check ();
|
||||
static value_range_base union_helper (const value_range_base *,
|
||||
|
@ -281,21 +296,17 @@ extern bool range_int_cst_singleton_p (const value_range_base *);
|
|||
extern int compare_values (tree, tree);
|
||||
extern int compare_values_warnv (tree, tree, bool *);
|
||||
extern int operand_less_p (tree, tree);
|
||||
extern bool vrp_val_is_min (const_tree);
|
||||
extern bool vrp_val_is_max (const_tree);
|
||||
extern bool vrp_val_is_min (const_tree, bool handle_pointers = false);
|
||||
extern bool vrp_val_is_max (const_tree, bool handle_pointers = false);
|
||||
|
||||
extern tree vrp_val_min (const_tree, bool handle_pointers = false);
|
||||
extern tree vrp_val_max (const_tree, bool handle_pointers = false);
|
||||
|
||||
extern void extract_range_from_unary_expr (value_range_base *vr,
|
||||
enum tree_code code,
|
||||
tree type,
|
||||
const value_range_base *vr0_,
|
||||
tree op0_type);
|
||||
extern void extract_range_from_binary_expr (value_range_base *,
|
||||
enum tree_code,
|
||||
tree, const value_range_base *,
|
||||
const value_range_base *);
|
||||
void range_fold_unary_expr (value_range_base *, enum tree_code, tree type,
|
||||
const value_range_base *, tree op0_type);
|
||||
void range_fold_binary_expr (value_range_base *, enum tree_code, tree type,
|
||||
const value_range_base *,
|
||||
const value_range_base *);
|
||||
|
||||
extern bool vrp_operand_equal_p (const_tree, const_tree);
|
||||
extern enum value_range_kind intersect_range_with_nonzero_bits
|
||||
|
|
|
@ -46,8 +46,10 @@ along with GCC; see the file COPYING3. If not see
|
|||
#include "case-cfn-macros.h"
|
||||
#include "alloc-pool.h"
|
||||
#include "attribs.h"
|
||||
#include "range.h"
|
||||
#include "vr-values.h"
|
||||
#include "cfghooks.h"
|
||||
#include "range-op.h"
|
||||
|
||||
/* Set value range VR to a non-negative range of type TYPE. */
|
||||
|
||||
|
@ -803,7 +805,7 @@ vr_values::extract_range_from_binary_expr (value_range *vr,
|
|||
vrp_val_max (expr_type));
|
||||
}
|
||||
|
||||
::extract_range_from_binary_expr (vr, code, expr_type, &vr0, &vr1);
|
||||
range_fold_binary_expr (vr, code, expr_type, &vr0, &vr1);
|
||||
|
||||
/* Set value_range for n in following sequence:
|
||||
def = __builtin_memchr (arg, 0, sz)
|
||||
|
@ -864,7 +866,7 @@ vr_values::extract_range_from_binary_expr (value_range *vr,
|
|||
else
|
||||
n_vr1.set (VR_RANGE, op1, op1);
|
||||
|
||||
::extract_range_from_binary_expr (vr, code, expr_type, &vr0, &n_vr1);
|
||||
range_fold_binary_expr (vr, code, expr_type, &vr0, &n_vr1);
|
||||
}
|
||||
|
||||
if (vr->varying_p ()
|
||||
|
@ -888,7 +890,7 @@ vr_values::extract_range_from_binary_expr (value_range *vr,
|
|||
else
|
||||
n_vr0.set (op0);
|
||||
|
||||
::extract_range_from_binary_expr (vr, code, expr_type, &n_vr0, &vr1);
|
||||
range_fold_binary_expr (vr, code, expr_type, &n_vr0, &vr1);
|
||||
}
|
||||
|
||||
/* If we didn't derive a range for MINUS_EXPR, and
|
||||
|
@ -929,7 +931,7 @@ vr_values::extract_range_from_unary_expr (value_range *vr, enum tree_code code,
|
|||
else
|
||||
vr0.set_varying (type);
|
||||
|
||||
::extract_range_from_unary_expr (vr, code, type, &vr0, TREE_TYPE (op0));
|
||||
range_fold_unary_expr (vr, code, type, &vr0, TREE_TYPE (op0));
|
||||
}
|
||||
|
||||
|
||||
|
@ -1427,8 +1429,7 @@ vr_values::extract_range_basic (value_range *vr, gimple *stmt)
|
|||
type, op0);
|
||||
extract_range_from_unary_expr (&vr1, NOP_EXPR,
|
||||
type, op1);
|
||||
::extract_range_from_binary_expr (vr, subcode, type,
|
||||
&vr0, &vr1);
|
||||
range_fold_binary_expr (vr, subcode, type, &vr0, &vr1);
|
||||
flag_wrapv = saved_flag_wrapv;
|
||||
}
|
||||
return;
|
||||
|
|
|
@ -1,865 +0,0 @@
|
|||
/* Support routines for range operations on wide ints.
|
||||
Copyright (C) 2018-2019 Free Software Foundation, Inc.
|
||||
|
||||
This file is part of GCC.
|
||||
|
||||
GCC is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation; either version 3, or (at your option)
|
||||
any later version.
|
||||
|
||||
GCC is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with GCC; see the file COPYING3. If not see
|
||||
<http://www.gnu.org/licenses/>. */
|
||||
|
||||
#include "config.h"
|
||||
#include "system.h"
|
||||
#include "coretypes.h"
|
||||
#include "tree.h"
|
||||
#include "function.h"
|
||||
#include "fold-const.h"
|
||||
#include "wide-int-range.h"
|
||||
|
||||
/* Wrapper around wide_int_binop that adjusts for overflow.
|
||||
|
||||
Return true if we can compute the result; i.e. if the operation
|
||||
doesn't overflow or if the overflow is undefined. In the latter
|
||||
case (if the operation overflows and overflow is undefined), then
|
||||
adjust the result to be -INF or +INF depending on CODE, VAL1 and
|
||||
VAL2. Return the value in *RES.
|
||||
|
||||
Return false for division by zero, for which the result is
|
||||
indeterminate. */
|
||||
|
||||
static bool
|
||||
wide_int_binop_overflow (wide_int &res,
|
||||
enum tree_code code,
|
||||
const wide_int &w0, const wide_int &w1,
|
||||
signop sign, bool overflow_undefined)
|
||||
{
|
||||
wi::overflow_type overflow;
|
||||
if (!wide_int_binop (res, code, w0, w1, sign, &overflow))
|
||||
return false;
|
||||
|
||||
/* If the operation overflowed return -INF or +INF depending on the
|
||||
operation and the combination of signs of the operands. */
|
||||
if (overflow && overflow_undefined)
|
||||
{
|
||||
switch (code)
|
||||
{
|
||||
case MULT_EXPR:
|
||||
/* For multiplication, the sign of the overflow is given
|
||||
by the comparison of the signs of the operands. */
|
||||
if (sign == UNSIGNED || w0.sign_mask () == w1.sign_mask ())
|
||||
res = wi::max_value (w0.get_precision (), sign);
|
||||
else
|
||||
res = wi::min_value (w0.get_precision (), sign);
|
||||
return true;
|
||||
|
||||
case TRUNC_DIV_EXPR:
|
||||
case FLOOR_DIV_EXPR:
|
||||
case CEIL_DIV_EXPR:
|
||||
case EXACT_DIV_EXPR:
|
||||
case ROUND_DIV_EXPR:
|
||||
/* For division, the only case is -INF / -1 = +INF. */
|
||||
res = wi::max_value (w0.get_precision (), sign);
|
||||
return true;
|
||||
|
||||
default:
|
||||
gcc_unreachable ();
|
||||
}
|
||||
}
|
||||
return !overflow;
|
||||
}
|
||||
|
||||
/* For range [LB, UB] compute two wide_int bit masks.
|
||||
|
||||
In the MAY_BE_NONZERO bit mask, if some bit is unset, it means that
|
||||
for all numbers in the range the bit is 0, otherwise it might be 0
|
||||
or 1.
|
||||
|
||||
In the MUST_BE_NONZERO bit mask, if some bit is set, it means that
|
||||
for all numbers in the range the bit is 1, otherwise it might be 0
|
||||
or 1. */
|
||||
|
||||
void
|
||||
wide_int_range_set_zero_nonzero_bits (signop sign,
|
||||
const wide_int &lb, const wide_int &ub,
|
||||
wide_int &may_be_nonzero,
|
||||
wide_int &must_be_nonzero)
|
||||
{
|
||||
may_be_nonzero = wi::minus_one (lb.get_precision ());
|
||||
must_be_nonzero = wi::zero (lb.get_precision ());
|
||||
|
||||
if (wi::eq_p (lb, ub))
|
||||
{
|
||||
may_be_nonzero = lb;
|
||||
must_be_nonzero = may_be_nonzero;
|
||||
}
|
||||
else if (wi::ge_p (lb, 0, sign) || wi::lt_p (ub, 0, sign))
|
||||
{
|
||||
wide_int xor_mask = lb ^ ub;
|
||||
may_be_nonzero = lb | ub;
|
||||
must_be_nonzero = lb & ub;
|
||||
if (xor_mask != 0)
|
||||
{
|
||||
wide_int mask = wi::mask (wi::floor_log2 (xor_mask), false,
|
||||
may_be_nonzero.get_precision ());
|
||||
may_be_nonzero = may_be_nonzero | mask;
|
||||
must_be_nonzero = wi::bit_and_not (must_be_nonzero, mask);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Order 2 sets of wide int ranges (w0/w1, w2/w3) and set MIN/MAX
|
||||
accordingly. */
|
||||
|
||||
static void
|
||||
wide_int_range_order_set (wide_int &min, wide_int &max,
|
||||
wide_int &w0, wide_int &w1,
|
||||
wide_int &w2, wide_int &w3,
|
||||
signop sign)
|
||||
{
|
||||
/* Order pairs w0,w1 and w2,w3. */
|
||||
if (wi::gt_p (w0, w1, sign))
|
||||
std::swap (w0, w1);
|
||||
if (wi::gt_p (w2, w3, sign))
|
||||
std::swap (w2, w3);
|
||||
|
||||
/* Choose min and max from the ordered pairs. */
|
||||
min = wi::min (w0, w2, sign);
|
||||
max = wi::max (w1, w3, sign);
|
||||
}
|
||||
|
||||
/* Calculate the cross product of two sets of ranges (VR0 and VR1) and
|
||||
store the result in [RES_LB, RES_UB].
|
||||
|
||||
CODE is the operation to perform with sign SIGN.
|
||||
|
||||
OVERFLOW_UNDEFINED is set if overflow is undefined for the operation type.
|
||||
|
||||
Return TRUE if we were able to calculate the cross product. */
|
||||
|
||||
bool
|
||||
wide_int_range_cross_product (wide_int &res_lb, wide_int &res_ub,
|
||||
enum tree_code code, signop sign,
|
||||
const wide_int &vr0_lb, const wide_int &vr0_ub,
|
||||
const wide_int &vr1_lb, const wide_int &vr1_ub,
|
||||
bool overflow_undefined)
|
||||
{
|
||||
wide_int cp1, cp2, cp3, cp4;
|
||||
|
||||
/* Compute the 4 cross operations, bailing if we get an overflow we
|
||||
can't handle. */
|
||||
|
||||
if (!wide_int_binop_overflow (cp1, code, vr0_lb, vr1_lb, sign,
|
||||
overflow_undefined))
|
||||
return false;
|
||||
|
||||
if (wi::eq_p (vr0_lb, vr0_ub))
|
||||
cp3 = cp1;
|
||||
else if (!wide_int_binop_overflow (cp3, code, vr0_ub, vr1_lb, sign,
|
||||
overflow_undefined))
|
||||
return false;
|
||||
|
||||
if (wi::eq_p (vr1_lb, vr1_ub))
|
||||
cp2 = cp1;
|
||||
else if (!wide_int_binop_overflow (cp2, code, vr0_lb, vr1_ub, sign,
|
||||
overflow_undefined))
|
||||
return false;
|
||||
|
||||
if (wi::eq_p (vr0_lb, vr0_ub))
|
||||
cp4 = cp2;
|
||||
else if (!wide_int_binop_overflow (cp4, code, vr0_ub, vr1_ub, sign,
|
||||
overflow_undefined))
|
||||
return false;
|
||||
|
||||
wide_int_range_order_set (res_lb, res_ub, cp1, cp2, cp3, cp4, sign);
|
||||
return true;
|
||||
}
|
||||
|
||||
/* Multiply two ranges when TYPE_OVERFLOW_WRAPS:
|
||||
|
||||
[RES_LB, RES_UB] = [MIN0, MAX0] * [MIN1, MAX1]
|
||||
|
||||
This is basically fancy code so we don't drop to varying with an
|
||||
unsigned [-3,-1]*[-3,-1].
|
||||
|
||||
Return TRUE if we were able to perform the operation. */
|
||||
|
||||
bool
|
||||
wide_int_range_mult_wrapping (wide_int &res_lb,
|
||||
wide_int &res_ub,
|
||||
signop sign,
|
||||
unsigned prec,
|
||||
const wide_int &min0_,
|
||||
const wide_int &max0_,
|
||||
const wide_int &min1_,
|
||||
const wide_int &max1_)
|
||||
{
|
||||
/* This test requires 2*prec bits if both operands are signed and
|
||||
2*prec + 2 bits if either is not. Therefore, extend the values
|
||||
using the sign of the result to PREC2. From here on out,
|
||||
everthing is just signed math no matter what the input types
|
||||
were. */
|
||||
widest2_int min0 = widest2_int::from (min0_, sign);
|
||||
widest2_int max0 = widest2_int::from (max0_, sign);
|
||||
widest2_int min1 = widest2_int::from (min1_, sign);
|
||||
widest2_int max1 = widest2_int::from (max1_, sign);
|
||||
widest2_int sizem1 = wi::mask <widest2_int> (prec, false);
|
||||
widest2_int size = sizem1 + 1;
|
||||
|
||||
/* Canonicalize the intervals. */
|
||||
if (sign == UNSIGNED)
|
||||
{
|
||||
if (wi::ltu_p (size, min0 + max0))
|
||||
{
|
||||
min0 -= size;
|
||||
max0 -= size;
|
||||
}
|
||||
|
||||
if (wi::ltu_p (size, min1 + max1))
|
||||
{
|
||||
min1 -= size;
|
||||
max1 -= size;
|
||||
}
|
||||
}
|
||||
|
||||
widest2_int prod0 = min0 * min1;
|
||||
widest2_int prod1 = min0 * max1;
|
||||
widest2_int prod2 = max0 * min1;
|
||||
widest2_int prod3 = max0 * max1;
|
||||
|
||||
/* Sort the 4 products so that min is in prod0 and max is in
|
||||
prod3. */
|
||||
/* min0min1 > max0max1 */
|
||||
if (prod0 > prod3)
|
||||
std::swap (prod0, prod3);
|
||||
|
||||
/* min0max1 > max0min1 */
|
||||
if (prod1 > prod2)
|
||||
std::swap (prod1, prod2);
|
||||
|
||||
if (prod0 > prod1)
|
||||
std::swap (prod0, prod1);
|
||||
|
||||
if (prod2 > prod3)
|
||||
std::swap (prod2, prod3);
|
||||
|
||||
/* diff = max - min. */
|
||||
prod2 = prod3 - prod0;
|
||||
if (wi::geu_p (prod2, sizem1))
|
||||
/* The range covers all values. */
|
||||
return false;
|
||||
|
||||
res_lb = wide_int::from (prod0, prec, sign);
|
||||
res_ub = wide_int::from (prod3, prec, sign);
|
||||
return true;
|
||||
}
|
||||
|
||||
/* Perform multiplicative operation CODE on two ranges:
|
||||
|
||||
[RES_LB, RES_UB] = [VR0_LB, VR0_UB] .CODE. [VR1_LB, VR1_LB]
|
||||
|
||||
Return TRUE if we were able to perform the operation.
|
||||
|
||||
NOTE: If code is MULT_EXPR and !TYPE_OVERFLOW_UNDEFINED, the resulting
|
||||
range must be canonicalized by the caller because its components
|
||||
may be swapped. */
|
||||
|
||||
bool
|
||||
wide_int_range_multiplicative_op (wide_int &res_lb, wide_int &res_ub,
|
||||
enum tree_code code,
|
||||
signop sign,
|
||||
unsigned prec,
|
||||
const wide_int &vr0_lb,
|
||||
const wide_int &vr0_ub,
|
||||
const wide_int &vr1_lb,
|
||||
const wide_int &vr1_ub,
|
||||
bool overflow_undefined)
|
||||
{
|
||||
/* Multiplications, divisions and shifts are a bit tricky to handle,
|
||||
depending on the mix of signs we have in the two ranges, we
|
||||
need to operate on different values to get the minimum and
|
||||
maximum values for the new range. One approach is to figure
|
||||
out all the variations of range combinations and do the
|
||||
operations.
|
||||
|
||||
However, this involves several calls to compare_values and it
|
||||
is pretty convoluted. It's simpler to do the 4 operations
|
||||
(MIN0 OP MIN1, MIN0 OP MAX1, MAX0 OP MIN1 and MAX0 OP MAX0 OP
|
||||
MAX1) and then figure the smallest and largest values to form
|
||||
the new range. */
|
||||
if (code == MULT_EXPR && !overflow_undefined)
|
||||
return wide_int_range_mult_wrapping (res_lb, res_ub,
|
||||
sign, prec,
|
||||
vr0_lb, vr0_ub, vr1_lb, vr1_ub);
|
||||
return wide_int_range_cross_product (res_lb, res_ub,
|
||||
code, sign,
|
||||
vr0_lb, vr0_ub, vr1_lb, vr1_ub,
|
||||
overflow_undefined);
|
||||
}
|
||||
|
||||
/* Perform a left shift operation on two ranges:
|
||||
|
||||
[RES_LB, RES_UB] = [VR0_LB, VR0_UB] << [VR1_LB, VR1_LB]
|
||||
|
||||
Return TRUE if we were able to perform the operation.
|
||||
|
||||
NOTE: The resulting range must be canonicalized by the caller
|
||||
because its contents components may be swapped. */
|
||||
|
||||
bool
|
||||
wide_int_range_lshift (wide_int &res_lb, wide_int &res_ub,
|
||||
signop sign, unsigned prec,
|
||||
const wide_int &vr0_lb, const wide_int &vr0_ub,
|
||||
const wide_int &vr1_lb, const wide_int &vr1_ub,
|
||||
bool overflow_undefined)
|
||||
{
|
||||
/* Transform left shifts by constants into multiplies. */
|
||||
if (wi::eq_p (vr1_lb, vr1_ub))
|
||||
{
|
||||
unsigned shift = vr1_ub.to_uhwi ();
|
||||
wide_int tmp = wi::set_bit_in_zero (shift, prec);
|
||||
return wide_int_range_multiplicative_op (res_lb, res_ub,
|
||||
MULT_EXPR, sign, prec,
|
||||
vr0_lb, vr0_ub, tmp, tmp,
|
||||
/*overflow_undefined=*/false);
|
||||
}
|
||||
|
||||
int overflow_pos = prec;
|
||||
if (sign == SIGNED)
|
||||
overflow_pos -= 1;
|
||||
int bound_shift = overflow_pos - vr1_ub.to_shwi ();
|
||||
/* If bound_shift == HOST_BITS_PER_WIDE_INT, the llshift can
|
||||
overflow. However, for that to happen, vr1.max needs to be
|
||||
zero, which means vr1 is a singleton range of zero, which
|
||||
means it should be handled by the previous LSHIFT_EXPR
|
||||
if-clause. */
|
||||
wide_int bound = wi::set_bit_in_zero (bound_shift, prec);
|
||||
wide_int complement = ~(bound - 1);
|
||||
wide_int low_bound, high_bound;
|
||||
bool in_bounds = false;
|
||||
if (sign == UNSIGNED)
|
||||
{
|
||||
low_bound = bound;
|
||||
high_bound = complement;
|
||||
if (wi::ltu_p (vr0_ub, low_bound))
|
||||
{
|
||||
/* [5, 6] << [1, 2] == [10, 24]. */
|
||||
/* We're shifting out only zeroes, the value increases
|
||||
monotonically. */
|
||||
in_bounds = true;
|
||||
}
|
||||
else if (wi::ltu_p (high_bound, vr0_lb))
|
||||
{
|
||||
/* [0xffffff00, 0xffffffff] << [1, 2]
|
||||
== [0xfffffc00, 0xfffffffe]. */
|
||||
/* We're shifting out only ones, the value decreases
|
||||
monotonically. */
|
||||
in_bounds = true;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
/* [-1, 1] << [1, 2] == [-4, 4]. */
|
||||
low_bound = complement;
|
||||
high_bound = bound;
|
||||
if (wi::lts_p (vr0_ub, high_bound)
|
||||
&& wi::lts_p (low_bound, vr0_lb))
|
||||
{
|
||||
/* For non-negative numbers, we're shifting out only
|
||||
zeroes, the value increases monotonically.
|
||||
For negative numbers, we're shifting out only ones, the
|
||||
value decreases monotomically. */
|
||||
in_bounds = true;
|
||||
}
|
||||
}
|
||||
if (in_bounds)
|
||||
return wide_int_range_multiplicative_op (res_lb, res_ub,
|
||||
LSHIFT_EXPR, sign, prec,
|
||||
vr0_lb, vr0_ub,
|
||||
vr1_lb, vr1_ub,
|
||||
overflow_undefined);
|
||||
return false;
|
||||
}
|
||||
|
||||
/* Return TRUE if a bit operation on two ranges can be easily
|
||||
optimized in terms of a mask.
|
||||
|
||||
Basically, for BIT_AND_EXPR or BIT_IOR_EXPR see if we can optimize:
|
||||
|
||||
[LB, UB] op Z
|
||||
into:
|
||||
[LB op Z, UB op Z]
|
||||
|
||||
It is up to the caller to perform the actual folding above. */
|
||||
|
||||
static bool
|
||||
wide_int_range_can_optimize_bit_op (tree_code code,
|
||||
const wide_int &lb, const wide_int &ub,
|
||||
const wide_int &mask)
|
||||
|
||||
{
|
||||
if (code != BIT_AND_EXPR && code != BIT_IOR_EXPR)
|
||||
return false;
|
||||
/* If Z is a constant which (for op | its bitwise not) has n
|
||||
consecutive least significant bits cleared followed by m 1
|
||||
consecutive bits set immediately above it and either
|
||||
m + n == precision, or (x >> (m + n)) == (y >> (m + n)).
|
||||
|
||||
The least significant n bits of all the values in the range are
|
||||
cleared or set, the m bits above it are preserved and any bits
|
||||
above these are required to be the same for all values in the
|
||||
range. */
|
||||
|
||||
wide_int w = mask;
|
||||
int m = 0, n = 0;
|
||||
if (code == BIT_IOR_EXPR)
|
||||
w = ~w;
|
||||
if (wi::eq_p (w, 0))
|
||||
n = w.get_precision ();
|
||||
else
|
||||
{
|
||||
n = wi::ctz (w);
|
||||
w = ~(w | wi::mask (n, false, w.get_precision ()));
|
||||
if (wi::eq_p (w, 0))
|
||||
m = w.get_precision () - n;
|
||||
else
|
||||
m = wi::ctz (w) - n;
|
||||
}
|
||||
wide_int new_mask = wi::mask (m + n, true, w.get_precision ());
|
||||
if ((new_mask & lb) == (new_mask & ub))
|
||||
return true;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/* Helper function for wide_int_range_optimize_bit_op.
|
||||
|
||||
Calculates bounds and mask for a pair of ranges. The mask is the
|
||||
singleton range among the ranges, if any. The bounds are the
|
||||
bounds for the remaining range. */
|
||||
|
||||
bool
|
||||
wide_int_range_get_mask_and_bounds (wide_int &mask,
|
||||
wide_int &lower_bound,
|
||||
wide_int &upper_bound,
|
||||
const wide_int &vr0_min,
|
||||
const wide_int &vr0_max,
|
||||
const wide_int &vr1_min,
|
||||
const wide_int &vr1_max)
|
||||
{
|
||||
if (wi::eq_p (vr1_min, vr1_max))
|
||||
{
|
||||
mask = vr1_min;
|
||||
lower_bound = vr0_min;
|
||||
upper_bound = vr0_max;
|
||||
return true;
|
||||
}
|
||||
else if (wi::eq_p (vr0_min, vr0_max))
|
||||
{
|
||||
mask = vr0_min;
|
||||
lower_bound = vr1_min;
|
||||
upper_bound = vr1_max;
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/* Optimize a bit operation (BIT_AND_EXPR or BIT_IOR_EXPR) if
|
||||
possible. If so, return TRUE and store the result in
|
||||
[RES_LB, RES_UB]. */
|
||||
|
||||
bool
|
||||
wide_int_range_optimize_bit_op (wide_int &res_lb, wide_int &res_ub,
|
||||
enum tree_code code,
|
||||
signop sign,
|
||||
const wide_int &vr0_min,
|
||||
const wide_int &vr0_max,
|
||||
const wide_int &vr1_min,
|
||||
const wide_int &vr1_max)
|
||||
{
|
||||
gcc_assert (code == BIT_AND_EXPR || code == BIT_IOR_EXPR);
|
||||
|
||||
wide_int lower_bound, upper_bound, mask;
|
||||
if (!wide_int_range_get_mask_and_bounds (mask, lower_bound, upper_bound,
|
||||
vr0_min, vr0_max, vr1_min, vr1_max))
|
||||
return false;
|
||||
if (wide_int_range_can_optimize_bit_op (code,
|
||||
lower_bound, upper_bound, mask))
|
||||
{
|
||||
wi::overflow_type ovf;
|
||||
wide_int_binop (res_lb, code, lower_bound, mask, sign, &ovf);
|
||||
wide_int_binop (res_ub, code, upper_bound, mask, sign, &ovf);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/* Calculate the XOR of two ranges and store the result in [WMIN,WMAX].
|
||||
The two input ranges are described by their MUST_BE_NONZERO and
|
||||
MAY_BE_NONZERO bit masks.
|
||||
|
||||
Return TRUE if we were able to successfully calculate the new range. */
|
||||
|
||||
bool
|
||||
wide_int_range_bit_xor (wide_int &wmin, wide_int &wmax,
|
||||
signop sign,
|
||||
unsigned prec,
|
||||
const wide_int &must_be_nonzero0,
|
||||
const wide_int &may_be_nonzero0,
|
||||
const wide_int &must_be_nonzero1,
|
||||
const wide_int &may_be_nonzero1)
|
||||
{
|
||||
wide_int result_zero_bits = ((must_be_nonzero0 & must_be_nonzero1)
|
||||
| ~(may_be_nonzero0 | may_be_nonzero1));
|
||||
wide_int result_one_bits
|
||||
= (wi::bit_and_not (must_be_nonzero0, may_be_nonzero1)
|
||||
| wi::bit_and_not (must_be_nonzero1, may_be_nonzero0));
|
||||
wmax = ~result_zero_bits;
|
||||
wmin = result_one_bits;
|
||||
/* If the range has all positive or all negative values, the result
|
||||
is better than VARYING. */
|
||||
if (wi::lt_p (wmin, 0, sign) || wi::ge_p (wmax, 0, sign))
|
||||
return true;
|
||||
wmin = wi::min_value (prec, sign);
|
||||
wmax = wi::max_value (prec, sign);
|
||||
return false;
|
||||
}
|
||||
|
||||
/* Calculate the IOR of two ranges and store the result in [WMIN,WMAX].
|
||||
Return TRUE if we were able to successfully calculate the new range. */
|
||||
|
||||
bool
|
||||
wide_int_range_bit_ior (wide_int &wmin, wide_int &wmax,
|
||||
signop sign,
|
||||
const wide_int &vr0_min,
|
||||
const wide_int &vr0_max,
|
||||
const wide_int &vr1_min,
|
||||
const wide_int &vr1_max,
|
||||
const wide_int &must_be_nonzero0,
|
||||
const wide_int &may_be_nonzero0,
|
||||
const wide_int &must_be_nonzero1,
|
||||
const wide_int &may_be_nonzero1)
|
||||
{
|
||||
if (wide_int_range_optimize_bit_op (wmin, wmax, BIT_IOR_EXPR, sign,
|
||||
vr0_min, vr0_max,
|
||||
vr1_min, vr1_max))
|
||||
return true;
|
||||
wmin = must_be_nonzero0 | must_be_nonzero1;
|
||||
wmax = may_be_nonzero0 | may_be_nonzero1;
|
||||
/* If the input ranges contain only positive values we can
|
||||
truncate the minimum of the result range to the maximum
|
||||
of the input range minima. */
|
||||
if (wi::ge_p (vr0_min, 0, sign)
|
||||
&& wi::ge_p (vr1_min, 0, sign))
|
||||
{
|
||||
wmin = wi::max (wmin, vr0_min, sign);
|
||||
wmin = wi::max (wmin, vr1_min, sign);
|
||||
}
|
||||
/* If either input range contains only negative values
|
||||
we can truncate the minimum of the result range to the
|
||||
respective minimum range. */
|
||||
if (wi::lt_p (vr0_max, 0, sign))
|
||||
wmin = wi::max (wmin, vr0_min, sign);
|
||||
if (wi::lt_p (vr1_max, 0, sign))
|
||||
wmin = wi::max (wmin, vr1_min, sign);
|
||||
/* If the limits got swapped around, indicate error so we can adjust
|
||||
the range to VARYING. */
|
||||
if (wi::gt_p (wmin, wmax,sign))
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
/* Calculate the bitwise AND of two ranges and store the result in [WMIN,WMAX].
|
||||
Return TRUE if we were able to successfully calculate the new range. */
|
||||
|
||||
bool
|
||||
wide_int_range_bit_and (wide_int &wmin, wide_int &wmax,
|
||||
signop sign,
|
||||
unsigned prec,
|
||||
const wide_int &vr0_min,
|
||||
const wide_int &vr0_max,
|
||||
const wide_int &vr1_min,
|
||||
const wide_int &vr1_max,
|
||||
const wide_int &must_be_nonzero0,
|
||||
const wide_int &may_be_nonzero0,
|
||||
const wide_int &must_be_nonzero1,
|
||||
const wide_int &may_be_nonzero1)
|
||||
{
|
||||
if (wide_int_range_optimize_bit_op (wmin, wmax, BIT_AND_EXPR, sign,
|
||||
vr0_min, vr0_max,
|
||||
vr1_min, vr1_max))
|
||||
return true;
|
||||
wmin = must_be_nonzero0 & must_be_nonzero1;
|
||||
wmax = may_be_nonzero0 & may_be_nonzero1;
|
||||
/* If both input ranges contain only negative values we can
|
||||
truncate the result range maximum to the minimum of the
|
||||
input range maxima. */
|
||||
if (wi::lt_p (vr0_max, 0, sign) && wi::lt_p (vr1_max, 0, sign))
|
||||
{
|
||||
wmax = wi::min (wmax, vr0_max, sign);
|
||||
wmax = wi::min (wmax, vr1_max, sign);
|
||||
}
|
||||
/* If either input range contains only non-negative values
|
||||
we can truncate the result range maximum to the respective
|
||||
maximum of the input range. */
|
||||
if (wi::ge_p (vr0_min, 0, sign))
|
||||
wmax = wi::min (wmax, vr0_max, sign);
|
||||
if (wi::ge_p (vr1_min, 0, sign))
|
||||
wmax = wi::min (wmax, vr1_max, sign);
|
||||
/* PR68217: In case of signed & sign-bit-CST should
|
||||
result in [-INF, 0] instead of [-INF, INF]. */
|
||||
if (wi::gt_p (wmin, wmax, sign))
|
||||
{
|
||||
wide_int sign_bit = wi::set_bit_in_zero (prec - 1, prec);
|
||||
if (sign == SIGNED
|
||||
&& ((wi::eq_p (vr0_min, vr0_max)
|
||||
&& !wi::cmps (vr0_min, sign_bit))
|
||||
|| (wi::eq_p (vr1_min, vr1_max)
|
||||
&& !wi::cmps (vr1_min, sign_bit))))
|
||||
{
|
||||
wmin = wi::min_value (prec, sign);
|
||||
wmax = wi::zero (prec);
|
||||
}
|
||||
}
|
||||
/* If the limits got swapped around, indicate error so we can adjust
|
||||
the range to VARYING. */
|
||||
if (wi::gt_p (wmin, wmax,sign))
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
/* Calculate TRUNC_MOD_EXPR on two ranges and store the result in
|
||||
[WMIN,WMAX]. */
|
||||
|
||||
void
|
||||
wide_int_range_trunc_mod (wide_int &wmin, wide_int &wmax,
|
||||
signop sign,
|
||||
unsigned prec,
|
||||
const wide_int &vr0_min,
|
||||
const wide_int &vr0_max,
|
||||
const wide_int &vr1_min,
|
||||
const wide_int &vr1_max)
|
||||
{
|
||||
wide_int tmp;
|
||||
|
||||
/* ABS (A % B) < ABS (B) and either
|
||||
0 <= A % B <= A or A <= A % B <= 0. */
|
||||
wmax = vr1_max - 1;
|
||||
if (sign == SIGNED)
|
||||
{
|
||||
tmp = -1 - vr1_min;
|
||||
wmax = wi::smax (wmax, tmp);
|
||||
}
|
||||
|
||||
if (sign == UNSIGNED)
|
||||
wmin = wi::zero (prec);
|
||||
else
|
||||
{
|
||||
wmin = -wmax;
|
||||
tmp = vr0_min;
|
||||
if (wi::gts_p (tmp, 0))
|
||||
tmp = wi::zero (prec);
|
||||
wmin = wi::smax (wmin, tmp);
|
||||
}
|
||||
tmp = vr0_max;
|
||||
if (sign == SIGNED && wi::neg_p (tmp))
|
||||
tmp = wi::zero (prec);
|
||||
wmax = wi::min (wmax, tmp, sign);
|
||||
}
|
||||
|
||||
/* Calculate ABS_EXPR on a range and store the result in [MIN, MAX]. */
|
||||
|
||||
bool
|
||||
wide_int_range_abs (wide_int &min, wide_int &max,
|
||||
signop sign, unsigned prec,
|
||||
const wide_int &vr0_min, const wide_int &vr0_max,
|
||||
bool overflow_undefined)
|
||||
{
|
||||
/* Pass through VR0 the easy cases. */
|
||||
if (sign == UNSIGNED || wi::ge_p (vr0_min, 0, sign))
|
||||
{
|
||||
min = vr0_min;
|
||||
max = vr0_max;
|
||||
return true;
|
||||
}
|
||||
|
||||
/* -TYPE_MIN_VALUE = TYPE_MIN_VALUE with flag_wrapv so we can't get a
|
||||
useful range. */
|
||||
wide_int min_value = wi::min_value (prec, sign);
|
||||
wide_int max_value = wi::max_value (prec, sign);
|
||||
if (!overflow_undefined && wi::eq_p (vr0_min, min_value))
|
||||
return false;
|
||||
|
||||
/* ABS_EXPR may flip the range around, if the original range
|
||||
included negative values. */
|
||||
if (wi::eq_p (vr0_min, min_value))
|
||||
min = max_value;
|
||||
else
|
||||
min = wi::abs (vr0_min);
|
||||
if (wi::eq_p (vr0_max, min_value))
|
||||
max = max_value;
|
||||
else
|
||||
max = wi::abs (vr0_max);
|
||||
|
||||
/* If the range contains zero then we know that the minimum value in the
|
||||
range will be zero. */
|
||||
if (wi::le_p (vr0_min, 0, sign) && wi::ge_p (vr0_max, 0, sign))
|
||||
{
|
||||
if (wi::gt_p (min, max, sign))
|
||||
max = min;
|
||||
min = wi::zero (prec);
|
||||
}
|
||||
else
|
||||
{
|
||||
/* If the range was reversed, swap MIN and MAX. */
|
||||
if (wi::gt_p (min, max, sign))
|
||||
std::swap (min, max);
|
||||
}
|
||||
|
||||
/* If the new range has its limits swapped around (MIN > MAX), then
|
||||
the operation caused one of them to wrap around. The only thing
|
||||
we know is that the result is positive. */
|
||||
if (wi::gt_p (min, max, sign))
|
||||
{
|
||||
min = wi::zero (prec);
|
||||
max = max_value;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/* Calculate ABSU_EXPR on a range and store the result in [MIN, MAX]. */
|
||||
|
||||
void
|
||||
wide_int_range_absu (wide_int &min, wide_int &max,
|
||||
unsigned prec, const wide_int &vr0_min,
|
||||
const wide_int &vr0_max)
|
||||
{
|
||||
/* Pass through VR0 the easy cases. */
|
||||
if (wi::ges_p (vr0_min, 0))
|
||||
{
|
||||
min = vr0_min;
|
||||
max = vr0_max;
|
||||
return;
|
||||
}
|
||||
|
||||
min = wi::abs (vr0_min);
|
||||
max = wi::abs (vr0_max);
|
||||
|
||||
/* If the range contains zero then we know that the minimum value in the
|
||||
range will be zero. */
|
||||
if (wi::ges_p (vr0_max, 0))
|
||||
{
|
||||
if (wi::gtu_p (min, max))
|
||||
max = min;
|
||||
min = wi::zero (prec);
|
||||
}
|
||||
else
|
||||
/* Otherwise, swap MIN and MAX. */
|
||||
std::swap (min, max);
|
||||
}
|
||||
|
||||
/* Convert range in [VR0_MIN, VR0_MAX] with INNER_SIGN and INNER_PREC,
|
||||
to a range in [MIN, MAX] with OUTER_SIGN and OUTER_PREC.
|
||||
|
||||
Return TRUE if we were able to successfully calculate the new range.
|
||||
|
||||
Caller is responsible for canonicalizing the resulting range. */
|
||||
|
||||
bool
|
||||
wide_int_range_convert (wide_int &min, wide_int &max,
|
||||
signop inner_sign,
|
||||
unsigned inner_prec,
|
||||
signop outer_sign,
|
||||
unsigned outer_prec,
|
||||
const wide_int &vr0_min,
|
||||
const wide_int &vr0_max)
|
||||
{
|
||||
/* If the conversion is not truncating we can convert the min and
|
||||
max values and canonicalize the resulting range. Otherwise we
|
||||
can do the conversion if the size of the range is less than what
|
||||
the precision of the target type can represent. */
|
||||
if (outer_prec >= inner_prec
|
||||
|| wi::rshift (wi::sub (vr0_max, vr0_min),
|
||||
wi::uhwi (outer_prec, inner_prec),
|
||||
inner_sign) == 0)
|
||||
{
|
||||
min = wide_int::from (vr0_min, outer_prec, inner_sign);
|
||||
max = wide_int::from (vr0_max, outer_prec, inner_sign);
|
||||
return (!wi::eq_p (min, wi::min_value (outer_prec, outer_sign))
|
||||
|| !wi::eq_p (max, wi::max_value (outer_prec, outer_sign)));
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/* Calculate a division operation on two ranges and store the result in
|
||||
[WMIN, WMAX] U [EXTRA_MIN, EXTRA_MAX].
|
||||
|
||||
If EXTRA_RANGE_P is set upon return, EXTRA_MIN/EXTRA_MAX hold
|
||||
meaningful information, otherwise they should be ignored.
|
||||
|
||||
Return TRUE if we were able to successfully calculate the new range. */
|
||||
|
||||
bool
|
||||
wide_int_range_div (wide_int &wmin, wide_int &wmax,
|
||||
tree_code code, signop sign, unsigned prec,
|
||||
const wide_int ÷nd_min, const wide_int ÷nd_max,
|
||||
const wide_int &divisor_min, const wide_int &divisor_max,
|
||||
bool overflow_undefined,
|
||||
bool &extra_range_p,
|
||||
wide_int &extra_min, wide_int &extra_max)
|
||||
{
|
||||
extra_range_p = false;
|
||||
|
||||
/* If we know we won't divide by zero, just do the division. */
|
||||
if (!wide_int_range_includes_zero_p (divisor_min, divisor_max, sign))
|
||||
return wide_int_range_multiplicative_op (wmin, wmax, code, sign, prec,
|
||||
dividend_min, dividend_max,
|
||||
divisor_min, divisor_max,
|
||||
overflow_undefined);
|
||||
|
||||
/* If flag_non_call_exceptions, we must not eliminate a division
|
||||
by zero. */
|
||||
if (cfun->can_throw_non_call_exceptions)
|
||||
return false;
|
||||
|
||||
/* If we're definitely dividing by zero, there's nothing to do. */
|
||||
if (wide_int_range_zero_p (divisor_min, divisor_max, prec))
|
||||
return false;
|
||||
|
||||
/* Perform the division in 2 parts, [LB, -1] and [1, UB],
|
||||
which will skip any division by zero.
|
||||
|
||||
First divide by the negative numbers, if any. */
|
||||
if (wi::neg_p (divisor_min, sign))
|
||||
{
|
||||
if (!wide_int_range_multiplicative_op (wmin, wmax,
|
||||
code, sign, prec,
|
||||
dividend_min, dividend_max,
|
||||
divisor_min, wi::minus_one (prec),
|
||||
overflow_undefined))
|
||||
return false;
|
||||
extra_range_p = true;
|
||||
}
|
||||
/* Then divide by the non-zero positive numbers, if any. */
|
||||
if (wi::gt_p (divisor_max, wi::zero (prec), sign))
|
||||
{
|
||||
if (!wide_int_range_multiplicative_op (extra_range_p ? extra_min : wmin,
|
||||
extra_range_p ? extra_max : wmax,
|
||||
code, sign, prec,
|
||||
dividend_min, dividend_max,
|
||||
wi::one (prec), divisor_max,
|
||||
overflow_undefined))
|
||||
return false;
|
||||
}
|
||||
else
|
||||
extra_range_p = false;
|
||||
return true;
|
||||
}
|
|
@ -1,188 +0,0 @@
|
|||
/* Support routines for range operations on wide ints.
|
||||
Copyright (C) 2018-2019 Free Software Foundation, Inc.
|
||||
|
||||
This file is part of GCC.
|
||||
|
||||
GCC is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation; either version 3, or (at your option)
|
||||
any later version.
|
||||
|
||||
GCC is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with GCC; see the file COPYING3. If not see
|
||||
<http://www.gnu.org/licenses/>. */
|
||||
|
||||
#ifndef GCC_WIDE_INT_RANGE_H
|
||||
#define GCC_WIDE_INT_RANGE_H
|
||||
|
||||
extern bool wide_int_range_cross_product (wide_int &res_lb, wide_int &res_ub,
|
||||
enum tree_code code, signop sign,
|
||||
const wide_int &, const wide_int &,
|
||||
const wide_int &, const wide_int &,
|
||||
bool overflow_undefined);
|
||||
extern bool wide_int_range_mult_wrapping (wide_int &res_lb,
|
||||
wide_int &res_ub,
|
||||
signop sign,
|
||||
unsigned prec,
|
||||
const wide_int &min0_,
|
||||
const wide_int &max0_,
|
||||
const wide_int &min1_,
|
||||
const wide_int &max1_);
|
||||
extern bool wide_int_range_multiplicative_op (wide_int &res_lb,
|
||||
wide_int &res_ub,
|
||||
enum tree_code code,
|
||||
signop sign,
|
||||
unsigned prec,
|
||||
const wide_int &vr0_lb,
|
||||
const wide_int &vr0_ub,
|
||||
const wide_int &vr1_lb,
|
||||
const wide_int &vr1_ub,
|
||||
bool overflow_undefined);
|
||||
extern bool wide_int_range_lshift (wide_int &res_lb, wide_int &res_ub,
|
||||
signop sign, unsigned prec,
|
||||
const wide_int &, const wide_int &,
|
||||
const wide_int &, const wide_int &,
|
||||
bool overflow_undefined);
|
||||
extern void wide_int_range_set_zero_nonzero_bits (signop,
|
||||
const wide_int &lb,
|
||||
const wide_int &ub,
|
||||
wide_int &may_be_nonzero,
|
||||
wide_int &must_be_nonzero);
|
||||
extern bool wide_int_range_optimize_bit_op (wide_int &res_lb, wide_int &res_ub,
|
||||
enum tree_code code,
|
||||
signop sign,
|
||||
const wide_int &vr0_lb,
|
||||
const wide_int &vr0_ub,
|
||||
const wide_int &vr1_lb,
|
||||
const wide_int &vr1_ub);
|
||||
extern bool wide_int_range_get_mask_and_bounds (wide_int &mask,
|
||||
wide_int &lower_bound,
|
||||
wide_int &upper_bound,
|
||||
const wide_int &vr0_min,
|
||||
const wide_int &vr0_max,
|
||||
const wide_int &vr1_min,
|
||||
const wide_int &vr1_max);
|
||||
extern bool wide_int_range_bit_xor (wide_int &wmin, wide_int &wmax,
|
||||
signop sign,
|
||||
unsigned prec,
|
||||
const wide_int &must_be_nonzero0,
|
||||
const wide_int &may_be_nonzero0,
|
||||
const wide_int &must_be_nonzero1,
|
||||
const wide_int &may_be_nonzero1);
|
||||
extern bool wide_int_range_bit_ior (wide_int &wmin, wide_int &wmax,
|
||||
signop sign,
|
||||
const wide_int &vr0_min,
|
||||
const wide_int &vr0_max,
|
||||
const wide_int &vr1_min,
|
||||
const wide_int &vr1_max,
|
||||
const wide_int &must_be_nonzero0,
|
||||
const wide_int &may_be_nonzero0,
|
||||
const wide_int &must_be_nonzero1,
|
||||
const wide_int &may_be_nonzero1);
|
||||
extern bool wide_int_range_bit_and (wide_int &wmin, wide_int &wmax,
|
||||
signop sign,
|
||||
unsigned prec,
|
||||
const wide_int &vr0_min,
|
||||
const wide_int &vr0_max,
|
||||
const wide_int &vr1_min,
|
||||
const wide_int &vr1_max,
|
||||
const wide_int &must_be_nonzero0,
|
||||
const wide_int &may_be_nonzero0,
|
||||
const wide_int &must_be_nonzero1,
|
||||
const wide_int &may_be_nonzero1);
|
||||
extern void wide_int_range_trunc_mod (wide_int &wmin, wide_int &wmax,
|
||||
signop sign,
|
||||
unsigned prec,
|
||||
const wide_int &vr0_min,
|
||||
const wide_int &vr0_max,
|
||||
const wide_int &vr1_min,
|
||||
const wide_int &vr1_max);
|
||||
extern bool wide_int_range_abs (wide_int &min, wide_int &max,
|
||||
signop sign, unsigned prec,
|
||||
const wide_int &vr0_min,
|
||||
const wide_int &vr0_max,
|
||||
bool overflow_undefined);
|
||||
extern void wide_int_range_absu (wide_int &min, wide_int &max,
|
||||
unsigned prec,
|
||||
const wide_int &vr0_min,
|
||||
const wide_int &vr0_max);
|
||||
extern bool wide_int_range_convert (wide_int &min, wide_int &max,
|
||||
signop inner_sign,
|
||||
unsigned inner_prec,
|
||||
signop outer_sign,
|
||||
unsigned outer_prec,
|
||||
const wide_int &vr0_min,
|
||||
const wide_int &vr0_max);
|
||||
extern bool wide_int_range_div (wide_int &wmin, wide_int &wmax,
|
||||
enum tree_code code,
|
||||
signop sign, unsigned prec,
|
||||
const wide_int ÷nd_min,
|
||||
const wide_int ÷nd_max,
|
||||
const wide_int &divisor_min,
|
||||
const wide_int &divisor_max,
|
||||
bool overflow_undefined,
|
||||
bool &extra_range_p,
|
||||
wide_int &extra_min, wide_int &extra_max);
|
||||
|
||||
/* Return TRUE if shifting by range [MIN, MAX] is undefined behavior,
|
||||
interpreting MIN and MAX according to SIGN. */
|
||||
|
||||
inline bool
|
||||
wide_int_range_shift_undefined_p (signop sign, unsigned prec,
|
||||
const wide_int &min, const wide_int &max)
|
||||
{
|
||||
/* ?? Note: The original comment said this only applied to
|
||||
RSHIFT_EXPR, but it was being applied to both left and right
|
||||
shifts. */
|
||||
|
||||
/* Shifting by any values outside [0..prec-1], gets undefined
|
||||
behavior from the shift operation. We cannot even trust
|
||||
SHIFT_COUNT_TRUNCATED at this stage, because that applies to rtl
|
||||
shifts, and the operation at the tree level may be widened. */
|
||||
return wi::lt_p (min, 0, sign) || wi::ge_p (max, prec, sign);
|
||||
}
|
||||
|
||||
/* Calculate MIN/MAX_EXPR of two ranges and store the result in [MIN, MAX]. */
|
||||
|
||||
inline bool
|
||||
wide_int_range_min_max (wide_int &min, wide_int &max,
|
||||
tree_code code,
|
||||
signop sign, unsigned prec,
|
||||
const wide_int &vr0_min, const wide_int &vr0_max,
|
||||
const wide_int &vr1_min, const wide_int &vr1_max)
|
||||
{
|
||||
wi::overflow_type overflow;
|
||||
wide_int_binop (min, code, vr0_min, vr1_min, sign, &overflow);
|
||||
wide_int_binop (max, code, vr0_max, vr1_max, sign, &overflow);
|
||||
/* If the new range covers the entire domain, that's really no range
|
||||
at all. */
|
||||
if (min == wi::min_value (prec, sign)
|
||||
&& max == wi::max_value (prec, sign))
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
/* Return TRUE if 0 is within [WMIN, WMAX]. */
|
||||
|
||||
inline bool
|
||||
wide_int_range_includes_zero_p (const wide_int &wmin, const wide_int &wmax,
|
||||
signop sign)
|
||||
{
|
||||
return wi::le_p (wmin, 0, sign) && wi::ge_p (wmax, 0, sign);
|
||||
}
|
||||
|
||||
/* Return TRUE if [WMIN, WMAX] is the singleton 0. */
|
||||
|
||||
inline bool
|
||||
wide_int_range_zero_p (const wide_int &wmin, const wide_int &wmax,
|
||||
unsigned prec)
|
||||
{
|
||||
return wmin == wmax && wi::eq_p (wmin, wi::zero (prec));
|
||||
}
|
||||
|
||||
#endif /* GCC_WIDE_INT_RANGE_H */
|
Loading…
Add table
Reference in a new issue