annotate gcc/gimple-ssa-sprintf.c @ 118:fd00160c1b76

ifdef TARGET_64BIT
author mir3636
date Tue, 27 Feb 2018 15:01:35 +0900
parents 04ced10e8804
children 84e7813d76e9
Ignore whitespace changes - Everywhere: Within whitespace: At end of lines:
rev   line source
111
kono
parents:
diff changeset
1 /* Copyright (C) 2016-2017 Free Software Foundation, Inc.
kono
parents:
diff changeset
2 Contributed by Martin Sebor <msebor@redhat.com>.
kono
parents:
diff changeset
3
kono
parents:
diff changeset
4 This file is part of GCC.
kono
parents:
diff changeset
5
kono
parents:
diff changeset
6 GCC is free software; you can redistribute it and/or modify it under
kono
parents:
diff changeset
7 the terms of the GNU General Public License as published by the Free
kono
parents:
diff changeset
8 Software Foundation; either version 3, or (at your option) any later
kono
parents:
diff changeset
9 version.
kono
parents:
diff changeset
10
kono
parents:
diff changeset
11 GCC is distributed in the hope that it will be useful, but WITHOUT ANY
kono
parents:
diff changeset
12 WARRANTY; without even the implied warranty of MERCHANTABILITY or
kono
parents:
diff changeset
13 FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
kono
parents:
diff changeset
14 for more details.
kono
parents:
diff changeset
15
kono
parents:
diff changeset
16 You should have received a copy of the GNU General Public License
kono
parents:
diff changeset
17 along with GCC; see the file COPYING3. If not see
kono
parents:
diff changeset
18 <http://www.gnu.org/licenses/>. */
kono
parents:
diff changeset
19
kono
parents:
diff changeset
20 /* This file implements the printf-return-value pass. The pass does
kono
parents:
diff changeset
21 two things: 1) it analyzes calls to formatted output functions like
kono
parents:
diff changeset
22 sprintf looking for possible buffer overflows and calls to bounded
kono
parents:
diff changeset
23 functions like snprintf for early truncation (and under the control
kono
parents:
diff changeset
24 of the -Wformat-length option issues warnings), and 2) under the
kono
parents:
diff changeset
25 control of the -fprintf-return-value option it folds the return
kono
parents:
diff changeset
26 value of safe calls into constants, making it possible to eliminate
kono
parents:
diff changeset
27 code that depends on the value of those constants.
kono
parents:
diff changeset
28
kono
parents:
diff changeset
29 For all functions (bounded or not) the pass uses the size of the
kono
parents:
diff changeset
30 destination object. That means that it will diagnose calls to
kono
parents:
diff changeset
31 snprintf not on the basis of the size specified by the function's
kono
parents:
diff changeset
32 second argument but rathger on the basis of the size the first
kono
parents:
diff changeset
33 argument points to (if possible). For bound-checking built-ins
kono
parents:
diff changeset
34 like __builtin___snprintf_chk the pass uses the size typically
kono
parents:
diff changeset
35 determined by __builtin_object_size and passed to the built-in
kono
parents:
diff changeset
36 by the Glibc inline wrapper.
kono
parents:
diff changeset
37
kono
parents:
diff changeset
38 The pass handles all forms standard sprintf format directives,
kono
parents:
diff changeset
39 including character, integer, floating point, pointer, and strings,
kono
parents:
diff changeset
40 with the standard C flags, widths, and precisions. For integers
kono
parents:
diff changeset
41 and strings it computes the length of output itself. For floating
kono
parents:
diff changeset
42 point it uses MPFR to fornmat known constants with up and down
kono
parents:
diff changeset
43 rounding and uses the resulting range of output lengths. For
kono
parents:
diff changeset
44 strings it uses the length of string literals and the sizes of
kono
parents:
diff changeset
45 character arrays that a character pointer may point to as a bound
kono
parents:
diff changeset
46 on the longest string. */
kono
parents:
diff changeset
47
kono
parents:
diff changeset
48 #include "config.h"
kono
parents:
diff changeset
49 #include "system.h"
kono
parents:
diff changeset
50 #include "coretypes.h"
kono
parents:
diff changeset
51 #include "backend.h"
kono
parents:
diff changeset
52 #include "tree.h"
kono
parents:
diff changeset
53 #include "gimple.h"
kono
parents:
diff changeset
54 #include "tree-pass.h"
kono
parents:
diff changeset
55 #include "ssa.h"
kono
parents:
diff changeset
56 #include "gimple-fold.h"
kono
parents:
diff changeset
57 #include "gimple-pretty-print.h"
kono
parents:
diff changeset
58 #include "diagnostic-core.h"
kono
parents:
diff changeset
59 #include "fold-const.h"
kono
parents:
diff changeset
60 #include "gimple-iterator.h"
kono
parents:
diff changeset
61 #include "tree-ssa.h"
kono
parents:
diff changeset
62 #include "tree-object-size.h"
kono
parents:
diff changeset
63 #include "params.h"
kono
parents:
diff changeset
64 #include "tree-cfg.h"
kono
parents:
diff changeset
65 #include "tree-ssa-propagate.h"
kono
parents:
diff changeset
66 #include "calls.h"
kono
parents:
diff changeset
67 #include "cfgloop.h"
kono
parents:
diff changeset
68 #include "intl.h"
kono
parents:
diff changeset
69 #include "langhooks.h"
kono
parents:
diff changeset
70
kono
parents:
diff changeset
71 #include "builtins.h"
kono
parents:
diff changeset
72 #include "stor-layout.h"
kono
parents:
diff changeset
73
kono
parents:
diff changeset
74 #include "realmpfr.h"
kono
parents:
diff changeset
75 #include "target.h"
kono
parents:
diff changeset
76
kono
parents:
diff changeset
77 #include "cpplib.h"
kono
parents:
diff changeset
78 #include "input.h"
kono
parents:
diff changeset
79 #include "toplev.h"
kono
parents:
diff changeset
80 #include "substring-locations.h"
kono
parents:
diff changeset
81 #include "diagnostic.h"
kono
parents:
diff changeset
82
kono
parents:
diff changeset
83 /* The likely worst case value of MB_LEN_MAX for the target, large enough
kono
parents:
diff changeset
84 for UTF-8. Ideally, this would be obtained by a target hook if it were
kono
parents:
diff changeset
85 to be used for optimization but it's good enough as is for warnings. */
kono
parents:
diff changeset
86 #define target_mb_len_max() 6
kono
parents:
diff changeset
87
kono
parents:
diff changeset
88 /* The maximum number of bytes a single non-string directive can result
kono
parents:
diff changeset
89 in. This is the result of printf("%.*Lf", INT_MAX, -LDBL_MAX) for
kono
parents:
diff changeset
90 LDBL_MAX_10_EXP of 4932. */
kono
parents:
diff changeset
91 #define IEEE_MAX_10_EXP 4932
kono
parents:
diff changeset
92 #define target_dir_max() (target_int_max () + IEEE_MAX_10_EXP + 2)
kono
parents:
diff changeset
93
kono
parents:
diff changeset
94 namespace {
kono
parents:
diff changeset
95
kono
parents:
diff changeset
96 const pass_data pass_data_sprintf_length = {
kono
parents:
diff changeset
97 GIMPLE_PASS, // pass type
kono
parents:
diff changeset
98 "printf-return-value", // pass name
kono
parents:
diff changeset
99 OPTGROUP_NONE, // optinfo_flags
kono
parents:
diff changeset
100 TV_NONE, // tv_id
kono
parents:
diff changeset
101 PROP_cfg, // properties_required
kono
parents:
diff changeset
102 0, // properties_provided
kono
parents:
diff changeset
103 0, // properties_destroyed
kono
parents:
diff changeset
104 0, // properties_start
kono
parents:
diff changeset
105 0, // properties_finish
kono
parents:
diff changeset
106 };
kono
parents:
diff changeset
107
kono
parents:
diff changeset
108 /* Set to the warning level for the current function which is equal
kono
parents:
diff changeset
109 either to warn_format_trunc for bounded functions or to
kono
parents:
diff changeset
110 warn_format_overflow otherwise. */
kono
parents:
diff changeset
111
kono
parents:
diff changeset
112 static int warn_level;
kono
parents:
diff changeset
113
kono
parents:
diff changeset
114 struct format_result;
kono
parents:
diff changeset
115
kono
parents:
diff changeset
116 class pass_sprintf_length : public gimple_opt_pass
kono
parents:
diff changeset
117 {
kono
parents:
diff changeset
118 bool fold_return_value;
kono
parents:
diff changeset
119
kono
parents:
diff changeset
120 public:
kono
parents:
diff changeset
121 pass_sprintf_length (gcc::context *ctxt)
kono
parents:
diff changeset
122 : gimple_opt_pass (pass_data_sprintf_length, ctxt),
kono
parents:
diff changeset
123 fold_return_value (false)
kono
parents:
diff changeset
124 { }
kono
parents:
diff changeset
125
kono
parents:
diff changeset
126 opt_pass * clone () { return new pass_sprintf_length (m_ctxt); }
kono
parents:
diff changeset
127
kono
parents:
diff changeset
128 virtual bool gate (function *);
kono
parents:
diff changeset
129
kono
parents:
diff changeset
130 virtual unsigned int execute (function *);
kono
parents:
diff changeset
131
kono
parents:
diff changeset
132 void set_pass_param (unsigned int n, bool param)
kono
parents:
diff changeset
133 {
kono
parents:
diff changeset
134 gcc_assert (n == 0);
kono
parents:
diff changeset
135 fold_return_value = param;
kono
parents:
diff changeset
136 }
kono
parents:
diff changeset
137
kono
parents:
diff changeset
138 bool handle_gimple_call (gimple_stmt_iterator *);
kono
parents:
diff changeset
139
kono
parents:
diff changeset
140 struct call_info;
kono
parents:
diff changeset
141 bool compute_format_length (call_info &, format_result *);
kono
parents:
diff changeset
142 };
kono
parents:
diff changeset
143
kono
parents:
diff changeset
144 bool
kono
parents:
diff changeset
145 pass_sprintf_length::gate (function *)
kono
parents:
diff changeset
146 {
kono
parents:
diff changeset
147 /* Run the pass iff -Warn-format-overflow or -Warn-format-truncation
kono
parents:
diff changeset
148 is specified and either not optimizing and the pass is being invoked
kono
parents:
diff changeset
149 early, or when optimizing and the pass is being invoked during
kono
parents:
diff changeset
150 optimization (i.e., "late"). */
kono
parents:
diff changeset
151 return ((warn_format_overflow > 0
kono
parents:
diff changeset
152 || warn_format_trunc > 0
kono
parents:
diff changeset
153 || flag_printf_return_value)
kono
parents:
diff changeset
154 && (optimize > 0) == fold_return_value);
kono
parents:
diff changeset
155 }
kono
parents:
diff changeset
156
kono
parents:
diff changeset
157 /* The minimum, maximum, likely, and unlikely maximum number of bytes
kono
parents:
diff changeset
158 of output either a formatting function or an individual directive
kono
parents:
diff changeset
159 can result in. */
kono
parents:
diff changeset
160
kono
parents:
diff changeset
161 struct result_range
kono
parents:
diff changeset
162 {
kono
parents:
diff changeset
163 /* The absolute minimum number of bytes. The result of a successful
kono
parents:
diff changeset
164 conversion is guaranteed to be no less than this. (An erroneous
kono
parents:
diff changeset
165 conversion can be indicated by MIN > HOST_WIDE_INT_MAX.) */
kono
parents:
diff changeset
166 unsigned HOST_WIDE_INT min;
kono
parents:
diff changeset
167 /* The likely maximum result that is used in diagnostics. In most
kono
parents:
diff changeset
168 cases MAX is the same as the worst case UNLIKELY result. */
kono
parents:
diff changeset
169 unsigned HOST_WIDE_INT max;
kono
parents:
diff changeset
170 /* The likely result used to trigger diagnostics. For conversions
kono
parents:
diff changeset
171 that result in a range of bytes [MIN, MAX], LIKELY is somewhere
kono
parents:
diff changeset
172 in that range. */
kono
parents:
diff changeset
173 unsigned HOST_WIDE_INT likely;
kono
parents:
diff changeset
174 /* In rare cases (e.g., for nultibyte characters) UNLIKELY gives
kono
parents:
diff changeset
175 the worst cases maximum result of a directive. In most cases
kono
parents:
diff changeset
176 UNLIKELY == MAX. UNLIKELY is used to control the return value
kono
parents:
diff changeset
177 optimization but not in diagnostics. */
kono
parents:
diff changeset
178 unsigned HOST_WIDE_INT unlikely;
kono
parents:
diff changeset
179 };
kono
parents:
diff changeset
180
kono
parents:
diff changeset
181 /* The result of a call to a formatted function. */
kono
parents:
diff changeset
182
kono
parents:
diff changeset
183 struct format_result
kono
parents:
diff changeset
184 {
kono
parents:
diff changeset
185 /* Range of characters written by the formatted function.
kono
parents:
diff changeset
186 Setting the minimum to HOST_WIDE_INT_MAX disables all
kono
parents:
diff changeset
187 length tracking for the remainder of the format string. */
kono
parents:
diff changeset
188 result_range range;
kono
parents:
diff changeset
189
kono
parents:
diff changeset
190 /* True when the range above is obtained from known values of
kono
parents:
diff changeset
191 directive arguments, or bounds on the amount of output such
kono
parents:
diff changeset
192 as width and precision, and not the result of heuristics that
kono
parents:
diff changeset
193 depend on warning levels. It's used to issue stricter diagnostics
kono
parents:
diff changeset
194 in cases where strings of unknown lengths are bounded by the arrays
kono
parents:
diff changeset
195 they are determined to refer to. KNOWNRANGE must not be used for
kono
parents:
diff changeset
196 the return value optimization. */
kono
parents:
diff changeset
197 bool knownrange;
kono
parents:
diff changeset
198
kono
parents:
diff changeset
199 /* True if no individual directive resulted in more than 4095 bytes
kono
parents:
diff changeset
200 of output (the total NUMBER_CHARS_{MIN,MAX} might be greater).
kono
parents:
diff changeset
201 Implementations are not required to handle directives that produce
kono
parents:
diff changeset
202 more than 4K bytes (leading to undefined behavior) and so when one
kono
parents:
diff changeset
203 is found it disables the return value optimization. */
kono
parents:
diff changeset
204 bool under4k;
kono
parents:
diff changeset
205
kono
parents:
diff changeset
206 /* True when a floating point directive has been seen in the format
kono
parents:
diff changeset
207 string. */
kono
parents:
diff changeset
208 bool floating;
kono
parents:
diff changeset
209
kono
parents:
diff changeset
210 /* True when an intermediate result has caused a warning. Used to
kono
parents:
diff changeset
211 avoid issuing duplicate warnings while finishing the processing
kono
parents:
diff changeset
212 of a call. WARNED also disables the return value optimization. */
kono
parents:
diff changeset
213 bool warned;
kono
parents:
diff changeset
214
kono
parents:
diff changeset
215 /* Preincrement the number of output characters by 1. */
kono
parents:
diff changeset
216 format_result& operator++ ()
kono
parents:
diff changeset
217 {
kono
parents:
diff changeset
218 return *this += 1;
kono
parents:
diff changeset
219 }
kono
parents:
diff changeset
220
kono
parents:
diff changeset
221 /* Postincrement the number of output characters by 1. */
kono
parents:
diff changeset
222 format_result operator++ (int)
kono
parents:
diff changeset
223 {
kono
parents:
diff changeset
224 format_result prev (*this);
kono
parents:
diff changeset
225 *this += 1;
kono
parents:
diff changeset
226 return prev;
kono
parents:
diff changeset
227 }
kono
parents:
diff changeset
228
kono
parents:
diff changeset
229 /* Increment the number of output characters by N. */
kono
parents:
diff changeset
230 format_result& operator+= (unsigned HOST_WIDE_INT);
kono
parents:
diff changeset
231 };
kono
parents:
diff changeset
232
kono
parents:
diff changeset
233 format_result&
kono
parents:
diff changeset
234 format_result::operator+= (unsigned HOST_WIDE_INT n)
kono
parents:
diff changeset
235 {
kono
parents:
diff changeset
236 gcc_assert (n < HOST_WIDE_INT_MAX);
kono
parents:
diff changeset
237
kono
parents:
diff changeset
238 if (range.min < HOST_WIDE_INT_MAX)
kono
parents:
diff changeset
239 range.min += n;
kono
parents:
diff changeset
240
kono
parents:
diff changeset
241 if (range.max < HOST_WIDE_INT_MAX)
kono
parents:
diff changeset
242 range.max += n;
kono
parents:
diff changeset
243
kono
parents:
diff changeset
244 if (range.likely < HOST_WIDE_INT_MAX)
kono
parents:
diff changeset
245 range.likely += n;
kono
parents:
diff changeset
246
kono
parents:
diff changeset
247 if (range.unlikely < HOST_WIDE_INT_MAX)
kono
parents:
diff changeset
248 range.unlikely += n;
kono
parents:
diff changeset
249
kono
parents:
diff changeset
250 return *this;
kono
parents:
diff changeset
251 }
kono
parents:
diff changeset
252
kono
parents:
diff changeset
253 /* Return the value of INT_MIN for the target. */
kono
parents:
diff changeset
254
kono
parents:
diff changeset
255 static inline HOST_WIDE_INT
kono
parents:
diff changeset
256 target_int_min ()
kono
parents:
diff changeset
257 {
kono
parents:
diff changeset
258 return tree_to_shwi (TYPE_MIN_VALUE (integer_type_node));
kono
parents:
diff changeset
259 }
kono
parents:
diff changeset
260
kono
parents:
diff changeset
261 /* Return the value of INT_MAX for the target. */
kono
parents:
diff changeset
262
kono
parents:
diff changeset
263 static inline unsigned HOST_WIDE_INT
kono
parents:
diff changeset
264 target_int_max ()
kono
parents:
diff changeset
265 {
kono
parents:
diff changeset
266 return tree_to_uhwi (TYPE_MAX_VALUE (integer_type_node));
kono
parents:
diff changeset
267 }
kono
parents:
diff changeset
268
kono
parents:
diff changeset
269 /* Return the value of SIZE_MAX for the target. */
kono
parents:
diff changeset
270
kono
parents:
diff changeset
271 static inline unsigned HOST_WIDE_INT
kono
parents:
diff changeset
272 target_size_max ()
kono
parents:
diff changeset
273 {
kono
parents:
diff changeset
274 return tree_to_uhwi (TYPE_MAX_VALUE (size_type_node));
kono
parents:
diff changeset
275 }
kono
parents:
diff changeset
276
kono
parents:
diff changeset
277 /* A straightforward mapping from the execution character set to the host
kono
parents:
diff changeset
278 character set indexed by execution character. */
kono
parents:
diff changeset
279
kono
parents:
diff changeset
280 static char target_to_host_charmap[256];
kono
parents:
diff changeset
281
kono
parents:
diff changeset
282 /* Initialize a mapping from the execution character set to the host
kono
parents:
diff changeset
283 character set. */
kono
parents:
diff changeset
284
kono
parents:
diff changeset
285 static bool
kono
parents:
diff changeset
286 init_target_to_host_charmap ()
kono
parents:
diff changeset
287 {
kono
parents:
diff changeset
288 /* If the percent sign is non-zero the mapping has already been
kono
parents:
diff changeset
289 initialized. */
kono
parents:
diff changeset
290 if (target_to_host_charmap['%'])
kono
parents:
diff changeset
291 return true;
kono
parents:
diff changeset
292
kono
parents:
diff changeset
293 /* Initialize the target_percent character (done elsewhere). */
kono
parents:
diff changeset
294 if (!init_target_chars ())
kono
parents:
diff changeset
295 return false;
kono
parents:
diff changeset
296
kono
parents:
diff changeset
297 /* The subset of the source character set used by printf conversion
kono
parents:
diff changeset
298 specifications (strictly speaking, not all letters are used but
kono
parents:
diff changeset
299 they are included here for the sake of simplicity). The dollar
kono
parents:
diff changeset
300 sign must be included even though it's not in the basic source
kono
parents:
diff changeset
301 character set. */
kono
parents:
diff changeset
302 const char srcset[] = " 0123456789!\"#%&'()*+,-./:;<=>?[\\]^_{|}~$"
kono
parents:
diff changeset
303 "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz";
kono
parents:
diff changeset
304
kono
parents:
diff changeset
305 /* Set the mapping for all characters to some ordinary value (i,e.,
kono
parents:
diff changeset
306 not none used in printf conversion specifications) and overwrite
kono
parents:
diff changeset
307 those that are used by conversion specifications with their
kono
parents:
diff changeset
308 corresponding values. */
kono
parents:
diff changeset
309 memset (target_to_host_charmap + 1, '?', sizeof target_to_host_charmap - 1);
kono
parents:
diff changeset
310
kono
parents:
diff changeset
311 /* Are the two sets of characters the same? */
kono
parents:
diff changeset
312 bool all_same_p = true;
kono
parents:
diff changeset
313
kono
parents:
diff changeset
314 for (const char *pc = srcset; *pc; ++pc)
kono
parents:
diff changeset
315 {
kono
parents:
diff changeset
316 /* Slice off the high end bits in case target characters are
kono
parents:
diff changeset
317 signed. All values are expected to be non-nul, otherwise
kono
parents:
diff changeset
318 there's a problem. */
kono
parents:
diff changeset
319 if (unsigned char tc = lang_hooks.to_target_charset (*pc))
kono
parents:
diff changeset
320 {
kono
parents:
diff changeset
321 target_to_host_charmap[tc] = *pc;
kono
parents:
diff changeset
322 if (tc != *pc)
kono
parents:
diff changeset
323 all_same_p = false;
kono
parents:
diff changeset
324 }
kono
parents:
diff changeset
325 else
kono
parents:
diff changeset
326 return false;
kono
parents:
diff changeset
327
kono
parents:
diff changeset
328 }
kono
parents:
diff changeset
329
kono
parents:
diff changeset
330 /* Set the first element to a non-zero value if the mapping
kono
parents:
diff changeset
331 is 1-to-1, otherwise leave it clear (NUL is assumed to be
kono
parents:
diff changeset
332 the same in both character sets). */
kono
parents:
diff changeset
333 target_to_host_charmap[0] = all_same_p;
kono
parents:
diff changeset
334
kono
parents:
diff changeset
335 return true;
kono
parents:
diff changeset
336 }
kono
parents:
diff changeset
337
kono
parents:
diff changeset
338 /* Return the host source character corresponding to the character
kono
parents:
diff changeset
339 CH in the execution character set if one exists, or some innocuous
kono
parents:
diff changeset
340 (non-special, non-nul) source character otherwise. */
kono
parents:
diff changeset
341
kono
parents:
diff changeset
342 static inline unsigned char
kono
parents:
diff changeset
343 target_to_host (unsigned char ch)
kono
parents:
diff changeset
344 {
kono
parents:
diff changeset
345 return target_to_host_charmap[ch];
kono
parents:
diff changeset
346 }
kono
parents:
diff changeset
347
kono
parents:
diff changeset
348 /* Convert an initial substring of the string TARGSTR consisting of
kono
parents:
diff changeset
349 characters in the execution character set into a string in the
kono
parents:
diff changeset
350 source character set on the host and store up to HOSTSZ characters
kono
parents:
diff changeset
351 in the buffer pointed to by HOSTR. Return HOSTR. */
kono
parents:
diff changeset
352
kono
parents:
diff changeset
353 static const char*
kono
parents:
diff changeset
354 target_to_host (char *hostr, size_t hostsz, const char *targstr)
kono
parents:
diff changeset
355 {
kono
parents:
diff changeset
356 /* Make sure the buffer is reasonably big. */
kono
parents:
diff changeset
357 gcc_assert (hostsz > 4);
kono
parents:
diff changeset
358
kono
parents:
diff changeset
359 /* The interesting subset of source and execution characters are
kono
parents:
diff changeset
360 the same so no conversion is necessary. However, truncate
kono
parents:
diff changeset
361 overlong strings just like the translated strings are. */
kono
parents:
diff changeset
362 if (target_to_host_charmap['\0'] == 1)
kono
parents:
diff changeset
363 {
kono
parents:
diff changeset
364 strncpy (hostr, targstr, hostsz - 4);
kono
parents:
diff changeset
365 if (strlen (targstr) >= hostsz)
kono
parents:
diff changeset
366 strcpy (hostr + hostsz - 4, "...");
kono
parents:
diff changeset
367 return hostr;
kono
parents:
diff changeset
368 }
kono
parents:
diff changeset
369
kono
parents:
diff changeset
370 /* Convert the initial substring of TARGSTR to the corresponding
kono
parents:
diff changeset
371 characters in the host set, appending "..." if TARGSTR is too
kono
parents:
diff changeset
372 long to fit. Using the static buffer assumes the function is
kono
parents:
diff changeset
373 not called in between sequence points (which it isn't). */
kono
parents:
diff changeset
374 for (char *ph = hostr; ; ++targstr)
kono
parents:
diff changeset
375 {
kono
parents:
diff changeset
376 *ph++ = target_to_host (*targstr);
kono
parents:
diff changeset
377 if (!*targstr)
kono
parents:
diff changeset
378 break;
kono
parents:
diff changeset
379
kono
parents:
diff changeset
380 if (size_t (ph - hostr) == hostsz - 4)
kono
parents:
diff changeset
381 {
kono
parents:
diff changeset
382 *ph = '\0';
kono
parents:
diff changeset
383 strcat (ph, "...");
kono
parents:
diff changeset
384 break;
kono
parents:
diff changeset
385 }
kono
parents:
diff changeset
386 }
kono
parents:
diff changeset
387
kono
parents:
diff changeset
388 return hostr;
kono
parents:
diff changeset
389 }
kono
parents:
diff changeset
390
kono
parents:
diff changeset
391 /* Convert the sequence of decimal digits in the execution character
kono
parents:
diff changeset
392 starting at S to a long, just like strtol does. Return the result
kono
parents:
diff changeset
393 and set *END to one past the last converted character. On range
kono
parents:
diff changeset
394 error set ERANGE to the digit that caused it. */
kono
parents:
diff changeset
395
kono
parents:
diff changeset
396 static inline long
kono
parents:
diff changeset
397 target_strtol10 (const char **ps, const char **erange)
kono
parents:
diff changeset
398 {
kono
parents:
diff changeset
399 unsigned HOST_WIDE_INT val = 0;
kono
parents:
diff changeset
400 for ( ; ; ++*ps)
kono
parents:
diff changeset
401 {
kono
parents:
diff changeset
402 unsigned char c = target_to_host (**ps);
kono
parents:
diff changeset
403 if (ISDIGIT (c))
kono
parents:
diff changeset
404 {
kono
parents:
diff changeset
405 c -= '0';
kono
parents:
diff changeset
406
kono
parents:
diff changeset
407 /* Check for overflow. */
kono
parents:
diff changeset
408 if (val > (LONG_MAX - c) / 10LU)
kono
parents:
diff changeset
409 {
kono
parents:
diff changeset
410 val = LONG_MAX;
kono
parents:
diff changeset
411 *erange = *ps;
kono
parents:
diff changeset
412
kono
parents:
diff changeset
413 /* Skip the remaining digits. */
kono
parents:
diff changeset
414 do
kono
parents:
diff changeset
415 c = target_to_host (*++*ps);
kono
parents:
diff changeset
416 while (ISDIGIT (c));
kono
parents:
diff changeset
417 break;
kono
parents:
diff changeset
418 }
kono
parents:
diff changeset
419 else
kono
parents:
diff changeset
420 val = val * 10 + c;
kono
parents:
diff changeset
421 }
kono
parents:
diff changeset
422 else
kono
parents:
diff changeset
423 break;
kono
parents:
diff changeset
424 }
kono
parents:
diff changeset
425
kono
parents:
diff changeset
426 return val;
kono
parents:
diff changeset
427 }
kono
parents:
diff changeset
428
kono
parents:
diff changeset
429 /* Return the constant initial value of DECL if available or DECL
kono
parents:
diff changeset
430 otherwise. Same as the synonymous function in c/c-typeck.c. */
kono
parents:
diff changeset
431
kono
parents:
diff changeset
432 static tree
kono
parents:
diff changeset
433 decl_constant_value (tree decl)
kono
parents:
diff changeset
434 {
kono
parents:
diff changeset
435 if (/* Don't change a variable array bound or initial value to a constant
kono
parents:
diff changeset
436 in a place where a variable is invalid. Note that DECL_INITIAL
kono
parents:
diff changeset
437 isn't valid for a PARM_DECL. */
kono
parents:
diff changeset
438 current_function_decl != 0
kono
parents:
diff changeset
439 && TREE_CODE (decl) != PARM_DECL
kono
parents:
diff changeset
440 && !TREE_THIS_VOLATILE (decl)
kono
parents:
diff changeset
441 && TREE_READONLY (decl)
kono
parents:
diff changeset
442 && DECL_INITIAL (decl) != 0
kono
parents:
diff changeset
443 && TREE_CODE (DECL_INITIAL (decl)) != ERROR_MARK
kono
parents:
diff changeset
444 /* This is invalid if initial value is not constant.
kono
parents:
diff changeset
445 If it has either a function call, a memory reference,
kono
parents:
diff changeset
446 or a variable, then re-evaluating it could give different results. */
kono
parents:
diff changeset
447 && TREE_CONSTANT (DECL_INITIAL (decl))
kono
parents:
diff changeset
448 /* Check for cases where this is sub-optimal, even though valid. */
kono
parents:
diff changeset
449 && TREE_CODE (DECL_INITIAL (decl)) != CONSTRUCTOR)
kono
parents:
diff changeset
450 return DECL_INITIAL (decl);
kono
parents:
diff changeset
451 return decl;
kono
parents:
diff changeset
452 }
kono
parents:
diff changeset
453
kono
parents:
diff changeset
454 /* Given FORMAT, set *PLOC to the source location of the format string
kono
parents:
diff changeset
455 and return the format string if it is known or null otherwise. */
kono
parents:
diff changeset
456
kono
parents:
diff changeset
457 static const char*
kono
parents:
diff changeset
458 get_format_string (tree format, location_t *ploc)
kono
parents:
diff changeset
459 {
kono
parents:
diff changeset
460 if (VAR_P (format))
kono
parents:
diff changeset
461 {
kono
parents:
diff changeset
462 /* Pull out a constant value if the front end didn't. */
kono
parents:
diff changeset
463 format = decl_constant_value (format);
kono
parents:
diff changeset
464 STRIP_NOPS (format);
kono
parents:
diff changeset
465 }
kono
parents:
diff changeset
466
kono
parents:
diff changeset
467 if (integer_zerop (format))
kono
parents:
diff changeset
468 {
kono
parents:
diff changeset
469 /* FIXME: Diagnose null format string if it hasn't been diagnosed
kono
parents:
diff changeset
470 by -Wformat (the latter diagnoses only nul pointer constants,
kono
parents:
diff changeset
471 this pass can do better). */
kono
parents:
diff changeset
472 return NULL;
kono
parents:
diff changeset
473 }
kono
parents:
diff changeset
474
kono
parents:
diff changeset
475 HOST_WIDE_INT offset = 0;
kono
parents:
diff changeset
476
kono
parents:
diff changeset
477 if (TREE_CODE (format) == POINTER_PLUS_EXPR)
kono
parents:
diff changeset
478 {
kono
parents:
diff changeset
479 tree arg0 = TREE_OPERAND (format, 0);
kono
parents:
diff changeset
480 tree arg1 = TREE_OPERAND (format, 1);
kono
parents:
diff changeset
481 STRIP_NOPS (arg0);
kono
parents:
diff changeset
482 STRIP_NOPS (arg1);
kono
parents:
diff changeset
483
kono
parents:
diff changeset
484 if (TREE_CODE (arg1) != INTEGER_CST)
kono
parents:
diff changeset
485 return NULL;
kono
parents:
diff changeset
486
kono
parents:
diff changeset
487 format = arg0;
kono
parents:
diff changeset
488
kono
parents:
diff changeset
489 /* POINTER_PLUS_EXPR offsets are to be interpreted signed. */
kono
parents:
diff changeset
490 if (!cst_and_fits_in_hwi (arg1))
kono
parents:
diff changeset
491 return NULL;
kono
parents:
diff changeset
492
kono
parents:
diff changeset
493 offset = int_cst_value (arg1);
kono
parents:
diff changeset
494 }
kono
parents:
diff changeset
495
kono
parents:
diff changeset
496 if (TREE_CODE (format) != ADDR_EXPR)
kono
parents:
diff changeset
497 return NULL;
kono
parents:
diff changeset
498
kono
parents:
diff changeset
499 *ploc = EXPR_LOC_OR_LOC (format, input_location);
kono
parents:
diff changeset
500
kono
parents:
diff changeset
501 format = TREE_OPERAND (format, 0);
kono
parents:
diff changeset
502
kono
parents:
diff changeset
503 if (TREE_CODE (format) == ARRAY_REF
kono
parents:
diff changeset
504 && tree_fits_shwi_p (TREE_OPERAND (format, 1))
kono
parents:
diff changeset
505 && (offset += tree_to_shwi (TREE_OPERAND (format, 1))) >= 0)
kono
parents:
diff changeset
506 format = TREE_OPERAND (format, 0);
kono
parents:
diff changeset
507
kono
parents:
diff changeset
508 if (offset < 0)
kono
parents:
diff changeset
509 return NULL;
kono
parents:
diff changeset
510
kono
parents:
diff changeset
511 tree array_init;
kono
parents:
diff changeset
512 tree array_size = NULL_TREE;
kono
parents:
diff changeset
513
kono
parents:
diff changeset
514 if (VAR_P (format)
kono
parents:
diff changeset
515 && TREE_CODE (TREE_TYPE (format)) == ARRAY_TYPE
kono
parents:
diff changeset
516 && (array_init = decl_constant_value (format)) != format
kono
parents:
diff changeset
517 && TREE_CODE (array_init) == STRING_CST)
kono
parents:
diff changeset
518 {
kono
parents:
diff changeset
519 /* Extract the string constant initializer. Note that this may
kono
parents:
diff changeset
520 include a trailing NUL character that is not in the array (e.g.
kono
parents:
diff changeset
521 const char a[3] = "foo";). */
kono
parents:
diff changeset
522 array_size = DECL_SIZE_UNIT (format);
kono
parents:
diff changeset
523 format = array_init;
kono
parents:
diff changeset
524 }
kono
parents:
diff changeset
525
kono
parents:
diff changeset
526 if (TREE_CODE (format) != STRING_CST)
kono
parents:
diff changeset
527 return NULL;
kono
parents:
diff changeset
528
kono
parents:
diff changeset
529 tree type = TREE_TYPE (format);
kono
parents:
diff changeset
530
kono
parents:
diff changeset
531 scalar_int_mode char_mode;
kono
parents:
diff changeset
532 if (!is_int_mode (TYPE_MODE (TREE_TYPE (type)), &char_mode)
kono
parents:
diff changeset
533 || GET_MODE_SIZE (char_mode) != 1)
kono
parents:
diff changeset
534 {
kono
parents:
diff changeset
535 /* Wide format string. */
kono
parents:
diff changeset
536 return NULL;
kono
parents:
diff changeset
537 }
kono
parents:
diff changeset
538
kono
parents:
diff changeset
539 const char *fmtstr = TREE_STRING_POINTER (format);
kono
parents:
diff changeset
540 unsigned fmtlen = TREE_STRING_LENGTH (format);
kono
parents:
diff changeset
541
kono
parents:
diff changeset
542 if (array_size)
kono
parents:
diff changeset
543 {
kono
parents:
diff changeset
544 /* Variable length arrays can't be initialized. */
kono
parents:
diff changeset
545 gcc_assert (TREE_CODE (array_size) == INTEGER_CST);
kono
parents:
diff changeset
546
kono
parents:
diff changeset
547 if (tree_fits_shwi_p (array_size))
kono
parents:
diff changeset
548 {
kono
parents:
diff changeset
549 HOST_WIDE_INT array_size_value = tree_to_shwi (array_size);
kono
parents:
diff changeset
550 if (array_size_value > 0
kono
parents:
diff changeset
551 && array_size_value == (int) array_size_value
kono
parents:
diff changeset
552 && fmtlen > array_size_value)
kono
parents:
diff changeset
553 fmtlen = array_size_value;
kono
parents:
diff changeset
554 }
kono
parents:
diff changeset
555 }
kono
parents:
diff changeset
556 if (offset)
kono
parents:
diff changeset
557 {
kono
parents:
diff changeset
558 if (offset >= fmtlen)
kono
parents:
diff changeset
559 return NULL;
kono
parents:
diff changeset
560
kono
parents:
diff changeset
561 fmtstr += offset;
kono
parents:
diff changeset
562 fmtlen -= offset;
kono
parents:
diff changeset
563 }
kono
parents:
diff changeset
564
kono
parents:
diff changeset
565 if (fmtlen < 1 || fmtstr[--fmtlen] != 0)
kono
parents:
diff changeset
566 {
kono
parents:
diff changeset
567 /* FIXME: Diagnose an unterminated format string if it hasn't been
kono
parents:
diff changeset
568 diagnosed by -Wformat. Similarly to a null format pointer,
kono
parents:
diff changeset
569 -Wformay diagnoses only nul pointer constants, this pass can
kono
parents:
diff changeset
570 do better). */
kono
parents:
diff changeset
571 return NULL;
kono
parents:
diff changeset
572 }
kono
parents:
diff changeset
573
kono
parents:
diff changeset
574 return fmtstr;
kono
parents:
diff changeset
575 }
kono
parents:
diff changeset
576
kono
parents:
diff changeset
577 /* The format_warning_at_substring function is not used here in a way
kono
parents:
diff changeset
578 that makes using attribute format viable. Suppress the warning. */
kono
parents:
diff changeset
579
kono
parents:
diff changeset
580 #pragma GCC diagnostic push
kono
parents:
diff changeset
581 #pragma GCC diagnostic ignored "-Wsuggest-attribute=format"
kono
parents:
diff changeset
582
kono
parents:
diff changeset
583 /* For convenience and brevity. */
kono
parents:
diff changeset
584
kono
parents:
diff changeset
585 static bool
kono
parents:
diff changeset
586 (* const fmtwarn) (const substring_loc &, location_t,
kono
parents:
diff changeset
587 const char *, int, const char *, ...)
kono
parents:
diff changeset
588 = format_warning_at_substring;
kono
parents:
diff changeset
589
kono
parents:
diff changeset
590 /* Format length modifiers. */
kono
parents:
diff changeset
591
kono
parents:
diff changeset
592 enum format_lengths
kono
parents:
diff changeset
593 {
kono
parents:
diff changeset
594 FMT_LEN_none,
kono
parents:
diff changeset
595 FMT_LEN_hh, // char argument
kono
parents:
diff changeset
596 FMT_LEN_h, // short
kono
parents:
diff changeset
597 FMT_LEN_l, // long
kono
parents:
diff changeset
598 FMT_LEN_ll, // long long
kono
parents:
diff changeset
599 FMT_LEN_L, // long double (and GNU long long)
kono
parents:
diff changeset
600 FMT_LEN_z, // size_t
kono
parents:
diff changeset
601 FMT_LEN_t, // ptrdiff_t
kono
parents:
diff changeset
602 FMT_LEN_j // intmax_t
kono
parents:
diff changeset
603 };
kono
parents:
diff changeset
604
kono
parents:
diff changeset
605
kono
parents:
diff changeset
606 /* Description of the result of conversion either of a single directive
kono
parents:
diff changeset
607 or the whole format string. */
kono
parents:
diff changeset
608
kono
parents:
diff changeset
609 struct fmtresult
kono
parents:
diff changeset
610 {
kono
parents:
diff changeset
611 /* Construct a FMTRESULT object with all counters initialized
kono
parents:
diff changeset
612 to MIN. KNOWNRANGE is set when MIN is valid. */
kono
parents:
diff changeset
613 fmtresult (unsigned HOST_WIDE_INT min = HOST_WIDE_INT_MAX)
kono
parents:
diff changeset
614 : argmin (), argmax (),
kono
parents:
diff changeset
615 knownrange (min < HOST_WIDE_INT_MAX),
kono
parents:
diff changeset
616 nullp ()
kono
parents:
diff changeset
617 {
kono
parents:
diff changeset
618 range.min = min;
kono
parents:
diff changeset
619 range.max = min;
kono
parents:
diff changeset
620 range.likely = min;
kono
parents:
diff changeset
621 range.unlikely = min;
kono
parents:
diff changeset
622 }
kono
parents:
diff changeset
623
kono
parents:
diff changeset
624 /* Construct a FMTRESULT object with MIN, MAX, and LIKELY counters.
kono
parents:
diff changeset
625 KNOWNRANGE is set when both MIN and MAX are valid. */
kono
parents:
diff changeset
626 fmtresult (unsigned HOST_WIDE_INT min, unsigned HOST_WIDE_INT max,
kono
parents:
diff changeset
627 unsigned HOST_WIDE_INT likely = HOST_WIDE_INT_MAX)
kono
parents:
diff changeset
628 : argmin (), argmax (),
kono
parents:
diff changeset
629 knownrange (min < HOST_WIDE_INT_MAX && max < HOST_WIDE_INT_MAX),
kono
parents:
diff changeset
630 nullp ()
kono
parents:
diff changeset
631 {
kono
parents:
diff changeset
632 range.min = min;
kono
parents:
diff changeset
633 range.max = max;
kono
parents:
diff changeset
634 range.likely = max < likely ? min : likely;
kono
parents:
diff changeset
635 range.unlikely = max;
kono
parents:
diff changeset
636 }
kono
parents:
diff changeset
637
kono
parents:
diff changeset
638 /* Adjust result upward to reflect the RANGE of values the specified
kono
parents:
diff changeset
639 width or precision is known to be in. */
kono
parents:
diff changeset
640 fmtresult& adjust_for_width_or_precision (const HOST_WIDE_INT[2],
kono
parents:
diff changeset
641 tree = NULL_TREE,
kono
parents:
diff changeset
642 unsigned = 0, unsigned = 0);
kono
parents:
diff changeset
643
kono
parents:
diff changeset
644 /* Return the maximum number of decimal digits a value of TYPE
kono
parents:
diff changeset
645 formats as on output. */
kono
parents:
diff changeset
646 static unsigned type_max_digits (tree, int);
kono
parents:
diff changeset
647
kono
parents:
diff changeset
648 /* The range a directive's argument is in. */
kono
parents:
diff changeset
649 tree argmin, argmax;
kono
parents:
diff changeset
650
kono
parents:
diff changeset
651 /* The minimum and maximum number of bytes that a directive
kono
parents:
diff changeset
652 results in on output for an argument in the range above. */
kono
parents:
diff changeset
653 result_range range;
kono
parents:
diff changeset
654
kono
parents:
diff changeset
655 /* True when the range above is obtained from a known value of
kono
parents:
diff changeset
656 a directive's argument or its bounds and not the result of
kono
parents:
diff changeset
657 heuristics that depend on warning levels. */
kono
parents:
diff changeset
658 bool knownrange;
kono
parents:
diff changeset
659
kono
parents:
diff changeset
660 /* True when the argument is a null pointer. */
kono
parents:
diff changeset
661 bool nullp;
kono
parents:
diff changeset
662 };
kono
parents:
diff changeset
663
kono
parents:
diff changeset
664 /* Adjust result upward to reflect the range ADJUST of values the
kono
parents:
diff changeset
665 specified width or precision is known to be in. When non-null,
kono
parents:
diff changeset
666 TYPE denotes the type of the directive whose result is being
kono
parents:
diff changeset
667 adjusted, BASE gives the base of the directive (octal, decimal,
kono
parents:
diff changeset
668 or hex), and ADJ denotes the additional adjustment to the LIKELY
kono
parents:
diff changeset
669 counter that may need to be added when ADJUST is a range. */
kono
parents:
diff changeset
670
kono
parents:
diff changeset
671 fmtresult&
kono
parents:
diff changeset
672 fmtresult::adjust_for_width_or_precision (const HOST_WIDE_INT adjust[2],
kono
parents:
diff changeset
673 tree type /* = NULL_TREE */,
kono
parents:
diff changeset
674 unsigned base /* = 0 */,
kono
parents:
diff changeset
675 unsigned adj /* = 0 */)
kono
parents:
diff changeset
676 {
kono
parents:
diff changeset
677 bool minadjusted = false;
kono
parents:
diff changeset
678
kono
parents:
diff changeset
679 /* Adjust the minimum and likely counters. */
kono
parents:
diff changeset
680 if (adjust[0] >= 0)
kono
parents:
diff changeset
681 {
kono
parents:
diff changeset
682 if (range.min < (unsigned HOST_WIDE_INT)adjust[0])
kono
parents:
diff changeset
683 {
kono
parents:
diff changeset
684 range.min = adjust[0];
kono
parents:
diff changeset
685 minadjusted = true;
kono
parents:
diff changeset
686 }
kono
parents:
diff changeset
687
kono
parents:
diff changeset
688 /* Adjust the likely counter. */
kono
parents:
diff changeset
689 if (range.likely < range.min)
kono
parents:
diff changeset
690 range.likely = range.min;
kono
parents:
diff changeset
691 }
kono
parents:
diff changeset
692 else if (adjust[0] == target_int_min ()
kono
parents:
diff changeset
693 && (unsigned HOST_WIDE_INT)adjust[1] == target_int_max ())
kono
parents:
diff changeset
694 knownrange = false;
kono
parents:
diff changeset
695
kono
parents:
diff changeset
696 /* Adjust the maximum counter. */
kono
parents:
diff changeset
697 if (adjust[1] > 0)
kono
parents:
diff changeset
698 {
kono
parents:
diff changeset
699 if (range.max < (unsigned HOST_WIDE_INT)adjust[1])
kono
parents:
diff changeset
700 {
kono
parents:
diff changeset
701 range.max = adjust[1];
kono
parents:
diff changeset
702
kono
parents:
diff changeset
703 /* Set KNOWNRANGE if both the minimum and maximum have been
kono
parents:
diff changeset
704 adjusted. Otherwise leave it at what it was before. */
kono
parents:
diff changeset
705 knownrange = minadjusted;
kono
parents:
diff changeset
706 }
kono
parents:
diff changeset
707 }
kono
parents:
diff changeset
708
kono
parents:
diff changeset
709 if (warn_level > 1 && type)
kono
parents:
diff changeset
710 {
kono
parents:
diff changeset
711 /* For large non-constant width or precision whose range spans
kono
parents:
diff changeset
712 the maximum number of digits produced by the directive for
kono
parents:
diff changeset
713 any argument, set the likely number of bytes to be at most
kono
parents:
diff changeset
714 the number digits plus other adjustment determined by the
kono
parents:
diff changeset
715 caller (one for sign or two for the hexadecimal "0x"
kono
parents:
diff changeset
716 prefix). */
kono
parents:
diff changeset
717 unsigned dirdigs = type_max_digits (type, base);
kono
parents:
diff changeset
718 if (adjust[0] < dirdigs && dirdigs < adjust[1]
kono
parents:
diff changeset
719 && range.likely < dirdigs)
kono
parents:
diff changeset
720 range.likely = dirdigs + adj;
kono
parents:
diff changeset
721 }
kono
parents:
diff changeset
722 else if (range.likely < (range.min ? range.min : 1))
kono
parents:
diff changeset
723 {
kono
parents:
diff changeset
724 /* Conservatively, set LIKELY to at least MIN but no less than
kono
parents:
diff changeset
725 1 unless MAX is zero. */
kono
parents:
diff changeset
726 range.likely = (range.min
kono
parents:
diff changeset
727 ? range.min
kono
parents:
diff changeset
728 : range.max && (range.max < HOST_WIDE_INT_MAX
kono
parents:
diff changeset
729 || warn_level > 1) ? 1 : 0);
kono
parents:
diff changeset
730 }
kono
parents:
diff changeset
731
kono
parents:
diff changeset
732 /* Finally adjust the unlikely counter to be at least as large as
kono
parents:
diff changeset
733 the maximum. */
kono
parents:
diff changeset
734 if (range.unlikely < range.max)
kono
parents:
diff changeset
735 range.unlikely = range.max;
kono
parents:
diff changeset
736
kono
parents:
diff changeset
737 return *this;
kono
parents:
diff changeset
738 }
kono
parents:
diff changeset
739
kono
parents:
diff changeset
740 /* Return the maximum number of digits a value of TYPE formats in
kono
parents:
diff changeset
741 BASE on output, not counting base prefix . */
kono
parents:
diff changeset
742
kono
parents:
diff changeset
743 unsigned
kono
parents:
diff changeset
744 fmtresult::type_max_digits (tree type, int base)
kono
parents:
diff changeset
745 {
kono
parents:
diff changeset
746 unsigned prec = TYPE_PRECISION (type);
kono
parents:
diff changeset
747 if (base == 8)
kono
parents:
diff changeset
748 return (prec + 2) / 3;
kono
parents:
diff changeset
749
kono
parents:
diff changeset
750 if (base == 16)
kono
parents:
diff changeset
751 return prec / 4;
kono
parents:
diff changeset
752
kono
parents:
diff changeset
753 /* Decimal approximation: yields 3, 5, 10, and 20 for precision
kono
parents:
diff changeset
754 of 8, 16, 32, and 64 bits. */
kono
parents:
diff changeset
755 return prec * 301 / 1000 + 1;
kono
parents:
diff changeset
756 }
kono
parents:
diff changeset
757
kono
parents:
diff changeset
758 static bool
kono
parents:
diff changeset
759 get_int_range (tree, HOST_WIDE_INT *, HOST_WIDE_INT *, bool, HOST_WIDE_INT);
kono
parents:
diff changeset
760
kono
parents:
diff changeset
761 /* Description of a format directive. A directive is either a plain
kono
parents:
diff changeset
762 string or a conversion specification that starts with '%'. */
kono
parents:
diff changeset
763
kono
parents:
diff changeset
764 struct directive
kono
parents:
diff changeset
765 {
kono
parents:
diff changeset
766 /* The 1-based directive number (for debugging). */
kono
parents:
diff changeset
767 unsigned dirno;
kono
parents:
diff changeset
768
kono
parents:
diff changeset
769 /* The first character of the directive and its length. */
kono
parents:
diff changeset
770 const char *beg;
kono
parents:
diff changeset
771 size_t len;
kono
parents:
diff changeset
772
kono
parents:
diff changeset
773 /* A bitmap of flags, one for each character. */
kono
parents:
diff changeset
774 unsigned flags[256 / sizeof (int)];
kono
parents:
diff changeset
775
kono
parents:
diff changeset
776 /* The range of values of the specified width, or -1 if not specified. */
kono
parents:
diff changeset
777 HOST_WIDE_INT width[2];
kono
parents:
diff changeset
778 /* The range of values of the specified precision, or -1 if not
kono
parents:
diff changeset
779 specified. */
kono
parents:
diff changeset
780 HOST_WIDE_INT prec[2];
kono
parents:
diff changeset
781
kono
parents:
diff changeset
782 /* Length modifier. */
kono
parents:
diff changeset
783 format_lengths modifier;
kono
parents:
diff changeset
784
kono
parents:
diff changeset
785 /* Format specifier character. */
kono
parents:
diff changeset
786 char specifier;
kono
parents:
diff changeset
787
kono
parents:
diff changeset
788 /* The argument of the directive or null when the directive doesn't
kono
parents:
diff changeset
789 take one or when none is available (such as for vararg functions). */
kono
parents:
diff changeset
790 tree arg;
kono
parents:
diff changeset
791
kono
parents:
diff changeset
792 /* Format conversion function that given a directive and an argument
kono
parents:
diff changeset
793 returns the formatting result. */
kono
parents:
diff changeset
794 fmtresult (*fmtfunc) (const directive &, tree);
kono
parents:
diff changeset
795
kono
parents:
diff changeset
796 /* Return True when a the format flag CHR has been used. */
kono
parents:
diff changeset
797 bool get_flag (char chr) const
kono
parents:
diff changeset
798 {
kono
parents:
diff changeset
799 unsigned char c = chr & 0xff;
kono
parents:
diff changeset
800 return (flags[c / (CHAR_BIT * sizeof *flags)]
kono
parents:
diff changeset
801 & (1U << (c % (CHAR_BIT * sizeof *flags))));
kono
parents:
diff changeset
802 }
kono
parents:
diff changeset
803
kono
parents:
diff changeset
804 /* Make a record of the format flag CHR having been used. */
kono
parents:
diff changeset
805 void set_flag (char chr)
kono
parents:
diff changeset
806 {
kono
parents:
diff changeset
807 unsigned char c = chr & 0xff;
kono
parents:
diff changeset
808 flags[c / (CHAR_BIT * sizeof *flags)]
kono
parents:
diff changeset
809 |= (1U << (c % (CHAR_BIT * sizeof *flags)));
kono
parents:
diff changeset
810 }
kono
parents:
diff changeset
811
kono
parents:
diff changeset
812 /* Reset the format flag CHR. */
kono
parents:
diff changeset
813 void clear_flag (char chr)
kono
parents:
diff changeset
814 {
kono
parents:
diff changeset
815 unsigned char c = chr & 0xff;
kono
parents:
diff changeset
816 flags[c / (CHAR_BIT * sizeof *flags)]
kono
parents:
diff changeset
817 &= ~(1U << (c % (CHAR_BIT * sizeof *flags)));
kono
parents:
diff changeset
818 }
kono
parents:
diff changeset
819
kono
parents:
diff changeset
820 /* Set both bounds of the width range to VAL. */
kono
parents:
diff changeset
821 void set_width (HOST_WIDE_INT val)
kono
parents:
diff changeset
822 {
kono
parents:
diff changeset
823 width[0] = width[1] = val;
kono
parents:
diff changeset
824 }
kono
parents:
diff changeset
825
kono
parents:
diff changeset
826 /* Set the width range according to ARG, with both bounds being
kono
parents:
diff changeset
827 no less than 0. For a constant ARG set both bounds to its value
kono
parents:
diff changeset
828 or 0, whichever is greater. For a non-constant ARG in some range
kono
parents:
diff changeset
829 set width to its range adjusting each bound to -1 if it's less.
kono
parents:
diff changeset
830 For an indeterminate ARG set width to [0, INT_MAX]. */
kono
parents:
diff changeset
831 void set_width (tree arg)
kono
parents:
diff changeset
832 {
kono
parents:
diff changeset
833 get_int_range (arg, width, width + 1, true, 0);
kono
parents:
diff changeset
834 }
kono
parents:
diff changeset
835
kono
parents:
diff changeset
836 /* Set both bounds of the precision range to VAL. */
kono
parents:
diff changeset
837 void set_precision (HOST_WIDE_INT val)
kono
parents:
diff changeset
838 {
kono
parents:
diff changeset
839 prec[0] = prec[1] = val;
kono
parents:
diff changeset
840 }
kono
parents:
diff changeset
841
kono
parents:
diff changeset
842 /* Set the precision range according to ARG, with both bounds being
kono
parents:
diff changeset
843 no less than -1. For a constant ARG set both bounds to its value
kono
parents:
diff changeset
844 or -1 whichever is greater. For a non-constant ARG in some range
kono
parents:
diff changeset
845 set precision to its range adjusting each bound to -1 if it's less.
kono
parents:
diff changeset
846 For an indeterminate ARG set precision to [-1, INT_MAX]. */
kono
parents:
diff changeset
847 void set_precision (tree arg)
kono
parents:
diff changeset
848 {
kono
parents:
diff changeset
849 get_int_range (arg, prec, prec + 1, false, -1);
kono
parents:
diff changeset
850 }
kono
parents:
diff changeset
851
kono
parents:
diff changeset
852 /* Return true if both width and precision are known to be
kono
parents:
diff changeset
853 either constant or in some range, false otherwise. */
kono
parents:
diff changeset
854 bool known_width_and_precision () const
kono
parents:
diff changeset
855 {
kono
parents:
diff changeset
856 return ((width[1] < 0
kono
parents:
diff changeset
857 || (unsigned HOST_WIDE_INT)width[1] <= target_int_max ())
kono
parents:
diff changeset
858 && (prec[1] < 0
kono
parents:
diff changeset
859 || (unsigned HOST_WIDE_INT)prec[1] < target_int_max ()));
kono
parents:
diff changeset
860 }
kono
parents:
diff changeset
861 };
kono
parents:
diff changeset
862
kono
parents:
diff changeset
863 /* Return the logarithm of X in BASE. */
kono
parents:
diff changeset
864
kono
parents:
diff changeset
865 static int
kono
parents:
diff changeset
866 ilog (unsigned HOST_WIDE_INT x, int base)
kono
parents:
diff changeset
867 {
kono
parents:
diff changeset
868 int res = 0;
kono
parents:
diff changeset
869 do
kono
parents:
diff changeset
870 {
kono
parents:
diff changeset
871 ++res;
kono
parents:
diff changeset
872 x /= base;
kono
parents:
diff changeset
873 } while (x);
kono
parents:
diff changeset
874 return res;
kono
parents:
diff changeset
875 }
kono
parents:
diff changeset
876
kono
parents:
diff changeset
877 /* Return the number of bytes resulting from converting into a string
kono
parents:
diff changeset
878 the INTEGER_CST tree node X in BASE with a minimum of PREC digits.
kono
parents:
diff changeset
879 PLUS indicates whether 1 for a plus sign should be added for positive
kono
parents:
diff changeset
880 numbers, and PREFIX whether the length of an octal ('O') or hexadecimal
kono
parents:
diff changeset
881 ('0x') prefix should be added for nonzero numbers. Return -1 if X cannot
kono
parents:
diff changeset
882 be represented. */
kono
parents:
diff changeset
883
kono
parents:
diff changeset
884 static HOST_WIDE_INT
kono
parents:
diff changeset
885 tree_digits (tree x, int base, HOST_WIDE_INT prec, bool plus, bool prefix)
kono
parents:
diff changeset
886 {
kono
parents:
diff changeset
887 unsigned HOST_WIDE_INT absval;
kono
parents:
diff changeset
888
kono
parents:
diff changeset
889 HOST_WIDE_INT res;
kono
parents:
diff changeset
890
kono
parents:
diff changeset
891 if (TYPE_UNSIGNED (TREE_TYPE (x)))
kono
parents:
diff changeset
892 {
kono
parents:
diff changeset
893 if (tree_fits_uhwi_p (x))
kono
parents:
diff changeset
894 {
kono
parents:
diff changeset
895 absval = tree_to_uhwi (x);
kono
parents:
diff changeset
896 res = plus;
kono
parents:
diff changeset
897 }
kono
parents:
diff changeset
898 else
kono
parents:
diff changeset
899 return -1;
kono
parents:
diff changeset
900 }
kono
parents:
diff changeset
901 else
kono
parents:
diff changeset
902 {
kono
parents:
diff changeset
903 if (tree_fits_shwi_p (x))
kono
parents:
diff changeset
904 {
kono
parents:
diff changeset
905 HOST_WIDE_INT i = tree_to_shwi (x);
kono
parents:
diff changeset
906 if (HOST_WIDE_INT_MIN == i)
kono
parents:
diff changeset
907 {
kono
parents:
diff changeset
908 /* Avoid undefined behavior due to negating a minimum. */
kono
parents:
diff changeset
909 absval = HOST_WIDE_INT_MAX;
kono
parents:
diff changeset
910 res = 1;
kono
parents:
diff changeset
911 }
kono
parents:
diff changeset
912 else if (i < 0)
kono
parents:
diff changeset
913 {
kono
parents:
diff changeset
914 absval = -i;
kono
parents:
diff changeset
915 res = 1;
kono
parents:
diff changeset
916 }
kono
parents:
diff changeset
917 else
kono
parents:
diff changeset
918 {
kono
parents:
diff changeset
919 absval = i;
kono
parents:
diff changeset
920 res = plus;
kono
parents:
diff changeset
921 }
kono
parents:
diff changeset
922 }
kono
parents:
diff changeset
923 else
kono
parents:
diff changeset
924 return -1;
kono
parents:
diff changeset
925 }
kono
parents:
diff changeset
926
kono
parents:
diff changeset
927 int ndigs = ilog (absval, base);
kono
parents:
diff changeset
928
kono
parents:
diff changeset
929 res += prec < ndigs ? ndigs : prec;
kono
parents:
diff changeset
930
kono
parents:
diff changeset
931 /* Adjust a non-zero value for the base prefix, either hexadecimal,
kono
parents:
diff changeset
932 or, unless precision has resulted in a leading zero, also octal. */
kono
parents:
diff changeset
933 if (prefix && absval && (base == 16 || prec <= ndigs))
kono
parents:
diff changeset
934 {
kono
parents:
diff changeset
935 if (base == 8)
kono
parents:
diff changeset
936 res += 1;
kono
parents:
diff changeset
937 else if (base == 16)
kono
parents:
diff changeset
938 res += 2;
kono
parents:
diff changeset
939 }
kono
parents:
diff changeset
940
kono
parents:
diff changeset
941 return res;
kono
parents:
diff changeset
942 }
kono
parents:
diff changeset
943
kono
parents:
diff changeset
944 /* Given the formatting result described by RES and NAVAIL, the number
kono
parents:
diff changeset
945 of available in the destination, return the range of bytes remaining
kono
parents:
diff changeset
946 in the destination. */
kono
parents:
diff changeset
947
kono
parents:
diff changeset
948 static inline result_range
kono
parents:
diff changeset
949 bytes_remaining (unsigned HOST_WIDE_INT navail, const format_result &res)
kono
parents:
diff changeset
950 {
kono
parents:
diff changeset
951 result_range range;
kono
parents:
diff changeset
952
kono
parents:
diff changeset
953 if (HOST_WIDE_INT_MAX <= navail)
kono
parents:
diff changeset
954 {
kono
parents:
diff changeset
955 range.min = range.max = range.likely = range.unlikely = navail;
kono
parents:
diff changeset
956 return range;
kono
parents:
diff changeset
957 }
kono
parents:
diff changeset
958
kono
parents:
diff changeset
959 /* The lower bound of the available range is the available size
kono
parents:
diff changeset
960 minus the maximum output size, and the upper bound is the size
kono
parents:
diff changeset
961 minus the minimum. */
kono
parents:
diff changeset
962 range.max = res.range.min < navail ? navail - res.range.min : 0;
kono
parents:
diff changeset
963
kono
parents:
diff changeset
964 range.likely = res.range.likely < navail ? navail - res.range.likely : 0;
kono
parents:
diff changeset
965
kono
parents:
diff changeset
966 if (res.range.max < HOST_WIDE_INT_MAX)
kono
parents:
diff changeset
967 range.min = res.range.max < navail ? navail - res.range.max : 0;
kono
parents:
diff changeset
968 else
kono
parents:
diff changeset
969 range.min = range.likely;
kono
parents:
diff changeset
970
kono
parents:
diff changeset
971 range.unlikely = (res.range.unlikely < navail
kono
parents:
diff changeset
972 ? navail - res.range.unlikely : 0);
kono
parents:
diff changeset
973
kono
parents:
diff changeset
974 return range;
kono
parents:
diff changeset
975 }
kono
parents:
diff changeset
976
kono
parents:
diff changeset
977 /* Description of a call to a formatted function. */
kono
parents:
diff changeset
978
kono
parents:
diff changeset
979 struct pass_sprintf_length::call_info
kono
parents:
diff changeset
980 {
kono
parents:
diff changeset
981 /* Function call statement. */
kono
parents:
diff changeset
982 gimple *callstmt;
kono
parents:
diff changeset
983
kono
parents:
diff changeset
984 /* Function called. */
kono
parents:
diff changeset
985 tree func;
kono
parents:
diff changeset
986
kono
parents:
diff changeset
987 /* Called built-in function code. */
kono
parents:
diff changeset
988 built_in_function fncode;
kono
parents:
diff changeset
989
kono
parents:
diff changeset
990 /* Format argument and format string extracted from it. */
kono
parents:
diff changeset
991 tree format;
kono
parents:
diff changeset
992 const char *fmtstr;
kono
parents:
diff changeset
993
kono
parents:
diff changeset
994 /* The location of the format argument. */
kono
parents:
diff changeset
995 location_t fmtloc;
kono
parents:
diff changeset
996
kono
parents:
diff changeset
997 /* The destination object size for __builtin___xxx_chk functions
kono
parents:
diff changeset
998 typically determined by __builtin_object_size, or -1 if unknown. */
kono
parents:
diff changeset
999 unsigned HOST_WIDE_INT objsize;
kono
parents:
diff changeset
1000
kono
parents:
diff changeset
1001 /* Number of the first variable argument. */
kono
parents:
diff changeset
1002 unsigned HOST_WIDE_INT argidx;
kono
parents:
diff changeset
1003
kono
parents:
diff changeset
1004 /* True for functions like snprintf that specify the size of
kono
parents:
diff changeset
1005 the destination, false for others like sprintf that don't. */
kono
parents:
diff changeset
1006 bool bounded;
kono
parents:
diff changeset
1007
kono
parents:
diff changeset
1008 /* True for bounded functions like snprintf that specify a zero-size
kono
parents:
diff changeset
1009 buffer as a request to compute the size of output without actually
kono
parents:
diff changeset
1010 writing any. NOWRITE is cleared in response to the %n directive
kono
parents:
diff changeset
1011 which has side-effects similar to writing output. */
kono
parents:
diff changeset
1012 bool nowrite;
kono
parents:
diff changeset
1013
kono
parents:
diff changeset
1014 /* Return true if the called function's return value is used. */
kono
parents:
diff changeset
1015 bool retval_used () const
kono
parents:
diff changeset
1016 {
kono
parents:
diff changeset
1017 return gimple_get_lhs (callstmt);
kono
parents:
diff changeset
1018 }
kono
parents:
diff changeset
1019
kono
parents:
diff changeset
1020 /* Return the warning option corresponding to the called function. */
kono
parents:
diff changeset
1021 int warnopt () const
kono
parents:
diff changeset
1022 {
kono
parents:
diff changeset
1023 return bounded ? OPT_Wformat_truncation_ : OPT_Wformat_overflow_;
kono
parents:
diff changeset
1024 }
kono
parents:
diff changeset
1025 };
kono
parents:
diff changeset
1026
kono
parents:
diff changeset
1027 /* Return the result of formatting a no-op directive (such as '%n'). */
kono
parents:
diff changeset
1028
kono
parents:
diff changeset
1029 static fmtresult
kono
parents:
diff changeset
1030 format_none (const directive &, tree)
kono
parents:
diff changeset
1031 {
kono
parents:
diff changeset
1032 fmtresult res (0);
kono
parents:
diff changeset
1033 return res;
kono
parents:
diff changeset
1034 }
kono
parents:
diff changeset
1035
kono
parents:
diff changeset
1036 /* Return the result of formatting the '%%' directive. */
kono
parents:
diff changeset
1037
kono
parents:
diff changeset
1038 static fmtresult
kono
parents:
diff changeset
1039 format_percent (const directive &, tree)
kono
parents:
diff changeset
1040 {
kono
parents:
diff changeset
1041 fmtresult res (1);
kono
parents:
diff changeset
1042 return res;
kono
parents:
diff changeset
1043 }
kono
parents:
diff changeset
1044
kono
parents:
diff changeset
1045
kono
parents:
diff changeset
1046 /* Compute intmax_type_node and uintmax_type_node similarly to how
kono
parents:
diff changeset
1047 tree.c builds size_type_node. */
kono
parents:
diff changeset
1048
kono
parents:
diff changeset
1049 static void
kono
parents:
diff changeset
1050 build_intmax_type_nodes (tree *pintmax, tree *puintmax)
kono
parents:
diff changeset
1051 {
kono
parents:
diff changeset
1052 if (strcmp (UINTMAX_TYPE, "unsigned int") == 0)
kono
parents:
diff changeset
1053 {
kono
parents:
diff changeset
1054 *pintmax = integer_type_node;
kono
parents:
diff changeset
1055 *puintmax = unsigned_type_node;
kono
parents:
diff changeset
1056 }
kono
parents:
diff changeset
1057 else if (strcmp (UINTMAX_TYPE, "long unsigned int") == 0)
kono
parents:
diff changeset
1058 {
kono
parents:
diff changeset
1059 *pintmax = long_integer_type_node;
kono
parents:
diff changeset
1060 *puintmax = long_unsigned_type_node;
kono
parents:
diff changeset
1061 }
kono
parents:
diff changeset
1062 else if (strcmp (UINTMAX_TYPE, "long long unsigned int") == 0)
kono
parents:
diff changeset
1063 {
kono
parents:
diff changeset
1064 *pintmax = long_long_integer_type_node;
kono
parents:
diff changeset
1065 *puintmax = long_long_unsigned_type_node;
kono
parents:
diff changeset
1066 }
kono
parents:
diff changeset
1067 else
kono
parents:
diff changeset
1068 {
kono
parents:
diff changeset
1069 for (int i = 0; i < NUM_INT_N_ENTS; i++)
kono
parents:
diff changeset
1070 if (int_n_enabled_p[i])
kono
parents:
diff changeset
1071 {
kono
parents:
diff changeset
1072 char name[50];
kono
parents:
diff changeset
1073 sprintf (name, "__int%d unsigned", int_n_data[i].bitsize);
kono
parents:
diff changeset
1074
kono
parents:
diff changeset
1075 if (strcmp (name, UINTMAX_TYPE) == 0)
kono
parents:
diff changeset
1076 {
kono
parents:
diff changeset
1077 *pintmax = int_n_trees[i].signed_type;
kono
parents:
diff changeset
1078 *puintmax = int_n_trees[i].unsigned_type;
kono
parents:
diff changeset
1079 return;
kono
parents:
diff changeset
1080 }
kono
parents:
diff changeset
1081 }
kono
parents:
diff changeset
1082 gcc_unreachable ();
kono
parents:
diff changeset
1083 }
kono
parents:
diff changeset
1084 }
kono
parents:
diff changeset
1085
kono
parents:
diff changeset
1086 /* Determine the range [*PMIN, *PMAX] that the expression ARG is
kono
parents:
diff changeset
1087 in and that is representable in type int.
kono
parents:
diff changeset
1088 Return true when the range is a subrange of that of int.
kono
parents:
diff changeset
1089 When ARG is null it is as if it had the full range of int.
kono
parents:
diff changeset
1090 When ABSOLUTE is true the range reflects the absolute value of
kono
parents:
diff changeset
1091 the argument. When ABSOLUTE is false, negative bounds of
kono
parents:
diff changeset
1092 the determined range are replaced with NEGBOUND. */
kono
parents:
diff changeset
1093
kono
parents:
diff changeset
1094 static bool
kono
parents:
diff changeset
1095 get_int_range (tree arg, HOST_WIDE_INT *pmin, HOST_WIDE_INT *pmax,
kono
parents:
diff changeset
1096 bool absolute, HOST_WIDE_INT negbound)
kono
parents:
diff changeset
1097 {
kono
parents:
diff changeset
1098 /* The type of the result. */
kono
parents:
diff changeset
1099 const_tree type = integer_type_node;
kono
parents:
diff changeset
1100
kono
parents:
diff changeset
1101 bool knownrange = false;
kono
parents:
diff changeset
1102
kono
parents:
diff changeset
1103 if (!arg)
kono
parents:
diff changeset
1104 {
kono
parents:
diff changeset
1105 *pmin = tree_to_shwi (TYPE_MIN_VALUE (type));
kono
parents:
diff changeset
1106 *pmax = tree_to_shwi (TYPE_MAX_VALUE (type));
kono
parents:
diff changeset
1107 }
kono
parents:
diff changeset
1108 else if (TREE_CODE (arg) == INTEGER_CST
kono
parents:
diff changeset
1109 && TYPE_PRECISION (TREE_TYPE (arg)) <= TYPE_PRECISION (type))
kono
parents:
diff changeset
1110 {
kono
parents:
diff changeset
1111 /* For a constant argument return its value adjusted as specified
kono
parents:
diff changeset
1112 by NEGATIVE and NEGBOUND and return true to indicate that the
kono
parents:
diff changeset
1113 result is known. */
kono
parents:
diff changeset
1114 *pmin = tree_fits_shwi_p (arg) ? tree_to_shwi (arg) : tree_to_uhwi (arg);
kono
parents:
diff changeset
1115 *pmax = *pmin;
kono
parents:
diff changeset
1116 knownrange = true;
kono
parents:
diff changeset
1117 }
kono
parents:
diff changeset
1118 else
kono
parents:
diff changeset
1119 {
kono
parents:
diff changeset
1120 /* True if the argument's range cannot be determined. */
kono
parents:
diff changeset
1121 bool unknown = true;
kono
parents:
diff changeset
1122
kono
parents:
diff changeset
1123 tree argtype = TREE_TYPE (arg);
kono
parents:
diff changeset
1124
kono
parents:
diff changeset
1125 /* Ignore invalid arguments with greater precision that that
kono
parents:
diff changeset
1126 of the expected type (e.g., in sprintf("%*i", 12LL, i)).
kono
parents:
diff changeset
1127 They will have been detected and diagnosed by -Wformat and
kono
parents:
diff changeset
1128 so it's not important to complicate this code to try to deal
kono
parents:
diff changeset
1129 with them again. */
kono
parents:
diff changeset
1130 if (TREE_CODE (arg) == SSA_NAME
kono
parents:
diff changeset
1131 && INTEGRAL_TYPE_P (argtype)
kono
parents:
diff changeset
1132 && TYPE_PRECISION (argtype) <= TYPE_PRECISION (type))
kono
parents:
diff changeset
1133 {
kono
parents:
diff changeset
1134 /* Try to determine the range of values of the integer argument. */
kono
parents:
diff changeset
1135 wide_int min, max;
kono
parents:
diff changeset
1136 enum value_range_type range_type = get_range_info (arg, &min, &max);
kono
parents:
diff changeset
1137 if (range_type == VR_RANGE)
kono
parents:
diff changeset
1138 {
kono
parents:
diff changeset
1139 HOST_WIDE_INT type_min
kono
parents:
diff changeset
1140 = (TYPE_UNSIGNED (argtype)
kono
parents:
diff changeset
1141 ? tree_to_uhwi (TYPE_MIN_VALUE (argtype))
kono
parents:
diff changeset
1142 : tree_to_shwi (TYPE_MIN_VALUE (argtype)));
kono
parents:
diff changeset
1143
kono
parents:
diff changeset
1144 HOST_WIDE_INT type_max = tree_to_uhwi (TYPE_MAX_VALUE (argtype));
kono
parents:
diff changeset
1145
kono
parents:
diff changeset
1146 *pmin = min.to_shwi ();
kono
parents:
diff changeset
1147 *pmax = max.to_shwi ();
kono
parents:
diff changeset
1148
kono
parents:
diff changeset
1149 if (*pmin < *pmax)
kono
parents:
diff changeset
1150 {
kono
parents:
diff changeset
1151 /* Return true if the adjusted range is a subrange of
kono
parents:
diff changeset
1152 the full range of the argument's type. *PMAX may
kono
parents:
diff changeset
1153 be less than *PMIN when the argument is unsigned
kono
parents:
diff changeset
1154 and its upper bound is in excess of TYPE_MAX. In
kono
parents:
diff changeset
1155 that (invalid) case disregard the range and use that
kono
parents:
diff changeset
1156 of the expected type instead. */
kono
parents:
diff changeset
1157 knownrange = type_min < *pmin || *pmax < type_max;
kono
parents:
diff changeset
1158
kono
parents:
diff changeset
1159 unknown = false;
kono
parents:
diff changeset
1160 }
kono
parents:
diff changeset
1161 }
kono
parents:
diff changeset
1162 }
kono
parents:
diff changeset
1163
kono
parents:
diff changeset
1164 /* Handle an argument with an unknown range as if none had been
kono
parents:
diff changeset
1165 provided. */
kono
parents:
diff changeset
1166 if (unknown)
kono
parents:
diff changeset
1167 return get_int_range (NULL_TREE, pmin, pmax, absolute, negbound);
kono
parents:
diff changeset
1168 }
kono
parents:
diff changeset
1169
kono
parents:
diff changeset
1170 /* Adjust each bound as specified by ABSOLUTE and NEGBOUND. */
kono
parents:
diff changeset
1171 if (absolute)
kono
parents:
diff changeset
1172 {
kono
parents:
diff changeset
1173 if (*pmin < 0)
kono
parents:
diff changeset
1174 {
kono
parents:
diff changeset
1175 if (*pmin == *pmax)
kono
parents:
diff changeset
1176 *pmin = *pmax = -*pmin;
kono
parents:
diff changeset
1177 else
kono
parents:
diff changeset
1178 {
kono
parents:
diff changeset
1179 /* Make sure signed overlow is avoided. */
kono
parents:
diff changeset
1180 gcc_assert (*pmin != HOST_WIDE_INT_MIN);
kono
parents:
diff changeset
1181
kono
parents:
diff changeset
1182 HOST_WIDE_INT tmp = -*pmin;
kono
parents:
diff changeset
1183 *pmin = 0;
kono
parents:
diff changeset
1184 if (*pmax < tmp)
kono
parents:
diff changeset
1185 *pmax = tmp;
kono
parents:
diff changeset
1186 }
kono
parents:
diff changeset
1187 }
kono
parents:
diff changeset
1188 }
kono
parents:
diff changeset
1189 else if (*pmin < negbound)
kono
parents:
diff changeset
1190 *pmin = negbound;
kono
parents:
diff changeset
1191
kono
parents:
diff changeset
1192 return knownrange;
kono
parents:
diff changeset
1193 }
kono
parents:
diff changeset
1194
kono
parents:
diff changeset
1195 /* With the range [*ARGMIN, *ARGMAX] of an integer directive's actual
kono
parents:
diff changeset
1196 argument, due to the conversion from either *ARGMIN or *ARGMAX to
kono
parents:
diff changeset
1197 the type of the directive's formal argument it's possible for both
kono
parents:
diff changeset
1198 to result in the same number of bytes or a range of bytes that's
kono
parents:
diff changeset
1199 less than the number of bytes that would result from formatting
kono
parents:
diff changeset
1200 some other value in the range [*ARGMIN, *ARGMAX]. This can be
kono
parents:
diff changeset
1201 determined by checking for the actual argument being in the range
kono
parents:
diff changeset
1202 of the type of the directive. If it isn't it must be assumed to
kono
parents:
diff changeset
1203 take on the full range of the directive's type.
kono
parents:
diff changeset
1204 Return true when the range has been adjusted to the full range
kono
parents:
diff changeset
1205 of DIRTYPE, and false otherwise. */
kono
parents:
diff changeset
1206
kono
parents:
diff changeset
1207 static bool
kono
parents:
diff changeset
1208 adjust_range_for_overflow (tree dirtype, tree *argmin, tree *argmax)
kono
parents:
diff changeset
1209 {
kono
parents:
diff changeset
1210 tree argtype = TREE_TYPE (*argmin);
kono
parents:
diff changeset
1211 unsigned argprec = TYPE_PRECISION (argtype);
kono
parents:
diff changeset
1212 unsigned dirprec = TYPE_PRECISION (dirtype);
kono
parents:
diff changeset
1213
kono
parents:
diff changeset
1214 /* If the actual argument and the directive's argument have the same
kono
parents:
diff changeset
1215 precision and sign there can be no overflow and so there is nothing
kono
parents:
diff changeset
1216 to adjust. */
kono
parents:
diff changeset
1217 if (argprec == dirprec && TYPE_SIGN (argtype) == TYPE_SIGN (dirtype))
kono
parents:
diff changeset
1218 return false;
kono
parents:
diff changeset
1219
kono
parents:
diff changeset
1220 /* The logic below was inspired/lifted from the CONVERT_EXPR_CODE_P
kono
parents:
diff changeset
1221 branch in the extract_range_from_unary_expr function in tree-vrp.c. */
kono
parents:
diff changeset
1222
kono
parents:
diff changeset
1223 if (TREE_CODE (*argmin) == INTEGER_CST
kono
parents:
diff changeset
1224 && TREE_CODE (*argmax) == INTEGER_CST
kono
parents:
diff changeset
1225 && (dirprec >= argprec
kono
parents:
diff changeset
1226 || integer_zerop (int_const_binop (RSHIFT_EXPR,
kono
parents:
diff changeset
1227 int_const_binop (MINUS_EXPR,
kono
parents:
diff changeset
1228 *argmax,
kono
parents:
diff changeset
1229 *argmin),
kono
parents:
diff changeset
1230 size_int (dirprec)))))
kono
parents:
diff changeset
1231 {
kono
parents:
diff changeset
1232 *argmin = force_fit_type (dirtype, wi::to_widest (*argmin), 0, false);
kono
parents:
diff changeset
1233 *argmax = force_fit_type (dirtype, wi::to_widest (*argmax), 0, false);
kono
parents:
diff changeset
1234
kono
parents:
diff changeset
1235 /* If *ARGMIN is still less than *ARGMAX the conversion above
kono
parents:
diff changeset
1236 is safe. Otherwise, it has overflowed and would be unsafe. */
kono
parents:
diff changeset
1237 if (tree_int_cst_le (*argmin, *argmax))
kono
parents:
diff changeset
1238 return false;
kono
parents:
diff changeset
1239 }
kono
parents:
diff changeset
1240
kono
parents:
diff changeset
1241 *argmin = TYPE_MIN_VALUE (dirtype);
kono
parents:
diff changeset
1242 *argmax = TYPE_MAX_VALUE (dirtype);
kono
parents:
diff changeset
1243 return true;
kono
parents:
diff changeset
1244 }
kono
parents:
diff changeset
1245
kono
parents:
diff changeset
1246 /* Return a range representing the minimum and maximum number of bytes
kono
parents:
diff changeset
1247 that the format directive DIR will output for any argument given
kono
parents:
diff changeset
1248 the WIDTH and PRECISION (extracted from DIR). This function is
kono
parents:
diff changeset
1249 used when the directive argument or its value isn't known. */
kono
parents:
diff changeset
1250
kono
parents:
diff changeset
1251 static fmtresult
kono
parents:
diff changeset
1252 format_integer (const directive &dir, tree arg)
kono
parents:
diff changeset
1253 {
kono
parents:
diff changeset
1254 tree intmax_type_node;
kono
parents:
diff changeset
1255 tree uintmax_type_node;
kono
parents:
diff changeset
1256
kono
parents:
diff changeset
1257 /* Base to format the number in. */
kono
parents:
diff changeset
1258 int base;
kono
parents:
diff changeset
1259
kono
parents:
diff changeset
1260 /* True when a conversion is preceded by a prefix indicating the base
kono
parents:
diff changeset
1261 of the argument (octal or hexadecimal). */
kono
parents:
diff changeset
1262 bool maybebase = dir.get_flag ('#');
kono
parents:
diff changeset
1263
kono
parents:
diff changeset
1264 /* True when a signed conversion is preceded by a sign or space. */
kono
parents:
diff changeset
1265 bool maybesign = false;
kono
parents:
diff changeset
1266
kono
parents:
diff changeset
1267 /* True for signed conversions (i.e., 'd' and 'i'). */
kono
parents:
diff changeset
1268 bool sign = false;
kono
parents:
diff changeset
1269
kono
parents:
diff changeset
1270 switch (dir.specifier)
kono
parents:
diff changeset
1271 {
kono
parents:
diff changeset
1272 case 'd':
kono
parents:
diff changeset
1273 case 'i':
kono
parents:
diff changeset
1274 /* Space and '+' are only meaningful for signed conversions. */
kono
parents:
diff changeset
1275 maybesign = dir.get_flag (' ') | dir.get_flag ('+');
kono
parents:
diff changeset
1276 sign = true;
kono
parents:
diff changeset
1277 base = 10;
kono
parents:
diff changeset
1278 break;
kono
parents:
diff changeset
1279 case 'u':
kono
parents:
diff changeset
1280 base = 10;
kono
parents:
diff changeset
1281 break;
kono
parents:
diff changeset
1282 case 'o':
kono
parents:
diff changeset
1283 base = 8;
kono
parents:
diff changeset
1284 break;
kono
parents:
diff changeset
1285 case 'X':
kono
parents:
diff changeset
1286 case 'x':
kono
parents:
diff changeset
1287 base = 16;
kono
parents:
diff changeset
1288 break;
kono
parents:
diff changeset
1289 default:
kono
parents:
diff changeset
1290 gcc_unreachable ();
kono
parents:
diff changeset
1291 }
kono
parents:
diff changeset
1292
kono
parents:
diff changeset
1293 /* The type of the "formal" argument expected by the directive. */
kono
parents:
diff changeset
1294 tree dirtype = NULL_TREE;
kono
parents:
diff changeset
1295
kono
parents:
diff changeset
1296 /* Determine the expected type of the argument from the length
kono
parents:
diff changeset
1297 modifier. */
kono
parents:
diff changeset
1298 switch (dir.modifier)
kono
parents:
diff changeset
1299 {
kono
parents:
diff changeset
1300 case FMT_LEN_none:
kono
parents:
diff changeset
1301 if (dir.specifier == 'p')
kono
parents:
diff changeset
1302 dirtype = ptr_type_node;
kono
parents:
diff changeset
1303 else
kono
parents:
diff changeset
1304 dirtype = sign ? integer_type_node : unsigned_type_node;
kono
parents:
diff changeset
1305 break;
kono
parents:
diff changeset
1306
kono
parents:
diff changeset
1307 case FMT_LEN_h:
kono
parents:
diff changeset
1308 dirtype = sign ? short_integer_type_node : short_unsigned_type_node;
kono
parents:
diff changeset
1309 break;
kono
parents:
diff changeset
1310
kono
parents:
diff changeset
1311 case FMT_LEN_hh:
kono
parents:
diff changeset
1312 dirtype = sign ? signed_char_type_node : unsigned_char_type_node;
kono
parents:
diff changeset
1313 break;
kono
parents:
diff changeset
1314
kono
parents:
diff changeset
1315 case FMT_LEN_l:
kono
parents:
diff changeset
1316 dirtype = sign ? long_integer_type_node : long_unsigned_type_node;
kono
parents:
diff changeset
1317 break;
kono
parents:
diff changeset
1318
kono
parents:
diff changeset
1319 case FMT_LEN_L:
kono
parents:
diff changeset
1320 case FMT_LEN_ll:
kono
parents:
diff changeset
1321 dirtype = (sign
kono
parents:
diff changeset
1322 ? long_long_integer_type_node
kono
parents:
diff changeset
1323 : long_long_unsigned_type_node);
kono
parents:
diff changeset
1324 break;
kono
parents:
diff changeset
1325
kono
parents:
diff changeset
1326 case FMT_LEN_z:
kono
parents:
diff changeset
1327 dirtype = signed_or_unsigned_type_for (!sign, size_type_node);
kono
parents:
diff changeset
1328 break;
kono
parents:
diff changeset
1329
kono
parents:
diff changeset
1330 case FMT_LEN_t:
kono
parents:
diff changeset
1331 dirtype = signed_or_unsigned_type_for (!sign, ptrdiff_type_node);
kono
parents:
diff changeset
1332 break;
kono
parents:
diff changeset
1333
kono
parents:
diff changeset
1334 case FMT_LEN_j:
kono
parents:
diff changeset
1335 build_intmax_type_nodes (&intmax_type_node, &uintmax_type_node);
kono
parents:
diff changeset
1336 dirtype = sign ? intmax_type_node : uintmax_type_node;
kono
parents:
diff changeset
1337 break;
kono
parents:
diff changeset
1338
kono
parents:
diff changeset
1339 default:
kono
parents:
diff changeset
1340 return fmtresult ();
kono
parents:
diff changeset
1341 }
kono
parents:
diff changeset
1342
kono
parents:
diff changeset
1343 /* The type of the argument to the directive, either deduced from
kono
parents:
diff changeset
1344 the actual non-constant argument if one is known, or from
kono
parents:
diff changeset
1345 the directive itself when none has been provided because it's
kono
parents:
diff changeset
1346 a va_list. */
kono
parents:
diff changeset
1347 tree argtype = NULL_TREE;
kono
parents:
diff changeset
1348
kono
parents:
diff changeset
1349 if (!arg)
kono
parents:
diff changeset
1350 {
kono
parents:
diff changeset
1351 /* When the argument has not been provided, use the type of
kono
parents:
diff changeset
1352 the directive's argument as an approximation. This will
kono
parents:
diff changeset
1353 result in false positives for directives like %i with
kono
parents:
diff changeset
1354 arguments with smaller precision (such as short or char). */
kono
parents:
diff changeset
1355 argtype = dirtype;
kono
parents:
diff changeset
1356 }
kono
parents:
diff changeset
1357 else if (TREE_CODE (arg) == INTEGER_CST)
kono
parents:
diff changeset
1358 {
kono
parents:
diff changeset
1359 /* When a constant argument has been provided use its value
kono
parents:
diff changeset
1360 rather than type to determine the length of the output. */
kono
parents:
diff changeset
1361 fmtresult res;
kono
parents:
diff changeset
1362
kono
parents:
diff changeset
1363 if ((dir.prec[0] <= 0 && dir.prec[1] >= 0) && integer_zerop (arg))
kono
parents:
diff changeset
1364 {
kono
parents:
diff changeset
1365 /* As a special case, a precision of zero with a zero argument
kono
parents:
diff changeset
1366 results in zero bytes except in base 8 when the '#' flag is
kono
parents:
diff changeset
1367 specified, and for signed conversions in base 8 and 10 when
kono
parents:
diff changeset
1368 either the space or '+' flag has been specified and it results
kono
parents:
diff changeset
1369 in just one byte (with width having the normal effect). This
kono
parents:
diff changeset
1370 must extend to the case of a specified precision with
kono
parents:
diff changeset
1371 an unknown value because it can be zero. */
kono
parents:
diff changeset
1372 res.range.min = ((base == 8 && dir.get_flag ('#')) || maybesign);
kono
parents:
diff changeset
1373 if (res.range.min == 0 && dir.prec[0] != dir.prec[1])
kono
parents:
diff changeset
1374 {
kono
parents:
diff changeset
1375 res.range.max = 1;
kono
parents:
diff changeset
1376 res.range.likely = 1;
kono
parents:
diff changeset
1377 }
kono
parents:
diff changeset
1378 else
kono
parents:
diff changeset
1379 {
kono
parents:
diff changeset
1380 res.range.max = res.range.min;
kono
parents:
diff changeset
1381 res.range.likely = res.range.min;
kono
parents:
diff changeset
1382 }
kono
parents:
diff changeset
1383 }
kono
parents:
diff changeset
1384 else
kono
parents:
diff changeset
1385 {
kono
parents:
diff changeset
1386 /* Convert the argument to the type of the directive. */
kono
parents:
diff changeset
1387 arg = fold_convert (dirtype, arg);
kono
parents:
diff changeset
1388
kono
parents:
diff changeset
1389 res.range.min = tree_digits (arg, base, dir.prec[0],
kono
parents:
diff changeset
1390 maybesign, maybebase);
kono
parents:
diff changeset
1391 if (dir.prec[0] == dir.prec[1])
kono
parents:
diff changeset
1392 res.range.max = res.range.min;
kono
parents:
diff changeset
1393 else
kono
parents:
diff changeset
1394 res.range.max = tree_digits (arg, base, dir.prec[1],
kono
parents:
diff changeset
1395 maybesign, maybebase);
kono
parents:
diff changeset
1396 res.range.likely = res.range.min;
kono
parents:
diff changeset
1397 res.knownrange = true;
kono
parents:
diff changeset
1398 }
kono
parents:
diff changeset
1399
kono
parents:
diff changeset
1400 res.range.unlikely = res.range.max;
kono
parents:
diff changeset
1401
kono
parents:
diff changeset
1402 /* Bump up the counters if WIDTH is greater than LEN. */
kono
parents:
diff changeset
1403 res.adjust_for_width_or_precision (dir.width, dirtype, base,
kono
parents:
diff changeset
1404 (sign | maybebase) + (base == 16));
kono
parents:
diff changeset
1405 /* Bump up the counters again if PRECision is greater still. */
kono
parents:
diff changeset
1406 res.adjust_for_width_or_precision (dir.prec, dirtype, base,
kono
parents:
diff changeset
1407 (sign | maybebase) + (base == 16));
kono
parents:
diff changeset
1408
kono
parents:
diff changeset
1409 return res;
kono
parents:
diff changeset
1410 }
kono
parents:
diff changeset
1411 else if (INTEGRAL_TYPE_P (TREE_TYPE (arg))
kono
parents:
diff changeset
1412 || TREE_CODE (TREE_TYPE (arg)) == POINTER_TYPE)
kono
parents:
diff changeset
1413 /* Determine the type of the provided non-constant argument. */
kono
parents:
diff changeset
1414 argtype = TREE_TYPE (arg);
kono
parents:
diff changeset
1415 else
kono
parents:
diff changeset
1416 /* Don't bother with invalid arguments since they likely would
kono
parents:
diff changeset
1417 have already been diagnosed, and disable any further checking
kono
parents:
diff changeset
1418 of the format string by returning [-1, -1]. */
kono
parents:
diff changeset
1419 return fmtresult ();
kono
parents:
diff changeset
1420
kono
parents:
diff changeset
1421 fmtresult res;
kono
parents:
diff changeset
1422
kono
parents:
diff changeset
1423 /* Using either the range the non-constant argument is in, or its
kono
parents:
diff changeset
1424 type (either "formal" or actual), create a range of values that
kono
parents:
diff changeset
1425 constrain the length of output given the warning level. */
kono
parents:
diff changeset
1426 tree argmin = NULL_TREE;
kono
parents:
diff changeset
1427 tree argmax = NULL_TREE;
kono
parents:
diff changeset
1428
kono
parents:
diff changeset
1429 if (arg
kono
parents:
diff changeset
1430 && TREE_CODE (arg) == SSA_NAME
kono
parents:
diff changeset
1431 && INTEGRAL_TYPE_P (argtype))
kono
parents:
diff changeset
1432 {
kono
parents:
diff changeset
1433 /* Try to determine the range of values of the integer argument
kono
parents:
diff changeset
1434 (range information is not available for pointers). */
kono
parents:
diff changeset
1435 wide_int min, max;
kono
parents:
diff changeset
1436 enum value_range_type range_type = get_range_info (arg, &min, &max);
kono
parents:
diff changeset
1437 if (range_type == VR_RANGE)
kono
parents:
diff changeset
1438 {
kono
parents:
diff changeset
1439 argmin = wide_int_to_tree (argtype, min);
kono
parents:
diff changeset
1440 argmax = wide_int_to_tree (argtype, max);
kono
parents:
diff changeset
1441
kono
parents:
diff changeset
1442 /* Set KNOWNRANGE if the argument is in a known subrange
kono
parents:
diff changeset
1443 of the directive's type and neither width nor precision
kono
parents:
diff changeset
1444 is unknown. (KNOWNRANGE may be reset below). */
kono
parents:
diff changeset
1445 res.knownrange
kono
parents:
diff changeset
1446 = ((!tree_int_cst_equal (TYPE_MIN_VALUE (dirtype), argmin)
kono
parents:
diff changeset
1447 || !tree_int_cst_equal (TYPE_MAX_VALUE (dirtype), argmax))
kono
parents:
diff changeset
1448 && dir.known_width_and_precision ());
kono
parents:
diff changeset
1449
kono
parents:
diff changeset
1450 res.argmin = argmin;
kono
parents:
diff changeset
1451 res.argmax = argmax;
kono
parents:
diff changeset
1452 }
kono
parents:
diff changeset
1453 else if (range_type == VR_ANTI_RANGE)
kono
parents:
diff changeset
1454 {
kono
parents:
diff changeset
1455 /* Handle anti-ranges if/when bug 71690 is resolved. */
kono
parents:
diff changeset
1456 }
kono
parents:
diff changeset
1457 else if (range_type == VR_VARYING)
kono
parents:
diff changeset
1458 {
kono
parents:
diff changeset
1459 /* The argument here may be the result of promoting the actual
kono
parents:
diff changeset
1460 argument to int. Try to determine the type of the actual
kono
parents:
diff changeset
1461 argument before promotion and narrow down its range that
kono
parents:
diff changeset
1462 way. */
kono
parents:
diff changeset
1463 gimple *def = SSA_NAME_DEF_STMT (arg);
kono
parents:
diff changeset
1464 if (is_gimple_assign (def))
kono
parents:
diff changeset
1465 {
kono
parents:
diff changeset
1466 tree_code code = gimple_assign_rhs_code (def);
kono
parents:
diff changeset
1467 if (code == INTEGER_CST)
kono
parents:
diff changeset
1468 {
kono
parents:
diff changeset
1469 arg = gimple_assign_rhs1 (def);
kono
parents:
diff changeset
1470 return format_integer (dir, arg);
kono
parents:
diff changeset
1471 }
kono
parents:
diff changeset
1472
kono
parents:
diff changeset
1473 if (code == NOP_EXPR)
kono
parents:
diff changeset
1474 {
kono
parents:
diff changeset
1475 tree type = TREE_TYPE (gimple_assign_rhs1 (def));
kono
parents:
diff changeset
1476 if (INTEGRAL_TYPE_P (type)
kono
parents:
diff changeset
1477 || TREE_CODE (type) == POINTER_TYPE)
kono
parents:
diff changeset
1478 argtype = type;
kono
parents:
diff changeset
1479 }
kono
parents:
diff changeset
1480 }
kono
parents:
diff changeset
1481 }
kono
parents:
diff changeset
1482 }
kono
parents:
diff changeset
1483
kono
parents:
diff changeset
1484 if (!argmin)
kono
parents:
diff changeset
1485 {
kono
parents:
diff changeset
1486 if (TREE_CODE (argtype) == POINTER_TYPE)
kono
parents:
diff changeset
1487 {
kono
parents:
diff changeset
1488 argmin = build_int_cst (pointer_sized_int_node, 0);
kono
parents:
diff changeset
1489 argmax = build_all_ones_cst (pointer_sized_int_node);
kono
parents:
diff changeset
1490 }
kono
parents:
diff changeset
1491 else
kono
parents:
diff changeset
1492 {
kono
parents:
diff changeset
1493 argmin = TYPE_MIN_VALUE (argtype);
kono
parents:
diff changeset
1494 argmax = TYPE_MAX_VALUE (argtype);
kono
parents:
diff changeset
1495 }
kono
parents:
diff changeset
1496 }
kono
parents:
diff changeset
1497
kono
parents:
diff changeset
1498 /* Clear KNOWNRANGE if the range has been adjusted to the maximum
kono
parents:
diff changeset
1499 of the directive. If it has been cleared then since ARGMIN and/or
kono
parents:
diff changeset
1500 ARGMAX have been adjusted also adjust the corresponding ARGMIN and
kono
parents:
diff changeset
1501 ARGMAX in the result to include in diagnostics. */
kono
parents:
diff changeset
1502 if (adjust_range_for_overflow (dirtype, &argmin, &argmax))
kono
parents:
diff changeset
1503 {
kono
parents:
diff changeset
1504 res.knownrange = false;
kono
parents:
diff changeset
1505 res.argmin = argmin;
kono
parents:
diff changeset
1506 res.argmax = argmax;
kono
parents:
diff changeset
1507 }
kono
parents:
diff changeset
1508
kono
parents:
diff changeset
1509 /* Recursively compute the minimum and maximum from the known range. */
kono
parents:
diff changeset
1510 if (TYPE_UNSIGNED (dirtype) || tree_int_cst_sgn (argmin) >= 0)
kono
parents:
diff changeset
1511 {
kono
parents:
diff changeset
1512 /* For unsigned conversions/directives or signed when
kono
parents:
diff changeset
1513 the minimum is positive, use the minimum and maximum to compute
kono
parents:
diff changeset
1514 the shortest and longest output, respectively. */
kono
parents:
diff changeset
1515 res.range.min = format_integer (dir, argmin).range.min;
kono
parents:
diff changeset
1516 res.range.max = format_integer (dir, argmax).range.max;
kono
parents:
diff changeset
1517 }
kono
parents:
diff changeset
1518 else if (tree_int_cst_sgn (argmax) < 0)
kono
parents:
diff changeset
1519 {
kono
parents:
diff changeset
1520 /* For signed conversions/directives if maximum is negative,
kono
parents:
diff changeset
1521 use the minimum as the longest output and maximum as the
kono
parents:
diff changeset
1522 shortest output. */
kono
parents:
diff changeset
1523 res.range.min = format_integer (dir, argmax).range.min;
kono
parents:
diff changeset
1524 res.range.max = format_integer (dir, argmin).range.max;
kono
parents:
diff changeset
1525 }
kono
parents:
diff changeset
1526 else
kono
parents:
diff changeset
1527 {
kono
parents:
diff changeset
1528 /* Otherwise, 0 is inside of the range and minimum negative. Use 0
kono
parents:
diff changeset
1529 as the shortest output and for the longest output compute the
kono
parents:
diff changeset
1530 length of the output of both minimum and maximum and pick the
kono
parents:
diff changeset
1531 longer. */
kono
parents:
diff changeset
1532 unsigned HOST_WIDE_INT max1 = format_integer (dir, argmin).range.max;
kono
parents:
diff changeset
1533 unsigned HOST_WIDE_INT max2 = format_integer (dir, argmax).range.max;
kono
parents:
diff changeset
1534 res.range.min = format_integer (dir, integer_zero_node).range.min;
kono
parents:
diff changeset
1535 res.range.max = MAX (max1, max2);
kono
parents:
diff changeset
1536 }
kono
parents:
diff changeset
1537
kono
parents:
diff changeset
1538 /* If the range is known, use the maximum as the likely length. */
kono
parents:
diff changeset
1539 if (res.knownrange)
kono
parents:
diff changeset
1540 res.range.likely = res.range.max;
kono
parents:
diff changeset
1541 else
kono
parents:
diff changeset
1542 {
kono
parents:
diff changeset
1543 /* Otherwise, use the minimum. Except for the case where for %#x or
kono
parents:
diff changeset
1544 %#o the minimum is just for a single value in the range (0) and
kono
parents:
diff changeset
1545 for all other values it is something longer, like 0x1 or 01.
kono
parents:
diff changeset
1546 Use the length for value 1 in that case instead as the likely
kono
parents:
diff changeset
1547 length. */
kono
parents:
diff changeset
1548 res.range.likely = res.range.min;
kono
parents:
diff changeset
1549 if (maybebase
kono
parents:
diff changeset
1550 && base != 10
kono
parents:
diff changeset
1551 && (tree_int_cst_sgn (argmin) < 0 || tree_int_cst_sgn (argmax) > 0))
kono
parents:
diff changeset
1552 {
kono
parents:
diff changeset
1553 if (res.range.min == 1)
kono
parents:
diff changeset
1554 res.range.likely += base == 8 ? 1 : 2;
kono
parents:
diff changeset
1555 else if (res.range.min == 2
kono
parents:
diff changeset
1556 && base == 16
kono
parents:
diff changeset
1557 && (dir.width[0] == 2 || dir.prec[0] == 2))
kono
parents:
diff changeset
1558 ++res.range.likely;
kono
parents:
diff changeset
1559 }
kono
parents:
diff changeset
1560 }
kono
parents:
diff changeset
1561
kono
parents:
diff changeset
1562 res.range.unlikely = res.range.max;
kono
parents:
diff changeset
1563 res.adjust_for_width_or_precision (dir.width, dirtype, base,
kono
parents:
diff changeset
1564 (sign | maybebase) + (base == 16));
kono
parents:
diff changeset
1565 res.adjust_for_width_or_precision (dir.prec, dirtype, base,
kono
parents:
diff changeset
1566 (sign | maybebase) + (base == 16));
kono
parents:
diff changeset
1567
kono
parents:
diff changeset
1568 return res;
kono
parents:
diff changeset
1569 }
kono
parents:
diff changeset
1570
kono
parents:
diff changeset
1571 /* Return the number of bytes that a format directive consisting of FLAGS,
kono
parents:
diff changeset
1572 PRECision, format SPECification, and MPFR rounding specifier RNDSPEC,
kono
parents:
diff changeset
1573 would result for argument X under ideal conditions (i.e., if PREC
kono
parents:
diff changeset
1574 weren't excessive). MPFR 3.1 allocates large amounts of memory for
kono
parents:
diff changeset
1575 values of PREC with large magnitude and can fail (see MPFR bug #21056).
kono
parents:
diff changeset
1576 This function works around those problems. */
kono
parents:
diff changeset
1577
kono
parents:
diff changeset
1578 static unsigned HOST_WIDE_INT
kono
parents:
diff changeset
1579 get_mpfr_format_length (mpfr_ptr x, const char *flags, HOST_WIDE_INT prec,
kono
parents:
diff changeset
1580 char spec, char rndspec)
kono
parents:
diff changeset
1581 {
kono
parents:
diff changeset
1582 char fmtstr[40];
kono
parents:
diff changeset
1583
kono
parents:
diff changeset
1584 HOST_WIDE_INT len = strlen (flags);
kono
parents:
diff changeset
1585
kono
parents:
diff changeset
1586 fmtstr[0] = '%';
kono
parents:
diff changeset
1587 memcpy (fmtstr + 1, flags, len);
kono
parents:
diff changeset
1588 memcpy (fmtstr + 1 + len, ".*R", 3);
kono
parents:
diff changeset
1589 fmtstr[len + 4] = rndspec;
kono
parents:
diff changeset
1590 fmtstr[len + 5] = spec;
kono
parents:
diff changeset
1591 fmtstr[len + 6] = '\0';
kono
parents:
diff changeset
1592
kono
parents:
diff changeset
1593 spec = TOUPPER (spec);
kono
parents:
diff changeset
1594 if (spec == 'E' || spec == 'F')
kono
parents:
diff changeset
1595 {
kono
parents:
diff changeset
1596 /* For %e, specify the precision explicitly since mpfr_sprintf
kono
parents:
diff changeset
1597 does its own thing just to be different (see MPFR bug 21088). */
kono
parents:
diff changeset
1598 if (prec < 0)
kono
parents:
diff changeset
1599 prec = 6;
kono
parents:
diff changeset
1600 }
kono
parents:
diff changeset
1601 else
kono
parents:
diff changeset
1602 {
kono
parents:
diff changeset
1603 /* Avoid passing negative precisions with larger magnitude to MPFR
kono
parents:
diff changeset
1604 to avoid exposing its bugs. (A negative precision is supposed
kono
parents:
diff changeset
1605 to be ignored.) */
kono
parents:
diff changeset
1606 if (prec < 0)
kono
parents:
diff changeset
1607 prec = -1;
kono
parents:
diff changeset
1608 }
kono
parents:
diff changeset
1609
kono
parents:
diff changeset
1610 HOST_WIDE_INT p = prec;
kono
parents:
diff changeset
1611
kono
parents:
diff changeset
1612 if (spec == 'G' && !strchr (flags, '#'))
kono
parents:
diff changeset
1613 {
kono
parents:
diff changeset
1614 /* For G/g without the pound flag, precision gives the maximum number
kono
parents:
diff changeset
1615 of significant digits which is bounded by LDBL_MAX_10_EXP, or, for
kono
parents:
diff changeset
1616 a 128 bit IEEE extended precision, 4932. Using twice as much here
kono
parents:
diff changeset
1617 should be more than sufficient for any real format. */
kono
parents:
diff changeset
1618 if ((IEEE_MAX_10_EXP * 2) < prec)
kono
parents:
diff changeset
1619 prec = IEEE_MAX_10_EXP * 2;
kono
parents:
diff changeset
1620 p = prec;
kono
parents:
diff changeset
1621 }
kono
parents:
diff changeset
1622 else
kono
parents:
diff changeset
1623 {
kono
parents:
diff changeset
1624 /* Cap precision arbitrarily at 1KB and add the difference
kono
parents:
diff changeset
1625 (if any) to the MPFR result. */
kono
parents:
diff changeset
1626 if (prec > 1024)
kono
parents:
diff changeset
1627 p = 1024;
kono
parents:
diff changeset
1628 }
kono
parents:
diff changeset
1629
kono
parents:
diff changeset
1630 len = mpfr_snprintf (NULL, 0, fmtstr, (int)p, x);
kono
parents:
diff changeset
1631
kono
parents:
diff changeset
1632 /* Handle the unlikely (impossible?) error by returning more than
kono
parents:
diff changeset
1633 the maximum dictated by the function's return type. */
kono
parents:
diff changeset
1634 if (len < 0)
kono
parents:
diff changeset
1635 return target_dir_max () + 1;
kono
parents:
diff changeset
1636
kono
parents:
diff changeset
1637 /* Adjust the return value by the difference. */
kono
parents:
diff changeset
1638 if (p < prec)
kono
parents:
diff changeset
1639 len += prec - p;
kono
parents:
diff changeset
1640
kono
parents:
diff changeset
1641 return len;
kono
parents:
diff changeset
1642 }
kono
parents:
diff changeset
1643
kono
parents:
diff changeset
1644 /* Return the number of bytes to format using the format specifier
kono
parents:
diff changeset
1645 SPEC and the precision PREC the largest value in the real floating
kono
parents:
diff changeset
1646 TYPE. */
kono
parents:
diff changeset
1647
kono
parents:
diff changeset
1648 static unsigned HOST_WIDE_INT
kono
parents:
diff changeset
1649 format_floating_max (tree type, char spec, HOST_WIDE_INT prec)
kono
parents:
diff changeset
1650 {
kono
parents:
diff changeset
1651 machine_mode mode = TYPE_MODE (type);
kono
parents:
diff changeset
1652
kono
parents:
diff changeset
1653 /* IBM Extended mode. */
kono
parents:
diff changeset
1654 if (MODE_COMPOSITE_P (mode))
kono
parents:
diff changeset
1655 mode = DFmode;
kono
parents:
diff changeset
1656
kono
parents:
diff changeset
1657 /* Get the real type format desription for the target. */
kono
parents:
diff changeset
1658 const real_format *rfmt = REAL_MODE_FORMAT (mode);
kono
parents:
diff changeset
1659 REAL_VALUE_TYPE rv;
kono
parents:
diff changeset
1660
kono
parents:
diff changeset
1661 real_maxval (&rv, 0, mode);
kono
parents:
diff changeset
1662
kono
parents:
diff changeset
1663 /* Convert the GCC real value representation with the precision
kono
parents:
diff changeset
1664 of the real type to the mpfr_t format with the GCC default
kono
parents:
diff changeset
1665 round-to-nearest mode. */
kono
parents:
diff changeset
1666 mpfr_t x;
kono
parents:
diff changeset
1667 mpfr_init2 (x, rfmt->p);
kono
parents:
diff changeset
1668 mpfr_from_real (x, &rv, GMP_RNDN);
kono
parents:
diff changeset
1669
kono
parents:
diff changeset
1670 /* Return a value one greater to account for the leading minus sign. */
kono
parents:
diff changeset
1671 unsigned HOST_WIDE_INT r
kono
parents:
diff changeset
1672 = 1 + get_mpfr_format_length (x, "", prec, spec, 'D');
kono
parents:
diff changeset
1673 mpfr_clear (x);
kono
parents:
diff changeset
1674 return r;
kono
parents:
diff changeset
1675 }
kono
parents:
diff changeset
1676
kono
parents:
diff changeset
1677 /* Return a range representing the minimum and maximum number of bytes
kono
parents:
diff changeset
1678 that the directive DIR will output for any argument. PREC gives
kono
parents:
diff changeset
1679 the adjusted precision range to account for negative precisions
kono
parents:
diff changeset
1680 meaning the default 6. This function is used when the directive
kono
parents:
diff changeset
1681 argument or its value isn't known. */
kono
parents:
diff changeset
1682
kono
parents:
diff changeset
1683 static fmtresult
kono
parents:
diff changeset
1684 format_floating (const directive &dir, const HOST_WIDE_INT prec[2])
kono
parents:
diff changeset
1685 {
kono
parents:
diff changeset
1686 tree type;
kono
parents:
diff changeset
1687
kono
parents:
diff changeset
1688 switch (dir.modifier)
kono
parents:
diff changeset
1689 {
kono
parents:
diff changeset
1690 case FMT_LEN_l:
kono
parents:
diff changeset
1691 case FMT_LEN_none:
kono
parents:
diff changeset
1692 type = double_type_node;
kono
parents:
diff changeset
1693 break;
kono
parents:
diff changeset
1694
kono
parents:
diff changeset
1695 case FMT_LEN_L:
kono
parents:
diff changeset
1696 type = long_double_type_node;
kono
parents:
diff changeset
1697 break;
kono
parents:
diff changeset
1698
kono
parents:
diff changeset
1699 case FMT_LEN_ll:
kono
parents:
diff changeset
1700 type = long_double_type_node;
kono
parents:
diff changeset
1701 break;
kono
parents:
diff changeset
1702
kono
parents:
diff changeset
1703 default:
kono
parents:
diff changeset
1704 return fmtresult ();
kono
parents:
diff changeset
1705 }
kono
parents:
diff changeset
1706
kono
parents:
diff changeset
1707 /* The minimum and maximum number of bytes produced by the directive. */
kono
parents:
diff changeset
1708 fmtresult res;
kono
parents:
diff changeset
1709
kono
parents:
diff changeset
1710 /* The minimum output as determined by flags. It's always at least 1.
kono
parents:
diff changeset
1711 When plus or space are set the output is preceded by either a sign
kono
parents:
diff changeset
1712 or a space. */
kono
parents:
diff changeset
1713 unsigned flagmin = (1 /* for the first digit */
kono
parents:
diff changeset
1714 + (dir.get_flag ('+') | dir.get_flag (' ')));
kono
parents:
diff changeset
1715
kono
parents:
diff changeset
1716 /* When the pound flag is set the decimal point is included in output
kono
parents:
diff changeset
1717 regardless of precision. Whether or not a decimal point is included
kono
parents:
diff changeset
1718 otherwise depends on the specification and precision. */
kono
parents:
diff changeset
1719 bool radix = dir.get_flag ('#');
kono
parents:
diff changeset
1720
kono
parents:
diff changeset
1721 switch (dir.specifier)
kono
parents:
diff changeset
1722 {
kono
parents:
diff changeset
1723 case 'A':
kono
parents:
diff changeset
1724 case 'a':
kono
parents:
diff changeset
1725 {
kono
parents:
diff changeset
1726 HOST_WIDE_INT minprec = 6 + !radix /* decimal point */;
kono
parents:
diff changeset
1727 if (dir.prec[0] <= 0)
kono
parents:
diff changeset
1728 minprec = 0;
kono
parents:
diff changeset
1729 else if (dir.prec[0] > 0)
kono
parents:
diff changeset
1730 minprec = dir.prec[0] + !radix /* decimal point */;
kono
parents:
diff changeset
1731
kono
parents:
diff changeset
1732 res.range.min = (2 /* 0x */
kono
parents:
diff changeset
1733 + flagmin
kono
parents:
diff changeset
1734 + radix
kono
parents:
diff changeset
1735 + minprec
kono
parents:
diff changeset
1736 + 3 /* p+0 */);
kono
parents:
diff changeset
1737
kono
parents:
diff changeset
1738 res.range.max = format_floating_max (type, 'a', prec[1]);
kono
parents:
diff changeset
1739 res.range.likely = res.range.min;
kono
parents:
diff changeset
1740
kono
parents:
diff changeset
1741 /* The unlikely maximum accounts for the longest multibyte
kono
parents:
diff changeset
1742 decimal point character. */
kono
parents:
diff changeset
1743 res.range.unlikely = res.range.max;
kono
parents:
diff changeset
1744 if (dir.prec[1] > 0)
kono
parents:
diff changeset
1745 res.range.unlikely += target_mb_len_max () - 1;
kono
parents:
diff changeset
1746
kono
parents:
diff changeset
1747 break;
kono
parents:
diff changeset
1748 }
kono
parents:
diff changeset
1749
kono
parents:
diff changeset
1750 case 'E':
kono
parents:
diff changeset
1751 case 'e':
kono
parents:
diff changeset
1752 {
kono
parents:
diff changeset
1753 /* Minimum output attributable to precision and, when it's
kono
parents:
diff changeset
1754 non-zero, decimal point. */
kono
parents:
diff changeset
1755 HOST_WIDE_INT minprec = prec[0] ? prec[0] + !radix : 0;
kono
parents:
diff changeset
1756
kono
parents:
diff changeset
1757 /* The minimum output is "[-+]1.234567e+00" regardless
kono
parents:
diff changeset
1758 of the value of the actual argument. */
kono
parents:
diff changeset
1759 res.range.min = (flagmin
kono
parents:
diff changeset
1760 + radix
kono
parents:
diff changeset
1761 + minprec
kono
parents:
diff changeset
1762 + 2 /* e+ */ + 2);
kono
parents:
diff changeset
1763
kono
parents:
diff changeset
1764 res.range.max = format_floating_max (type, 'e', prec[1]);
kono
parents:
diff changeset
1765 res.range.likely = res.range.min;
kono
parents:
diff changeset
1766
kono
parents:
diff changeset
1767 /* The unlikely maximum accounts for the longest multibyte
kono
parents:
diff changeset
1768 decimal point character. */
kono
parents:
diff changeset
1769 if (dir.prec[0] != dir.prec[1]
kono
parents:
diff changeset
1770 || dir.prec[0] == -1 || dir.prec[0] > 0)
kono
parents:
diff changeset
1771 res.range.unlikely = res.range.max + target_mb_len_max () -1;
kono
parents:
diff changeset
1772 else
kono
parents:
diff changeset
1773 res.range.unlikely = res.range.max;
kono
parents:
diff changeset
1774 break;
kono
parents:
diff changeset
1775 }
kono
parents:
diff changeset
1776
kono
parents:
diff changeset
1777 case 'F':
kono
parents:
diff changeset
1778 case 'f':
kono
parents:
diff changeset
1779 {
kono
parents:
diff changeset
1780 /* Minimum output attributable to precision and, when it's non-zero,
kono
parents:
diff changeset
1781 decimal point. */
kono
parents:
diff changeset
1782 HOST_WIDE_INT minprec = prec[0] ? prec[0] + !radix : 0;
kono
parents:
diff changeset
1783
kono
parents:
diff changeset
1784 /* The lower bound when precision isn't specified is 8 bytes
kono
parents:
diff changeset
1785 ("1.23456" since precision is taken to be 6). When precision
kono
parents:
diff changeset
1786 is zero, the lower bound is 1 byte (e.g., "1"). Otherwise,
kono
parents:
diff changeset
1787 when precision is greater than zero, then the lower bound
kono
parents:
diff changeset
1788 is 2 plus precision (plus flags). */
kono
parents:
diff changeset
1789 res.range.min = flagmin + radix + minprec;
kono
parents:
diff changeset
1790
kono
parents:
diff changeset
1791 /* Compute the upper bound for -TYPE_MAX. */
kono
parents:
diff changeset
1792 res.range.max = format_floating_max (type, 'f', prec[1]);
kono
parents:
diff changeset
1793
kono
parents:
diff changeset
1794 /* The minimum output with unknown precision is a single byte
kono
parents:
diff changeset
1795 (e.g., "0") but the more likely output is 3 bytes ("0.0"). */
kono
parents:
diff changeset
1796 if (dir.prec[0] < 0 && dir.prec[1] > 0)
kono
parents:
diff changeset
1797 res.range.likely = 3;
kono
parents:
diff changeset
1798 else
kono
parents:
diff changeset
1799 res.range.likely = res.range.min;
kono
parents:
diff changeset
1800
kono
parents:
diff changeset
1801 /* The unlikely maximum accounts for the longest multibyte
kono
parents:
diff changeset
1802 decimal point character. */
kono
parents:
diff changeset
1803 if (dir.prec[0] != dir.prec[1]
kono
parents:
diff changeset
1804 || dir.prec[0] == -1 || dir.prec[0] > 0)
kono
parents:
diff changeset
1805 res.range.unlikely = res.range.max + target_mb_len_max () - 1;
kono
parents:
diff changeset
1806 break;
kono
parents:
diff changeset
1807 }
kono
parents:
diff changeset
1808
kono
parents:
diff changeset
1809 case 'G':
kono
parents:
diff changeset
1810 case 'g':
kono
parents:
diff changeset
1811 {
kono
parents:
diff changeset
1812 /* The %g output depends on precision and the exponent of
kono
parents:
diff changeset
1813 the argument. Since the value of the argument isn't known
kono
parents:
diff changeset
1814 the lower bound on the range of bytes (not counting flags
kono
parents:
diff changeset
1815 or width) is 1 plus radix (i.e., either "0" or "0." for
kono
parents:
diff changeset
1816 "%g" and "%#g", respectively, with a zero argument). */
kono
parents:
diff changeset
1817 res.range.min = flagmin + radix;
kono
parents:
diff changeset
1818
kono
parents:
diff changeset
1819 char spec = 'g';
kono
parents:
diff changeset
1820 HOST_WIDE_INT maxprec = dir.prec[1];
kono
parents:
diff changeset
1821 if (radix && maxprec)
kono
parents:
diff changeset
1822 {
kono
parents:
diff changeset
1823 /* When the pound flag (radix) is set, trailing zeros aren't
kono
parents:
diff changeset
1824 trimmed and so the longest output is the same as for %e,
kono
parents:
diff changeset
1825 except with precision minus 1 (as specified in C11). */
kono
parents:
diff changeset
1826 spec = 'e';
kono
parents:
diff changeset
1827 if (maxprec > 0)
kono
parents:
diff changeset
1828 --maxprec;
kono
parents:
diff changeset
1829 else if (maxprec < 0)
kono
parents:
diff changeset
1830 maxprec = 5;
kono
parents:
diff changeset
1831 }
kono
parents:
diff changeset
1832 else
kono
parents:
diff changeset
1833 maxprec = prec[1];
kono
parents:
diff changeset
1834
kono
parents:
diff changeset
1835 res.range.max = format_floating_max (type, spec, maxprec);
kono
parents:
diff changeset
1836
kono
parents:
diff changeset
1837 /* The likely output is either the maximum computed above
kono
parents:
diff changeset
1838 minus 1 (assuming the maximum is positive) when precision
kono
parents:
diff changeset
1839 is known (or unspecified), or the same minimum as for %e
kono
parents:
diff changeset
1840 (which is computed for a non-negative argument). Unlike
kono
parents:
diff changeset
1841 for the other specifiers above the likely output isn't
kono
parents:
diff changeset
1842 the minimum because for %g that's 1 which is unlikely. */
kono
parents:
diff changeset
1843 if (dir.prec[1] < 0
kono
parents:
diff changeset
1844 || (unsigned HOST_WIDE_INT)dir.prec[1] < target_int_max ())
kono
parents:
diff changeset
1845 res.range.likely = res.range.max - 1;
kono
parents:
diff changeset
1846 else
kono
parents:
diff changeset
1847 {
kono
parents:
diff changeset
1848 HOST_WIDE_INT minprec = 6 + !radix /* decimal point */;
kono
parents:
diff changeset
1849 res.range.likely = (flagmin
kono
parents:
diff changeset
1850 + radix
kono
parents:
diff changeset
1851 + minprec
kono
parents:
diff changeset
1852 + 2 /* e+ */ + 2);
kono
parents:
diff changeset
1853 }
kono
parents:
diff changeset
1854
kono
parents:
diff changeset
1855 /* The unlikely maximum accounts for the longest multibyte
kono
parents:
diff changeset
1856 decimal point character. */
kono
parents:
diff changeset
1857 res.range.unlikely = res.range.max + target_mb_len_max () - 1;
kono
parents:
diff changeset
1858 break;
kono
parents:
diff changeset
1859 }
kono
parents:
diff changeset
1860
kono
parents:
diff changeset
1861 default:
kono
parents:
diff changeset
1862 return fmtresult ();
kono
parents:
diff changeset
1863 }
kono
parents:
diff changeset
1864
kono
parents:
diff changeset
1865 /* Bump up the byte counters if WIDTH is greater. */
kono
parents:
diff changeset
1866 res.adjust_for_width_or_precision (dir.width);
kono
parents:
diff changeset
1867 return res;
kono
parents:
diff changeset
1868 }
kono
parents:
diff changeset
1869
kono
parents:
diff changeset
1870 /* Return a range representing the minimum and maximum number of bytes
kono
parents:
diff changeset
1871 that the directive DIR will write on output for the floating argument
kono
parents:
diff changeset
1872 ARG. */
kono
parents:
diff changeset
1873
kono
parents:
diff changeset
1874 static fmtresult
kono
parents:
diff changeset
1875 format_floating (const directive &dir, tree arg)
kono
parents:
diff changeset
1876 {
kono
parents:
diff changeset
1877 HOST_WIDE_INT prec[] = { dir.prec[0], dir.prec[1] };
kono
parents:
diff changeset
1878
kono
parents:
diff changeset
1879 /* For an indeterminate precision the lower bound must be assumed
kono
parents:
diff changeset
1880 to be zero. */
kono
parents:
diff changeset
1881 if (TOUPPER (dir.specifier) == 'A')
kono
parents:
diff changeset
1882 {
kono
parents:
diff changeset
1883 /* Get the number of fractional decimal digits needed to represent
kono
parents:
diff changeset
1884 the argument without a loss of accuracy. */
kono
parents:
diff changeset
1885 tree type = arg ? TREE_TYPE (arg) :
kono
parents:
diff changeset
1886 (dir.modifier == FMT_LEN_L || dir.modifier == FMT_LEN_ll
kono
parents:
diff changeset
1887 ? long_double_type_node : double_type_node);
kono
parents:
diff changeset
1888
kono
parents:
diff changeset
1889 unsigned fmtprec
kono
parents:
diff changeset
1890 = REAL_MODE_FORMAT (TYPE_MODE (type))->p;
kono
parents:
diff changeset
1891
kono
parents:
diff changeset
1892 /* The precision of the IEEE 754 double format is 53.
kono
parents:
diff changeset
1893 The precision of all other GCC binary double formats
kono
parents:
diff changeset
1894 is 56 or less. */
kono
parents:
diff changeset
1895 unsigned maxprec = fmtprec <= 56 ? 13 : 15;
kono
parents:
diff changeset
1896
kono
parents:
diff changeset
1897 /* For %a, leave the minimum precision unspecified to let
kono
parents:
diff changeset
1898 MFPR trim trailing zeros (as it and many other systems
kono
parents:
diff changeset
1899 including Glibc happen to do) and set the maximum
kono
parents:
diff changeset
1900 precision to reflect what it would be with trailing zeros
kono
parents:
diff changeset
1901 present (as Solaris and derived systems do). */
kono
parents:
diff changeset
1902 if (dir.prec[1] < 0)
kono
parents:
diff changeset
1903 {
kono
parents:
diff changeset
1904 /* Both bounds are negative implies that precision has
kono
parents:
diff changeset
1905 not been specified. */
kono
parents:
diff changeset
1906 prec[0] = maxprec;
kono
parents:
diff changeset
1907 prec[1] = -1;
kono
parents:
diff changeset
1908 }
kono
parents:
diff changeset
1909 else if (dir.prec[0] < 0)
kono
parents:
diff changeset
1910 {
kono
parents:
diff changeset
1911 /* With a negative lower bound and a non-negative upper
kono
parents:
diff changeset
1912 bound set the minimum precision to zero and the maximum
kono
parents:
diff changeset
1913 to the greater of the maximum precision (i.e., with
kono
parents:
diff changeset
1914 trailing zeros present) and the specified upper bound. */
kono
parents:
diff changeset
1915 prec[0] = 0;
kono
parents:
diff changeset
1916 prec[1] = dir.prec[1] < maxprec ? maxprec : dir.prec[1];
kono
parents:
diff changeset
1917 }
kono
parents:
diff changeset
1918 }
kono
parents:
diff changeset
1919 else if (dir.prec[0] < 0)
kono
parents:
diff changeset
1920 {
kono
parents:
diff changeset
1921 if (dir.prec[1] < 0)
kono
parents:
diff changeset
1922 {
kono
parents:
diff changeset
1923 /* A precision in a strictly negative range is ignored and
kono
parents:
diff changeset
1924 the default of 6 is used instead. */
kono
parents:
diff changeset
1925 prec[0] = prec[1] = 6;
kono
parents:
diff changeset
1926 }
kono
parents:
diff changeset
1927 else
kono
parents:
diff changeset
1928 {
kono
parents:
diff changeset
1929 /* For a precision in a partly negative range, the lower bound
kono
parents:
diff changeset
1930 must be assumed to be zero and the new upper bound is the
kono
parents:
diff changeset
1931 greater of 6 (the default precision used when the specified
kono
parents:
diff changeset
1932 precision is negative) and the upper bound of the specified
kono
parents:
diff changeset
1933 range. */
kono
parents:
diff changeset
1934 prec[0] = 0;
kono
parents:
diff changeset
1935 prec[1] = dir.prec[1] < 6 ? 6 : dir.prec[1];
kono
parents:
diff changeset
1936 }
kono
parents:
diff changeset
1937 }
kono
parents:
diff changeset
1938
kono
parents:
diff changeset
1939 if (!arg || TREE_CODE (arg) != REAL_CST)
kono
parents:
diff changeset
1940 return format_floating (dir, prec);
kono
parents:
diff changeset
1941
kono
parents:
diff changeset
1942 /* The minimum and maximum number of bytes produced by the directive. */
kono
parents:
diff changeset
1943 fmtresult res;
kono
parents:
diff changeset
1944
kono
parents:
diff changeset
1945 /* Get the real type format desription for the target. */
kono
parents:
diff changeset
1946 const REAL_VALUE_TYPE *rvp = TREE_REAL_CST_PTR (arg);
kono
parents:
diff changeset
1947 const real_format *rfmt = REAL_MODE_FORMAT (TYPE_MODE (TREE_TYPE (arg)));
kono
parents:
diff changeset
1948
kono
parents:
diff changeset
1949 char fmtstr [40];
kono
parents:
diff changeset
1950 char *pfmt = fmtstr;
kono
parents:
diff changeset
1951
kono
parents:
diff changeset
1952 /* Append flags. */
kono
parents:
diff changeset
1953 for (const char *pf = "-+ #0"; *pf; ++pf)
kono
parents:
diff changeset
1954 if (dir.get_flag (*pf))
kono
parents:
diff changeset
1955 *pfmt++ = *pf;
kono
parents:
diff changeset
1956
kono
parents:
diff changeset
1957 *pfmt = '\0';
kono
parents:
diff changeset
1958
kono
parents:
diff changeset
1959 {
kono
parents:
diff changeset
1960 /* Set up an array to easily iterate over. */
kono
parents:
diff changeset
1961 unsigned HOST_WIDE_INT* const minmax[] = {
kono
parents:
diff changeset
1962 &res.range.min, &res.range.max
kono
parents:
diff changeset
1963 };
kono
parents:
diff changeset
1964
kono
parents:
diff changeset
1965 for (int i = 0; i != sizeof minmax / sizeof *minmax; ++i)
kono
parents:
diff changeset
1966 {
kono
parents:
diff changeset
1967 /* Convert the GCC real value representation with the precision
kono
parents:
diff changeset
1968 of the real type to the mpfr_t format rounding down in the
kono
parents:
diff changeset
1969 first iteration that computes the minimm and up in the second
kono
parents:
diff changeset
1970 that computes the maximum. This order is arbibtrary because
kono
parents:
diff changeset
1971 rounding in either direction can result in longer output. */
kono
parents:
diff changeset
1972 mpfr_t mpfrval;
kono
parents:
diff changeset
1973 mpfr_init2 (mpfrval, rfmt->p);
kono
parents:
diff changeset
1974 mpfr_from_real (mpfrval, rvp, i ? GMP_RNDU : GMP_RNDD);
kono
parents:
diff changeset
1975
kono
parents:
diff changeset
1976 /* Use the MPFR rounding specifier to round down in the first
kono
parents:
diff changeset
1977 iteration and then up. In most but not all cases this will
kono
parents:
diff changeset
1978 result in the same number of bytes. */
kono
parents:
diff changeset
1979 char rndspec = "DU"[i];
kono
parents:
diff changeset
1980
kono
parents:
diff changeset
1981 /* Format it and store the result in the corresponding member
kono
parents:
diff changeset
1982 of the result struct. */
kono
parents:
diff changeset
1983 *minmax[i] = get_mpfr_format_length (mpfrval, fmtstr, prec[i],
kono
parents:
diff changeset
1984 dir.specifier, rndspec);
kono
parents:
diff changeset
1985 mpfr_clear (mpfrval);
kono
parents:
diff changeset
1986 }
kono
parents:
diff changeset
1987 }
kono
parents:
diff changeset
1988
kono
parents:
diff changeset
1989 /* Make sure the minimum is less than the maximum (MPFR rounding
kono
parents:
diff changeset
1990 in the call to mpfr_snprintf can result in the reverse. */
kono
parents:
diff changeset
1991 if (res.range.max < res.range.min)
kono
parents:
diff changeset
1992 {
kono
parents:
diff changeset
1993 unsigned HOST_WIDE_INT tmp = res.range.min;
kono
parents:
diff changeset
1994 res.range.min = res.range.max;
kono
parents:
diff changeset
1995 res.range.max = tmp;
kono
parents:
diff changeset
1996 }
kono
parents:
diff changeset
1997
kono
parents:
diff changeset
1998 /* The range is known unless either width or precision is unknown. */
kono
parents:
diff changeset
1999 res.knownrange = dir.known_width_and_precision ();
kono
parents:
diff changeset
2000
kono
parents:
diff changeset
2001 /* For the same floating point constant, unless width or precision
kono
parents:
diff changeset
2002 is unknown, use the longer output as the likely maximum since
kono
parents:
diff changeset
2003 with round to nearest either is equally likely. Otheriwse, when
kono
parents:
diff changeset
2004 precision is unknown, use the greater of the minimum and 3 as
kono
parents:
diff changeset
2005 the likely output (for "0.0" since zero precision is unlikely). */
kono
parents:
diff changeset
2006 if (res.knownrange)
kono
parents:
diff changeset
2007 res.range.likely = res.range.max;
kono
parents:
diff changeset
2008 else if (res.range.min < 3
kono
parents:
diff changeset
2009 && dir.prec[0] < 0
kono
parents:
diff changeset
2010 && (unsigned HOST_WIDE_INT)dir.prec[1] == target_int_max ())
kono
parents:
diff changeset
2011 res.range.likely = 3;
kono
parents:
diff changeset
2012 else
kono
parents:
diff changeset
2013 res.range.likely = res.range.min;
kono
parents:
diff changeset
2014
kono
parents:
diff changeset
2015 res.range.unlikely = res.range.max;
kono
parents:
diff changeset
2016
kono
parents:
diff changeset
2017 if (res.range.max > 2 && (prec[0] != 0 || prec[1] != 0))
kono
parents:
diff changeset
2018 {
kono
parents:
diff changeset
2019 /* Unless the precision is zero output longer than 2 bytes may
kono
parents:
diff changeset
2020 include the decimal point which must be a single character
kono
parents:
diff changeset
2021 up to MB_LEN_MAX in length. This is overly conservative
kono
parents:
diff changeset
2022 since in some conversions some constants result in no decimal
kono
parents:
diff changeset
2023 point (e.g., in %g). */
kono
parents:
diff changeset
2024 res.range.unlikely += target_mb_len_max () - 1;
kono
parents:
diff changeset
2025 }
kono
parents:
diff changeset
2026
kono
parents:
diff changeset
2027 res.adjust_for_width_or_precision (dir.width);
kono
parents:
diff changeset
2028 return res;
kono
parents:
diff changeset
2029 }
kono
parents:
diff changeset
2030
kono
parents:
diff changeset
2031 /* Return a FMTRESULT struct set to the lengths of the shortest and longest
kono
parents:
diff changeset
2032 strings referenced by the expression STR, or (-1, -1) when not known.
kono
parents:
diff changeset
2033 Used by the format_string function below. */
kono
parents:
diff changeset
2034
kono
parents:
diff changeset
2035 static fmtresult
kono
parents:
diff changeset
2036 get_string_length (tree str)
kono
parents:
diff changeset
2037 {
kono
parents:
diff changeset
2038 if (!str)
kono
parents:
diff changeset
2039 return fmtresult ();
kono
parents:
diff changeset
2040
kono
parents:
diff changeset
2041 if (tree slen = c_strlen (str, 1))
kono
parents:
diff changeset
2042 {
kono
parents:
diff changeset
2043 /* Simply return the length of the string. */
kono
parents:
diff changeset
2044 fmtresult res (tree_to_shwi (slen));
kono
parents:
diff changeset
2045 return res;
kono
parents:
diff changeset
2046 }
kono
parents:
diff changeset
2047
kono
parents:
diff changeset
2048 /* Determine the length of the shortest and longest string referenced
kono
parents:
diff changeset
2049 by STR. Strings of unknown lengths are bounded by the sizes of
kono
parents:
diff changeset
2050 arrays that subexpressions of STR may refer to. Pointers that
kono
parents:
diff changeset
2051 aren't known to point any such arrays result in LENRANGE[1] set
kono
parents:
diff changeset
2052 to SIZE_MAX. */
kono
parents:
diff changeset
2053 tree lenrange[2];
kono
parents:
diff changeset
2054 bool flexarray = get_range_strlen (str, lenrange);
kono
parents:
diff changeset
2055
kono
parents:
diff changeset
2056 if (lenrange [0] || lenrange [1])
kono
parents:
diff changeset
2057 {
kono
parents:
diff changeset
2058 HOST_WIDE_INT min
kono
parents:
diff changeset
2059 = (tree_fits_uhwi_p (lenrange[0])
kono
parents:
diff changeset
2060 ? tree_to_uhwi (lenrange[0])
kono
parents:
diff changeset
2061 : 0);
kono
parents:
diff changeset
2062
kono
parents:
diff changeset
2063 HOST_WIDE_INT max
kono
parents:
diff changeset
2064 = (tree_fits_uhwi_p (lenrange[1])
kono
parents:
diff changeset
2065 ? tree_to_uhwi (lenrange[1])
kono
parents:
diff changeset
2066 : HOST_WIDE_INT_M1U);
kono
parents:
diff changeset
2067
kono
parents:
diff changeset
2068 /* get_range_strlen() returns the target value of SIZE_MAX for
kono
parents:
diff changeset
2069 strings of unknown length. Bump it up to HOST_WIDE_INT_M1U
kono
parents:
diff changeset
2070 which may be bigger. */
kono
parents:
diff changeset
2071 if ((unsigned HOST_WIDE_INT)min == target_size_max ())
kono
parents:
diff changeset
2072 min = HOST_WIDE_INT_M1U;
kono
parents:
diff changeset
2073 if ((unsigned HOST_WIDE_INT)max == target_size_max ())
kono
parents:
diff changeset
2074 max = HOST_WIDE_INT_M1U;
kono
parents:
diff changeset
2075
kono
parents:
diff changeset
2076 fmtresult res (min, max);
kono
parents:
diff changeset
2077
kono
parents:
diff changeset
2078 /* Set RES.KNOWNRANGE to true if and only if all strings referenced
kono
parents:
diff changeset
2079 by STR are known to be bounded (though not necessarily by their
kono
parents:
diff changeset
2080 actual length but perhaps by their maximum possible length). */
kono
parents:
diff changeset
2081 if (res.range.max < target_int_max ())
kono
parents:
diff changeset
2082 {
kono
parents:
diff changeset
2083 res.knownrange = true;
kono
parents:
diff changeset
2084 /* When the the length of the longest string is known and not
kono
parents:
diff changeset
2085 excessive use it as the likely length of the string(s). */
kono
parents:
diff changeset
2086 res.range.likely = res.range.max;
kono
parents:
diff changeset
2087 }
kono
parents:
diff changeset
2088 else
kono
parents:
diff changeset
2089 {
kono
parents:
diff changeset
2090 /* When the upper bound is unknown (it can be zero or excessive)
kono
parents:
diff changeset
2091 set the likely length to the greater of 1 and the length of
kono
parents:
diff changeset
2092 the shortest string and reset the lower bound to zero. */
kono
parents:
diff changeset
2093 res.range.likely = res.range.min ? res.range.min : warn_level > 1;
kono
parents:
diff changeset
2094 res.range.min = 0;
kono
parents:
diff changeset
2095 }
kono
parents:
diff changeset
2096
kono
parents:
diff changeset
2097 /* If the range of string length has been estimated from the size
kono
parents:
diff changeset
2098 of an array at the end of a struct assume that it's longer than
kono
parents:
diff changeset
2099 the array bound says it is in case it's used as a poor man's
kono
parents:
diff changeset
2100 flexible array member, such as in struct S { char a[4]; }; */
kono
parents:
diff changeset
2101 res.range.unlikely = flexarray ? HOST_WIDE_INT_MAX : res.range.max;
kono
parents:
diff changeset
2102
kono
parents:
diff changeset
2103 return res;
kono
parents:
diff changeset
2104 }
kono
parents:
diff changeset
2105
kono
parents:
diff changeset
2106 return get_string_length (NULL_TREE);
kono
parents:
diff changeset
2107 }
kono
parents:
diff changeset
2108
kono
parents:
diff changeset
2109 /* Return the minimum and maximum number of characters formatted
kono
parents:
diff changeset
2110 by the '%c' format directives and its wide character form for
kono
parents:
diff changeset
2111 the argument ARG. ARG can be null (for functions such as
kono
parents:
diff changeset
2112 vsprinf). */
kono
parents:
diff changeset
2113
kono
parents:
diff changeset
2114 static fmtresult
kono
parents:
diff changeset
2115 format_character (const directive &dir, tree arg)
kono
parents:
diff changeset
2116 {
kono
parents:
diff changeset
2117 fmtresult res;
kono
parents:
diff changeset
2118
kono
parents:
diff changeset
2119 res.knownrange = true;
kono
parents:
diff changeset
2120
kono
parents:
diff changeset
2121 if (dir.modifier == FMT_LEN_l)
kono
parents:
diff changeset
2122 {
kono
parents:
diff changeset
2123 /* A wide character can result in as few as zero bytes. */
kono
parents:
diff changeset
2124 res.range.min = 0;
kono
parents:
diff changeset
2125
kono
parents:
diff changeset
2126 HOST_WIDE_INT min, max;
kono
parents:
diff changeset
2127 if (get_int_range (arg, &min, &max, false, 0))
kono
parents:
diff changeset
2128 {
kono
parents:
diff changeset
2129 if (min == 0 && max == 0)
kono
parents:
diff changeset
2130 {
kono
parents:
diff changeset
2131 /* The NUL wide character results in no bytes. */
kono
parents:
diff changeset
2132 res.range.max = 0;
kono
parents:
diff changeset
2133 res.range.likely = 0;
kono
parents:
diff changeset
2134 res.range.unlikely = 0;
kono
parents:
diff changeset
2135 }
kono
parents:
diff changeset
2136 else if (min > 0 && min < 128)
kono
parents:
diff changeset
2137 {
kono
parents:
diff changeset
2138 /* A wide character in the ASCII range most likely results
kono
parents:
diff changeset
2139 in a single byte, and only unlikely in up to MB_LEN_MAX. */
kono
parents:
diff changeset
2140 res.range.max = 1;
kono
parents:
diff changeset
2141 res.range.likely = 1;
kono
parents:
diff changeset
2142 res.range.unlikely = target_mb_len_max ();
kono
parents:
diff changeset
2143 }
kono
parents:
diff changeset
2144 else
kono
parents:
diff changeset
2145 {
kono
parents:
diff changeset
2146 /* A wide character outside the ASCII range likely results
kono
parents:
diff changeset
2147 in up to two bytes, and only unlikely in up to MB_LEN_MAX. */
kono
parents:
diff changeset
2148 res.range.max = target_mb_len_max ();
kono
parents:
diff changeset
2149 res.range.likely = 2;
kono
parents:
diff changeset
2150 res.range.unlikely = res.range.max;
kono
parents:
diff changeset
2151 }
kono
parents:
diff changeset
2152 }
kono
parents:
diff changeset
2153 else
kono
parents:
diff changeset
2154 {
kono
parents:
diff changeset
2155 /* An unknown wide character is treated the same as a wide
kono
parents:
diff changeset
2156 character outside the ASCII range. */
kono
parents:
diff changeset
2157 res.range.max = target_mb_len_max ();
kono
parents:
diff changeset
2158 res.range.likely = 2;
kono
parents:
diff changeset
2159 res.range.unlikely = res.range.max;
kono
parents:
diff changeset
2160 }
kono
parents:
diff changeset
2161 }
kono
parents:
diff changeset
2162 else
kono
parents:
diff changeset
2163 {
kono
parents:
diff changeset
2164 /* A plain '%c' directive. Its ouput is exactly 1. */
kono
parents:
diff changeset
2165 res.range.min = res.range.max = 1;
kono
parents:
diff changeset
2166 res.range.likely = res.range.unlikely = 1;
kono
parents:
diff changeset
2167 res.knownrange = true;
kono
parents:
diff changeset
2168 }
kono
parents:
diff changeset
2169
kono
parents:
diff changeset
2170 /* Bump up the byte counters if WIDTH is greater. */
kono
parents:
diff changeset
2171 return res.adjust_for_width_or_precision (dir.width);
kono
parents:
diff changeset
2172 }
kono
parents:
diff changeset
2173
kono
parents:
diff changeset
2174 /* Return the minimum and maximum number of characters formatted
kono
parents:
diff changeset
2175 by the '%s' format directive and its wide character form for
kono
parents:
diff changeset
2176 the argument ARG. ARG can be null (for functions such as
kono
parents:
diff changeset
2177 vsprinf). */
kono
parents:
diff changeset
2178
kono
parents:
diff changeset
2179 static fmtresult
kono
parents:
diff changeset
2180 format_string (const directive &dir, tree arg)
kono
parents:
diff changeset
2181 {
kono
parents:
diff changeset
2182 fmtresult res;
kono
parents:
diff changeset
2183
kono
parents:
diff changeset
2184 /* Compute the range the argument's length can be in. */
kono
parents:
diff changeset
2185 fmtresult slen = get_string_length (arg);
kono
parents:
diff changeset
2186 if (slen.range.min == slen.range.max
kono
parents:
diff changeset
2187 && slen.range.min < HOST_WIDE_INT_MAX)
kono
parents:
diff changeset
2188 {
kono
parents:
diff changeset
2189 /* The argument is either a string constant or it refers
kono
parents:
diff changeset
2190 to one of a number of strings of the same length. */
kono
parents:
diff changeset
2191
kono
parents:
diff changeset
2192 /* A '%s' directive with a string argument with constant length. */
kono
parents:
diff changeset
2193 res.range = slen.range;
kono
parents:
diff changeset
2194
kono
parents:
diff changeset
2195 if (dir.modifier == FMT_LEN_l)
kono
parents:
diff changeset
2196 {
kono
parents:
diff changeset
2197 /* In the worst case the length of output of a wide string S
kono
parents:
diff changeset
2198 is bounded by MB_LEN_MAX * wcslen (S). */
kono
parents:
diff changeset
2199 res.range.max *= target_mb_len_max ();
kono
parents:
diff changeset
2200 res.range.unlikely = res.range.max;
kono
parents:
diff changeset
2201 /* It's likely that the the total length is not more that
kono
parents:
diff changeset
2202 2 * wcslen (S).*/
kono
parents:
diff changeset
2203 res.range.likely = res.range.min * 2;
kono
parents:
diff changeset
2204
kono
parents:
diff changeset
2205 if (dir.prec[1] >= 0
kono
parents:
diff changeset
2206 && (unsigned HOST_WIDE_INT)dir.prec[1] < res.range.max)
kono
parents:
diff changeset
2207 {
kono
parents:
diff changeset
2208 res.range.max = dir.prec[1];
kono
parents:
diff changeset
2209 res.range.likely = dir.prec[1];
kono
parents:
diff changeset
2210 res.range.unlikely = dir.prec[1];
kono
parents:
diff changeset
2211 }
kono
parents:
diff changeset
2212
kono
parents:
diff changeset
2213 if (dir.prec[0] < 0 && dir.prec[1] > -1)
kono
parents:
diff changeset
2214 res.range.min = 0;
kono
parents:
diff changeset
2215 else if (dir.prec[0] >= 0)
kono
parents:
diff changeset
2216 res.range.likely = dir.prec[0];
kono
parents:
diff changeset
2217
kono
parents:
diff changeset
2218 /* Even a non-empty wide character string need not convert into
kono
parents:
diff changeset
2219 any bytes. */
kono
parents:
diff changeset
2220 res.range.min = 0;
kono
parents:
diff changeset
2221 }
kono
parents:
diff changeset
2222 else
kono
parents:
diff changeset
2223 {
kono
parents:
diff changeset
2224 res.knownrange = true;
kono
parents:
diff changeset
2225
kono
parents:
diff changeset
2226 if (dir.prec[0] < 0 && dir.prec[1] > -1)
kono
parents:
diff changeset
2227 res.range.min = 0;
kono
parents:
diff changeset
2228 else if ((unsigned HOST_WIDE_INT)dir.prec[0] < res.range.min)
kono
parents:
diff changeset
2229 res.range.min = dir.prec[0];
kono
parents:
diff changeset
2230
kono
parents:
diff changeset
2231 if ((unsigned HOST_WIDE_INT)dir.prec[1] < res.range.max)
kono
parents:
diff changeset
2232 {
kono
parents:
diff changeset
2233 res.range.max = dir.prec[1];
kono
parents:
diff changeset
2234 res.range.likely = dir.prec[1];
kono
parents:
diff changeset
2235 res.range.unlikely = dir.prec[1];
kono
parents:
diff changeset
2236 }
kono
parents:
diff changeset
2237 }
kono
parents:
diff changeset
2238 }
kono
parents:
diff changeset
2239 else if (arg && integer_zerop (arg))
kono
parents:
diff changeset
2240 {
kono
parents:
diff changeset
2241 /* Handle null pointer argument. */
kono
parents:
diff changeset
2242
kono
parents:
diff changeset
2243 fmtresult res (0);
kono
parents:
diff changeset
2244 res.nullp = true;
kono
parents:
diff changeset
2245 return res;
kono
parents:
diff changeset
2246 }
kono
parents:
diff changeset
2247 else
kono
parents:
diff changeset
2248 {
kono
parents:
diff changeset
2249 /* For a '%s' and '%ls' directive with a non-constant string (either
kono
parents:
diff changeset
2250 one of a number of strings of known length or an unknown string)
kono
parents:
diff changeset
2251 the minimum number of characters is lesser of PRECISION[0] and
kono
parents:
diff changeset
2252 the length of the shortest known string or zero, and the maximum
kono
parents:
diff changeset
2253 is the lessser of the length of the longest known string or
kono
parents:
diff changeset
2254 PTRDIFF_MAX and PRECISION[1]. The likely length is either
kono
parents:
diff changeset
2255 the minimum at level 1 and the greater of the minimum and 1
kono
parents:
diff changeset
2256 at level 2. This result is adjust upward for width (if it's
kono
parents:
diff changeset
2257 specified). */
kono
parents:
diff changeset
2258
kono
parents:
diff changeset
2259 if (dir.modifier == FMT_LEN_l)
kono
parents:
diff changeset
2260 {
kono
parents:
diff changeset
2261 /* A wide character converts to as few as zero bytes. */
kono
parents:
diff changeset
2262 slen.range.min = 0;
kono
parents:
diff changeset
2263 if (slen.range.max < target_int_max ())
kono
parents:
diff changeset
2264 slen.range.max *= target_mb_len_max ();
kono
parents:
diff changeset
2265
kono
parents:
diff changeset
2266 if (slen.range.likely < target_int_max ())
kono
parents:
diff changeset
2267 slen.range.likely *= 2;
kono
parents:
diff changeset
2268
kono
parents:
diff changeset
2269 if (slen.range.likely < target_int_max ())
kono
parents:
diff changeset
2270 slen.range.unlikely *= target_mb_len_max ();
kono
parents:
diff changeset
2271 }
kono
parents:
diff changeset
2272
kono
parents:
diff changeset
2273 res.range = slen.range;
kono
parents:
diff changeset
2274
kono
parents:
diff changeset
2275 if (dir.prec[0] >= 0)
kono
parents:
diff changeset
2276 {
kono
parents:
diff changeset
2277 /* Adjust the minimum to zero if the string length is unknown,
kono
parents:
diff changeset
2278 or at most the lower bound of the precision otherwise. */
kono
parents:
diff changeset
2279 if (slen.range.min >= target_int_max ())
kono
parents:
diff changeset
2280 res.range.min = 0;
kono
parents:
diff changeset
2281 else if ((unsigned HOST_WIDE_INT)dir.prec[0] < slen.range.min)
kono
parents:
diff changeset
2282 res.range.min = dir.prec[0];
kono
parents:
diff changeset
2283
kono
parents:
diff changeset
2284 /* Make both maxima no greater than the upper bound of precision. */
kono
parents:
diff changeset
2285 if ((unsigned HOST_WIDE_INT)dir.prec[1] < slen.range.max
kono
parents:
diff changeset
2286 || slen.range.max >= target_int_max ())
kono
parents:
diff changeset
2287 {
kono
parents:
diff changeset
2288 res.range.max = dir.prec[1];
kono
parents:
diff changeset
2289 res.range.unlikely = dir.prec[1];
kono
parents:
diff changeset
2290 }
kono
parents:
diff changeset
2291
kono
parents:
diff changeset
2292 /* If precision is constant, set the likely counter to the lesser
kono
parents:
diff changeset
2293 of it and the maximum string length. Otherwise, if the lower
kono
parents:
diff changeset
2294 bound of precision is greater than zero, set the likely counter
kono
parents:
diff changeset
2295 to the minimum. Otherwise set it to zero or one based on
kono
parents:
diff changeset
2296 the warning level. */
kono
parents:
diff changeset
2297 if (dir.prec[0] == dir.prec[1])
kono
parents:
diff changeset
2298 res.range.likely
kono
parents:
diff changeset
2299 = ((unsigned HOST_WIDE_INT)dir.prec[0] < slen.range.max
kono
parents:
diff changeset
2300 ? dir.prec[0] : slen.range.max);
kono
parents:
diff changeset
2301 else if (dir.prec[0] > 0)
kono
parents:
diff changeset
2302 res.range.likely = res.range.min;
kono
parents:
diff changeset
2303 else
kono
parents:
diff changeset
2304 res.range.likely = warn_level > 1;
kono
parents:
diff changeset
2305 }
kono
parents:
diff changeset
2306 else if (dir.prec[1] >= 0)
kono
parents:
diff changeset
2307 {
kono
parents:
diff changeset
2308 res.range.min = 0;
kono
parents:
diff changeset
2309 if ((unsigned HOST_WIDE_INT)dir.prec[1] < slen.range.max)
kono
parents:
diff changeset
2310 res.range.max = dir.prec[1];
kono
parents:
diff changeset
2311 res.range.likely = dir.prec[1] ? warn_level > 1 : 0;
kono
parents:
diff changeset
2312 }
kono
parents:
diff changeset
2313 else if (slen.range.min >= target_int_max ())
kono
parents:
diff changeset
2314 {
kono
parents:
diff changeset
2315 res.range.min = 0;
kono
parents:
diff changeset
2316 res.range.max = HOST_WIDE_INT_MAX;
kono
parents:
diff changeset
2317 /* At level 1 strings of unknown length are assumed to be
kono
parents:
diff changeset
2318 empty, while at level 1 they are assumed to be one byte
kono
parents:
diff changeset
2319 long. */
kono
parents:
diff changeset
2320 res.range.likely = warn_level > 1;
kono
parents:
diff changeset
2321 }
kono
parents:
diff changeset
2322 else
kono
parents:
diff changeset
2323 {
kono
parents:
diff changeset
2324 /* A string of unknown length unconstrained by precision is
kono
parents:
diff changeset
2325 assumed to be empty at level 1 and just one character long
kono
parents:
diff changeset
2326 at higher levels. */
kono
parents:
diff changeset
2327 if (res.range.likely >= target_int_max ())
kono
parents:
diff changeset
2328 res.range.likely = warn_level > 1;
kono
parents:
diff changeset
2329 }
kono
parents:
diff changeset
2330
kono
parents:
diff changeset
2331 res.range.unlikely = res.range.max;
kono
parents:
diff changeset
2332 }
kono
parents:
diff changeset
2333
kono
parents:
diff changeset
2334 /* Bump up the byte counters if WIDTH is greater. */
kono
parents:
diff changeset
2335 return res.adjust_for_width_or_precision (dir.width);
kono
parents:
diff changeset
2336 }
kono
parents:
diff changeset
2337
kono
parents:
diff changeset
2338 /* Format plain string (part of the format string itself). */
kono
parents:
diff changeset
2339
kono
parents:
diff changeset
2340 static fmtresult
kono
parents:
diff changeset
2341 format_plain (const directive &dir, tree)
kono
parents:
diff changeset
2342 {
kono
parents:
diff changeset
2343 fmtresult res (dir.len);
kono
parents:
diff changeset
2344 return res;
kono
parents:
diff changeset
2345 }
kono
parents:
diff changeset
2346
kono
parents:
diff changeset
2347 /* Return true if the RESULT of a directive in a call describe by INFO
kono
parents:
diff changeset
2348 should be diagnosed given the AVAILable space in the destination. */
kono
parents:
diff changeset
2349
kono
parents:
diff changeset
2350 static bool
kono
parents:
diff changeset
2351 should_warn_p (const pass_sprintf_length::call_info &info,
kono
parents:
diff changeset
2352 const result_range &avail, const result_range &result)
kono
parents:
diff changeset
2353 {
kono
parents:
diff changeset
2354 if (result.max <= avail.min)
kono
parents:
diff changeset
2355 {
kono
parents:
diff changeset
2356 /* The least amount of space remaining in the destination is big
kono
parents:
diff changeset
2357 enough for the longest output. */
kono
parents:
diff changeset
2358 return false;
kono
parents:
diff changeset
2359 }
kono
parents:
diff changeset
2360
kono
parents:
diff changeset
2361 if (info.bounded)
kono
parents:
diff changeset
2362 {
kono
parents:
diff changeset
2363 if (warn_format_trunc == 1 && result.min <= avail.max
kono
parents:
diff changeset
2364 && info.retval_used ())
kono
parents:
diff changeset
2365 {
kono
parents:
diff changeset
2366 /* The likely amount of space remaining in the destination is big
kono
parents:
diff changeset
2367 enough for the least output and the return value is used. */
kono
parents:
diff changeset
2368 return false;
kono
parents:
diff changeset
2369 }
kono
parents:
diff changeset
2370
kono
parents:
diff changeset
2371 if (warn_format_trunc == 1 && result.likely <= avail.likely
kono
parents:
diff changeset
2372 && !info.retval_used ())
kono
parents:
diff changeset
2373 {
kono
parents:
diff changeset
2374 /* The likely amount of space remaining in the destination is big
kono
parents:
diff changeset
2375 enough for the likely output and the return value is unused. */
kono
parents:
diff changeset
2376 return false;
kono
parents:
diff changeset
2377 }
kono
parents:
diff changeset
2378
kono
parents:
diff changeset
2379 if (warn_format_trunc == 2
kono
parents:
diff changeset
2380 && result.likely <= avail.min
kono
parents:
diff changeset
2381 && (result.max <= avail.min
kono
parents:
diff changeset
2382 || result.max > HOST_WIDE_INT_MAX))
kono
parents:
diff changeset
2383 {
kono
parents:
diff changeset
2384 /* The minimum amount of space remaining in the destination is big
kono
parents:
diff changeset
2385 enough for the longest output. */
kono
parents:
diff changeset
2386 return false;
kono
parents:
diff changeset
2387 }
kono
parents:
diff changeset
2388 }
kono
parents:
diff changeset
2389 else
kono
parents:
diff changeset
2390 {
kono
parents:
diff changeset
2391 if (warn_level == 1 && result.likely <= avail.likely)
kono
parents:
diff changeset
2392 {
kono
parents:
diff changeset
2393 /* The likely amount of space remaining in the destination is big
kono
parents:
diff changeset
2394 enough for the likely output. */
kono
parents:
diff changeset
2395 return false;
kono
parents:
diff changeset
2396 }
kono
parents:
diff changeset
2397
kono
parents:
diff changeset
2398 if (warn_level == 2
kono
parents:
diff changeset
2399 && result.likely <= avail.min
kono
parents:
diff changeset
2400 && (result.max <= avail.min
kono
parents:
diff changeset
2401 || result.max > HOST_WIDE_INT_MAX))
kono
parents:
diff changeset
2402 {
kono
parents:
diff changeset
2403 /* The minimum amount of space remaining in the destination is big
kono
parents:
diff changeset
2404 enough for the longest output. */
kono
parents:
diff changeset
2405 return false;
kono
parents:
diff changeset
2406 }
kono
parents:
diff changeset
2407 }
kono
parents:
diff changeset
2408
kono
parents:
diff changeset
2409 return true;
kono
parents:
diff changeset
2410 }
kono
parents:
diff changeset
2411
kono
parents:
diff changeset
2412 /* At format string location describe by DIRLOC in a call described
kono
parents:
diff changeset
2413 by INFO, issue a warning for a directive DIR whose output may be
kono
parents:
diff changeset
2414 in excess of the available space AVAIL_RANGE in the destination
kono
parents:
diff changeset
2415 given the formatting result FMTRES. This function does nothing
kono
parents:
diff changeset
2416 except decide whether to issue a warning for a possible write
kono
parents:
diff changeset
2417 past the end or truncation and, if so, format the warning.
kono
parents:
diff changeset
2418 Return true if a warning has been issued. */
kono
parents:
diff changeset
2419
kono
parents:
diff changeset
2420 static bool
kono
parents:
diff changeset
2421 maybe_warn (substring_loc &dirloc, location_t argloc,
kono
parents:
diff changeset
2422 const pass_sprintf_length::call_info &info,
kono
parents:
diff changeset
2423 const result_range &avail_range, const result_range &res,
kono
parents:
diff changeset
2424 const directive &dir)
kono
parents:
diff changeset
2425 {
kono
parents:
diff changeset
2426 if (!should_warn_p (info, avail_range, res))
kono
parents:
diff changeset
2427 return false;
kono
parents:
diff changeset
2428
kono
parents:
diff changeset
2429 /* A warning will definitely be issued below. */
kono
parents:
diff changeset
2430
kono
parents:
diff changeset
2431 /* The maximum byte count to reference in the warning. Larger counts
kono
parents:
diff changeset
2432 imply that the upper bound is unknown (and could be anywhere between
kono
parents:
diff changeset
2433 RES.MIN + 1 and SIZE_MAX / 2) are printed as "N or more bytes" rather
kono
parents:
diff changeset
2434 than "between N and X" where X is some huge number. */
kono
parents:
diff changeset
2435 unsigned HOST_WIDE_INT maxbytes = target_dir_max ();
kono
parents:
diff changeset
2436
kono
parents:
diff changeset
2437 /* True when there is enough room in the destination for the least
kono
parents:
diff changeset
2438 amount of a directive's output but not enough for its likely or
kono
parents:
diff changeset
2439 maximum output. */
kono
parents:
diff changeset
2440 bool maybe = (res.min <= avail_range.max
kono
parents:
diff changeset
2441 && (avail_range.min < res.likely
kono
parents:
diff changeset
2442 || (res.max < HOST_WIDE_INT_MAX
kono
parents:
diff changeset
2443 && avail_range.min < res.max)));
kono
parents:
diff changeset
2444
kono
parents:
diff changeset
2445 /* Buffer for the directive in the host character set (used when
kono
parents:
diff changeset
2446 the source character set is different). */
kono
parents:
diff changeset
2447 char hostdir[32];
kono
parents:
diff changeset
2448
kono
parents:
diff changeset
2449 if (avail_range.min == avail_range.max)
kono
parents:
diff changeset
2450 {
kono
parents:
diff changeset
2451 /* The size of the destination region is exact. */
kono
parents:
diff changeset
2452 unsigned HOST_WIDE_INT navail = avail_range.max;
kono
parents:
diff changeset
2453
kono
parents:
diff changeset
2454 if (target_to_host (*dir.beg) != '%')
kono
parents:
diff changeset
2455 {
kono
parents:
diff changeset
2456 /* For plain character directives (i.e., the format string itself)
kono
parents:
diff changeset
2457 but not others, point the caret at the first character that's
kono
parents:
diff changeset
2458 past the end of the destination. */
kono
parents:
diff changeset
2459 dirloc.set_caret_index (dirloc.get_caret_idx () + navail);
kono
parents:
diff changeset
2460 }
kono
parents:
diff changeset
2461
kono
parents:
diff changeset
2462 if (*dir.beg == '\0')
kono
parents:
diff changeset
2463 {
kono
parents:
diff changeset
2464 /* This is the terminating nul. */
kono
parents:
diff changeset
2465 gcc_assert (res.min == 1 && res.min == res.max);
kono
parents:
diff changeset
2466
kono
parents:
diff changeset
2467 const char *fmtstr
kono
parents:
diff changeset
2468 = (info.bounded
kono
parents:
diff changeset
2469 ? (maybe
kono
parents:
diff changeset
2470 ? G_("%qE output may be truncated before the last format "
kono
parents:
diff changeset
2471 "character")
kono
parents:
diff changeset
2472 : G_("%qE output truncated before the last format character"))
kono
parents:
diff changeset
2473 : (maybe
kono
parents:
diff changeset
2474 ? G_("%qE may write a terminating nul past the end "
kono
parents:
diff changeset
2475 "of the destination")
kono
parents:
diff changeset
2476 : G_("%qE writing a terminating nul past the end "
kono
parents:
diff changeset
2477 "of the destination")));
kono
parents:
diff changeset
2478
kono
parents:
diff changeset
2479 return fmtwarn (dirloc, UNKNOWN_LOCATION, NULL, info.warnopt (),
kono
parents:
diff changeset
2480 fmtstr, info.func);
kono
parents:
diff changeset
2481 }
kono
parents:
diff changeset
2482
kono
parents:
diff changeset
2483 if (res.min == res.max)
kono
parents:
diff changeset
2484 {
kono
parents:
diff changeset
2485 const char* fmtstr
kono
parents:
diff changeset
2486 = (res.min == 1
kono
parents:
diff changeset
2487 ? (info.bounded
kono
parents:
diff changeset
2488 ? (maybe
kono
parents:
diff changeset
2489 ? G_("%<%.*s%> directive output may be truncated writing "
kono
parents:
diff changeset
2490 "%wu byte into a region of size %wu")
kono
parents:
diff changeset
2491 : G_("%<%.*s%> directive output truncated writing "
kono
parents:
diff changeset
2492 "%wu byte into a region of size %wu"))
kono
parents:
diff changeset
2493 : G_("%<%.*s%> directive writing %wu byte "
kono
parents:
diff changeset
2494 "into a region of size %wu"))
kono
parents:
diff changeset
2495 : (info.bounded
kono
parents:
diff changeset
2496 ? (maybe
kono
parents:
diff changeset
2497 ? G_("%<%.*s%> directive output may be truncated writing "
kono
parents:
diff changeset
2498 "%wu bytes into a region of size %wu")
kono
parents:
diff changeset
2499 : G_("%<%.*s%> directive output truncated writing "
kono
parents:
diff changeset
2500 "%wu bytes into a region of size %wu"))
kono
parents:
diff changeset
2501 : G_("%<%.*s%> directive writing %wu bytes "
kono
parents:
diff changeset
2502 "into a region of size %wu")));
kono
parents:
diff changeset
2503 return fmtwarn (dirloc, argloc, NULL,
kono
parents:
diff changeset
2504 info.warnopt (), fmtstr, dir.len,
kono
parents:
diff changeset
2505 target_to_host (hostdir, sizeof hostdir, dir.beg),
kono
parents:
diff changeset
2506 res.min, navail);
kono
parents:
diff changeset
2507 }
kono
parents:
diff changeset
2508
kono
parents:
diff changeset
2509 if (res.min == 0 && res.max < maxbytes)
kono
parents:
diff changeset
2510 {
kono
parents:
diff changeset
2511 const char* fmtstr
kono
parents:
diff changeset
2512 = (info.bounded
kono
parents:
diff changeset
2513 ? (maybe
kono
parents:
diff changeset
2514 ? G_("%<%.*s%> directive output may be truncated writing "
kono
parents:
diff changeset
2515 "up to %wu bytes into a region of size %wu")
kono
parents:
diff changeset
2516 : G_("%<%.*s%> directive output truncated writing "
kono
parents:
diff changeset
2517 "up to %wu bytes into a region of size %wu"))
kono
parents:
diff changeset
2518 : G_("%<%.*s%> directive writing up to %wu bytes "
kono
parents:
diff changeset
2519 "into a region of size %wu"));
kono
parents:
diff changeset
2520 return fmtwarn (dirloc, argloc, NULL,
kono
parents:
diff changeset
2521 info.warnopt (), fmtstr, dir.len,
kono
parents:
diff changeset
2522 target_to_host (hostdir, sizeof hostdir, dir.beg),
kono
parents:
diff changeset
2523 res.max, navail);
kono
parents:
diff changeset
2524 }
kono
parents:
diff changeset
2525
kono
parents:
diff changeset
2526 if (res.min == 0 && maxbytes <= res.max)
kono
parents:
diff changeset
2527 {
kono
parents:
diff changeset
2528 /* This is a special case to avoid issuing the potentially
kono
parents:
diff changeset
2529 confusing warning:
kono
parents:
diff changeset
2530 writing 0 or more bytes into a region of size 0. */
kono
parents:
diff changeset
2531 const char* fmtstr
kono
parents:
diff changeset
2532 = (info.bounded
kono
parents:
diff changeset
2533 ? (maybe
kono
parents:
diff changeset
2534 ? G_("%<%.*s%> directive output may be truncated writing "
kono
parents:
diff changeset
2535 "likely %wu or more bytes into a region of size %wu")
kono
parents:
diff changeset
2536 : G_("%<%.*s%> directive output truncated writing "
kono
parents:
diff changeset
2537 "likely %wu or more bytes into a region of size %wu"))
kono
parents:
diff changeset
2538 : G_("%<%.*s%> directive writing likely %wu or more bytes "
kono
parents:
diff changeset
2539 "into a region of size %wu"));
kono
parents:
diff changeset
2540 return fmtwarn (dirloc, argloc, NULL,
kono
parents:
diff changeset
2541 info.warnopt (), fmtstr, dir.len,
kono
parents:
diff changeset
2542 target_to_host (hostdir, sizeof hostdir, dir.beg),
kono
parents:
diff changeset
2543 res.likely, navail);
kono
parents:
diff changeset
2544 }
kono
parents:
diff changeset
2545
kono
parents:
diff changeset
2546 if (res.max < maxbytes)
kono
parents:
diff changeset
2547 {
kono
parents:
diff changeset
2548 const char* fmtstr
kono
parents:
diff changeset
2549 = (info.bounded
kono
parents:
diff changeset
2550 ? (maybe
kono
parents:
diff changeset
2551 ? G_("%<%.*s%> directive output may be truncated writing "
kono
parents:
diff changeset
2552 "between %wu and %wu bytes into a region of size %wu")
kono
parents:
diff changeset
2553 : G_("%<%.*s%> directive output truncated writing "
kono
parents:
diff changeset
2554 "between %wu and %wu bytes into a region of size %wu"))
kono
parents:
diff changeset
2555 : G_("%<%.*s%> directive writing between %wu and "
kono
parents:
diff changeset
2556 "%wu bytes into a region of size %wu"));
kono
parents:
diff changeset
2557 return fmtwarn (dirloc, argloc, NULL,
kono
parents:
diff changeset
2558 info.warnopt (), fmtstr, dir.len,
kono
parents:
diff changeset
2559 target_to_host (hostdir, sizeof hostdir, dir.beg),
kono
parents:
diff changeset
2560 res.min, res.max, navail);
kono
parents:
diff changeset
2561 }
kono
parents:
diff changeset
2562
kono
parents:
diff changeset
2563 const char* fmtstr
kono
parents:
diff changeset
2564 = (info.bounded
kono
parents:
diff changeset
2565 ? (maybe
kono
parents:
diff changeset
2566 ? G_("%<%.*s%> directive output may be truncated writing "
kono
parents:
diff changeset
2567 "%wu or more bytes into a region of size %wu")
kono
parents:
diff changeset
2568 : G_("%<%.*s%> directive output truncated writing "
kono
parents:
diff changeset
2569 "%wu or more bytes into a region of size %wu"))
kono
parents:
diff changeset
2570 : G_("%<%.*s%> directive writing %wu or more bytes "
kono
parents:
diff changeset
2571 "into a region of size %wu"));
kono
parents:
diff changeset
2572 return fmtwarn (dirloc, argloc, NULL,
kono
parents:
diff changeset
2573 info.warnopt (), fmtstr, dir.len,
kono
parents:
diff changeset
2574 target_to_host (hostdir, sizeof hostdir, dir.beg),
kono
parents:
diff changeset
2575 res.min, navail);
kono
parents:
diff changeset
2576 }
kono
parents:
diff changeset
2577
kono
parents:
diff changeset
2578 /* The size of the destination region is a range. */
kono
parents:
diff changeset
2579
kono
parents:
diff changeset
2580 if (target_to_host (*dir.beg) != '%')
kono
parents:
diff changeset
2581 {
kono
parents:
diff changeset
2582 unsigned HOST_WIDE_INT navail = avail_range.max;
kono
parents:
diff changeset
2583
kono
parents:
diff changeset
2584 /* For plain character directives (i.e., the format string itself)
kono
parents:
diff changeset
2585 but not others, point the caret at the first character that's
kono
parents:
diff changeset
2586 past the end of the destination. */
kono
parents:
diff changeset
2587 dirloc.set_caret_index (dirloc.get_caret_idx () + navail);
kono
parents:
diff changeset
2588 }
kono
parents:
diff changeset
2589
kono
parents:
diff changeset
2590 if (*dir.beg == '\0')
kono
parents:
diff changeset
2591 {
kono
parents:
diff changeset
2592 gcc_assert (res.min == 1 && res.min == res.max);
kono
parents:
diff changeset
2593
kono
parents:
diff changeset
2594 const char *fmtstr
kono
parents:
diff changeset
2595 = (info.bounded
kono
parents:
diff changeset
2596 ? (maybe
kono
parents:
diff changeset
2597 ? G_("%qE output may be truncated before the last format "
kono
parents:
diff changeset
2598 "character")
kono
parents:
diff changeset
2599 : G_("%qE output truncated before the last format character"))
kono
parents:
diff changeset
2600 : (maybe
kono
parents:
diff changeset
2601 ? G_("%qE may write a terminating nul past the end "
kono
parents:
diff changeset
2602 "of the destination")
kono
parents:
diff changeset
2603 : G_("%qE writing a terminating nul past the end "
kono
parents:
diff changeset
2604 "of the destination")));
kono
parents:
diff changeset
2605
kono
parents:
diff changeset
2606 return fmtwarn (dirloc, UNKNOWN_LOCATION, NULL, info.warnopt (), fmtstr,
kono
parents:
diff changeset
2607 info.func);
kono
parents:
diff changeset
2608 }
kono
parents:
diff changeset
2609
kono
parents:
diff changeset
2610 if (res.min == res.max)
kono
parents:
diff changeset
2611 {
kono
parents:
diff changeset
2612 const char* fmtstr
kono
parents:
diff changeset
2613 = (res.min == 1
kono
parents:
diff changeset
2614 ? (info.bounded
kono
parents:
diff changeset
2615 ? (maybe
kono
parents:
diff changeset
2616 ? G_("%<%.*s%> directive output may be truncated writing "
kono
parents:
diff changeset
2617 "%wu byte into a region of size between %wu and %wu")
kono
parents:
diff changeset
2618 : G_("%<%.*s%> directive output truncated writing "
kono
parents:
diff changeset
2619 "%wu byte into a region of size between %wu and %wu"))
kono
parents:
diff changeset
2620 : G_("%<%.*s%> directive writing %wu byte "
kono
parents:
diff changeset
2621 "into a region of size between %wu and %wu"))
kono
parents:
diff changeset
2622 : (info.bounded
kono
parents:
diff changeset
2623 ? (maybe
kono
parents:
diff changeset
2624 ? G_("%<%.*s%> directive output may be truncated writing "
kono
parents:
diff changeset
2625 "%wu bytes into a region of size between %wu and %wu")
kono
parents:
diff changeset
2626 : G_("%<%.*s%> directive output truncated writing "
kono
parents:
diff changeset
2627 "%wu bytes into a region of size between %wu and %wu"))
kono
parents:
diff changeset
2628 : G_("%<%.*s%> directive writing %wu bytes "
kono
parents:
diff changeset
2629 "into a region of size between %wu and %wu")));
kono
parents:
diff changeset
2630
kono
parents:
diff changeset
2631 return fmtwarn (dirloc, argloc, NULL,
kono
parents:
diff changeset
2632 info.warnopt (), fmtstr, dir.len,
kono
parents:
diff changeset
2633 target_to_host (hostdir, sizeof hostdir, dir.beg),
kono
parents:
diff changeset
2634 res.min, avail_range.min, avail_range.max);
kono
parents:
diff changeset
2635 }
kono
parents:
diff changeset
2636
kono
parents:
diff changeset
2637 if (res.min == 0 && res.max < maxbytes)
kono
parents:
diff changeset
2638 {
kono
parents:
diff changeset
2639 const char* fmtstr
kono
parents:
diff changeset
2640 = (info.bounded
kono
parents:
diff changeset
2641 ? (maybe
kono
parents:
diff changeset
2642 ? G_("%<%.*s%> directive output may be truncated writing "
kono
parents:
diff changeset
2643 "up to %wu bytes into a region of size between "
kono
parents:
diff changeset
2644 "%wu and %wu")
kono
parents:
diff changeset
2645 : G_("%<%.*s%> directive output truncated writing "
kono
parents:
diff changeset
2646 "up to %wu bytes into a region of size between "
kono
parents:
diff changeset
2647 "%wu and %wu"))
kono
parents:
diff changeset
2648 : G_("%<%.*s%> directive writing up to %wu bytes "
kono
parents:
diff changeset
2649 "into a region of size between %wu and %wu"));
kono
parents:
diff changeset
2650 return fmtwarn (dirloc, argloc, NULL,
kono
parents:
diff changeset
2651 info.warnopt (), fmtstr, dir.len,
kono
parents:
diff changeset
2652 target_to_host (hostdir, sizeof hostdir, dir.beg),
kono
parents:
diff changeset
2653 res.max, avail_range.min, avail_range.max);
kono
parents:
diff changeset
2654 }
kono
parents:
diff changeset
2655
kono
parents:
diff changeset
2656 if (res.min == 0 && maxbytes <= res.max)
kono
parents:
diff changeset
2657 {
kono
parents:
diff changeset
2658 /* This is a special case to avoid issuing the potentially confusing
kono
parents:
diff changeset
2659 warning:
kono
parents:
diff changeset
2660 writing 0 or more bytes into a region of size between 0 and N. */
kono
parents:
diff changeset
2661 const char* fmtstr
kono
parents:
diff changeset
2662 = (info.bounded
kono
parents:
diff changeset
2663 ? (maybe
kono
parents:
diff changeset
2664 ? G_("%<%.*s%> directive output may be truncated writing "
kono
parents:
diff changeset
2665 "likely %wu or more bytes into a region of size between "
kono
parents:
diff changeset
2666 "%wu and %wu")
kono
parents:
diff changeset
2667 : G_("%<%.*s%> directive output truncated writing likely "
kono
parents:
diff changeset
2668 "%wu or more bytes into a region of size between "
kono
parents:
diff changeset
2669 "%wu and %wu"))
kono
parents:
diff changeset
2670 : G_("%<%.*s%> directive writing likely %wu or more bytes "
kono
parents:
diff changeset
2671 "into a region of size between %wu and %wu"));
kono
parents:
diff changeset
2672 return fmtwarn (dirloc, argloc, NULL,
kono
parents:
diff changeset
2673 info.warnopt (), fmtstr, dir.len,
kono
parents:
diff changeset
2674 target_to_host (hostdir, sizeof hostdir, dir.beg),
kono
parents:
diff changeset
2675 res.likely, avail_range.min, avail_range.max);
kono
parents:
diff changeset
2676 }
kono
parents:
diff changeset
2677
kono
parents:
diff changeset
2678 if (res.max < maxbytes)
kono
parents:
diff changeset
2679 {
kono
parents:
diff changeset
2680 const char* fmtstr
kono
parents:
diff changeset
2681 = (info.bounded
kono
parents:
diff changeset
2682 ? (maybe
kono
parents:
diff changeset
2683 ? G_("%<%.*s%> directive output may be truncated writing "
kono
parents:
diff changeset
2684 "between %wu and %wu bytes into a region of size "
kono
parents:
diff changeset
2685 "between %wu and %wu")
kono
parents:
diff changeset
2686 : G_("%<%.*s%> directive output truncated writing "
kono
parents:
diff changeset
2687 "between %wu and %wu bytes into a region of size "
kono
parents:
diff changeset
2688 "between %wu and %wu"))
kono
parents:
diff changeset
2689 : G_("%<%.*s%> directive writing between %wu and "
kono
parents:
diff changeset
2690 "%wu bytes into a region of size between %wu and %wu"));
kono
parents:
diff changeset
2691 return fmtwarn (dirloc, argloc, NULL,
kono
parents:
diff changeset
2692 info.warnopt (), fmtstr, dir.len,
kono
parents:
diff changeset
2693 target_to_host (hostdir, sizeof hostdir, dir.beg),
kono
parents:
diff changeset
2694 res.min, res.max, avail_range.min, avail_range.max);
kono
parents:
diff changeset
2695 }
kono
parents:
diff changeset
2696
kono
parents:
diff changeset
2697 const char* fmtstr
kono
parents:
diff changeset
2698 = (info.bounded
kono
parents:
diff changeset
2699 ? (maybe
kono
parents:
diff changeset
2700 ? G_("%<%.*s%> directive output may be truncated writing "
kono
parents:
diff changeset
2701 "%wu or more bytes into a region of size between "
kono
parents:
diff changeset
2702 "%wu and %wu")
kono
parents:
diff changeset
2703 : G_("%<%.*s%> directive output truncated writing "
kono
parents:
diff changeset
2704 "%wu or more bytes into a region of size between "
kono
parents:
diff changeset
2705 "%wu and %wu"))
kono
parents:
diff changeset
2706 : G_("%<%.*s%> directive writing %wu or more bytes "
kono
parents:
diff changeset
2707 "into a region of size between %wu and %wu"));
kono
parents:
diff changeset
2708 return fmtwarn (dirloc, argloc, NULL,
kono
parents:
diff changeset
2709 info.warnopt (), fmtstr, dir.len,
kono
parents:
diff changeset
2710 target_to_host (hostdir, sizeof hostdir, dir.beg),
kono
parents:
diff changeset
2711 res.min, avail_range.min, avail_range.max);
kono
parents:
diff changeset
2712 }
kono
parents:
diff changeset
2713
kono
parents:
diff changeset
2714 /* Compute the length of the output resulting from the directive DIR
kono
parents:
diff changeset
2715 in a call described by INFO and update the overall result of the call
kono
parents:
diff changeset
2716 in *RES. Return true if the directive has been handled. */
kono
parents:
diff changeset
2717
kono
parents:
diff changeset
2718 static bool
kono
parents:
diff changeset
2719 format_directive (const pass_sprintf_length::call_info &info,
kono
parents:
diff changeset
2720 format_result *res, const directive &dir)
kono
parents:
diff changeset
2721 {
kono
parents:
diff changeset
2722 /* Offset of the beginning of the directive from the beginning
kono
parents:
diff changeset
2723 of the format string. */
kono
parents:
diff changeset
2724 size_t offset = dir.beg - info.fmtstr;
kono
parents:
diff changeset
2725 size_t start = offset;
kono
parents:
diff changeset
2726 size_t length = offset + dir.len - !!dir.len;
kono
parents:
diff changeset
2727
kono
parents:
diff changeset
2728 /* Create a location for the whole directive from the % to the format
kono
parents:
diff changeset
2729 specifier. */
kono
parents:
diff changeset
2730 substring_loc dirloc (info.fmtloc, TREE_TYPE (info.format),
kono
parents:
diff changeset
2731 offset, start, length);
kono
parents:
diff changeset
2732
kono
parents:
diff changeset
2733 /* Also get the location of the argument if possible.
kono
parents:
diff changeset
2734 This doesn't work for integer literals or function calls. */
kono
parents:
diff changeset
2735 location_t argloc = UNKNOWN_LOCATION;
kono
parents:
diff changeset
2736 if (dir.arg)
kono
parents:
diff changeset
2737 argloc = EXPR_LOCATION (dir.arg);
kono
parents:
diff changeset
2738
kono
parents:
diff changeset
2739 /* Bail when there is no function to compute the output length,
kono
parents:
diff changeset
2740 or when minimum length checking has been disabled. */
kono
parents:
diff changeset
2741 if (!dir.fmtfunc || res->range.min >= HOST_WIDE_INT_MAX)
kono
parents:
diff changeset
2742 return false;
kono
parents:
diff changeset
2743
kono
parents:
diff changeset
2744 /* Compute the range of lengths of the formatted output. */
kono
parents:
diff changeset
2745 fmtresult fmtres = dir.fmtfunc (dir, dir.arg);
kono
parents:
diff changeset
2746
kono
parents:
diff changeset
2747 /* Record whether the output of all directives is known to be
kono
parents:
diff changeset
2748 bounded by some maximum, implying that their arguments are
kono
parents:
diff changeset
2749 either known exactly or determined to be in a known range
kono
parents:
diff changeset
2750 or, for strings, limited by the upper bounds of the arrays
kono
parents:
diff changeset
2751 they refer to. */
kono
parents:
diff changeset
2752 res->knownrange &= fmtres.knownrange;
kono
parents:
diff changeset
2753
kono
parents:
diff changeset
2754 if (!fmtres.knownrange)
kono
parents:
diff changeset
2755 {
kono
parents:
diff changeset
2756 /* Only when the range is known, check it against the host value
kono
parents:
diff changeset
2757 of INT_MAX + (the number of bytes of the "%.*Lf" directive with
kono
parents:
diff changeset
2758 INT_MAX precision, which is the longest possible output of any
kono
parents:
diff changeset
2759 single directive). That's the largest valid byte count (though
kono
parents:
diff changeset
2760 not valid call to a printf-like function because it can never
kono
parents:
diff changeset
2761 return such a count). Otherwise, the range doesn't correspond
kono
parents:
diff changeset
2762 to known values of the argument. */
kono
parents:
diff changeset
2763 if (fmtres.range.max > target_dir_max ())
kono
parents:
diff changeset
2764 {
kono
parents:
diff changeset
2765 /* Normalize the MAX counter to avoid having to deal with it
kono
parents:
diff changeset
2766 later. The counter can be less than HOST_WIDE_INT_M1U
kono
parents:
diff changeset
2767 when compiling for an ILP32 target on an LP64 host. */
kono
parents:
diff changeset
2768 fmtres.range.max = HOST_WIDE_INT_M1U;
kono
parents:
diff changeset
2769 /* Disable exact and maximum length checking after a failure
kono
parents:
diff changeset
2770 to determine the maximum number of characters (for example
kono
parents:
diff changeset
2771 for wide characters or wide character strings) but continue
kono
parents:
diff changeset
2772 tracking the minimum number of characters. */
kono
parents:
diff changeset
2773 res->range.max = HOST_WIDE_INT_M1U;
kono
parents:
diff changeset
2774 }
kono
parents:
diff changeset
2775
kono
parents:
diff changeset
2776 if (fmtres.range.min > target_dir_max ())
kono
parents:
diff changeset
2777 {
kono
parents:
diff changeset
2778 /* Disable exact length checking after a failure to determine
kono
parents:
diff changeset
2779 even the minimum number of characters (it shouldn't happen
kono
parents:
diff changeset
2780 except in an error) but keep tracking the minimum and maximum
kono
parents:
diff changeset
2781 number of characters. */
kono
parents:
diff changeset
2782 return true;
kono
parents:
diff changeset
2783 }
kono
parents:
diff changeset
2784 }
kono
parents:
diff changeset
2785
kono
parents:
diff changeset
2786 /* Buffer for the directive in the host character set (used when
kono
parents:
diff changeset
2787 the source character set is different). */
kono
parents:
diff changeset
2788 char hostdir[32];
kono
parents:
diff changeset
2789
kono
parents:
diff changeset
2790 int dirlen = dir.len;
kono
parents:
diff changeset
2791
kono
parents:
diff changeset
2792 if (fmtres.nullp)
kono
parents:
diff changeset
2793 {
kono
parents:
diff changeset
2794 fmtwarn (dirloc, argloc, NULL, info.warnopt (),
kono
parents:
diff changeset
2795 "%<%.*s%> directive argument is null",
kono
parents:
diff changeset
2796 dirlen, target_to_host (hostdir, sizeof hostdir, dir.beg));
kono
parents:
diff changeset
2797
kono
parents:
diff changeset
2798 /* Don't bother processing the rest of the format string. */
kono
parents:
diff changeset
2799 res->warned = true;
kono
parents:
diff changeset
2800 res->range.min = HOST_WIDE_INT_M1U;
kono
parents:
diff changeset
2801 res->range.max = HOST_WIDE_INT_M1U;
kono
parents:
diff changeset
2802 return false;
kono
parents:
diff changeset
2803 }
kono
parents:
diff changeset
2804
kono
parents:
diff changeset
2805 /* Compute the number of available bytes in the destination. There
kono
parents:
diff changeset
2806 must always be at least one byte of space for the terminating
kono
parents:
diff changeset
2807 NUL that's appended after the format string has been processed. */
kono
parents:
diff changeset
2808 result_range avail_range = bytes_remaining (info.objsize, *res);
kono
parents:
diff changeset
2809
kono
parents:
diff changeset
2810 bool warned = res->warned;
kono
parents:
diff changeset
2811
kono
parents:
diff changeset
2812 if (!warned)
kono
parents:
diff changeset
2813 warned = maybe_warn (dirloc, argloc, info, avail_range,
kono
parents:
diff changeset
2814 fmtres.range, dir);
kono
parents:
diff changeset
2815
kono
parents:
diff changeset
2816 /* Bump up the total maximum if it isn't too big. */
kono
parents:
diff changeset
2817 if (res->range.max < HOST_WIDE_INT_MAX
kono
parents:
diff changeset
2818 && fmtres.range.max < HOST_WIDE_INT_MAX)
kono
parents:
diff changeset
2819 res->range.max += fmtres.range.max;
kono
parents:
diff changeset
2820
kono
parents:
diff changeset
2821 /* Raise the total unlikely maximum by the larger of the maximum
kono
parents:
diff changeset
2822 and the unlikely maximum. */
kono
parents:
diff changeset
2823 unsigned HOST_WIDE_INT save = res->range.unlikely;
kono
parents:
diff changeset
2824 if (fmtres.range.max < fmtres.range.unlikely)
kono
parents:
diff changeset
2825 res->range.unlikely += fmtres.range.unlikely;
kono
parents:
diff changeset
2826 else
kono
parents:
diff changeset
2827 res->range.unlikely += fmtres.range.max;
kono
parents:
diff changeset
2828
kono
parents:
diff changeset
2829 if (res->range.unlikely < save)
kono
parents:
diff changeset
2830 res->range.unlikely = HOST_WIDE_INT_M1U;
kono
parents:
diff changeset
2831
kono
parents:
diff changeset
2832 res->range.min += fmtres.range.min;
kono
parents:
diff changeset
2833 res->range.likely += fmtres.range.likely;
kono
parents:
diff changeset
2834
kono
parents:
diff changeset
2835 /* Has the minimum directive output length exceeded the maximum
kono
parents:
diff changeset
2836 of 4095 bytes required to be supported? */
kono
parents:
diff changeset
2837 bool minunder4k = fmtres.range.min < 4096;
kono
parents:
diff changeset
2838 bool maxunder4k = fmtres.range.max < 4096;
kono
parents:
diff changeset
2839 /* Clear UNDER4K in the overall result if the maximum has exceeded
kono
parents:
diff changeset
2840 the 4k (this is necessary to avoid the return valuye optimization
kono
parents:
diff changeset
2841 that may not be safe in the maximum case). */
kono
parents:
diff changeset
2842 if (!maxunder4k)
kono
parents:
diff changeset
2843 res->under4k = false;
kono
parents:
diff changeset
2844
kono
parents:
diff changeset
2845 if (!warned
kono
parents:
diff changeset
2846 /* Only warn at level 2. */
kono
parents:
diff changeset
2847 && 1 < warn_level
kono
parents:
diff changeset
2848 && (!minunder4k
kono
parents:
diff changeset
2849 || (!maxunder4k && fmtres.range.max < HOST_WIDE_INT_MAX)))
kono
parents:
diff changeset
2850 {
kono
parents:
diff changeset
2851 /* The directive output may be longer than the maximum required
kono
parents:
diff changeset
2852 to be handled by an implementation according to 7.21.6.1, p15
kono
parents:
diff changeset
2853 of C11. Warn on this only at level 2 but remember this and
kono
parents:
diff changeset
2854 prevent folding the return value when done. This allows for
kono
parents:
diff changeset
2855 the possibility of the actual libc call failing due to ENOMEM
kono
parents:
diff changeset
2856 (like Glibc does under some conditions). */
kono
parents:
diff changeset
2857
kono
parents:
diff changeset
2858 if (fmtres.range.min == fmtres.range.max)
kono
parents:
diff changeset
2859 warned = fmtwarn (dirloc, argloc, NULL,
kono
parents:
diff changeset
2860 info.warnopt (),
kono
parents:
diff changeset
2861 "%<%.*s%> directive output of %wu bytes exceeds "
kono
parents:
diff changeset
2862 "minimum required size of 4095",
kono
parents:
diff changeset
2863 dirlen,
kono
parents:
diff changeset
2864 target_to_host (hostdir, sizeof hostdir, dir.beg),
kono
parents:
diff changeset
2865 fmtres.range.min);
kono
parents:
diff changeset
2866 else
kono
parents:
diff changeset
2867 {
kono
parents:
diff changeset
2868 const char *fmtstr
kono
parents:
diff changeset
2869 = (minunder4k
kono
parents:
diff changeset
2870 ? G_("%<%.*s%> directive output between %wu and %wu "
kono
parents:
diff changeset
2871 "bytes may exceed minimum required size of 4095")
kono
parents:
diff changeset
2872 : G_("%<%.*s%> directive output between %wu and %wu "
kono
parents:
diff changeset
2873 "bytes exceeds minimum required size of 4095"));
kono
parents:
diff changeset
2874
kono
parents:
diff changeset
2875 warned = fmtwarn (dirloc, argloc, NULL,
kono
parents:
diff changeset
2876 info.warnopt (), fmtstr, dirlen,
kono
parents:
diff changeset
2877 target_to_host (hostdir, sizeof hostdir, dir.beg),
kono
parents:
diff changeset
2878 fmtres.range.min, fmtres.range.max);
kono
parents:
diff changeset
2879 }
kono
parents:
diff changeset
2880 }
kono
parents:
diff changeset
2881
kono
parents:
diff changeset
2882 /* Has the likely and maximum directive output exceeded INT_MAX? */
kono
parents:
diff changeset
2883 bool likelyximax = *dir.beg && res->range.likely > target_int_max ();
kono
parents:
diff changeset
2884 /* Don't consider the maximum to be in excess when it's the result
kono
parents:
diff changeset
2885 of a string of unknown length (i.e., whose maximum has been set
kono
parents:
diff changeset
2886 to be greater than or equal to HOST_WIDE_INT_MAX. */
kono
parents:
diff changeset
2887 bool maxximax = (*dir.beg
kono
parents:
diff changeset
2888 && res->range.max > target_int_max ()
kono
parents:
diff changeset
2889 && res->range.max < HOST_WIDE_INT_MAX);
kono
parents:
diff changeset
2890
kono
parents:
diff changeset
2891 if (!warned
kono
parents:
diff changeset
2892 /* Warn for the likely output size at level 1. */
kono
parents:
diff changeset
2893 && (likelyximax
kono
parents:
diff changeset
2894 /* But only warn for the maximum at level 2. */
kono
parents:
diff changeset
2895 || (1 < warn_level
kono
parents:
diff changeset
2896 && maxximax
kono
parents:
diff changeset
2897 && fmtres.range.max < HOST_WIDE_INT_MAX)))
kono
parents:
diff changeset
2898 {
kono
parents:
diff changeset
2899 /* The directive output causes the total length of output
kono
parents:
diff changeset
2900 to exceed INT_MAX bytes. */
kono
parents:
diff changeset
2901
kono
parents:
diff changeset
2902 if (fmtres.range.min == fmtres.range.max)
kono
parents:
diff changeset
2903 warned = fmtwarn (dirloc, argloc, NULL, info.warnopt (),
kono
parents:
diff changeset
2904 "%<%.*s%> directive output of %wu bytes causes "
kono
parents:
diff changeset
2905 "result to exceed %<INT_MAX%>",
kono
parents:
diff changeset
2906 dirlen,
kono
parents:
diff changeset
2907 target_to_host (hostdir, sizeof hostdir, dir.beg),
kono
parents:
diff changeset
2908 fmtres.range.min);
kono
parents:
diff changeset
2909 else
kono
parents:
diff changeset
2910 {
kono
parents:
diff changeset
2911 const char *fmtstr
kono
parents:
diff changeset
2912 = (fmtres.range.min > target_int_max ()
kono
parents:
diff changeset
2913 ? G_ ("%<%.*s%> directive output between %wu and %wu "
kono
parents:
diff changeset
2914 "bytes causes result to exceed %<INT_MAX%>")
kono
parents:
diff changeset
2915 : G_ ("%<%.*s%> directive output between %wu and %wu "
kono
parents:
diff changeset
2916 "bytes may cause result to exceed %<INT_MAX%>"));
kono
parents:
diff changeset
2917 warned = fmtwarn (dirloc, argloc, NULL,
kono
parents:
diff changeset
2918 info.warnopt (), fmtstr, dirlen,
kono
parents:
diff changeset
2919 target_to_host (hostdir, sizeof hostdir, dir.beg),
kono
parents:
diff changeset
2920 fmtres.range.min, fmtres.range.max);
kono
parents:
diff changeset
2921 }
kono
parents:
diff changeset
2922 }
kono
parents:
diff changeset
2923
kono
parents:
diff changeset
2924 if (warned && fmtres.range.min < fmtres.range.likely
kono
parents:
diff changeset
2925 && fmtres.range.likely < fmtres.range.max)
kono
parents:
diff changeset
2926 {
kono
parents:
diff changeset
2927 inform (info.fmtloc,
kono
parents:
diff changeset
2928 (1 == fmtres.range.likely
kono
parents:
diff changeset
2929 ? G_("assuming directive output of %wu byte")
kono
parents:
diff changeset
2930 : G_("assuming directive output of %wu bytes")),
kono
parents:
diff changeset
2931 fmtres.range.likely);
kono
parents:
diff changeset
2932 }
kono
parents:
diff changeset
2933
kono
parents:
diff changeset
2934 if (warned && fmtres.argmin)
kono
parents:
diff changeset
2935 {
kono
parents:
diff changeset
2936 if (fmtres.argmin == fmtres.argmax)
kono
parents:
diff changeset
2937 inform (info.fmtloc, "directive argument %qE", fmtres.argmin);
kono
parents:
diff changeset
2938 else if (fmtres.knownrange)
kono
parents:
diff changeset
2939 inform (info.fmtloc, "directive argument in the range [%E, %E]",
kono
parents:
diff changeset
2940 fmtres.argmin, fmtres.argmax);
kono
parents:
diff changeset
2941 else
kono
parents:
diff changeset
2942 inform (info.fmtloc,
kono
parents:
diff changeset
2943 "using the range [%E, %E] for directive argument",
kono
parents:
diff changeset
2944 fmtres.argmin, fmtres.argmax);
kono
parents:
diff changeset
2945 }
kono
parents:
diff changeset
2946
kono
parents:
diff changeset
2947 res->warned |= warned;
kono
parents:
diff changeset
2948
kono
parents:
diff changeset
2949 if (!dir.beg[0] && res->warned && info.objsize < HOST_WIDE_INT_MAX)
kono
parents:
diff changeset
2950 {
kono
parents:
diff changeset
2951 /* If a warning has been issued for buffer overflow or truncation
kono
parents:
diff changeset
2952 (but not otherwise) help the user figure out how big a buffer
kono
parents:
diff changeset
2953 they need. */
kono
parents:
diff changeset
2954
kono
parents:
diff changeset
2955 location_t callloc = gimple_location (info.callstmt);
kono
parents:
diff changeset
2956
kono
parents:
diff changeset
2957 unsigned HOST_WIDE_INT min = res->range.min;
kono
parents:
diff changeset
2958 unsigned HOST_WIDE_INT max = res->range.max;
kono
parents:
diff changeset
2959
kono
parents:
diff changeset
2960 if (min == max)
kono
parents:
diff changeset
2961 inform (callloc,
kono
parents:
diff changeset
2962 (min == 1
kono
parents:
diff changeset
2963 ? G_("%qE output %wu byte into a destination of size %wu")
kono
parents:
diff changeset
2964 : G_("%qE output %wu bytes into a destination of size %wu")),
kono
parents:
diff changeset
2965 info.func, min, info.objsize);
kono
parents:
diff changeset
2966 else if (max < HOST_WIDE_INT_MAX)
kono
parents:
diff changeset
2967 inform (callloc,
kono
parents:
diff changeset
2968 "%qE output between %wu and %wu bytes into "
kono
parents:
diff changeset
2969 "a destination of size %wu",
kono
parents:
diff changeset
2970 info.func, min, max, info.objsize);
kono
parents:
diff changeset
2971 else if (min < res->range.likely && res->range.likely < max)
kono
parents:
diff changeset
2972 inform (callloc,
kono
parents:
diff changeset
2973 "%qE output %wu or more bytes (assuming %wu) into "
kono
parents:
diff changeset
2974 "a destination of size %wu",
kono
parents:
diff changeset
2975 info.func, min, res->range.likely, info.objsize);
kono
parents:
diff changeset
2976 else
kono
parents:
diff changeset
2977 inform (callloc,
kono
parents:
diff changeset
2978 "%qE output %wu or more bytes into a destination of size %wu",
kono
parents:
diff changeset
2979 info.func, min, info.objsize);
kono
parents:
diff changeset
2980 }
kono
parents:
diff changeset
2981
kono
parents:
diff changeset
2982 if (dump_file && *dir.beg)
kono
parents:
diff changeset
2983 {
kono
parents:
diff changeset
2984 fprintf (dump_file, " Result: %lli, %lli, %lli, %lli "
kono
parents:
diff changeset
2985 "(%lli, %lli, %lli, %lli)\n",
kono
parents:
diff changeset
2986 (long long)fmtres.range.min,
kono
parents:
diff changeset
2987 (long long)fmtres.range.likely,
kono
parents:
diff changeset
2988 (long long)fmtres.range.max,
kono
parents:
diff changeset
2989 (long long)fmtres.range.unlikely,
kono
parents:
diff changeset
2990 (long long)res->range.min,
kono
parents:
diff changeset
2991 (long long)res->range.likely,
kono
parents:
diff changeset
2992 (long long)res->range.max,
kono
parents:
diff changeset
2993 (long long)res->range.unlikely);
kono
parents:
diff changeset
2994 }
kono
parents:
diff changeset
2995
kono
parents:
diff changeset
2996 return true;
kono
parents:
diff changeset
2997 }
kono
parents:
diff changeset
2998
kono
parents:
diff changeset
2999 #pragma GCC diagnostic pop
kono
parents:
diff changeset
3000
kono
parents:
diff changeset
3001 /* Parse a format directive in function call described by INFO starting
kono
parents:
diff changeset
3002 at STR and populate DIR structure. Bump up *ARGNO by the number of
kono
parents:
diff changeset
3003 arguments extracted for the directive. Return the length of
kono
parents:
diff changeset
3004 the directive. */
kono
parents:
diff changeset
3005
kono
parents:
diff changeset
3006 static size_t
kono
parents:
diff changeset
3007 parse_directive (pass_sprintf_length::call_info &info,
kono
parents:
diff changeset
3008 directive &dir, format_result *res,
kono
parents:
diff changeset
3009 const char *str, unsigned *argno)
kono
parents:
diff changeset
3010 {
kono
parents:
diff changeset
3011 const char *pcnt = strchr (str, target_percent);
kono
parents:
diff changeset
3012 dir.beg = str;
kono
parents:
diff changeset
3013
kono
parents:
diff changeset
3014 if (size_t len = pcnt ? pcnt - str : *str ? strlen (str) : 1)
kono
parents:
diff changeset
3015 {
kono
parents:
diff changeset
3016 /* This directive is either a plain string or the terminating nul
kono
parents:
diff changeset
3017 (which isn't really a directive but it simplifies things to
kono
parents:
diff changeset
3018 handle it as if it were). */
kono
parents:
diff changeset
3019 dir.len = len;
kono
parents:
diff changeset
3020 dir.fmtfunc = format_plain;
kono
parents:
diff changeset
3021
kono
parents:
diff changeset
3022 if (dump_file)
kono
parents:
diff changeset
3023 {
kono
parents:
diff changeset
3024 fprintf (dump_file, " Directive %u at offset %llu: \"%.*s\", "
kono
parents:
diff changeset
3025 "length = %llu\n",
kono
parents:
diff changeset
3026 dir.dirno,
kono
parents:
diff changeset
3027 (unsigned long long)(size_t)(dir.beg - info.fmtstr),
kono
parents:
diff changeset
3028 (int)dir.len, dir.beg, (unsigned long long)dir.len);
kono
parents:
diff changeset
3029 }
kono
parents:
diff changeset
3030
kono
parents:
diff changeset
3031 return len - !*str;
kono
parents:
diff changeset
3032 }
kono
parents:
diff changeset
3033
kono
parents:
diff changeset
3034 const char *pf = pcnt + 1;
kono
parents:
diff changeset
3035
kono
parents:
diff changeset
3036 /* POSIX numbered argument index or zero when none. */
kono
parents:
diff changeset
3037 HOST_WIDE_INT dollar = 0;
kono
parents:
diff changeset
3038
kono
parents:
diff changeset
3039 /* With and precision. -1 when not specified, HOST_WIDE_INT_MIN
kono
parents:
diff changeset
3040 when given by a va_list argument, and a non-negative value
kono
parents:
diff changeset
3041 when specified in the format string itself. */
kono
parents:
diff changeset
3042 HOST_WIDE_INT width = -1;
kono
parents:
diff changeset
3043 HOST_WIDE_INT precision = -1;
kono
parents:
diff changeset
3044
kono
parents:
diff changeset
3045 /* Pointers to the beginning of the width and precision decimal
kono
parents:
diff changeset
3046 string (if any) within the directive. */
kono
parents:
diff changeset
3047 const char *pwidth = 0;
kono
parents:
diff changeset
3048 const char *pprec = 0;
kono
parents:
diff changeset
3049
kono
parents:
diff changeset
3050 /* When the value of the decimal string that specifies width or
kono
parents:
diff changeset
3051 precision is out of range, points to the digit that causes
kono
parents:
diff changeset
3052 the value to exceed the limit. */
kono
parents:
diff changeset
3053 const char *werange = NULL;
kono
parents:
diff changeset
3054 const char *perange = NULL;
kono
parents:
diff changeset
3055
kono
parents:
diff changeset
3056 /* Width specified via the asterisk. Need not be INTEGER_CST.
kono
parents:
diff changeset
3057 For vararg functions set to void_node. */
kono
parents:
diff changeset
3058 tree star_width = NULL_TREE;
kono
parents:
diff changeset
3059
kono
parents:
diff changeset
3060 /* Width specified via the asterisk. Need not be INTEGER_CST.
kono
parents:
diff changeset
3061 For vararg functions set to void_node. */
kono
parents:
diff changeset
3062 tree star_precision = NULL_TREE;
kono
parents:
diff changeset
3063
kono
parents:
diff changeset
3064 if (ISDIGIT (target_to_host (*pf)))
kono
parents:
diff changeset
3065 {
kono
parents:
diff changeset
3066 /* This could be either a POSIX positional argument, the '0'
kono
parents:
diff changeset
3067 flag, or a width, depending on what follows. Store it as
kono
parents:
diff changeset
3068 width and sort it out later after the next character has
kono
parents:
diff changeset
3069 been seen. */
kono
parents:
diff changeset
3070 pwidth = pf;
kono
parents:
diff changeset
3071 width = target_strtol10 (&pf, &werange);
kono
parents:
diff changeset
3072 }
kono
parents:
diff changeset
3073 else if (target_to_host (*pf) == '*')
kono
parents:
diff changeset
3074 {
kono
parents:
diff changeset
3075 /* Similarly to the block above, this could be either a POSIX
kono
parents:
diff changeset
3076 positional argument or a width, depending on what follows. */
kono
parents:
diff changeset
3077 if (*argno < gimple_call_num_args (info.callstmt))
kono
parents:
diff changeset
3078 star_width = gimple_call_arg (info.callstmt, (*argno)++);
kono
parents:
diff changeset
3079 else
kono
parents:
diff changeset
3080 star_width = void_node;
kono
parents:
diff changeset
3081 ++pf;
kono
parents:
diff changeset
3082 }
kono
parents:
diff changeset
3083
kono
parents:
diff changeset
3084 if (target_to_host (*pf) == '$')
kono
parents:
diff changeset
3085 {
kono
parents:
diff changeset
3086 /* Handle the POSIX dollar sign which references the 1-based
kono
parents:
diff changeset
3087 positional argument number. */
kono
parents:
diff changeset
3088 if (width != -1)
kono
parents:
diff changeset
3089 dollar = width + info.argidx;
kono
parents:
diff changeset
3090 else if (star_width
kono
parents:
diff changeset
3091 && TREE_CODE (star_width) == INTEGER_CST
kono
parents:
diff changeset
3092 && (TYPE_PRECISION (TREE_TYPE (star_width))
kono
parents:
diff changeset
3093 <= TYPE_PRECISION (integer_type_node)))
kono
parents:
diff changeset
3094 dollar = width + tree_to_shwi (star_width);
kono
parents:
diff changeset
3095
kono
parents:
diff changeset
3096 /* Bail when the numbered argument is out of range (it will
kono
parents:
diff changeset
3097 have already been diagnosed by -Wformat). */
kono
parents:
diff changeset
3098 if (dollar == 0
kono
parents:
diff changeset
3099 || dollar == (int)info.argidx
kono
parents:
diff changeset
3100 || dollar > gimple_call_num_args (info.callstmt))
kono
parents:
diff changeset
3101 return false;
kono
parents:
diff changeset
3102
kono
parents:
diff changeset
3103 --dollar;
kono
parents:
diff changeset
3104
kono
parents:
diff changeset
3105 star_width = NULL_TREE;
kono
parents:
diff changeset
3106 width = -1;
kono
parents:
diff changeset
3107 ++pf;
kono
parents:
diff changeset
3108 }
kono
parents:
diff changeset
3109
kono
parents:
diff changeset
3110 if (dollar || !star_width)
kono
parents:
diff changeset
3111 {
kono
parents:
diff changeset
3112 if (width != -1)
kono
parents:
diff changeset
3113 {
kono
parents:
diff changeset
3114 if (width == 0)
kono
parents:
diff changeset
3115 {
kono
parents:
diff changeset
3116 /* The '0' that has been interpreted as a width above is
kono
parents:
diff changeset
3117 actually a flag. Reset HAVE_WIDTH, set the '0' flag,
kono
parents:
diff changeset
3118 and continue processing other flags. */
kono
parents:
diff changeset
3119 width = -1;
kono
parents:
diff changeset
3120 dir.set_flag ('0');
kono
parents:
diff changeset
3121 }
kono
parents:
diff changeset
3122 else if (!dollar)
kono
parents:
diff changeset
3123 {
kono
parents:
diff changeset
3124 /* (Non-zero) width has been seen. The next character
kono
parents:
diff changeset
3125 is either a period or a digit. */
kono
parents:
diff changeset
3126 goto start_precision;
kono
parents:
diff changeset
3127 }
kono
parents:
diff changeset
3128 }
kono
parents:
diff changeset
3129 /* When either '$' has been seen, or width has not been seen,
kono
parents:
diff changeset
3130 the next field is the optional flags followed by an optional
kono
parents:
diff changeset
3131 width. */
kono
parents:
diff changeset
3132 for ( ; ; ) {
kono
parents:
diff changeset
3133 switch (target_to_host (*pf))
kono
parents:
diff changeset
3134 {
kono
parents:
diff changeset
3135 case ' ':
kono
parents:
diff changeset
3136 case '0':
kono
parents:
diff changeset
3137 case '+':
kono
parents:
diff changeset
3138 case '-':
kono
parents:
diff changeset
3139 case '#':
kono
parents:
diff changeset
3140 dir.set_flag (target_to_host (*pf++));
kono
parents:
diff changeset
3141 break;
kono
parents:
diff changeset
3142
kono
parents:
diff changeset
3143 default:
kono
parents:
diff changeset
3144 goto start_width;
kono
parents:
diff changeset
3145 }
kono
parents:
diff changeset
3146 }
kono
parents:
diff changeset
3147
kono
parents:
diff changeset
3148 start_width:
kono
parents:
diff changeset
3149 if (ISDIGIT (target_to_host (*pf)))
kono
parents:
diff changeset
3150 {
kono
parents:
diff changeset
3151 werange = 0;
kono
parents:
diff changeset
3152 pwidth = pf;
kono
parents:
diff changeset
3153 width = target_strtol10 (&pf, &werange);
kono
parents:
diff changeset
3154 }
kono
parents:
diff changeset
3155 else if (target_to_host (*pf) == '*')
kono
parents:
diff changeset
3156 {
kono
parents:
diff changeset
3157 if (*argno < gimple_call_num_args (info.callstmt))
kono
parents:
diff changeset
3158 star_width = gimple_call_arg (info.callstmt, (*argno)++);
kono
parents:
diff changeset
3159 else
kono
parents:
diff changeset
3160 {
kono
parents:
diff changeset
3161 /* This is (likely) a va_list. It could also be an invalid
kono
parents:
diff changeset
3162 call with insufficient arguments. */
kono
parents:
diff changeset
3163 star_width = void_node;
kono
parents:
diff changeset
3164 }
kono
parents:
diff changeset
3165 ++pf;
kono
parents:
diff changeset
3166 }
kono
parents:
diff changeset
3167 else if (target_to_host (*pf) == '\'')
kono
parents:
diff changeset
3168 {
kono
parents:
diff changeset
3169 /* The POSIX apostrophe indicating a numeric grouping
kono
parents:
diff changeset
3170 in the current locale. Even though it's possible to
kono
parents:
diff changeset
3171 estimate the upper bound on the size of the output
kono
parents:
diff changeset
3172 based on the number of digits it probably isn't worth
kono
parents:
diff changeset
3173 continuing. */
kono
parents:
diff changeset
3174 return 0;
kono
parents:
diff changeset
3175 }
kono
parents:
diff changeset
3176 }
kono
parents:
diff changeset
3177
kono
parents:
diff changeset
3178 start_precision:
kono
parents:
diff changeset
3179 if (target_to_host (*pf) == '.')
kono
parents:
diff changeset
3180 {
kono
parents:
diff changeset
3181 ++pf;
kono
parents:
diff changeset
3182
kono
parents:
diff changeset
3183 if (ISDIGIT (target_to_host (*pf)))
kono
parents:
diff changeset
3184 {
kono
parents:
diff changeset
3185 pprec = pf;
kono
parents:
diff changeset
3186 precision = target_strtol10 (&pf, &perange);
kono
parents:
diff changeset
3187 }
kono
parents:
diff changeset
3188 else if (target_to_host (*pf) == '*')
kono
parents:
diff changeset
3189 {
kono
parents:
diff changeset
3190 if (*argno < gimple_call_num_args (info.callstmt))
kono
parents:
diff changeset
3191 star_precision = gimple_call_arg (info.callstmt, (*argno)++);
kono
parents:
diff changeset
3192 else
kono
parents:
diff changeset
3193 {
kono
parents:
diff changeset
3194 /* This is (likely) a va_list. It could also be an invalid
kono
parents:
diff changeset
3195 call with insufficient arguments. */
kono
parents:
diff changeset
3196 star_precision = void_node;
kono
parents:
diff changeset
3197 }
kono
parents:
diff changeset
3198 ++pf;
kono
parents:
diff changeset
3199 }
kono
parents:
diff changeset
3200 else
kono
parents:
diff changeset
3201 {
kono
parents:
diff changeset
3202 /* The decimal precision or the asterisk are optional.
kono
parents:
diff changeset
3203 When neither is dirified it's taken to be zero. */
kono
parents:
diff changeset
3204 precision = 0;
kono
parents:
diff changeset
3205 }
kono
parents:
diff changeset
3206 }
kono
parents:
diff changeset
3207
kono
parents:
diff changeset
3208 switch (target_to_host (*pf))
kono
parents:
diff changeset
3209 {
kono
parents:
diff changeset
3210 case 'h':
kono
parents:
diff changeset
3211 if (target_to_host (pf[1]) == 'h')
kono
parents:
diff changeset
3212 {
kono
parents:
diff changeset
3213 ++pf;
kono
parents:
diff changeset
3214 dir.modifier = FMT_LEN_hh;
kono
parents:
diff changeset
3215 }
kono
parents:
diff changeset
3216 else
kono
parents:
diff changeset
3217 dir.modifier = FMT_LEN_h;
kono
parents:
diff changeset
3218 ++pf;
kono
parents:
diff changeset
3219 break;
kono
parents:
diff changeset
3220
kono
parents:
diff changeset
3221 case 'j':
kono
parents:
diff changeset
3222 dir.modifier = FMT_LEN_j;
kono
parents:
diff changeset
3223 ++pf;
kono
parents:
diff changeset
3224 break;
kono
parents:
diff changeset
3225
kono
parents:
diff changeset
3226 case 'L':
kono
parents:
diff changeset
3227 dir.modifier = FMT_LEN_L;
kono
parents:
diff changeset
3228 ++pf;
kono
parents:
diff changeset
3229 break;
kono
parents:
diff changeset
3230
kono
parents:
diff changeset
3231 case 'l':
kono
parents:
diff changeset
3232 if (target_to_host (pf[1]) == 'l')
kono
parents:
diff changeset
3233 {
kono
parents:
diff changeset
3234 ++pf;
kono
parents:
diff changeset
3235 dir.modifier = FMT_LEN_ll;
kono
parents:
diff changeset
3236 }
kono
parents:
diff changeset
3237 else
kono
parents:
diff changeset
3238 dir.modifier = FMT_LEN_l;
kono
parents:
diff changeset
3239 ++pf;
kono
parents:
diff changeset
3240 break;
kono
parents:
diff changeset
3241
kono
parents:
diff changeset
3242 case 't':
kono
parents:
diff changeset
3243 dir.modifier = FMT_LEN_t;
kono
parents:
diff changeset
3244 ++pf;
kono
parents:
diff changeset
3245 break;
kono
parents:
diff changeset
3246
kono
parents:
diff changeset
3247 case 'z':
kono
parents:
diff changeset
3248 dir.modifier = FMT_LEN_z;
kono
parents:
diff changeset
3249 ++pf;
kono
parents:
diff changeset
3250 break;
kono
parents:
diff changeset
3251 }
kono
parents:
diff changeset
3252
kono
parents:
diff changeset
3253 switch (target_to_host (*pf))
kono
parents:
diff changeset
3254 {
kono
parents:
diff changeset
3255 /* Handle a sole '%' character the same as "%%" but since it's
kono
parents:
diff changeset
3256 undefined prevent the result from being folded. */
kono
parents:
diff changeset
3257 case '\0':
kono
parents:
diff changeset
3258 --pf;
kono
parents:
diff changeset
3259 res->range.min = res->range.max = HOST_WIDE_INT_M1U;
kono
parents:
diff changeset
3260 /* FALLTHRU */
kono
parents:
diff changeset
3261 case '%':
kono
parents:
diff changeset
3262 dir.fmtfunc = format_percent;
kono
parents:
diff changeset
3263 break;
kono
parents:
diff changeset
3264
kono
parents:
diff changeset
3265 case 'a':
kono
parents:
diff changeset
3266 case 'A':
kono
parents:
diff changeset
3267 case 'e':
kono
parents:
diff changeset
3268 case 'E':
kono
parents:
diff changeset
3269 case 'f':
kono
parents:
diff changeset
3270 case 'F':
kono
parents:
diff changeset
3271 case 'g':
kono
parents:
diff changeset
3272 case 'G':
kono
parents:
diff changeset
3273 res->floating = true;
kono
parents:
diff changeset
3274 dir.fmtfunc = format_floating;
kono
parents:
diff changeset
3275 break;
kono
parents:
diff changeset
3276
kono
parents:
diff changeset
3277 case 'd':
kono
parents:
diff changeset
3278 case 'i':
kono
parents:
diff changeset
3279 case 'o':
kono
parents:
diff changeset
3280 case 'u':
kono
parents:
diff changeset
3281 case 'x':
kono
parents:
diff changeset
3282 case 'X':
kono
parents:
diff changeset
3283 dir.fmtfunc = format_integer;
kono
parents:
diff changeset
3284 break;
kono
parents:
diff changeset
3285
kono
parents:
diff changeset
3286 case 'p':
kono
parents:
diff changeset
3287 /* The %p output is implementation-defined. It's possible
kono
parents:
diff changeset
3288 to determine this format but due to extensions (edirially
kono
parents:
diff changeset
3289 those of the Linux kernel -- see bug 78512) the first %p
kono
parents:
diff changeset
3290 in the format string disables any further processing. */
kono
parents:
diff changeset
3291 return false;
kono
parents:
diff changeset
3292
kono
parents:
diff changeset
3293 case 'n':
kono
parents:
diff changeset
3294 /* %n has side-effects even when nothing is actually printed to
kono
parents:
diff changeset
3295 any buffer. */
kono
parents:
diff changeset
3296 info.nowrite = false;
kono
parents:
diff changeset
3297 dir.fmtfunc = format_none;
kono
parents:
diff changeset
3298 break;
kono
parents:
diff changeset
3299
kono
parents:
diff changeset
3300 case 'c':
kono
parents:
diff changeset
3301 dir.fmtfunc = format_character;
kono
parents:
diff changeset
3302 break;
kono
parents:
diff changeset
3303
kono
parents:
diff changeset
3304 case 'S':
kono
parents:
diff changeset
3305 case 's':
kono
parents:
diff changeset
3306 dir.fmtfunc = format_string;
kono
parents:
diff changeset
3307 break;
kono
parents:
diff changeset
3308
kono
parents:
diff changeset
3309 default:
kono
parents:
diff changeset
3310 /* Unknown conversion specification. */
kono
parents:
diff changeset
3311 return 0;
kono
parents:
diff changeset
3312 }
kono
parents:
diff changeset
3313
kono
parents:
diff changeset
3314 dir.specifier = target_to_host (*pf++);
kono
parents:
diff changeset
3315
kono
parents:
diff changeset
3316 /* Store the length of the format directive. */
kono
parents:
diff changeset
3317 dir.len = pf - pcnt;
kono
parents:
diff changeset
3318
kono
parents:
diff changeset
3319 /* Buffer for the directive in the host character set (used when
kono
parents:
diff changeset
3320 the source character set is different). */
kono
parents:
diff changeset
3321 char hostdir[32];
kono
parents:
diff changeset
3322
kono
parents:
diff changeset
3323 if (star_width)
kono
parents:
diff changeset
3324 {
kono
parents:
diff changeset
3325 if (INTEGRAL_TYPE_P (TREE_TYPE (star_width)))
kono
parents:
diff changeset
3326 dir.set_width (star_width);
kono
parents:
diff changeset
3327 else
kono
parents:
diff changeset
3328 {
kono
parents:
diff changeset
3329 /* Width specified by a va_list takes on the range [0, -INT_MIN]
kono
parents:
diff changeset
3330 (width is the absolute value of that specified). */
kono
parents:
diff changeset
3331 dir.width[0] = 0;
kono
parents:
diff changeset
3332 dir.width[1] = target_int_max () + 1;
kono
parents:
diff changeset
3333 }
kono
parents:
diff changeset
3334 }
kono
parents:
diff changeset
3335 else
kono
parents:
diff changeset
3336 {
kono
parents:
diff changeset
3337 if (width == LONG_MAX && werange)
kono
parents:
diff changeset
3338 {
kono
parents:
diff changeset
3339 size_t begin = dir.beg - info.fmtstr + (pwidth - pcnt);
kono
parents:
diff changeset
3340 size_t caret = begin + (werange - pcnt);
kono
parents:
diff changeset
3341 size_t end = pf - info.fmtstr - 1;
kono
parents:
diff changeset
3342
kono
parents:
diff changeset
3343 /* Create a location for the width part of the directive,
kono
parents:
diff changeset
3344 pointing the caret at the first out-of-range digit. */
kono
parents:
diff changeset
3345 substring_loc dirloc (info.fmtloc, TREE_TYPE (info.format),
kono
parents:
diff changeset
3346 caret, begin, end);
kono
parents:
diff changeset
3347
kono
parents:
diff changeset
3348 fmtwarn (dirloc, UNKNOWN_LOCATION, NULL,
kono
parents:
diff changeset
3349 info.warnopt (), "%<%.*s%> directive width out of range",
kono
parents:
diff changeset
3350 dir.len, target_to_host (hostdir, sizeof hostdir, dir.beg));
kono
parents:
diff changeset
3351 }
kono
parents:
diff changeset
3352
kono
parents:
diff changeset
3353 dir.set_width (width);
kono
parents:
diff changeset
3354 }
kono
parents:
diff changeset
3355
kono
parents:
diff changeset
3356 if (star_precision)
kono
parents:
diff changeset
3357 {
kono
parents:
diff changeset
3358 if (INTEGRAL_TYPE_P (TREE_TYPE (star_precision)))
kono
parents:
diff changeset
3359 dir.set_precision (star_precision);
kono
parents:
diff changeset
3360 else
kono
parents:
diff changeset
3361 {
kono
parents:
diff changeset
3362 /* Precision specified by a va_list takes on the range [-1, INT_MAX]
kono
parents:
diff changeset
3363 (unlike width, negative precision is ignored). */
kono
parents:
diff changeset
3364 dir.prec[0] = -1;
kono
parents:
diff changeset
3365 dir.prec[1] = target_int_max ();
kono
parents:
diff changeset
3366 }
kono
parents:
diff changeset
3367 }
kono
parents:
diff changeset
3368 else
kono
parents:
diff changeset
3369 {
kono
parents:
diff changeset
3370 if (precision == LONG_MAX && perange)
kono
parents:
diff changeset
3371 {
kono
parents:
diff changeset
3372 size_t begin = dir.beg - info.fmtstr + (pprec - pcnt) - 1;
kono
parents:
diff changeset
3373 size_t caret = dir.beg - info.fmtstr + (perange - pcnt) - 1;
kono
parents:
diff changeset
3374 size_t end = pf - info.fmtstr - 2;
kono
parents:
diff changeset
3375
kono
parents:
diff changeset
3376 /* Create a location for the precision part of the directive,
kono
parents:
diff changeset
3377 including the leading period, pointing the caret at the first
kono
parents:
diff changeset
3378 out-of-range digit . */
kono
parents:
diff changeset
3379 substring_loc dirloc (info.fmtloc, TREE_TYPE (info.format),
kono
parents:
diff changeset
3380 caret, begin, end);
kono
parents:
diff changeset
3381
kono
parents:
diff changeset
3382 fmtwarn (dirloc, UNKNOWN_LOCATION, NULL,
kono
parents:
diff changeset
3383 info.warnopt (), "%<%.*s%> directive precision out of range",
kono
parents:
diff changeset
3384 dir.len, target_to_host (hostdir, sizeof hostdir, dir.beg));
kono
parents:
diff changeset
3385 }
kono
parents:
diff changeset
3386
kono
parents:
diff changeset
3387 dir.set_precision (precision);
kono
parents:
diff changeset
3388 }
kono
parents:
diff changeset
3389
kono
parents:
diff changeset
3390 /* Extract the argument if the directive takes one and if it's
kono
parents:
diff changeset
3391 available (e.g., the function doesn't take a va_list). Treat
kono
parents:
diff changeset
3392 missing arguments the same as va_list, even though they will
kono
parents:
diff changeset
3393 have likely already been diagnosed by -Wformat. */
kono
parents:
diff changeset
3394 if (dir.specifier != '%'
kono
parents:
diff changeset
3395 && *argno < gimple_call_num_args (info.callstmt))
kono
parents:
diff changeset
3396 dir.arg = gimple_call_arg (info.callstmt, dollar ? dollar : (*argno)++);
kono
parents:
diff changeset
3397
kono
parents:
diff changeset
3398 if (dump_file)
kono
parents:
diff changeset
3399 {
kono
parents:
diff changeset
3400 fprintf (dump_file, " Directive %u at offset %llu: \"%.*s\"",
kono
parents:
diff changeset
3401 dir.dirno, (unsigned long long)(size_t)(dir.beg - info.fmtstr),
kono
parents:
diff changeset
3402 (int)dir.len, dir.beg);
kono
parents:
diff changeset
3403 if (star_width)
kono
parents:
diff changeset
3404 {
kono
parents:
diff changeset
3405 if (dir.width[0] == dir.width[1])
kono
parents:
diff changeset
3406 fprintf (dump_file, ", width = %lli", (long long)dir.width[0]);
kono
parents:
diff changeset
3407 else
kono
parents:
diff changeset
3408 fprintf (dump_file, ", width in range [%lli, %lli]",
kono
parents:
diff changeset
3409 (long long)dir.width[0], (long long)dir.width[1]);
kono
parents:
diff changeset
3410 }
kono
parents:
diff changeset
3411
kono
parents:
diff changeset
3412 if (star_precision)
kono
parents:
diff changeset
3413 {
kono
parents:
diff changeset
3414 if (dir.prec[0] == dir.prec[1])
kono
parents:
diff changeset
3415 fprintf (dump_file, ", precision = %lli", (long long)dir.prec[0]);
kono
parents:
diff changeset
3416 else
kono
parents:
diff changeset
3417 fprintf (dump_file, ", precision in range [%lli, %lli]",
kono
parents:
diff changeset
3418 (long long)dir.prec[0], (long long)dir.prec[1]);
kono
parents:
diff changeset
3419 }
kono
parents:
diff changeset
3420 fputc ('\n', dump_file);
kono
parents:
diff changeset
3421 }
kono
parents:
diff changeset
3422
kono
parents:
diff changeset
3423 return dir.len;
kono
parents:
diff changeset
3424 }
kono
parents:
diff changeset
3425
kono
parents:
diff changeset
3426 /* Compute the length of the output resulting from the call to a formatted
kono
parents:
diff changeset
3427 output function described by INFO and store the result of the call in
kono
parents:
diff changeset
3428 *RES. Issue warnings for detected past the end writes. Return true
kono
parents:
diff changeset
3429 if the complete format string has been processed and *RES can be relied
kono
parents:
diff changeset
3430 on, false otherwise (e.g., when a unknown or unhandled directive was seen
kono
parents:
diff changeset
3431 that caused the processing to be terminated early). */
kono
parents:
diff changeset
3432
kono
parents:
diff changeset
3433 bool
kono
parents:
diff changeset
3434 pass_sprintf_length::compute_format_length (call_info &info,
kono
parents:
diff changeset
3435 format_result *res)
kono
parents:
diff changeset
3436 {
kono
parents:
diff changeset
3437 if (dump_file)
kono
parents:
diff changeset
3438 {
kono
parents:
diff changeset
3439 location_t callloc = gimple_location (info.callstmt);
kono
parents:
diff changeset
3440 fprintf (dump_file, "%s:%i: ",
kono
parents:
diff changeset
3441 LOCATION_FILE (callloc), LOCATION_LINE (callloc));
kono
parents:
diff changeset
3442 print_generic_expr (dump_file, info.func, dump_flags);
kono
parents:
diff changeset
3443
kono
parents:
diff changeset
3444 fprintf (dump_file, ": objsize = %llu, fmtstr = \"%s\"\n",
kono
parents:
diff changeset
3445 (unsigned long long)info.objsize, info.fmtstr);
kono
parents:
diff changeset
3446 }
kono
parents:
diff changeset
3447
kono
parents:
diff changeset
3448 /* Reset the minimum and maximum byte counters. */
kono
parents:
diff changeset
3449 res->range.min = res->range.max = 0;
kono
parents:
diff changeset
3450
kono
parents:
diff changeset
3451 /* No directive has been seen yet so the length of output is bounded
kono
parents:
diff changeset
3452 by the known range [0, 0] (with no conversion producing more than
kono
parents:
diff changeset
3453 4K bytes) until determined otherwise. */
kono
parents:
diff changeset
3454 res->knownrange = true;
kono
parents:
diff changeset
3455 res->under4k = true;
kono
parents:
diff changeset
3456 res->floating = false;
kono
parents:
diff changeset
3457 res->warned = false;
kono
parents:
diff changeset
3458
kono
parents:
diff changeset
3459 /* 1-based directive counter. */
kono
parents:
diff changeset
3460 unsigned dirno = 1;
kono
parents:
diff changeset
3461
kono
parents:
diff changeset
3462 /* The variadic argument counter. */
kono
parents:
diff changeset
3463 unsigned argno = info.argidx;
kono
parents:
diff changeset
3464
kono
parents:
diff changeset
3465 for (const char *pf = info.fmtstr; ; ++dirno)
kono
parents:
diff changeset
3466 {
kono
parents:
diff changeset
3467 directive dir = directive ();
kono
parents:
diff changeset
3468 dir.dirno = dirno;
kono
parents:
diff changeset
3469
kono
parents:
diff changeset
3470 size_t n = parse_directive (info, dir, res, pf, &argno);
kono
parents:
diff changeset
3471
kono
parents:
diff changeset
3472 /* Return failure if the format function fails. */
kono
parents:
diff changeset
3473 if (!format_directive (info, res, dir))
kono
parents:
diff changeset
3474 return false;
kono
parents:
diff changeset
3475
kono
parents:
diff changeset
3476 /* Return success the directive is zero bytes long and it's
kono
parents:
diff changeset
3477 the last think in the format string (i.e., it's the terminating
kono
parents:
diff changeset
3478 nul, which isn't really a directive but handling it as one makes
kono
parents:
diff changeset
3479 things simpler). */
kono
parents:
diff changeset
3480 if (!n)
kono
parents:
diff changeset
3481 return *pf == '\0';
kono
parents:
diff changeset
3482
kono
parents:
diff changeset
3483 pf += n;
kono
parents:
diff changeset
3484 }
kono
parents:
diff changeset
3485
kono
parents:
diff changeset
3486 /* The complete format string was processed (with or without warnings). */
kono
parents:
diff changeset
3487 return true;
kono
parents:
diff changeset
3488 }
kono
parents:
diff changeset
3489
kono
parents:
diff changeset
3490 /* Return the size of the object referenced by the expression DEST if
kono
parents:
diff changeset
3491 available, or -1 otherwise. */
kono
parents:
diff changeset
3492
kono
parents:
diff changeset
3493 static unsigned HOST_WIDE_INT
kono
parents:
diff changeset
3494 get_destination_size (tree dest)
kono
parents:
diff changeset
3495 {
kono
parents:
diff changeset
3496 /* Initialize object size info before trying to compute it. */
kono
parents:
diff changeset
3497 init_object_sizes ();
kono
parents:
diff changeset
3498
kono
parents:
diff changeset
3499 /* Use __builtin_object_size to determine the size of the destination
kono
parents:
diff changeset
3500 object. When optimizing, determine the smallest object (such as
kono
parents:
diff changeset
3501 a member array as opposed to the whole enclosing object), otherwise
kono
parents:
diff changeset
3502 use type-zero object size to determine the size of the enclosing
kono
parents:
diff changeset
3503 object (the function fails without optimization in this type). */
kono
parents:
diff changeset
3504 int ost = optimize > 0;
kono
parents:
diff changeset
3505 unsigned HOST_WIDE_INT size;
kono
parents:
diff changeset
3506 if (compute_builtin_object_size (dest, ost, &size))
kono
parents:
diff changeset
3507 return size;
kono
parents:
diff changeset
3508
kono
parents:
diff changeset
3509 return HOST_WIDE_INT_M1U;
kono
parents:
diff changeset
3510 }
kono
parents:
diff changeset
3511
kono
parents:
diff changeset
3512 /* Return true if the call described by INFO with result RES safe to
kono
parents:
diff changeset
3513 optimize (i.e., no undefined behavior), and set RETVAL to the range
kono
parents:
diff changeset
3514 of its return values. */
kono
parents:
diff changeset
3515
kono
parents:
diff changeset
3516 static bool
kono
parents:
diff changeset
3517 is_call_safe (const pass_sprintf_length::call_info &info,
kono
parents:
diff changeset
3518 const format_result &res, bool under4k,
kono
parents:
diff changeset
3519 unsigned HOST_WIDE_INT retval[2])
kono
parents:
diff changeset
3520 {
kono
parents:
diff changeset
3521 if (under4k && !res.under4k)
kono
parents:
diff changeset
3522 return false;
kono
parents:
diff changeset
3523
kono
parents:
diff changeset
3524 /* The minimum return value. */
kono
parents:
diff changeset
3525 retval[0] = res.range.min;
kono
parents:
diff changeset
3526
kono
parents:
diff changeset
3527 /* The maximum return value is in most cases bounded by RES.RANGE.MAX
kono
parents:
diff changeset
3528 but in cases involving multibyte characters could be as large as
kono
parents:
diff changeset
3529 RES.RANGE.UNLIKELY. */
kono
parents:
diff changeset
3530 retval[1]
kono
parents:
diff changeset
3531 = res.range.unlikely < res.range.max ? res.range.max : res.range.unlikely;
kono
parents:
diff changeset
3532
kono
parents:
diff changeset
3533 /* Adjust the number of bytes which includes the terminating nul
kono
parents:
diff changeset
3534 to reflect the return value of the function which does not.
kono
parents:
diff changeset
3535 Because the valid range of the function is [INT_MIN, INT_MAX],
kono
parents:
diff changeset
3536 a valid range before the adjustment below is [0, INT_MAX + 1]
kono
parents:
diff changeset
3537 (the functions only return negative values on error or undefined
kono
parents:
diff changeset
3538 behavior). */
kono
parents:
diff changeset
3539 if (retval[0] <= target_int_max () + 1)
kono
parents:
diff changeset
3540 --retval[0];
kono
parents:
diff changeset
3541 if (retval[1] <= target_int_max () + 1)
kono
parents:
diff changeset
3542 --retval[1];
kono
parents:
diff changeset
3543
kono
parents:
diff changeset
3544 /* Avoid the return value optimization when the behavior of the call
kono
parents:
diff changeset
3545 is undefined either because any directive may have produced 4K or
kono
parents:
diff changeset
3546 more of output, or the return value exceeds INT_MAX, or because
kono
parents:
diff changeset
3547 the output overflows the destination object (but leave it enabled
kono
parents:
diff changeset
3548 when the function is bounded because then the behavior is well-
kono
parents:
diff changeset
3549 defined). */
kono
parents:
diff changeset
3550 if (retval[0] == retval[1]
kono
parents:
diff changeset
3551 && (info.bounded || retval[0] < info.objsize)
kono
parents:
diff changeset
3552 && retval[0] <= target_int_max ())
kono
parents:
diff changeset
3553 return true;
kono
parents:
diff changeset
3554
kono
parents:
diff changeset
3555 if ((info.bounded || retval[1] < info.objsize)
kono
parents:
diff changeset
3556 && (retval[0] < target_int_max ()
kono
parents:
diff changeset
3557 && retval[1] < target_int_max ()))
kono
parents:
diff changeset
3558 return true;
kono
parents:
diff changeset
3559
kono
parents:
diff changeset
3560 if (!under4k && (info.bounded || retval[0] < info.objsize))
kono
parents:
diff changeset
3561 return true;
kono
parents:
diff changeset
3562
kono
parents:
diff changeset
3563 return false;
kono
parents:
diff changeset
3564 }
kono
parents:
diff changeset
3565
kono
parents:
diff changeset
3566 /* Given a suitable result RES of a call to a formatted output function
kono
parents:
diff changeset
3567 described by INFO, substitute the result for the return value of
kono
parents:
diff changeset
3568 the call. The result is suitable if the number of bytes it represents
kono
parents:
diff changeset
3569 is known and exact. A result that isn't suitable for substitution may
kono
parents:
diff changeset
3570 have its range set to the range of return values, if that is known.
kono
parents:
diff changeset
3571 Return true if the call is removed and gsi_next should not be performed
kono
parents:
diff changeset
3572 in the caller. */
kono
parents:
diff changeset
3573
kono
parents:
diff changeset
3574 static bool
kono
parents:
diff changeset
3575 try_substitute_return_value (gimple_stmt_iterator *gsi,
kono
parents:
diff changeset
3576 const pass_sprintf_length::call_info &info,
kono
parents:
diff changeset
3577 const format_result &res)
kono
parents:
diff changeset
3578 {
kono
parents:
diff changeset
3579 tree lhs = gimple_get_lhs (info.callstmt);
kono
parents:
diff changeset
3580
kono
parents:
diff changeset
3581 /* Set to true when the entire call has been removed. */
kono
parents:
diff changeset
3582 bool removed = false;
kono
parents:
diff changeset
3583
kono
parents:
diff changeset
3584 /* The minimum and maximum return value. */
kono
parents:
diff changeset
3585 unsigned HOST_WIDE_INT retval[2];
kono
parents:
diff changeset
3586 bool safe = is_call_safe (info, res, true, retval);
kono
parents:
diff changeset
3587
kono
parents:
diff changeset
3588 if (safe
kono
parents:
diff changeset
3589 && retval[0] == retval[1]
kono
parents:
diff changeset
3590 /* Not prepared to handle possibly throwing calls here; they shouldn't
kono
parents:
diff changeset
3591 appear in non-artificial testcases, except when the __*_chk routines
kono
parents:
diff changeset
3592 are badly declared. */
kono
parents:
diff changeset
3593 && !stmt_ends_bb_p (info.callstmt))
kono
parents:
diff changeset
3594 {
kono
parents:
diff changeset
3595 tree cst = build_int_cst (integer_type_node, retval[0]);
kono
parents:
diff changeset
3596
kono
parents:
diff changeset
3597 if (lhs == NULL_TREE
kono
parents:
diff changeset
3598 && info.nowrite)
kono
parents:
diff changeset
3599 {
kono
parents:
diff changeset
3600 /* Remove the call to the bounded function with a zero size
kono
parents:
diff changeset
3601 (e.g., snprintf(0, 0, "%i", 123)) if there is no lhs. */
kono
parents:
diff changeset
3602 unlink_stmt_vdef (info.callstmt);
kono
parents:
diff changeset
3603 gsi_remove (gsi, true);
kono
parents:
diff changeset
3604 removed = true;
kono
parents:
diff changeset
3605 }
kono
parents:
diff changeset
3606 else if (info.nowrite)
kono
parents:
diff changeset
3607 {
kono
parents:
diff changeset
3608 /* Replace the call to the bounded function with a zero size
kono
parents:
diff changeset
3609 (e.g., snprintf(0, 0, "%i", 123) with the constant result
kono
parents:
diff changeset
3610 of the function. */
kono
parents:
diff changeset
3611 if (!update_call_from_tree (gsi, cst))
kono
parents:
diff changeset
3612 gimplify_and_update_call_from_tree (gsi, cst);
kono
parents:
diff changeset
3613 gimple *callstmt = gsi_stmt (*gsi);
kono
parents:
diff changeset
3614 update_stmt (callstmt);
kono
parents:
diff changeset
3615 }
kono
parents:
diff changeset
3616 else if (lhs)
kono
parents:
diff changeset
3617 {
kono
parents:
diff changeset
3618 /* Replace the left-hand side of the call with the constant
kono
parents:
diff changeset
3619 result of the formatted function. */
kono
parents:
diff changeset
3620 gimple_call_set_lhs (info.callstmt, NULL_TREE);
kono
parents:
diff changeset
3621 gimple *g = gimple_build_assign (lhs, cst);
kono
parents:
diff changeset
3622 gsi_insert_after (gsi, g, GSI_NEW_STMT);
kono
parents:
diff changeset
3623 update_stmt (info.callstmt);
kono
parents:
diff changeset
3624 }
kono
parents:
diff changeset
3625
kono
parents:
diff changeset
3626 if (dump_file)
kono
parents:
diff changeset
3627 {
kono
parents:
diff changeset
3628 if (removed)
kono
parents:
diff changeset
3629 fprintf (dump_file, " Removing call statement.");
kono
parents:
diff changeset
3630 else
kono
parents:
diff changeset
3631 {
kono
parents:
diff changeset
3632 fprintf (dump_file, " Substituting ");
kono
parents:
diff changeset
3633 print_generic_expr (dump_file, cst, dump_flags);
kono
parents:
diff changeset
3634 fprintf (dump_file, " for %s.\n",
kono
parents:
diff changeset
3635 info.nowrite ? "statement" : "return value");
kono
parents:
diff changeset
3636 }
kono
parents:
diff changeset
3637 }
kono
parents:
diff changeset
3638 }
kono
parents:
diff changeset
3639 else if (lhs)
kono
parents:
diff changeset
3640 {
kono
parents:
diff changeset
3641 bool setrange = false;
kono
parents:
diff changeset
3642
kono
parents:
diff changeset
3643 if (safe
kono
parents:
diff changeset
3644 && (info.bounded || retval[1] < info.objsize)
kono
parents:
diff changeset
3645 && (retval[0] < target_int_max ()
kono
parents:
diff changeset
3646 && retval[1] < target_int_max ()))
kono
parents:
diff changeset
3647 {
kono
parents:
diff changeset
3648 /* If the result is in a valid range bounded by the size of
kono
parents:
diff changeset
3649 the destination set it so that it can be used for subsequent
kono
parents:
diff changeset
3650 optimizations. */
kono
parents:
diff changeset
3651 int prec = TYPE_PRECISION (integer_type_node);
kono
parents:
diff changeset
3652
kono
parents:
diff changeset
3653 wide_int min = wi::shwi (retval[0], prec);
kono
parents:
diff changeset
3654 wide_int max = wi::shwi (retval[1], prec);
kono
parents:
diff changeset
3655 set_range_info (lhs, VR_RANGE, min, max);
kono
parents:
diff changeset
3656
kono
parents:
diff changeset
3657 setrange = true;
kono
parents:
diff changeset
3658 }
kono
parents:
diff changeset
3659
kono
parents:
diff changeset
3660 if (dump_file)
kono
parents:
diff changeset
3661 {
kono
parents:
diff changeset
3662 const char *inbounds
kono
parents:
diff changeset
3663 = (retval[0] < info.objsize
kono
parents:
diff changeset
3664 ? (retval[1] < info.objsize
kono
parents:
diff changeset
3665 ? "in" : "potentially out-of")
kono
parents:
diff changeset
3666 : "out-of");
kono
parents:
diff changeset
3667
kono
parents:
diff changeset
3668 const char *what = setrange ? "Setting" : "Discarding";
kono
parents:
diff changeset
3669 if (retval[0] != retval[1])
kono
parents:
diff changeset
3670 fprintf (dump_file,
kono
parents:
diff changeset
3671 " %s %s-bounds return value range [%llu, %llu].\n",
kono
parents:
diff changeset
3672 what, inbounds,
kono
parents:
diff changeset
3673 (unsigned long long)retval[0],
kono
parents:
diff changeset
3674 (unsigned long long)retval[1]);
kono
parents:
diff changeset
3675 else
kono
parents:
diff changeset
3676 fprintf (dump_file, " %s %s-bounds return value %llu.\n",
kono
parents:
diff changeset
3677 what, inbounds, (unsigned long long)retval[0]);
kono
parents:
diff changeset
3678 }
kono
parents:
diff changeset
3679 }
kono
parents:
diff changeset
3680
kono
parents:
diff changeset
3681 if (dump_file)
kono
parents:
diff changeset
3682 fputc ('\n', dump_file);
kono
parents:
diff changeset
3683
kono
parents:
diff changeset
3684 return removed;
kono
parents:
diff changeset
3685 }
kono
parents:
diff changeset
3686
kono
parents:
diff changeset
3687 /* Try to simplify a s{,n}printf call described by INFO with result
kono
parents:
diff changeset
3688 RES by replacing it with a simpler and presumably more efficient
kono
parents:
diff changeset
3689 call (such as strcpy). */
kono
parents:
diff changeset
3690
kono
parents:
diff changeset
3691 static bool
kono
parents:
diff changeset
3692 try_simplify_call (gimple_stmt_iterator *gsi,
kono
parents:
diff changeset
3693 const pass_sprintf_length::call_info &info,
kono
parents:
diff changeset
3694 const format_result &res)
kono
parents:
diff changeset
3695 {
kono
parents:
diff changeset
3696 unsigned HOST_WIDE_INT dummy[2];
kono
parents:
diff changeset
3697 if (!is_call_safe (info, res, info.retval_used (), dummy))
kono
parents:
diff changeset
3698 return false;
kono
parents:
diff changeset
3699
kono
parents:
diff changeset
3700 switch (info.fncode)
kono
parents:
diff changeset
3701 {
kono
parents:
diff changeset
3702 case BUILT_IN_SNPRINTF:
kono
parents:
diff changeset
3703 return gimple_fold_builtin_snprintf (gsi);
kono
parents:
diff changeset
3704
kono
parents:
diff changeset
3705 case BUILT_IN_SPRINTF:
kono
parents:
diff changeset
3706 return gimple_fold_builtin_sprintf (gsi);
kono
parents:
diff changeset
3707
kono
parents:
diff changeset
3708 default:
kono
parents:
diff changeset
3709 ;
kono
parents:
diff changeset
3710 }
kono
parents:
diff changeset
3711
kono
parents:
diff changeset
3712 return false;
kono
parents:
diff changeset
3713 }
kono
parents:
diff changeset
3714
kono
parents:
diff changeset
3715 /* Determine if a GIMPLE CALL is to one of the sprintf-like built-in
kono
parents:
diff changeset
3716 functions and if so, handle it. Return true if the call is removed
kono
parents:
diff changeset
3717 and gsi_next should not be performed in the caller. */
kono
parents:
diff changeset
3718
kono
parents:
diff changeset
3719 bool
kono
parents:
diff changeset
3720 pass_sprintf_length::handle_gimple_call (gimple_stmt_iterator *gsi)
kono
parents:
diff changeset
3721 {
kono
parents:
diff changeset
3722 call_info info = call_info ();
kono
parents:
diff changeset
3723
kono
parents:
diff changeset
3724 info.callstmt = gsi_stmt (*gsi);
kono
parents:
diff changeset
3725 if (!gimple_call_builtin_p (info.callstmt, BUILT_IN_NORMAL))
kono
parents:
diff changeset
3726 return false;
kono
parents:
diff changeset
3727
kono
parents:
diff changeset
3728 info.func = gimple_call_fndecl (info.callstmt);
kono
parents:
diff changeset
3729 info.fncode = DECL_FUNCTION_CODE (info.func);
kono
parents:
diff changeset
3730
kono
parents:
diff changeset
3731 /* The size of the destination as in snprintf(dest, size, ...). */
kono
parents:
diff changeset
3732 unsigned HOST_WIDE_INT dstsize = HOST_WIDE_INT_M1U;
kono
parents:
diff changeset
3733
kono
parents:
diff changeset
3734 /* The size of the destination determined by __builtin_object_size. */
kono
parents:
diff changeset
3735 unsigned HOST_WIDE_INT objsize = HOST_WIDE_INT_M1U;
kono
parents:
diff changeset
3736
kono
parents:
diff changeset
3737 /* Buffer size argument number (snprintf and vsnprintf). */
kono
parents:
diff changeset
3738 unsigned HOST_WIDE_INT idx_dstsize = HOST_WIDE_INT_M1U;
kono
parents:
diff changeset
3739
kono
parents:
diff changeset
3740 /* Object size argument number (snprintf_chk and vsnprintf_chk). */
kono
parents:
diff changeset
3741 unsigned HOST_WIDE_INT idx_objsize = HOST_WIDE_INT_M1U;
kono
parents:
diff changeset
3742
kono
parents:
diff changeset
3743 /* Format string argument number (valid for all functions). */
kono
parents:
diff changeset
3744 unsigned idx_format;
kono
parents:
diff changeset
3745
kono
parents:
diff changeset
3746 switch (info.fncode)
kono
parents:
diff changeset
3747 {
kono
parents:
diff changeset
3748 case BUILT_IN_SPRINTF:
kono
parents:
diff changeset
3749 // Signature:
kono
parents:
diff changeset
3750 // __builtin_sprintf (dst, format, ...)
kono
parents:
diff changeset
3751 idx_format = 1;
kono
parents:
diff changeset
3752 info.argidx = 2;
kono
parents:
diff changeset
3753 break;
kono
parents:
diff changeset
3754
kono
parents:
diff changeset
3755 case BUILT_IN_SPRINTF_CHK:
kono
parents:
diff changeset
3756 // Signature:
kono
parents:
diff changeset
3757 // __builtin___sprintf_chk (dst, ost, objsize, format, ...)
kono
parents:
diff changeset
3758 idx_objsize = 2;
kono
parents:
diff changeset
3759 idx_format = 3;
kono
parents:
diff changeset
3760 info.argidx = 4;
kono
parents:
diff changeset
3761 break;
kono
parents:
diff changeset
3762
kono
parents:
diff changeset
3763 case BUILT_IN_SNPRINTF:
kono
parents:
diff changeset
3764 // Signature:
kono
parents:
diff changeset
3765 // __builtin_snprintf (dst, size, format, ...)
kono
parents:
diff changeset
3766 idx_dstsize = 1;
kono
parents:
diff changeset
3767 idx_format = 2;
kono
parents:
diff changeset
3768 info.argidx = 3;
kono
parents:
diff changeset
3769 info.bounded = true;
kono
parents:
diff changeset
3770 break;
kono
parents:
diff changeset
3771
kono
parents:
diff changeset
3772 case BUILT_IN_SNPRINTF_CHK:
kono
parents:
diff changeset
3773 // Signature:
kono
parents:
diff changeset
3774 // __builtin___snprintf_chk (dst, size, ost, objsize, format, ...)
kono
parents:
diff changeset
3775 idx_dstsize = 1;
kono
parents:
diff changeset
3776 idx_objsize = 3;
kono
parents:
diff changeset
3777 idx_format = 4;
kono
parents:
diff changeset
3778 info.argidx = 5;
kono
parents:
diff changeset
3779 info.bounded = true;
kono
parents:
diff changeset
3780 break;
kono
parents:
diff changeset
3781
kono
parents:
diff changeset
3782 case BUILT_IN_VSNPRINTF:
kono
parents:
diff changeset
3783 // Signature:
kono
parents:
diff changeset
3784 // __builtin_vsprintf (dst, size, format, va)
kono
parents:
diff changeset
3785 idx_dstsize = 1;
kono
parents:
diff changeset
3786 idx_format = 2;
kono
parents:
diff changeset
3787 info.argidx = -1;
kono
parents:
diff changeset
3788 info.bounded = true;
kono
parents:
diff changeset
3789 break;
kono
parents:
diff changeset
3790
kono
parents:
diff changeset
3791 case BUILT_IN_VSNPRINTF_CHK:
kono
parents:
diff changeset
3792 // Signature:
kono
parents:
diff changeset
3793 // __builtin___vsnprintf_chk (dst, size, ost, objsize, format, va)
kono
parents:
diff changeset
3794 idx_dstsize = 1;
kono
parents:
diff changeset
3795 idx_objsize = 3;
kono
parents:
diff changeset
3796 idx_format = 4;
kono
parents:
diff changeset
3797 info.argidx = -1;
kono
parents:
diff changeset
3798 info.bounded = true;
kono
parents:
diff changeset
3799 break;
kono
parents:
diff changeset
3800
kono
parents:
diff changeset
3801 case BUILT_IN_VSPRINTF:
kono
parents:
diff changeset
3802 // Signature:
kono
parents:
diff changeset
3803 // __builtin_vsprintf (dst, format, va)
kono
parents:
diff changeset
3804 idx_format = 1;
kono
parents:
diff changeset
3805 info.argidx = -1;
kono
parents:
diff changeset
3806 break;
kono
parents:
diff changeset
3807
kono
parents:
diff changeset
3808 case BUILT_IN_VSPRINTF_CHK:
kono
parents:
diff changeset
3809 // Signature:
kono
parents:
diff changeset
3810 // __builtin___vsprintf_chk (dst, ost, objsize, format, va)
kono
parents:
diff changeset
3811 idx_format = 3;
kono
parents:
diff changeset
3812 idx_objsize = 2;
kono
parents:
diff changeset
3813 info.argidx = -1;
kono
parents:
diff changeset
3814 break;
kono
parents:
diff changeset
3815
kono
parents:
diff changeset
3816 default:
kono
parents:
diff changeset
3817 return false;
kono
parents:
diff changeset
3818 }
kono
parents:
diff changeset
3819
kono
parents:
diff changeset
3820 /* Set the global warning level for this function. */
kono
parents:
diff changeset
3821 warn_level = info.bounded ? warn_format_trunc : warn_format_overflow;
kono
parents:
diff changeset
3822
kono
parents:
diff changeset
3823 /* The first argument is a pointer to the destination. */
kono
parents:
diff changeset
3824 tree dstptr = gimple_call_arg (info.callstmt, 0);
kono
parents:
diff changeset
3825
kono
parents:
diff changeset
3826 info.format = gimple_call_arg (info.callstmt, idx_format);
kono
parents:
diff changeset
3827
kono
parents:
diff changeset
3828 /* True when the destination size is constant as opposed to the lower
kono
parents:
diff changeset
3829 or upper bound of a range. */
kono
parents:
diff changeset
3830 bool dstsize_cst_p = true;
kono
parents:
diff changeset
3831
kono
parents:
diff changeset
3832 if (idx_dstsize == HOST_WIDE_INT_M1U)
kono
parents:
diff changeset
3833 {
kono
parents:
diff changeset
3834 /* For non-bounded functions like sprintf, determine the size
kono
parents:
diff changeset
3835 of the destination from the object or pointer passed to it
kono
parents:
diff changeset
3836 as the first argument. */
kono
parents:
diff changeset
3837 dstsize = get_destination_size (dstptr);
kono
parents:
diff changeset
3838 }
kono
parents:
diff changeset
3839 else if (tree size = gimple_call_arg (info.callstmt, idx_dstsize))
kono
parents:
diff changeset
3840 {
kono
parents:
diff changeset
3841 /* For bounded functions try to get the size argument. */
kono
parents:
diff changeset
3842
kono
parents:
diff changeset
3843 if (TREE_CODE (size) == INTEGER_CST)
kono
parents:
diff changeset
3844 {
kono
parents:
diff changeset
3845 dstsize = tree_to_uhwi (size);
kono
parents:
diff changeset
3846 /* No object can be larger than SIZE_MAX bytes (half the address
kono
parents:
diff changeset
3847 space) on the target.
kono
parents:
diff changeset
3848 The functions are defined only for output of at most INT_MAX
kono
parents:
diff changeset
3849 bytes. Specifying a bound in excess of that limit effectively
kono
parents:
diff changeset
3850 defeats the bounds checking (and on some implementations such
kono
parents:
diff changeset
3851 as Solaris cause the function to fail with EINVAL). */
kono
parents:
diff changeset
3852 if (dstsize > target_size_max () / 2)
kono
parents:
diff changeset
3853 {
kono
parents:
diff changeset
3854 /* Avoid warning if -Wstringop-overflow is specified since
kono
parents:
diff changeset
3855 it also warns for the same thing though only for the
kono
parents:
diff changeset
3856 checking built-ins. */
kono
parents:
diff changeset
3857 if ((idx_objsize == HOST_WIDE_INT_M1U
kono
parents:
diff changeset
3858 || !warn_stringop_overflow))
kono
parents:
diff changeset
3859 warning_at (gimple_location (info.callstmt), info.warnopt (),
kono
parents:
diff changeset
3860 "specified bound %wu exceeds maximum object size "
kono
parents:
diff changeset
3861 "%wu",
kono
parents:
diff changeset
3862 dstsize, target_size_max () / 2);
kono
parents:
diff changeset
3863 }
kono
parents:
diff changeset
3864 else if (dstsize > target_int_max ())
kono
parents:
diff changeset
3865 warning_at (gimple_location (info.callstmt), info.warnopt (),
kono
parents:
diff changeset
3866 "specified bound %wu exceeds %<INT_MAX%>",
kono
parents:
diff changeset
3867 dstsize);
kono
parents:
diff changeset
3868 }
kono
parents:
diff changeset
3869 else if (TREE_CODE (size) == SSA_NAME)
kono
parents:
diff changeset
3870 {
kono
parents:
diff changeset
3871 /* Try to determine the range of values of the argument
kono
parents:
diff changeset
3872 and use the greater of the two at level 1 and the smaller
kono
parents:
diff changeset
3873 of them at level 2. */
kono
parents:
diff changeset
3874 wide_int min, max;
kono
parents:
diff changeset
3875 enum value_range_type range_type
kono
parents:
diff changeset
3876 = get_range_info (size, &min, &max);
kono
parents:
diff changeset
3877 if (range_type == VR_RANGE)
kono
parents:
diff changeset
3878 {
kono
parents:
diff changeset
3879 dstsize
kono
parents:
diff changeset
3880 = (warn_level < 2
kono
parents:
diff changeset
3881 ? wi::fits_uhwi_p (max) ? max.to_uhwi () : max.to_shwi ()
kono
parents:
diff changeset
3882 : wi::fits_uhwi_p (min) ? min.to_uhwi () : min.to_shwi ());
kono
parents:
diff changeset
3883 }
kono
parents:
diff changeset
3884
kono
parents:
diff changeset
3885 /* The destination size is not constant. If the function is
kono
parents:
diff changeset
3886 bounded (e.g., snprintf) a lower bound of zero doesn't
kono
parents:
diff changeset
3887 necessarily imply it can be eliminated. */
kono
parents:
diff changeset
3888 dstsize_cst_p = false;
kono
parents:
diff changeset
3889 }
kono
parents:
diff changeset
3890 }
kono
parents:
diff changeset
3891
kono
parents:
diff changeset
3892 if (idx_objsize != HOST_WIDE_INT_M1U)
kono
parents:
diff changeset
3893 if (tree size = gimple_call_arg (info.callstmt, idx_objsize))
kono
parents:
diff changeset
3894 if (tree_fits_uhwi_p (size))
kono
parents:
diff changeset
3895 objsize = tree_to_uhwi (size);
kono
parents:
diff changeset
3896
kono
parents:
diff changeset
3897 if (info.bounded && !dstsize)
kono
parents:
diff changeset
3898 {
kono
parents:
diff changeset
3899 /* As a special case, when the explicitly specified destination
kono
parents:
diff changeset
3900 size argument (to a bounded function like snprintf) is zero
kono
parents:
diff changeset
3901 it is a request to determine the number of bytes on output
kono
parents:
diff changeset
3902 without actually producing any. Pretend the size is
kono
parents:
diff changeset
3903 unlimited in this case. */
kono
parents:
diff changeset
3904 info.objsize = HOST_WIDE_INT_MAX;
kono
parents:
diff changeset
3905 info.nowrite = dstsize_cst_p;
kono
parents:
diff changeset
3906 }
kono
parents:
diff changeset
3907 else
kono
parents:
diff changeset
3908 {
kono
parents:
diff changeset
3909 /* For calls to non-bounded functions or to those of bounded
kono
parents:
diff changeset
3910 functions with a non-zero size, warn if the destination
kono
parents:
diff changeset
3911 pointer is null. */
kono
parents:
diff changeset
3912 if (integer_zerop (dstptr))
kono
parents:
diff changeset
3913 {
kono
parents:
diff changeset
3914 /* This is diagnosed with -Wformat only when the null is a constant
kono
parents:
diff changeset
3915 pointer. The warning here diagnoses instances where the pointer
kono
parents:
diff changeset
3916 is not constant. */
kono
parents:
diff changeset
3917 location_t loc = gimple_location (info.callstmt);
kono
parents:
diff changeset
3918 warning_at (EXPR_LOC_OR_LOC (dstptr, loc),
kono
parents:
diff changeset
3919 info.warnopt (), "null destination pointer");
kono
parents:
diff changeset
3920 return false;
kono
parents:
diff changeset
3921 }
kono
parents:
diff changeset
3922
kono
parents:
diff changeset
3923 /* Set the object size to the smaller of the two arguments
kono
parents:
diff changeset
3924 of both have been specified and they're not equal. */
kono
parents:
diff changeset
3925 info.objsize = dstsize < objsize ? dstsize : objsize;
kono
parents:
diff changeset
3926
kono
parents:
diff changeset
3927 if (info.bounded
kono
parents:
diff changeset
3928 && dstsize < target_size_max () / 2 && objsize < dstsize
kono
parents:
diff changeset
3929 /* Avoid warning if -Wstringop-overflow is specified since
kono
parents:
diff changeset
3930 it also warns for the same thing though only for the
kono
parents:
diff changeset
3931 checking built-ins. */
kono
parents:
diff changeset
3932 && (idx_objsize == HOST_WIDE_INT_M1U
kono
parents:
diff changeset
3933 || !warn_stringop_overflow))
kono
parents:
diff changeset
3934 {
kono
parents:
diff changeset
3935 warning_at (gimple_location (info.callstmt), info.warnopt (),
kono
parents:
diff changeset
3936 "specified bound %wu exceeds the size %wu "
kono
parents:
diff changeset
3937 "of the destination object", dstsize, objsize);
kono
parents:
diff changeset
3938 }
kono
parents:
diff changeset
3939 }
kono
parents:
diff changeset
3940
kono
parents:
diff changeset
3941 if (integer_zerop (info.format))
kono
parents:
diff changeset
3942 {
kono
parents:
diff changeset
3943 /* This is diagnosed with -Wformat only when the null is a constant
kono
parents:
diff changeset
3944 pointer. The warning here diagnoses instances where the pointer
kono
parents:
diff changeset
3945 is not constant. */
kono
parents:
diff changeset
3946 location_t loc = gimple_location (info.callstmt);
kono
parents:
diff changeset
3947 warning_at (EXPR_LOC_OR_LOC (info.format, loc),
kono
parents:
diff changeset
3948 info.warnopt (), "null format string");
kono
parents:
diff changeset
3949 return false;
kono
parents:
diff changeset
3950 }
kono
parents:
diff changeset
3951
kono
parents:
diff changeset
3952 info.fmtstr = get_format_string (info.format, &info.fmtloc);
kono
parents:
diff changeset
3953 if (!info.fmtstr)
kono
parents:
diff changeset
3954 return false;
kono
parents:
diff changeset
3955
kono
parents:
diff changeset
3956 /* The result is the number of bytes output by the formatted function,
kono
parents:
diff changeset
3957 including the terminating NUL. */
kono
parents:
diff changeset
3958 format_result res = format_result ();
kono
parents:
diff changeset
3959
kono
parents:
diff changeset
3960 bool success = compute_format_length (info, &res);
kono
parents:
diff changeset
3961
kono
parents:
diff changeset
3962 /* When optimizing and the printf return value optimization is enabled,
kono
parents:
diff changeset
3963 attempt to substitute the computed result for the return value of
kono
parents:
diff changeset
3964 the call. Avoid this optimization when -frounding-math is in effect
kono
parents:
diff changeset
3965 and the format string contains a floating point directive. */
kono
parents:
diff changeset
3966 bool call_removed = false;
kono
parents:
diff changeset
3967 if (success && optimize > 0)
kono
parents:
diff changeset
3968 {
kono
parents:
diff changeset
3969 /* Save a copy of the iterator pointing at the call. The iterator
kono
parents:
diff changeset
3970 may change to point past the call in try_substitute_return_value
kono
parents:
diff changeset
3971 but the original value is needed in try_simplify_call. */
kono
parents:
diff changeset
3972 gimple_stmt_iterator gsi_call = *gsi;
kono
parents:
diff changeset
3973
kono
parents:
diff changeset
3974 if (flag_printf_return_value
kono
parents:
diff changeset
3975 && (!flag_rounding_math || !res.floating))
kono
parents:
diff changeset
3976 call_removed = try_substitute_return_value (gsi, info, res);
kono
parents:
diff changeset
3977
kono
parents:
diff changeset
3978 if (!call_removed)
kono
parents:
diff changeset
3979 try_simplify_call (&gsi_call, info, res);
kono
parents:
diff changeset
3980 }
kono
parents:
diff changeset
3981
kono
parents:
diff changeset
3982 return call_removed;
kono
parents:
diff changeset
3983 }
kono
parents:
diff changeset
3984
kono
parents:
diff changeset
3985 /* Execute the pass for function FUN. */
kono
parents:
diff changeset
3986
kono
parents:
diff changeset
3987 unsigned int
kono
parents:
diff changeset
3988 pass_sprintf_length::execute (function *fun)
kono
parents:
diff changeset
3989 {
kono
parents:
diff changeset
3990 init_target_to_host_charmap ();
kono
parents:
diff changeset
3991
kono
parents:
diff changeset
3992 basic_block bb;
kono
parents:
diff changeset
3993 FOR_EACH_BB_FN (bb, fun)
kono
parents:
diff changeset
3994 {
kono
parents:
diff changeset
3995 for (gimple_stmt_iterator si = gsi_start_bb (bb); !gsi_end_p (si); )
kono
parents:
diff changeset
3996 {
kono
parents:
diff changeset
3997 /* Iterate over statements, looking for function calls. */
kono
parents:
diff changeset
3998 gimple *stmt = gsi_stmt (si);
kono
parents:
diff changeset
3999
kono
parents:
diff changeset
4000 if (is_gimple_call (stmt) && handle_gimple_call (&si))
kono
parents:
diff changeset
4001 /* If handle_gimple_call returns true, the iterator is
kono
parents:
diff changeset
4002 already pointing to the next statement. */
kono
parents:
diff changeset
4003 continue;
kono
parents:
diff changeset
4004
kono
parents:
diff changeset
4005 gsi_next (&si);
kono
parents:
diff changeset
4006 }
kono
parents:
diff changeset
4007 }
kono
parents:
diff changeset
4008
kono
parents:
diff changeset
4009 /* Clean up object size info. */
kono
parents:
diff changeset
4010 fini_object_sizes ();
kono
parents:
diff changeset
4011
kono
parents:
diff changeset
4012 return 0;
kono
parents:
diff changeset
4013 }
kono
parents:
diff changeset
4014
kono
parents:
diff changeset
4015 } /* Unnamed namespace. */
kono
parents:
diff changeset
4016
kono
parents:
diff changeset
4017 /* Return a pointer to a pass object newly constructed from the context
kono
parents:
diff changeset
4018 CTXT. */
kono
parents:
diff changeset
4019
kono
parents:
diff changeset
4020 gimple_opt_pass *
kono
parents:
diff changeset
4021 make_pass_sprintf_length (gcc::context *ctxt)
kono
parents:
diff changeset
4022 {
kono
parents:
diff changeset
4023 return new pass_sprintf_length (ctxt);
kono
parents:
diff changeset
4024 }