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 : : }
|