Commit ae9b0701 authored by Reid Kleckner's avatar Reid Kleckner
Browse files

[MS] Always use base dtors in place of complete/vbase dtors when possible

Summary:
Previously we tried too hard to uphold the fiction that destructor
variants work like they do on Itanium throughout the ABI-neutral parts
of clang. This lead to MS C++ ABI incompatiblities and other bugs. Now,
-mconstructor-aliases will no longer control this ABI detail, and clang
-cc1's LLVM IR output will be this much closer to the clang driver's.

Based on a patch by Zahira Ammarguellat:
  https://reviews.llvm.org/D39063

I've tried to move the logic that Zahira added into MicrosoftCXXABI.cpp.
There is only one ABI-specific detail sticking out, and that is in
CodeGenModule::getAddrOfCXXStructor, where we collapse complete dtors to
base dtors in the MS ABI.

This fixes PR32990.

Reviewers: erichkeane, zahiraam, majnemer, rjmccall

Subscribers: cfe-commits

Differential Revision: https://reviews.llvm.org/D44505

llvm-svn: 327732
parent 5ba2fe37
Loading
Loading
Loading
Loading
+5 −0
Original line number Diff line number Diff line
@@ -242,6 +242,11 @@ llvm::Constant *CodeGenModule::getAddrOfCXXStructor(
  if (auto *CD = dyn_cast<CXXConstructorDecl>(MD)) {
    GD = GlobalDecl(CD, toCXXCtorType(Type));
  } else {
    // Always alias equivalent complete destructors to base destructors in the
    // MS ABI.
    if (getTarget().getCXXABI().isMicrosoft() &&
        Type == StructorType::Complete && MD->getParent()->getNumVBases() == 0)
      Type = StructorType::Base;
    GD = GlobalDecl(cast<CXXDestructorDecl>(MD), toCXXDtorType(Type));
  }

+14 −0
Original line number Diff line number Diff line
@@ -287,6 +287,20 @@ CGCXXABI::EmitCtorCompleteObjectHandler(CodeGenFunction &CGF,
  return nullptr;
}

void CGCXXABI::setCXXDestructorDLLStorage(llvm::GlobalValue *GV,
                                          const CXXDestructorDecl *Dtor,
                                          CXXDtorType DT) const {
  // Assume the base C++ ABI has no special rules for destructor variants.
  CGM.setDLLImportDLLExport(GV, Dtor);
}

llvm::GlobalValue::LinkageTypes CGCXXABI::getCXXDestructorLinkage(
    GVALinkage Linkage, const CXXDestructorDecl *Dtor, CXXDtorType DT) const {
  // Delegate back to CGM by default.
  return CGM.getLLVMLinkageForDeclarator(Dtor, Linkage,
                                         /*isConstantVariable=*/false);
}

bool CGCXXABI::NeedsVTTParameter(GlobalDecl GD) {
  return false;
}
+8 −0
Original line number Diff line number Diff line
@@ -319,6 +319,14 @@ public:
  virtual bool useThunkForDtorVariant(const CXXDestructorDecl *Dtor,
                                      CXXDtorType DT) const = 0;

  virtual void setCXXDestructorDLLStorage(llvm::GlobalValue *GV,
                                          const CXXDestructorDecl *Dtor,
                                          CXXDtorType DT) const;

  virtual llvm::GlobalValue::LinkageTypes
  getCXXDestructorLinkage(GVALinkage Linkage, const CXXDestructorDecl *Dtor,
                          CXXDtorType DT) const;

  /// Emit destructor variants required by this ABI.
  virtual void EmitCXXDestructors(const CXXDestructorDecl *D) = 0;

+5 −13
Original line number Diff line number Diff line
@@ -786,13 +786,11 @@ void CodeGenModule::setDSOLocal(llvm::GlobalValue *GV) const {
void CodeGenModule::setDLLImportDLLExport(llvm::GlobalValue *GV,
                                          GlobalDecl GD) const {
  const auto *D = dyn_cast<NamedDecl>(GD.getDecl());
  // C++ destructors have a few C++ ABI specific special cases.
  if (const auto *Dtor = dyn_cast_or_null<CXXDestructorDecl>(D)) {
    if (getCXXABI().useThunkForDtorVariant(Dtor, GD.getDtorType())) {
      // Don't dllexport/import destructor thunks.
      GV->setDLLStorageClass(llvm::GlobalValue::DefaultStorageClass);
    getCXXABI().setCXXDestructorDLLStorage(GV, Dtor, GD.getDtorType());
    return;
  }
  }
  setDLLImportDLLExport(GV, D);
}

@@ -1074,14 +1072,8 @@ CodeGenModule::getFunctionLinkage(GlobalDecl GD) {

  GVALinkage Linkage = getContext().GetGVALinkageForFunction(D);

  if (isa<CXXDestructorDecl>(D) &&
      getCXXABI().useThunkForDtorVariant(cast<CXXDestructorDecl>(D),
                                         GD.getDtorType())) {
    // Destructor variants in the Microsoft C++ ABI are always internal or
    // linkonce_odr thunks emitted on an as-needed basis.
    return Linkage == GVA_Internal ? llvm::GlobalValue::InternalLinkage
                                   : llvm::GlobalValue::LinkOnceODRLinkage;
  }
  if (const auto *Dtor = dyn_cast<CXXDestructorDecl>(D))
    return getCXXABI().getCXXDestructorLinkage(Linkage, Dtor, GD.getDtorType());

  if (isa<CXXConstructorDecl>(D) &&
      cast<CXXConstructorDecl>(D)->isInheritingConstructor() &&
+66 −13
Original line number Diff line number Diff line
@@ -216,6 +216,14 @@ public:
    return DT != Dtor_Base;
  }

  void setCXXDestructorDLLStorage(llvm::GlobalValue *GV,
                                  const CXXDestructorDecl *Dtor,
                                  CXXDtorType DT) const override;

  llvm::GlobalValue::LinkageTypes
  getCXXDestructorLinkage(GVALinkage Linkage, const CXXDestructorDecl *Dtor,
                          CXXDtorType DT) const override;

  void EmitCXXDestructors(const CXXDestructorDecl *D) override;

  const CXXRecordDecl *
@@ -1310,6 +1318,52 @@ MicrosoftCXXABI::buildStructorSignature(const CXXMethodDecl *MD, StructorType T,
  return Added;
}

void MicrosoftCXXABI::setCXXDestructorDLLStorage(llvm::GlobalValue *GV,
                                                 const CXXDestructorDecl *Dtor,
                                                 CXXDtorType DT) const {
  // Deleting destructor variants are never imported or exported. Give them the
  // default storage class.
  if (DT == Dtor_Deleting) {
    GV->setDLLStorageClass(llvm::GlobalValue::DefaultStorageClass);
  } else {
    const NamedDecl *ND = Dtor;
    CGM.setDLLImportDLLExport(GV, ND);
  }
}

llvm::GlobalValue::LinkageTypes MicrosoftCXXABI::getCXXDestructorLinkage(
    GVALinkage Linkage, const CXXDestructorDecl *Dtor, CXXDtorType DT) const {
  // Internal things are always internal, regardless of attributes. After this,
  // we know the thunk is externally visible.
  if (Linkage == GVA_Internal)
    return llvm::GlobalValue::InternalLinkage;

  switch (DT) {
  case Dtor_Base:
    // The base destructor most closely tracks the user-declared constructor, so
    // we delegate back to the normal declarator case.
    return CGM.getLLVMLinkageForDeclarator(Dtor, Linkage,
                                           /*isConstantVariable=*/false);
  case Dtor_Complete:
    // The complete destructor is like an inline function, but it may be
    // imported and therefore must be exported as well. This requires changing
    // the linkage if a DLL attribute is present.
    if (Dtor->hasAttr<DLLExportAttr>())
      return llvm::GlobalValue::WeakODRLinkage;
    if (Dtor->hasAttr<DLLImportAttr>())
      return llvm::GlobalValue::AvailableExternallyLinkage;
    return llvm::GlobalValue::LinkOnceODRLinkage;
  case Dtor_Deleting:
    // Deleting destructors are like inline functions. They have vague linkage
    // and are emitted everywhere they are used. They are internal if the class
    // is internal.
    return llvm::GlobalValue::LinkOnceODRLinkage;
  case Dtor_Comdat:
    llvm_unreachable("MS C++ ABI does not support comdat dtors");
  }
  llvm_unreachable("invalid dtor type");
}

void MicrosoftCXXABI::EmitCXXDestructors(const CXXDestructorDecl *D) {
  // The TU defining a dtor is only guaranteed to emit a base destructor.  All
  // other destructor variants are delegating thunks.
@@ -1549,6 +1603,12 @@ void MicrosoftCXXABI::EmitDestructorCall(CodeGenFunction &CGF,
                                         const CXXDestructorDecl *DD,
                                         CXXDtorType Type, bool ForVirtualBase,
                                         bool Delegating, Address This) {
  // Use the base destructor variant in place of the complete destructor variant
  // if the class has no virtual bases. This effectively implements some of the
  // -mconstructor-aliases optimization, but as part of the MS C++ ABI.
  if (Type == Dtor_Complete && DD->getParent()->getNumVBases() == 0)
    Type = Dtor_Base;

  CGCallee Callee = CGCallee::forDirect(
                          CGM.getAddrOfCXXStructor(DD, getFromDtorType(Type)),
                                        DD);
@@ -3821,19 +3881,12 @@ static void emitCXXConstructor(CodeGenModule &CGM,

static void emitCXXDestructor(CodeGenModule &CGM, const CXXDestructorDecl *dtor,
                              StructorType dtorType) {
  // The complete destructor is equivalent to the base destructor for
  // classes with no virtual bases, so try to emit it as an alias.
  if (!dtor->getParent()->getNumVBases() &&
      (dtorType == StructorType::Complete || dtorType == StructorType::Base)) {
    bool ProducedAlias = !CGM.TryEmitDefinitionAsAlias(
        GlobalDecl(dtor, Dtor_Complete), GlobalDecl(dtor, Dtor_Base));
    if (ProducedAlias) {
      if (dtorType == StructorType::Complete)
        return;
      if (dtor->isVirtual())
        CGM.getVTables().EmitThunks(GlobalDecl(dtor, Dtor_Complete));
    }
  }
  // Emit the base destructor if the base and complete (vbase) destructors are
  // equivalent. This effectively implements -mconstructor-aliases as part of
  // the ABI.
  if (dtorType == StructorType::Complete &&
      dtor->getParent()->getNumVBases() == 0)
    dtorType = StructorType::Base;

  // The base destructor is equivalent to the base destructor of its
  // base class if there is exactly one non-virtual base class with a
Loading