On 8/11/23 09:18, Javier Martinez wrote:
> Hi Jason,
>
> Regarding the initialization example - no, the set of classes that we
> consider cold is more loosely defined.
>
> On Thu, Aug 10, 2023 at 11:01 PM Jason Merrill <jason@redhat.com
> <mailto:jason@redhat.com>> wrote:
> > Yes, but that's because the implicit op== isn't declared lazily like
> > some other special member functions (CLASSTYPE_LAZY_*/lazily_declare_fn)
> > which can happen after the class is complete.
>
> I see, thanks. I have fixed this now by injecting it directly from
> lazily_declare_fn, works well. Doing it from grokclassfn instead seems
> to be a nuisance because the explicit method attribute might be
> processed after the class-propagated attribute is injected, which is the
> wrong way around for the desired precedence.
>
> > I think it would work to check for (flags & (ATTR_FLAG_FUNCTION_NEXT |
> > ATTR_FLAG_DECL_NEXT)) and return without warning in that case. You'd
> > still set *no_add_attr.
>
> Correct, done.
>
> I have added the patch as an attachment, if it garbles it then I will
> use git-send-email next time.
That worked fine, thanks.
> @@ -1110,6 +1110,28 @@ handle_hot_attribute (tree *node, tree name, tree ARG_UNUSED (args),
> {
> /* Attribute hot processing is done later with lookup_attribute. */
> }
> + else if ((TREE_CODE (*node) == RECORD_TYPE
> + || TREE_CODE (*node) == UNION_TYPE)
> + && c_dialect_cxx ())
I think you also want to check for ATTR_FLAG_TYPE_IN_PLACE.
> @@ -7866,6 +7891,10 @@ finish_struct (tree t, tree attributes)
> && !LAMBDA_TYPE_P (t))
> add_stmt (build_min (TAG_DEFN, t));
>
> + /* This must be done after all lazily declared special member functions
> + have been injected. */
> + propagate_class_warmth_attribute (t);
Maybe call this in check_bases_and_members instead?
Jason
From 684ee3b19463fe7f447fbaa96a7b44522f1ce594 Mon Sep 17 00:00:00 2001
From: Javier Martinez <javier.martinez.bugzilla@gmail.com>
Date: Thu, 10 Aug 2023 17:08:27 +0200
Subject: [PATCH v3] c++: extend cold, hot attributes to classes
Signed-off-by: Javier Martinez <javier.martinez.bugzilla@gmail.com>
gcc/c-family/ChangeLog:
* c-attribs.cc (handle_hot_attribute): remove warning on RECORD_TYPE
and UNION_TYPE when in c_dialect_xx.
(handle_cold_attribute): Likewise
gcc/cp/ChangeLog:
* class.cc (propagate_class_warmth_attribute): New function.
(finish_struct): propagate hot and cold attributes to all
FUNCTION_DECL when the record is marked hot or cold.
* cp-tree.h (maybe_propagate_warmth_attributes): New function.
* decl2.cc (maybe_propagate_warmth_attributes): New function.
* method.cc (lazily_declare_fn): propagate hot and cold
attributes to lazily declared functions when the record is
marked hot or cold.
gcc/testsuite/ChangeLog:
* g++.dg/ext/attr-hotness.C: New test.
---
gcc/c-family/c-attribs.cc | 48 +++++++++++++++++++++++--
gcc/cp/class.cc | 29 +++++++++++++++
gcc/cp/cp-tree.h | 1 +
gcc/cp/decl2.cc | 37 +++++++++++++++++++
gcc/cp/method.cc | 6 ++++
gcc/testsuite/g++.dg/ext/attr-hotness.C | 16 +++++++++
6 files changed, 135 insertions(+), 2 deletions(-)
create mode 100644 gcc/testsuite/g++.dg/ext/attr-hotness.C
@@ -452,10 +452,10 @@ const struct attribute_spec c_common_attribute_table[] =
{ "alloc_size", 1, 2, false, true, true, false,
handle_alloc_size_attribute,
attr_alloc_exclusions },
- { "cold", 0, 0, true, false, false, false,
+ { "cold", 0, 0, false, false, false, false,
handle_cold_attribute,
attr_cold_hot_exclusions },
- { "hot", 0, 0, true, false, false, false,
+ { "hot", 0, 0, false, false, false, false,
handle_hot_attribute,
attr_cold_hot_exclusions },
{ "no_address_safety_analysis",
@@ -1110,6 +1110,28 @@ handle_hot_attribute (tree *node, tree name, tree ARG_UNUSED (args),
{
/* Attribute hot processing is done later with lookup_attribute. */
}
+ else if ((TREE_CODE (*node) == RECORD_TYPE
+ || TREE_CODE (*node) == UNION_TYPE)
+ && c_dialect_cxx ())
+ {
+ /* Check conflict here as decl_attributes will otherwise only catch
+ it late at the function when the attribute is used on a class. */
+ tree cold_attr = lookup_attribute ("cold", TYPE_ATTRIBUTES (*node));
+ if (cold_attr)
+ {
+ warning (OPT_Wattributes, "ignoring attribute %qE because it "
+ "conflicts with attribute %qs", name, "cold");
+ *no_add_attrs = true;
+ }
+ }
+ else if (flags & ((int) ATTR_FLAG_FUNCTION_NEXT
+ | (int) ATTR_FLAG_DECL_NEXT))
+ {
+ /* Avoid applying the attribute to a function return type when
+ used as: void __attribute ((hot)) foo (void). It will be
+ passed to the function. */
+ *no_add_attrs = true;
+ }
else
{
warning (OPT_Wattributes, "%qE attribute ignored", name);
@@ -1131,6 +1153,28 @@ handle_cold_attribute (tree *node, tree name, tree ARG_UNUSED (args),
{
/* Attribute cold processing is done later with lookup_attribute. */
}
+ else if ((TREE_CODE (*node) == RECORD_TYPE
+ || TREE_CODE (*node) == UNION_TYPE)
+ && c_dialect_cxx ())
+ {
+ /* Check conflict here as decl_attributes will otherwise only catch
+ it late at the function when the attribute is used on a class. */
+ tree hot_attr = lookup_attribute ("hot", TYPE_ATTRIBUTES (*node));
+ if (hot_attr)
+ {
+ warning (OPT_Wattributes, "ignoring attribute %qE because it "
+ "conflicts with attribute %qs", name, "hot");
+ *no_add_attrs = true;
+ }
+ }
+ else if (flags & ((int) ATTR_FLAG_FUNCTION_NEXT
+ | (int) ATTR_FLAG_DECL_NEXT))
+ {
+ /* Avoid applying the attribute to a function return type when
+ used as: void __attribute ((cold)) foo (void). It will be
+ passed to the function. */
+ *no_add_attrs = true;
+ }
else
{
warning (OPT_Wattributes, "%qE attribute ignored", name);
@@ -205,6 +205,7 @@ static tree get_vcall_index (tree, tree);
static bool type_maybe_constexpr_default_constructor (tree);
static bool type_maybe_constexpr_destructor (tree);
static bool field_poverlapping_p (tree);
+static void propagate_class_warmth_attribute (tree);
/* Set CURRENT_ACCESS_SPECIFIER based on the protection of DECL. */
@@ -7733,6 +7734,30 @@ unreverse_member_declarations (tree t)
}
}
+/* Classes, structs or unions T marked with hotness attributes propagate
+ the attribute to all methods. */
+
+void
+propagate_class_warmth_attribute (tree t)
+{
+
+ if (t == NULL_TREE
+ || !(TREE_CODE (t) == RECORD_TYPE
+ || TREE_CODE (t) == UNION_TYPE))
+ return;
+
+ tree class_has_cold_attr = lookup_attribute ("cold",
+ TYPE_ATTRIBUTES (t));
+ tree class_has_hot_attr = lookup_attribute ("hot",
+ TYPE_ATTRIBUTES (t));
+
+ if (class_has_cold_attr || class_has_hot_attr)
+ for (tree f = TYPE_FIELDS (t); f; f = DECL_CHAIN (f))
+ if (TREE_CODE (f) == FUNCTION_DECL)
+ maybe_propagate_warmth_attributes (f, t);
+
+}
+
tree
finish_struct (tree t, tree attributes)
{
@@ -7866,6 +7891,10 @@ finish_struct (tree t, tree attributes)
&& !LAMBDA_TYPE_P (t))
add_stmt (build_min (TAG_DEFN, t));
+ /* This must be done after all lazily declared special member functions
+ have been injected. */
+ propagate_class_warmth_attribute (t);
+
return t;
}
@@ -7033,6 +7033,7 @@ extern int parm_index (tree);
extern tree vtv_start_verification_constructor_init_function (void);
extern tree vtv_finish_verification_constructor_init_function (tree);
extern void cp_check_const_attributes (tree);
+extern void maybe_propagate_warmth_attributes (tree, tree);
/* in error.cc */
extern const char *type_as_string (tree, int);
@@ -1604,6 +1604,43 @@ cp_check_const_attributes (tree attributes)
}
}
+/* Copies hot or cold attributes to a function FN if present on the
+ encapsulating class, struct, or union TYPE. */
+
+void
+maybe_propagate_warmth_attributes (tree fn, tree type)
+{
+ if (fn == NULL_TREE || type == NULL_TREE
+ || !(TREE_CODE (type) == RECORD_TYPE
+ || TREE_CODE (type) == UNION_TYPE))
+ return;
+
+ tree has_cold_attr = lookup_attribute ("cold", TYPE_ATTRIBUTES (type));
+ tree has_hot_attr = lookup_attribute ("hot", TYPE_ATTRIBUTES (type));
+
+ if (has_cold_attr || has_hot_attr)
+ {
+
+ /* Transparently ignore the new warmth attribute if it
+ conflicts with a present function attribute. Otherwise
+ decl_attribute would still honour the present attribute,
+ but producing an undesired warning in the process. */
+
+ if (has_cold_attr)
+ {
+ if (lookup_attribute ("hot", DECL_ATTRIBUTES (fn)) == NULL)
+ decl_attributes (&fn,
+ tree_cons (get_identifier ("cold"), NULL, NULL), 0);
+ }
+ else if (has_hot_attr)
+ {
+ if (lookup_attribute ("cold", DECL_ATTRIBUTES (fn)) == NULL)
+ decl_attributes (&fn,
+ tree_cons (get_identifier ("hot"), NULL, NULL), 0);
+ }
+ }
+}
+
/* Return the last pushed declaration for the symbol DECL or NULL
when no such declaration exists. */
@@ -3592,6 +3592,12 @@ lazily_declare_fn (special_function_kind sfk, tree type)
/* Create appropriate clones. */
clone_cdtor (fn, /*update_methods=*/true);
+/* Classes, structs or unions TYPE marked with hotness attributes propagate
+ the attribute to all methods. This is typically done in finish_struct,
+ but we must also inject them for deferred lazily_declared functions. */
+
+ maybe_propagate_warmth_attributes (fn, type);
+
return fn;
}
new file mode 100644
@@ -0,0 +1,16 @@
+/* { dg-do compile } */
+/* { dg-options "-O0 -Wattributes -fdump-tree-gimple" } */
+
+
+struct __attribute((cold)) A { __attribute((noinline, used)) void foo(void) { } };
+
+struct __attribute((hot)) B { __attribute((noinline, used)) void foo(void) { } };
+
+struct __attribute((hot, cold)) C { __attribute((noinline, used)) void foo(void) { } }; /* { dg-warning "ignoring attribute .cold. because it conflicts with attribute .hot." } */
+
+struct __attribute((cold, hot)) D { __attribute((noinline, used)) void foo(void) { } }; /* { dg-warning "ignoring attribute .hot. because it conflicts with attribute .cold." } */
+
+
+/* { dg-final { scan-tree-dump-times "cold" 2 "gimple" } } */
+/* { dg-final { scan-tree-dump-times "hot" 2 "gimple" } } */
+
--
2.34.1