[gcc r17-3013] Add the callback_only attribute.
Josef Melcr via Gcc-cvs <[email protected]> Thu, 6 Aug 2026 09:13:28 +0000 (GMT)
| Newsgroups | gmane.comp.gcc.cvs |
|---|---|
| Message-ID | <[email protected]> |
https://gcc.gnu.org/g:cad654efbba62da3d98d9ca6a4c1658fcd47ffb4 commit r17-3013-gcad654efbba62da3d98d9ca6a4c1658fcd47ffb4 Author: Josef Melcr <[email protected]> Date: Mon Jul 27 16:18:54 2026 +0200 Add the callback_only attribute. This patch allows the callback attribute to be used in user programs under the name 'callback_only'. It moves the attribute handler into c-attribs.cc and renames some of the identifiers from callback to callback_only for the sake of consistency. gcc/ChangeLog: * attr-callback.cc (callback_build_attr): Use "callback_only" instead of the macro. (callback_edge_callee_has_attr): Likewise. (callback_fetch_attr_by_edge): Likewise. (get_nth_list_elem): Move to c-family/c-attribs.cc. (handle_callback_attribute): Rename to handle_callback_only_attribute, move to c-family/c-attribs.cc. * attr-callback.h (CALLBACK_ATTR_IDENT): Remove the macro. (handle_callback_attribute): Remove the declaration. * builtin-attrs.def (ATTR_CALLBACK): Rename to ATTR_CALLBACK_ONLY. (ATTR_CALLBACK_ONLY): Identifier for the attribute for builtins. (DEF_CALLBACK_ATTRIBUTE): Rename to DEF_CALLBACK_ONLY_ATTRIBUTE. (DEF_CALLBACK_ONLY_ATTRIBUTE): Renamed definition macro for builtins. (GOMP): Use the renamed macro. (ATTR_CALLBACK_GOMP_LIST): Renamed to ATTR_CALLBACK_ONLY_GOMP_LIST. (ATTR_CALLBACK_ONLY_GOMP_LIST): ATTR_NOTHROW_LIST with the callback_only attribute for libgomp functions. * cgraph.cc (cgraph_edge::redirect_call_stmt_to_callee): Use "callback_only" instead of the macro. (cgraph_node::verify_node): Likewise. * doc/extend.texi: Add callback_only attribute docs. * ipa-cp.cc (purge_useless_callback_edges): Use "callback_only" instead of the macro. * ipa-param-manipulation.cc (drop_decl_attribute_if_params_changed_p): Likewise. * ipa-prop.cc (ipa_compute_jump_functions_for_edge): Likewise. * omp-builtins.def (BUILT_IN_GOMP_PARALLEL_LOOP_STATIC): Use the renamed attribute list. (BUILT_IN_GOMP_PARALLEL_LOOP_GUIDED): Likewise. (BUILT_IN_GOMP_PARALLEL_LOOP_NONMONOTONIC_DYNAMIC): Likewise. (BUILT_IN_GOMP_PARALLEL_LOOP_NONMONOTONIC_RUNTIME): Likewise. (BUILT_IN_GOMP_PARALLEL): Likewise. (BUILT_IN_GOMP_PARALLEL_SECTIONS): Likewise. (BUILT_IN_GOMP_TEAMS_REG): Likewise. * tree-core.h: Update the ECF_CB flag comment. gcc/c-family/ChangeLog: * c-attribs.cc (handle_callback_only_attribute): Moved from attr-callback.cc. (get_nth_list_elem): Likewise. gcc/fortran/ChangeLog: * f95-lang.cc (ATTR_CALLBACK_GOMP_LIST): Rename to ATTR_CALLBACK_ONLY_GOMP_LIST. (ATTR_CALLBACK_ONLY_GOMP_LIST): Fortran counterpart for the attribute list in builtin-attrs.def. gcc/testsuite/ChangeLog: * gcc.dg/attr-callback.c: New test. * gcc.dg/ipa/ipcp-cb2.c: New test. Signed-off-by: Josef Melcr <[email protected]> Diff: --- gcc/attr-callback.cc | 187 +---------------------------------- gcc/attr-callback.h | 7 -- gcc/builtin-attrs.def | 18 ++-- gcc/c-family/c-attribs.cc | 184 +++++++++++++++++++++++++++++++++- gcc/cgraph.cc | 11 ++- gcc/doc/extend.texi | 39 ++++++++ gcc/fortran/f95-lang.cc | 2 +- gcc/ipa-cp.cc | 2 +- gcc/ipa-param-manipulation.cc | 2 +- gcc/ipa-prop.cc | 4 +- gcc/omp-builtins.def | 26 ++--- gcc/testsuite/gcc.dg/attr-callback.c | 107 ++++++++++++++++++++ gcc/testsuite/gcc.dg/ipa/ipcp-cb2.c | 60 +++++++++++ gcc/tree-core.h | 2 +- 14 files changed, 427 insertions(+), 224 deletions(-) diff --git a/gcc/attr-callback.cc b/gcc/attr-callback.cc index c654d74164db..19a8770707b4 100644 --- a/gcc/attr-callback.cc +++ b/gcc/attr-callback.cc @@ -54,7 +54,7 @@ callback_build_attr (unsigned fn_idx, unsigned arg_count...) cblist = tree_cons (NULL_TREE, build_int_cst (integer_type_node, fn_idx), cblist); tree attr - = tree_cons (get_identifier (CALLBACK_ATTR_IDENT), cblist, NULL_TREE); + = tree_cons (get_identifier ("callback_only"), cblist, NULL_TREE); return attr; } @@ -87,7 +87,7 @@ callback_special_case_attr (tree decl) bool callback_edge_callee_has_attr (cgraph_edge *e) { - return lookup_attribute (CALLBACK_ATTR_IDENT, + return lookup_attribute ("callback_only", DECL_ATTRIBUTES (e->callee->decl)) || callback_is_special_cased (e->callee->decl, e->call_stmt); } @@ -113,12 +113,12 @@ callback_fetch_attr_by_edge (cgraph_edge *e, cgraph_edge *carrying) if (callback_is_special_cased (carrying->callee->decl, e->call_stmt)) return callback_special_case_attr (carrying->callee->decl); - tree cb_attr = lookup_attribute (CALLBACK_ATTR_IDENT, + tree cb_attr = lookup_attribute ("callback_only", DECL_ATTRIBUTES (carrying->callee->decl)); gcc_checking_assert (cb_attr); tree res = NULL_TREE; for (; cb_attr; - cb_attr = lookup_attribute (CALLBACK_ATTR_IDENT, TREE_CHAIN (cb_attr))) + cb_attr = lookup_attribute ("callback_only", TREE_CHAIN (cb_attr))) { unsigned id = callback_get_fn_index (cb_attr); if (id == e->callback_id) @@ -167,185 +167,6 @@ callback_fetch_fn_position (cgraph_edge *e, cgraph_edge *carrying) return callback_get_fn_index (attr); } -/* Returns the element at index idx in the list or NULL_TREE if - the list isn't long enough. NULL_TREE is used as the endpoint. */ -static tree -get_nth_list_elem (tree list, unsigned idx) -{ - tree res = NULL_TREE; - unsigned i = 0; - tree it; - for (it = list; it != NULL_TREE; it = TREE_CHAIN (it), i++) - { - if (i == idx) - { - res = TREE_VALUE (it); - break; - } - } - return res; -} - -/* Handle a "callback" attribute; arguments as in - struct attribute_spec.handler. */ -tree -handle_callback_attribute (tree *node, tree name, tree args, - int ARG_UNUSED (flags), bool *no_add_attrs) -{ - tree decl = *node; - if (TREE_CODE (decl) != FUNCTION_DECL) - { - error_at (DECL_SOURCE_LOCATION (decl), - "%qE attribute can only be used on functions", name); - *no_add_attrs = true; - } - - tree cb_fn_idx_node = TREE_VALUE (args); - if (TREE_CODE (cb_fn_idx_node) != INTEGER_CST) - { - error_at (DECL_SOURCE_LOCATION (decl), - "argument specifying callback function position is not an " - "integer constant"); - *no_add_attrs = true; - return NULL_TREE; - } - /* We have to use the function type for validation, as - DECL_ARGUMENTS returns NULL at this point. */ - int callback_fn_idx = TREE_INT_CST_LOW (cb_fn_idx_node); - tree decl_type_args = TYPE_ARG_TYPES (TREE_TYPE (decl)); - tree it; - int decl_nargs = list_length (decl_type_args); - for (it = decl_type_args; it != NULL_TREE; it = TREE_CHAIN (it)) - if (it == void_list_node) - { - --decl_nargs; - break; - } - if (callback_fn_idx == CB_UNKNOWN_POS) - { - error_at (DECL_SOURCE_LOCATION (decl), - "callback function position cannot be marked as unknown"); - *no_add_attrs = true; - return NULL_TREE; - } - --callback_fn_idx; - if (callback_fn_idx >= decl_nargs) - { - error_at (DECL_SOURCE_LOCATION (decl), - "callback function position out of range"); - *no_add_attrs = true; - return NULL_TREE; - } - - /* Search for the type of the callback function - in parameters of the original function. */ - tree cfn = get_nth_list_elem (decl_type_args, callback_fn_idx); - if (cfn == NULL_TREE) - { - error_at (DECL_SOURCE_LOCATION (decl), - "could not retrieve callback function from arguments"); - *no_add_attrs = true; - return NULL_TREE; - } - tree cfn_pointee_type = TREE_TYPE (cfn); - if (TREE_CODE (cfn) != POINTER_TYPE - || TREE_CODE (cfn_pointee_type) != FUNCTION_TYPE) - { - error_at (DECL_SOURCE_LOCATION (decl), - "argument no. %d is not an address of a function", - callback_fn_idx + 1); - *no_add_attrs = true; - return NULL_TREE; - } - - tree type_args = TYPE_ARG_TYPES (cfn_pointee_type); - /* Compare the length of the list of argument indices - and the real number of parameters the callback takes. */ - unsigned cfn_nargs = list_length (TREE_CHAIN (args)); - unsigned type_nargs = list_length (type_args); - for (it = type_args; it != NULL_TREE; it = TREE_CHAIN (it)) - if (it == void_list_node) - { - --type_nargs; - break; - } - if (cfn_nargs != type_nargs) - { - error_at (DECL_SOURCE_LOCATION (decl), - "argument number mismatch, %d expected, got %d", type_nargs, - cfn_nargs); - *no_add_attrs = true; - return NULL_TREE; - } - - unsigned curr = 0; - tree cfn_it; - /* Validate type compatibility of the arguments passed - from caller function to callback. "it" is used to step - through the parameters of the caller, "cfn_it" is - stepping through the parameters of the callback. */ - for (it = type_args, cfn_it = TREE_CHAIN (args); curr < type_nargs; - it = TREE_CHAIN (it), cfn_it = TREE_CHAIN (cfn_it), curr++) - { - if (TREE_CODE (TREE_VALUE (cfn_it)) != INTEGER_CST) - { - error_at (DECL_SOURCE_LOCATION (decl), - "argument no. %d is not an integer constant", curr + 1); - *no_add_attrs = true; - continue; - } - - int arg_idx = TREE_INT_CST_LOW (TREE_VALUE (cfn_it)); - - /* No need to check for type compatibility, - if we don't know what we are passing. */ - if (arg_idx == CB_UNKNOWN_POS) - continue; - - arg_idx -= 1; - /* Report an error if the position is out of bounds, - but we can still check the rest of the arguments. */ - if (arg_idx >= decl_nargs) - { - error_at (DECL_SOURCE_LOCATION (decl), - "callback argument index %d is out of range", arg_idx + 1); - *no_add_attrs = true; - continue; - } - - tree arg_type = get_nth_list_elem (decl_type_args, arg_idx); - tree expected_type = TREE_VALUE (it); - /* Check the type of the value we are about to pass ("arg_type") - for compatibility with the actual type the callback function - expects ("expected_type"). */ - if (!types_compatible_p (expected_type, arg_type)) - { - error_at (DECL_SOURCE_LOCATION (decl), - "argument type at index %d is not compatible with callback " - "argument type at index %d", - arg_idx + 1, curr + 1); - *no_add_attrs = true; - continue; - } - } - - /* Check that the decl does not already have a callback attribute describing - the same argument. */ - it = lookup_attribute (CALLBACK_ATTR_IDENT, DECL_ATTRIBUTES (decl)); - for (; it; it = lookup_attribute (CALLBACK_ATTR_IDENT, TREE_CHAIN (it))) - if (callback_get_fn_index (it) == callback_fn_idx) - { - error_at (DECL_SOURCE_LOCATION (decl), - "function declaration has multiple callback attributes " - "describing argument no. %d", - callback_fn_idx + 1); - *no_add_attrs = true; - break; - } - - return NULL_TREE; -} - /* Returns TRUE if E is considered useful in the callgraph, FALSE otherwise. If this predicate returns FALSE, then E wasn't used to optimize its callee and can be safely removed from the callgraph. */ diff --git a/gcc/attr-callback.h b/gcc/attr-callback.h index b2c1c3c09c5d..5ed93b3afefc 100644 --- a/gcc/attr-callback.h +++ b/gcc/attr-callback.h @@ -28,8 +28,6 @@ enum callback_position CB_UNKNOWN_POS = 0 }; -#define CALLBACK_ATTR_IDENT " callback" - /* Returns a callback attribute with callback index FN_IDX, and ARG_COUNT arguments specified by VA_ARGS. */ tree callback_build_attr (unsigned fn_idx, unsigned arg_count...); @@ -64,11 +62,6 @@ auto_vec<int> callback_get_arg_mapping (cgraph_edge *e, cgraph_edge *carrying); E's callee in the argument list of CARRYING's callee decl. */ int callback_fetch_fn_position (cgraph_edge *e, cgraph_edge *carrying); -/* Handle a "callback" attribute; arguments as in - struct attribute_spec.handler. */ -tree handle_callback_attribute (tree *node, tree name, tree args, int flags, - bool *no_add_attrs); - /* Returns TRUE if E is considered useful in the callgraph, FALSE otherwise. If this predicate returns FALSE, then E wasn't used to optimize its callee and can be safely removed from the callgraph. */ diff --git a/gcc/builtin-attrs.def b/gcc/builtin-attrs.def index be80184b1a16..e7458ed01b06 100644 --- a/gcc/builtin-attrs.def +++ b/gcc/builtin-attrs.def @@ -130,7 +130,7 @@ DEF_ATTR_IDENT (ATTR_TM_TMPURE, "transaction_pure") DEF_ATTR_IDENT (ATTR_RETURNS_TWICE, "returns_twice") DEF_ATTR_IDENT (ATTR_RETURNS_NONNULL, "returns_nonnull") DEF_ATTR_IDENT (ATTR_WARN_UNUSED_RESULT, "warn_unused_result") -DEF_ATTR_IDENT (ATTR_CALLBACK, " callback") +DEF_ATTR_IDENT (ATTR_CALLBACK_ONLY, "callback_only") DEF_ATTR_TREE_LIST (ATTR_NOVOPS_LIST, ATTR_NOVOPS, ATTR_NULL, ATTR_NULL) @@ -431,15 +431,15 @@ DEF_FORMAT_ATTRIBUTE_NOTHROW(STRFMON,3,3_4) #undef DEF_FORMAT_ATTRIBUTE_NOTHROW #undef DEF_FORMAT_ATTRIBUTE_BOTH -/* Construct callback attributes for GOMP builtins. */ -#define DEF_CALLBACK_ATTRIBUTE(TYPE, CA, VALUES) \ - DEF_ATTR_TREE_LIST (ATTR_CALLBACK_##TYPE##_##CA##_##VALUES, ATTR_CALLBACK,\ - ATTR_##CA, ATTR_LIST_##VALUES) +/* Construct callback_only attributes for GOMP builtins. */ +#define DEF_CALLBACK_ONLY_ATTRIBUTE(TYPE, CA, VALUES) \ + DEF_ATTR_TREE_LIST (ATTR_CALLBACK_ONLY_##TYPE##_##CA##_##VALUES, \ + ATTR_CALLBACK_ONLY, ATTR_##CA, ATTR_LIST_##VALUES) -DEF_CALLBACK_ATTRIBUTE(GOMP, 1, 2) -DEF_ATTR_TREE_LIST(ATTR_CALLBACK_GOMP_LIST, ATTR_CALLBACK, - ATTR_CALLBACK_GOMP_1_2, ATTR_NOTHROW_LIST) -#undef DEF_CALLBACK_ATTRIBUTE +DEF_CALLBACK_ONLY_ATTRIBUTE(GOMP, 1, 2) +DEF_ATTR_TREE_LIST(ATTR_CALLBACK_ONLY_GOMP_LIST, ATTR_CALLBACK_ONLY, + ATTR_CALLBACK_ONLY_GOMP_1_2, ATTR_NOTHROW_LIST) +#undef DEF_CALLBACK_ONLY_ATTRIBUTE /* Transactional memory variants of the above. */ diff --git a/gcc/c-family/c-attribs.cc b/gcc/c-family/c-attribs.cc index d668ab96630a..72702b35aedb 100644 --- a/gcc/c-family/c-attribs.cc +++ b/gcc/c-family/c-attribs.cc @@ -131,6 +131,7 @@ static tree handle_no_limit_stack_attribute (tree *, tree, tree, int, static tree handle_pure_attribute (tree *, tree, tree, int, bool *); static tree handle_tm_attribute (tree *, tree, tree, int, bool *); static tree handle_tm_wrap_attribute (tree *, tree, tree, int, bool *); +static tree handle_callback_only_attribute (tree *, tree, tree, int, bool *); static tree handle_novops_attribute (tree *, tree, tree, int, bool *); static tree handle_unavailable_attribute (tree *, tree, tree, int, bool *); @@ -485,8 +486,8 @@ const struct attribute_spec c_common_gnu_attributes[] = handle_tm_attribute, NULL }, { "transaction_may_cancel_outer", 0, 0, false, true, false, false, handle_tm_attribute, NULL }, - { CALLBACK_ATTR_IDENT, 1, -1, true, false, false, false, - handle_callback_attribute, NULL }, + { "callback_only", 1, -1, true, false, false, false, + handle_callback_only_attribute, NULL }, /* ??? These two attributes didn't make the transition from the Intel language document to the multi-vendor language document. */ { "transaction_pure", 0, 0, false, true, false, false, @@ -4672,6 +4673,185 @@ handle_tm_wrap_attribute (tree *node, tree name, tree args, return NULL_TREE; } +/* Returns the element at index idx in the list or NULL_TREE if + the list isn't long enough. NULL_TREE is used as the endpoint. */ +static tree +get_nth_list_elem (tree list, unsigned idx) +{ + tree res = NULL_TREE; + unsigned i = 0; + tree it; + for (it = list; it != NULL_TREE; it = TREE_CHAIN (it), i++) + { + if (i == idx) + { + res = TREE_VALUE (it); + break; + } + } + return res; +} + +/* Handle a "callback_only" attribute; arguments as in + struct attribute_spec.handler. */ +tree +handle_callback_only_attribute (tree *node, tree name, tree args, + int ARG_UNUSED (flags), bool *no_add_attrs) +{ + tree decl = *node; + if (TREE_CODE (decl) != FUNCTION_DECL) + { + error_at (DECL_SOURCE_LOCATION (decl), + "%qE attribute can only be used on functions", name); + *no_add_attrs = true; + } + + tree cb_fn_idx_node = TREE_VALUE (args); + if (TREE_CODE (cb_fn_idx_node) != INTEGER_CST) + { + error_at (DECL_SOURCE_LOCATION (decl), + "argument specifying callback function position is not an " + "integer constant"); + *no_add_attrs = true; + return NULL_TREE; + } + /* We have to use the function type for validation, as + DECL_ARGUMENTS returns NULL at this point. */ + int callback_fn_idx = TREE_INT_CST_LOW (cb_fn_idx_node); + tree decl_type_args = TYPE_ARG_TYPES (TREE_TYPE (decl)); + tree it; + int decl_nargs = list_length (decl_type_args); + for (it = decl_type_args; it != NULL_TREE; it = TREE_CHAIN (it)) + if (it == void_list_node) + { + --decl_nargs; + break; + } + if (callback_fn_idx == CB_UNKNOWN_POS) + { + error_at (DECL_SOURCE_LOCATION (decl), + "callback function position cannot be marked as unknown"); + *no_add_attrs = true; + return NULL_TREE; + } + --callback_fn_idx; + if (callback_fn_idx >= decl_nargs) + { + error_at (DECL_SOURCE_LOCATION (decl), + "callback function position out of range"); + *no_add_attrs = true; + return NULL_TREE; + } + + /* Search for the type of the callback function + in parameters of the original function. */ + tree cfn = get_nth_list_elem (decl_type_args, callback_fn_idx); + if (cfn == NULL_TREE) + { + error_at (DECL_SOURCE_LOCATION (decl), + "could not retrieve callback function from arguments"); + *no_add_attrs = true; + return NULL_TREE; + } + tree cfn_pointee_type = TREE_TYPE (cfn); + if (TREE_CODE (cfn) != POINTER_TYPE + || TREE_CODE (cfn_pointee_type) != FUNCTION_TYPE) + { + error_at (DECL_SOURCE_LOCATION (decl), + "argument no. %d is not an address of a function", + callback_fn_idx + 1); + *no_add_attrs = true; + return NULL_TREE; + } + + tree type_args = TYPE_ARG_TYPES (cfn_pointee_type); + /* Compare the length of the list of argument indices + and the real number of parameters the callback takes. */ + unsigned cfn_nargs = list_length (TREE_CHAIN (args)); + unsigned type_nargs = list_length (type_args); + for (it = type_args; it != NULL_TREE; it = TREE_CHAIN (it)) + if (it == void_list_node) + { + --type_nargs; + break; + } + if (cfn_nargs != type_nargs) + { + error_at (DECL_SOURCE_LOCATION (decl), + "argument number mismatch, %d expected, got %d", type_nargs, + cfn_nargs); + *no_add_attrs = true; + return NULL_TREE; + } + + unsigned curr = 0; + tree cfn_it; + /* Validate type compatibility of the arguments passed + from caller function to callback. "it" is used to step + through the parameters of the caller, "cfn_it" is + stepping through the parameters of the callback. */ + for (it = type_args, cfn_it = TREE_CHAIN (args); curr < type_nargs; + it = TREE_CHAIN (it), cfn_it = TREE_CHAIN (cfn_it), curr++) + { + if (TREE_CODE (TREE_VALUE (cfn_it)) != INTEGER_CST) + { + error_at (DECL_SOURCE_LOCATION (decl), + "argument no. %d is not an integer constant", curr + 1); + *no_add_attrs = true; + continue; + } + + int arg_idx = TREE_INT_CST_LOW (TREE_VALUE (cfn_it)); + + /* No need to check for type compatibility, + if we don't know what we are passing. */ + if (arg_idx == CB_UNKNOWN_POS) + continue; + + arg_idx -= 1; + /* Report an error if the position is out of bounds, + but we can still check the rest of the arguments. */ + if (arg_idx >= decl_nargs) + { + error_at (DECL_SOURCE_LOCATION (decl), + "callback argument index %d is out of range", arg_idx + 1); + *no_add_attrs = true; + continue; + } + + tree arg_type = get_nth_list_elem (decl_type_args, arg_idx); + tree expected_type = TREE_VALUE (it); + /* Check the type of the value we are about to pass ("arg_type") + for compatibility with the actual type the callback function + expects ("expected_type"). */ + if (!types_compatible_p (expected_type, arg_type)) + { + error_at (DECL_SOURCE_LOCATION (decl), + "argument type at index %d is not compatible with callback " + "argument type at index %d", + arg_idx + 1, curr + 1); + *no_add_attrs = true; + continue; + } + } + + /* Check that the decl does not already have a callback attribute describing + the same argument. */ + it = lookup_attribute ("callback_only", DECL_ATTRIBUTES (decl)); + for (; it; it = lookup_attribute ("callback_only", TREE_CHAIN (it))) + if (callback_get_fn_index (it) == callback_fn_idx) + { + error_at (DECL_SOURCE_LOCATION (decl), + "function declaration has multiple callback attributes " + "describing argument no. %d", + callback_fn_idx + 1); + *no_add_attrs = true; + break; + } + + return NULL_TREE; +} + /* Ignore the given attribute. Used when this attribute may be usefully overridden by the target, but is not used generically. */ diff --git a/gcc/cgraph.cc b/gcc/cgraph.cc index 4b22cad9f116..da770ccc4af7 100644 --- a/gcc/cgraph.cc +++ b/gcc/cgraph.cc @@ -1844,7 +1844,7 @@ cgraph_edge::redirect_call_stmt_to_callee (cgraph_edge *e, { cgraph_edge *carrying = e->get_callback_carrying_edge (); if (!callback_is_special_cased (carrying->callee->decl, e->call_stmt) - && !lookup_attribute (CALLBACK_ATTR_IDENT, + && !lookup_attribute ("callback_only", DECL_ATTRIBUTES (carrying->callee->decl))) /* Callback attribute is removed if the dispatching function changes signature, as the indices wouldn't be correct anymore. These edges @@ -4420,10 +4420,11 @@ cgraph_node::verify_node (void) { int ncallbacks = 0; int nfound_edges = 0; - for (tree cb = lookup_attribute (CALLBACK_ATTR_IDENT, DECL_ATTRIBUTES ( - e->callee->decl)); - cb; cb = lookup_attribute (CALLBACK_ATTR_IDENT, TREE_CHAIN (cb)), - ncallbacks++) + for (tree cb + = lookup_attribute ("callback_only", + DECL_ATTRIBUTES (e->callee->decl)); + cb; cb = lookup_attribute ("callback_only", TREE_CHAIN (cb)), + ncallbacks++) ; for (cgraph_edge *cbe = callees; cbe; cbe = cbe->next_callee) { diff --git a/gcc/doc/extend.texi b/gcc/doc/extend.texi index 9a3f158792d7..225f75e7217e 100644 --- a/gcc/doc/extend.texi +++ b/gcc/doc/extend.texi @@ -2354,6 +2354,45 @@ Note that the BTF format currently only has a representation for type tags associated with pointer types. Type tags on non-pointer types may be silently skipped when generating BTF. +@atindex @code{callback_only} +@cindex functions with callbacks +@item callback_only +The @code{callback_only} attribute specifies that the annotated function may +call the specified callback function. The first parameter identifies the index +of the callback function, the rest of the arguments specify the indices of the +arguments of the indirect call. All indices start from 1. If the function takes +the implicit @code{this} pointer, it is referred to by the index 1, with the +rest of the arguments starting at index 2. The index 0 marks an argument not +present in the arguments of the annotated function, an argument which is +modified before calling the callback function. The annotated function must pass +the specified arguments in the specified order to the callback function, which +must be callable with the number, order and type of the arguments. The +specified pointer to the callback may not escape the translation unit of the +annotated function and it may not be captured. The annotated function is +required to pass the arguments through, it may not change or dereference them. +The arguments also may not escape. The attribute may be used multiple times per +function, though only one @code{callback_only} attribute may be used per +function parameter. + +The attribute exposes the potentially hidden callsite in the annotated +function, enabling interprocedural optimizations which may not be possible +without the attribute. It is most useful for annotating functions from +dynamically linked libraries, as their bodies are not available during +compilation. + +This attribute is similar to the clang @code{callback} attribute but it is not +compatible with it. The clang implementation allows identifiers as arguments, +marks an unknown argument with -1 and the @code{this} pointer with the index 0. + +An example usage: + +@smallexample +[[gnu::callback_only(1, 3, 2)]] +void foo(void (*bar)(int*, double*), double* y, int* x); +@end smallexample + +means that there is a call @code{bar (x, y)} inside @code{foo}. + @atindex @code{cleanup} @cindex cleanup functions @item cleanup (@var{cleanup_function}) diff --git a/gcc/fortran/f95-lang.cc b/gcc/fortran/f95-lang.cc index b796bcf142d7..bfc2651c0767 100644 --- a/gcc/fortran/f95-lang.cc +++ b/gcc/fortran/f95-lang.cc @@ -582,7 +582,7 @@ gfc_builtin_function (tree decl) #define ATTR_COLD_NORETURN_NOTHROW_LEAF_LIST \ (ECF_COLD | ECF_NORETURN | \ ECF_NOTHROW | ECF_LEAF) -#define ATTR_CALLBACK_GOMP_LIST (ECF_CB_1_2 | ATTR_NOTHROW_LIST) +#define ATTR_CALLBACK_ONLY_GOMP_LIST (ECF_CB_1_2 | ATTR_NOTHROW_LIST) #define ATTR_PURE_NOTHROW_LIST (ECF_PURE | ECF_NOTHROW) static void diff --git a/gcc/ipa-cp.cc b/gcc/ipa-cp.cc index 431da4989215..d2e3254643b6 100644 --- a/gcc/ipa-cp.cc +++ b/gcc/ipa-cp.cc @@ -6477,7 +6477,7 @@ purge_useless_callback_edges () if (dump_file) fprintf (dump_file, "\tExamining callbacks of edge %s -> %s:\n", e->caller->dump_name (), e->callee->dump_name ()); - if (!lookup_attribute (CALLBACK_ATTR_IDENT, + if (!lookup_attribute ("callback_only", DECL_ATTRIBUTES (e->callee->decl)) && !callback_is_special_cased (e->callee->decl, e->call_stmt)) { diff --git a/gcc/ipa-param-manipulation.cc b/gcc/ipa-param-manipulation.cc index 6f85d10a29e1..0f384848ce55 100644 --- a/gcc/ipa-param-manipulation.cc +++ b/gcc/ipa-param-manipulation.cc @@ -314,7 +314,7 @@ drop_type_attribute_if_params_changed_p (tree name) static bool drop_decl_attribute_if_params_changed_p (tree name) { - if (is_attribute_p (CALLBACK_ATTR_IDENT, name)) + if (is_attribute_p ("callback_only", name)) return true; return false; } diff --git a/gcc/ipa-prop.cc b/gcc/ipa-prop.cc index b0b078b9cd21..e66c84ebcdf4 100644 --- a/gcc/ipa-prop.cc +++ b/gcc/ipa-prop.cc @@ -2595,11 +2595,11 @@ ipa_compute_jump_functions_for_edge (struct ipa_func_body_info *fbi, /* Argument is a pointer to a function. Look for a callback attribute describing this argument. */ tree callback_attr - = lookup_attribute (CALLBACK_ATTR_IDENT, + = lookup_attribute ("callback_only", DECL_ATTRIBUTES (cs->callee->decl)); for (; callback_attr; callback_attr - = lookup_attribute (CALLBACK_ATTR_IDENT, + = lookup_attribute ("callback_only", TREE_CHAIN (callback_attr))) if (callback_get_fn_index (callback_attr) == n) break; diff --git a/gcc/omp-builtins.def b/gcc/omp-builtins.def index 500cc32ab4d4..192ff414e917 100644 --- a/gcc/omp-builtins.def +++ b/gcc/omp-builtins.def @@ -368,35 +368,35 @@ DEF_GOMP_BUILTIN (BUILT_IN_GOMP_LOOP_ULL_ORDERED_RUNTIME_NEXT, DEF_GOMP_BUILTIN (BUILT_IN_GOMP_PARALLEL_LOOP_STATIC, "GOMP_parallel_loop_static", BT_FN_VOID_OMPFN_PTR_UINT_LONG_LONG_LONG_LONG_UINT, - ATTR_CALLBACK_GOMP_LIST) + ATTR_CALLBACK_ONLY_GOMP_LIST) DEF_GOMP_BUILTIN (BUILT_IN_GOMP_PARALLEL_LOOP_DYNAMIC, "GOMP_parallel_loop_dynamic", BT_FN_VOID_OMPFN_PTR_UINT_LONG_LONG_LONG_LONG_UINT, - ATTR_CALLBACK_GOMP_LIST) + ATTR_CALLBACK_ONLY_GOMP_LIST) DEF_GOMP_BUILTIN (BUILT_IN_GOMP_PARALLEL_LOOP_GUIDED, "GOMP_parallel_loop_guided", BT_FN_VOID_OMPFN_PTR_UINT_LONG_LONG_LONG_LONG_UINT, - ATTR_CALLBACK_GOMP_LIST) + ATTR_CALLBACK_ONLY_GOMP_LIST) DEF_GOMP_BUILTIN (BUILT_IN_GOMP_PARALLEL_LOOP_RUNTIME, "GOMP_parallel_loop_runtime", BT_FN_VOID_OMPFN_PTR_UINT_LONG_LONG_LONG_UINT, - ATTR_CALLBACK_GOMP_LIST) + ATTR_CALLBACK_ONLY_GOMP_LIST) DEF_GOMP_BUILTIN (BUILT_IN_GOMP_PARALLEL_LOOP_NONMONOTONIC_DYNAMIC, "GOMP_parallel_loop_nonmonotonic_dynamic", BT_FN_VOID_OMPFN_PTR_UINT_LONG_LONG_LONG_LONG_UINT, - ATTR_CALLBACK_GOMP_LIST) + ATTR_CALLBACK_ONLY_GOMP_LIST) DEF_GOMP_BUILTIN (BUILT_IN_GOMP_PARALLEL_LOOP_NONMONOTONIC_GUIDED, "GOMP_parallel_loop_nonmonotonic_guided", BT_FN_VOID_OMPFN_PTR_UINT_LONG_LONG_LONG_LONG_UINT, - ATTR_CALLBACK_GOMP_LIST) + ATTR_CALLBACK_ONLY_GOMP_LIST) DEF_GOMP_BUILTIN (BUILT_IN_GOMP_PARALLEL_LOOP_NONMONOTONIC_RUNTIME, "GOMP_parallel_loop_nonmonotonic_runtime", BT_FN_VOID_OMPFN_PTR_UINT_LONG_LONG_LONG_UINT, - ATTR_CALLBACK_GOMP_LIST) + ATTR_CALLBACK_ONLY_GOMP_LIST) DEF_GOMP_BUILTIN (BUILT_IN_GOMP_PARALLEL_LOOP_MAYBE_NONMONOTONIC_RUNTIME, "GOMP_parallel_loop_maybe_nonmonotonic_runtime", BT_FN_VOID_OMPFN_PTR_UINT_LONG_LONG_LONG_UINT, - ATTR_CALLBACK_GOMP_LIST) + ATTR_CALLBACK_ONLY_GOMP_LIST) DEF_GOMP_BUILTIN (BUILT_IN_GOMP_LOOP_END, "GOMP_loop_end", BT_FN_VOID, ATTR_NOTHROW_LEAF_LIST) DEF_GOMP_BUILTIN (BUILT_IN_GOMP_LOOP_END_CANCEL, "GOMP_loop_end_cancel", @@ -419,10 +419,10 @@ DEF_GOMP_BUILTIN (BUILT_IN_GOMP_INTEROP, "GOMP_interop", BT_FN_VOID_INT_INT_PTR_PTR_PTR_INT_PTR_INT_PTR_UINT_PTR, ATTR_NOTHROW_LIST) DEF_GOMP_BUILTIN (BUILT_IN_GOMP_PARALLEL, "GOMP_parallel", - BT_FN_VOID_OMPFN_PTR_UINT_UINT, ATTR_CALLBACK_GOMP_LIST) + BT_FN_VOID_OMPFN_PTR_UINT_UINT, ATTR_CALLBACK_ONLY_GOMP_LIST) DEF_GOMP_BUILTIN (BUILT_IN_GOMP_PARALLEL_REDUCTIONS, "GOMP_parallel_reductions", - BT_FN_UINT_OMPFN_PTR_UINT_UINT, ATTR_CALLBACK_GOMP_LIST) + BT_FN_UINT_OMPFN_PTR_UINT_UINT, ATTR_CALLBACK_ONLY_GOMP_LIST) DEF_GOMP_BUILTIN (BUILT_IN_GOMP_TASK, "GOMP_task", BT_FN_VOID_OMPFN_PTR_OMPCPYFN_LONG_LONG_BOOL_UINT_PTR_INT_PTR, ATTR_NOTHROW_LIST) @@ -440,7 +440,8 @@ DEF_GOMP_BUILTIN (BUILT_IN_GOMP_SECTIONS_NEXT, "GOMP_sections_next", BT_FN_UINT, ATTR_NOTHROW_LEAF_LIST) DEF_GOMP_BUILTIN (BUILT_IN_GOMP_PARALLEL_SECTIONS, "GOMP_parallel_sections", - BT_FN_VOID_OMPFN_PTR_UINT_UINT_UINT, ATTR_CALLBACK_GOMP_LIST) + BT_FN_VOID_OMPFN_PTR_UINT_UINT_UINT, + ATTR_CALLBACK_ONLY_GOMP_LIST) DEF_GOMP_BUILTIN (BUILT_IN_GOMP_SECTIONS_END, "GOMP_sections_end", BT_FN_VOID, ATTR_NOTHROW_LEAF_LIST) DEF_GOMP_BUILTIN (BUILT_IN_GOMP_SECTIONS_END_CANCEL, @@ -490,7 +491,8 @@ DEF_GOMP_BUILTIN (BUILT_IN_GOMP_TARGET_MAP_INDIRECT_PTR, DEF_GOMP_BUILTIN (BUILT_IN_GOMP_TEAMS4, "GOMP_teams4", BT_FN_BOOL_UINT_UINT_UINT_BOOL, ATTR_NOTHROW_LIST) DEF_GOMP_BUILTIN (BUILT_IN_GOMP_TEAMS_REG, "GOMP_teams_reg", - BT_FN_VOID_OMPFN_PTR_UINT_UINT_UINT, ATTR_CALLBACK_GOMP_LIST) + BT_FN_VOID_OMPFN_PTR_UINT_UINT_UINT, + ATTR_CALLBACK_ONLY_GOMP_LIST) DEF_GOMP_BUILTIN (BUILT_IN_GOMP_TASKGROUP_REDUCTION_REGISTER, "GOMP_taskgroup_reduction_register", BT_FN_VOID_PTR, ATTR_NOTHROW_LEAF_LIST) diff --git a/gcc/testsuite/gcc.dg/attr-callback.c b/gcc/testsuite/gcc.dg/attr-callback.c new file mode 100755 index 000000000000..d9cc67c3affe --- /dev/null +++ b/gcc/testsuite/gcc.dg/attr-callback.c @@ -0,0 +1,107 @@ +/* Test callback attribute error checking. */ + +/* { dg-do compile } */ +/* { dg-options "-std=gnu17 -Wattributes" } */ + +[[gnu::callback_only(1, 2)]] +void +correct_1(void (*)(int*), int*); + +[[gnu::callback_only(1, 2, 3)]] +void +correct_2(void (*)(int*, double*), int*, double*); + +[[gnu::callback_only(1, 2, 3), gnu::callback_only(4, 5)]] +void +correct_3(void (*)(int*, double*), int*, double*, int (*)(void*), void*); + +[[gnu::callback_only(1, 0)]] +void +unknown_1(void (*)(int*)); + +[[gnu::callback_only(1, 2, 0)]] +void +unknown_2(void (*)(int*, double*), int*, double*, char*); + +[[gnu::callback_only(1, 0, 3, 3)]] +void +too_many(void (*)(int*, double*), int*, double*); /* { dg-error "argument number mismatch, 2 expected, got 3" }*/ + +[[gnu::callback_only(1, 2)]] +void +too_few_1(void (*)(int*, double*), int*, double*); /* { dg-error "argument number mismatch, 2 expected, got 1" }*/ + +[[gnu::callback_only(1)]] +void +too_few_2(void (*)(int*, double*), int*, double*); /* { dg-error "argument number mismatch, 2 expected, got 0" }*/ + +[[gnu::callback_only(3, 1)]] +void +promotion(char*, float, int (*)(int*)); + +[[gnu::callback_only(2, 3)]] +void +downcast(char*, void* (*)(float*), double*); + +[[gnu::callback_only(1, 2, 5)]] +void +out_of_range_1(char (*)(float*, double*), float*, double*, int*); /* { dg-error "callback argument index 5 is out of range" } */ + +[[gnu::callback_only(1, -2, 3)]] +void +out_of_range_2(char (*)(float*, double*), float*, double*, int*); /* { dg-error "callback argument index -2 is out of range" } */ + +[[gnu::callback_only(-1, 2, 3)]] +void +out_of_range_3(char (*)(float*, double*), float*, double*, int*); /* { dg-error "callback function index -1 is out of range" } */ + +[[gnu::callback_only(67, 2, 3)]] +void +out_of_range_4(char (*)(float*, double*), float*, double*, int*); /* { dg-error "callback function index 67 is out of range" } */ + +[[gnu::callback_only(0, 2, 3)]] +void +unknown_fn(char (*)(float*, double*), float*, double*, int*); /* { dg-error "callback function position cannot be marked as unknown" } */ + +[[gnu::callback_only(1, 2)]] +void +not_a_fn(int, int); /* { dg-error "argument no. 1 is not an address of a function" } */ + +struct S +{ + int x; +}; + +[[gnu::callback_only(1, 2)]] +void +incompatible_types_1(void (*)(struct S*), struct S); /* { dg-error "argument type at index 2 is not compatible with callback argument type at index 1" } */ + +[[gnu::callback_only(1, 3, 2)]] +void +incompatible_types_2(void (*)(struct S*, int*), int*, double); /* { dg-error "argument type at index 3 is not compatible with callback argument type at index 1" } */ + +[[gnu::callback_only(1, "2")]] +void +wrong_arg_type_1(void (*)(void*), void*); /* { dg-error "argument no. 1 is not an integer constant" } */ + +[[gnu::callback_only("not a number", 2, 2)]] +void +wrong_arg_type_2(void (*)(void*, void*), void*); /* { dg-error "argument specifying callback function position is not an integer constant" } */ + +[[gnu::callback_only(1, 2), gnu::callback_only(1, 3)]] +void +multiple_single_fn(void (*)(int*), int*, int*); /* { dg-error "function declaration has multiple callback attributes describing argument no. 1" } */ + +/* Check that the attribute won't resolve outside of our namespace. */ + +[[callback(1, 2)]] /* { dg-warning "ignored" } */ +void +ignore_1(void (*)(int*), int*); + +[[gnu::callback(1, 2)]] +void +ignore_2(void (*)(int*), int*); /* { dg-warning "ignored" } */ + +[[clang::callback_only(1, 2)]] +void +ignore_3(void (*)(int*), int*); /* { dg-warning "ignored" } */ diff --git a/gcc/testsuite/gcc.dg/ipa/ipcp-cb2.c b/gcc/testsuite/gcc.dg/ipa/ipcp-cb2.c new file mode 100644 index 000000000000..201d41d48f3c --- /dev/null +++ b/gcc/testsuite/gcc.dg/ipa/ipcp-cb2.c @@ -0,0 +1,60 @@ +/* Test that we can handle multiple callback attributes and use them to + propagate into callbacks. 'cb1' body borrowed from a ipa-cp test to get the + pass to work. */ + +/* { dg-do compile } */ +/* { dg-options "-O3 -fdump-ipa-cp" } */ + +struct S +{ + int a, b, c; +}; + +extern void *blah (int, void *); + +[[gnu::callback_only (1, 2), gnu::callback_only (3, 4, 5)]] extern void +call (void (*fn1) (struct S *), struct S *a, + void (*fn2) (struct S *, struct S *), struct S *b, struct S *c); + +void +cb1 (struct S *p) +{ + int i, c = p->c; + int b = p->b; + void *v = (void *) p; + + for (i = 0; i < c; i++) + v = blah (b + i, v); +} + +void +cb2 (struct S *a, struct S *b) +{ + cb1 (a); + cb1 (b); +} + +void +test (int a, int b, int c) +{ + struct S s; + s.a = a; + s.b = b; + s.c = c; + struct S ss; + ss.a = s.c; + ss.b = s.b; + ss.c = s.a; + call (cb1, &s, cb2, &s, &ss); +} + +int +main () +{ + test (1, 64, 32); + return 0; +} + +/* { dg-final { scan-ipa-dump "Creating a specialized node of cb1" "cp" } } */ +/* { dg-final { scan-ipa-dump "Creating a specialized node of cb2" "cp" } } */ +/* { dg-final { scan-ipa-dump-times "Aggregate replacements: " 2 "cp" } } */ diff --git a/gcc/tree-core.h b/gcc/tree-core.h index 3ac0d56fc990..1687b1f94672 100644 --- a/gcc/tree-core.h +++ b/gcc/tree-core.h @@ -103,7 +103,7 @@ class irange; meant to be used for the construction of builtin functions. They were only added because Fortran uses them for attributes of builtins. */ -/* callback(1, 2) */ +/* callback_only (1, 2) */ #define ECF_CB_1_2 (1 << 17) /* Call argument flags. */