[PATCH] [ms-cxxabi] Implement MSVC virtual base adjustment

Aaron Ballman aaron at aaronballman.com
Tue May 28 06:47:16 PDT 2013


> Index: lib/CodeGen/CGCXXABI.h
> ===================================================================
> --- lib/CodeGen/CGCXXABI.h
> +++ lib/CodeGen/CGCXXABI.h
> @@ -208,6 +208,11 @@
>                                                llvm::Value *ptr,
>                                                QualType type) = 0;
>
> +  virtual llvm::Value *GetVirtualBaseClassOffset(CodeGenFunction &CGF,
> +                                                 llvm::Value *This,
> +                                                 const CXXRecordDecl *ClassDecl,
> +                                                 const CXXRecordDecl *BaseClassDecl) = 0;

This (and a few other places) extend past 80 characters.

> +
>    /// Build the signature of the given constructor variant by adding
>    /// any required parameters.  For convenience, ResTy has been
>    /// initialized to 'void', and ArgTys has been initialized with the
> Index: lib/CodeGen/CGClass.cpp
> ===================================================================
> --- lib/CodeGen/CGClass.cpp
> +++ lib/CodeGen/CGClass.cpp
> @@ -198,7 +198,8 @@
>    // Compute the virtual offset.
>    llvm::Value *VirtualOffset = 0;
>    if (VBase) {
> -    VirtualOffset = GetVirtualBaseClassOffset(Value, Derived, VBase);
> +    VirtualOffset =
> +      CGM.getCXXABI().GetVirtualBaseClassOffset(*this, Value, Derived, VBase);
>    }
>
>    // Apply both offsets.
> @@ -1872,28 +1873,6 @@
>    PushDestructorCleanup(D, Addr);
>  }
>
> -llvm::Value *
> -CodeGenFunction::GetVirtualBaseClassOffset(llvm::Value *This,
> -                                           const CXXRecordDecl *ClassDecl,
> -                                           const CXXRecordDecl *BaseClassDecl) {
> -  llvm::Value *VTablePtr = GetVTablePtr(This, Int8PtrTy);
> -  CharUnits VBaseOffsetOffset =
> -    CGM.getVTableContext().getVirtualBaseOffsetOffset(ClassDecl, BaseClassDecl);
> -
> -  llvm::Value *VBaseOffsetPtr =
> -    Builder.CreateConstGEP1_64(VTablePtr, VBaseOffsetOffset.getQuantity(),
> -                               "vbase.offset.ptr");
> -  llvm::Type *PtrDiffTy =
> -    ConvertType(getContext().getPointerDiffType());
> -
> -  VBaseOffsetPtr = Builder.CreateBitCast(VBaseOffsetPtr,
> -                                         PtrDiffTy->getPointerTo());
> -
> -  llvm::Value *VBaseOffset = Builder.CreateLoad(VBaseOffsetPtr, "vbase.offset");
> -
> -  return VBaseOffset;
> -}
> -
>  void
>  CodeGenFunction::InitializeVTablePointer(BaseSubobject Base,
>                                           const CXXRecordDecl *NearestVBase,
> @@ -1933,8 +1912,10 @@
>    if (CodeGenVTables::needsVTTParameter(CurGD) && NearestVBase) {
>      // We need to use the virtual base offset offset because the virtual base
>      // might have a different offset in the most derived class.
> -    VirtualOffset = GetVirtualBaseClassOffset(LoadCXXThis(), VTableClass,
> -                                              NearestVBase);
> +    VirtualOffset = CGM.getCXXABI().GetVirtualBaseClassOffset(*this,
> +                                                              LoadCXXThis(),
> +                                                              VTableClass,
> +                                                              NearestVBase);
>      NonVirtualOffset = OffsetFromNearestVBase;
>    } else {
>      // We can just use the base offset in the complete class.
> Index: lib/CodeGen/CodeGenFunction.h
> ===================================================================
> --- lib/CodeGen/CodeGenFunction.h
> +++ lib/CodeGen/CodeGenFunction.h
> @@ -1975,10 +1975,6 @@
>                                          CastExpr::path_const_iterator PathEnd,
>                                          bool NullCheckValue);
>
> -  llvm::Value *GetVirtualBaseClassOffset(llvm::Value *This,
> -                                         const CXXRecordDecl *ClassDecl,
> -                                         const CXXRecordDecl *BaseClassDecl);
> -
>    /// GetVTTParameter - Return the VTT parameter that should be passed to a
>    /// base constructor/destructor with virtual bases.
>    /// FIXME: VTTs are Itanium ABI-specific, so the definition should move
> Index: lib/CodeGen/ItaniumCXXABI.cpp
> ===================================================================
> --- lib/CodeGen/ItaniumCXXABI.cpp
> +++ lib/CodeGen/ItaniumCXXABI.cpp
> @@ -98,6 +98,11 @@
>                                        llvm::Value *ptr,
>                                        QualType type);
>
> +  llvm::Value *GetVirtualBaseClassOffset(CodeGenFunction &CGF,
> +                                         llvm::Value *This,
> +                                         const CXXRecordDecl *ClassDecl,
> +                                         const CXXRecordDecl *BaseClassDecl);
> +
>    void BuildConstructorSignature(const CXXConstructorDecl *Ctor,
>                                   CXXCtorType T,
>                                   CanQualType &ResTy,
> @@ -720,6 +725,29 @@
>    return CGF.Builder.CreateInBoundsGEP(ptr, offset);
>  }
>
> +llvm::Value *
> +ItaniumCXXABI::GetVirtualBaseClassOffset(CodeGenFunction &CGF,
> +                                         llvm::Value *This,
> +                                         const CXXRecordDecl *ClassDecl,
> +                                         const CXXRecordDecl *BaseClassDecl) {
> +  llvm::Value *VTablePtr = CGF.GetVTablePtr(This, CGM.Int8PtrTy);
> +  CharUnits VBaseOffsetOffset =
> +    CGM.getVTableContext().getVirtualBaseOffsetOffset(ClassDecl, BaseClassDecl);
> +
> +  llvm::Value *VBaseOffsetPtr =
> +    CGF.Builder.CreateConstGEP1_64(VTablePtr, VBaseOffsetOffset.getQuantity(),
> +                                   "vbase.offset.ptr");
> +  llvm::Type *PtrDiffTy =
> +    CGF.ConvertType(getContext().getPointerDiffType());
> +
> +  VBaseOffsetPtr = CGF.Builder.CreateBitCast(VBaseOffsetPtr,
> +                                             CGM.PtrDiffTy->getPointerTo());
> +
> +  llvm::Value *VBaseOffset = CGF.Builder.CreateLoad(VBaseOffsetPtr, "vbase.offset");
> +
> +  return VBaseOffset;
> +}
> +
>  /// The generic ABI passes 'this', plus a VTT if it's initializing a
>  /// base subobject.
>  void ItaniumCXXABI::BuildConstructorSignature(const CXXConstructorDecl *Ctor,
> Index: lib/CodeGen/MicrosoftCXXABI.cpp
> ===================================================================
> --- lib/CodeGen/MicrosoftCXXABI.cpp
> +++ lib/CodeGen/MicrosoftCXXABI.cpp
> @@ -48,6 +48,11 @@
>                                        llvm::Value *ptr,
>                                        QualType type);
>
> +  llvm::Value *GetVirtualBaseClassOffset(CodeGenFunction &CGF,
> +                                         llvm::Value *This,
> +                                         const CXXRecordDecl *ClassDecl,
> +                                         const CXXRecordDecl *BaseClassDecl);
> +
>    void BuildConstructorSignature(const CXXConstructorDecl *Ctor,
>                                   CXXCtorType Type,
>                                   CanQualType &ResTy,
> @@ -142,6 +147,22 @@
>    GetNullMemberPointerFields(const MemberPointerType *MPT,
>                               llvm::SmallVectorImpl<llvm::Constant *> &fields);
>
> +  /// \brief Finds the offset from the base of RD to the vbptr it uses, even if
> +  /// it is reusing a vbptr from a non-virtual base.  RD must have morally
> +  /// virtual bases.
> +  CharUnits GetVBPtrOffsetFromBases(const CXXRecordDecl *RD);
> +
> +  /// \brief Shared code for virtual base adjustment.  Returns the offset from
> +  /// the vbptr to the virtual base.  Optionally returns the address of the
> +  /// vbptr itself.
> +  llvm::Value *GetVBaseOffsetFromVBPtr(CodeGenFunction &CGF,
> +                                       llvm::Value *Base,
> +                                       llvm::Value *VBPtrOffset,
> +                                       llvm::Value *VBTableOffset,
> +                                       llvm::Value **VBPtr = 0);
> +
> +  /// \brief Performs a full virtual base adjustment.  Used to dereference
> +  /// pointers to members of virtual bases.
>    llvm::Value *AdjustVirtualBase(CodeGenFunction &CGF, const CXXRecordDecl *RD,
>                                   llvm::Value *Base,
>                                   llvm::Value *VirtualBaseAdjustmentOffset,
> @@ -212,6 +233,68 @@
>    return ptr;
>  }
>
> +CharUnits MicrosoftCXXABI::GetVBPtrOffsetFromBases(const CXXRecordDecl *RD) {
> +  assert(RD->getNumVBases());
> +  CharUnits Total = CharUnits::Zero();
> +  while (RD) {
> +    const ASTRecordLayout &RDLayout = getContext().getASTRecordLayout(RD);
> +    CharUnits VBPtrOffset = RDLayout.getVBPtrOffset();
> +    // -1 is the sentinel for no vbptr.
> +    if (VBPtrOffset != CharUnits::fromQuantity(-1)) {
> +      Total += VBPtrOffset;
> +      break;
> +    }
> +
> +    // RD is reusing the vbptr of a non-virtual base.  Find it and continue.
> +    const CXXRecordDecl *FirstNVBaseWithVBases = 0;
> +    for (CXXRecordDecl::base_class_const_iterator I = RD->bases_begin(),
> +         E = RD->bases_end(); I != E; ++I) {
> +      const CXXRecordDecl *Base = I->getType()->getAsCXXRecordDecl();
> +      if (!I->isVirtual() && Base->getNumVBases() > 0) {
> +        FirstNVBaseWithVBases = Base;

Instead of manually breaking, it might be more clear to make
FirstNVBaseWithVBases part of the for loop test itself.

> +        break;
> +      }
> +    }
> +    assert(FirstNVBaseWithVBases);
> +    Total += RDLayout.getBaseClassOffset(FirstNVBaseWithVBases);
> +    RD = FirstNVBaseWithVBases;
> +  }
> +  return Total;
> +}
> +
> +llvm::Value *
> +MicrosoftCXXABI::GetVirtualBaseClassOffset(CodeGenFunction &CGF,
> +                                           llvm::Value *This,
> +                                           const CXXRecordDecl *ClassDecl,
> +                                           const CXXRecordDecl *BaseClassDecl) {
> +  int64_t VBPtrChars = GetVBPtrOffsetFromBases(ClassDecl).getQuantity();
> +  llvm::Value *VBPtrOffset = llvm::ConstantInt::get(CGM.PtrDiffTy, VBPtrChars);
> +
> +  // The vbtable is an array of i32 offsets.  The first entry is a self entry,
> +  // and the rest are offsets from the vbptr to virtual bases.  The bases are
> +  // ordered the same way our vbases are ordered: as they appear in a
> +  // left-to-right depth-first search of the hierarchy.
> +  unsigned VBTableIndex = 1;  // Start with one to skip the self entry.
> +  for (CXXRecordDecl::base_class_const_iterator I = ClassDecl->vbases_begin(),
> +       E = ClassDecl->vbases_end(); I != E; ++I) {
> +    if (I->getType()->getAsCXXRecordDecl() == BaseClassDecl)
> +      break;
> +    VBTableIndex++;
> +  }
> +  assert(VBTableIndex != 1 + ClassDecl->getNumVBases() &&
> +         "BaseClassDecl must be a vbase of ClassDecl");
> +  CharUnits IntSize = getContext().getTypeSizeInChars(getContext().IntTy);
> +  CharUnits VBTableChars = IntSize * VBTableIndex;
> +  llvm::Value *VBTableOffset =
> +    llvm::ConstantInt::get(CGM.IntTy, VBTableChars.getQuantity());
> +
> +  llvm::Value *VBPtrToNewBase =
> +    GetVBaseOffsetFromVBPtr(CGF, This, VBTableOffset, VBPtrOffset);
> +  VBPtrToNewBase =
> +    CGF.Builder.CreateSExtOrBitCast(VBPtrToNewBase, CGM.PtrDiffTy);
> +  return CGF.Builder.CreateNSWAdd(VBPtrOffset, VBPtrToNewBase);
> +}
> +
>  bool MicrosoftCXXABI::needThisReturn(GlobalDecl GD) {
>    const CXXMethodDecl* MD = cast<CXXMethodDecl>(GD.getDecl());
>    return isa<CXXConstructorDecl>(MD);
> @@ -781,12 +864,33 @@
>    return I == E;
>  }
>
> +llvm::Value *
> +MicrosoftCXXABI::GetVBaseOffsetFromVBPtr(CodeGenFunction &CGF,
> +                                         llvm::Value *This,
> +                                         llvm::Value *VBTableOffset,
> +                                         llvm::Value *VBPtrOffset,
> +                                         llvm::Value **VBPtrOut) {
> +  CGBuilderTy &Builder = CGF.Builder;
> +  // Load the vbtable pointer from the vbptr in the instance.
> +  This = Builder.CreateBitCast(This, CGM.Int8PtrTy);
> +  llvm::Value *VBPtr =
> +    Builder.CreateInBoundsGEP(This, VBPtrOffset, "vbptr");
> +  if (VBPtrOut) *VBPtrOut = VBPtr;
> +  VBPtr = Builder.CreateBitCast(VBPtr, CGM.Int8PtrTy->getPointerTo(0));
> +  llvm::Value *VBTable = Builder.CreateLoad(VBPtr, "vbtable");
> +
> +  // Load an i32 offset from the vb-table.
> +  llvm::Value *VBaseOffs = Builder.CreateInBoundsGEP(VBTable, VBTableOffset);
> +  VBaseOffs = Builder.CreateBitCast(VBaseOffs, CGM.Int32Ty->getPointerTo(0));
> +  return Builder.CreateLoad(VBaseOffs, "vbase_offs");
> +}
> +
>  // Returns an adjusted base cast to i8*, since we do more address arithmetic on
>  // it.
>  llvm::Value *
>  MicrosoftCXXABI::AdjustVirtualBase(CodeGenFunction &CGF,
>                                     const CXXRecordDecl *RD, llvm::Value *Base,
> -                                   llvm::Value *VirtualBaseAdjustmentOffset,
> +                                   llvm::Value *VBTableOffset,
>                                     llvm::Value *VBPtrOffset) {
>    CGBuilderTy &Builder = CGF.Builder;
>    Base = Builder.CreateBitCast(Base, CGM.Int8PtrTy);
> @@ -803,30 +907,24 @@
>      VBaseAdjustBB = CGF.createBasicBlock("memptr.vadjust");
>      SkipAdjustBB = CGF.createBasicBlock("memptr.skip_vadjust");
>      llvm::Value *IsVirtual =
> -      Builder.CreateICmpNE(VirtualBaseAdjustmentOffset, getZeroInt(),
> +      Builder.CreateICmpNE(VBTableOffset, getZeroInt(),
>                             "memptr.is_vbase");
>      Builder.CreateCondBr(IsVirtual, VBaseAdjustBB, SkipAdjustBB);
>      CGF.EmitBlock(VBaseAdjustBB);
>    }
>
>    // If we weren't given a dynamic vbptr offset, RD should be complete and we'll
>    // know the vbptr offset.
>    if (!VBPtrOffset) {
> -    CharUnits offs = getContext().getASTRecordLayout(RD).getVBPtrOffset();
> +    CharUnits offs = CharUnits::Zero();
> +    if (RD->getNumVBases()) {
> +      offs = GetVBPtrOffsetFromBases(RD);
> +    }
>      VBPtrOffset = llvm::ConstantInt::get(CGM.IntTy, offs.getQuantity());
>    }
> -  // Load the vbtable pointer from the vbtable offset in the instance.
> -  llvm::Value *VBPtr =
> -    Builder.CreateInBoundsGEP(Base, VBPtrOffset, "memptr.vbptr");
> -  llvm::Value *VBTable =
> -    Builder.CreateBitCast(VBPtr, CGM.Int8PtrTy->getPointerTo(0));
> -  VBTable = Builder.CreateLoad(VBTable, "memptr.vbtable");
> -  // Load an i32 offset from the vb-table.
> +  llvm::Value *VBPtr = 0;
>    llvm::Value *VBaseOffs =
> -    Builder.CreateInBoundsGEP(VBTable, VirtualBaseAdjustmentOffset);
> -  VBaseOffs = Builder.CreateBitCast(VBaseOffs, CGM.Int32Ty->getPointerTo(0));
> -  VBaseOffs = Builder.CreateLoad(VBaseOffs, "memptr.vbase_offs");
> -  // Add it to VBPtr.  GEP will sign extend the i32 value for us.
> +    GetVBaseOffsetFromVBPtr(CGF, Base, VBTableOffset, VBPtrOffset, &VBPtr);
>    llvm::Value *AdjustedBase = Builder.CreateInBoundsGEP(VBPtr, VBaseOffs);
>
>    // Merge control flow with the case where we didn't have to adjust.
> Index: test/CodeGenCXX/virtual-base-cast.cpp
> ===================================================================
> --- test/CodeGenCXX/virtual-base-cast.cpp
> +++ test/CodeGenCXX/virtual-base-cast.cpp
> @@ -1,4 +1,5 @@
>  // RUN: %clang_cc1 -emit-llvm %s -o - -triple i686-pc-linux-gnu | FileCheck %s
> +// RUN: %clang_cc1 -cxx-abi microsoft -emit-llvm %s -o - -triple i686-pc-win32 | FileCheck -check-prefix MSVC %s
>
>  struct A { int a; virtual int aa(); };
>  struct B { int b; virtual int bb(); };
> @@ -17,19 +18,72 @@
>  // CHECK: load i32* [[CASTVBASEOFFSETPTRA]]
>  // CHECK: }
>
> +// MSVC: @"\01?a@@YAPAUA@@XZ"() [[NUW:#[0-9]+]] {
> +// MSVC:   %[[vbptr_off:.*]] = getelementptr inbounds i8* {{.*}}, i32 0
> +// MSVC:   %[[vbptr:.*]] = bitcast i8* %[[vbptr_off]] to i8**
> +// MSVC:   %[[vbtable:.*]] = load i8** %[[vbptr]]
> +// MSVC:   %[[entry:.*]] = getelementptr inbounds i8* {{.*}}, i32 4
> +// MSVC:   %[[entry_i32:.*]] = bitcast i8* %[[entry]] to i32*
> +// MSVC:   %[[offset:.*]] = load i32* %[[entry_i32]]
> +// MSVC:   add nsw i32 0, %[[offset]]
> +// MSVC: }
> +
>  B* b() { return x; }
>  // CHECK: @_Z1bv() [[NUW]]
>  // CHECK: [[VBASEOFFSETPTRA:%[a-zA-Z0-9\.]+]] = getelementptr i8* {{.*}}, i64 -20
>  // CHECK: [[CASTVBASEOFFSETPTRA:%[a-zA-Z0-9\.]+]] = bitcast i8* [[VBASEOFFSETPTRA]] to i32*
>  // CHECK: load i32* [[CASTVBASEOFFSETPTRA]]
>  // CHECK: }
>
> +// Same as 'a' except we use a different vbtable offset.
> +// MSVC: @"\01?b@@YAPAUB@@XZ"() [[NUW:#[0-9]+]] {
> +// MSVC:   %[[vbptr_off:.*]] = getelementptr inbounds i8* {{.*}}, i32 0
> +// MSVC:   %[[vbptr:.*]] = bitcast i8* %[[vbptr_off]] to i8**
> +// MSVC:   %[[vbtable:.*]] = load i8** %[[vbptr]]
> +// MSVC:   %[[entry:.*]] = getelementptr inbounds i8* {{.*}}, i32 8
> +// MSVC:   %[[entry_i32:.*]] = bitcast i8* %[[entry]] to i32*
> +// MSVC:   %[[offset:.*]] = load i32* %[[entry_i32]]
> +// MSVC:   add nsw i32 0, %[[offset]]
> +// MSVC: }
> +
> +
>  BB* c() { return x; }
>  // CHECK: @_Z1cv() [[NUW]]
>  // CHECK: [[VBASEOFFSETPTRC:%[a-zA-Z0-9\.]+]] = getelementptr i8* {{.*}}, i64 -24
>  // CHECK: [[CASTVBASEOFFSETPTRC:%[a-zA-Z0-9\.]+]] = bitcast i8* [[VBASEOFFSETPTRC]] to i32*
>  // CHECK: [[VBASEOFFSETC:%[a-zA-Z0-9\.]+]] = load i32* [[CASTVBASEOFFSETPTRC]]
>  // CHECK: add i32 [[VBASEOFFSETC]], 8
>  // CHECK: }
>
> +// Same as 'a' except we use a different vbtable offset.
> +// MSVC: @"\01?c@@YAPAUBB@@XZ"() [[NUW:#[0-9]+]] {
> +// MSVC:   %[[vbptr_off:.*]] = getelementptr inbounds i8* {{.*}}, i32 0
> +// MSVC:   %[[vbptr:.*]] = bitcast i8* %[[vbptr_off]] to i8**
> +// MSVC:   %[[vbtable:.*]] = load i8** %[[vbptr]]
> +// MSVC:   %[[entry:.*]] = getelementptr inbounds i8* {{.*}}, i32 16
> +// MSVC:   %[[entry_i32:.*]] = bitcast i8* %[[entry]] to i32*
> +// MSVC:   %[[offset:.*]] = load i32* %[[entry_i32]]
> +// MSVC:   add nsw i32 0, %[[offset]]
> +// MSVC: }
> +
> +// Put the vbptr at a non-zero offset inside a non-virtual base.
> +struct E { int e; };
> +struct F : E, D { int f; };
> +
> +F* y;
> +
> +BB* d() { return y; }
> +
> +// Same as 'c' except the vbptr offset is 4, changing the initial GEP and the
> +// final add.
> +// MSVC: @"\01?d@@YAPAUBB@@XZ"() [[NUW:#[0-9]+]] {
> +// MSVC:   %[[vbptr_off:.*]] = getelementptr inbounds i8* {{.*}}, i32 4
> +// MSVC:   %[[vbptr:.*]] = bitcast i8* %[[vbptr_off]] to i8**
> +// MSVC:   %[[vbtable:.*]] = load i8** %[[vbptr]]
> +// MSVC:   %[[entry:.*]] = getelementptr inbounds i8* {{.*}}, i32 16
> +// MSVC:   %[[entry_i32:.*]] = bitcast i8* %[[entry]] to i32*
> +// MSVC:   %[[offset:.*]] = load i32* %[[entry_i32]]
> +// MSVC:   add nsw i32 4, %[[offset]]
> +// MSVC: }
> +
>  // CHECK: attributes [[NUW]] = { nounwind{{.*}} }
>

Otherwise, LGTM.

~Aaron



More information about the cfe-commits mailing list