/* We don't need to process the original function any further. */
return 1;
}
+
+/* If maybe_clone_body is called while the cdtor is still tentative,
+ DECL_ONE_ONLY will be false and so will be can_alias_cdtor (fn).
+ In that case we wouldn't try to optimize using an alias and instead
+ would emit separate base and complete cdtor. The following function
+ attempts to still optimize that case when we import_export_decl
+ is called first time on one of the clones. */
+
+void
+maybe_optimize_cdtor (tree orig_decl)
+{
+ tree fns[3];
+ tree fn = DECL_CLONED_FUNCTION (orig_decl);
+ gcc_checking_assert (DECL_MAYBE_IN_CHARGE_CDTOR_P (fn));
+ if (DECL_INTERFACE_KNOWN (fn)
+ || !TREE_ASM_WRITTEN (fn)
+ || !DECL_ONE_ONLY (orig_decl)
+ || symtab->global_info_ready)
+ return;
+
+ populate_clone_array (fn, fns);
+
+ if (!fns[0] || !fns[1])
+ return;
+ for (int i = 2 - !fns[2]; i >= 0; --i)
+ if (fns[i] != orig_decl && DECL_INTERFACE_KNOWN (fns[i]))
+ return;
+ DECL_INTERFACE_KNOWN (fn) = 1;
+ comdat_linkage (fn);
+ if (!can_alias_cdtor (fn))
+ return;
+ /* For comdat base and complete cdtors put them into the same,
+ *[CD]5* comdat group instead of *[CD][12]*. */
+ auto n0 = cgraph_node::get_create (fns[0]);
+ auto n1 = cgraph_node::get_create (fns[1]);
+ auto n2 = fns[2] ? cgraph_node::get_create (fns[1]) : NULL;
+ if (n0->lowered || n1->lowered || (n2 && n2->lowered))
+ return;
+ import_export_decl (fns[0]);
+ n1->definition = false;
+ if (!n0->create_same_body_alias (fns[1], fns[0]))
+ return;
+
+ tree comdat_group = cdtor_comdat_group (fns[1], fns[0]);
+ n1 = cgraph_node::get (fns[1]);
+ n0->set_comdat_group (comdat_group);
+ if (n1->same_comdat_group)
+ n1->remove_from_same_comdat_group ();
+ n1->add_to_same_comdat_group (n0);
+ if (fns[2])
+ n2->add_to_same_comdat_group (n0);
+ import_export_decl (fns[1]);
+ /* Remove the body now that it is an alias. */
+ release_function_body (fns[1]);
+}
--- /dev/null
+// PR lto/113208
+// { dg-do compile { target { c++11 && { *-*-*gnu* } } } }
+// { dg-additional-options "-O2 -fkeep-inline-functions" }
+// { dg-final { scan-assembler "_ZN1BI1CEC5ERKS1_,comdat" } }
+// { dg-final { scan-assembler-not "_ZN1BI1CEC1ERKS1_,comdat" } }
+// { dg-final { scan-assembler-not "_ZN1BI1CEC2ERKS1_,comdat" } }
+
+template <typename T>
+struct A {
+ int foo () const;
+ A (int, int);
+};
+template <typename T>
+struct B : A<T> {
+ constexpr B (const B &x) : A<T> (1, x.foo ()) {}
+ B () : A<T> (1, 2) {}
+};
+struct C;
+struct D : B<C> {};
+void bar (D);
+
+void
+baz (D x)
+{
+ bar (x);
+}
--- /dev/null
+// PR lto/113208
+// { dg-do compile { target { c++11 && { *-*-*gnu* } } } }
+// { dg-additional-options "-O2" }
+// { dg-final { scan-assembler-not "_ZN1BI1CEC5ERKS1_,comdat" } }
+// { dg-final { scan-assembler-not "_ZN1BI1CEC1ERKS1_,comdat" } }
+// { dg-final { scan-assembler-not "_ZN1BI1CEC2ERKS1_,comdat" } }
+
+template <typename T>
+struct A {
+ int foo () const;
+ A (int, int);
+};
+template <typename T>
+struct B : A<T> {
+ constexpr B (const B &x) : A<T> (1, x.foo ()) {}
+ B () : A<T> (1, 2) {}
+};
+struct C;
+struct D : B<C> {};
+void bar (D);
+
+void
+baz (D x)
+{
+ bar (x);
+}
+
+extern template struct B<C>;