summaryrefslogtreecommitdiff
path: root/gcc/gimple-ssa-sprintf.c
diff options
context:
space:
mode:
Diffstat (limited to 'gcc/gimple-ssa-sprintf.c')
-rw-r--r--gcc/gimple-ssa-sprintf.c133
1 files changed, 79 insertions, 54 deletions
diff --git a/gcc/gimple-ssa-sprintf.c b/gcc/gimple-ssa-sprintf.c
index a2dd54502e8..1189d9fea35 100644
--- a/gcc/gimple-ssa-sprintf.c
+++ b/gcc/gimple-ssa-sprintf.c
@@ -80,6 +80,9 @@ along with GCC; see the file COPYING3. If not see
#include "substring-locations.h"
#include "diagnostic.h"
#include "domwalk.h"
+#include "alloc-pool.h"
+#include "vr-values.h"
+#include "gimple-ssa-evrp-analyze.h"
/* The likely worst case value of MB_LEN_MAX for the target, large enough
for UTF-8. Ideally, this would be obtained by a target hook if it were
@@ -121,10 +124,12 @@ class sprintf_dom_walker : public dom_walker
~sprintf_dom_walker () {}
edge before_dom_children (basic_block) FINAL OVERRIDE;
+ void after_dom_children (basic_block) FINAL OVERRIDE;
bool handle_gimple_call (gimple_stmt_iterator *);
struct call_info;
bool compute_format_length (call_info &, format_result *);
+ class evrp_range_analyzer evrp_range_analyzer;
};
class pass_sprintf_length : public gimple_opt_pass
@@ -766,7 +771,8 @@ fmtresult::type_max_digits (tree type, int base)
}
static bool
-get_int_range (tree, HOST_WIDE_INT *, HOST_WIDE_INT *, bool, HOST_WIDE_INT);
+get_int_range (tree, HOST_WIDE_INT *, HOST_WIDE_INT *, bool, HOST_WIDE_INT,
+ class vr_values *vr_values);
/* Description of a format directive. A directive is either a plain
string or a conversion specification that starts with '%'. */
@@ -801,7 +807,7 @@ struct directive
/* Format conversion function that given a directive and an argument
returns the formatting result. */
- fmtresult (*fmtfunc) (const directive &, tree);
+ fmtresult (*fmtfunc) (const directive &, tree, vr_values *);
/* Return True when a the format flag CHR has been used. */
bool get_flag (char chr) const
@@ -838,9 +844,9 @@ struct directive
or 0, whichever is greater. For a non-constant ARG in some range
set width to its range adjusting each bound to -1 if it's less.
For an indeterminate ARG set width to [0, INT_MAX]. */
- void set_width (tree arg)
+ void set_width (tree arg, vr_values *vr_values)
{
- get_int_range (arg, width, width + 1, true, 0);
+ get_int_range (arg, width, width + 1, true, 0, vr_values);
}
/* Set both bounds of the precision range to VAL. */
@@ -854,9 +860,9 @@ struct directive
or -1 whichever is greater. For a non-constant ARG in some range
set precision to its range adjusting each bound to -1 if it's less.
For an indeterminate ARG set precision to [-1, INT_MAX]. */
- void set_precision (tree arg)
+ void set_precision (tree arg, vr_values *vr_values)
{
- get_int_range (arg, prec, prec + 1, false, -1);
+ get_int_range (arg, prec, prec + 1, false, -1, vr_values);
}
/* Return true if both width and precision are known to be
@@ -1037,7 +1043,7 @@ struct sprintf_dom_walker::call_info
/* Return the result of formatting a no-op directive (such as '%n'). */
static fmtresult
-format_none (const directive &, tree)
+format_none (const directive &, tree, vr_values *)
{
fmtresult res (0);
return res;
@@ -1046,7 +1052,7 @@ format_none (const directive &, tree)
/* Return the result of formatting the '%%' directive. */
static fmtresult
-format_percent (const directive &, tree)
+format_percent (const directive &, tree, vr_values *)
{
fmtresult res (1);
return res;
@@ -1103,7 +1109,8 @@ build_intmax_type_nodes (tree *pintmax, tree *puintmax)
static bool
get_int_range (tree arg, HOST_WIDE_INT *pmin, HOST_WIDE_INT *pmax,
- bool absolute, HOST_WIDE_INT negbound)
+ bool absolute, HOST_WIDE_INT negbound,
+ class vr_values *vr_values)
{
/* The type of the result. */
const_tree type = integer_type_node;
@@ -1142,9 +1149,10 @@ get_int_range (tree arg, HOST_WIDE_INT *pmin, HOST_WIDE_INT *pmax,
&& TYPE_PRECISION (argtype) <= TYPE_PRECISION (type))
{
/* Try to determine the range of values of the integer argument. */
- wide_int min, max;
- enum value_range_type range_type = get_range_info (arg, &min, &max);
- if (range_type == VR_RANGE)
+ value_range *vr = vr_values->get_value_range (arg);
+ if (vr->type == VR_RANGE
+ && TREE_CODE (vr->min) == INTEGER_CST
+ && TREE_CODE (vr->max) == INTEGER_CST)
{
HOST_WIDE_INT type_min
= (TYPE_UNSIGNED (argtype)
@@ -1153,8 +1161,8 @@ get_int_range (tree arg, HOST_WIDE_INT *pmin, HOST_WIDE_INT *pmax,
HOST_WIDE_INT type_max = tree_to_uhwi (TYPE_MAX_VALUE (argtype));
- *pmin = min.to_shwi ();
- *pmax = max.to_shwi ();
+ *pmin = TREE_INT_CST_LOW (vr->min);
+ *pmax = TREE_INT_CST_LOW (vr->max);
if (*pmin < *pmax)
{
@@ -1174,7 +1182,8 @@ get_int_range (tree arg, HOST_WIDE_INT *pmin, HOST_WIDE_INT *pmax,
/* Handle an argument with an unknown range as if none had been
provided. */
if (unknown)
- return get_int_range (NULL_TREE, pmin, pmax, absolute, negbound);
+ return get_int_range (NULL_TREE, pmin, pmax, absolute,
+ negbound, vr_values);
}
/* Adjust each bound as specified by ABSOLUTE and NEGBOUND. */
@@ -1259,7 +1268,7 @@ adjust_range_for_overflow (tree dirtype, tree *argmin, tree *argmax)
used when the directive argument or its value isn't known. */
static fmtresult
-format_integer (const directive &dir, tree arg)
+format_integer (const directive &dir, tree arg, vr_values *vr_values)
{
tree intmax_type_node;
tree uintmax_type_node;
@@ -1442,12 +1451,13 @@ format_integer (const directive &dir, tree arg)
{
/* Try to determine the range of values of the integer argument
(range information is not available for pointers). */
- wide_int min, max;
- enum value_range_type range_type = get_range_info (arg, &min, &max);
- if (range_type == VR_RANGE)
+ value_range *vr = vr_values->get_value_range (arg);
+ if (vr->type == VR_RANGE
+ && TREE_CODE (vr->min) == INTEGER_CST
+ && TREE_CODE (vr->max) == INTEGER_CST)
{
- argmin = wide_int_to_tree (argtype, min);
- argmax = wide_int_to_tree (argtype, max);
+ argmin = vr->min;
+ argmax = vr->max;
/* Set KNOWNRANGE if the argument is in a known subrange
of the directive's type and neither width nor precision
@@ -1460,11 +1470,12 @@ format_integer (const directive &dir, tree arg)
res.argmin = argmin;
res.argmax = argmax;
}
- else if (range_type == VR_ANTI_RANGE)
+ else if (vr->type == VR_ANTI_RANGE)
{
/* Handle anti-ranges if/when bug 71690 is resolved. */
}
- else if (range_type == VR_VARYING)
+ else if (vr->type == VR_VARYING
+ || vr->type == VR_UNDEFINED)
{
/* The argument here may be the result of promoting the actual
argument to int. Try to determine the type of the actual
@@ -1477,7 +1488,7 @@ format_integer (const directive &dir, tree arg)
if (code == INTEGER_CST)
{
arg = gimple_assign_rhs1 (def);
- return format_integer (dir, arg);
+ return format_integer (dir, arg, vr_values);
}
if (code == NOP_EXPR)
@@ -1522,16 +1533,16 @@ format_integer (const directive &dir, tree arg)
/* For unsigned conversions/directives or signed when
the minimum is positive, use the minimum and maximum to compute
the shortest and longest output, respectively. */
- res.range.min = format_integer (dir, argmin).range.min;
- res.range.max = format_integer (dir, argmax).range.max;
+ res.range.min = format_integer (dir, argmin, vr_values).range.min;
+ res.range.max = format_integer (dir, argmax, vr_values).range.max;
}
else if (tree_int_cst_sgn (argmax) < 0)
{
/* For signed conversions/directives if maximum is negative,
use the minimum as the longest output and maximum as the
shortest output. */
- res.range.min = format_integer (dir, argmax).range.min;
- res.range.max = format_integer (dir, argmin).range.max;
+ res.range.min = format_integer (dir, argmax, vr_values).range.min;
+ res.range.max = format_integer (dir, argmin, vr_values).range.max;
}
else
{
@@ -1539,9 +1550,12 @@ format_integer (const directive &dir, tree arg)
as the shortest output and for the longest output compute the
length of the output of both minimum and maximum and pick the
longer. */
- unsigned HOST_WIDE_INT max1 = format_integer (dir, argmin).range.max;
- unsigned HOST_WIDE_INT max2 = format_integer (dir, argmax).range.max;
- res.range.min = format_integer (dir, integer_zero_node).range.min;
+ unsigned HOST_WIDE_INT max1
+ = format_integer (dir, argmin, vr_values).range.max;
+ unsigned HOST_WIDE_INT max2
+ = format_integer (dir, argmax, vr_values).range.max;
+ res.range.min
+ = format_integer (dir, integer_zero_node, vr_values).range.min;
res.range.max = MAX (max1, max2);
}
@@ -1882,7 +1896,7 @@ format_floating (const directive &dir, const HOST_WIDE_INT prec[2])
ARG. */
static fmtresult
-format_floating (const directive &dir, tree arg)
+format_floating (const directive &dir, tree arg, vr_values *)
{
HOST_WIDE_INT prec[] = { dir.prec[0], dir.prec[1] };
tree type = (dir.modifier == FMT_LEN_L || dir.modifier == FMT_LEN_ll
@@ -2122,7 +2136,7 @@ get_string_length (tree str)
vsprinf). */
static fmtresult
-format_character (const directive &dir, tree arg)
+format_character (const directive &dir, tree arg, vr_values *vr_values)
{
fmtresult res;
@@ -2134,7 +2148,7 @@ format_character (const directive &dir, tree arg)
res.range.min = 0;
HOST_WIDE_INT min, max;
- if (get_int_range (arg, &min, &max, false, 0))
+ if (get_int_range (arg, &min, &max, false, 0, vr_values))
{
if (min == 0 && max == 0)
{
@@ -2187,7 +2201,7 @@ format_character (const directive &dir, tree arg)
vsprinf). */
static fmtresult
-format_string (const directive &dir, tree arg)
+format_string (const directive &dir, tree arg, vr_values *)
{
fmtresult res;
@@ -2348,7 +2362,7 @@ format_string (const directive &dir, tree arg)
/* Format plain string (part of the format string itself). */
static fmtresult
-format_plain (const directive &dir, tree)
+format_plain (const directive &dir, tree, vr_values *)
{
fmtresult res (dir.len);
return res;
@@ -2729,7 +2743,8 @@ maybe_warn (substring_loc &dirloc, location_t argloc,
static bool
format_directive (const sprintf_dom_walker::call_info &info,
- format_result *res, const directive &dir)
+ format_result *res, const directive &dir,
+ class vr_values *vr_values)
{
/* Offset of the beginning of the directive from the beginning
of the format string. */
@@ -2754,7 +2769,7 @@ format_directive (const sprintf_dom_walker::call_info &info,
return false;
/* Compute the range of lengths of the formatted output. */
- fmtresult fmtres = dir.fmtfunc (dir, dir.arg);
+ fmtresult fmtres = dir.fmtfunc (dir, dir.arg, vr_values);
/* Record whether the output of all directives is known to be
bounded by some maximum, implying that their arguments are
@@ -3020,7 +3035,8 @@ format_directive (const sprintf_dom_walker::call_info &info,
static size_t
parse_directive (sprintf_dom_walker::call_info &info,
directive &dir, format_result *res,
- const char *str, unsigned *argno)
+ const char *str, unsigned *argno,
+ vr_values *vr_values)
{
const char *pcnt = strchr (str, target_percent);
dir.beg = str;
@@ -3338,7 +3354,7 @@ parse_directive (sprintf_dom_walker::call_info &info,
if (star_width)
{
if (INTEGRAL_TYPE_P (TREE_TYPE (star_width)))
- dir.set_width (star_width);
+ dir.set_width (star_width, vr_values);
else
{
/* Width specified by a va_list takes on the range [0, -INT_MIN]
@@ -3371,7 +3387,7 @@ parse_directive (sprintf_dom_walker::call_info &info,
if (star_precision)
{
if (INTEGRAL_TYPE_P (TREE_TYPE (star_precision)))
- dir.set_precision (star_precision);
+ dir.set_precision (star_precision, vr_values);
else
{
/* Precision specified by a va_list takes on the range [-1, INT_MAX]
@@ -3456,7 +3472,7 @@ parse_directive (sprintf_dom_walker::call_info &info,
bool
sprintf_dom_walker::compute_format_length (call_info &info,
- format_result *res)
+ format_result *res)
{
if (dump_file)
{
@@ -3493,10 +3509,12 @@ sprintf_dom_walker::compute_format_length (call_info &info,
directive dir = directive ();
dir.dirno = dirno;
- size_t n = parse_directive (info, dir, res, pf, &argno);
+ size_t n = parse_directive (info, dir, res, pf, &argno,
+ evrp_range_analyzer.get_vr_values ());
/* Return failure if the format function fails. */
- if (!format_directive (info, res, dir))
+ if (!format_directive (info, res, dir,
+ evrp_range_analyzer.get_vr_values ()))
return false;
/* Return success the directive is zero bytes long and it's
@@ -3898,16 +3916,13 @@ sprintf_dom_walker::handle_gimple_call (gimple_stmt_iterator *gsi)
/* Try to determine the range of values of the argument
and use the greater of the two at level 1 and the smaller
of them at level 2. */
- wide_int min, max;
- enum value_range_type range_type
- = get_range_info (size, &min, &max);
- if (range_type == VR_RANGE)
- {
- dstsize
- = (warn_level < 2
- ? wi::fits_uhwi_p (max) ? max.to_uhwi () : max.to_shwi ()
- : wi::fits_uhwi_p (min) ? min.to_uhwi () : min.to_shwi ());
- }
+ value_range *vr = evrp_range_analyzer.get_value_range (size);
+ if (vr->type == VR_RANGE
+ && TREE_CODE (vr->min) == INTEGER_CST
+ && TREE_CODE (vr->max) == INTEGER_CST)
+ dstsize = (warn_level < 2
+ ? TREE_INT_CST_LOW (vr->max)
+ : TREE_INT_CST_LOW (vr->min));
/* The destination size is not constant. If the function is
bounded (e.g., snprintf) a lower bound of zero doesn't
@@ -4012,11 +4027,15 @@ sprintf_dom_walker::handle_gimple_call (gimple_stmt_iterator *gsi)
edge
sprintf_dom_walker::before_dom_children (basic_block bb)
{
+ evrp_range_analyzer.enter (bb);
for (gimple_stmt_iterator si = gsi_start_bb (bb); !gsi_end_p (si); )
{
/* Iterate over statements, looking for function calls. */
gimple *stmt = gsi_stmt (si);
+ /* First record ranges generated by this statement. */
+ evrp_range_analyzer.record_ranges_from_stmt (stmt, false);
+
if (is_gimple_call (stmt) && handle_gimple_call (&si))
/* If handle_gimple_call returns true, the iterator is
already pointing to the next statement. */
@@ -4027,6 +4046,12 @@ sprintf_dom_walker::before_dom_children (basic_block bb)
return NULL;
}
+void
+sprintf_dom_walker::after_dom_children (basic_block bb)
+{
+ evrp_range_analyzer.leave (bb);
+}
+
/* Execute the pass for function FUN. */
unsigned int