[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