LCOV - code coverage report
Current view: top level - gcc - tree-assume.cc (source / functions) Coverage Total Hit
Test: gcc.info Lines: 75.4 % 183 138
Test Date: 2026-02-28 14:20:25 Functions: 100.0 % 9 9
Legend: Lines:     hit not hit

            Line data    Source code
       1              : /* Support for C++23 ASSUME keyword functionailty.
       2              :    Copyright (C) 2023-2026 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 "basic-block.h"
      25              : #include "bitmap.h"
      26              : #include "options.h"
      27              : #include "function.h"
      28              : #include "cfg.h"
      29              : #include "tree.h"
      30              : #include "gimple.h"
      31              : #include "tree-pass.h"
      32              : #include "ssa.h"
      33              : #include "gimple-iterator.h"
      34              : #include "gimple-range.h"
      35              : #include "tree-dfa.h"
      36              : #include "tree-cfg.h"
      37              : #include "gimple-pretty-print.h"
      38              : 
      39              : // An assume query utilizes the current range query to implement the assume
      40              : // keyword.
      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
      46              : // function.
      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
      49              : // mechanism.
      50              : //   See gimple-range-infer.cc::gimple_infer_range::check_assume_func ()
      51              : //
      52              : // my_func (int x)
      53              : // {
      54              : //   <...>
      55              : //   assume [[(x == 1 || x ==4))]]
      56              : //   if (x ==3)
      57              : //
      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]
      63              : //
      64              : // Meanwhile., my_func has been rewritten to be:
      65              : //
      66              : // my_func (int x_2)
      67              : // {
      68              : //   <...>
      69              : //   assume_builtin_call  assume_f1 (x_2);
      70              : //   if (x_2 == 3)
      71              : //
      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]
      75              : //
      76              : // Any uses of x_2 after this statement will now utilize this inferred range.
      77              : //
      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
      80              : //   if (0 != 0)
      81              : 
      82           86 : class assume_query
      83              : {
      84              : public:
      85              :   assume_query (function *f, bitmap p);
      86              : protected:
      87           77 :   inline void process_stmts (gimple *s, vrange &lhs_range)
      88              :   {
      89          154 :     fur_depend src (s, get_range_query (m_func));
      90           77 :     calculate_stmt (s, lhs_range, src);
      91           77 :     update_parms (src);
      92           77 :   }
      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);
      97              : 
      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.
     101              :   function *m_func;
     102              : };
     103              : 
     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.
     107              : 
     108          172 : assume_query::assume_query (function *f, bitmap p) : m_parm_list (p),
     109           86 :                                                      m_func (f)
     110              : {
     111           86 :   basic_block exit_bb = EXIT_BLOCK_PTR_FOR_FN (f);
     112              :   // If there is more than one predecessor to the exit block, bail.
     113           86 :   if (!single_pred_p (exit_bb))
     114            6 :     return;
     115              : 
     116           86 :   basic_block bb = single_pred (exit_bb);
     117           86 :   gimple_stmt_iterator gsi = gsi_last_nondebug_bb (bb);
     118           86 :   if (gsi_end_p (gsi))
     119              :     return;
     120           86 :   gimple *s = gsi_stmt (gsi);
     121           86 :   if (!is_a<greturn *> (s))
     122              :     return;
     123              : 
     124              :   // Check if the single return value is a symbolic and supported type.
     125           86 :   greturn *gret = as_a<greturn *> (s);
     126           86 :   tree op = gimple_return_retval (gret);
     127           86 :   if (!gimple_range_ssa_p (op))
     128              :     return;
     129           80 :   tree lhs_type = TREE_TYPE (op);
     130           80 :   if (!irange::supports_p (lhs_type))
     131              :     return;
     132              : 
     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           80 :   unsigned prec = TYPE_PRECISION (lhs_type);
     137           80 :   int_range<2> lhs_range (lhs_type, wi::one (prec), wi::one (prec));
     138              : 
     139           80 :   gimple *def = SSA_NAME_DEF_STMT (op);
     140           80 :   if (!def || gimple_get_lhs (def) != op || gimple_bb (def) != bb)
     141            0 :     return;
     142              : 
     143              :   // Determine if this is a PHI or a linear sequence to deal with.
     144           80 :   if (is_a<gphi *> (def))
     145           21 :     calculate_phi (as_a<gphi *> (def), lhs_range);
     146              :   else
     147           59 :     process_stmts (def, lhs_range);
     148              : 
     149           80 :   if (dump_file)
     150            0 :     fprintf (dump_file, "\n\nAssumptions :\n--------------\n");
     151              : 
     152              :   // Now export any interesting values that were found.
     153           80 :   bitmap_iterator bi;
     154           80 :   unsigned x;
     155          197 :   EXECUTE_IF_SET_IN_BITMAP (m_parm_list, 0, x, bi)
     156              :     {
     157          117 :       tree name = ssa_name (x);
     158          117 :       tree type = TREE_TYPE (name);
     159          117 :       value_range assume_range (type);
     160              :       // Set the global range of NAME to anything calculated.
     161          117 :       if (m_parms.get_range (assume_range, name) && !assume_range.varying_p ())
     162          102 :         set_range_info (name, assume_range);
     163          117 :     }
     164              : 
     165           80 :   if (dump_file)
     166              :    {
     167            0 :      fputc ('\n', dump_file);
     168            0 :      gimple_dump_cfg (dump_file, dump_flags & ~TDF_DETAILS);
     169              :    }
     170           80 : }
     171              : 
     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.
     179              : 
     180              : void
     181           82 : assume_query::update_parms (fur_source &src)
     182              : {
     183           82 :   if (dump_file && (dump_flags & TDF_DETAILS))
     184            0 :     fprintf (dump_file, "\nupdate parameters\n");
     185              : 
     186              :   // Merge any parameter values.
     187           82 :   bitmap_iterator bi;
     188           82 :   unsigned x;
     189          201 :   EXECUTE_IF_SET_IN_BITMAP (m_parm_list, 0, x, bi)
     190              :     {
     191          119 :       tree name = ssa_name (x);
     192          119 :       tree type = TREE_TYPE (name);
     193              : 
     194          119 :       if (dump_file && (dump_flags & TDF_DETAILS))
     195              :         {
     196            0 :           fprintf (dump_file, "PARAMETER ");
     197            0 :           print_generic_expr (dump_file, name, TDF_SLIM);
     198              :         }
     199          119 :       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          119 :       if (m_path.get_range (glob_range, name))
     203              :         {
     204           87 :           if (dump_file && (dump_flags & TDF_DETAILS))
     205              :             {
     206            0 :               fprintf (dump_file, "\n  Calculated path range:");
     207            0 :               glob_range.dump (dump_file);
     208              :             }
     209              :         }
     210              :       // Otherwise, let ranger determine the range at the SRC location.
     211           32 :       else if (src.get_operand (glob_range, name))
     212              :         {
     213           32 :           if (dump_file && (dump_flags & TDF_DETAILS))
     214              :             {
     215            0 :               fprintf (dump_file, "\n  Ranger Computes path range:");
     216            0 :               glob_range.dump (dump_file);
     217              :             }
     218              :         }
     219              :       else
     220            0 :         glob_range.set_varying (type);
     221              : 
     222              :       // Find any current saved value of parm, and combine them.
     223          119 :       value_range parm_range (type);
     224          119 :       if (m_parms.get_range (parm_range, name))
     225            2 :         glob_range.union_ (parm_range);
     226              : 
     227          119 :       if (dump_file && (dump_flags & TDF_DETAILS))
     228              :         {
     229            0 :           fprintf (dump_file, "\n  Combine with previous range:");
     230            0 :           parm_range.dump (dump_file);
     231            0 :           fputc ('\n', dump_file);
     232            0 :           print_generic_expr (dump_file, name, TDF_SLIM);
     233            0 :           fprintf (dump_file, " = ");
     234            0 :           glob_range.dump (dump_file);
     235            0 :           fputc ('\n', dump_file);
     236              :         }
     237              :       // Set this new value.
     238          119 :       m_parms.set_range (name, glob_range);
     239          119 :     }
     240              :   // Now reset the path values for the next path.
     241           82 :   if (dump_file && (dump_flags & TDF_DETAILS))
     242            0 :     fprintf (dump_file, "---------------------\n");
     243           82 :   m_path.clear ();
     244           82 : }
     245              : 
     246              : 
     247              : // Evaluate PHI statement, using the provided LHS range.
     248              : // Only process edge that are taken and return the LHS of the PHI.
     249              : 
     250              : void
     251           21 : assume_query::calculate_phi (gphi *phi, vrange &lhs_range)
     252              : {
     253           21 :   if (dump_file && (dump_flags & TDF_DETAILS))
     254              :     {
     255            0 :       fprintf (dump_file, "Processing PHI feeding return value:\n");
     256            0 :       print_gimple_stmt (dump_file, phi, 0, TDF_SLIM);
     257              :     }
     258           69 :   for (unsigned x= 0; x < gimple_phi_num_args (phi); x++)
     259              :     {
     260           48 :       tree arg = gimple_phi_arg_def (phi, x);
     261           48 :       value_range arg_range (TREE_TYPE (arg));
     262           48 :       edge e = gimple_phi_arg_edge (phi, x);
     263           48 :       value_range edge_range (TREE_TYPE (arg));
     264           48 :       if (dump_file && (dump_flags & TDF_DETAILS))
     265              :         {
     266            0 :           fprintf (dump_file, "\nArgument %d (bb%d->bb%d): ", x, e->src->index,
     267            0 :                    e->dest->index);
     268            0 :           print_generic_expr (dump_file, arg, TDF_SLIM);
     269            0 :           fputc ('\n', dump_file);
     270              :         }
     271              :       // If we can't get an edge range, be conservative and assume the
     272              :       // edge can be taken.
     273           96 :       if (get_range_query (m_func)->range_on_edge (edge_range, e, arg))
     274              :         {
     275           48 :           if (gimple_range_ssa_p (arg))
     276              :             {
     277           27 :               arg_range = lhs_range;
     278           27 :               range_cast (arg_range, TREE_TYPE (arg));
     279              : 
     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           27 :               arg_range.intersect (edge_range);
     285           27 :               if (arg_range.undefined_p ())
     286              :                 {
     287            9 :                   if (dump_file && (dump_flags & TDF_DETAILS))
     288              :                     {
     289            0 :                       fprintf (dump_file, "  IGNORE edge :  LHS range :");
     290            0 :                       lhs_range.dump (dump_file);
     291            0 :                       fprintf (dump_file, " Edge produces : ");
     292            0 :                       edge_range.dump (dump_file);
     293            0 :                       fputc ('\n', dump_file);
     294              :                     }
     295            9 :                   continue;
     296              :                 }
     297              : 
     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           18 :               gimple *def_stmt = SSA_NAME_DEF_STMT (arg);
     303           18 :               if (def_stmt && gimple_get_lhs (def_stmt) == arg
     304           36 :                   && gimple_bb (def_stmt) == e->src)
     305              :                 {
     306           18 :                   process_stmts (def_stmt, arg_range);
     307           18 :                   continue;
     308              :                 }
     309              :               // Fall through to process the parameter values on the edge.
     310              :             }
     311              :           else
     312              :             {
     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           21 :               edge_range.intersect (lhs_range);
     317           21 :               if (edge_range.undefined_p ())
     318              :                 {
     319           16 :                   if (dump_file && (dump_flags & TDF_DETAILS))
     320            0 :                     fprintf (dump_file, "  IGNORE : const edge not taken\n");
     321           16 :                   continue;
     322              :                 }
     323            5 :               if (dump_file && (dump_flags & TDF_DETAILS))
     324            0 :                 fprintf (dump_file,
     325              :                          "  Const edge executed, compute incoming ranges.\n");
     326              : 
     327              :             }
     328              :         }
     329              :       // The parameters on the edge now need calculating.
     330           10 :       fur_edge src (e, get_range_query (m_func));
     331            5 :       update_parms (src);
     332           48 :     }
     333           21 : }
     334              : 
     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.
     338              : 
     339              : void
     340          160 : assume_query::calculate_op (tree op, gimple *s, vrange &lhs, fur_source &src)
     341              : {
     342          160 :   basic_block bb = gimple_bb (s);
     343          160 :   value_range op_range (TREE_TYPE (op));
     344          320 :   if (src.gori () &&
     345          160 :       src.gori ()->compute_operand_range (op_range, s, lhs, op, src)
     346          320 :       && !op_range.varying_p ())
     347              :     {
     348          160 :       if (dump_file && (dump_flags & TDF_DETAILS))
     349              :         {
     350            0 :           fprintf (dump_file, "  Operand ");
     351            0 :           print_generic_expr (dump_file, op, TDF_SLIM);
     352            0 :           fprintf (dump_file, " calculated as ");
     353            0 :           op_range.dump (dump_file);
     354              :         }
     355              :       // Set the global range, merging if there is already a range.
     356          160 :       m_path.merge_range (op, op_range);
     357          160 :       m_path.get_range (op_range, op);
     358          160 :       if (dump_file && (dump_flags & TDF_DETAILS))
     359              :         {
     360            0 :           fprintf (dump_file, "  New path range :");
     361            0 :           op_range.dump (dump_file);
     362            0 :           fputc ('\n', dump_file);
     363              :         }
     364          160 :       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          160 :       if (def_stmt && gimple_get_lhs (def_stmt) == op
     368          233 :           && gimple_bb (def_stmt) == bb)
     369           70 :         calculate_stmt (def_stmt, op_range, src);
     370              :     }
     371          160 : }
     372              : 
     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.
     376              : 
     377              : void
     378          147 : assume_query::calculate_stmt (gimple *s, vrange &lhs_range, fur_source &src)
     379              : {
     380          147 :   if (dump_file && (dump_flags & TDF_DETAILS))
     381              :     {
     382            0 :       fprintf (dump_file, "  Processing stmt with LHS = ");
     383            0 :       lhs_range.dump (dump_file);
     384            0 :       fprintf (dump_file, " : ");
     385            0 :       print_gimple_stmt (dump_file, s, 0, TDF_SLIM);
     386              :     }
     387          147 :   gimple_range_op_handler handler (s);
     388          147 :   if (handler)
     389              :     {
     390          136 :       tree op = gimple_range_ssa_p (handler.operand1 ());
     391          136 :       if (op)
     392          136 :         calculate_op (op, s, lhs_range, src);
     393          136 :       op = gimple_range_ssa_p (handler.operand2 ());
     394          136 :       if (op)
     395           24 :         calculate_op (op, s, lhs_range, src);
     396              :     }
     397          147 : }
     398              : 
     399              : namespace {
     400              : 
     401              : const pass_data pass_data_assumptions =
     402              : {
     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 */
     412              : };
     413              : 
     414              : 
     415              : class pass_assumptions : public gimple_opt_pass
     416              : {
     417              : public:
     418       285722 :   pass_assumptions (gcc::context *ctxt)
     419       571444 :     : gimple_opt_pass (pass_data_assumptions, ctxt)
     420              :   {}
     421              : 
     422              :   /* opt_pass methods: */
     423      1472258 :   bool gate (function *fun) final override { return fun->assume_function; }
     424          108 :   unsigned int execute (function *fun) final override
     425              :     {
     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 ().
     432          108 :       auto_bitmap decls;
     433          247 :       for (tree arg = DECL_ARGUMENTS (fun->decl); arg; arg = DECL_CHAIN (arg))
     434              :         {
     435          139 :           tree name = ssa_default_def (fun, arg);
     436          139 :           if (!name || !gimple_range_ssa_p (name))
     437           16 :             continue;
     438          123 :           tree type = TREE_TYPE (name);
     439          123 :           if (!value_range::supports_type_p (type))
     440            0 :             continue;
     441          123 :           bitmap_set_bit (decls, SSA_NAME_VERSION (name));
     442              :         }
     443              :       // If there are no parameters to map, simply return;
     444          108 :       if (bitmap_empty_p (decls))
     445              :         return TODO_discard_function;
     446              : 
     447           86 :       enable_ranger (fun);
     448              : 
     449              :       // This assume query will set any global values required.
     450           86 :       assume_query query (fun, decls);
     451              : 
     452           86 :       disable_ranger (fun);
     453           86 :       return TODO_discard_function;
     454           86 :     }
     455              : 
     456              : }; // class pass_assumptions
     457              : 
     458              : } // anon namespace
     459              : 
     460              : gimple_opt_pass *
     461       285722 : make_pass_assumptions (gcc::context *ctx)
     462              : {
     463       285722 :   return new pass_assumptions (ctx);
     464              : }
        

Generated by: LCOV version 2.4-beta

LCOV profile is generated on x86_64 machine using following configure options: configure --disable-bootstrap --enable-coverage=opt --enable-languages=c,c++,fortran,go,jit,lto,rust,m2 --enable-host-shared. GCC test suite is run with the built compiler.