]> git.ipfire.org Git - thirdparty/gcc.git/blob - gcc/gimple-range-infer.cc
612f66626af35dede7173f623b8621a9c0963c36
[thirdparty/gcc.git] / gcc / gimple-range-infer.cc
1 /* Gimple range inference implementation.
2 Copyright (C) 2022-2025 Free Software Foundation, Inc.
3 Contributed by Andrew MacLeod <amacleod@redhat.com>.
4
5 This file is part of GCC.
6
7 GCC is free software; you can redistribute it and/or modify
8 it under the terms of the GNU General Public License as published by
9 the Free Software Foundation; either version 3, or (at your option)
10 any later version.
11
12 GCC is distributed in the hope that it will be useful,
13 but WITHOUT ANY WARRANTY; without even the implied warranty of
14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 GNU General Public License for more details.
16
17 You should have received a copy of the GNU General Public License
18 along with GCC; see the file COPYING3. If not see
19 <http://www.gnu.org/licenses/>. */
20
21 #include "config.h"
22 #include "system.h"
23 #include "coretypes.h"
24 #include "backend.h"
25 #include "insn-codes.h"
26 #include "tree.h"
27 #include "gimple.h"
28 #include "ssa.h"
29 #include "gimple-pretty-print.h"
30 #include "gimple-range.h"
31 #include "value-range-storage.h"
32 #include "tree-cfg.h"
33 #include "target.h"
34 #include "attribs.h"
35 #include "gimple-iterator.h"
36 #include "gimple-walk.h"
37 #include "cfganal.h"
38 #include "tree-dfa.h"
39
40 // Create the global oracle.
41
42 infer_range_oracle infer_oracle;
43
44 // This class is merely an accessor which is granted internals to
45 // gimple_infer_range such that non_null_loadstore as a static callback can
46 // call the protected add_nonzero ().
47 // Static functions ccannot be friends, so we do it through a class wrapper.
48
49 class non_null_wrapper
50 {
51 public:
52 inline non_null_wrapper (gimple_infer_range *infer) : m_infer (infer) { }
53 inline void add_nonzero (tree name) { m_infer->add_nonzero (name); }
54 inline void add_range (tree t, vrange &r) { m_infer->add_range (t, r); }
55 private:
56 gimple_infer_range *m_infer;
57 };
58
59 // Adapted from infer_nonnull_range_by_dereference and check_loadstore
60 // to process nonnull ssa_name OP in S. DATA contains a pointer to a
61 // stmt range inference instance.
62
63 static bool
64 non_null_loadstore (gimple *, tree op, tree, void *data)
65 {
66 if (TREE_CODE (op) == MEM_REF || TREE_CODE (op) == TARGET_MEM_REF)
67 {
68 /* Some address spaces may legitimately dereference zero. */
69 addr_space_t as = TYPE_ADDR_SPACE (TREE_TYPE (op));
70 if (!targetm.addr_space.zero_address_valid (as))
71 {
72 non_null_wrapper wrapper ((gimple_infer_range *)data);
73 wrapper.add_nonzero (TREE_OPERAND (op, 0));
74 }
75 }
76 return false;
77 }
78
79 // Process an ASSUME call to see if there are any inferred ranges available.
80
81 void
82 gimple_infer_range::check_assume_func (gcall *call)
83 {
84 tree arg;
85 unsigned i;
86 tree assume_id = TREE_OPERAND (gimple_call_arg (call, 0), 0);
87 if (!assume_id)
88 return;
89 struct function *fun = DECL_STRUCT_FUNCTION (assume_id);
90 if (!fun)
91 return;
92 // Loop over arguments, matching them to the assume parameters.
93 for (arg = DECL_ARGUMENTS (assume_id), i = 1;
94 arg && i < gimple_call_num_args (call);
95 i++, arg = DECL_CHAIN (arg))
96 {
97 tree op = gimple_call_arg (call, i);
98 tree type = TREE_TYPE (op);
99 if (gimple_range_ssa_p (op) && value_range::supports_type_p (type))
100 {
101 tree default_def = ssa_default_def (fun, arg);
102 if (!default_def || type != TREE_TYPE (default_def))
103 continue;
104 // Query the global range of the default def in the assume function.
105 value_range assume_range (type);
106 gimple_range_global (assume_range, default_def, fun);
107 // If there is a non-varying result, add it as an inferred range.
108 if (!assume_range.varying_p ())
109 {
110 add_range (op, assume_range);
111 if (dump_file)
112 {
113 print_generic_expr (dump_file, assume_id, TDF_SLIM);
114 fprintf (dump_file, " assume inferred range of ");
115 print_generic_expr (dump_file, op, TDF_SLIM);
116 fprintf (dump_file, " (param ");
117 print_generic_expr (dump_file, arg, TDF_SLIM);
118 fprintf (dump_file, ") = ");
119 assume_range.dump (dump_file);
120 fputc ('\n', dump_file);
121 }
122 }
123 }
124 }
125 }
126
127 // Add NAME and RANGE to the range inference summary.
128
129 void
130 gimple_infer_range::add_range (tree name, vrange &range)
131 {
132 // Do not add an inferred range if it is VARYING.
133 if (range.varying_p ())
134 return;
135 m_names[num_args] = name;
136 m_ranges[num_args] = range;
137 if (num_args < size_limit - 1)
138 num_args++;
139 }
140
141 // Add a nonzero range for NAME to the range inference summary.
142
143 void
144 gimple_infer_range::add_nonzero (tree name)
145 {
146 if (!gimple_range_ssa_p (name))
147 return;
148 prange nz;
149 nz.set_nonzero (TREE_TYPE (name));
150 add_range (name, nz);
151 }
152
153 // Process S for range inference and fill in the summary list.
154 // This is the routine where any new inferred ranges should be added.
155 // If USE_RANGEOPS is true, invoke range-ops on stmts with a single
156 // ssa-name a constant to reflect an inferred range. ie
157 // x_2 = y_3 + 1 will provide an inferred range for y_3 of [-INF, +INF - 1].
158 // This defaults to FALSE as it can be expensive.,
159
160 gimple_infer_range::gimple_infer_range (gimple *s, range_query *q,
161 bool use_rangeops)
162 {
163 num_args = 0;
164
165 if (is_a<gphi *> (s))
166 return;
167
168 // Default to the global query if none provided.
169 if (!q)
170 q = get_global_range_query ();
171
172 if (is_a<gcall *> (s) && flag_delete_null_pointer_checks)
173 {
174 tree fntype = gimple_call_fntype (s);
175 bitmap nonnullargs = get_nonnull_args (fntype);
176 // Process any non-null arguments
177 if (nonnullargs)
178 {
179 for (unsigned i = 0; i < gimple_call_num_args (s); i++)
180 {
181 if (bitmap_empty_p (nonnullargs)
182 || bitmap_bit_p (nonnullargs, i))
183 {
184 tree op = gimple_call_arg (s, i);
185 if (POINTER_TYPE_P (TREE_TYPE (op)))
186 add_nonzero (op);
187 }
188 }
189 BITMAP_FREE (nonnullargs);
190 }
191 if (fntype)
192 for (tree attrs = TYPE_ATTRIBUTES (fntype);
193 (attrs = lookup_attribute ("nonnull_if_nonzero", attrs));
194 attrs = TREE_CHAIN (attrs))
195 {
196 tree args = TREE_VALUE (attrs);
197 unsigned int idx = TREE_INT_CST_LOW (TREE_VALUE (args)) - 1;
198 unsigned int idx2
199 = TREE_INT_CST_LOW (TREE_VALUE (TREE_CHAIN (args))) - 1;
200 unsigned int idx3 = idx2;
201 if (tree chain2 = TREE_CHAIN (TREE_CHAIN (args)))
202 idx3 = TREE_INT_CST_LOW (TREE_VALUE (chain2)) - 1;
203 if (idx < gimple_call_num_args (s)
204 && idx2 < gimple_call_num_args (s)
205 && idx3 < gimple_call_num_args (s))
206 {
207 tree arg = gimple_call_arg (s, idx);
208 tree arg2 = gimple_call_arg (s, idx2);
209 tree arg3 = gimple_call_arg (s, idx3);
210 if (!POINTER_TYPE_P (TREE_TYPE (arg))
211 || !INTEGRAL_TYPE_P (TREE_TYPE (arg2))
212 || !INTEGRAL_TYPE_P (TREE_TYPE (arg3))
213 || integer_zerop (arg2)
214 || integer_zerop (arg3))
215 continue;
216 if (integer_nonzerop (arg2) && integer_nonzerop (arg3))
217 add_nonzero (arg);
218 else
219 {
220 value_range r (TREE_TYPE (arg2));
221 if (q->range_of_expr (r, arg2, s)
222 && !r.contains_p (build_zero_cst (TREE_TYPE (arg2))))
223 {
224 if (idx2 == idx3)
225 add_nonzero (arg);
226 else
227 {
228 value_range r2 (TREE_TYPE (arg3));
229 tree zero3 = build_zero_cst (TREE_TYPE (arg3));
230 if (q->range_of_expr (r2, arg3, s)
231 && !r2.contains_p (zero3))
232 add_nonzero (arg);
233 }
234 }
235 }
236 }
237 }
238 // Fallthru and walk load/store ops now.
239 }
240
241 // Check for inferred ranges from ASSUME calls.
242 if (is_a<gcall *> (s) && gimple_call_internal_p (s)
243 && gimple_call_internal_fn (s) == IFN_ASSUME)
244 check_assume_func (as_a<gcall *> (s));
245
246 // Look for possible non-null values.
247 if (flag_delete_null_pointer_checks && gimple_code (s) != GIMPLE_ASM
248 && !gimple_clobber_p (s))
249 walk_stmt_load_store_ops (s, (void *)this, non_null_loadstore,
250 non_null_loadstore);
251
252 // Gated by flag.
253 if (!use_rangeops)
254 return;
255
256 // Check if there are any inferred ranges from range-ops.
257 gimple_range_op_handler handler (s);
258 if (!handler)
259 return;
260
261 // Only proceed if ONE operand is an SSA_NAME, This may provide an
262 // inferred range for 'y + 3' , but will bypass expressions like
263 // 'y + z' as it depends on symbolic values.
264 tree ssa1 = gimple_range_ssa_p (handler.operand1 ());
265 tree ssa2 = gimple_range_ssa_p (handler.operand2 ());
266 if ((ssa1 != NULL) == (ssa2 != NULL))
267 return;
268
269 // The other operand should be a constant, so just use the global range
270 // query to pick up any other values.
271 if (ssa1)
272 {
273 value_range op1 (TREE_TYPE (ssa1));
274 if (op1_range (op1, s, q) && !op1.varying_p ())
275 add_range (ssa1, op1);
276 }
277 else
278 {
279 gcc_checking_assert (ssa2);
280 value_range op2 (TREE_TYPE (ssa2));
281 if (op2_range (op2, s, q) && !op2.varying_p ())
282 add_range (ssa2, op2);
283 }
284 }
285
286 // Create an single inferred range for NAMe using range R.
287
288 gimple_infer_range::gimple_infer_range (tree name, vrange &r)
289 {
290 num_args = 0;
291 add_range (name, r);
292 }
293
294 // -------------------------------------------------------------------------
295
296 // This class is an element in the list of inferred ranges.
297
298 class exit_range
299 {
300 public:
301 tree name;
302 gimple *stmt;
303 vrange_storage *range;
304 exit_range *next;
305 };
306
307
308 // If there is an element which matches SSA, return a pointer to the element.
309 // Otherwise return NULL.
310
311 exit_range *
312 infer_range_manager::exit_range_head::find_ptr (tree ssa)
313 {
314 // Return NULL if SSA is not in this list.
315 if (!m_names || !bitmap_bit_p (m_names, SSA_NAME_VERSION (ssa)))
316 return NULL;
317 for (exit_range *ptr = head; ptr != NULL; ptr = ptr->next)
318 if (ptr->name == ssa)
319 return ptr;
320 // Should be unreachable.
321 gcc_unreachable ();
322 return NULL;
323 }
324
325 // Construct a range infer manager. DO_SEARCH indicates whether an immediate
326 // use scan should be made the first time a name is processed. This is for
327 // on-demand clients who may not visit every statement and may miss uses.
328 // Q is the range_query to use for any lookups. Default is NULL which maps
329 // to the global_range_query.
330
331 infer_range_manager::infer_range_manager (bool do_search, range_query *q)
332 {
333 // Set the range query to use.
334 m_query = q ? q : get_global_range_query ();
335
336 bitmap_obstack_initialize (&m_bitmaps);
337 m_on_exit.create (0);
338 m_on_exit.safe_grow_cleared (last_basic_block_for_fn (cfun) + 1);
339 // m_seen == NULL indicates no scanning. Otherwise the bit indicates a
340 // scan has been performed on NAME.
341 if (do_search)
342 m_seen = BITMAP_ALLOC (&m_bitmaps);
343 else
344 m_seen = NULL;
345 obstack_init (&m_list_obstack);
346 // Non-zero elements are very common, so cache them for each ssa-name.
347 m_nonzero.create (0);
348 m_nonzero.safe_grow_cleared (num_ssa_names + 1);
349 m_range_allocator = new vrange_allocator;
350 }
351
352 // Destruct a range infer manager.
353
354 infer_range_manager::~infer_range_manager ()
355 {
356 m_nonzero.release ();
357 obstack_free (&m_list_obstack, NULL);
358 m_on_exit.release ();
359 bitmap_obstack_release (&m_bitmaps);
360 delete m_range_allocator;
361 }
362
363 // Return a non-zero range value of the appropriate type for NAME from
364 // the cache, creating it if necessary.
365
366 const vrange&
367 infer_range_manager::get_nonzero (tree name)
368 {
369 unsigned v = SSA_NAME_VERSION (name);
370 if (v >= m_nonzero.length ())
371 m_nonzero.safe_grow_cleared (num_ssa_names + 20);
372 if (!m_nonzero[v])
373 {
374 m_nonzero[v]
375 = (irange *) m_range_allocator->alloc (sizeof (int_range <2>));
376 m_nonzero[v]->set_nonzero (TREE_TYPE (name));
377 }
378 return *(m_nonzero[v]);
379 }
380
381 // Return TRUE if NAME has a range inference in block BB. If NAME is NULL,
382 // return TRUE if there are any name sin BB.
383
384 bool
385 infer_range_manager::has_range_p (basic_block bb, tree name)
386 {
387 // Check if this is an immediate use search model.
388 if (name && m_seen && !bitmap_bit_p (m_seen, SSA_NAME_VERSION (name)))
389 register_all_uses (name);
390
391 if (bb->index >= (int)m_on_exit.length ())
392 return false;
393
394 bitmap b = m_on_exit[bb->index].m_names;
395 if (!b)
396 return false;
397
398 if (name)
399 return bitmap_bit_p (m_on_exit[bb->index].m_names, SSA_NAME_VERSION (name));
400 return !bitmap_empty_p (b);
401 }
402
403 // Return TRUE if NAME has a range inference in block BB, and adjust range R
404 // to include it.
405
406 bool
407 infer_range_manager::maybe_adjust_range (vrange &r, tree name, basic_block bb)
408 {
409 if (!has_range_p (bb, name))
410 return false;
411 exit_range *ptr = m_on_exit[bb->index].find_ptr (name);
412 gcc_checking_assert (ptr);
413 // Return true if this exit range changes R, otherwise false.
414 tree type = TREE_TYPE (name);
415 value_range tmp (type);
416 ptr->range->get_vrange (tmp, type);
417 return r.intersect (tmp);
418 }
419
420 // Add all inferred ranges in INFER at stmt S.
421
422 void
423 infer_range_manager::add_ranges (gimple *s, gimple_infer_range &infer)
424 {
425 for (unsigned x = 0; x < infer.num (); x++)
426 {
427 tree arg = infer.name (x);
428 value_range r (TREE_TYPE (arg));
429 m_query->range_of_expr (r, arg, s);
430 // Only add the inferred range if it changes the current range.
431 if (r.intersect (infer.range (x)))
432 add_range (arg, s, infer.range (x));
433 }
434 }
435
436 // Add range R as an inferred range for NAME on stmt S.
437
438 void
439 infer_range_manager::add_range (tree name, gimple *s, const vrange &r)
440 {
441 basic_block bb = gimple_bb (s);
442 if (!bb)
443 return;
444 if (bb->index >= (int)m_on_exit.length ())
445 m_on_exit.safe_grow_cleared (last_basic_block_for_fn (cfun) + 1);
446
447 // Create the summary list bitmap if it doesn't exist.
448 if (!m_on_exit[bb->index].m_names)
449 m_on_exit[bb->index].m_names = BITMAP_ALLOC (&m_bitmaps);
450
451 if (dump_file && (dump_flags & TDF_DETAILS))
452 {
453 fprintf (dump_file, " on-exit update ");
454 print_generic_expr (dump_file, name, TDF_SLIM);
455 fprintf (dump_file, " in BB%d : ",bb->index);
456 r.dump (dump_file);
457 fprintf (dump_file, "\n");
458 }
459
460 // If NAME already has a range, intersect them and done.
461 exit_range *ptr = m_on_exit[bb->index].find_ptr (name);
462 if (ptr)
463 {
464 tree type = TREE_TYPE (name);
465 value_range cur (r), name_range (type);
466 ptr->range->get_vrange (name_range, type);
467 // If no new info is added, just return.
468 if (!cur.intersect (name_range))
469 return;
470 if (ptr->range->fits_p (cur))
471 ptr->range->set_vrange (cur);
472 else
473 ptr->range = m_range_allocator->clone (cur);
474 ptr->stmt = s;
475 return;
476 }
477
478 // Otherwise create a record.
479 bitmap_set_bit (m_on_exit[bb->index].m_names, SSA_NAME_VERSION (name));
480 ptr = (exit_range *)obstack_alloc (&m_list_obstack, sizeof (exit_range));
481 ptr->range = m_range_allocator->clone (r);
482 ptr->name = name;
483 ptr->stmt = s;
484 ptr->next = m_on_exit[bb->index].head;
485 m_on_exit[bb->index].head = ptr;
486 }
487
488 // Add a non-zero inferred range for NAME at stmt S.
489
490 void
491 infer_range_manager::add_nonzero (tree name, gimple *s)
492 {
493 add_range (name, s, get_nonzero (name));
494 }
495
496 // Follow immediate use chains and find all inferred ranges for NAME.
497
498 void
499 infer_range_manager::register_all_uses (tree name)
500 {
501 gcc_checking_assert (m_seen);
502
503 // Check if we've already processed this name.
504 unsigned v = SSA_NAME_VERSION (name);
505 if (bitmap_bit_p (m_seen, v))
506 return;
507 bitmap_set_bit (m_seen, v);
508
509 use_operand_p use_p;
510 imm_use_iterator iter;
511
512 // Loop over each immediate use and see if it has an inferred range.
513 FOR_EACH_IMM_USE_FAST (use_p, iter, name)
514 {
515 gimple *s = USE_STMT (use_p);
516 gimple_infer_range infer (s, m_query);
517 for (unsigned x = 0; x < infer.num (); x++)
518 {
519 if (name == infer.name (x))
520 add_range (name, s, infer.range (x));
521 }
522 }
523 }