1 /* Gimple range inference implementation.
2 Copyright (C) 2022-2023 Free Software Foundation, Inc.
3 Contributed by Andrew MacLeod <amacleod@redhat.com>.
5 This file is part of GCC.
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)
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.
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/>. */
23 #include "coretypes.h"
25 #include "insn-codes.h"
29 #include "gimple-pretty-print.h"
30 #include "gimple-range.h"
31 #include "value-range-storage.h"
35 #include "gimple-iterator.h"
36 #include "gimple-walk.h"
40 // Adapted from infer_nonnull_range_by_dereference and check_loadstore
41 // to process nonnull ssa_name OP in S. DATA contains a pointer to a
42 // stmt range inference instance.
45 non_null_loadstore (gimple
*, tree op
, tree
, void *data
)
47 if (TREE_CODE (op
) == MEM_REF
|| TREE_CODE (op
) == TARGET_MEM_REF
)
49 /* Some address spaces may legitimately dereference zero. */
50 addr_space_t as
= TYPE_ADDR_SPACE (TREE_TYPE (op
));
51 if (!targetm
.addr_space
.zero_address_valid (as
))
53 tree ssa
= TREE_OPERAND (op
, 0);
54 ((gimple_infer_range
*)data
)->add_nonzero (ssa
);
60 // Process an ASSUME call to see if there are any inferred ranges available.
63 gimple_infer_range::check_assume_func (gcall
*call
)
67 tree assume_id
= TREE_OPERAND (gimple_call_arg (call
, 0), 0);
70 struct function
*fun
= DECL_STRUCT_FUNCTION (assume_id
);
73 // Loop over arguments, matching them to the assume parameters.
74 for (arg
= DECL_ARGUMENTS (assume_id
), i
= 1;
75 arg
&& i
< gimple_call_num_args (call
);
76 i
++, arg
= DECL_CHAIN (arg
))
78 tree op
= gimple_call_arg (call
, i
);
79 tree type
= TREE_TYPE (op
);
80 if (gimple_range_ssa_p (op
) && Value_Range::supports_type_p (type
))
82 tree default_def
= ssa_default_def (fun
, arg
);
83 if (!default_def
|| type
!= TREE_TYPE (default_def
))
85 // Query the global range of the default def in the assume function.
86 Value_Range
assume_range (type
);
87 gimple_range_global (assume_range
, default_def
, fun
);
88 // If there is a non-varying result, add it as an inferred range.
89 if (!assume_range
.varying_p ())
91 add_range (op
, assume_range
);
94 print_generic_expr (dump_file
, assume_id
, TDF_SLIM
);
95 fprintf (dump_file
, " assume inferred range of ");
96 print_generic_expr (dump_file
, op
, TDF_SLIM
);
97 fprintf (dump_file
, " (param ");
98 print_generic_expr (dump_file
, arg
, TDF_SLIM
);
99 fprintf (dump_file
, ") = ");
100 assume_range
.dump (dump_file
);
101 fputc ('\n', dump_file
);
108 // Add NAME and RANGE to the range inference summary.
111 gimple_infer_range::add_range (tree name
, vrange
&range
)
113 m_names
[num_args
] = name
;
114 m_ranges
[num_args
] = range
;
115 if (num_args
< size_limit
- 1)
119 // Add a nonzero range for NAME to the range inference summary.
122 gimple_infer_range::add_nonzero (tree name
)
124 if (!gimple_range_ssa_p (name
))
127 nz
.set_nonzero (TREE_TYPE (name
));
128 add_range (name
, nz
);
131 // Process S for range inference and fill in the summary list.
132 // This is the routine where new inferred ranges should be added.
134 gimple_infer_range::gimple_infer_range (gimple
*s
)
138 if (is_a
<gphi
*> (s
))
141 if (is_a
<gcall
*> (s
) && flag_delete_null_pointer_checks
)
143 tree fntype
= gimple_call_fntype (s
);
144 bitmap nonnullargs
= get_nonnull_args (fntype
);
145 // Process any non-null arguments
148 for (unsigned i
= 0; i
< gimple_call_num_args (s
); i
++)
150 if (bitmap_empty_p (nonnullargs
)
151 || bitmap_bit_p (nonnullargs
, i
))
153 tree op
= gimple_call_arg (s
, i
);
154 if (POINTER_TYPE_P (TREE_TYPE (op
)))
158 BITMAP_FREE (nonnullargs
);
160 // Fallthru and walk load/store ops now.
163 // Check for inferred ranges from ASSUME calls.
164 if (is_a
<gcall
*> (s
) && gimple_call_internal_p (s
)
165 && gimple_call_internal_fn (s
) == IFN_ASSUME
)
166 check_assume_func (as_a
<gcall
*> (s
));
168 // Look for possible non-null values.
169 if (flag_delete_null_pointer_checks
&& gimple_code (s
) != GIMPLE_ASM
170 && !gimple_clobber_p (s
))
171 walk_stmt_load_store_ops (s
, (void *)this, non_null_loadstore
,
176 // -------------------------------------------------------------------------
178 // This class is an element in the list of inferred ranges.
184 vrange_storage
*range
;
188 // If there is an element which matches SSA, return a pointer to the element.
189 // Otherwise return NULL.
192 infer_range_manager::exit_range_head::find_ptr (tree ssa
)
194 // Return NULL if SSA is not in this list.
195 if (!m_names
|| !bitmap_bit_p (m_names
, SSA_NAME_VERSION (ssa
)))
197 for (exit_range
*ptr
= head
; ptr
!= NULL
; ptr
= ptr
->next
)
198 if (ptr
->name
== ssa
)
200 // Should be unreachable.
205 // Construct a range infer manager. DO_SEARCH indicates whether an immediate
206 // use scan should be made the first time a name is processed. This is for
207 // on-demand clients who may not visit every statement and may miss uses.
209 infer_range_manager::infer_range_manager (bool do_search
)
211 bitmap_obstack_initialize (&m_bitmaps
);
212 m_on_exit
.create (0);
213 m_on_exit
.safe_grow_cleared (last_basic_block_for_fn (cfun
) + 1);
214 // m_seen == NULL indicates no scanning. Otherwise the bit indicates a
215 // scan has been performed on NAME.
217 m_seen
= BITMAP_ALLOC (&m_bitmaps
);
220 obstack_init (&m_list_obstack
);
221 // Non-zero elements are very common, so cache them for each ssa-name.
222 m_nonzero
.create (0);
223 m_nonzero
.safe_grow_cleared (num_ssa_names
+ 1);
224 m_range_allocator
= new vrange_allocator
;
227 // Destruct a range infer manager.
229 infer_range_manager::~infer_range_manager ()
231 m_nonzero
.release ();
232 obstack_free (&m_list_obstack
, NULL
);
233 m_on_exit
.release ();
234 bitmap_obstack_release (&m_bitmaps
);
235 delete m_range_allocator
;
238 // Return a non-zero range value of the appropriate type for NAME from
239 // the cache, creating it if necessary.
242 infer_range_manager::get_nonzero (tree name
)
244 unsigned v
= SSA_NAME_VERSION (name
);
245 if (v
>= m_nonzero
.length ())
246 m_nonzero
.safe_grow_cleared (num_ssa_names
+ 20);
250 = (irange
*) m_range_allocator
->alloc (sizeof (int_range
<2>));
251 m_nonzero
[v
]->set_nonzero (TREE_TYPE (name
));
253 return *(m_nonzero
[v
]);
256 // Return TRUE if there are any range inferences in block BB.
259 infer_range_manager::has_range_p (basic_block bb
)
261 if (bb
->index
>= (int)m_on_exit
.length ())
263 bitmap b
= m_on_exit
[bb
->index
].m_names
;
264 return b
&& !bitmap_empty_p (b
);
267 // Return TRUE if NAME has a range inference in block BB.
270 infer_range_manager::has_range_p (tree name
, basic_block bb
)
272 // Check if this is an immediate use search model.
273 if (m_seen
&& !bitmap_bit_p (m_seen
, SSA_NAME_VERSION (name
)))
274 register_all_uses (name
);
276 if (bb
->index
>= (int)m_on_exit
.length ())
278 if (!m_on_exit
[bb
->index
].m_names
)
280 if (!bitmap_bit_p (m_on_exit
[bb
->index
].m_names
, SSA_NAME_VERSION (name
)))
285 // Return TRUE if NAME has a range inference in block BB, and adjust range R
289 infer_range_manager::maybe_adjust_range (vrange
&r
, tree name
, basic_block bb
)
291 if (!has_range_p (name
, bb
))
293 exit_range
*ptr
= m_on_exit
[bb
->index
].find_ptr (name
);
294 gcc_checking_assert (ptr
);
295 // Return true if this exit range changes R, otherwise false.
296 tree type
= TREE_TYPE (name
);
297 Value_Range
tmp (type
);
298 ptr
->range
->get_vrange (tmp
, type
);
299 return r
.intersect (tmp
);
302 // Add range R as an inferred range for NAME in block BB.
305 infer_range_manager::add_range (tree name
, basic_block bb
, const vrange
&r
)
307 if (bb
->index
>= (int)m_on_exit
.length ())
308 m_on_exit
.safe_grow_cleared (last_basic_block_for_fn (cfun
) + 1);
310 // Create the summary list bitmap if it doesn't exist.
311 if (!m_on_exit
[bb
->index
].m_names
)
312 m_on_exit
[bb
->index
].m_names
= BITMAP_ALLOC (&m_bitmaps
);
314 if (dump_file
&& (dump_flags
& TDF_DETAILS
))
316 fprintf (dump_file
, " on-exit update ");
317 print_generic_expr (dump_file
, name
, TDF_SLIM
);
318 fprintf (dump_file
, " in BB%d : ",bb
->index
);
320 fprintf (dump_file
, "\n");
323 // If NAME already has a range, intersect them and done.
324 exit_range
*ptr
= m_on_exit
[bb
->index
].find_ptr (name
);
327 tree type
= TREE_TYPE (name
);
328 Value_Range
cur (r
), name_range (type
);
329 ptr
->range
->get_vrange (name_range
, type
);
330 // If no new info is added, just return.
331 if (!cur
.intersect (name_range
))
333 if (ptr
->range
->fits_p (cur
))
334 ptr
->range
->set_vrange (cur
);
336 ptr
->range
= m_range_allocator
->clone (cur
);
340 // Otherwise create a record.
341 bitmap_set_bit (m_on_exit
[bb
->index
].m_names
, SSA_NAME_VERSION (name
));
342 ptr
= (exit_range
*)obstack_alloc (&m_list_obstack
, sizeof (exit_range
));
343 ptr
->range
= m_range_allocator
->clone (r
);
345 ptr
->next
= m_on_exit
[bb
->index
].head
;
346 m_on_exit
[bb
->index
].head
= ptr
;
349 // Add a non-zero inferred range for NAME in block BB.
352 infer_range_manager::add_nonzero (tree name
, basic_block bb
)
354 add_range (name
, bb
, get_nonzero (name
));
357 // Follow immediate use chains and find all inferred ranges for NAME.
360 infer_range_manager::register_all_uses (tree name
)
362 gcc_checking_assert (m_seen
);
364 // Check if we've already processed this name.
365 unsigned v
= SSA_NAME_VERSION (name
);
366 if (bitmap_bit_p (m_seen
, v
))
368 bitmap_set_bit (m_seen
, v
);
371 imm_use_iterator iter
;
373 // Loop over each immediate use and see if it has an inferred range.
374 FOR_EACH_IMM_USE_FAST (use_p
, iter
, name
)
376 gimple
*s
= USE_STMT (use_p
);
377 gimple_infer_range
infer (s
);
378 for (unsigned x
= 0; x
< infer
.num (); x
++)
380 if (name
== infer
.name (x
))
381 add_range (name
, gimple_bb (s
), infer
.range (x
));