On Sun, Apr 27, 2025 at 11:56:21AM +0200, Josef Melcr wrote: > * builtin-attrs.def (0): New int list.
This is just weird. Write * builtin-attrs.def: Add DEF_LIST_INT_INT (0,2). ? > +DEF_CALLBACK_ATTRIBUTE(GOMP, 1, 0) > +DEF_CALLBACK_ATTRIBUTE(GOMP, 1, 2) > +DEF_CALLBACK_ATTRIBUTE(OACC, 2, 4) > +DEF_CALLBACK_ATTRIBUTE(GOMP, 3, 0_2) > +DEF_ATTR_TREE_LIST(ATTR_CALLBACK_GOMP_LIST, ATTR_CALLBACK, > + > ATTR_CALLBACK_GOMP_1_2, ATTR_NOTHROW_LIST) > +DEF_ATTR_TREE_LIST(ATTR_CALLBACK_GOMP_TASK_HELPER_LIST, ATTR_CALLBACK, > + > ATTR_CALLBACK_GOMP_1_0, ATTR_NOTHROW_LIST) > +DEF_ATTR_TREE_LIST(ATTR_CALLBACK_GOMP_TASK_LIST, ATTR_CALLBACK, > + > ATTR_CALLBACK_GOMP_3_0_2, ATTR_CALLBACK_GOMP_TASK_HELPER_LIST) > +DEF_ATTR_TREE_LIST(ATTR_CALLBACK_OACC_LIST, ATTR_CALLBACK, > + > ATTR_CALLBACK_OACC_2_4, ATTR_NOTHROW_LIST) Why so many tabs? Can't ATTR_CALLBACK_GOMP_[123]* go below ATTR_CALLBACK_? > @@ -465,6 +466,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", 1, -1, true, false, false, false, > + handle_callback_attribute, NULL}, This line should be indented like for the other attributes. > + /* We want to work with the parent edge whenever possible. When it comes to > + callback edges, a call statement might have multiple callback edges > + attached to it. These can be easily obtained from the parent edge > instead. Dot should be followed by 2 spaces, not one (seen many times in the patch, both in function comments and in documentation). */ should go on the same line as the last comment line. If that would be after the 80 column limit, wrap line before the last word (so "instead. */" on the last line). > + */ > + if (e && e->callback) > + e = e->get_callback_parent_edge (); > + > if (n > 100) > { > call_site_hash = hash_table<cgraph_edge_hasher>::create_ggc (120); > @@ -837,8 +855,31 @@ cgraph_edge::set_call_stmt (cgraph_edge *e, gcall > *new_stmt, > return e_indirect ? indirect : direct; > } > > - if (new_direct_callee) > - e = make_direct (e, new_direct_callee); > + /* Callback edges also need their call stmts changed. > + We can use the same flag as for speculative edges. */ Two spaces instead of one. > --- a/gcc/doc/extend.texi > +++ b/gcc/doc/extend.texi > @@ -1970,6 +1970,43 @@ declares that @code{my_alloc1} returns 16-byte aligned > pointers and > that @code{my_alloc2} returns a pointer whose value modulo 32 is equal > to 8. > > +@cindex @code{callback} function attribute > +@item callback (@var{function-pos}, @var{...}) > +The @code{callback} attribute specifies that a function takes a pointer to > +a callback function as one of it's parameters and passes it some of it's own > +parameters. For example: Most importantly, I wonder if it is a good idea to use same attribute names as clang when it behaves differently. Yes, it seems the clang folks screwed up the design by having it behave differently from other attributes (nonnull, format, ...), by allowing also argument names instead of numbers, haven't verified but from reading their docs it seems they actually count arguments differently in methods (nonnull, format, ... count this as 1 and user specified is 2+, while my understanding from the docs is that 1+ are the user specified arguments and 0 is this). Sure, for [[gnu::callback (...)]]] we could do whatever we want, but __attribute__((callback(...))) is the problematic one. So, I wonder if either we should implement what clang does (sure, the argument names are a little bit awkward, we'd need special case for the arguments of this attribute where it would parse an identifier or number, and later on we'd remap the identifiers to numbers. Not sure how exactly it works though, because e.g. for nonnull attribute in both compilers one can specify the numbers not just as literals, but also constant expressions: constexpr int a = 2; __attribute__((nonnull (a, a + 2))) void foo (int, void *, void *, void *); void bar (void) { foo (0, nullptr, nullptr, nullptr); } or use different name of the attribute. Another question is about GOMP_task. Can you handle any constant propagation into that? I see you've tried to deal with it by using 2 callback attributes but with using 0 for the argument. Wouldn't it be better to just special case BUILT_IN_GOMP_TASK in IPA? Consider: void bar (int, int *); void foo (int a) { a = 42; #pragma omp task bar (a, 0); } void baz (int n) { int a[n], b; __builtin_memset (a, 0, sizeof (a)); b = 42; #pragma omp task bar (b, a); } In the foo case, it is simple GOMP_task (foo._omp_fn.0, &.omp_data_o.1, 0B, 4, 4, 1, 0, 0B, 0, 0B); call, which acts as callback (1, 2) (though note that in the GOMP_task case the argument size and argument alignment are passed in (that is the 4, 4), so if e.g. some IPA modification would not just propagate constants, but also change the layout of that, that would need to be modified). In the second case it is more complex. There is GOMP_task (baz._omp_fn.0, &.omp_data_o.7, baz._omp_cpyfn.1, D.3044, 8, 1, 0, 0B, 0, 0B); and the behavior from what IPA should care about is that there is void *data = __builtin_alloca_with_align (D.3044, 8); baz._omp_cpyfn.1 (data, &.omp_data_o.7); baz._omp_fn.0 (data); The reason for this is that the memory allocated is done somewhere where it can persist until the task can be dispatched and either there are needed constructors or some other operations (like here actually turning a pointer to VLA into copying the content of the VLA as data). So, in order to constant propagate the 42 one needs to modify not just one place, but 2. baz has: b = 42; ... .omp_data_o.7.b = b; and baz._omp_cpyfn.1 has: _6 = .omp_data_i->b; .omp_data_o.10_5->b = _6; and baz._omp_fn.0 has: b = .omp_data_i->b; So, best would be to change _6 = .omp_data_i->b; to _6 = 42; and b = .omp_data_i->b; to b = 42; as well. Also, it would be nice if IPA could handle IFN_ASSUME calls like if they had the callback attribute. Internal calls obviously can't have attributes, but otherwise it should work similarly. > + 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)))); Please avoid calls with ( at the end of line if possible. tree two = build_int_cst (integer_type_node, 2); tree args = tree_cons (NULL_TREE, build_int_cst (integer_type_node, 3), tree_cons (NULL_TREE, integer_zero_node, build_tree_list (NULL_TREE, two))); Jakub