r194132 - Fix PR17738 - add support for vtordisp thunks when using -cxx-abi microsoft

Timur Iskhodzhanov timurrrr at google.com
Tue Nov 5 22:24:32 PST 2013


Author: timurrrr
Date: Wed Nov  6 00:24:31 2013
New Revision: 194132

URL: http://llvm.org/viewvc/llvm-project?rev=194132&view=rev
Log:
Fix PR17738 - add support for vtordisp thunks when using -cxx-abi microsoft

Added:
    cfe/trunk/test/CodeGenCXX/microsoft-abi-virtual-inheritance-vtordisps.cpp
    cfe/trunk/test/CodeGenCXX/microsoft-abi-vtables-virtual-inheritance-vtordisps.cpp
Modified:
    cfe/trunk/include/clang/Basic/ABI.h
    cfe/trunk/lib/AST/ItaniumMangle.cpp
    cfe/trunk/lib/AST/MicrosoftMangle.cpp
    cfe/trunk/lib/AST/VTableBuilder.cpp
    cfe/trunk/lib/CodeGen/ItaniumCXXABI.cpp
    cfe/trunk/lib/CodeGen/MicrosoftCXXABI.cpp
    cfe/trunk/test/CodeGenCXX/microsoft-abi-thunks.cpp

Modified: cfe/trunk/include/clang/Basic/ABI.h
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/include/clang/Basic/ABI.h?rev=194132&r1=194131&r2=194132&view=diff
==============================================================================
--- cfe/trunk/include/clang/Basic/ABI.h (original)
+++ cfe/trunk/include/clang/Basic/ABI.h Wed Nov  6 00:24:31 2013
@@ -106,18 +106,53 @@ struct ThisAdjustment {
   /// nearest virtual base.
   int64_t NonVirtual;
 
-  /// \brief The offset (in bytes), relative to the address point,
-  /// of the virtual call offset.
-  int64_t VCallOffsetOffset;
+  /// \brief Holds the ABI-specific information about the virtual this
+  /// adjustment, if needed.
+  union VirtualAdjustment {
+    // Itanium ABI
+    struct {
+      /// \brief The offset (in bytes), relative to the address point,
+      /// of the virtual call offset.
+      int64_t VCallOffsetOffset;
+    } Itanium;
+
+    struct {
+      /// \brief The offset of the vtordisp (in bytes), relative to the ECX.
+      int32_t VtordispOffset;
+
+      /// \brief The offset of the vbptr of the derived class (in bytes),
+      /// relative to the ECX after vtordisp adjustment.
+      int32_t VBPtrOffset;
+
+      /// \brief The offset (in bytes) of the vbase offset in the vbtable.
+      int32_t VBOffsetOffset;
+    } Microsoft;
+
+    VirtualAdjustment() {
+      memset(this, 0, sizeof(*this));
+    }
+
+    bool Equals(const VirtualAdjustment &Other) const {
+      return memcmp(this, &Other, sizeof(Other)) == 0;
+    }
+
+    bool isEmpty() const {
+      VirtualAdjustment Zero;
+      return Equals(Zero);
+    }
+
+    bool Less(const VirtualAdjustment &RHS) const {
+      return memcmp(this, &RHS, sizeof(RHS)) < 0;
+    }
+  } Virtual;
   
-  ThisAdjustment() : NonVirtual(0), VCallOffsetOffset(0) { }
+  ThisAdjustment() : NonVirtual(0) { }
 
-  bool isEmpty() const { return !NonVirtual && !VCallOffsetOffset; }
+  bool isEmpty() const { return !NonVirtual && Virtual.isEmpty(); }
 
   friend bool operator==(const ThisAdjustment &LHS, 
                          const ThisAdjustment &RHS) {
-    return LHS.NonVirtual == RHS.NonVirtual && 
-      LHS.VCallOffsetOffset == RHS.VCallOffsetOffset;
+    return LHS.NonVirtual == RHS.NonVirtual && LHS.Virtual.Equals(RHS.Virtual);
   }
 
   friend bool operator!=(const ThisAdjustment &LHS, const ThisAdjustment &RHS) {
@@ -129,8 +164,7 @@ struct ThisAdjustment {
     if (LHS.NonVirtual < RHS.NonVirtual)
       return true;
     
-    return LHS.NonVirtual == RHS.NonVirtual && 
-      LHS.VCallOffsetOffset < RHS.VCallOffsetOffset;
+    return LHS.NonVirtual == RHS.NonVirtual && LHS.Virtual.Less(RHS.Virtual);
   }
 };
 

Modified: cfe/trunk/lib/AST/ItaniumMangle.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/AST/ItaniumMangle.cpp?rev=194132&r1=194131&r2=194132&view=diff
==============================================================================
--- cfe/trunk/lib/AST/ItaniumMangle.cpp (original)
+++ cfe/trunk/lib/AST/ItaniumMangle.cpp Wed Nov  6 00:24:31 2013
@@ -3657,8 +3657,9 @@ void ItaniumMangleContextImpl::mangleThu
     Mangler.getStream() << 'c';
   
   // Mangle the 'this' pointer adjustment.
-  Mangler.mangleCallOffset(Thunk.This.NonVirtual, Thunk.This.VCallOffsetOffset);
-  
+  Mangler.mangleCallOffset(Thunk.This.NonVirtual,
+                           Thunk.This.Virtual.Itanium.VCallOffsetOffset);
+
   // Mangle the return pointer adjustment if there is one.
   if (!Thunk.Return.isEmpty())
     Mangler.mangleCallOffset(Thunk.Return.NonVirtual,
@@ -3677,7 +3678,7 @@ void ItaniumMangleContextImpl::mangleCXX
 
   // Mangle the 'this' pointer adjustment.
   Mangler.mangleCallOffset(ThisAdjustment.NonVirtual, 
-                           ThisAdjustment.VCallOffsetOffset);
+                           ThisAdjustment.Virtual.Itanium.VCallOffsetOffset);
 
   Mangler.mangleFunctionEncoding(DD);
 }

Modified: cfe/trunk/lib/AST/MicrosoftMangle.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/AST/MicrosoftMangle.cpp?rev=194132&r1=194131&r2=194132&view=diff
==============================================================================
--- cfe/trunk/lib/AST/MicrosoftMangle.cpp (original)
+++ cfe/trunk/lib/AST/MicrosoftMangle.cpp Wed Nov  6 00:24:31 2013
@@ -121,7 +121,7 @@ public:
   void mangleDeclaration(const NamedDecl *ND);
   void mangleFunctionEncoding(const FunctionDecl *FD);
   void mangleVariableEncoding(const VarDecl *VD);
