Bootstrapped and regtested on x86_64-pc-linux-gnu, does this look OK for trunk?
-- >8 -- Here the result of A::make(0, 0, 1), (0, 1, 0) and (1, 0, 0) are each represented as a single-element CONSTRUCTOR with CONSTRUCTOR_NO_CLEARING cleared, and we end up mangling them all as A{1}, i.e. eliding both the implicit initial and trailing zeros. Mangling them all the same seems clearly wrong since they're logically different values. This patch makes us include initial zeros in such mangling, which seems like the natural behavior (and matches e.g. Clang). PR c++/121231 gcc/ChangeLog: * common.opt: Document additional -fabi-version=21 change. * doc/invoke.texi: Likewise. gcc/cp/ChangeLog: * mangle.cc (write_expression): gcc/testsuite/ChangeLog: * g++.dg/abi/mangle82.C: New test. --- gcc/common.opt | 2 + gcc/cp/mangle.cc | 49 ++++++++++++++++-- gcc/doc/invoke.texi | 5 +- gcc/testsuite/g++.dg/abi/mangle82.C | 79 +++++++++++++++++++++++++++++ 4 files changed, 128 insertions(+), 7 deletions(-) create mode 100644 gcc/testsuite/g++.dg/abi/mangle82.C diff --git a/gcc/common.opt b/gcc/common.opt index 70659fabebd5..97b1e27456ce 100644 --- a/gcc/common.opt +++ b/gcc/common.opt @@ -1067,6 +1067,8 @@ Driver Undocumented ; ; 21: Fix noexcept lambda capture pruning. ; Fix C++20 layout of base with all explicitly defaulted constructors. +; Fix mangling of class and array objects with implicitly +; zero-initialized initial subojects. ; Default in G++ 16. ; ; Additional positive integers will be assigned as new versions of diff --git a/gcc/cp/mangle.cc b/gcc/cp/mangle.cc index 13d5dedebd29..f317dfd978ae 100644 --- a/gcc/cp/mangle.cc +++ b/gcc/cp/mangle.cc @@ -3739,11 +3739,50 @@ write_expression (tree expr) constructor_elt *ce; if (!undigested) - for (HOST_WIDE_INT i = 0; vec_safe_iterate (elts, i, &ce); ++i) - if ((TREE_CODE (etype) == UNION_TYPE - && ce->index != first_field (etype)) - || !zero_init_expr_p (ce->value)) - last_nonzero = i; + { + for (HOST_WIDE_INT i = 0; vec_safe_iterate (elts, i, &ce); ++i) + { + if (i == 0 + && !CONSTRUCTOR_NO_CLEARING (expr)) + { + /* Write out any implicit initial zeros. */ + if (TREE_CODE (TREE_TYPE (expr)) == RECORD_TYPE) + for (tree field = TYPE_FIELDS (TREE_TYPE (expr)); + field; field = DECL_CHAIN (field)) + { + field = next_subobject_field (field); + if (!field || field == ce->index) + break; + if (abi_check (21)) + write_expression (build_zero_cst + (TREE_TYPE (field))); + } + else if (TREE_CODE (TREE_TYPE (expr)) == ARRAY_TYPE) + { + unsigned HOST_WIDE_INT j; + if (TREE_CODE (ce->index) == RANGE_EXPR) + j = tree_to_uhwi (TREE_OPERAND (ce->index, 0)); + else + j = tree_to_uhwi (ce->index); + tree zero = NULL_TREE; + for (; j != 0; --j) + if (abi_check (21)) + { + if (!zero) + zero = build_zero_cst (TREE_TYPE + (TREE_TYPE (expr))); + write_expression (zero); + } + } + } + + if ((TREE_CODE (etype) == UNION_TYPE + && ce->index != first_field (etype)) + || !zero_init_expr_p (ce->value)) + last_nonzero = i; + } + + } if (undigested || last_nonzero != UINT_MAX) for (HOST_WIDE_INT i = 0; vec_safe_iterate (elts, i, &ce); ++i) diff --git a/gcc/doc/invoke.texi b/gcc/doc/invoke.texi index e442a9cb73e4..23cd736e5fd1 100644 --- a/gcc/doc/invoke.texi +++ b/gcc/doc/invoke.texi @@ -3016,8 +3016,9 @@ Version 20, which first appeared in G++ 15, fixes manglings of lambdas in static data member initializers. Version 21, which first appeared in G++ 16, fixes unnecessary captures -in noexcept lambdas (c++/119764) and layout of a base class -with all explicitly defaulted constructors (c++/120012). +in noexcept lambdas (c++/119764), layout of a base class with all explicitly +defaulted constructors (c++/120012), and mangling of class and array +objects with implicitly zero-initialized initial subojects (c++/121231). See also @option{-Wabi}. diff --git a/gcc/testsuite/g++.dg/abi/mangle82.C b/gcc/testsuite/g++.dg/abi/mangle82.C new file mode 100644 index 000000000000..529c75ee8997 --- /dev/null +++ b/gcc/testsuite/g++.dg/abi/mangle82.C @@ -0,0 +1,79 @@ +// Test mangling of C++20 class NTTP objects with implicitly zeroed initial subojects +// PR c++/121231 +// { dg-do compile { target c++20 } } + +struct A { + int x, y, z; + + static constexpr A make(int x, int y, int z) { + A a{}; + if (x != 0) + a.x = x; + if (y != 0) + a.y = y; + if (z != 0) + a.z = z; + return a; + } + +}; + +struct B : A { + int w; + + static constexpr B make(int x, int y, int z, int w) { + B b{}; + if (x != 0 || y != 0 || z != 0) + static_cast<A&>(b) = A::make(x, y, z); + if (w != 0) + b.w = w; + return b; + } +}; + +struct C { + int xyz[3]; + + static constexpr C make(int x, int y, int z) { + C c{}; + if (x != 0) + c.xyz[0] = x; + if (y != 0) + c.xyz[1] = y; + if (z != 0) + c.xyz[2] = z; + return c; + } +}; + +template<int N, A a> void f(); +template<int N, B b> void g(); +template<int N, C c> void h(); + +int main() { + f<0, A::make(0, 0, 1)>(); // void f<0, A{0, 0, 1}>() + f<1, A::make(0, 1, 0)>(); // void f<1, A{0, 1}>() + f<2, A::make(0, 0, 0)>(); // void f<2, A{}>() + + g<0, B::make(0, 0, 0, 1)>(); // void g<0, B{A{}, 1}>() + g<1, B::make(0, 0, 1, 0)>(); // void g<1, B{A{0, 0, 1}}>() + g<2, B::make(0, 1, 0, 0)>(); // void g<2, B{A{0, 1}}>() + g<3, B::make(0, 0, 0, 0)>(); // void g<3, B{}>() + + h<0, C::make(0, 0, 1)>(); // void h<0, C{int [3]{0, 0, 1}}>() + h<1, C::make(0, 1, 0)>(); // void h<1, C{int [3]{0, 1}}>() + h<2, C::make(0, 0, 0)>(); // void h<2, C{}>() +} + +// { dg-final { scan-assembler "_Z1fILi0EXtl1ALi0ELi0ELi1EEEEvv" } } +// { dg-final { scan-assembler "_Z1fILi1EXtl1ALi0ELi1EEEEvv" } } +// { dg-final { scan-assembler "_Z1fILi2EXtl1AEEEvv" } } + +// { dg-final { scan-assembler "_Z1gILi0EXtl1Btl1AELi1EEEEvv" } } +// { dg-final { scan-assembler "_Z1gILi1EXtl1Btl1ALi0ELi0ELi1EEEEEvv" } } +// { dg-final { scan-assembler "_Z1gILi2EXtl1Btl1ALi0ELi1EEEEEvv" } } +// { dg-final { scan-assembler "_Z1gILi3EXtl1BEEEvv" } } + +// { dg-final { scan-assembler "_Z1hILi0EXtl1CtlA3_iLi0ELi0ELi1EEEEEvv" } } +// { dg-final { scan-assembler "_Z1hILi1EXtl1CtlA3_iLi0ELi1EEEEEvv" } } +// { dg-final { scan-assembler "_Z1hILi2EXtl1CEEEvv" } } -- 2.50.1.469.ge813a0200a