Hi rnk,

Hi Reid,

This is a preliminary patch that makes clang-cl pass a few sanity tests that 
require thunks for multiple and/or virtual inheritance.

The patch still fails one test [strange, I think I've fixed that one already] I 
hope to get the patch that passes "check-clang" tomorrow.

In the meantime, can you please take a look at the general direction? 

--
Timur

http://llvm-reviews.chandlerc.com/D1787

Files:
  include/clang/Basic/ABI.h
  lib/AST/MicrosoftMangle.cpp
  lib/AST/VTableBuilder.cpp
  lib/CodeGen/CGCXXABI.h
  lib/CodeGen/CGVTables.cpp
  lib/CodeGen/ItaniumCXXABI.cpp
  lib/CodeGen/MicrosoftCXXABI.cpp
  test/CodeGenCXX/destructor-exception-spec.cpp
  test/CodeGenCXX/microsoft-abi-thunks.cpp
  test/CodeGenCXX/microsoft-abi-vtables-virtual-inheritance.cpp
Index: include/clang/Basic/ABI.h
===================================================================
--- include/clang/Basic/ABI.h
+++ include/clang/Basic/ABI.h
@@ -103,10 +103,14 @@
   /// \brief The return adjustment.
   ReturnAdjustment Return;
 
-  ThunkInfo() { }
+  /// \brief Optional ABI-specific data.
+  const void *OpaqueData;
 
-  ThunkInfo(const ThisAdjustment &This, const ReturnAdjustment &Return)
-    : This(This), Return(Return) { }
+  ThunkInfo() : OpaqueData(0) { }
+
+  ThunkInfo(const ThisAdjustment &This, const ReturnAdjustment &Return,
+            const void *OpaqueData = 0)
+      : This(This), Return(Return), OpaqueData(OpaqueData) {}
 
   friend bool operator==(const ThunkInfo &LHS, const ThunkInfo &RHS) {
     return LHS.This == RHS.This && LHS.Return == RHS.Return;
Index: lib/AST/MicrosoftMangle.cpp
===================================================================
--- lib/AST/MicrosoftMangle.cpp
+++ lib/AST/MicrosoftMangle.cpp
@@ -15,6 +15,7 @@
 #include "clang/AST/ASTContext.h"
 #include "clang/AST/Attr.h"
 #include "clang/AST/CharUnits.h"
+#include "clang/AST/CXXInheritance.h"
 #include "clang/AST/Decl.h"
 #include "clang/AST/DeclCXX.h"
 #include "clang/AST/DeclObjC.h"
@@ -1417,6 +1418,7 @@
   if (const CXXMethodDecl *MD = dyn_cast<CXXMethodDecl>(FD)) {
     switch (MD->getAccess()) {
       default:
+        llvm_unreachable("Unsupported access specifier");
       case AS_private:
         if (MD->isStatic())
           Out << 'C';
@@ -1869,36 +1871,75 @@
   return Mangler.mangle(D);
 }
 
+static void mangleThunkThisAdjustment(const CXXMethodDecl *MD,
+                                      const ThisAdjustment &Adjustment,
+                                      MicrosoftCXXNameMangler &Mangler,
+                                      raw_ostream &Out) {
+  // FIXME: add support for vtordisp thunks.
+  if (Adjustment.NonVirtual != 0) {
+    switch (MD->getAccess()) {
+    default:
+      llvm_unreachable("Unsupported access specifier");
+    case AS_private:
+      Out << "G";
+      break;
+    case AS_protected:
+      Out << "O";
+      break;
+    case AS_public:
+      Out << "W";
+    }
+    llvm::APSInt APSNumber(/*BitWidth=*/32, /*isUnsigned=*/true);
+    APSNumber = -Adjustment.NonVirtual;
+    Mangler.mangleNumber(APSNumber);
+  } else {
+    switch (MD->getAccess()) {
+    default:
+      llvm_unreachable("Unsupported access specifier");
+    case AS_private:
+      Out << "A";
+      break;
+    case AS_protected:
+      Out << "I";
+      break;
+    case AS_public:
+      Out << "Q";
+    }
+  }
+}
+
 void MicrosoftMangleContext::mangleThunk(const CXXMethodDecl *MD,
                                          const ThunkInfo &Thunk,
                                          raw_ostream &Out) {
-  // FIXME: this is not yet a complete implementation, but merely a
-  // reasonably-working stub to avoid crashing when required to emit a thunk.
   MicrosoftCXXNameMangler Mangler(*this, Out);
   Out << "\01?";
   Mangler.mangleName(MD);
-  if (Thunk.This.NonVirtual != 0) {
-    // FIXME: add support for protected/private or use mangleFunctionClass.
-    Out << "W";
-    llvm::APSInt APSNumber(/*BitWidth=*/32 /*FIXME: check on x64*/,
-                           /*isUnsigned=*/true);
-    APSNumber = -Thunk.This.NonVirtual;
-    Mangler.mangleNumber(APSNumber);
+  mangleThunkThisAdjustment(MD, Thunk.This, Mangler, Out);
+  const CXXMethodDecl *DeclForFPT;
+  if (Thunk.Return.isEmpty()) {
+    DeclForFPT = MD;
   } else {
-    // FIXME: add support for protected/private or use mangleFunctionClass.
-    Out << "Q";
+    assert(Thunk.OpaqueData != 0 &&
+           "Thunk info should hold the overridee decl");
+    DeclForFPT = static_cast<const CXXMethodDecl*>(Thunk.OpaqueData);
   }
-  // FIXME: mangle return adjustment? Most likely includes using an overridee FPT?
-  Mangler.mangleFunctionType(MD->getType()->castAs<FunctionProtoType>(), MD, false, true);
-}
-
-void MicrosoftMangleContext::mangleCXXDtorThunk(const CXXDestructorDecl *DD,
-                                                CXXDtorType Type,
-                                                const ThisAdjustment &,
-                                                raw_ostream &) {
-  unsigned DiagID = getDiags().getCustomDiagID(DiagnosticsEngine::Error,
-    "cannot mangle thunk for this destructor yet");
-  getDiags().Report(DD->getLocation(), DiagID);
+  Mangler.mangleFunctionType(DeclForFPT->getType()->castAs<FunctionProtoType>(),
+                             MD, false, true);
+}
+
+void MicrosoftMangleContext::mangleCXXDtorThunk(
+    const CXXDestructorDecl *DD, CXXDtorType Type,
+    const ThisAdjustment &Adjustment, raw_ostream &Out) {
+  // FIXME: Actually, the dtor thunk should be emitted for vector deleting
+  // dtors rather than scalar deleting dtors. Just use the vector deleting dtor
+  // mangling manually until we support both deleting dtor types.
+  assert(Type == Dtor_Deleting);
+  MicrosoftCXXNameMangler Mangler(*this, Out, DD, Type);
+  Out << "\01??_E";
+  Mangler.mangleName(DD->getParent());
+  mangleThunkThisAdjustment(DD, Adjustment, Mangler, Out);
+  Mangler.mangleFunctionType(DD->getType()->castAs<FunctionProtoType>(),
+                             DD, true, true);
 }
 
 void MicrosoftMangleContext::mangleCXXVTable(const CXXRecordDecl *RD,
Index: lib/AST/VTableBuilder.cpp
===================================================================
--- lib/AST/VTableBuilder.cpp
+++ lib/AST/VTableBuilder.cpp
@@ -992,6 +992,7 @@
     MostDerivedClassIsVirtual(MostDerivedClassIsVirtual), 
     LayoutClass(LayoutClass), Context(MostDerivedClass->getASTContext()), 
     Overriders(MostDerivedClass, MostDerivedClassOffset, LayoutClass) {
+    assert(!Context.getTargetInfo().getCXXABI().isMicrosoft());
 
     LayoutVTable();
 
@@ -2520,18 +2521,14 @@
 
   /// AddMethod - Add a single virtual member function to the vftable
   /// components vector.
-  void AddMethod(const CXXMethodDecl *MD, ThisAdjustment ThisAdjustment,
-                 ReturnAdjustment ReturnAdjustment) {
+  void AddMethod(const CXXMethodDecl *MD, ThunkInfo TI) {
     if (const CXXDestructorDecl *DD = dyn_cast<CXXDestructorDecl>(MD)) {
-      assert(ReturnAdjustment.isEmpty() &&
+      assert(TI.Return.isEmpty() &&
              "Destructor can't have return adjustment!");
       Components.push_back(VTableComponent::MakeDeletingDtor(DD));
     } else {
-      // Add the return adjustment if necessary.
-      if (!ReturnAdjustment.isEmpty() || !ThisAdjustment.isEmpty()) {
-        VTableThunks[Components.size()].Return = ReturnAdjustment;
-        VTableThunks[Components.size()].This = ThisAdjustment;
-      }
+      if (!TI.isEmpty())
+        VTableThunks[Components.size()] = TI;
       Components.push_back(VTableComponent::MakeFunction(MD));
     }
   }
@@ -2886,7 +2883,8 @@
       }
     }
 
-    AddMethod(Overrider.Method, ThisAdjustmentOffset, ReturnAdjustment);
+    AddMethod(Overrider.Method,
+              ThunkInfo(ThisAdjustmentOffset, ReturnAdjustment, MD));
   }
 }
 
Index: lib/CodeGen/CGCXXABI.h
===================================================================
--- lib/CodeGen/CGCXXABI.h
+++ lib/CodeGen/CGCXXABI.h
@@ -339,11 +339,22 @@
                                          SourceLocation CallLoc,
                                          llvm::Value *This) = 0;
 
+  virtual void adjustCallArgsForDestructorThunk(CodeGenFunction &CGF,
+                                                GlobalDecl GD,
+                                                CallArgList &CallArgs) {}
+
   /// Emit any tables needed to implement virtual inheritance.  For Itanium,
   /// this emits virtual table tables.  For the MSVC++ ABI, this emits virtual
   /// base tables.
   virtual void emitVirtualInheritanceTables(const CXXRecordDecl *RD) = 0;
 
+  /// If true, this ABI expects thunks to be emitted even for virtual functions
+  /// defined in other TUs.
+  virtual bool shouldTryToEmitThunksForExternalVirtualFunctions() = 0;
+
+  virtual void setThunkLinkage(llvm::Function *Thunk,
+                               bool AvailableExternally) = 0;
+
   virtual void EmitReturnFromThunk(CodeGenFunction &CGF,
                                    RValue RV, QualType ResultType);
 
Index: lib/CodeGen/CGVTables.cpp
===================================================================
--- lib/CodeGen/CGVTables.cpp
+++ lib/CodeGen/CGVTables.cpp
@@ -333,6 +333,9 @@
   // Add our adjusted 'this' pointer.
   CallArgs.add(RValue::get(AdjustedThisPtr), ThisType);
 
+  if (isa<CXXDestructorDecl>(MD))
+    CGM.getCXXABI().adjustCallArgsForDestructorThunk(*this, GD, CallArgs);
+
   // Add the rest of the parameters.
   for (FunctionDecl::param_const_iterator I = MD->param_begin(),
        E = MD->param_end(); I != E; ++I) {
@@ -393,11 +396,6 @@
 void CodeGenVTables::EmitThunk(GlobalDecl GD, const ThunkInfo &Thunk, 
                                bool UseAvailableExternallyLinkage)
 {
-  if (CGM.getTarget().getCXXABI().isMicrosoft()) {
-    // Emission of thunks is not supported yet in Microsoft ABI.
-    return;
-  }
-
   const CGFunctionInfo &FnInfo = CGM.getTypes().arrangeGlobalDeclaration(GD);
 
   // FIXME: re-use FnInfo in this computation.
@@ -466,15 +464,17 @@
     CodeGenFunction(CGM).GenerateThunk(ThunkFn, FnInfo, GD, Thunk);
   }
 
-  if (UseAvailableExternallyLinkage)
-    ThunkFn->setLinkage(llvm::GlobalValue::AvailableExternallyLinkage);
+  CGM.getCXXABI().setThunkLinkage(ThunkFn, UseAvailableExternallyLinkage);
 }
 
 void CodeGenVTables::MaybeEmitThunkAvailableExternally(GlobalDecl GD,
                                                        const ThunkInfo &Thunk) {
-  // We only want to do this when building with optimizations.
-  if (!CGM.getCodeGenOpts().OptimizationLevel)
+  // We only want to do this when building with optimizations,
+  // unless ABI wants otherwise.
+  if (!CGM.getCXXABI().shouldTryToEmitThunksForExternalVirtualFunctions() &&
+      !CGM.getCodeGenOpts().OptimizationLevel) {
     return;
+  }
 
   // We can't emit thunks for member functions with incomplete types.
   const CXXMethodDecl *MD = cast<CXXMethodDecl>(GD.getDecl());
@@ -494,21 +494,24 @@
   if (isa<CXXDestructorDecl>(MD) && GD.getDtorType() == Dtor_Base)
     return;
 
+  const VTableContext::ThunkInfoVectorTy *ThunkInfoVector;
   if (VFTContext.isValid()) {
-    // FIXME: This is a temporary solution to force generation of vftables in
-    // Microsoft ABI. Remove when we thread VFTableContext through CodeGen.
-    VFTContext->getVFPtrOffsets(MD->getParent());
-    return;
+    // Don't need to generate thunks for complete destructor in this ABI.
+    if (isa<CXXDestructorDecl>(MD) && GD.getDtorType() == Dtor_Complete)
+      return;
+    ThunkInfoVector = VFTContext->getThunkInfo(MD);
+  } else {
+    ThunkInfoVector = VTContext.getThunkInfo(MD);
   }
 
-  const VTableContext::ThunkInfoVectorTy *ThunkInfoVector =
-    VTContext.getThunkInfo(MD);
   if (!ThunkInfoVector)
     return;
 
-  for (unsigned I = 0, E = ThunkInfoVector->size(); I != E; ++I)
-    EmitThunk(GD, (*ThunkInfoVector)[I],
-              /*UseAvailableExternallyLinkage=*/false);
+  for (unsigned I = 0, E = ThunkInfoVector->size(); I != E; ++I) {
+    bool AvailableExternally =
+        CGM.getCXXABI().shouldTryToEmitThunksForExternalVirtualFunctions();
+    EmitThunk(GD, (*ThunkInfoVector)[I], AvailableExternally);
+  }
 }
 
 llvm::Constant *
Index: lib/CodeGen/ItaniumCXXABI.cpp
===================================================================
--- lib/CodeGen/ItaniumCXXABI.cpp
+++ lib/CodeGen/ItaniumCXXABI.cpp
@@ -169,6 +169,13 @@
 
   void emitVirtualInheritanceTables(const CXXRecordDecl *RD);
 
+  bool shouldTryToEmitThunksForExternalVirtualFunctions() { return false; }
+
+  void setThunkLinkage(llvm::Function *Thunk, bool AvailableExternally) {
+    if (AvailableExternally)
+      Thunk->setLinkage(llvm::GlobalValue::AvailableExternallyLinkage);
+  }
+
   StringRef GetPureVirtualCallName() { return "__cxa_pure_virtual"; }
   StringRef GetDeletedVirtualCallName() { return "__cxa_deleted_virtual"; }
 
Index: lib/CodeGen/MicrosoftCXXABI.cpp
===================================================================
--- lib/CodeGen/MicrosoftCXXABI.cpp
+++ lib/CodeGen/MicrosoftCXXABI.cpp
@@ -173,8 +173,23 @@
                                  CXXDtorType DtorType, SourceLocation CallLoc,
                                  llvm::Value *This);
 
+  void adjustCallArgsForDestructorThunk(CodeGenFunction &CGF, GlobalDecl GD,
+                                        CallArgList &CallArgs) {
+    assert(GD.getDtorType() == Dtor_Deleting &&
+           "Only deleting destructor thunks are available in this ABI");
+    CallArgs.add(RValue::get(getStructorImplicitParamValue(CGF)),
+                             CGM.getContext().IntTy);
+  }
+
   void emitVirtualInheritanceTables(const CXXRecordDecl *RD);
 
+  bool shouldTryToEmitThunksForExternalVirtualFunctions() { return true; }
+
+  void setThunkLinkage(llvm::Function *Thunk, bool AvailableExternally) {
+    assert(AvailableExternally && "All thunks should be available externally");
+    Thunk->setLinkage(llvm::GlobalValue::WeakAnyLinkage);
+  }
+
   void EmitGuardedInit(CodeGenFunction &CGF, const VarDecl &D,
                        llvm::GlobalVariable *DeclPtr,
                        bool PerformInit);
Index: test/CodeGenCXX/destructor-exception-spec.cpp
===================================================================
--- test/CodeGenCXX/destructor-exception-spec.cpp
+++ test/CodeGenCXX/destructor-exception-spec.cpp
@@ -1,4 +1,5 @@
 // RUN: %clang_cc1 -emit-llvm-only %s -std=c++11
+// RUN: %clang_cc1 -cxx-abi microsoft -emit-llvm-only %s -std=c++11
 
 // PR13479: don't crash with -fno-exceptions.
 namespace {
Index: test/CodeGenCXX/microsoft-abi-thunks.cpp
===================================================================
--- /dev/null
+++ test/CodeGenCXX/microsoft-abi-thunks.cpp
@@ -0,0 +1,96 @@
+// RUN: %clang_cc1 -fno-rtti -emit-llvm %s -o - -cxx-abi microsoft -triple=i386-pc-win32 >%t 2>&1
+// RUN: FileCheck --check-prefix=MANGLING %s < %t
+// RUN: FileCheck --check-prefix=CODEGEN %s < %t
+// RUN: %clang_cc1 -fno-rtti -emit-llvm %s -o - -cxx-abi microsoft -triple=x86_64-pc-win32 2>&1 | FileCheck --check-prefix=MANGLING-X64 %s
+
+void foo(void *);
+
+struct A {
+  virtual ~A();
+  virtual void public_f();
+ protected:
+  virtual void protected_f();
+ private:
+  virtual void private_f();
+};
+
+struct B {
+  virtual ~B();
+  virtual void public_f();
+ protected:
+  virtual void protected_f();
+ private:
+  virtual void private_f();
+};
+
+
+struct C : A, B {
+  C();
+
+  virtual ~C();
+  // MANGLING-DAG: @"\01??1C@@UAE@XZ"
+  // MANGLING-DAG: @"\01??_GC@@UAEPAXI@Z"
+  // MANGLING-DAG: @"\01??_EC@@W3AEPAXI@Z"
+  // MANGLING-X64-DAG: @"\01??1C@@UEAA@XZ"
+  // MANGLING-X64-DAG: @"\01??_EC@@W7EAAPEAXI@Z"
+
+  // Overrides public_f() of two subobjects with distinct vfptrs, thus needs a thunk.
+  virtual void public_f();
+  // MANGLING-DAG: @"\01?public_f@C@@UAEXXZ"
+  // MANGLING-DAG: @"\01?public_f@C@@W3AEXXZ"
+  // MANGLING-X64-DAG: @"\01?public_f@C@@UEAAXXZ"
+  // MANGLING-X64-DAG: @"\01?public_f@C@@W7EAAXXZ"
+ protected:
+  virtual void protected_f();
+  // MANGLING-DAG: @"\01?protected_f@C@@O3AEXXZ"
+  // MANGLING-X64-DAG: @"\01?protected_f@C@@O7EAAXXZ"
+
+ private:
+  virtual void private_f();
+  // MANGLING-DAG: @"\01?private_f@C@@G3AEXXZ"
+  // MANGLING-X64-DAG: @"\01?private_f@C@@G7EAAXXZ"
+};
+
+C::C() {}  // Emits vftable and forces thunk generation.
+
+// CODEGEN: define weak x86_thiscallcc void @"\01??_EC@@W3AEPAXI@Z"(%struct.C* %this, i32 %should_call_delete)
+// CODEGEN:   getelementptr inbounds i8* {{.*}}, i64 -4
+// FIXME: should actually call _EC, not _GC.
+// CODEGEN:   call x86_thiscallcc void @"\01??_GC@@UAEPAXI@Z"
+// CODEGEN: ret
+
+// CODEGEN: define weak x86_thiscallcc void @"\01?public_f@C@@W3AEXXZ"(%struct.C*
+// CODEGEN:   getelementptr inbounds i8* {{.*}}, i64 -4
+// CODEGEN:   call x86_thiscallcc void @"\01?public_f@C@@UAEXXZ"(%struct.C*
+// CODEGEN: ret
+
+void zoo(C* obj) {
+  delete obj;
+}
+
+struct D {
+  virtual B* goo();
+};
+
+struct E : D {
+  virtual C* goo();
+  // MANGLING-DAG: @"\01?goo@E@@UAEPAUC@@XZ"
+  // MANGLING-DAG: @"\01?goo@E@@QAEPAUB@@XZ"
+  // MANGLING-X64-DAG: @"\01?goo@E@@UEAAPEAUC@@XZ"
+  // MANGLING-X64-DAG: @"\01?goo@E@@QEAAPEAUB@@XZ"
+};
+
+E e;  // Emits vftable and forces thunk generation.
+
+// CODEGEN: define weak x86_thiscallcc %struct.C* @"\01?goo@E@@QAEPAUB@@XZ"
+// CODEGEN:   call x86_thiscallcc %struct.C* @"\01?goo@E@@UAEPAUC@@XZ"
+// CODEGEN:   getelementptr inbounds i8* {{.*}}, i64 4
+// CODEGEN: ret
+
+struct F : virtual A, virtual B {
+  virtual ~F();
+};
+
+F f;  // Just make sure we don't crash, e.g. mangling the complete dtor.
+
+// FIXME: Write vtordisp adjusting thunk tests
Index: test/CodeGenCXX/microsoft-abi-vtables-virtual-inheritance.cpp
===================================================================
--- test/CodeGenCXX/microsoft-abi-vtables-virtual-inheritance.cpp
+++ test/CodeGenCXX/microsoft-abi-vtables-virtual-inheritance.cpp
@@ -154,6 +154,9 @@
   // TEST4-NOT: VFTable indices for 'Test4::X'
 
   // MANGLING-DAG: @"\01??_7X@Test4@@6B@"
+
+  // Also check the mangling of the thunk.
+  // MANGLING-DAG: define weak x86_thiscallcc void @"\01?f@C@@WPPPPPPPE@AEXXZ"
 };
 
 X x;
_______________________________________________
cfe-commits mailing list
[email protected]
http://lists.cs.uiuc.edu/mailman/listinfo/cfe-commits

Reply via email to