-  void mangleNumber(int64_t Number);
+  void mangleNumber(uint32_t Number);
   void mangleNumber(const llvm::APSInt &Value);
   void mangleType(QualType T, SourceRange Range,
                   QualifierMangleMode QMM = QMM_Mangle);
@@ -387,8 +387,8 @@ void MicrosoftCXXNameMangler::mangleName
   Out << '@';
 }
 
-void MicrosoftCXXNameMangler::mangleNumber(int64_t Number) {
-  llvm::APSInt APSNumber(/*BitWidth=*/64, /*isUnsigned=*/false);
+void MicrosoftCXXNameMangler::mangleNumber(uint32_t Number) {
+  llvm::APSInt APSNumber(/*BitWidth=*/32, /*isUnsigned=*/true);
   APSNumber = Number;
   mangleNumber(APSNumber);
 }
@@ -836,7 +836,7 @@ void MicrosoftCXXNameMangler::mangleLoca
   // functions. You could have a method baz of class C inside a function bar
   // inside a function foo, like so:
   // ?baz at C@?3??bar@?1??foo@@YAXXZ at YAXXZ@QAEXXZ
-  int NestLevel = getLocalNestingLevel(FD);
+  unsigned NestLevel = getLocalNestingLevel(FD);
   Out << '?';
   mangleNumber(NestLevel);
   Out << '?';
@@ -1367,24 +1367,18 @@ void MicrosoftCXXNameMangler::mangleFunc
   //                   ::= D # private: static far
   //                   ::= E # private: virtual near
   //                   ::= F # private: virtual far
-  //                   ::= G # private: thunk near
-  //                   ::= H # private: thunk far
   //                   ::= I # protected: near
   //                   ::= J # protected: far
   //                   ::= K # protected: static near
   //                   ::= L # protected: static far
   //                   ::= M # protected: virtual near
   //                   ::= N # protected: virtual far
-  //                   ::= O # protected: thunk near
-  //                   ::= P # protected: thunk far
   //                   ::= Q # public: near
   //                   ::= R # public: far
   //                   ::= S # public: static near
   //                   ::= T # public: static far
   //                   ::= U # public: virtual near
   //                   ::= V # public: virtual far
-  //                   ::= W # public: thunk near
-  //                   ::= X # public: thunk far
   // <global-function> ::= Y # global near
   //                   ::= Z # global far
   if (const CXXMethodDecl *MD = dyn_cast<CXXMethodDecl>(FD)) {
@@ -1843,12 +1837,61 @@ void MicrosoftMangleContextImpl::mangleC
   return Mangler.mangle(D);
 }
 
+// <this-adjustment> ::= <no-adjustment> | <static-adjustment> |
+//                       <virtual-adjustment>
+// <no-adjustment>      ::= A # private near
+//                      ::= B # private far
+//                      ::= I # protected near
+//                      ::= J # protected far
+//                      ::= Q # public near
+//                      ::= R # public far
+// <static-adjustment>  ::= G <static-offset> # private near
+//                      ::= H <static-offset> # private far
+//                      ::= O <static-offset> # protected near
+//                      ::= P <static-offset> # protected far
+//                      ::= W <static-offset> # public near
+//                      ::= X <static-offset> # public far
+// <virtual-adjustment> ::= $0 <virtual-shift> <static-offset> # private near
+//                      ::= $1 <virtual-shift> <static-offset> # private far
+//                      ::= $2 <virtual-shift> <static-offset> # protected near
+//                      ::= $3 <virtual-shift> <static-offset> # protected far
+//                      ::= $4 <virtual-shift> <static-offset> # public near
+//                      ::= $5 <virtual-shift> <static-offset> # public far
+// <virtual-shift>      ::= <vtordisp-shift> | <vtordispex-shift>
+// <vtordisp-shift>     ::= <offset-to-vtordisp>
+// <vtordispex-shift>   ::= <offset-to-vbptr> <vbase-offset-offset>
+//                          <offset-to-vtordisp>
 static void mangleThunkThisAdjustment(const CXXMethodDecl *MD,
                                       const ThisAdjustment &Adjustment,
                                       MicrosoftCXXNameMangler &Mangler,
                                       raw_ostream &Out) {
-  // FIXME: add support for vtordisp thunks.
-  if (Adjustment.NonVirtual != 0) {
+  if (!Adjustment.Virtual.isEmpty()) {
+    Out << '$';
+    char AccessSpec;
+    switch (MD->getAccess()) {
+    case AS_none:
+      llvm_unreachable("Unsupported access specifier");
+    case AS_private:
+      AccessSpec = '0';
+      break;
+    case AS_protected:
+      AccessSpec = '2';
+      break;
+    case AS_public:
+      AccessSpec = '4';
+    }
+    if (Adjustment.Virtual.Microsoft.VBPtrOffset) {
+      Out << 'R' << AccessSpec;
+      Mangler.mangleNumber(Adjustment.Virtual.Microsoft.VBPtrOffset);
+      Mangler.mangleNumber(Adjustment.Virtual.Microsoft.VBOffsetOffset);
+      Mangler.mangleNumber(Adjustment.Virtual.Microsoft.VtordispOffset);
+      Mangler.mangleNumber(Adjustment.NonVirtual);
+    } else {
+      Out << AccessSpec;
+      Mangler.mangleNumber(Adjustment.Virtual.Microsoft.VtordispOffset);
+      Mangler.mangleNumber(-Adjustment.NonVirtual);
+    }
+  } else if (Adjustment.NonVirtual != 0) {
     switch (MD->getAccess()) {
     case AS_none:
       llvm_unreachable("Unsupported access specifier");
@@ -1861,9 +1904,7 @@ static void mangleThunkThisAdjustment(co
     case AS_public:
       Out << 'W';
     }
-    llvm::APSInt APSNumber(/*BitWidth=*/32, /*isUnsigned=*/true);
-    APSNumber = -Adjustment.NonVirtual;
-    Mangler.mangleNumber(APSNumber);
+    Mangler.mangleNumber(-Adjustment.NonVirtual);
   } else {
     switch (MD->getAccess()) {
     case AS_none:

Modified: cfe/trunk/lib/AST/VTableBuilder.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/AST/VTableBuilder.cpp?rev=194132&r1=194131&r2=194132&view=diff
==============================================================================
--- cfe/trunk/lib/AST/VTableBuilder.cpp (original)
+++ cfe/trunk/lib/AST/VTableBuilder.cpp Wed Nov  6 00:24:31 2013
@@ -1304,7 +1304,7 @@ ThisAdjustment ItaniumVTableBuilder::Com
       VCallOffsets = Builder.getVCallOffsets();
     }
       
-    Adjustment.VCallOffsetOffset =
+    Adjustment.Virtual.Itanium.VCallOffsetOffset =
       VCallOffsets.getVCallOffsetOffset(MD).getQuantity();
   }
 
@@ -1552,7 +1552,7 @@ void ItaniumVTableBuilder::AddMethods(
             ComputeThisAdjustment(OverriddenMD, BaseOffsetInLayoutClass,
                                   Overrider);
 
-          if (ThisAdjustment.VCallOffsetOffset &&
+          if (ThisAdjustment.Virtual.Itanium.VCallOffsetOffset &&
               Overrider.Method->getParent() == MostDerivedClass) {
 
             // There's no return adjustment from OverriddenMD and MD,
@@ -2009,8 +2009,8 @@ void ItaniumVTableBuilder::dumpLayout(ra
           Out << "\n       [this adjustment: ";
           Out << Thunk.This.NonVirtual << " non-virtual";
           
-          if (Thunk.This.VCallOffsetOffset) {
-            Out << ", " << Thunk.This.VCallOffsetOffset;
+          if (Thunk.This.Virtual.Itanium.VCallOffsetOffset) {
+            Out << ", " << Thunk.This.Virtual.Itanium.VCallOffsetOffset;
             Out << " vcall offset offset";
           }
 
@@ -2044,8 +2044,8 @@ void ItaniumVTableBuilder::dumpLayout(ra
           Out << "\n       [this adjustment: ";
           Out << Thunk.This.NonVirtual << " non-virtual";
           
-          if (Thunk.This.VCallOffsetOffset) {
-            Out << ", " << Thunk.This.VCallOffsetOffset;
+          if (Thunk.This.Virtual.Itanium.VCallOffsetOffset) {
+            Out << ", " << Thunk.This.Virtual.Itanium.VCallOffsetOffset;
             Out << " vcall offset offset";
           }
           
@@ -2186,8 +2186,8 @@ void ItaniumVTableBuilder::dumpLayout(ra
           Out << "this adjustment: ";
           Out << Thunk.This.NonVirtual << " non-virtual";
           
-          if (Thunk.This.VCallOffsetOffset) {
-            Out << ", " << Thunk.This.VCallOffsetOffset;
+          if (Thunk.This.Virtual.Itanium.VCallOffsetOffset) {
+            Out << ", " << Thunk.This.Virtual.Itanium.VCallOffsetOffset;
             Out << " vcall offset offset";
           }
         }
@@ -2527,6 +2527,9 @@ private:
                               BaseSubobject Base,
                               FinalOverriders::OverriderInfo Overrider);
 
+  void CalculateVtordispAdjustment(FinalOverriders::OverriderInfo Overrider,
+                                   CharUnits ThisOffset, ThisAdjustment &TA);
+
   /// AddMethod - Add a single virtual member function to the vftable
   /// components vector.
   void AddMethod(const CXXMethodDecl *MD, ThunkInfo TI) {
@@ -2672,7 +2675,7 @@ VFTableBuilder::ComputeThisOffset(const
        I != E; ++I) {
     const CXXBasePath &Path = (*I);
     CharUnits ThisOffset = Base.getBaseOffset();
-    bool SeenVBase = false;
+    CharUnits LastVBaseOffset;
 
     // For each path from the overrider to the parents of the overridden methods,
     // traverse the path, calculating the this offset in the most derived class.
@@ -2684,7 +2687,7 @@ VFTableBuilder::ComputeThisOffset(const
       const ASTRecordLayout &Layout = Context.getASTRecordLayout(PrevRD);
 
       if (Element.Base->isVirtual()) {
-        SeenVBase = true;
+        LastVBaseOffset = MostDerivedClassLayout.getVBaseClassOffset(CurRD);
         if (Overrider.Method->getParent() == PrevRD) {
           // This one's interesting. If the final overrider is in a vbase B of the
           // most derived class and it overrides a method of the B's own vbase A,
@@ -2695,23 +2698,25 @@ VFTableBuilder::ComputeThisOffset(const
           // differently in the most derived class.
           ThisOffset += Layout.getVBaseClassOffset(CurRD);
         } else {
-          ThisOffset = MostDerivedClassLayout.getVBaseClassOffset(CurRD);
+          ThisOffset = LastVBaseOffset;
         }
-
-        // A virtual destructor of a virtual base takes the address of the
-        // virtual base subobject as the "this" argument.
-        if (isa<CXXDestructorDecl>(MD))
-          break;
       } else {
         ThisOffset += Layout.getBaseClassOffset(CurRD);
       }
     }
 
-    // If a "Base" class has at least one non-virtual base with a virtual
-    // destructor, the "Base" virtual destructor will take the address of the
-    // "Base" subobject as the "this" argument.
-    if (!SeenVBase && isa<CXXDestructorDecl>(MD))
-      return Base.getBaseOffset();
+    if (isa<CXXDestructorDecl>(MD)) {
+      if (LastVBaseOffset.isZero()) {
+        // If a "Base" class has at least one non-virtual base with a virtual
+        // destructor, the "Base" virtual destructor will take the address
+        // of the "Base" subobject as the "this" argument.
+        return Base.getBaseOffset();
+      } else {
+        // A virtual destructor of a virtual base takes the address of the
+        // virtual base subobject as the "this" argument.
+        return LastVBaseOffset;
+      }
+    }
 
     if (Ret > ThisOffset || First) {
       First = false;
@@ -2723,6 +2728,49 @@ VFTableBuilder::ComputeThisOffset(const
   return Ret;
 }
 
+void VFTableBuilder::CalculateVtordispAdjustment(
+    FinalOverriders::OverriderInfo Overrider, CharUnits ThisOffset,
+    ThisAdjustment &TA) {
+  const ASTRecordLayout::VBaseOffsetsMapTy &VBaseMap =
+      MostDerivedClassLayout.getVBaseOffsetsMap();
+  const ASTRecordLayout::VBaseOffsetsMapTy::const_iterator &VBaseMapEntry =
+      VBaseMap.find(WhichVFPtr.LastVBase);
+  assert(VBaseMapEntry != VBaseMap.end());
+
+  // Check if we need a vtordisp adjustment at all.
+  if (!VBaseMapEntry->second.hasVtorDisp())
+    return;
+
+  CharUnits VFPtrVBaseOffset = VBaseMapEntry->second.VBaseOffset;
+  // The implicit vtordisp field is located right before the vbase.
+  TA.Virtual.Microsoft.VtordispOffset =
+      (VFPtrVBaseOffset - WhichVFPtr.VFPtrFullOffset).getQuantity() - 4;
+
+  // If the final overrider is defined in either:
+  // - the most derived class or its non-virtual base or
+  // - the same vbase as the initial declaration,
+  // a simple vtordisp thunk will suffice.
+  const CXXRecordDecl *OverriderRD = Overrider.Method->getParent();
+  if (OverriderRD == MostDerivedClass)
+    return;
+
+  const CXXRecordDecl *OverriderVBase =
+      ComputeBaseOffset(Context, OverriderRD, MostDerivedClass).VirtualBase;
+  if (!OverriderVBase || OverriderVBase == WhichVFPtr.LastVBase)
+    return;
+
+  // Otherwise, we need to do use the dynamic offset of the final overrider
+  // in order to get "this" adjustment right.
+  TA.Virtual.Microsoft.VBPtrOffset =
+      (VFPtrVBaseOffset + WhichVFPtr.VFPtrOffset -
+       MostDerivedClassLayout.getVBPtrOffset()).getQuantity();
+  TA.Virtual.Microsoft.VBOffsetOffset =
+      Context.getTypeSizeInChars(Context.IntTy).getQuantity() *
+      VTables.getVBTableIndex(MostDerivedClass, OverriderVBase);
+
+  TA.NonVirtual = (ThisOffset - Overrider.Offset).getQuantity();
+}
+
 static void GroupNewVirtualOverloads(
     const CXXRecordDecl *RD,
     SmallVector<const CXXMethodDecl *, 10> &VirtualMethods) {
@@ -2829,6 +2877,12 @@ void VFTableBuilder::AddMethods(BaseSubo
       if (TI != WhichVFPtr.VFPtrFullOffset) {
         ThisAdjustmentOffset.NonVirtual =
             (TI - WhichVFPtr.VFPtrFullOffset).getQuantity();
+      }
+
+      if (WhichVFPtr.LastVBase)
+        CalculateVtordispAdjustment(Overrider, TI, ThisAdjustmentOffset);
+
+      if (!ThisAdjustmentOffset.isEmpty()) {
         VTableThunks[OverriddenMethodInfo.VFTableIndex].This =
             ThisAdjustmentOffset;
         AddThunk(MD, VTableThunks[OverriddenMethodInfo.VFTableIndex]);
@@ -2962,8 +3016,17 @@ static void dumpMicrosoftThunkAdjustment
     if (Multiline || !ContinueFirstLine)
       Out << LinePrefix;
     Out << "[this adjustment: ";
-    assert(TI.This.VCallOffsetOffset == 0 &&
-           "VtorDisp adjustment is not supported yet");
+    if (!TI.This.Virtual.isEmpty()) {
+      assert(T.Virtual.Microsoft.VtordispOffset < 0);
+      Out << "vtordisp at " << T.Virtual.Microsoft.VtordispOffset << ", ";
+      if (T.Virtual.Microsoft.VBPtrOffset) {
+        Out << "vbptr at " << T.Virtual.Microsoft.VBPtrOffset
+            << " to the left, ";
+        assert(T.Virtual.Microsoft.VBOffsetOffset > 0);
+        Out << LinePrefix << " vboffset at "
+            << T.Virtual.Microsoft.VBOffsetOffset << " in the vbtable, ";
+      }
+    }
     Out << T.NonVirtual << " non-virtual]";
   }
 }

Modified: cfe/trunk/lib/CodeGen/ItaniumCXXABI.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/CodeGen/ItaniumCXXABI.cpp?rev=194132&r1=194131&r2=194132&view=diff
==============================================================================
--- cfe/trunk/lib/CodeGen/ItaniumCXXABI.cpp (original)
+++ cfe/trunk/lib/CodeGen/ItaniumCXXABI.cpp Wed Nov  6 00:24:31 2013
@@ -1118,7 +1118,8 @@ static llvm::Value *performTypeAdjustmen
 llvm::Value *ItaniumCXXABI::performThisAdjustment(CodeGenFunction &CGF,
                                                   llvm::Value *This,
                                                   const ThisAdjustment &TA) {
-  return performTypeAdjustment(CGF, This, TA.NonVirtual, TA.VCallOffsetOffset,
+  return performTypeAdjustment(CGF, This, TA.NonVirtual,
+                               TA.Virtual.Itanium.VCallOffsetOffset,
                                /*IsReturnAdjustment=*/false);
 }
 

Modified: cfe/trunk/lib/CodeGen/MicrosoftCXXABI.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/CodeGen/MicrosoftCXXABI.cpp?rev=194132&r1=194131&r2=194132&view=diff
==============================================================================
--- cfe/trunk/lib/CodeGen/MicrosoftCXXABI.cpp (original)
+++ cfe/trunk/lib/CodeGen/MicrosoftCXXABI.cpp Wed Nov  6 00:24:31 2013
@@ -988,8 +988,29 @@ llvm::Value *MicrosoftCXXABI::performThi
 
   llvm::Value *V = CGF.Builder.CreateBitCast(This, CGF.Int8PtrTy);
 
-  assert(TA.VCallOffsetOffset == 0 &&
-         "VtorDisp adjustment is not supported yet");
+  if (!TA.Virtual.isEmpty()) {
+    assert(TA.Virtual.Microsoft.VtordispOffset < 0);
+    // Adjust the this argument based on the vtordisp value.
+    llvm::Value *VtorDispPtr =
+        CGF.Builder.CreateConstGEP1_32(V, TA.Virtual.Microsoft.VtordispOffset);
+    VtorDispPtr =
+        CGF.Builder.CreateBitCast(VtorDispPtr, CGF.Int32Ty->getPointerTo());
+    llvm::Value *VtorDisp = CGF.Builder.CreateLoad(VtorDispPtr, "vtordisp");
+    V = CGF.Builder.CreateGEP(V, CGF.Builder.CreateNeg(VtorDisp));
+
+    if (TA.Virtual.Microsoft.VBPtrOffset) {
+      // If the final overrider is defined in a virtual base other than the one
+      // that holds the vfptr, we have to use a vtordispex thunk which looks up
+      // the vbtable of the derived class.
+      assert(TA.Virtual.Microsoft.VBPtrOffset > 0);
+      assert(TA.Virtual.Microsoft.VBOffsetOffset >= 0);
+      llvm::Value *VBPtr;
+      llvm::Value *VBaseOffset =
+          GetVBaseOffsetFromVBPtr(CGF, V, -TA.Virtual.Microsoft.VBPtrOffset,
+                                  TA.Virtual.Microsoft.VBOffsetOffset, &VBPtr);
+      V = CGF.Builder.CreateInBoundsGEP(VBPtr, VBaseOffset);
+    }
+  }
 
   if (TA.NonVirtual) {
     // Non-virtual adjustment might result in a pointer outside the allocated

Modified: cfe/trunk/test/CodeGenCXX/microsoft-abi-thunks.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/test/CodeGenCXX/microsoft-abi-thunks.cpp?rev=194132&r1=194131&r2=194132&view=diff
==============================================================================
--- cfe/trunk/test/CodeGenCXX/microsoft-abi-thunks.cpp (original)
+++ cfe/trunk/test/CodeGenCXX/microsoft-abi-thunks.cpp Wed Nov  6 00:24:31 2013
@@ -138,8 +138,6 @@ I::I() {}  // Emits vftable and forces t
 // CODEGEN: phi %struct.F* {{.*}} %[[RES]]
 // CODEGEN: ret %struct.{{[BF]}}*
 
-// FIXME: Write vtordisp adjusting thunk tests
-
 namespace CrashOnThunksForAttributedType {
 // We used to crash on this because the type of foo is an AttributedType, not
 // FunctionType, and we had to look through the sugar.

Added: cfe/trunk/test/CodeGenCXX/microsoft-abi-virtual-inheritance-vtordisps.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/test/CodeGenCXX/microsoft-abi-virtual-inheritance-vtordisps.cpp?rev=194132&view=auto
==============================================================================
--- cfe/trunk/test/CodeGenCXX/microsoft-abi-virtual-inheritance-vtordisps.cpp (added)
+++ cfe/trunk/test/CodeGenCXX/microsoft-abi-virtual-inheritance-vtordisps.cpp Wed Nov  6 00:24:31 2013
@@ -0,0 +1,83 @@
+// RUN: %clang_cc1 %s -fno-rtti -cxx-abi microsoft -triple=i386-pc-win32 -emit-llvm -o - | FileCheck %s
+
+// For now, just make sure x86_64 doesn't crash.
+// RUN: %clang_cc1 %s -fno-rtti -cxx-abi microsoft -triple=x86_64-pc-win32 -emit-llvm -o %t
+
+struct A {
+  virtual void f();
+};
+
+struct B {
+  virtual void f();
+};
+
+struct C : A, B {};
+
+struct D : virtual C {
+  D();
+  ~D();
+  virtual void f();
+  void g();
+  int xxx;
+};
+
+D::D() {}  // Forces vftable emission.
+
+// CHECK-LABEL: define weak x86_thiscallcc void @"\01?f at D@@$4PPPPPPPM at A@AEXXZ"
+// CHECK: %[[ECX:.*]] = load %struct.D** %{{.*}}
+// CHECK: %[[ECX_i8:.*]] = bitcast %struct.D* %[[ECX]] to i8*
+// CHECK: %[[VTORDISP_PTR_i8:.*]] = getelementptr i8* %[[ECX_i8]], i32 -4
+// CHECK: %[[VTORDISP_PTR:.*]] = bitcast i8* %[[VTORDISP_PTR_i8]] to i32*
+// CHECK: %[[VTORDISP:.*]] = load i32* %[[VTORDISP_PTR]]
+// CHECK: %[[VTORDISP_NEG:.*]] = sub i32 0, %[[VTORDISP]]
+// CHECK: %[[ADJUSTED_i8:.*]] = getelementptr i8* %[[ECX_i8]], i32 %[[VTORDISP_NEG]]
+// CHECK: call x86_thiscallcc void @"\01?f at D@@UAEXXZ"(i8* %[[ADJUSTED_i8]])
+// CHECK: ret void
+
+// CHECK-LABEL: define weak x86_thiscallcc void @"\01?f at D@@$4PPPPPPPI at 3AEXXZ"
+// CHECK: %[[ECX:.*]] = load %struct.D** %{{.*}}
+// CHECK: %[[ECX_i8:.*]] = bitcast %struct.D* %[[ECX]] to i8*
+// CHECK: %[[VTORDISP_PTR_i8:.*]] = getelementptr i8* %[[ECX_i8]], i32 -8
+// CHECK: %[[VTORDISP_PTR:.*]] = bitcast i8* %[[VTORDISP_PTR_i8]] to i32*
+// CHECK: %[[VTORDISP:.*]] = load i32* %[[VTORDISP_PTR]]
+// CHECK: %[[VTORDISP_NEG:.*]] = sub i32 0, %[[VTORDISP]]
+// CHECK: %[[VTORDISP_ADJUSTED_i8:.*]] = getelementptr i8* %[[ECX_i8]], i32 %[[VTORDISP_NEG]]
+// CHECK: %[[ADJUSTED_i8:.*]] = getelementptr i8* %[[VTORDISP_ADJUSTED_i8]], i32 -4
+// CHECK: call x86_thiscallcc void @"\01?f at D@@UAEXXZ"(i8* %[[ADJUSTED_i8]])
+// CHECK: ret void
+
+struct E : virtual A {
+  virtual void f();
+  ~E();
+};
+
+struct F {
+  virtual void z();
+};
+
+struct G : virtual F, virtual E {
+  int ggg;
+  G();
+  ~G();
+};
+
+G::G() {}  // Forces vftable emission.
+
+// CHECK-LABEL: define weak x86_thiscallcc void @"\01?f at E@@$R4BA at M@PPPPPPPM at 7AEXXZ"(i8*)
+// CHECK: %[[ECX:.*]] = load %struct.E** %{{.*}}
+// CHECK: %[[ECX_i8:.*]] = bitcast %struct.E* %[[ECX]] to i8*
+// CHECK: %[[VTORDISP_PTR_i8:.*]] = getelementptr i8* %[[ECX_i8]], i32 -4
+// CHECK: %[[VTORDISP_PTR:.*]] = bitcast i8* %[[VTORDISP_PTR_i8]] to i32*
+// CHECK: %[[VTORDISP:.*]] = load i32* %[[VTORDISP_PTR]]
+// CHECK: %[[VTORDISP_NEG:.*]] = sub i32 0, %[[VTORDISP]]
+// CHECK: %[[VTORDISP_ADJUSTED_i8:.*]] = getelementptr i8* %[[ECX_i8]], i32 %[[VTORDISP_NEG]]
+// CHECK: %[[VBPTR_i8:.*]] = getelementptr inbounds i8* %[[VTORDISP_ADJUSTED_i8]], i32 -16
+// CHECK: %[[VBPTR:.*]] = bitcast i8* %[[VBPTR_i8]] to i8**
+// CHECK: %[[VBTABLE:.*]] = load i8** %[[VBPTR]]
+// CHECK: %[[VBOFFSET_PTR_i8:.*]] = getelementptr inbounds i8* %[[VBTABLE]], i32 12
+// CHECK: %[[VBOFFSET_PTR:.*]] = bitcast i8* %[[VBOFFSET_PTR_i8]] to i32*
+// CHECK: %[[VBASE_OFFSET:.*]] = load i32* %[[VBOFFSET_PTR]]
+// CHECK: %[[VBASE:.*]] = getelementptr inbounds i8* %[[VBPTR_i8]], i32 %[[VBASE_OFFSET]]
+// CHECK: %[[ARG_i8:.*]] = getelementptr i8* %[[VBASE]], i32 8
+// CHECK: call x86_thiscallcc void @"\01?f at E@@UAEXXZ"(i8* %[[ARG_i8]])
+// CHECK: ret void

Added: cfe/trunk/test/CodeGenCXX/microsoft-abi-vtables-virtual-inheritance-vtordisps.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/test/CodeGenCXX/microsoft-abi-vtables-virtual-inheritance-vtordisps.cpp?rev=194132&view=auto
==============================================================================
--- cfe/trunk/test/CodeGenCXX/microsoft-abi-vtables-virtual-inheritance-vtordisps.cpp (added)
+++ cfe/trunk/test/CodeGenCXX/microsoft-abi-vtables-virtual-inheritance-vtordisps.cpp Wed Nov  6 00:24:31 2013
@@ -0,0 +1,324 @@
+// RUN: %clang_cc1 -fno-rtti -emit-llvm -fdump-vtable-layouts %s -o - -cxx-abi microsoft -triple=i386-pc-win32 >%t 2>&1
+// RUN: FileCheck --check-prefix=VTABLE-SIMPLE-A %s < %t
+// RUN: FileCheck --check-prefix=VTABLE-SIMPLE-B %s < %t
+// RUN: FileCheck --check-prefix=VTABLE-SIMPLE-C %s < %t
+// RUN: FileCheck --check-prefix=VTABLE-EXTENDED-A %s < %t
+// RUN: FileCheck --check-prefix=VTABLE-EXTENDED-B %s < %t
+// RUN: FileCheck --check-prefix=VTABLE-EXTENDED-C %s < %t
+// RUN: FileCheck --check-prefix=VTABLE-EXTENDED-E %s < %t
+// RUN: FileCheck --check-prefix=VTABLE-EXTENDED-F %s < %t
+// RUN: FileCheck --check-prefix=VTABLE-EXTENDED-G %s < %t
+// RUN: FileCheck --check-prefix=VTABLE-EXTENDED-H %s < %t
+// RUN: FileCheck --check-prefix=VTABLE-PR17738-A %s < %t
+// RUN: FileCheck --check-prefix=MANGLING %s < %t
+
+// For now, just make sure x86_64 doesn't crash.
+// RUN: %clang_cc1 -fno-rtti -emit-llvm -fdump-vtable-layouts %s -o - -cxx-abi microsoft -triple=x86_64-pc-win32 >%t 2>&1
+
+struct V1 {
+  virtual void f();
+  virtual ~V1();
+};
+
+struct V2 {
+  virtual void f();
+  virtual ~V2();
+  int v;
+};
+
+struct Z {
+  virtual void g();
+  virtual ~Z();
+  int x;
+};
+
+struct V3 : Z, V2 {
+};
+
+struct V4 : Z, V1, V2 {
+  int y;
+};
+
+void use_somewhere_else(void*);
+
+namespace simple {
+// In case of a single-layer virtual inheritance, the "this" adjustment is done
+// staically:
+//   struct A {
+//     virtual void f();  // Expects "(A*)this" in ECX
+//   };
+//   struct B : virtual A {
+//     virtual void f();  // Expects "(char*)(B*)this + 12" in ECX
+//     virtual ~B();      // Might call f()
+//   };
+//
+// If a class overrides a virtual function of its base and has a non-trivial
+// ctor/dtor that call(s) the virtual function (or may escape "this" to some
+// code that might call it), a virtual adjustment might be needed in case the
+// current class layout and the most derived class layout are different.
+// This is done using vtordisp thunks.
+//
+// A simple vtordisp{A,B} thunk for Method at Class is something like:
+//   sub  ecx, [ecx+A]  // apply the vtordisp adjustment
+//   sub  ecx, B        // apply the subobject adjustment, if needed.
+//   jmp Method at Class
+
+struct A : virtual V1 {
+  // VTABLE-SIMPLE-A: VFTable for 'V1' in 'simple::A' (2 entries).
+  // VTABLE-SIMPLE-A-NEXT: 0 | void simple::A::f()
+  // VTABLE-SIMPLE-A-NEXT:     [this adjustment: vtordisp at -4, 0 non-virtual]
+  // VTABLE-SIMPLE-A-NEXT: 1 | simple::A::~A() [scalar deleting]
+  // VTABLE-SIMPLE-A-NEXT:     [this adjustment: vtordisp at -4, 0 non-virtual]
+
+  virtual void f();
+  // MANGLING-DAG: @"\01?f at A@simple@@$4PPPPPPPM at A@AEXXZ"
+
+  virtual ~A();
+  // MANGLING-DAG: @"\01??_EA at simple@@$4PPPPPPPM at A@AEPAXI at Z"
+};
+
+A a;
+
+struct B : virtual V3 {
+  // VTABLE-SIMPLE-B: VFTable for 'Z' in 'V3' in 'simple::B' (2 entries).
+  // VTABLE-SIMPLE-B-NEXT: 0 | void Z::g()
+  // VTABLE-SIMPLE-B-NEXT: 1 | simple::B::~B() [scalar deleting]
+  // VTABLE-SIMPLE-B-NEXT:     [this adjustment: vtordisp at -4, 0 non-virtual]
+
+  // VTABLE-SIMPLE-B: VFTable for 'V2' in 'V3' in 'simple::B' (2 entries).
+  // VTABLE-SIMPLE-B-NEXT: 0 | void simple::B::f()
+  // VTABLE-SIMPLE-B-NEXT:     [this adjustment: vtordisp at -12, 0 non-virtual]
+  // VTABLE-SIMPLE-B-NEXT: 1 | simple::B::~B() [scalar deleting]
+  // VTABLE-SIMPLE-B-NEXT:     [this adjustment: vtordisp at -12, -8 non-virtual]
+
+  // FIXME: The vtordisp thunk should only get emitted for a constructor
+  // if "this" leaves scope.
+  B() { use_somewhere_else(this); }
+
+  virtual void f();
+  // MANGLING-DAG: @"\01?f at B@simple@@$4PPPPPPPE at A@AEXXZ"
+
+  // Has an implicit destructor.
+  // MANGLING-DAG: @"\01??_EB at simple@@$4PPPPPPPE at 7AEPAXI@Z"
+  // MANGLING-DAG: @"\01??_EB at simple@@$4PPPPPPPM at A@AEPAXI at Z"
+};
+
+B b;
+
+struct C : virtual V4 {
+  // VTABLE-SIMPLE-C: VFTable for 'Z' in 'V4' in 'simple::C' (2 entries).
+  // VTABLE-SIMPLE-C-NEXT: 0 | void Z::g()
+  // VTABLE-SIMPLE-C-NEXT: 1 | simple::C::~C() [scalar deleting]
+  // VTABLE-SIMPLE-C-NEXT:     [this adjustment: vtordisp at -4, 0 non-virtual]
+
+  // VTABLE-SIMPLE-C: VFTable for 'V1' in 'V4' in 'simple::C' (2 entries).
+  // VTABLE-SIMPLE-C-NEXT: 0 | void simple::C::f()
+  // VTABLE-SIMPLE-C-NEXT:     [this adjustment: vtordisp at -12, 0 non-virtual]
+  // VTABLE-SIMPLE-C-NEXT: 1 | simple::C::~C() [scalar deleting]
+  // VTABLE-SIMPLE-C-NEXT:     [this adjustment: vtordisp at -12, -8 non-virtual]
+
+  // VTABLE-SIMPLE-C: VFTable for 'V2' in 'V4' in 'simple::C' (2 entries).
+  // VTABLE-SIMPLE-C-NEXT: 0 | void simple::C::f()
+  // VTABLE-SIMPLE-C-NEXT:     [this adjustment: vtordisp at -16, -4 non-virtual]
+  // VTABLE-SIMPLE-C-NEXT: 1 | simple::C::~C() [scalar deleting]
+  // VTABLE-SIMPLE-C-NEXT:     [this adjustment: vtordisp at -16, -12 non-virtual]
+
+  int x;
+  virtual void f();
+  // MANGLING-DAG: @"\01?f at C@simple@@$4PPPPPPPA at 3AEXXZ"
+  // MANGLING-DAG: @"\01?f at C@simple@@$4PPPPPPPE at A@AEXXZ"
+  virtual ~C();
+  // MANGLING-DAG: @"\01??_EC at simple@@$4PPPPPPPA at M@AEPAXI at Z"
+  // MANGLING-DAG: @"\01??_EC at simple@@$4PPPPPPPE at 7AEPAXI@Z"
+  // MANGLING-DAG: @"\01??_EC at simple@@$4PPPPPPPM at A@AEPAXI at Z"
+};
+
+C c;
+}
+
+namespace extended {
+// If a virtual function requires vtordisp adjustment and the final overrider
+// is defined in another vitual base of the most derived class,
+// we need to know two vbase offsets.
+// In this case, we should use the extended form of vtordisp thunks, called
+// vtordispex thunks.
+//
+// vtordispex{A,B,C,D} thunk for Method at Class is something like:
+//   sub  ecx, [ecx+C]  // apply the vtordisp adjustment
+//   sub  ecx, A        // jump to the vbtable of the most derived class
+//   mov  eax, [ecx]    // load the vbtable address
+//   add  ecx, [eax+B]  // lookup the final overrider's vbase offset
+//   add  ecx, D        // apphy the subobject offset if needed
+//   jmp Method at Class
+
+struct A : virtual simple::A {
+  // VTABLE-EXTENDED-A: VFTable for 'V1' in 'simple::A' in 'extended::A' (2 entries).
+  // VTABLE-EXTENDED-A-NEXT: 0 | void simple::A::f()
+  // VTABLE-EXTENDED-A-NEXT:     [this adjustment: vtordisp at -4, vbptr at 8 to the left,
+  // VTABLE-EXTENDED-A-NEXT:      vboffset at 8 in the vbtable, 8 non-virtual]
+  // VTABLE-EXTENDED-A-NEXT: 1 | extended::A::~A() [scalar deleting]
+  // VTABLE-EXTENDED-A-NEXT:     [this adjustment: vtordisp at -4, 0 non-virtual]
+
+  // `vtordispex{8,8,4294967292,8}'
+  // MANGLING-DAG: @"\01?f at A@simple@@$R477PPPPPPPM at 7AEXXZ"
+
+  virtual ~A();
+  // vtordisp{4294967292,0}
+  // MANGLING-DAG: @"\01??_EA at extended@@$4PPPPPPPM at A@AEPAXI at Z"
+};
+
+A a;
+
+struct B : virtual simple::A {
+  // This class has an implicit dtor.  Vdtors don't require vtordispex thunks
+  // as the most derived class always has an implicit dtor,
+  // which is a final overrider.
+
+  // VTABLE-EXTENDED-B: VFTable for 'V1' in 'simple::A' in 'extended::B' (2 entries).
+  //  ...
+  // VTABLE-EXTENDED-B: 1 | extended::B::~B() [scalar deleting]
+  // VTABLE-EXTENDED-B-NEXT: [this adjustment: vtordisp at -4, 0 non-virtual]
+
+  // vtordisp{4294967292,0}
+  // MANGLING-DAG: @"\01??_EB at extended@@$4PPPPPPPM at A@AEPAXI at Z"
+};
+
+B b;
+
+struct C : virtual simple::A {
+  // VTABLE-EXTENDED-C: VFTable for 'V1' in 'simple::A' in 'extended::C' (2 entries).
+  // VTABLE-EXTENDED-C-NEXT: 0 | void simple::A::f()
+  // VTABLE-EXTENDED-C-NEXT:     [this adjustment: vtordisp at -4, vbptr at 12 to the left,
+  // VTABLE-EXTENDED-C-NEXT:      vboffset at 8 in the vbtable, 8 non-virtual]
+
+  // `vtordispex{12,8,4294967292,8}'
+  // MANGLING-DAG: @"\01?f at A@simple@@$R4M at 7PPPPPPPM@7AEXXZ"
+  int x;
+  virtual ~C();
+  // MANGLING-DAG: @"\01??_EC at extended@@$4PPPPPPPM at A@AEPAXI at Z"
+};
+
+C c;
+
+struct D : virtual V2 {
+  virtual void f();
+  virtual ~D();
+  int x;
+};
+
+struct E : virtual D {
+  // VTABLE-EXTENDED-E: VFTable for 'V2' in 'extended::D' in 'extended::E' (2 entries).
+  // VTABLE-EXTENDED-E-NEXT: 0 | void extended::D::f()
+  // VTABLE-EXTENDED-E-NEXT:     [this adjustment: vtordisp at -4, vbptr at 8 to the left,
+  // VTABLE-EXTENDED-E-NEXT:      vboffset at 8 in the vbtable, 12 non-virtual]
+
+  // `vtordispex{8,8,4294967292,12}'
+  // MANGLING-DAG: @"\01?f at D@extended@@$R477PPPPPPPM at M@AEXXZ"
+
+  virtual ~E();
+  // MANGLING-DAG: @"\01??_EE at extended@@$4PPPPPPPM at A@AEPAXI at Z"
+};
+
+E e;
+
+struct F : virtual Z, virtual D {
+  // VTABLE-EXTENDED-F: VFTable for 'V2' in 'extended::D' in 'extended::F' (2 entries).
+  // VTABLE-EXTENDED-F-NEXT: 0 | void extended::D::f()
+  // VTABLE-EXTENDED-F-NEXT:     [this adjustment: vtordisp at -4, vbptr at 20 to the left,
+  // VTABLE-EXTENDED-F-NEXT:      vboffset at 12 in the vbtable, 12 non-virtual]
+
+  // `vtordispex{20,12,4294967292,12}'
+  // MANGLING-DAG: @"\01?f at D@extended@@$R4BE at M@PPPPPPPM at M@AEXXZ"
+  int x;
+  virtual ~F();
+  // MANGLING-DAG: @"\01??_EF at extended@@$4PPPPPPPM at M@AEPAXI at Z"
+};
+
+F f;
+
+struct G : virtual simple::A {
+  // VTABLE-EXTENDED-G: VFTable for 'extended::G' (1 entries).
+  // VTABLE-EXTENDED-G-NEXT: 0 | void extended::G::g()
+
+  // VTABLE-EXTENDED-G: VFTable for 'V1' in 'simple::A' in 'extended::G' (2 entries).
+  // VTABLE-EXTENDED-G-NEXT: 0 | void simple::A::f()
+  // VTABLE-EXTENDED-G-NEXT:     [this adjustment: vtordisp at -4, vbptr at 8 to the left,
+  // VTABLE-EXTENDED-G-NEXT:      vboffset at 8 in the vbtable, 8 non-virtual]
+  // VTABLE-EXTENDED-G-NEXT: 1 | extended::G::~G() [scalar deleting]
+  // VTABLE-EXTENDED-G-NEXT:     [this adjustment: vtordisp at -4, 0 non-virtual]
+
+  // Emits a G's own vfptr, thus moving the vbptr in the layout.
+  virtual void g();
+
+  virtual ~G();
+  // vtordisp{4294967292,0}
+  // MANGLING-DAG: @"\01??_EG at extended@@$4PPPPPPPM at A@AEPAXI at Z"
+};
+
+G g;
+
+struct H : Z, A {
+  // VTABLE-EXTENDED-H: VFTable for 'Z' in 'extended::H' (2 entries).
+  // VTABLE-EXTENDED-H-NEXT: 0 | void Z::g()
+  // VTABLE-EXTENDED-H-NEXT: 1 | extended::H::~H() [scalar deleting]
+
+  // VTABLE-EXTENDED-H: VFTable for 'V1' in 'simple::A' in 'extended::A' in 'extended::H' (2 entries).
+  // VTABLE-EXTENDED-H-NEXT: 0 | void simple::A::f()
+  // VTABLE-EXTENDED-H-NEXT:     [this adjustment: vtordisp at -4, vbptr at 8 to the left,
+  // VTABLE-EXTENDED-H-NEXT:      vboffset at 8 in the vbtable, 8 non-virtual]
+
+  // MANGLING-DAG: @"\01?f at A@simple@@$R477PPPPPPPM at 7AEXXZ"
+  // MANGLING-DAG: @"\01??_EH at extended@@$4PPPPPPPM at BA@AEPAXI at Z"
+};
+
+H h;
+}
+
+namespace pr17738 {
+// These classes should have vtordispex thunks but MSVS CL miscompiles them.
+// Just do the right thing.
+
+struct A : virtual simple::B {
+  // VTABLE-PR17738-A: VFTable for 'V2' in 'V3' in 'simple::B' in 'pr17738::A' (2 entries).
+  // VTABLE-PR17738-A-NEXT: 0 | void simple::B::f()
+  // VTABLE-PR17738-A-NEXT:     [this adjustment: vtordisp at -12, vbptr at 20 to the left,
+  // VTABLE-PR17738-A-NEXT:      vboffset at 8 in the vbtable, 16 non-virtual]
+
+  // MANGLING-DAG: @"\01?f at B@simple@@$R4BE at 7PPPPPPPE@BA at AEXXZ"
+  int a;
+  virtual ~A();
+};
+
+A a;
+}
+
+namespace access {
+struct A {
+  virtual ~A();
+protected:
+  virtual void prot();
+private:
+  virtual void priv();
+};
+
+struct B : virtual A {
+  virtual ~B();
+protected:
+  virtual void prot();
+  // MANGLING-DAG: @"\01?prot at B@access@@$2PPPPPPPM at A@AEXXZ"
+private:
+  virtual void priv();
+  // MANGLING-DAG: @"\01?priv at B@access@@$0PPPPPPPM at A@AEXXZ"
+};
+
+B b;
+
+struct C : virtual B {
+  virtual ~C();
+
+  // MANGLING-DAG: @"\01?prot at B@access@@$R277PPPPPPPM at 7AEXXZ"
+  // MANGLING-DAG: @"\01?priv at B@access@@$R077PPPPPPPM at 7AEXXZ"
+};
+
+C c;
+}





More information about the cfe-commits mailing list