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 : 375400 : callback_build_attr (unsigned fn_idx, unsigned arg_count...)
40 : : {
41 : 375400 : va_list args;
42 : 375400 : va_start (args, arg_count);
43 : :
44 : 375400 : tree cblist = NULL_TREE;
45 : 375400 : tree *pp = &cblist;
46 : 375400 : unsigned i;
47 : 750800 : for (i = 0; i < arg_count; i++)
48 : : {
49 : 375400 : int num = va_arg (args, int);
50 : 375400 : tree tnum = build_int_cst (integer_type_node, num);
51 : 375400 : *pp = build_tree_list (NULL, tnum);
52 : 375400 : pp = &TREE_CHAIN (*pp);
53 : : }
54 : 375400 : cblist
55 : 375400 : = tree_cons (NULL_TREE, build_int_cst (integer_type_node, fn_idx), cblist);
56 : 375400 : tree attr
57 : 375400 : = tree_cons (get_identifier (CALLBACK_ATTR_IDENT), cblist, NULL_TREE);
58 : 375400 : 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 : 2721447 : callback_is_special_cased (tree decl, gcall *stmt)
67 : : {
68 : 2721447 : if (fndecl_built_in_p (decl, BUILT_IN_GOMP_TASK))
69 : : {
70 : 18962 : if (stmt)
71 : 18962 : 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 : 4276 : callback_special_case_attr (tree decl)
80 : : {
81 : 4276 : if (fndecl_built_in_p (decl, BUILT_IN_GOMP_TASK))
82 : 4276 : 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 : 2586621 : callback_edge_callee_has_attr (cgraph_edge *e)
89 : : {
90 : 2586621 : return lookup_attribute (CALLBACK_ATTR_IDENT,
91 : 2586621 : DECL_ATTRIBUTES (e->callee->decl))
92 : 2586621 : || 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 : 34430 : callback_get_fn_index (tree cb_attr)
99 : : {
100 : 34430 : tree args = TREE_VALUE (cb_attr);
101 : 34430 : int idx = TREE_INT_CST_LOW (TREE_VALUE (args)) - 1;
102 : 34430 : 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 : 18476 : callback_fetch_attr_by_edge (cgraph_edge *e, cgraph_edge *carrying)
109 : : {
110 : 18476 : gcc_checking_assert (e->call_stmt == carrying->call_stmt
111 : : && e->lto_stmt_uid == carrying->lto_stmt_uid);
112 : :
113 : 18476 : if (callback_is_special_cased (carrying->callee->decl, e->call_stmt))
114 : 2522 : return callback_special_case_attr (carrying->callee->decl);
115 : :
116 : 15954 : tree cb_attr = lookup_attribute (CALLBACK_ATTR_IDENT,
117 : 15954 : DECL_ATTRIBUTES (carrying->callee->decl));
118 : 15954 : gcc_checking_assert (cb_attr);
119 : 15954 : tree res = NULL_TREE;
120 : 15954 : for (; cb_attr;
121 : 0 : cb_attr = lookup_attribute (CALLBACK_ATTR_IDENT, TREE_CHAIN (cb_attr)))
122 : : {
123 : 15954 : unsigned id = callback_get_fn_index (cb_attr);
124 : 15954 : if (id == e->callback_id)
125 : : {
126 : : res = cb_attr;
127 : : break;
128 : : }
129 : : }
130 : 15954 : 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 : 15054 : callback_get_arg_mapping (cgraph_edge *e, cgraph_edge *carrying)
140 : : {
141 : 15054 : tree attr = callback_fetch_attr_by_edge (e, carrying);
142 : 15054 : gcc_checking_assert (attr);
143 : 15054 : tree args = TREE_VALUE (attr);
144 : 15054 : auto_vec<int> res;
145 : 15054 : tree it;
146 : :
147 : : /* Skip over the first argument, which denotes
148 : : which argument is the called function. */
149 : 30108 : for (it = TREE_CHAIN (args); it != NULL_TREE; it = TREE_CHAIN (it))
150 : : {
151 : 15054 : 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 : 15054 : idx = idx == CB_UNKNOWN_POS ? -1 : idx - 1;
155 : 15054 : res.safe_push (idx);
156 : : }
157 : :
158 : 15054 : 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 : 3422 : callback_fetch_fn_position (cgraph_edge *e, cgraph_edge *carrying)
165 : : {
166 : 3422 : tree attr = callback_fetch_attr_by_edge (e, carrying);
167 : 3422 : 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 : 190080 : get_nth_list_elem (tree list, unsigned idx)
174 : : {
175 : 190080 : tree res = NULL_TREE;
176 : 190080 : unsigned i = 0;
177 : 190080 : tree it;
178 : 285120 : for (it = list; it != NULL_TREE; it = TREE_CHAIN (it), i++)
179 : : {
180 : 285120 : if (i == idx)
181 : : {
182 : 190080 : res = TREE_VALUE (it);
183 : 190080 : break;
184 : : }
185 : : }
186 : 190080 : return res;
187 : : }
188 : :
189 : : /* Handle a "callback" attribute; arguments as in
190 : : struct attribute_spec.handler. */
191 : : tree
192 : 95040 : handle_callback_attribute (tree *node, tree name, tree args,
193 : : int ARG_UNUSED (flags), bool *no_add_attrs)
194 : : {
195 : 95040 : tree decl = *node;
196 : 95040 : 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 : 95040 : tree cb_fn_idx_node = TREE_VALUE (args);
204 : 95040 : 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 : 95040 : int callback_fn_idx = TREE_INT_CST_LOW (cb_fn_idx_node);
215 : 95040 : tree decl_type_args = TYPE_ARG_TYPES (TREE_TYPE (decl));
216 : 95040 : tree it;
217 : 95040 : int decl_nargs = list_length (decl_type_args);
218 : 815760 : for (it = decl_type_args; it != NULL_TREE; it = TREE_CHAIN (it))
219 : 720720 : if (it == void_list_node)
220 : : {
221 : 95040 : --decl_nargs;
222 : 95040 : break;
223 : : }
224 : 95040 : 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 : 95040 : --callback_fn_idx;
232 : 95040 : 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 : 95040 : tree cfn = get_nth_list_elem (decl_type_args, callback_fn_idx);
243 : 95040 : 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 : 95040 : tree cfn_pointee_type = TREE_TYPE (cfn);
251 : 95040 : if (TREE_CODE (cfn) != POINTER_TYPE
252 : 95040 : || 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 : 95040 : 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 : 95040 : unsigned cfn_nargs = list_length (TREE_CHAIN (args));
265 : 95040 : unsigned type_nargs = list_length (type_args);
266 : 190080 : for (it = type_args; it != NULL_TREE; it = TREE_CHAIN (it))
267 : 190080 : if (it == void_list_node)
268 : : {
269 : 95040 : --type_nargs;
270 : 95040 : break;
271 : : }
272 : 95040 : 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 : 95040 : unsigned curr = 0;
282 : 95040 : 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 : 190080 : for (it = type_args, cfn_it = TREE_CHAIN (args); curr < type_nargs;
288 : 95040 : it = TREE_CHAIN (it), cfn_it = TREE_CHAIN (cfn_it), curr++)
289 : : {
290 : 95040 : 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 : 95040 : 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 : 95040 : if (arg_idx == CB_UNKNOWN_POS)
303 : 0 : continue;
304 : :
305 : 95040 : 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 : 95040 : 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 : 95040 : tree arg_type = get_nth_list_elem (decl_type_args, arg_idx);
317 : 95040 : 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 : 95040 : 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 : 95040 : it = lookup_attribute (CALLBACK_ATTR_IDENT, DECL_ATTRIBUTES (decl));
335 : 190080 : 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 : 13645 : callback_edge_useful_p (cgraph_edge *e)
354 : : {
355 : 13645 : gcc_checking_assert (e->callback);
356 : : /* If the edge is pointing towards a clone, it is useful. */
357 : 13645 : 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 : 13548 : 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 : 15054 : callback_num_args (tree attr)
376 : : {
377 : 15054 : tree args = TREE_VALUE (attr);
378 : 15054 : size_t res = 0;
379 : 15054 : tree it;
380 : :
381 : 30108 : for (it = TREE_CHAIN (args); it != NULL_TREE; it = TREE_CHAIN (it), ++res)
382 : : ;
383 : 15054 : return res;
384 : : }
|