rsmith created this revision.
rsmith added a reviewer: rjmccall.
Herald added a project: clang.
Herald added a subscriber: cfe-commits.
rsmith requested review of this revision.

As far as I can determine, the only case we need to be careful about is
when we would generate a reference to the symbol and not emit any
corresponding definition, and that only happens when generating an
available_externally body of the corresponding complete structor
variant. Disable all complete -> base forwarding in that case for
structors of final classes.

See https://github.com/itanium-cxx-abi/cxx-abi/issues/104 for more
background.


Repository:
  rG LLVM Github Monorepo

https://reviews.llvm.org/D86494

Files:
  clang/lib/CodeGen/CGClass.cpp
  clang/lib/CodeGen/CodeGenModule.h
  clang/lib/CodeGen/ItaniumCXXABI.cpp
  clang/test/CodeGenCXX/final-structor-delegation.cpp

Index: clang/test/CodeGenCXX/final-structor-delegation.cpp
===================================================================
--- /dev/null
+++ clang/test/CodeGenCXX/final-structor-delegation.cpp
@@ -0,0 +1,56 @@
+// RUN: %clang_cc1 -triple x86_64-linux-gnu -std=c++20 -emit-llvm %s -O2 -disable-llvm-passes -o /tmp/noalias
+// RUN: %clang_cc1 -triple x86_64-linux-gnu -std=c++20 -emit-llvm %s -O2 -disable-llvm-passes -mconstructor-aliases -o /tmp/alias
+// RUN: %clang_cc1 -triple x86_64-linux-gnu -std=c++20 -emit-llvm %s -O2 -disable-llvm-passes -o - | FileCheck %s --check-prefixes=CHECK,NOALIAS
+// RUN: %clang_cc1 -triple x86_64-linux-gnu -std=c++20 -emit-llvm %s -O2 -disable-llvm-passes -mconstructor-aliases -o - | FileCheck %s --check-prefixes=CHECK,ALIAS
+
+extern "C" void f(), g();
+
+// Non-final class: we can assume that the base subobject variants of structors
+// exist whenever the complete object variants exist.
+template <typename T> struct A {
+  A() { f(); }
+  ~A() { g(); }
+};
+extern template struct A<int>;
+// CHECK-LABEL: define {{.*}}init
+// NOALIAS: call void @_ZN1AIiEC1Ev({{.*}} @a)
+// NOALIAS: call {{.*}}cxa_atexit{{.*}} @_ZN1AIiED1Ev {{.*}} @a
+// ALIAS: call void @_ZN1AIiEC2Ev({{.*}} @a)
+// ALIAS: call {{.*}}cxa_atexit{{.*}} @_ZN1AIiED2Ev {{.*}} @a
+A<int> a;
+
+// NOALIAS-LABEL: define available_externally {{.*}} @_ZN1AIiEC1Ev(
+// NOALIAS-NOT: call void @f(
+// NOALIAS: call void @_ZN1AIiEC2Ev(
+// NOALIAS-NOT: call void @f(
+// NOALIAS: }
+
+// NOALIAS-LABEL: define available_externally {{.*}} @_ZN1AIiED1Ev(
+// NOALIAS-NOT: call void @g(
+// NOALIAS: call void @_ZN1AIiED2Ev(
+// NOALIAS-NOT: call void @g(
+// NOALIAS: }
+
+// Final class: we can't assume that the base subobject variants exist, because
+// GCC 10 doesn't emit them (even though the ABI requires them).
+template <typename T> struct B final {
+  B() { f(); }
+  ~B() { g(); }
+};
+extern template struct B<int>;
+// CHECK-LABEL: define {{.*}}init
+// CHECK: call void @_ZN1BIiEC1Ev({{.*}} @b)
+// CHECK: call {{.*}}cxa_atexit{{.*}} @_ZN1BIiED1Ev {{.*}} @b
+B<int> b;
+
+// CHECK-LABEL: define available_externally {{.*}} @_ZN1BIiEC1Ev(
+// CHECK-NOT: call void @_ZN1BIiEC2Ev(
+// CHECK: call void @f(
+// CHECK-NOT: call void @_ZN1BIiEC2Ev(
+// CHECK: }
+
+// CHECK-LABEL: define available_externally {{.*}} @_ZN1BIiED1Ev(
+// CHECK-NOT: call void @_ZN1BIiED2Ev(
+// CHECK: call void @g(
+// CHECK-NOT: call void @_ZN1BIiED2Ev(
+// CHECK: }
Index: clang/lib/CodeGen/ItaniumCXXABI.cpp
===================================================================
--- clang/lib/CodeGen/ItaniumCXXABI.cpp
+++ clang/lib/CodeGen/ItaniumCXXABI.cpp
@@ -1594,6 +1594,7 @@
 
   // The constructor used for constructing this as a base class;
   // ignores virtual bases.
