1 /* Support for C++23 ASSUME keyword functionailty.
2 Copyright (C) 2023-2025 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"
24 #include "basic-block.h"
31 #include "tree-pass.h"
33 #include "gimple-iterator.h"
34 #include "gimple-range.h"
37 #include "gimple-pretty-print.h"
39 // An assume query utilizes the current range query to implement the assume
41 // For any return value of 1 from the function, it attempts to determine
42 // which paths lead to a 1 value being returned. On those paths, it determines
43 // the ranges of any ssa_names listed in bitmap P (usually the parm list for
44 // the function), and combines them all.
45 // These ranges are then set as the global ranges for those parms in this
47 // Other functions which refer to this function in an assume builtin
48 // will then pick up these ranges for the parameters via the inferred range
50 // See gimple-range-infer.cc::gimple_infer_range::check_assume_func ()
55 // assume [[(x == 1 || x ==4))]]
58 // a small temporary assume function consisting of
59 // assume_f1 (int x) { return x == 1 || x == 4; }
60 // is constructed by the front end, and optimized, at the very end of
61 // optimization, instead of generating code, we instead invoke the assume pass
62 // which uses this query to set the the global value of parm x to [1,1][4,4]
64 // Meanwhile., my_func has been rewritten to be:
69 // assume_builtin_call assume_f1 (x_2);
72 // When ranger is processing the assume_builtin_call, it looks up the global
73 // value of the parameter in assume_f1, which is [1,1][4,4]. It then registers
74 // and inferred range at this statement setting the value x_2 to [1,1][4,4]
76 // Any uses of x_2 after this statement will now utilize this inferred range.
78 // When VRP processes if (x_2 == 3), it picks up the inferred range, and
79 // determines that x_2 can never be 3, and will rewrite the branch to
85 assume_query (function
*f
, bitmap p
);
87 inline void process_stmts (gimple
*s
, vrange
&lhs_range
)
89 fur_depend
src (s
, get_range_query (m_func
));
90 calculate_stmt (s
, lhs_range
, src
);
93 void update_parms (fur_source
&src
);
94 void calculate_stmt (gimple
*s
, vrange
&lhs_range
, fur_source
&src
);
95 void calculate_op (tree op
, gimple
*s
, vrange
&lhs
, fur_source
&src
);
96 void calculate_phi (gphi
*phi
, vrange
&lhs_range
);
98 ssa_lazy_cache m_path
; // Values found on path
99 ssa_lazy_cache m_parms
; // Cumulative parameter value calculated
100 bitmap m_parm_list
; // Parameter ssa-names list.
104 // If function F returns a integral value, and has a single return
105 // statement, try to calculate the range of each value in P that leads
106 // to the return statement returning TRUE.
108 assume_query::assume_query (function
*f
, bitmap p
) : m_parm_list (p
),
111 basic_block exit_bb
= EXIT_BLOCK_PTR_FOR_FN (f
);
112 // If there is more than one predecessor to the exit block, bail.
113 if (!single_pred_p (exit_bb
))
116 basic_block bb
= single_pred (exit_bb
);
117 gimple_stmt_iterator gsi
= gsi_last_nondebug_bb (bb
);
120 gimple
*s
= gsi_stmt (gsi
);
121 if (!is_a
<greturn
*> (s
))
124 // Check if the single return value is a symbolic and supported type.
125 greturn
*gret
= as_a
<greturn
*> (s
);
126 tree op
= gimple_return_retval (gret
);
127 if (!gimple_range_ssa_p (op
))
129 tree lhs_type
= TREE_TYPE (op
);
130 if (!irange::supports_p (lhs_type
))
133 // Only values of interest are when the return value is 1. The definition
134 // of the return value must be in the same block, or we have
135 // complicated flow control we don't understand, and just return.
136 unsigned prec
= TYPE_PRECISION (lhs_type
);
137 int_range
<2> lhs_range (lhs_type
, wi::one (prec
), wi::one (prec
));
139 gimple
*def
= SSA_NAME_DEF_STMT (op
);
140 if (!def
|| gimple_get_lhs (def
) != op
|| gimple_bb (def
) != bb
)
143 // Determine if this is a PHI or a linear sequence to deal with.
144 if (is_a
<gphi
*> (def
))
145 calculate_phi (as_a
<gphi
*> (def
), lhs_range
);
147 process_stmts (def
, lhs_range
);
150 fprintf (dump_file
, "\n\nAssumptions :\n--------------\n");
152 // Now export any interesting values that were found.
155 EXECUTE_IF_SET_IN_BITMAP (m_parm_list
, 0, x
, bi
)
157 tree name
= ssa_name (x
);
158 tree type
= TREE_TYPE (name
);
159 value_range
assume_range (type
);
160 // Set the global range of NAME to anything calculated.
161 if (m_parms
.get_range (assume_range
, name
) && !assume_range
.varying_p ())
162 set_range_info (name
, assume_range
);
167 fputc ('\n', dump_file
);
168 gimple_dump_cfg (dump_file
, dump_flags
& ~TDF_DETAILS
);
172 // This function will update all the current values of interesting parameters.
173 // It tries, in order:
174 // a) a range found via path calculations.
175 // b) range of the parm at SRC point in the IL. (either edge or stmt)
176 // c) VARYING if those options fail.
177 // The value is then unioned with any existing value, allowing for the
178 // cumulation of all ranges leading to the return that return 1.
181 assume_query::update_parms (fur_source
&src
)
183 if (dump_file
&& (dump_flags
& TDF_DETAILS
))
184 fprintf (dump_file
, "\nupdate parameters\n");
186 // Merge any parameter values.
189 EXECUTE_IF_SET_IN_BITMAP (m_parm_list
, 0, x
, bi
)
191 tree name
= ssa_name (x
);
192 tree type
= TREE_TYPE (name
);
194 if (dump_file
&& (dump_flags
& TDF_DETAILS
))
196 fprintf (dump_file
, "PARAMETER ");
197 print_generic_expr (dump_file
, name
, TDF_SLIM
);
199 value_range
glob_range (type
);
200 // Find a value from calculations.
201 // There will be a value in m_path if GORI calculated an operand value.
202 if (m_path
.get_range (glob_range
, name
))
204 if (dump_file
&& (dump_flags
& TDF_DETAILS
))
206 fprintf (dump_file
, "\n Calculated path range:");
207 glob_range
.dump (dump_file
);
210 // Otherwise, let ranger determine the range at the SRC location.
211 else if (src
.get_operand (glob_range
, name
))
213 if (dump_file
&& (dump_flags
& TDF_DETAILS
))
215 fprintf (dump_file
, "\n Ranger Computes path range:");
216 glob_range
.dump (dump_file
);
220 glob_range
.set_varying (type
);
222 // Find any current saved value of parm, and combine them.
223 value_range
parm_range (type
);
224 if (m_parms
.get_range (parm_range
, name
))
225 glob_range
.union_ (parm_range
);
227 if (dump_file
&& (dump_flags
& TDF_DETAILS
))
229 fprintf (dump_file
, "\n Combine with previous range:");
230 parm_range
.dump (dump_file
);
231 fputc ('\n', dump_file
);
232 print_generic_expr (dump_file
, name
, TDF_SLIM
);
233 fprintf (dump_file
, " = ");
234 glob_range
.dump (dump_file
);
235 fputc ('\n', dump_file
);
237 // Set this new value.
238 m_parms
.set_range (name
, glob_range
);
240 // Now reset the path values for the next path.
241 if (dump_file
&& (dump_flags
& TDF_DETAILS
))
242 fprintf (dump_file
, "---------------------\n");
247 // Evaluate PHI statement, using the provided LHS range.
248 // Only process edge that are taken and return the LHS of the PHI.
251 assume_query::calculate_phi (gphi
*phi
, vrange
&lhs_range
)
253 if (dump_file
&& (dump_flags
& TDF_DETAILS
))
255 fprintf (dump_file
, "Processing PHI feeding return value:\n");
256 print_gimple_stmt (dump_file
, phi
, 0, TDF_SLIM
);
258 for (unsigned x
= 0; x
< gimple_phi_num_args (phi
); x
++)
260 tree arg
= gimple_phi_arg_def (phi
, x
);
261 value_range
arg_range (TREE_TYPE (arg
));
262 edge e
= gimple_phi_arg_edge (phi
, x
);
263 value_range
edge_range (TREE_TYPE (arg
));
264 if (dump_file
&& (dump_flags
& TDF_DETAILS
))
266 fprintf (dump_file
, "\nArgument %d (bb%d->bb%d): ", x
, e
->src
->index
,
268 print_generic_expr (dump_file
, arg
, TDF_SLIM
);
269 fputc ('\n', dump_file
);
271 // If we can't get an edge range, be conservative and assume the
272 // edge can be taken.
273 if (get_range_query (m_func
)->range_on_edge (edge_range
, e
, arg
))
275 if (gimple_range_ssa_p (arg
))
277 arg_range
= lhs_range
;
278 range_cast (arg_range
, TREE_TYPE (arg
));
280 // An SSA_NAME arg will start with the LHS value.
281 // Check the range of ARG on the edge leading here. If that range
282 // cannot be any value from the LHS of the PHI, then this branch
283 // will not be taken to return the LHS value and can be ignored.
284 arg_range
.intersect (edge_range
);
285 if (arg_range
.undefined_p ())
287 if (dump_file
&& (dump_flags
& TDF_DETAILS
))
289 fprintf (dump_file
, " IGNORE edge : LHS range :");
290 lhs_range
.dump (dump_file
);
291 fprintf (dump_file
, " Edge produces : ");
292 edge_range
.dump (dump_file
);
293 fputc ('\n', dump_file
);
298 // If the def is in the immediate preceeding block, process it
299 // with GORI to determine what values can produce this
300 // argument value. Otherwise there is more CFG flow, so query
301 // the edge for parm ranges. This is conservative.
302 gimple
*def_stmt
= SSA_NAME_DEF_STMT (arg
);
303 if (def_stmt
&& gimple_get_lhs (def_stmt
) == arg
304 && gimple_bb (def_stmt
) == e
->src
)
306 process_stmts (def_stmt
, arg_range
);
309 // Fall through to process the parameter values on the edge.
313 // If this is a constant value that differs from LHS, this
314 // edge cannot be taken and we can ignore it. Otherwise fall
315 // thorugh and process the parameters on the edge.
316 edge_range
.intersect (lhs_range
);
317 if (edge_range
.undefined_p ())
319 if (dump_file
&& (dump_flags
& TDF_DETAILS
))
320 fprintf (dump_file
, " IGNORE : const edge not taken\n");
323 if (dump_file
&& (dump_flags
& TDF_DETAILS
))
325 " Const edge executed, compute incoming ranges.\n");
329 // The parameters on the edge now need calculating.
330 fur_edge
src (e
, get_range_query (m_func
));
335 // Evaluate operand OP on statement S, using the provided LHS range.
336 // If successful, set the range in path table, then visit OP's def stmt
337 // if it is in the same BB.
340 assume_query::calculate_op (tree op
, gimple
*s
, vrange
&lhs
, fur_source
&src
)
342 basic_block bb
= gimple_bb (s
);
343 value_range
op_range (TREE_TYPE (op
));
345 src
.gori ()->compute_operand_range (op_range
, s
, lhs
, op
, src
)
346 && !op_range
.varying_p ())
348 if (dump_file
&& (dump_flags
& TDF_DETAILS
))
350 fprintf (dump_file
, " Operand ");
351 print_generic_expr (dump_file
, op
, TDF_SLIM
);
352 fprintf (dump_file
, " calculated as ");
353 op_range
.dump (dump_file
);
355 // Set the global range, merging if there is already a range.
356 m_path
.merge_range (op
, op_range
);
357 m_path
.get_range (op_range
, op
);
358 if (dump_file
&& (dump_flags
& TDF_DETAILS
))
360 fprintf (dump_file
, " New path range :");
361 op_range
.dump (dump_file
);
362 fputc ('\n', dump_file
);
364 gimple
*def_stmt
= SSA_NAME_DEF_STMT (op
);
365 // Terminate if the pathway leads to a different block as we
366 // are not dealing with flow. Ranger will make those queries.
367 if (def_stmt
&& gimple_get_lhs (def_stmt
) == op
368 && gimple_bb (def_stmt
) == bb
)
369 calculate_stmt (def_stmt
, op_range
, src
);
373 // Evaluate statement S which produces range LHS_RANGE. Use GORI to
374 // determine what values the operands can have to produce the LHS,
375 // and set these in the M_PATH table.
378 assume_query::calculate_stmt (gimple
*s
, vrange
&lhs_range
, fur_source
&src
)
380 if (dump_file
&& (dump_flags
& TDF_DETAILS
))
382 fprintf (dump_file
, " Processing stmt with LHS = ");
383 lhs_range
.dump (dump_file
);
384 fprintf (dump_file
, " : ");
385 print_gimple_stmt (dump_file
, s
, 0, TDF_SLIM
);
387 gimple_range_op_handler
handler (s
);
390 tree op
= gimple_range_ssa_p (handler
.operand1 ());
392 calculate_op (op
, s
, lhs_range
, src
);
393 op
= gimple_range_ssa_p (handler
.operand2 ());
395 calculate_op (op
, s
, lhs_range
, src
);
401 const pass_data pass_data_assumptions
=
403 GIMPLE_PASS
, /* type */
404 "assumptions", /* name */
405 OPTGROUP_NONE
, /* optinfo_flags */
406 TV_TREE_ASSUMPTIONS
, /* tv_id */
407 PROP_ssa
, /* properties_required */
408 PROP_assumptions_done
, /* properties_provided */
409 0, /* properties_destroyed */
410 0, /* todo_flags_start */
411 0, /* todo_flags_end */
415 class pass_assumptions
: public gimple_opt_pass
418 pass_assumptions (gcc::context
*ctxt
)
419 : gimple_opt_pass (pass_data_assumptions
, ctxt
)
422 /* opt_pass methods: */
423 bool gate (function
*fun
) final override
{ return fun
->assume_function
; }
424 unsigned int execute (function
*fun
) final override
426 // Create a bitmap of all the parameters in this function.
427 // Invoke the assume_query to determine what values these parameters
428 // have when the function returns TRUE, and set the global values of
429 // those parameters in this function based on that. This will later be
430 // utilized by ranger when processing builtin IFN_ASSUME function calls.
431 // See gimple-range-infer.cc::check_assume_func ().
433 for (tree arg
= DECL_ARGUMENTS (fun
->decl
); arg
; arg
= DECL_CHAIN (arg
))
435 tree name
= ssa_default_def (fun
, arg
);
436 if (!name
|| !gimple_range_ssa_p (name
))
438 tree type
= TREE_TYPE (name
);
439 if (!value_range::supports_type_p (type
))
441 bitmap_set_bit (decls
, SSA_NAME_VERSION (name
));
443 // If there are no parameters to map, simply return;
444 if (bitmap_empty_p (decls
))
445 return TODO_discard_function
;
449 // This assume query will set any global values required.
450 assume_query
query (fun
, decls
);
452 disable_ranger (fun
);
453 return TODO_discard_function
;
456 }; // class pass_assumptions
461 make_pass_assumptions (gcc::context
*ctx
)
463 return new pass_assumptions (ctx
);