LCOV - code coverage report
Current view: top level - gcc - attr-callback.cc (source / functions) Coverage Total Hit
Test: gcc.info Lines: 74.2 % 151 112
Test Date: 2025-10-18 14:39:06 Functions: 90.9 % 11 10
Legend: Lines: hit not hit | Branches: + taken - not taken # not executed Branches: - 0 0

             Branch data     Line data    Source code
       1                 :             : /* Callback attribute handling
       2                 :             :    Copyright (C) 2025 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                 :      370038 : callback_build_attr (unsigned fn_idx, unsigned arg_count...)
      40                 :             : {
      41                 :      370038 :   va_list args;
      42                 :      370038 :   va_start (args, arg_count);
      43                 :             : 
      44                 :      370038 :   tree cblist = NULL_TREE;
      45                 :      370038 :   tree *pp = &cblist;
      46                 :      370038 :   unsigned i;
      47                 :      740076 :   for (i = 0; i < arg_count; i++)
      48                 :             :     {
      49                 :      370038 :       int num = va_arg (args, int);
      50                 :      370038 :       tree tnum = build_int_cst (integer_type_node, num);
      51                 :      370038 :       *pp = build_tree_list (NULL, tnum PASS_MEM_STAT);
      52                 :      370038 :       pp = &TREE_CHAIN (*pp);
      53                 :             :     }
      54                 :      370038 :   cblist
      55                 :      370038 :     = tree_cons (NULL_TREE, build_int_cst (integer_type_node, fn_idx), cblist);
      56                 :      370038 :   tree attr
      57                 :      370038 :     = tree_cons (get_identifier (CALLBACK_ATTR_IDENT), cblist, NULL_TREE);
      58                 :      370038 :   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                 :       27883 : callback_is_special_cased (tree decl, gcall *stmt)
      67                 :             : {
      68                 :       27883 :   if (fndecl_built_in_p (decl, BUILT_IN_GOMP_TASK))
      69                 :             :     {
      70                 :          15 :       if (stmt)
      71                 :          15 :         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                 :           6 : callback_special_case_attr (tree decl)
      80                 :             : {
      81                 :           6 :   if (fndecl_built_in_p (decl, BUILT_IN_GOMP_TASK))
      82                 :           6 :     return callback_build_attr (1, 1, 2);
      83                 :           0 :   gcc_unreachable ();
      84                 :             : }
      85                 :             : 
      86                 :             : /* Given an instance of callback attribute, return the 0-based
      87                 :             :    index of the called function in question.  */
      88                 :             : int
      89                 :          29 : callback_get_fn_index (tree cb_attr)
      90                 :             : {
      91                 :          29 :   tree args = TREE_VALUE (cb_attr);
      92                 :          29 :   int idx = TREE_INT_CST_LOW (TREE_VALUE (args)) - 1;
      93                 :          29 :   return idx;
      94                 :             : }
      95                 :             : 
      96                 :             : /* For a given callback pair, retrieves the callback attribute used
      97                 :             :    to create E from the callee of CARRYING.  */
      98                 :             : tree
      99                 :          16 : callback_fetch_attr_by_edge (cgraph_edge *e, cgraph_edge *carrying)
     100                 :             : {
     101                 :          16 :   gcc_checking_assert (e->call_stmt == carrying->call_stmt
     102                 :             :                        && e->lto_stmt_uid == carrying->lto_stmt_uid);
     103                 :             : 
     104                 :          16 :   if (callback_is_special_cased (carrying->callee->decl, e->call_stmt))
     105                 :           3 :     return callback_special_case_attr (carrying->callee->decl);
     106                 :             : 
     107                 :          13 :   tree cb_attr = lookup_attribute (CALLBACK_ATTR_IDENT,
     108                 :          13 :                                    DECL_ATTRIBUTES (carrying->callee->decl));
     109                 :          13 :   gcc_checking_assert (cb_attr);
     110                 :          13 :   tree res = NULL_TREE;
     111                 :          13 :   for (; cb_attr;
     112                 :           0 :        cb_attr = lookup_attribute (CALLBACK_ATTR_IDENT, TREE_CHAIN (cb_attr)))
     113                 :             :     {
     114                 :          13 :       unsigned id = callback_get_fn_index (cb_attr);
     115                 :          13 :       if (id == e->callback_id)
     116                 :             :         {
     117                 :             :           res = cb_attr;
     118                 :             :           break;
     119                 :             :         }
     120                 :             :     }
     121                 :          13 :   gcc_checking_assert (res != NULL_TREE);
     122                 :             :   return res;
     123                 :             : }
     124                 :             : 
     125                 :             : /* Given an instance of callback attribute, return the 0-base indices
     126                 :             :    of arguments passed to the callback.  For a callback function taking
     127                 :             :    n parameters, returns a vector of n indices of their values in the parameter
     128                 :             :    list of it's caller.  Indices with unknown positions contain -1.  */
     129                 :             : auto_vec<int>
     130                 :          16 : callback_get_arg_mapping (cgraph_edge *e, cgraph_edge *carrying)
     131                 :             : {
     132                 :          16 :   tree attr = callback_fetch_attr_by_edge (e, carrying);
     133                 :          16 :   gcc_checking_assert (attr);
     134                 :          16 :   tree args = TREE_VALUE (attr);
     135                 :          16 :   auto_vec<int> res;
     136                 :          16 :   tree it;
     137                 :             : 
     138                 :             :   /* Skip over the first argument, which denotes
     139                 :             :      which argument is the called function.  */
     140                 :          32 :   for (it = TREE_CHAIN (args); it != NULL_TREE; it = TREE_CHAIN (it))
     141                 :             :     {
     142                 :          16 :       int idx = TREE_INT_CST_LOW (TREE_VALUE (it));
     143                 :             :       /* Subtract 1 to account for 1-based indexing.  If the value is unknown,
     144                 :             :          use constant -1 instead.  */
     145                 :          16 :       idx = idx == CB_UNKNOWN_POS ? -1 : idx - 1;
     146                 :          16 :       res.safe_push (idx);
     147                 :             :     }
     148                 :             : 
     149                 :          16 :   return res;
     150                 :             : }
     151                 :             : 
     152                 :             : /* For a callback pair, returns the 0-based index of the address of
     153                 :             :    E's callee in the argument list of CARRYING's callee decl.  */
     154                 :             : int
     155                 :           0 : callback_fetch_fn_position (cgraph_edge *e, cgraph_edge *carrying)
     156                 :             : {
     157                 :           0 :   tree attr = callback_fetch_attr_by_edge (e, carrying);
     158                 :           0 :   return callback_get_fn_index (attr);
     159                 :             : }
     160                 :             : 
     161                 :             : /* Returns the element at index idx in the list or NULL_TREE if
     162                 :             :    the list isn't long enough.  NULL_TREE is used as the endpoint.  */
     163                 :             : static tree
     164                 :      189720 : get_nth_list_elem (tree list, unsigned idx)
     165                 :             : {
     166                 :      189720 :   tree res = NULL_TREE;
     167                 :      189720 :   unsigned i = 0;
     168                 :      189720 :   tree it;
     169                 :      284580 :   for (it = list; it != NULL_TREE; it = TREE_CHAIN (it), i++)
     170                 :             :     {
     171                 :      284580 :       if (i == idx)
     172                 :             :         {
     173                 :      189720 :           res = TREE_VALUE (it);
     174                 :      189720 :           break;
     175                 :             :         }
     176                 :             :     }
     177                 :      189720 :   return res;
     178                 :             : }
     179                 :             : 
     180                 :             : /* Handle a "callback" attribute; arguments as in
     181                 :             :    struct attribute_spec.handler.  */
     182                 :             : tree
     183                 :       94860 : handle_callback_attribute (tree *node, tree name, tree args,
     184                 :             :                            int ARG_UNUSED (flags), bool *no_add_attrs)
     185                 :             : {
     186                 :       94860 :   tree decl = *node;
     187                 :       94860 :   if (TREE_CODE (decl) != FUNCTION_DECL)
     188                 :             :     {
     189                 :           0 :       error_at (DECL_SOURCE_LOCATION (decl),
     190                 :             :                 "%qE attribute can only be used on functions", name);
     191                 :           0 :       *no_add_attrs = true;
     192                 :             :     }
     193                 :             : 
     194                 :       94860 :   tree cb_fn_idx_node = TREE_VALUE (args);
     195                 :       94860 :   if (TREE_CODE (cb_fn_idx_node) != INTEGER_CST)
     196                 :             :     {
     197                 :           0 :       error_at (DECL_SOURCE_LOCATION (decl),
     198                 :             :                 "argument specifying callback function position is not an "
     199                 :             :                 "integer constant");
     200                 :           0 :       *no_add_attrs = true;
     201                 :           0 :       return NULL_TREE;
     202                 :             :     }
     203                 :             :   /* We have to use the function type for validation, as
     204                 :             :      DECL_ARGUMENTS returns NULL at this point.  */
     205                 :       94860 :   int callback_fn_idx = TREE_INT_CST_LOW (cb_fn_idx_node);
     206                 :       94860 :   tree decl_type_args = TYPE_ARG_TYPES (TREE_TYPE (decl));
     207                 :       94860 :   tree it;
     208                 :       94860 :   int decl_nargs = list_length (decl_type_args);
     209                 :      814215 :   for (it = decl_type_args; it != NULL_TREE; it = TREE_CHAIN (it))
     210                 :      719355 :     if (it == void_list_node)
     211                 :             :       {
     212                 :       94860 :         --decl_nargs;
     213                 :       94860 :         break;
     214                 :             :       }
     215                 :       94860 :   if (callback_fn_idx == CB_UNKNOWN_POS)
     216                 :             :     {
     217                 :           0 :       error_at (DECL_SOURCE_LOCATION (decl),
     218                 :             :                 "callback function position cannot be marked as unknown");
     219                 :           0 :       *no_add_attrs = true;
     220                 :           0 :       return NULL_TREE;
     221                 :             :     }
     222                 :       94860 :   --callback_fn_idx;
     223                 :       94860 :   if (callback_fn_idx >= decl_nargs)
     224                 :             :     {
     225                 :           0 :       error_at (DECL_SOURCE_LOCATION (decl),
     226                 :             :                 "callback function position out of range");
     227                 :           0 :       *no_add_attrs = true;
     228                 :           0 :       return NULL_TREE;
     229                 :             :     }
     230                 :             : 
     231                 :             :   /* Search for the type of the callback function
     232                 :             :      in parameters of the original function.  */
     233                 :       94860 :   tree cfn = get_nth_list_elem (decl_type_args, callback_fn_idx);
     234                 :       94860 :   if (cfn == NULL_TREE)
     235                 :             :     {
     236                 :           0 :       error_at (DECL_SOURCE_LOCATION (decl),
     237                 :             :                 "could not retrieve callback function from arguments");
     238                 :           0 :       *no_add_attrs = true;
     239                 :           0 :       return NULL_TREE;
     240                 :             :     }
     241                 :       94860 :   tree cfn_pointee_type = TREE_TYPE (cfn);
     242                 :       94860 :   if (TREE_CODE (cfn) != POINTER_TYPE
     243                 :       94860 :       || TREE_CODE (cfn_pointee_type) != FUNCTION_TYPE)
     244                 :             :     {
     245                 :           0 :       error_at (DECL_SOURCE_LOCATION (decl),
     246                 :             :                 "argument no. %d is not an address of a function",
     247                 :             :                 callback_fn_idx + 1);
     248                 :           0 :       *no_add_attrs = true;
     249                 :           0 :       return NULL_TREE;
     250                 :             :     }
     251                 :             : 
     252                 :       94860 :   tree type_args = TYPE_ARG_TYPES (cfn_pointee_type);
     253                 :             :   /* Compare the length of the list of argument indices
     254                 :             :      and the real number of parameters the callback takes.  */
     255                 :       94860 :   unsigned cfn_nargs = list_length (TREE_CHAIN (args));
     256                 :       94860 :   unsigned type_nargs = list_length (type_args);
     257                 :      189720 :   for (it = type_args; it != NULL_TREE; it = TREE_CHAIN (it))
     258                 :      189720 :     if (it == void_list_node)
     259                 :             :       {
     260                 :       94860 :         --type_nargs;
     261                 :       94860 :         break;
     262                 :             :       }
     263                 :       94860 :   if (cfn_nargs != type_nargs)
     264                 :             :     {
     265                 :           0 :       error_at (DECL_SOURCE_LOCATION (decl),
     266                 :             :                 "argument number mismatch, %d expected, got %d", type_nargs,
     267                 :             :                 cfn_nargs);
     268                 :           0 :       *no_add_attrs = true;
     269                 :           0 :       return NULL_TREE;
     270                 :             :     }
     271                 :             : 
     272                 :       94860 :   unsigned curr = 0;
     273                 :       94860 :   tree cfn_it;
     274                 :             :   /* Validate type compatibility of the arguments passed
     275                 :             :      from caller function to callback.  "it" is used to step
     276                 :             :      through the parameters of the caller, "cfn_it" is
     277                 :             :      stepping through the parameters of the callback.  */
     278                 :      189720 :   for (it = type_args, cfn_it = TREE_CHAIN (args); curr < type_nargs;
     279                 :       94860 :        it = TREE_CHAIN (it), cfn_it = TREE_CHAIN (cfn_it), curr++)
     280                 :             :     {
     281                 :       94860 :       if (TREE_CODE (TREE_VALUE (cfn_it)) != INTEGER_CST)
     282                 :             :         {
     283                 :           0 :           error_at (DECL_SOURCE_LOCATION (decl),
     284                 :             :                     "argument no. %d is not an integer constant", curr + 1);
     285                 :           0 :           *no_add_attrs = true;
     286                 :           0 :           continue;
     287                 :             :         }
     288                 :             : 
     289                 :       94860 :       int arg_idx = TREE_INT_CST_LOW (TREE_VALUE (cfn_it));
     290                 :             : 
     291                 :             :       /* No need to check for type compatibility,
     292                 :             :          if we don't know what we are passing.  */
     293                 :       94860 :       if (arg_idx == CB_UNKNOWN_POS)
     294                 :           0 :         continue;
     295                 :             : 
     296                 :       94860 :       arg_idx -= 1;
     297                 :             :       /* Report an error if the position is out of bounds,
     298                 :             :          but we can still check the rest of the arguments.  */
     299                 :       94860 :       if (arg_idx >= decl_nargs)
     300                 :             :         {
     301                 :           0 :           error_at (DECL_SOURCE_LOCATION (decl),
     302                 :             :                     "callback argument index %d is out of range", arg_idx + 1);
     303                 :           0 :           *no_add_attrs = true;
     304                 :           0 :           continue;
     305                 :             :         }
     306                 :             : 
     307                 :       94860 :       tree arg_type = get_nth_list_elem (decl_type_args, arg_idx);
     308                 :       94860 :       tree expected_type = TREE_VALUE (it);
     309                 :             :       /* Check the type of the value we are about to pass ("arg_type")
     310                 :             :          for compatibility with the actual type the callback function
     311                 :             :          expects ("expected_type").  */
     312                 :       94860 :       if (!types_compatible_p (expected_type, arg_type))
     313                 :             :         {
     314                 :           0 :           error_at (DECL_SOURCE_LOCATION (decl),
     315                 :             :                     "argument type at index %d is not compatible with callback "
     316                 :             :                     "argument type at index %d",
     317                 :             :                     arg_idx + 1, curr + 1);
     318                 :           0 :           *no_add_attrs = true;
     319                 :           0 :           continue;
     320                 :             :         }
     321                 :             :     }
     322                 :             : 
     323                 :             :   /* Check that the decl does not already have a callback attribute describing
     324                 :             :      the same argument.  */
     325                 :       94860 :   it = lookup_attribute (CALLBACK_ATTR_IDENT, DECL_ATTRIBUTES (decl));
     326                 :      189720 :   for (; it; it = lookup_attribute (CALLBACK_ATTR_IDENT, TREE_CHAIN (it)))
     327                 :           0 :     if (callback_get_fn_index (it) == callback_fn_idx)
     328                 :             :       {
     329                 :           0 :         error_at (DECL_SOURCE_LOCATION (decl),
     330                 :             :                   "function declaration has multiple callback attributes "
     331                 :             :                   "describing argument no. %d",
     332                 :             :                   callback_fn_idx + 1);
     333                 :           0 :         *no_add_attrs = true;
     334                 :           0 :         break;
     335                 :             :       }
     336                 :             : 
     337                 :             :   return NULL_TREE;
     338                 :             : }
     339                 :             : 
     340                 :             : /* Returns TRUE if E is considered useful in the callgraph, FALSE otherwise.  If
     341                 :             :    this predicate returns FALSE, then E wasn't used to optimize its callee and
     342                 :             :    can be safely removed from the callgraph.  */
     343                 :             : bool
     344                 :           9 : callback_edge_useful_p (cgraph_edge *e)
     345                 :             : {
     346                 :           9 :   gcc_checking_assert (e->callback);
     347                 :             :   /* If the edge is not pointing towards a clone, it is no longer useful as its
     348                 :             :      entire purpose is to produce clones of callbacks.  */
     349                 :           9 :   if (!e->callee->clone_of)
     350                 :           9 :     return false;
     351                 :             :   return true;
     352                 :             : }
     353                 :             : 
     354                 :             : /* Returns the number of arguments the callback function described by ATTR
     355                 :             :    takes.  */
     356                 :             : 
     357                 :             : size_t
     358                 :          16 : callback_num_args (tree attr)
     359                 :             : {
     360                 :          16 :   tree args = TREE_VALUE (attr);
     361                 :          16 :   size_t res = 0;
     362                 :          16 :   tree it;
     363                 :             : 
     364                 :          32 :   for (it = TREE_CHAIN (args); it != NULL_TREE; it = TREE_CHAIN (it), ++res)
     365                 :             :     ;
     366                 :          16 :   return res;
     367                 :             : }
        

Generated by: LCOV version 2.1-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.