+  // FIXME: Skip this for final classes once the ABI permits us to.
   CGM.EmitGlobal(GlobalDecl(D, Ctor_Base));
 
   // The constructor used for constructing this as a complete class;
@@ -1626,6 +1627,7 @@
 void ItaniumCXXABI::EmitCXXDestructors(const CXXDestructorDecl *D) {
   // The destructor used for destructing this as a base class; ignores
   // virtual bases.
+  // FIXME: Skip this for final classes once the ABI permits us to.
   CGM.EmitGlobal(GlobalDecl(D, Dtor_Base));
 
   // The destructor used for destructing this as a most-derived class;
@@ -4050,6 +4052,12 @@
     const auto *CD = cast<CXXConstructorDecl>(MD);
     AliasDecl = GlobalDecl(CD, Ctor_Complete);
   }
+
+  // If we don't know that the base class version will definitely exist, we
+  // can't emit a reference to it.
+  if (!CGM.isBaseStructorEmitted(AliasDecl))
+    return StructorCodegen::Emit;
+
   llvm::GlobalValue::LinkageTypes Linkage = CGM.getFunctionLinkage(AliasDecl);
 
   if (llvm::GlobalValue::isDiscardableIfUnused(Linkage))
Index: clang/lib/CodeGen/CodeGenModule.h
===================================================================
--- clang/lib/CodeGen/CodeGenModule.h
+++ clang/lib/CodeGen/CodeGenModule.h
@@ -710,6 +710,11 @@
   CtorList &getGlobalCtors() { return GlobalCtors; }
   CtorList &getGlobalDtors() { return GlobalDtors; }
 
+  /// For a GlobalDecl representing a complete-object constructor or destructor
+  /// that we will emit, determine whether we know that the base subobject
+  /// constructor or destructor will definitely be emitted if referenced.
+  bool isBaseStructorEmitted(GlobalDecl GD);
+
   /// getTBAATypeInfo - Get metadata used to describe accesses to objects of
   /// the given type.
   llvm::MDNode *getTBAATypeInfo(QualType QTy);
Index: clang/lib/CodeGen/CGClass.cpp
===================================================================
--- clang/lib/CodeGen/CGClass.cpp
+++ clang/lib/CodeGen/CGClass.cpp
@@ -722,6 +722,25 @@
     pushEHDestroy(dtorKind, LHS.getAddress(*this), FieldType);
 }
 
+bool CodeGenModule::isBaseStructorEmitted(GlobalDecl GD) {
+  assert((isa<CXXConstructorDecl>(GD.getDecl()) &&
+          GD.getCtorType() == Ctor_Complete) ||
+         (isa<CXXDestructorDecl>(GD.getDecl()) &&
+          GD.getDtorType() == Dtor_Complete));
+  // For a non-final class, the base structor version is always emitted
+  // whenever the complete version is emitted.
+  if (!cast<CXXMethodDecl>(GD.getDecl())->getParent()->isEffectivelyFinal())
+    return true;
+  // For a final class, the ABI still requires the base structor to be emitted
+  // with the complete object structor, but GCC 10 does not do this. Don't emit
+  // a reference from an available_externally complete object structor to a
+  // potentially-non-existent base subobject structor.
+  //
+  // See https://github.com/itanium-cxx-abi/cxx-abi/issues/104.
+  return !llvm::GlobalValue::isAvailableExternallyLinkage(
+      getFunctionLinkage(GD));
+}
+
 /// Checks whether the given constructor is a valid subject for the
 /// complete-to-base constructor delegation optimization, i.e.
 /// emitting the complete constructor as a simple call to the base
@@ -849,7 +868,8 @@
   // Before we go any further, try the complete->base constructor
   // delegation optimization.
   if (CtorType == Ctor_Complete && IsConstructorDelegationValid(Ctor) &&
-      CGM.getTarget().getCXXABI().hasConstructorVariants()) {
+      CGM.getTarget().getCXXABI().hasConstructorVariants() &&
+      CGM.isBaseStructorEmitted(CurGD)) {
     EmitDelegateCXXConstructorCall(Ctor, Ctor_Base, Args, Ctor->getEndLoc());
     return;
   }
@@ -1502,7 +1522,7 @@
     // Enter the cleanup scopes for virtual bases.
     EnterDtorCleanups(Dtor, Dtor_Complete);
 
-    if (!isTryBody) {
+    if (!isTryBody && CGM.isBaseStructorEmitted(CurGD)) {
       QualType ThisTy = Dtor->getThisObjectType();
       EmitCXXDestructorCall(Dtor, Dtor_Base, /*ForVirtualBase=*/false,
                             /*Delegating=*/false, LoadCXXThisAddress(), ThisTy);
_______________________________________________
cfe-commits mailing list
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits

Reply via email to