Changes from v4:
- Drop `[[gnu::trivial_abi]]` spelling; only `__attribute__((trivial_abi))`
and `[[clang::trivial_abi]]` are accepted. `[[gnu::trivial_abi]]` now
warns and suggests the supported spellings.
- Remove `has_trivial_abi` bitfield from `lang_type`; use
`lookup_attribute` via new `classtype_has_trivial_abi` function instead.
- Remove module serialization of the bitfield and manual propagation in
`instantiate_class_template` (`apply_late_template_attributes` already
handles `TYPE_ATTRIBUTES`).
- Use `remove_attribute` in `validate_trivial_abi_attribute` to strip
the attribute on validation failure.
- Restore original formatting of `cxx_gnu_attributes` table entries.
- Fix `cxx_clang_attributes` table formatting (`{` on next line).
- Update tests to use `[[clang::trivial_abi]]`.
On Wed, Feb 18, 2026 at 11:02 PM Jason Merrill <[email protected]> wrote:
>
> On 2/19/26 4:16 AM, Yuxuan Chen wrote:
> > This is v4 of the patch to implement the trivial_abi attribute for GCC.
> >
> > Changes from v3:
> > - Remove trivial_for_calls_p; use CLASSTYPE_HAS_TRIVIAL_ABI directly
> > in finish_struct_bits and store_parm_decls.
> > - Use TREE_ADDRESSABLE to check bases/fields in
> > validate_trivial_abi_attribute instead of trivial_for_calls_p.
> > - Move validate_trivial_abi_attribute call before finish_struct_bits
> > in finish_struct_1 so the flag is resolved before ABI decisions.
> > - Remove __builtin_is_trivially_relocatable from tests; the trait
> > is not available on trunk after trivial relocation rework.
> > - Replace CONST_CAST_TREE with const_cast<tree> per upstream change.
> > - Use "non-static data member" instead of "field" in diagnostics.
> > - Add [[clang::trivial_abi]] spelling via cxx_clang_attribute_table.
> >
> > The attribute is supported with __attribute__((trivial_abi)),
> > [[gnu::trivial_abi]], and [[clang::trivial_abi]] spellings. One thing
> > I didn't figure out how to address is to stop supporting
> > [[gnu::trivial_abi]] spelling of the attribute as suggested by
> > reviews. If I remove it from cxx_gnu_attributes,
> > `__attribute__((trivial_abi))` stops working.
>
> Aha. You should be able to hack around this by using two different
> handle_ functions, like handle_maybe_unused_attribute vs
> handle_unused_attribute, and checking for ATTR_FLAG_CXX11.
>
> > * cp-tree.h (struct lang_type): Add has_trivial_abi flag.
> I don't think we need a new bit, we can use lookup_attribute (probably
> wrapped in an inline function) instead of the macro.
>
> > /* Table of valid C++ attributes. */
> > -static const attribute_spec cxx_gnu_attributes[] =
> > -{
> > +static const attribute_spec cxx_gnu_attributes[] = {
> > /* { name, min_len, max_len, decl_req, type_req, fn_type_req,
> > affects_type_identity, handler, exclude } */
> > - { "init_priority", 1, 1, true, false, false, false,
> > - handle_init_priority_attribute, NULL },
> > - { "abi_tag", 1, -1, false, false, false, true,
> > - handle_abi_tag_attribute, NULL },
> > - { "no_dangling", 0, 1, false, true, false, false,
> > - handle_no_dangling_attribute, NULL },
> > + {"init_priority", 1, 1, true, false, false, false,
> > + handle_init_priority_attribute, NULL},
> > + {"abi_tag", 1, -1, false, false, false, true, handle_abi_tag_attribute,
> > NULL},
> > + {"no_dangling", 0, 1, false, true, false, false,
> > handle_no_dangling_attribute,
> > + NULL},
> > + {"trivial_abi", 0, 0, false, true, false, true,
> > handle_trivial_abi_attribute,
> > + NULL},
> > };
>
> Let's follow the existing formatting rather than reformat the existing
> lines.
>
> > +/* Table of C++ attributes also recognized in the clang:: namespace. */
> > +static const attribute_spec cxx_clang_attributes[] = {
>
> And this table should have the { on the next line like the other tables.
>
> Jason
>
From 2beaed240a1bdcfaea331a69899738e87f4e9212 Mon Sep 17 00:00:00 2001
From: Yuxuan Chen <[email protected]>
Date: Mon, 9 Mar 2026 12:03:24 -0700
Subject: [PATCH] c++: Add support for [[gnu::trivial_abi]] attribute
[PR107187]
Changes from v4:
- Drop `[[gnu::trivial_abi]]` spelling; only `__attribute__((trivial_abi))`
and `[[clang::trivial_abi]]` are accepted. `[[gnu::trivial_abi]]` now
warns and suggests the supported spellings.
- Remove `has_trivial_abi` bitfield from `lang_type`; use
`lookup_attribute` via new `classtype_has_trivial_abi` function instead.
- Remove module serialization of the bitfield and manual propagation in
`instantiate_class_template` (`apply_late_template_attributes` already
handles `TYPE_ATTRIBUTES`).
- Use `remove_attribute` in `validate_trivial_abi_attribute` to strip
the attribute on validation failure.
- Restore original formatting of `cxx_gnu_attributes` table entries.
- Fix `cxx_clang_attributes` table formatting (`{` on next line).
- Update tests to use `[[clang::trivial_abi]]`.
Changes from v3:
- Remove `trivial_for_calls_p`; use `CLASSTYPE_HAS_TRIVIAL_ABI` directly
in `finish_struct_bits` and `store_parm_decls`.
- Use `TREE_ADDRESSABLE` to check bases/fields in
`validate_trivial_abi_attribute` instead of `trivial_for_calls_p`.
- Move `validate_trivial_abi_attribute` call before `finish_struct_bits`
in `finish_struct_1` so the flag is resolved before ABI decisions.
- Remove `__builtin_is_trivially_relocatable` from tests; the trait
is not available on trunk after trivial relocation rework.
- Replace `CONST_CAST_TREE` with `const_cast<tree>` per upstream change.
- Use "non-static data member" instead of "field" in diagnostics.
- Add `[[clang::trivial_abi]]` spelling via `cxx_clang_attribute_table`.
- Handle merge conflicts post changes to remove trivial relocation.
Changes from v2:
- Handling cases where bases can be dependent.
- gcc/testsuite/g++.dg/cpp0x/attr-trivial_abi_syntax.C now uses the same
test as Clang to ensure no difference in semantics.
Changes from v1:
- Addressing code review comments.
- Registered cleanup in callee for trivial abi. Added test
`gcc/testsuite/g++.dg/abi/invisiref3a.C`
- Added doc in `gcc/doc/extend.texi`. Pretty much the same as in
Clang's attributes documentation.
Implement the trivial_abi attribute for GCC to fix ABI compatibility
issues with Clang. Currently, GCC silently ignores this attribute,
causing call convention mismatches when linking GCC-compiled code with
Clang-compiled object files that use trivial_abi types.
This attribute allows types to be treated as trivial for ABI purposes,
enabling pass in registers instead of invisible references. The
attribute is supported with `__attribute__((trivial_abi))` and
`[[clang::trivial_abi]]` spellings.
PR c++/107187
gcc/cp/ChangeLog:
* cp-tree.h (classtype_has_trivial_abi): New function.
(validate_trivial_abi_attribute): Declare.
(classtype_has_non_deleted_copy_or_move_ctor): Declare.
(cxx_clang_attribute_table): Declare.
* tree.cc (handle_trivial_abi_attribute): New function.
(handle_gnu_trivial_abi_attribute): New function.
(classtype_has_trivial_abi): New function.
(validate_trivial_abi_attribute): New function.
(cxx_gnu_attributes): Add trivial_abi entry.
(cxx_clang_attributes): New table for [[clang::trivial_abi]].
* class.cc (finish_struct_bits): Skip BLKmode for types with
trivial_abi attribute.
(classtype_has_non_deleted_copy_or_move_ctor): New function.
(finish_struct_1): Call validate_trivial_abi_attribute before
finish_struct_bits.
* cp-objcp-common.h (cp_objcp_attribute_table): Register
cxx_clang_attribute_table.
* decl.cc (store_parm_decls): Register cleanups for trivial_abi
parameters.
Signed-off-by: Yuxuan Chen <[email protected]>
---
gcc/cp/class.cc | 25 ++-
gcc/cp/cp-objcp-common.h | 1 +
gcc/cp/cp-tree.h | 5 +
gcc/cp/decl.cc | 17 ++
gcc/cp/tree.cc | 154 ++++++++++++++++++
gcc/doc/extend.texi | 69 ++++++++
gcc/testsuite/g++.dg/abi/invisiref3.C | 28 ++++
gcc/testsuite/g++.dg/abi/invisiref3a.C | 26 +++
.../g++.dg/cpp0x/attr-trivial_abi1.C | 41 +++++
.../g++.dg/cpp0x/attr-trivial_abi2.C | 70 ++++++++
.../g++.dg/cpp0x/attr-trivial_abi3.C | 58 +++++++
.../g++.dg/cpp0x/attr-trivial_abi4.C | 57 +++++++
.../g++.dg/cpp0x/attr-trivial_abi5.C | 60 +++++++
.../g++.dg/cpp0x/attr-trivial_abi6.C | 22 +++
.../g++.dg/cpp0x/attr-trivial_abi_syntax.C | 150 +++++++++++++++++
15 files changed, 781 insertions(+), 2 deletions(-)
create mode 100644 gcc/testsuite/g++.dg/abi/invisiref3.C
create mode 100644 gcc/testsuite/g++.dg/abi/invisiref3a.C
create mode 100644 gcc/testsuite/g++.dg/cpp0x/attr-trivial_abi1.C
create mode 100644 gcc/testsuite/g++.dg/cpp0x/attr-trivial_abi2.C
create mode 100644 gcc/testsuite/g++.dg/cpp0x/attr-trivial_abi3.C
create mode 100644 gcc/testsuite/g++.dg/cpp0x/attr-trivial_abi4.C
create mode 100644 gcc/testsuite/g++.dg/cpp0x/attr-trivial_abi5.C
create mode 100644 gcc/testsuite/g++.dg/cpp0x/attr-trivial_abi6.C
create mode 100644 gcc/testsuite/g++.dg/cpp0x/attr-trivial_abi_syntax.C
diff --git a/gcc/cp/class.cc b/gcc/cp/class.cc
index 8beeb797a08..d1a0688a6b2 100644
--- a/gcc/cp/class.cc
+++ b/gcc/cp/class.cc
@@ -2420,8 +2420,9 @@ finish_struct_bits (tree t)
mode to be BLKmode, and force its TREE_ADDRESSABLE bit to be
nonzero. This will cause it to be passed by invisible reference
and prevent it from being returned in a register. */
- if (type_has_nontrivial_copy_init (t)
- || TYPE_HAS_NONTRIVIAL_DESTRUCTOR (t))
+ if (!(CLASS_TYPE_P (t) && classtype_has_trivial_abi (t))
+ && (type_has_nontrivial_copy_init (t)
+ || TYPE_HAS_NONTRIVIAL_DESTRUCTOR (t)))
{
SET_DECL_MODE (TYPE_MAIN_DECL (t), BLKmode);
SET_TYPE_MODE (t, BLKmode);
@@ -6086,6 +6087,24 @@ classtype_has_non_deleted_move_ctor (tree t)
return false;
}
+/* True iff T has a copy or move constructor that is not deleted. */
+
+bool
+classtype_has_non_deleted_copy_or_move_ctor (tree t)
+{
+ if (CLASSTYPE_LAZY_COPY_CTOR (t))
+ lazily_declare_fn (sfk_copy_constructor, t);
+ if (CLASSTYPE_LAZY_MOVE_CTOR (t))
+ lazily_declare_fn (sfk_move_constructor, t);
+ for (ovl_iterator iter (CLASSTYPE_CONSTRUCTORS (t)); iter; ++iter)
+ {
+ tree fn = *iter;
+ if ((copy_fn_p (fn) || move_fn_p (fn)) && !DECL_DELETED_FN (fn))
+ return true;
+ }
+ return false;
+}
+
/* If T, a class, has a user-provided copy constructor, copy assignment
operator, or destructor, returns that function. Otherwise, null. */
@@ -8085,6 +8104,8 @@ finish_struct_1 (tree t)
}
}
+ validate_trivial_abi_attribute (t);
+
finish_struct_bits (t);
set_method_tm_attributes (t);
diff --git a/gcc/cp/cp-objcp-common.h b/gcc/cp/cp-objcp-common.h
index 2999b36a5dd..ed29e65e4f3 100644
--- a/gcc/cp/cp-objcp-common.h
+++ b/gcc/cp/cp-objcp-common.h
@@ -127,6 +127,7 @@ static const scoped_attribute_specs *const cp_objcp_attribute_table[] =
{
&std_attribute_table,
&cxx_gnu_attribute_table,
+ &cxx_clang_attribute_table,
&c_common_gnu_attribute_table,
&c_common_clang_attribute_table,
&c_common_format_attribute_table,
diff --git a/gcc/cp/cp-tree.h b/gcc/cp/cp-tree.h
index 7f31aca47b0..99e3e98aa58 100644
--- a/gcc/cp/cp-tree.h
+++ b/gcc/cp/cp-tree.h
@@ -3003,6 +3003,7 @@ struct GTY(()) lang_type {
above (c++/120012). This could also be a hash_set. */
#define CLASSTYPE_NON_AGGREGATE_POD(NODE) \
(LANG_TYPE_CLASS_CHECK (NODE)->non_aggregate_pod)
+
/* Additional macros for inheritance information. */
@@ -7414,6 +7415,7 @@ extern bool type_has_virtual_destructor (tree);
extern bool type_has_non_deleted_trivial_default_ctor (tree);
extern bool classtype_has_move_assign_or_move_ctor_p (tree, bool user_declared);
extern bool classtype_has_non_deleted_move_ctor (tree);
+extern bool classtype_has_non_deleted_copy_or_move_ctor (tree);
extern tree classtype_has_depr_implicit_copy (tree);
extern bool classtype_has_op (tree, tree_code);
extern tree classtype_has_defaulted_op (tree, tree_code);
@@ -8628,6 +8630,8 @@ extern bool std_layout_type_p (const_tree);
extern bool trivial_type_p (const_tree);
extern bool implicit_lifetime_type_p (tree);
extern bool trivially_copyable_p (const_tree);
+extern bool classtype_has_trivial_abi (tree);
+extern void validate_trivial_abi_attribute (tree);
extern bool type_has_unique_obj_representations (const_tree, bool = false);
extern bool scalarish_type_p (const_tree);
extern bool structural_type_p (tree, bool = false);
@@ -8735,6 +8739,7 @@ extern bool is_dummy_object (const_tree);
extern bool is_byte_access_type (tree);
extern bool is_byte_access_type_not_plain_char (tree);
extern const struct scoped_attribute_specs cxx_gnu_attribute_table;
+extern const struct scoped_attribute_specs cxx_clang_attribute_table;
extern const struct scoped_attribute_specs std_attribute_table;
extern const struct scoped_attribute_specs internal_attribute_table;
extern tree make_ptrmem_cst (tree, tree);
diff --git a/gcc/cp/decl.cc b/gcc/cp/decl.cc
index 40e71bfb30a..c05d292986d 100644
--- a/gcc/cp/decl.cc
+++ b/gcc/cp/decl.cc
@@ -20186,6 +20186,23 @@ store_parm_decls (tree current_function_parms)
DECL_ARGUMENTS is not modified. */
current_binding_level->names = chainon (nonparms, DECL_ARGUMENTS (fndecl));
+ /* Register cleanups for parameters with trivial_abi attribute, the cleanup of
+ * which is the callee's responsibility. */
+ for (tree parm = DECL_ARGUMENTS (fndecl); parm; parm = DECL_CHAIN (parm))
+ {
+ if (TREE_CODE (parm) == PARM_DECL)
+ {
+ tree parm_type = TREE_TYPE (parm);
+ if (CLASS_TYPE_P (parm_type) && classtype_has_trivial_abi (parm_type))
+ {
+ tree cleanup
+ = cxx_maybe_build_cleanup (parm, tf_warning_or_error);
+ if (cleanup && cleanup != error_mark_node)
+ finish_decl_cleanup (parm, cleanup);
+ }
+ }
+ }
+
if (use_eh_spec_block (current_function_decl))
current_eh_spec_block = begin_eh_spec_block ();
}
diff --git a/gcc/cp/tree.cc b/gcc/cp/tree.cc
index 20288ed04eb..42463441f92 100644
--- a/gcc/cp/tree.cc
+++ b/gcc/cp/tree.cc
@@ -48,6 +48,8 @@ static tree handle_init_priority_attribute (tree *, tree, tree, int, bool *);
static tree handle_abi_tag_attribute (tree *, tree, tree, int, bool *);
static tree handle_no_dangling_attribute (tree *, tree, tree, int, bool *);
static tree handle_annotation_attribute (tree *, tree, tree, int, bool *);
+static tree handle_trivial_abi_attribute (tree *, tree, tree, int, bool *);
+static tree handle_gnu_trivial_abi_attribute (tree *, tree, tree, int, bool *);
/* If REF is an lvalue, returns the kind of lvalue that REF is.
Otherwise, returns clk_none. */
@@ -5553,6 +5555,7 @@ handle_indeterminate_attribute (tree *node, tree name, tree, int,
}
/* Table of valid C++ attributes. */
+// clang-format off
static const attribute_spec cxx_gnu_attributes[] =
{
/* { name, min_len, max_len, decl_req, type_req, fn_type_req,
@@ -5563,7 +5566,10 @@ static const attribute_spec cxx_gnu_attributes[] =
handle_abi_tag_attribute, NULL },
{ "no_dangling", 0, 1, false, true, false, false,
handle_no_dangling_attribute, NULL },
+ { "trivial_abi", 0, 0, false, true, false, true,
+ handle_gnu_trivial_abi_attribute, NULL },
};
+// clang-format on
const scoped_attribute_specs cxx_gnu_attribute_table =
{
@@ -5614,6 +5620,20 @@ const scoped_attribute_specs internal_attribute_table =
"internal ", { internal_attributes }
};
+/* Table of C++ attributes also recognized in the clang:: namespace. */
+// clang-format off
+static const attribute_spec cxx_clang_attributes[] =
+{
+ { "trivial_abi", 0, 0, false, true, false, true,
+ handle_trivial_abi_attribute, NULL },
+};
+
+const scoped_attribute_specs cxx_clang_attribute_table =
+{
+ "clang", { cxx_clang_attributes }
+};
+// clang-format on
+
/* Handle an "init_priority" attribute; arguments as in
struct attribute_spec.handler. */
static tree
@@ -5953,6 +5973,140 @@ handle_annotation_attribute (tree *node, tree ARG_UNUSED (name),
return NULL_TREE;
}
+/* Handle a "trivial_abi" attribute applied via [[gnu::trivial_abi]].
+ We reject that spelling; suggest [[clang::trivial_abi]] or
+ __attribute__((trivial_abi)) instead. */
+
+static tree
+handle_gnu_trivial_abi_attribute (tree *node, tree name, tree args, int flags,
+ bool *no_add_attrs)
+{
+ if (flags & ATTR_FLAG_CXX11)
+ {
+ warning (OPT_Wattributes,
+ "%<[[gnu::trivial_abi]]%> is not supported; use "
+ "%<[[clang::trivial_abi]]%> or "
+ "%<__attribute__((trivial_abi))%> instead");
+ *no_add_attrs = true;
+ return NULL_TREE;
+ }
+ return handle_trivial_abi_attribute (node, name, args, flags, no_add_attrs);
+}
+
+/* Handle a "trivial_abi" attribute. */
+
+static tree
+handle_trivial_abi_attribute (tree *node, tree name, tree, int,
+ bool *no_add_attrs)
+{
+ tree type = *node;
+
+ /* Only allow on class types (struct, class, union) */
+ if (TREE_CODE (type) != RECORD_TYPE && TREE_CODE (type) != UNION_TYPE)
+ {
+ warning (OPT_Wattributes, "%qE attribute only applies to classes", name);
+ *no_add_attrs = true;
+ return NULL_TREE;
+ }
+
+ return NULL_TREE;
+}
+
+/* Return true if TYPE has the trivial_abi attribute. */
+
+bool
+classtype_has_trivial_abi (tree type)
+{
+ return lookup_attribute ("trivial_abi", TYPE_ATTRIBUTES (type));
+}
+
+/* Validate the trivial_abi attribute on a completed class type.
+ Called from finish_struct after the class is complete. */
+
+void
+validate_trivial_abi_attribute (tree type)
+{
+ if (!classtype_has_trivial_abi (type))
+ return;
+
+ gcc_assert (COMPLETE_TYPE_P (type));
+
+ /* Check for virtual bases. */
+ if (CLASSTYPE_VBASECLASSES (type))
+ {
+ if (warning (OPT_Wattributes, "%<trivial_abi%> cannot be applied to %qT",
+ type))
+ inform (input_location, "has a virtual base");
+ TYPE_ATTRIBUTES (type)
+ = remove_attribute ("trivial_abi", TYPE_ATTRIBUTES (type));
+ return;
+ }
+
+ /* Check for virtual member functions. */
+ if (TYPE_POLYMORPHIC_P (type))
+ {
+ if (warning (OPT_Wattributes, "%<trivial_abi%> cannot be applied to %qT",
+ type))
+ inform (input_location, "is polymorphic");
+ TYPE_ATTRIBUTES (type)
+ = remove_attribute ("trivial_abi", TYPE_ATTRIBUTES (type));
+ return;
+ }
+
+ /* Check for non-trivial base classes. */
+ if (TYPE_BINFO (type))
+ {
+ unsigned int n_bases = BINFO_N_BASE_BINFOS (TYPE_BINFO (type));
+ for (unsigned int i = 0; i < n_bases; ++i)
+ {
+ tree base_binfo = BINFO_BASE_BINFO (TYPE_BINFO (type), i);
+ tree base_type = BINFO_TYPE (base_binfo);
+
+ if (TREE_ADDRESSABLE (base_type))
+ {
+ if (warning (OPT_Wattributes,
+ "%<trivial_abi%> cannot be applied to %qT", type))
+ inform (input_location,
+ "has a base class with non-trivial layout");
+ TYPE_ATTRIBUTES (type)
+ = remove_attribute ("trivial_abi", TYPE_ATTRIBUTES (type));
+ return;
+ }
+ }
+ }
+
+ /* Check for non-trivial member types. */
+ for (tree field = TYPE_FIELDS (type); field; field = DECL_CHAIN (field))
+ {
+ if (TREE_CODE (field) == FIELD_DECL && !DECL_ARTIFICIAL (field))
+ {
+ tree field_type = strip_array_types (TREE_TYPE (field));
+
+ if (CLASS_TYPE_P (field_type) && TREE_ADDRESSABLE (field_type))
+ {
+ if (warning (OPT_Wattributes,
+ "%<trivial_abi%> cannot be applied to %qT", type))
+ inform (input_location,
+ "has a non-static data member of a non-trivial class type");
+ TYPE_ATTRIBUTES (type)
+ = remove_attribute ("trivial_abi", TYPE_ATTRIBUTES (type));
+ return;
+ }
+ }
+ }
+
+ /* Check that not all copy/move constructors are deleted. */
+ if (!classtype_has_non_deleted_copy_or_move_ctor (type))
+ {
+ if (warning (OPT_Wattributes, "%<trivial_abi%> cannot be applied to %qT",
+ type))
+ inform (input_location,
+ "copy constructors and move constructors are all deleted");
+ TYPE_ATTRIBUTES (type)
+ = remove_attribute ("trivial_abi", TYPE_ATTRIBUTES (type));
+ return;
+ }
+}
/* Return a new PTRMEM_CST of the indicated TYPE. The MEMBER is the
thing pointed to by the constant. */
diff --git a/gcc/doc/extend.texi b/gcc/doc/extend.texi
index eb057fb7c40..55aea79e6e5 100644
--- a/gcc/doc/extend.texi
+++ b/gcc/doc/extend.texi
@@ -31199,6 +31199,75 @@ these attributes on a type has the effect of propagating it to every
member function of the type, including implicit special member
functions. @xref{Common Attributes}.
+@cindex @code{trivial_abi} type attribute
+@item trivial_abi
+
+The @code{trivial_abi} attribute can be applied to a C++ class, struct,
+or union. It instructs the compiler to pass and return the type using
+the C ABI for the underlying type when the type would otherwise be
+considered non-trivial for the purpose of calls.
+
+A class annotated with @code{trivial_abi} can have non-trivial destructors
+or copy/move constructors without automatically becoming non-trivial for
+the purposes of calls. For example:
+
+@smallexample
+// A is trivial for the purposes of calls because trivial_abi makes the
+// user-provided special functions trivial.
+struct [[gnu::trivial_abi]] A @{
+ ~A();
+ A(const A &);
+ A(A &&);
+ int x;
+@};
+
+// B's destructor and copy/move constructor are considered trivial for the
+// purpose of calls because A is trivial.
+struct B @{
+ A a;
+@};
+@end smallexample
+
+If a type is trivial for the purposes of calls, has a non-trivial destructor,
+and is passed as an argument by value, the convention is that the callee will
+destroy the object before returning. The lifetime of the copy of the parameter
+in the caller ends without a destructor call when the call begins.
+
+If a type is trivial for the purpose of calls, it is assumed to be trivially
+relocatable.
+
+When a type marked with @code{trivial_abi} is used as a function argument,
+the compiler may omit the call to the copy constructor. Thus, side effects
+of the copy constructor are potentially not performed. For example, objects
+that contain pointers to themselves or otherwise depend on their address
+(or the address of their subobjects) should not be declared with
+@code{trivial_abi}.
+
+Attribute @code{trivial_abi} has no effect in the following cases:
+
+@itemize @bullet
+@item
+The class directly declares a virtual base or virtual methods.
+
+@item
+Copy constructors and move constructors of the class are all deleted.
+
+@item
+The class has a base class that is non-trivial for the purposes of calls.
+
+@item
+The class has a non-static data member whose type is non-trivial for the
+purposes of calls, which includes:
+
+@itemize @bullet
+@item
+classes that are non-trivial for the purposes of calls
+
+@item
+arrays of any of the above
+@end itemize
+@end itemize
+
@end table
@node Function Multiversioning
diff --git a/gcc/testsuite/g++.dg/abi/invisiref3.C b/gcc/testsuite/g++.dg/abi/invisiref3.C
new file mode 100644
index 00000000000..dbb817d4e53
--- /dev/null
+++ b/gcc/testsuite/g++.dg/abi/invisiref3.C
@@ -0,0 +1,28 @@
+// PR c++/107187
+// { dg-do compile { target c++11 } }
+// { dg-additional-options "-fdump-tree-gimple" }
+// { dg-final { scan-tree-dump-not "struct S &" "gimple" } }
+// { dg-final { scan-tree-dump "S::~S" "gimple" } }
+
+struct __attribute__ ((__trivial_abi__)) S
+{
+ S () {}
+ ~S ();
+ int i;
+};
+
+int
+foo (S s)
+{
+ return s.i;
+}
+
+S getS ();
+
+void
+bar ()
+{
+ foo (getS ());
+}
+
+static_assert (!__is_trivially_copyable (S), "");
diff --git a/gcc/testsuite/g++.dg/abi/invisiref3a.C b/gcc/testsuite/g++.dg/abi/invisiref3a.C
new file mode 100644
index 00000000000..427853fabe2
--- /dev/null
+++ b/gcc/testsuite/g++.dg/abi/invisiref3a.C
@@ -0,0 +1,26 @@
+// PR c++/107187
+// { dg-do compile { target c++11 } }
+// { dg-additional-options "-Wabi=10 -fdump-tree-gimple" }
+// { dg-final { scan-tree-dump "struct S &" "gimple" } }
+
+struct S
+{
+ ~S ();
+ int i;
+};
+
+int
+foo (S s)
+{
+ return s.i;
+}
+
+S getS ();
+
+void
+bar ()
+{
+ foo (getS ());
+}
+
+static_assert (!__is_trivially_copyable (S), "");
diff --git a/gcc/testsuite/g++.dg/cpp0x/attr-trivial_abi1.C b/gcc/testsuite/g++.dg/cpp0x/attr-trivial_abi1.C
new file mode 100644
index 00000000000..deda9646d85
--- /dev/null
+++ b/gcc/testsuite/g++.dg/cpp0x/attr-trivial_abi1.C
@@ -0,0 +1,41 @@
+// { dg-do compile { target c++11 } }
+// Test basic functionality of [[clang::trivial_abi]] attribute
+
+struct [[clang::trivial_abi]] TrivialAbi1
+{
+ int x;
+ ~TrivialAbi1 () {
+ } // Non-trivial destructor, but should be allowed with trivial_abi
+};
+
+class [[clang::trivial_abi]] TrivialAbi2
+{
+ int y;
+
+public:
+ TrivialAbi2 (const TrivialAbi2 &) {
+ } // Non-trivial copy constructor, but should be allowed
+ ~TrivialAbi2 () {}
+};
+
+// Test that the attribute is recognized and the types compile successfully
+
+// Test basic usage in function parameters and return values
+TrivialAbi1
+func1 (TrivialAbi1 param)
+{
+ return param;
+}
+
+TrivialAbi2
+func2 (TrivialAbi2 param)
+{
+ return param;
+}
+
+union [[clang::trivial_abi]] TrivialUnion
+{
+ int a;
+ float b;
+ ~TrivialUnion () {}
+};
diff --git a/gcc/testsuite/g++.dg/cpp0x/attr-trivial_abi2.C b/gcc/testsuite/g++.dg/cpp0x/attr-trivial_abi2.C
new file mode 100644
index 00000000000..76d47a5dc71
--- /dev/null
+++ b/gcc/testsuite/g++.dg/cpp0x/attr-trivial_abi2.C
@@ -0,0 +1,70 @@
+// { dg-do compile { target c++11 } }
+// Test error cases and restrictions for __attribute__((trivial_abi)) attribute
+
+// Test: attribute rejected on non-class types
+int __attribute__ ((trivial_abi))
+x; // { dg-warning "'trivial_abi' attribute only applies to classes" }
+typedef int __attribute__ ((trivial_abi))
+int_t; // { dg-warning "'trivial_abi' attribute only applies to classes" }
+
+// Test: attribute rejected on types with virtual functions
+struct __attribute__ ((trivial_abi))
+WithVirtual // { dg-warning "'trivial_abi' cannot be applied to 'WithVirtual'" }
+{
+ virtual void f () {}
+};
+
+// Test: attribute rejected on types with virtual bases
+struct Base
+{
+};
+
+struct __attribute__ ((trivial_abi))
+WithVirtualBase // { dg-warning "'trivial_abi' cannot be applied" }
+ : virtual Base
+{
+ int x;
+};
+
+// Test: attribute rejected when all copy/move constructors are deleted
+struct __attribute__ ((trivial_abi))
+AllDeleted // { dg-warning "'trivial_abi' cannot be applied to 'AllDeleted'" }
+{
+ AllDeleted (const AllDeleted &) = delete;
+ AllDeleted (AllDeleted &&) = delete;
+ int x;
+};
+
+// Test: attribute rejected on types with non-trivial base classes
+struct NonTrivialBase
+{
+ NonTrivialBase (const NonTrivialBase &) {} // Non-trivial copy
+};
+
+struct __attribute__ ((trivial_abi))
+WithNonTrivialBase // { dg-warning "'trivial_abi' cannot be applied" }
+ : NonTrivialBase
+{
+ int x;
+};
+
+// Test: attribute rejected on types with non-trivial member types
+struct NonTrivialMember
+{
+ NonTrivialMember (const NonTrivialMember &) {} // Non-trivial copy
+};
+
+struct __attribute__ ((trivial_abi))
+WithNonTrivialMember // { dg-warning "'trivial_abi' cannot be applied" }
+{
+ NonTrivialMember member;
+ int x;
+};
+
+// Test: attribute rejected on array members of non-trivial types
+struct __attribute__ ((trivial_abi))
+WithNonTrivialArray // { dg-warning "'trivial_abi' cannot be applied" }
+{
+ NonTrivialMember arr[5];
+ int x;
+};
diff --git a/gcc/testsuite/g++.dg/cpp0x/attr-trivial_abi3.C b/gcc/testsuite/g++.dg/cpp0x/attr-trivial_abi3.C
new file mode 100644
index 00000000000..8be389b79b6
--- /dev/null
+++ b/gcc/testsuite/g++.dg/cpp0x/attr-trivial_abi3.C
@@ -0,0 +1,58 @@
+// { dg-do compile { target c++11 } }
+// Test ABI behavior of [[clang::trivial_abi]] attribute
+// This test verifies that types with trivial_abi are passed by value
+// instead of by invisible reference, even with non-trivial constructors
+
+struct [[clang::trivial_abi]] TrivialAbiType
+{
+ int data;
+ TrivialAbiType () : data (0) {}
+ TrivialAbiType (const TrivialAbiType &other) : data (other.data) {}
+ TrivialAbiType (TrivialAbiType &&other) : data (other.data) {}
+ ~TrivialAbiType () {}
+};
+
+struct NonTrivialType
+{
+ int data;
+ NonTrivialType () : data (0) {}
+ NonTrivialType (const NonTrivialType &other) : data (other.data) {}
+ NonTrivialType (NonTrivialType &&other) : data (other.data) {}
+ ~NonTrivialType () {}
+};
+
+// Test that trivial_abi types are considered trivially relocatable
+// (ABI behavior is tested separately in invisiref3.C)
+
+// Function templates to test parameter passing
+template <typename T>
+void
+test_param_passing (T param)
+{
+ // Function body doesn't matter for ABI test
+}
+
+// Test usage
+void
+test_functions ()
+{
+ TrivialAbiType trivial_obj;
+ NonTrivialType non_trivial_obj;
+
+ // Both should compile, but with different ABI behavior
+ test_param_passing (trivial_obj); // Should pass by value
+ test_param_passing (non_trivial_obj); // Should pass by invisible reference
+}
+
+// Test return values
+TrivialAbiType
+return_trivial ()
+{
+ return TrivialAbiType{};
+}
+
+NonTrivialType
+return_non_trivial ()
+{
+ return NonTrivialType{};
+}
diff --git a/gcc/testsuite/g++.dg/cpp0x/attr-trivial_abi4.C b/gcc/testsuite/g++.dg/cpp0x/attr-trivial_abi4.C
new file mode 100644
index 00000000000..b9771ee45c7
--- /dev/null
+++ b/gcc/testsuite/g++.dg/cpp0x/attr-trivial_abi4.C
@@ -0,0 +1,57 @@
+// { dg-do compile { target c++11 } }
+// Test edge cases and inheritance with [[clang::trivial_abi]] attribute
+
+// Test: Derived classes with trivial_abi base classes
+struct [[clang::trivial_abi]] TrivialBase
+{
+ int x;
+ ~TrivialBase () {}
+};
+
+// This should be allowed - base class has trivial_abi
+struct [[clang::trivial_abi]] DerivedFromTrivial : TrivialBase
+{
+ int y;
+ ~DerivedFromTrivial () {}
+};
+
+// Test: Multiple inheritance with all trivial bases
+struct [[clang::trivial_abi]] TrivialBase2
+{
+ int z;
+ ~TrivialBase2 () {}
+};
+
+struct [[clang::trivial_abi]] MultipleInheritance : TrivialBase, TrivialBase2
+{
+ int w;
+ ~MultipleInheritance () {}
+};
+
+// Test: Empty class with trivial_abi
+struct [[clang::trivial_abi]] EmptyTrivialAbi
+{
+ ~EmptyTrivialAbi () {}
+};
+
+// Test: Class with only trivial members
+struct [[clang::trivial_abi]] OnlyTrivialMembers
+{
+ int a;
+ float b;
+ char c[10];
+ ~OnlyTrivialMembers () {}
+};
+
+// Test: Template class with trivial_abi
+template <typename T> struct [[clang::trivial_abi]] TemplateTrivialAbi
+{
+ T data;
+ ~TemplateTrivialAbi () {}
+};
+
+// This should work for scalar types
+using IntTrivialAbi = TemplateTrivialAbi<int>;
+
+// Test with another trivial_abi type as template parameter
+using NestedTrivialAbi = TemplateTrivialAbi<TrivialBase>;
diff --git a/gcc/testsuite/g++.dg/cpp0x/attr-trivial_abi5.C b/gcc/testsuite/g++.dg/cpp0x/attr-trivial_abi5.C
new file mode 100644
index 00000000000..27abe9c8abc
--- /dev/null
+++ b/gcc/testsuite/g++.dg/cpp0x/attr-trivial_abi5.C
@@ -0,0 +1,60 @@
+// { dg-do compile { target c++11 } }
+// Test compatibility of [[clang::trivial_abi]] with other GCC features
+
+// Test: Compatibility with other attributes
+struct [[clang::trivial_abi]] [[gnu::packed]] TrivialAbiPacked
+{
+ char a;
+ int b;
+ ~TrivialAbiPacked () {}
+};
+
+// Test: Compatibility with alignas
+struct [[clang::trivial_abi]] alignas (16) TrivialAbiAligned
+{
+ int x;
+ ~TrivialAbiAligned () {}
+};
+
+// Test: Forward declaration and later definition
+struct TrivialForward;
+struct [[clang::trivial_abi]] TrivialForward
+{
+ int x;
+ ~TrivialForward () {}
+};
+
+// Test: typedef and using declarations
+using TrivialAlias = TrivialAbiPacked;
+typedef TrivialAbiAligned TrivialTypedef;
+
+// Test: Local classes
+void
+test_local_class ()
+{
+ struct [[clang::trivial_abi]] LocalTrivial
+ {
+ int x;
+ ~LocalTrivial () {}
+ };
+}
+
+// Test: Named nested structs with trivial_abi
+struct ContainerClass
+{
+ struct [[clang::trivial_abi]] NestedStruct
+ {
+ int nested_member;
+ ~NestedStruct () {}
+ };
+};
+
+// Test: Nested classes
+struct Outer
+{
+ struct [[clang::trivial_abi]] Nested
+ {
+ int value;
+ ~Nested () {}
+ };
+};
diff --git a/gcc/testsuite/g++.dg/cpp0x/attr-trivial_abi6.C b/gcc/testsuite/g++.dg/cpp0x/attr-trivial_abi6.C
new file mode 100644
index 00000000000..2f431d63e68
--- /dev/null
+++ b/gcc/testsuite/g++.dg/cpp0x/attr-trivial_abi6.C
@@ -0,0 +1,22 @@
+// { dg-do compile { target c++11 } }
+// Test that [[gnu::trivial_abi]] is rejected
+
+struct [[gnu::trivial_abi]] S0 // { dg-warning "'..gnu::trivial_abi..' is not supported" }
+{
+ int a;
+ ~S0 () {}
+};
+
+// __attribute__((trivial_abi)) should still work
+struct __attribute__ ((trivial_abi)) S1
+{
+ int a;
+ ~S1 () {}
+};
+
+// [[clang::trivial_abi]] should still work
+struct [[clang::trivial_abi]] S2
+{
+ int a;
+ ~S2 () {}
+};
diff --git a/gcc/testsuite/g++.dg/cpp0x/attr-trivial_abi_syntax.C b/gcc/testsuite/g++.dg/cpp0x/attr-trivial_abi_syntax.C
new file mode 100644
index 00000000000..7d6f375f412
--- /dev/null
+++ b/gcc/testsuite/g++.dg/cpp0x/attr-trivial_abi_syntax.C
@@ -0,0 +1,150 @@
+// { dg-do compile { target c++11 } }
+// Test comprehensive trivial_abi attribute behavior
+// Based on clang's attr-trivial-abi.cpp test
+
+void __attribute__ ((trivial_abi))
+foo (); // { dg-warning "'trivial_abi' attribute only applies to classes" }
+
+// Should not crash.
+template <class> class __attribute__ ((trivial_abi)) a
+{
+ a (a &&);
+};
+
+struct [[clang::trivial_abi]] S0
+{
+ int a;
+};
+
+struct __attribute__ ((trivial_abi)) S1
+{
+ int a;
+};
+
+struct __attribute__ ((trivial_abi))
+S3 // { dg-warning "'trivial_abi' cannot be applied to 'S3'" }
+{ // { dg-message "is polymorphic" "" { target *-*-* } .-1 }
+ virtual void m ();
+};
+
+struct S3_2 // { dg-warning "'trivial_abi' cannot be applied to 'S3_2'" }
+{ // { dg-message "is polymorphic" "" { target *-*-* } .-1 }
+ virtual void m ();
+} __attribute__ ((trivial_abi));
+
+// clang-format off
+struct __attribute__ ((trivial_abi))
+S3_3 // { dg-warning "'trivial_abi' cannot be applied to 'S3_3'" }
+{ // { dg-message "has a non-static data member of a non-trivial class type" "" { target *-*-* } .-1 }
+ S3_3 (S3_3 &&);
+ S3_2 s32;
+};
+// clang-format on
+
+// Diagnose invalid trivial_abi even when the type is templated because it has a
+// non-trivial field. Note: GCC only diagnoses this at template instantiation,
+// unlike Clang which diagnoses it earlier.
+template <class T> struct __attribute__ ((trivial_abi)) S3_4
+{
+ S3_4 (S3_4 &&);
+ S3_2 s32;
+};
+
+struct S4
+{
+ int a;
+};
+static_assert (__is_trivially_copyable (S4), "");
+
+struct __attribute__ ((
+ trivial_abi)) S5 // { dg-warning "'trivial_abi' cannot be applied to 'S5'" }
+ : public virtual S4
+// { dg-message "has a virtual base" "" { target *-*-* } .-2 }
+{
+};
+
+struct __attribute__ ((trivial_abi)) S9 : public S4
+{
+};
+
+struct __attribute__ ((trivial_abi (1)))
+S8 // { dg-error "wrong number of arguments" }
+{ // { dg-message "expected 0, found 1" "" { target *-*-* } .-1 }
+ int a;
+};
+
+// Do not warn about deleted ctors when 'trivial_abi' is used to annotate a
+// template class.
+template <class T> struct __attribute__ ((trivial_abi)) S10
+{
+ T p;
+};
+
+S10<int *> p1;
+
+template <class T> struct S14
+{
+ T a;
+};
+
+template <class T> struct __attribute__ ((trivial_abi)) S15 : S14<T>
+{
+};
+
+S15<int> s15;
+
+template <class T> struct __attribute__ ((trivial_abi)) S16
+{
+ S14<T> a;
+};
+
+S16<int> s16;
+
+template <class T> struct __attribute__ ((trivial_abi)) S17
+{
+};
+
+S17<int> s17;
+
+// clang-format off
+namespace deletedCopyMoveConstructor {
+struct __attribute__ ((trivial_abi))
+CopyMoveDeleted // { dg-warning "cannot be applied" }
+{ // { dg-message "copy constructors and move constructors are all deleted" "" { target *-*-* } .-1 }
+ CopyMoveDeleted (const CopyMoveDeleted &) = delete;
+ CopyMoveDeleted (CopyMoveDeleted &&) = delete;
+};
+
+struct __attribute__ ((trivial_abi)) S18 // { dg-warning "cannot be applied" }
+{ // { dg-message "has a non-static data member of a non-trivial class type" "" { target *-*-* } .-1 }
+ CopyMoveDeleted a;
+};
+// clang-format on
+
+struct __attribute__ ((trivial_abi)) CopyDeleted
+{
+ CopyDeleted (const CopyDeleted &) = delete;
+ CopyDeleted (CopyDeleted &&) = default;
+};
+
+struct __attribute__ ((trivial_abi)) MoveDeleted
+{
+ MoveDeleted (const MoveDeleted &) = default;
+ MoveDeleted (MoveDeleted &&) = delete;
+};
+
+// clang-format off
+struct __attribute__ ((trivial_abi)) S19 // { dg-warning "cannot be applied" }
+{ // { dg-message "copy constructors and move constructors are all deleted" "" { target *-*-* } .-1 }
+ CopyDeleted a;
+ MoveDeleted b;
+};
+// clang-format on
+
+// This is fine since the move constructor isn't deleted.
+struct __attribute__ ((trivial_abi)) S20
+{
+ int &&a; // a member of rvalue reference type deletes the copy constructor.
+};
+
+} // namespace deletedCopyMoveConstructor
--
2.53.0