https://gcc.gnu.org/g:660de7e0a3cd8f1ddf6719d3cfe0a6b452557f9c
commit 660de7e0a3cd8f1ddf6719d3cfe0a6b452557f9c Author: Josef Melcr <melcr...@fit.cvut.cz> Date: Sun Mar 30 19:37:22 2025 +0200 omp-cp: multiple callbacks in progress gcc/ChangeLog: * attr-callback.h (callback_edge_useful_p): New function. * cgraph.cc (cgraph_edge::purge_callback_children): New function. (cgraph_edge::redirect_call_stmt_to_callee): Add redirection skipping for callback edges. * cgraph.h: Declarations. * ipa-cp.cc (purge_useless_callback_edges): New function. (ipcp_decision_stage): Integrate the above function. * ipa-param-manipulation.cc (drop_decl_attribute_if_params_changed_p): New predicate. (ipa_param_adjustments::build_new_function_type): Add output parameter. (ipa_param_adjustments::adjust_decl): Add attribute dropping. * ipa-param-manipulation.h: Change signature. * tree-core.h (ECF_CB_1_0): New constant for callback attribute. (ECF_CB_1_2): Likewise. (ECF_CB_2_4): Likewise. (ECF_CB_3_0_2): Likewise. * tree.cc (set_call_expr_flags): Integrate the above flags. gcc/fortran/ChangeLog: * f95-lang.cc (ATTR_CALLBACK_GOMP_LIST): Add constant for list, see builtin-attrs.def. (ATTR_CALLBACK_GOMP_TASK_LIST): Likewise. (ATTR_CALLBACK_OACC_LIST): Likewise. Signed-off-by: Josef Melcr <melcr...@fit.cvut.cz> Diff: --- gcc/attr-callback.h | 11 ++++++++++ gcc/cgraph.cc | 20 ++++++++++++++++++ gcc/cgraph.h | 3 +++ gcc/fortran/f95-lang.cc | 4 ++++ gcc/ipa-cp.cc | 47 ++++++++++++++++++++++++++++++++++++++++++- gcc/ipa-param-manipulation.cc | 28 ++++++++++++++++++++++++-- gcc/ipa-param-manipulation.h | 2 +- gcc/tree-core.h | 14 +++++++++++++ gcc/tree.cc | 43 +++++++++++++++++++++++++++++++++++++++ 9 files changed, 168 insertions(+), 4 deletions(-) diff --git a/gcc/attr-callback.h b/gcc/attr-callback.h index ea9c9cbbc780..b6a1a0c4106c 100644 --- a/gcc/attr-callback.h +++ b/gcc/attr-callback.h @@ -270,4 +270,15 @@ handle_callback_attribute (tree *node, tree name, tree args, return NULL_TREE; } +inline bool +callback_edge_useful_p (cgraph_edge *e) +{ + gcc_checking_assert (e->callback); + /* If the edge is not pointing towards a clone, it is no longer useful as its + entire purpose is to produce clones of callbacks. */ + if (!e->callee->clone_of) + return false; + return true; +} + #endif /* ATTR_CALLBACK_H */ diff --git a/gcc/cgraph.cc b/gcc/cgraph.cc index e032cc0b4864..3bcfe1f2da12 100644 --- a/gcc/cgraph.cc +++ b/gcc/cgraph.cc @@ -1248,6 +1248,19 @@ cgraph_edge::next_callback_target () return e; } +void +cgraph_edge::purge_callback_children () +{ + gcc_checking_assert (has_callback); + cgraph_edge *e, *next; + for (e = first_callback_target (); e; e = next) + { + next = e->next_callback_target (); + cgraph_edge::remove (e); + } + has_callback = false; +} + /* Speculative call consists of an indirect edge and one or more direct edge+ref pairs. @@ -1610,6 +1623,13 @@ cgraph_edge::redirect_call_stmt_to_callee (cgraph_edge *e, redirecting to. */ if (e->callback) { + cgraph_edge *parent = e->get_callback_parent_edge (); + if (!lookup_attribute ("callback", + DECL_ATTRIBUTES (parent->callee->decl))) + /* Callback attribute is removed if the offloading function changes + signature, as the indices would be correct anymore. These edges will + get cleaned up later, ignore their redirection for now. */ + return e->call_stmt; int fn_idx = callback_fetch_fn_position (e->call_stmt, DECL_ATTRIBUTES (decl), e->callee->decl); diff --git a/gcc/cgraph.h b/gcc/cgraph.h index 044639793486..18756cf9f4f8 100644 --- a/gcc/cgraph.h +++ b/gcc/cgraph.h @@ -1753,6 +1753,9 @@ public: /* TODO DOCS */ cgraph_edge *next_callback_target (); + /* TODO DOCS */ + void purge_callback_children (); + /* Speculative call consists of an indirect edge and one or more direct edge+ref pairs. Speculative will expand to the following sequence: diff --git a/gcc/fortran/f95-lang.cc b/gcc/fortran/f95-lang.cc index aed19a9822ee..1c4b4b0de3d8 100644 --- a/gcc/fortran/f95-lang.cc +++ b/gcc/fortran/f95-lang.cc @@ -574,6 +574,10 @@ 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_GOMP_TASK_LIST \ + (ECF_CB_3_0_2 | ECF_CB_1_0 | ATTR_NOTHROW_LIST) +#define ATTR_CALLBACK_OACC_LIST (ECF_CB_2_4 | ATTR_NOTHROW_LIST) static void gfc_define_builtin (const char *name, tree type, enum built_in_function code, diff --git a/gcc/ipa-cp.cc b/gcc/ipa-cp.cc index e6d707c286db..b259e51d8e74 100644 --- a/gcc/ipa-cp.cc +++ b/gcc/ipa-cp.cc @@ -131,7 +131,7 @@ along with GCC; see the file COPYING3. If not see #include "dbgcnt.h" #include "symtab-clones.h" #include "gimple-range.h" - +#include "attr-callback.h" /* Allocation pools for values and their sources in ipa-cp. */ @@ -6208,6 +6208,49 @@ identify_dead_nodes (struct cgraph_node *node) } } +static void +purge_useless_callback_edges () +{ + if (dump_file) + fprintf (dump_file, "\nPurging useless callback edges:\n"); + + cgraph_edge *e; + cgraph_node *node; + FOR_EACH_FUNCTION_WITH_GIMPLE_BODY (node) + { + for (e = node->callees; e; e = e->next_callee) + { + if (e->has_callback) + { + if (dump_file) + fprintf (dump_file, "\tExamining children of edge %s -> %s:\n", + e->caller->name (), e->callee->name ()); + if (!lookup_attribute ("callback", + DECL_ATTRIBUTES (e->callee->decl))) + { + if (dump_file) + fprintf (dump_file, + "\t\tPurging children, because the offloading " + "function no longer has any callback attributes.\n"); + e->purge_callback_children (); + continue; + } + cgraph_edge *cbe, *next; + for (cbe = e->first_callback_target(); cbe; cbe = next) { + next = cbe->next_callback_target(); + if (!callback_edge_useful_p(cbe)) { + if (dump_file) + fprintf(dump_file, "\t\tCallback edge %s -> %s not deemed useful, removing.\n", cbe->caller->name(), cbe->callee->name()); + cgraph_edge::remove(cbe); + } + } + } + } + } + if (dump_file) + fprintf(dump_file, "\n"); +} + /* The decision stage. Iterate over the topological order of call graph nodes TOPO and make specialized clones if deemed beneficial. */ @@ -6238,6 +6281,8 @@ ipcp_decision_stage (class ipa_topo_info *topo) if (change) identify_dead_nodes (node); } + if (0) + purge_useless_callback_edges(); } /* Look up all VR and bits information that we have discovered and copy it diff --git a/gcc/ipa-param-manipulation.cc b/gcc/ipa-param-manipulation.cc index ad36b8389c00..13abedf100a4 100644 --- a/gcc/ipa-param-manipulation.cc +++ b/gcc/ipa-param-manipulation.cc @@ -308,6 +308,14 @@ drop_type_attribute_if_params_changed_p (tree name) return false; } +static bool +drop_decl_attribute_if_params_changed_p (tree name) +{ + if (is_attribute_p ("callback", name)) + return true; + return false; +} + /* Build and return a function type just like ORIG_TYPE but with parameter types given in NEW_PARAM_TYPES - which can be NULL if, but only if, ORIG_TYPE itself has NULL TREE_ARG_TYPEs. If METHOD2FUNC is true, also make @@ -492,7 +500,7 @@ ipa_param_adjustments::method2func_p (tree orig_type) tree ipa_param_adjustments::build_new_function_type (tree old_type, - bool type_original_p) + bool type_original_p, bool *args_modified /* = NULL */) { auto_vec<tree,16> new_param_types, *new_param_types_p; if (prototype_p (old_type)) @@ -518,6 +526,8 @@ ipa_param_adjustments::build_new_function_type (tree old_type, || get_original_index (index) != (int)index) modified = true; + if (args_modified) + *args_modified = modified; return build_adjusted_function_type (old_type, new_param_types_p, method2func_p (old_type), m_skip_return, @@ -536,10 +546,11 @@ ipa_param_adjustments::adjust_decl (tree orig_decl) { tree new_decl = copy_node (orig_decl); tree orig_type = TREE_TYPE (orig_decl); + bool args_modified = false; if (prototype_p (orig_type) || (m_skip_return && !VOID_TYPE_P (TREE_TYPE (orig_type)))) { - tree new_type = build_new_function_type (orig_type, false); + tree new_type = build_new_function_type (orig_type, false, &args_modified); TREE_TYPE (new_decl) = new_type; } if (method2func_p (orig_type)) @@ -556,6 +567,19 @@ ipa_param_adjustments::adjust_decl (tree orig_decl) if (m_skip_return) DECL_IS_MALLOC (new_decl) = 0; + if (args_modified && DECL_ATTRIBUTES (new_decl)) + { + tree t = DECL_ATTRIBUTES (new_decl); + tree *last = &DECL_ATTRIBUTES (new_decl); + DECL_ATTRIBUTES (new_decl) = NULL; + for (; t; t = TREE_CHAIN (t)) + if (!drop_decl_attribute_if_params_changed_p (get_attribute_name (t))) + { + *last = copy_node (t); + TREE_CHAIN (*last) = NULL; + last = &TREE_CHAIN (*last); + } + } return new_decl; } diff --git a/gcc/ipa-param-manipulation.h b/gcc/ipa-param-manipulation.h index 8dd5e5bdeaee..adf4321a10d4 100644 --- a/gcc/ipa-param-manipulation.h +++ b/gcc/ipa-param-manipulation.h @@ -229,7 +229,7 @@ public: /* Return if the first parameter is left intact. */ bool first_param_intact_p (); /* Build a function type corresponding to the modified call. */ - tree build_new_function_type (tree old_type, bool type_is_original_p); + tree build_new_function_type (tree old_type, bool type_is_original_p, bool *args_modified = NULL); /* Build a declaration corresponding to the target of the modified call. */ tree adjust_decl (tree orig_decl); /* Fill a vector marking which parameters are intact by the described diff --git a/gcc/tree-core.h b/gcc/tree-core.h index 1776c154d94e..7d9c2154415d 100644 --- a/gcc/tree-core.h +++ b/gcc/tree-core.h @@ -98,6 +98,20 @@ struct die_struct; /* Nonzero if this is a function expected to end with an exception. */ #define ECF_XTHROW (1 << 16) +/* Flags for various callback attribute combinations. */ + +/* callback(1, 0) */ +#define ECF_CB_1_0 (1 << 17) + +/* callback(1, 2) */ +#define ECF_CB_1_2 (1 << 18) + +/* callback(2, 4) */ +#define ECF_CB_2_4 (1 << 19) + +/* callback(3, 0, 2) */ +#define ECF_CB_3_0_2 (1 << 20) + /* Call argument flags. */ /* Nonzero if the argument is not used by the function. */ diff --git a/gcc/tree.cc b/gcc/tree.cc index 83a03374a325..5d0bf03281a4 100644 --- a/gcc/tree.cc +++ b/gcc/tree.cc @@ -9817,6 +9817,7 @@ build_common_tree_nodes (bool signed_char) void set_call_expr_flags (tree decl, int flags) { + // ahoj if (flags & ECF_NOTHROW) TREE_NOTHROW (decl) = 1; if (flags & ECF_CONST) @@ -9850,6 +9851,48 @@ set_call_expr_flags (tree decl, int flags) DECL_ATTRIBUTES (decl) = tree_cons (get_identifier ("expected_throw"), NULL, DECL_ATTRIBUTES (decl)); + + if (flags & ECF_CB_1_0) + { + tree args + = tree_cons (NULL_TREE, build_int_cst (integer_type_node, 1), + build_tree_list (NULL_TREE, + build_int_cst (integer_type_node, 0))); + DECL_ATTRIBUTES (decl) + = tree_cons (get_identifier ("callback"), args, DECL_ATTRIBUTES (decl)); + } + + if (flags & ECF_CB_1_2) + { + tree args + = tree_cons (NULL_TREE, build_int_cst (integer_type_node, 1), + build_tree_list (NULL_TREE, + build_int_cst (integer_type_node, 2))); + DECL_ATTRIBUTES (decl) + = tree_cons (get_identifier ("callback"), args, DECL_ATTRIBUTES (decl)); + } + + if (flags & ECF_CB_2_4) + { + tree args + = tree_cons (NULL_TREE, build_int_cst (integer_type_node, 2), + build_tree_list (NULL_TREE, + build_int_cst (integer_type_node, 4))); + DECL_ATTRIBUTES (decl) + = tree_cons (get_identifier ("callback"), args, DECL_ATTRIBUTES (decl)); + } + + if (flags & ECF_CB_3_0_2) + { + tree args = tree_cons ( + NULL_TREE, build_int_cst (integer_type_node, 3), + tree_cons (NULL_TREE, build_int_cst (integer_type_node, 0), + build_tree_list (NULL_TREE, + build_int_cst (integer_type_node, 2)))); + DECL_ATTRIBUTES (decl) + = tree_cons (get_identifier ("callback"), args, DECL_ATTRIBUTES (decl)); + } + /* Looping const or pure is implied by noreturn. There is currently no way to declare looping const or looping pure alone. */ gcc_assert (!(flags & ECF_LOOPING_CONST_OR_PURE)