LCOV - code coverage report
Current view: top level - gcc - attr-callback.cc (source / functions) Coverage Total Hit
Test: gcc.info Lines: 76.9 % 156 120
Test Date: 2026-02-28 14:20:25 Functions: 100.0 % 12 12
Legend: Lines:     hit not hit

            Line data    Source code
       1              : /* Callback attribute handling
       2              :    Copyright (C) 2025-2026 Free Software Foundation, Inc.
       3              :    Contributed by Josef Melcr <jmelcr@gcc.gnu.org>
       4              : 
       5              :    This file is part of GCC.
       6              : 
       7              :    GCC is free software; you can redistribute it and/or modify
       8              :    under the terms of the GNU General Public License as published by
       9              :    the Free Software Foundation; either version 3 of the License, or
      10              :    (at your option) 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 "tree.h"
      26              : #include "gimple.h"
      27              : #include "alloc-pool.h"
      28              : #include "cgraph.h"
      29              : #include "diagnostic.h"
      30              : #include "builtins.h"
      31              : #include "options.h"
      32              : #include "gimple-range.h"
      33              : #include "attribs.h"
      34              : #include "attr-callback.h"
      35              : 
      36              : /* Returns a callback attribute with callback index FN_IDX, and ARG_COUNT
      37              :    arguments specified by VA_ARGS.  */
      38              : tree
      39       379772 : callback_build_attr (unsigned fn_idx, unsigned arg_count...)
      40              : {
      41       379772 :   va_list args;
      42       379772 :   va_start (args, arg_count);
      43              : 
      44       379772 :   tree cblist = NULL_TREE;
      45       379772 :   tree *pp = &cblist;
      46       379772 :   unsigned i;
      47       759544 :   for (i = 0; i < arg_count; i++)
      48              :     {
      49       379772 :       int num = va_arg (args, int);
      50       379772 :       tree tnum = build_int_cst (integer_type_node, num);
      51       379772 :       *pp = build_tree_list (NULL, tnum);
      52       379772 :       pp = &TREE_CHAIN (*pp);
      53              :     }
      54       379772 :   cblist
      55       379772 :     = tree_cons (NULL_TREE, build_int_cst (integer_type_node, fn_idx), cblist);
      56       379772 :   tree attr
      57       379772 :     = tree_cons (get_identifier (CALLBACK_ATTR_IDENT), cblist, NULL_TREE);
      58       379772 :   return attr;
      59              : }
      60              : 
      61              : /* Returns TRUE if a function should be treated as if it had a callback
      62              :    attribute despite the DECL not having it.  STMT can be passed NULL
      63              :    if the call statement is not available at the time, for example WPA, but it
      64              :    should be called with the statement itself whenever possible.  */
      65              : bool
      66      2716078 : callback_is_special_cased (tree decl, gcall *stmt)
      67              : {
      68      2716078 :   if (fndecl_built_in_p (decl, BUILT_IN_GOMP_TASK))
      69              :     {
      70        19098 :       if (stmt)
      71        19098 :         return gimple_call_arg (stmt, 2) == null_pointer_node;
      72              :       return true;
      73              :     }
      74              :   return false;
      75              : }
      76              : 
      77              : /* Returns an attribute for a special cased function.  */
      78              : tree
      79         4304 : callback_special_case_attr (tree decl)
      80              : {
      81         4304 :   if (fndecl_built_in_p (decl, BUILT_IN_GOMP_TASK))
      82         4304 :     return callback_build_attr (1, 1, 2);
      83            0 :   gcc_unreachable ();
      84              : }
      85              : 
      86              : /* Returns TRUE if the callee of E has a callback attribute.  */
      87              : bool
      88      2580419 : callback_edge_callee_has_attr (cgraph_edge *e)
      89              : {
      90      2580419 :   return lookup_attribute (CALLBACK_ATTR_IDENT,
      91      2580419 :                            DECL_ATTRIBUTES (e->callee->decl))
      92      2580419 :          || callback_is_special_cased (e->callee->decl, e->call_stmt);
      93              : }
      94              : 
      95              : /* Given an instance of callback attribute, return the 0-based
      96              :    index of the called function in question.  */
      97              : int
      98        34452 : callback_get_fn_index (tree cb_attr)
      99              : {
     100        34452 :   tree args = TREE_VALUE (cb_attr);
     101        34452 :   int idx = TREE_INT_CST_LOW (TREE_VALUE (args)) - 1;
     102        34452 :   return idx;
     103              : }
     104              : 
     105              : /* For a given callback pair, retrieves the callback attribute used
     106              :    to create E from the callee of CARRYING.  */
     107              : tree
     108        18494 : callback_fetch_attr_by_edge (cgraph_edge *e, cgraph_edge *carrying)
     109              : {
     110        18494 :   gcc_checking_assert (e->call_stmt == carrying->call_stmt
     111              :                        && e->lto_stmt_uid == carrying->lto_stmt_uid);
     112              : 
     113        18494 :   if (callback_is_special_cased (carrying->callee->decl, e->call_stmt))
     114         2536 :     return callback_special_case_attr (carrying->callee->decl);
     115              : 
     116        15958 :   tree cb_attr = lookup_attribute (CALLBACK_ATTR_IDENT,
     117        15958 :                                    DECL_ATTRIBUTES (carrying->callee->decl));
     118        15958 :   gcc_checking_assert (cb_attr);
     119        15958 :   tree res = NULL_TREE;
     120        15958 :   for (; cb_attr;
     121            0 :        cb_attr = lookup_attribute (CALLBACK_ATTR_IDENT, TREE_CHAIN (cb_attr)))
     122              :     {
     123        15958 :       unsigned id = callback_get_fn_index (cb_attr);
     124        15958 :       if (id == e->callback_id)
     125              :         {
     126              :           res = cb_attr;
     127              :           break;
     128              :         }
     129              :     }
     130        15958 :   gcc_checking_assert (res != NULL_TREE);
     131              :   return res;
     132              : }
     133              : 
     134              : /* Given an instance of callback attribute, return the 0-base indices
     135              :    of arguments passed to the callback.  For a callback function taking
     136              :    n parameters, returns a vector of n indices of their values in the parameter
     137              :    list of it's caller.  Indices with unknown positions contain -1.  */
     138              : auto_vec<int>
     139        15069 : callback_get_arg_mapping (cgraph_edge *e, cgraph_edge *carrying)
     140              : {
     141        15069 :   tree attr = callback_fetch_attr_by_edge (e, carrying);
     142        15069 :   gcc_checking_assert (attr);
     143        15069 :   tree args = TREE_VALUE (attr);
     144        15069 :   auto_vec<int> res;
     145        15069 :   tree it;
     146              : 
     147              :   /* Skip over the first argument, which denotes
     148              :      which argument is the called function.  */
     149        30138 :   for (it = TREE_CHAIN (args); it != NULL_TREE; it = TREE_CHAIN (it))
     150              :     {
     151        15069 :       int idx = TREE_INT_CST_LOW (TREE_VALUE (it));
     152              :       /* Subtract 1 to account for 1-based indexing.  If the value is unknown,
     153              :          use constant -1 instead.  */
     154        15069 :       idx = idx == CB_UNKNOWN_POS ? -1 : idx - 1;
     155        15069 :       res.safe_push (idx);
     156              :     }
     157              : 
     158        15069 :   return res;
     159              : }
     160              : 
     161              : /* For a callback pair, returns the 0-based index of the address of
     162              :    E's callee in the argument list of CARRYING's callee decl.  */
     163              : int
     164         3425 : callback_fetch_fn_position (cgraph_edge *e, cgraph_edge *carrying)
     165              : {
     166         3425 :   tree attr = callback_fetch_attr_by_edge (e, carrying);
     167         3425 :   return callback_get_fn_index (attr);
     168              : }
     169              : 
     170              : /* Returns the element at index idx in the list or NULL_TREE if
     171              :    the list isn't long enough.  NULL_TREE is used as the endpoint.  */
     172              : static tree
     173       195600 : get_nth_list_elem (tree list, unsigned idx)
     174              : {
     175       195600 :   tree res = NULL_TREE;
     176       195600 :   unsigned i = 0;
     177       195600 :   tree it;
     178       293400 :   for (it = list; it != NULL_TREE; it = TREE_CHAIN (it), i++)
     179              :     {
     180       293400 :       if (i == idx)
     181              :         {
     182       195600 :           res = TREE_VALUE (it);
     183       195600 :           break;
     184              :         }
     185              :     }
     186       195600 :   return res;
     187              : }
     188              : 
     189              : /* Handle a "callback" attribute; arguments as in
     190              :    struct attribute_spec.handler.  */
     191              : tree
     192        97800 : handle_callback_attribute (tree *node, tree name, tree args,
     193              :                            int ARG_UNUSED (flags), bool *no_add_attrs)
     194              : {
     195        97800 :   tree decl = *node;
     196        97800 :   if (TREE_CODE (decl) != FUNCTION_DECL)
     197              :     {
     198            0 :       error_at (DECL_SOURCE_LOCATION (decl),
     199              :                 "%qE attribute can only be used on functions", name);
     200            0 :       *no_add_attrs = true;
     201              :     }
     202              : 
     203        97800 :   tree cb_fn_idx_node = TREE_VALUE (args);
     204        97800 :   if (TREE_CODE (cb_fn_idx_node) != INTEGER_CST)
     205              :     {
     206            0 :       error_at (DECL_SOURCE_LOCATION (decl),
     207              :                 "argument specifying callback function position is not an "
     208              :                 "integer constant");
     209            0 :       *no_add_attrs = true;
     210            0 :       return NULL_TREE;
     211              :     }
     212              :   /* We have to use the function type for validation, as
     213              :      DECL_ARGUMENTS returns NULL at this point.  */
     214        97800 :   int callback_fn_idx = TREE_INT_CST_LOW (cb_fn_idx_node);
     215        97800 :   tree decl_type_args = TYPE_ARG_TYPES (TREE_TYPE (decl));
     216        97800 :   tree it;
     217        97800 :   int decl_nargs = list_length (decl_type_args);
     218       839450 :   for (it = decl_type_args; it != NULL_TREE; it = TREE_CHAIN (it))
     219       741650 :     if (it == void_list_node)
     220              :       {
     221        97800 :         --decl_nargs;
     222        97800 :         break;
     223              :       }
     224        97800 :   if (callback_fn_idx == CB_UNKNOWN_POS)
     225              :     {
     226            0 :       error_at (DECL_SOURCE_LOCATION (decl),
     227              :                 "callback function position cannot be marked as unknown");
     228            0 :       *no_add_attrs = true;
     229            0 :       return NULL_TREE;
     230              :     }
     231        97800 :   --callback_fn_idx;
     232        97800 :   if (callback_fn_idx >= decl_nargs)
     233              :     {
     234            0 :       error_at (DECL_SOURCE_LOCATION (decl),
     235              :                 "callback function position out of range");
     236            0 :       *no_add_attrs = true;
     237            0 :       return NULL_TREE;
     238              :     }
     239              : 
     240              :   /* Search for the type of the callback function
     241              :      in parameters of the original function.  */
     242        97800 :   tree cfn = get_nth_list_elem (decl_type_args, callback_fn_idx);
     243        97800 :   if (cfn == NULL_TREE)
     244              :     {
     245            0 :       error_at (DECL_SOURCE_LOCATION (decl),
     246              :                 "could not retrieve callback function from arguments");
     247            0 :       *no_add_attrs = true;
     248            0 :       return NULL_TREE;
     249              :     }
     250        97800 :   tree cfn_pointee_type = TREE_TYPE (cfn);
     251        97800 :   if (TREE_CODE (cfn) != POINTER_TYPE
     252        97800 :       || TREE_CODE (cfn_pointee_type) != FUNCTION_TYPE)
     253              :     {
     254            0 :       error_at (DECL_SOURCE_LOCATION (decl),
     255              :                 "argument no. %d is not an address of a function",
     256              :                 callback_fn_idx + 1);
     257            0 :       *no_add_attrs = true;
     258            0 :       return NULL_TREE;
     259              :     }
     260              : 
     261        97800 :   tree type_args = TYPE_ARG_TYPES (cfn_pointee_type);
     262              :   /* Compare the length of the list of argument indices
     263              :      and the real number of parameters the callback takes.  */
     264        97800 :   unsigned cfn_nargs = list_length (TREE_CHAIN (args));
     265        97800 :   unsigned type_nargs = list_length (type_args);
     266       195600 :   for (it = type_args; it != NULL_TREE; it = TREE_CHAIN (it))
     267       195600 :     if (it == void_list_node)
     268              :       {
     269        97800 :         --type_nargs;
     270        97800 :         break;
     271              :       }
     272        97800 :   if (cfn_nargs != type_nargs)
     273              :     {
     274            0 :       error_at (DECL_SOURCE_LOCATION (decl),
     275              :                 "argument number mismatch, %d expected, got %d", type_nargs,
     276              :                 cfn_nargs);
     277            0 :       *no_add_attrs = true;
     278            0 :       return NULL_TREE;
     279              :     }
     280              : 
     281        97800 :   unsigned curr = 0;
     282        97800 :   tree cfn_it;
     283              :   /* Validate type compatibility of the arguments passed
     284              :      from caller function to callback.  "it" is used to step
     285              :      through the parameters of the caller, "cfn_it" is
     286              :      stepping through the parameters of the callback.  */
     287       195600 :   for (it = type_args, cfn_it = TREE_CHAIN (args); curr < type_nargs;
     288        97800 :        it = TREE_CHAIN (it), cfn_it = TREE_CHAIN (cfn_it), curr++)
     289              :     {
     290        97800 :       if (TREE_CODE (TREE_VALUE (cfn_it)) != INTEGER_CST)
     291              :         {
     292            0 :           error_at (DECL_SOURCE_LOCATION (decl),
     293              :                     "argument no. %d is not an integer constant", curr + 1);
     294            0 :           *no_add_attrs = true;
     295            0 :           continue;
     296              :         }
     297              : 
     298        97800 :       int arg_idx = TREE_INT_CST_LOW (TREE_VALUE (cfn_it));
     299              : 
     300              :       /* No need to check for type compatibility,
     301              :          if we don't know what we are passing.  */
     302        97800 :       if (arg_idx == CB_UNKNOWN_POS)
     303            0 :         continue;
     304              : 
     305        97800 :       arg_idx -= 1;
     306              :       /* Report an error if the position is out of bounds,
     307              :          but we can still check the rest of the arguments.  */
     308        97800 :       if (arg_idx >= decl_nargs)
     309              :         {
     310            0 :           error_at (DECL_SOURCE_LOCATION (decl),
     311              :                     "callback argument index %d is out of range", arg_idx + 1);
     312            0 :           *no_add_attrs = true;
     313            0 :           continue;
     314              :         }
     315              : 
     316        97800 :       tree arg_type = get_nth_list_elem (decl_type_args, arg_idx);
     317        97800 :       tree expected_type = TREE_VALUE (it);
     318              :       /* Check the type of the value we are about to pass ("arg_type")
     319              :          for compatibility with the actual type the callback function
     320              :          expects ("expected_type").  */
     321        97800 :       if (!types_compatible_p (expected_type, arg_type))
     322              :         {
     323            0 :           error_at (DECL_SOURCE_LOCATION (decl),
     324              :                     "argument type at index %d is not compatible with callback "
     325              :                     "argument type at index %d",
     326              :                     arg_idx + 1, curr + 1);
     327            0 :           *no_add_attrs = true;
     328            0 :           continue;
     329              :         }
     330              :     }
     331              : 
     332              :   /* Check that the decl does not already have a callback attribute describing
     333              :      the same argument.  */
     334        97800 :   it = lookup_attribute (CALLBACK_ATTR_IDENT, DECL_ATTRIBUTES (decl));
     335       195600 :   for (; it; it = lookup_attribute (CALLBACK_ATTR_IDENT, TREE_CHAIN (it)))
     336            0 :     if (callback_get_fn_index (it) == callback_fn_idx)
     337              :       {
     338            0 :         error_at (DECL_SOURCE_LOCATION (decl),
     339              :                   "function declaration has multiple callback attributes "
     340              :                   "describing argument no. %d",
     341              :                   callback_fn_idx + 1);
     342            0 :         *no_add_attrs = true;
     343            0 :         break;
     344              :       }
     345              : 
     346              :   return NULL_TREE;
     347              : }
     348              : 
     349              : /* Returns TRUE if E is considered useful in the callgraph, FALSE otherwise.  If
     350              :    this predicate returns FALSE, then E wasn't used to optimize its callee and
     351              :    can be safely removed from the callgraph.  */
     352              : bool
     353        13675 : callback_edge_useful_p (cgraph_edge *e)
     354              : {
     355        13675 :   gcc_checking_assert (e->callback);
     356              :   /* If the edge is pointing towards a clone, it is useful.  */
     357        13675 :   if (e->callee->clone_of)
     358              :     return true;
     359              : 
     360              :   /* If the callee has been produced by icf, the edge is useful, as it will be
     361              :      used to for the redirection.  */
     362        13576 :   if (e->callee->icf_merged)
     363          273 :     return true;
     364              : 
     365              :   /* In case some future pass redirects edges, it should be added as a case
     366              :      here.  */
     367              : 
     368              :   return false;
     369              : }
     370              : 
     371              : /* Returns the number of arguments the callback function described by ATTR
     372              :    takes.  */
     373              : 
     374              : size_t
     375        15069 : callback_num_args (tree attr)
     376              : {
     377        15069 :   tree args = TREE_VALUE (attr);
     378        15069 :   size_t res = 0;
     379        15069 :   tree it;
     380              : 
     381        30138 :   for (it = TREE_CHAIN (args); it != NULL_TREE; it = TREE_CHAIN (it), ++res)
     382              :     ;
     383        15069 :   return res;
     384              : }
        

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.