r270457 - [MS ABI] Implement __declspec(empty_bases) and __declspec(layout_version)

Aaron Ballman via cfe-commits cfe-commits at lists.llvm.org
Mon May 23 10:45:57 PDT 2016


On Mon, May 23, 2016 at 1:36 PM, David Majnemer
<david.majnemer at gmail.com> wrote:
>
>
> On Mon, May 23, 2016 at 10:28 AM, Aaron Ballman <aaron at aaronballman.com>
> wrote:
>>
>> On Mon, May 23, 2016 at 1:16 PM, David Majnemer via cfe-commits
>> <cfe-commits at lists.llvm.org> wrote:
>> > Author: majnemer
>> > Date: Mon May 23 12:16:12 2016
>> > New Revision: 270457
>> >
>> > URL: http://llvm.org/viewvc/llvm-project?rev=270457&view=rev
>> > Log:
>> > [MS ABI] Implement __declspec(empty_bases) and
>> > __declspec(layout_version)
>> >
>> > The layout_version attribute is pretty straightforward: use the layout
>> > rules from version XYZ of MSVC when used like
>> > struct __declspec(layout_version(XYZ)) S {};
>> >
>> > The empty_bases attribute is more interesting.  It tries to get the C++
>> > empty base optimization to fire more often by tweaking the MSVC ABI
>> > rules in subtle ways:
>> > 1. Disable the leading and trailing zero-sized object flags if a class
>> >    is marked __declspec(empty_bases) and is empty.
>> >
>> >    This means that given:
>> >    struct __declspec(empty_bases) A {};
>> >    struct __declspec(empty_bases) B {};
>> >    struct C : A, B {};
>> >
>> >    'C' will have size 1 and nvsize 0 despite not being annotated
>> >    __declspec(empty_bases).
>> >
>> > 2. When laying out virtual or non-virtual bases, disable the injection
>> >    of padding between classes if the most derived class is marked
>> >    __declspec(empty_bases).
>> >
>> >    This means that given:
>> >    struct A {};
>> >    struct B {};
>> >    struct __declspec(empty_bases) C : A, B {};
>> >
>> >    'C' will have size 1 and nvsize 0.
>> >
>> > 3. When calculating the offset of a non-virtual base, choose offset zero
>> >    if the most derived class is marked __declspec(empty_bases) and the
>> >    base is empty _and_ has an nvsize of 0.
>> >
>> >    Because of the ABI rules, this does not mean that empty bases
>> >    reliably get placed at offset 0!
>> >
>> >    For example:
>> >    struct A {};
>> >    struct B {};
>> >    struct __declspec(empty_bases) C : A, B { virtual ~C(); };
>> >
>> >    'C' will be pointer sized to account for the vfptr at offset 0.
>> >    'A' and 'B' will _not_ be at offset 0 despite being empty!
>> >    Instead, they will be located right after the vfptr.
>> >
>> >    This occurs due to the interaction betweeen non-virtual base layout
>> >    and virtual function pointer injection: injection occurs after the
>> >    nv-bases and shifts them down by the size of a pointer.
>> >
>> > Added:
>> >     cfe/trunk/test/Layout/ms-x86-declspec-empty_bases.cpp
>> >     cfe/trunk/test/SemaCXX/ms-empty_bases.cpp
>> >     cfe/trunk/test/SemaCXX/ms-layout_version.cpp
>> > Modified:
>> >     cfe/trunk/include/clang/AST/RecordLayout.h
>> >     cfe/trunk/include/clang/Basic/Attr.td
>> >     cfe/trunk/include/clang/Basic/AttrDocs.td
>> >     cfe/trunk/lib/AST/RecordLayout.cpp
>> >     cfe/trunk/lib/AST/RecordLayoutBuilder.cpp
>> >     cfe/trunk/lib/Sema/SemaDeclAttr.cpp
>> >
>> > Modified: cfe/trunk/include/clang/AST/RecordLayout.h
>> > URL:
>> > http://llvm.org/viewvc/llvm-project/cfe/trunk/include/clang/AST/RecordLayout.h?rev=270457&r1=270456&r2=270457&view=diff
>> >
>> > ==============================================================================
>> > --- cfe/trunk/include/clang/AST/RecordLayout.h (original)
>> > +++ cfe/trunk/include/clang/AST/RecordLayout.h Mon May 23 12:16:12 2016
>> > @@ -104,10 +104,10 @@ private:
>> >      /// a primary base class.
>> >      bool HasExtendableVFPtr : 1;
>> >
>> > -    /// HasZeroSizedSubObject - True if this class contains a zero
>> > sized member
>> > -    /// or base or a base with a zero sized member or base.  Only used
>> > for
>> > -    /// MS-ABI.
>> > -    bool HasZeroSizedSubObject : 1;
>> > +    /// EndsWithZeroSizedObject - True if this class contains a zero
>> > sized
>> > +    /// member or base or a base with a zero sized member or base.
>> > +    /// Only used for MS-ABI.
>> > +    bool EndsWithZeroSizedObject : 1;
>> >
>> >      /// \brief True if this class is zero sized or first base is zero
>> > sized or
>> >      /// has this property.  Only used for MS-ABI.
>> > @@ -154,7 +154,7 @@ private:
>> >                    const CXXRecordDecl *PrimaryBase,
>> >                    bool IsPrimaryBaseVirtual,
>> >                    const CXXRecordDecl *BaseSharingVBPtr,
>> > -                  bool HasZeroSizedSubObject,
>> > +                  bool EndsWithZeroSizedObject,
>> >                    bool LeadsWithZeroSizedBase,
>> >                    const BaseOffsetsMapTy& BaseOffsets,
>> >                    const VBaseOffsetsMapTy& VBaseOffsets);
>> > @@ -283,8 +283,8 @@ public:
>> >      return RequiredAlignment;
>> >    }
>> >
>> > -  bool hasZeroSizedSubObject() const {
>> > -    return CXXInfo && CXXInfo->HasZeroSizedSubObject;
>> > +  bool endsWithZeroSizedObject() const {
>> > +    return CXXInfo && CXXInfo->EndsWithZeroSizedObject;
>> >    }
>> >
>> >    bool leadsWithZeroSizedBase() const {
>> >
>> > Modified: cfe/trunk/include/clang/Basic/Attr.td
>> > URL:
>> > http://llvm.org/viewvc/llvm-project/cfe/trunk/include/clang/Basic/Attr.td?rev=270457&r1=270456&r2=270457&view=diff
>> >
>> > ==============================================================================
>> > --- cfe/trunk/include/clang/Basic/Attr.td (original)
>> > +++ cfe/trunk/include/clang/Basic/Attr.td Mon May 23 12:16:12 2016
>> > @@ -745,6 +745,12 @@ def Destructor : InheritableAttr {
>> >    let Documentation = [Undocumented];
>> >  }
>> >
>> > +def EmptyBases : InheritableAttr,
>> > TargetSpecificAttr<TargetMicrosoftCXXABI> {
>> > +  let Spellings = [Declspec<"empty_bases">];
>> > +  let Subjects = SubjectList<[CXXRecord]>;
>> > +  let Documentation = [EmptyBasesDocs];
>> > +}
>> > +
>> >  def EnableIf : InheritableAttr {
>> >    let Spellings = [GNU<"enable_if">];
>> >    let Subjects = SubjectList<[Function]>;
>> > @@ -868,6 +874,13 @@ def Restrict : InheritableAttr {
>> >    let Documentation = [Undocumented];
>> >  }
>> >
>> > +def LayoutVersion : InheritableAttr,
>> > TargetSpecificAttr<TargetMicrosoftCXXABI> {
>> > +  let Spellings = [Declspec<"layout_version">];
>> > +  let Args = [UnsignedArgument<"Version">];
>> > +  let Subjects = SubjectList<[CXXRecord]>;
>> > +  let Documentation = [LayoutVersionDocs];
>> > +}
>> > +
>> >  def MaxFieldAlignment : InheritableAttr {
>> >    // This attribute has no spellings as it is only ever created
>> > implicitly.
>> >    let Spellings = [];
>> >
>> > Modified: cfe/trunk/include/clang/Basic/AttrDocs.td
>> > URL:
>> > http://llvm.org/viewvc/llvm-project/cfe/trunk/include/clang/Basic/AttrDocs.td?rev=270457&r1=270456&r2=270457&view=diff
>> >
>> > ==============================================================================
>> > --- cfe/trunk/include/clang/Basic/AttrDocs.td (original)
>> > +++ cfe/trunk/include/clang/Basic/AttrDocs.td Mon May 23 12:16:12 2016
>> > @@ -1553,6 +1553,24 @@ manipulating bits of the enumerator when
>> >    }];
>> >  }
>> >
>> > +def EmptyBasesDocs : Documentation {
>> > +  let Category = DocCatType;
>> > +  let Content = [{
>> > +The empty_bases attribute permits the compiler to utilize the
>> > +empty-base-optimization more frequently.
>> > +It is only supported when using the Microsoft C++ ABI.
>> > +  }];
>> > +}
>> > +
>> > +def LayoutVersionDocs : Documentation {
>> > +  let Category = DocCatType;
>> > +  let Content = [{
>> > +The layout_version attribute requests that the compiler utilize the
>> > class
>> > +layout rules of a particular compiler version.
>> > +It is only supported when using the Microsoft C++ ABI.
>> > +  }];
>>
>> Can you link to MSDN documentation for these two attributes to provide
>> a bit more information? If not, it would be good to point out that
>> both of these only apply to struct, class, and union types, and any
>> other constraints that may be of interest to the user.
>
>
> Microsoft hasn't updated the list of documented declspecs for quite some
> time.  I'll amend our documentation.

Thank you!

>
>>
>>
>> > +}
>> > +
>> >  def MSInheritanceDocs : Documentation {
>> >    let Category = DocCatType;
>> >    let Heading = "__single_inhertiance, __multiple_inheritance,
>> > __virtual_inheritance";
>> >
>> > Modified: cfe/trunk/lib/AST/RecordLayout.cpp
>> > URL:
>> > http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/AST/RecordLayout.cpp?rev=270457&r1=270456&r2=270457&view=diff
>> >
>> > ==============================================================================
>> > --- cfe/trunk/lib/AST/RecordLayout.cpp (original)
>> > +++ cfe/trunk/lib/AST/RecordLayout.cpp Mon May 23 12:16:12 2016
>> > @@ -58,7 +58,7 @@ ASTRecordLayout::ASTRecordLayout(const A
>> >                                   const CXXRecordDecl *PrimaryBase,
>> >                                   bool IsPrimaryBaseVirtual,
>> >                                   const CXXRecordDecl *BaseSharingVBPtr,
>> > -                                 bool HasZeroSizedSubObject,
>> > +                                 bool EndsWithZeroSizedObject,
>> >                                   bool LeadsWithZeroSizedBase,
>> >                                   const BaseOffsetsMapTy& BaseOffsets,
>> >                                   const VBaseOffsetsMapTy& VBaseOffsets)
>> > @@ -82,7 +82,7 @@ ASTRecordLayout::ASTRecordLayout(const A
>> >    CXXInfo->VBPtrOffset = vbptroffset;
>> >    CXXInfo->HasExtendableVFPtr = hasExtendableVFPtr;
>> >    CXXInfo->BaseSharingVBPtr = BaseSharingVBPtr;
>> > -  CXXInfo->HasZeroSizedSubObject = HasZeroSizedSubObject;
>> > +  CXXInfo->EndsWithZeroSizedObject = EndsWithZeroSizedObject;
>> >    CXXInfo->LeadsWithZeroSizedBase = LeadsWithZeroSizedBase;
>> >
>> >
>> >
>> > Modified: cfe/trunk/lib/AST/RecordLayoutBuilder.cpp
>> > URL:
>> > http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/AST/RecordLayoutBuilder.cpp?rev=270457&r1=270456&r2=270457&view=diff
>> >
>> > ==============================================================================
>> > --- cfe/trunk/lib/AST/RecordLayoutBuilder.cpp (original)
>> > +++ cfe/trunk/lib/AST/RecordLayoutBuilder.cpp Mon May 23 12:16:12 2016
>> > @@ -2228,7 +2228,8 @@ public:
>> >    /// laid out.
>> >    void initializeCXXLayout(const CXXRecordDecl *RD);
>> >    void layoutNonVirtualBases(const CXXRecordDecl *RD);
>> > -  void layoutNonVirtualBase(const CXXRecordDecl *BaseDecl,
>> > +  void layoutNonVirtualBase(const CXXRecordDecl *RD,
>> > +                            const CXXRecordDecl *BaseDecl,
>> >                              const ASTRecordLayout &BaseLayout,
>> >                              const ASTRecordLayout
>> > *&PreviousBaseLayout);
>> >    void injectVFPtr(const CXXRecordDecl *RD);
>> > @@ -2334,7 +2335,7 @@ MicrosoftRecordLayoutBuilder::getAdjuste
>> >    if (!MaxFieldAlignment.isZero())
>> >      Info.Alignment = std::min(Info.Alignment, MaxFieldAlignment);
>> >    // Track zero-sized subobjects here where it's already available.
>> > -  EndsWithZeroSizedObject = Layout.hasZeroSizedSubObject();
>> > +  EndsWithZeroSizedObject = Layout.endsWithZeroSizedObject();
>> >    // Respect required alignment, this is necessary because we may have
>> > adjusted
>> >    // the alignment in the case of pragam pack.  Note that the required
>> > alignment
>> >    // doesn't actually apply to the struct alignment at this point.
>> > @@ -2369,7 +2370,7 @@ MicrosoftRecordLayoutBuilder::getAdjuste
>> >      if (auto RT =
>> >
>> > FD->getType()->getBaseElementTypeUnsafe()->getAs<RecordType>()) {
>> >        auto const &Layout = Context.getASTRecordLayout(RT->getDecl());
>> > -      EndsWithZeroSizedObject = Layout.hasZeroSizedSubObject();
>> > +      EndsWithZeroSizedObject = Layout.endsWithZeroSizedObject();
>> >        FieldRequiredAlignment = std::max(FieldRequiredAlignment,
>> >                                          Layout.getRequiredAlignment());
>> >      }
>> > @@ -2502,7 +2503,7 @@ MicrosoftRecordLayoutBuilder::layoutNonV
>> >        LeadsWithZeroSizedBase = BaseLayout.leadsWithZeroSizedBase();
>> >      }
>> >      // Lay out the base.
>> > -    layoutNonVirtualBase(BaseDecl, BaseLayout, PreviousBaseLayout);
>> > +    layoutNonVirtualBase(RD, BaseDecl, BaseLayout, PreviousBaseLayout);
>> >    }
>> >    // Figure out if we need a fresh VFPtr for this class.
>> >    if (!PrimaryBase && RD->isDynamicClass())
>> > @@ -2531,7 +2532,7 @@ MicrosoftRecordLayoutBuilder::layoutNonV
>> >        LeadsWithZeroSizedBase = BaseLayout.leadsWithZeroSizedBase();
>> >      }
>> >      // Lay out the base.
>> > -    layoutNonVirtualBase(BaseDecl, BaseLayout, PreviousBaseLayout);
>> > +    layoutNonVirtualBase(RD, BaseDecl, BaseLayout, PreviousBaseLayout);
>> >      VBPtrOffset = Bases[BaseDecl] + BaseLayout.getNonVirtualSize();
>> >    }
>> >    // Set our VBPtroffset if we know it at this point.
>> > @@ -2543,15 +2544,32 @@ MicrosoftRecordLayoutBuilder::layoutNonV
>> >    }
>> >  }
>> >
>> > +static bool recordUsesEBO(const RecordDecl *RD) {
>> > +  if (!isa<CXXRecordDecl>(RD))
>> > +    return false;
>> > +  if (RD->hasAttr<EmptyBasesAttr>())
>> > +    return true;
>> > +  if (auto *LVA = RD->getAttr<LayoutVersionAttr>())
>> > +    // TODO: Double check with the next version of MSVC.
>> > +    if (LVA->getVersion() <= LangOptions::MSVC2015)
>> > +      return false;
>> > +  // TODO: Some later version of MSVC will change the default behavior
>> > of the
>> > +  // compiler to enable EBO by default.  When this happens, we will
>> > need an
>> > +  // additional isCompatibleWithMSVC check.
>> > +  return false;
>> > +}
>> > +
>> >  void MicrosoftRecordLayoutBuilder::layoutNonVirtualBase(
>> > +    const CXXRecordDecl *RD,
>> >      const CXXRecordDecl *BaseDecl,
>> >      const ASTRecordLayout &BaseLayout,
>> >      const ASTRecordLayout *&PreviousBaseLayout) {
>> >    // Insert padding between two bases if the left first one is zero
>> > sized or
>> >    // contains a zero sized subobject and the right is zero sized or one
>> > leads
>> >    // with a zero sized base.
>> > -  if (PreviousBaseLayout && PreviousBaseLayout->hasZeroSizedSubObject()
>> > &&
>> > -      BaseLayout.leadsWithZeroSizedBase())
>> > +  bool MDCUsesEBO = recordUsesEBO(RD);
>> > +  if (PreviousBaseLayout &&
>> > PreviousBaseLayout->endsWithZeroSizedObject() &&
>> > +      BaseLayout.leadsWithZeroSizedBase() && !MDCUsesEBO)
>> >      Size++;
>> >    ElementInfo Info = getAdjustedElementInfo(BaseLayout);
>> >    CharUnits BaseOffset;
>> > @@ -2560,14 +2578,23 @@ void MicrosoftRecordLayoutBuilder::layou
>> >    bool FoundBase = false;
>> >    if (UseExternalLayout) {
>> >      FoundBase = External.getExternalNVBaseOffset(BaseDecl, BaseOffset);
>> > -    if (FoundBase)
>> > +    if (FoundBase) {
>> >        assert(BaseOffset >= Size && "base offset already allocated");
>> > +      Size = BaseOffset;
>> > +    }
>> >    }
>> >
>> > -  if (!FoundBase)
>> > -    BaseOffset = Size.alignTo(Info.Alignment);
>> > +  if (!FoundBase) {
>> > +    if (MDCUsesEBO && BaseDecl->isEmpty() &&
>> > +        BaseLayout.getNonVirtualSize() == CharUnits::Zero()) {
>> > +      BaseOffset = CharUnits::Zero();
>> > +    } else {
>> > +      // Otherwise, lay the base out at the end of the MDC.
>> > +      BaseOffset = Size = Size.alignTo(Info.Alignment);
>> > +    }
>> > +  }
>> >    Bases.insert(std::make_pair(BaseDecl, BaseOffset));
>> > -  Size = BaseOffset + BaseLayout.getNonVirtualSize();
>> > +  Size += BaseLayout.getNonVirtualSize();
>> >    PreviousBaseLayout = &BaseLayout;
>> >  }
>> >
>> > @@ -2746,8 +2773,9 @@ void MicrosoftRecordLayoutBuilder::layou
>> >      // with a zero sized base.  The padding between virtual bases is 4
>> >      // bytes (in both 32 and 64 bits modes) and always involves
>> > rounding up to
>> >      // the required alignment, we don't know why.
>> > -    if ((PreviousBaseLayout &&
>> > PreviousBaseLayout->hasZeroSizedSubObject() &&
>> > -        BaseLayout.leadsWithZeroSizedBase()) || HasVtordisp) {
>> > +    if ((PreviousBaseLayout &&
>> > PreviousBaseLayout->endsWithZeroSizedObject() &&
>> > +         BaseLayout.leadsWithZeroSizedBase() && !recordUsesEBO(RD)) ||
>> > +        HasVtordisp) {
>> >        Size = Size.alignTo(VtorDispAlignment) + VtorDispSize;
>> >        Alignment = std::max(VtorDispAlignment, Alignment);
>> >      }
>> > @@ -2785,8 +2813,10 @@ void MicrosoftRecordLayoutBuilder::final
>> >      Size = Size.alignTo(RoundingAlignment);
>> >    }
>> >    if (Size.isZero()) {
>> > -    EndsWithZeroSizedObject = true;
>> > -    LeadsWithZeroSizedBase = true;
>> > +    if (!recordUsesEBO(RD) || !cast<CXXRecordDecl>(RD)->isEmpty()) {
>> > +      EndsWithZeroSizedObject = true;
>> > +      LeadsWithZeroSizedBase = true;
>> > +    }
>> >      // Zero-sized structures have size equal to their alignment if a
>> >      // __declspec(align) came into play.
>> >      if (RequiredAlignment >= MinEmptyStructSize)
>> > @@ -2919,7 +2949,7 @@ ASTContext::getASTRecordLayout(const Rec
>> >        NewEntry = new (*this) ASTRecordLayout(
>> >            *this, Builder.Size, Builder.Alignment,
>> > Builder.RequiredAlignment,
>> >            Builder.HasOwnVFPtr, Builder.HasOwnVFPtr ||
>> > Builder.PrimaryBase,
>> > -          Builder.VBPtrOffset, Builder.NonVirtualSize,
>> > +          Builder.VBPtrOffset, Builder.DataSize,
>> >            Builder.FieldOffsets.data(), Builder.FieldOffsets.size(),
>> >            Builder.NonVirtualSize, Builder.Alignment, CharUnits::Zero(),
>> >            Builder.PrimaryBase, false, Builder.SharedVBPtrBase,
>> >
>> > Modified: cfe/trunk/lib/Sema/SemaDeclAttr.cpp
>> > URL:
>> > http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/Sema/SemaDeclAttr.cpp?rev=270457&r1=270456&r2=270457&view=diff
>> >
>> > ==============================================================================
>> > --- cfe/trunk/lib/Sema/SemaDeclAttr.cpp (original)
>> > +++ cfe/trunk/lib/Sema/SemaDeclAttr.cpp Mon May 23 12:16:12 2016
>> > @@ -4968,6 +4968,24 @@ static void handleX86ForceAlignArgPointe
>> >
>> > Attr.getAttributeSpellingListIndex()));
>> >  }
>> >
>> > +static void handleLayoutVersion(Sema &S, Decl *D, const AttributeList
>> > &Attr) {
>> > +  uint32_t Version;
>> > +  Expr *VersionExpr = static_cast<Expr *>(Attr.getArgAsExpr(0));
>> > +  if (!checkUInt32Argument(S, Attr, Attr.getArgAsExpr(0), Version))
>> > +    return;
>> > +
>> > +  // TODO: Investigate what happens with the next major version of
>> > MSVC.
>> > +  if (Version != LangOptions::MSVC2015) {
>> > +    S.Diag(Attr.getLoc(), diag::err_attribute_argument_out_of_bounds)
>> > +        << Attr.getName() << Version << VersionExpr->getSourceRange();
>> > +    return;
>> > +  }
>>
>> So this cannot accept minor version numbers? (Presumably because minor
>> and patch versions should not be changing layouts, I suppose.)
>
>
> It cannot.

Makes sense, thank you.

>
>>
>>
>> > +
>> > +  D->addAttr(::new (S.Context)
>> > +                 LayoutVersionAttr(Attr.getRange(), S.Context, Version,
>> > +
>> > Attr.getAttributeSpellingListIndex()));
>> > +}
>> > +
>> >  DLLImportAttr *Sema::mergeDLLImportAttr(Decl *D, SourceRange Range,
>> >                                          unsigned AttrSpellingListIndex)
>> > {
>> >    if (D->hasAttr<DLLExportAttr>()) {
>> > @@ -5749,6 +5767,12 @@ static void ProcessDeclAttribute(Sema &S
>> >      break;
>> >
>> >    // Microsoft attributes:
>> > +  case AttributeList::AT_EmptyBases:
>> > +    handleSimpleAttribute<EmptyBasesAttr>(S, D, Attr);
>> > +    break;
>> > +  case AttributeList::AT_LayoutVersion:
>> > +    handleLayoutVersion(S, D, Attr);
>> > +    break;
>> >    case AttributeList::AT_MSNoVTable:
>> >      handleSimpleAttribute<MSNoVTableAttr>(S, D, Attr);
>> >      break;
>> > @@ -5767,6 +5791,7 @@ static void ProcessDeclAttribute(Sema &S
>> >    case AttributeList::AT_Thread:
>> >      handleDeclspecThreadAttr(S, D, Attr);
>> >      break;
>> > +
>>
>> Spurious newline added.
>
>
> Spurious newline intended.  the abi_tag attribute is most certainly _not_ a
> Microsoft attribute.

Ah, drive-by newline, I like it.

>
>>
>>
>> >    case AttributeList::AT_AbiTag:
>> >      handleAbiTagAttr(S, D, Attr);
>> >      break;
>> >
>> > Added: cfe/trunk/test/Layout/ms-x86-declspec-empty_bases.cpp
>> > URL:
>> > http://llvm.org/viewvc/llvm-project/cfe/trunk/test/Layout/ms-x86-declspec-empty_bases.cpp?rev=270457&view=auto
>> >
>> > ==============================================================================
>> > --- cfe/trunk/test/Layout/ms-x86-declspec-empty_bases.cpp (added)
>> > +++ cfe/trunk/test/Layout/ms-x86-declspec-empty_bases.cpp Mon May 23
>> > 12:16:12 2016
>> > @@ -0,0 +1,266 @@
>> > +// RUN: %clang_cc1 -fno-rtti -emit-llvm-only -triple i686-pc-win32
>> > -fms-extensions -fdump-record-layouts -fsyntax-only %s 2>/dev/null \
>> > +// RUN:            | FileCheck %s
>> > +// RUN: %clang_cc1 -fno-rtti -emit-llvm-only -triple x86_64-pc-win32
>> > -fms-extensions -fdump-record-layouts -fsyntax-only %s 2>/dev/null \
>> > +// RUN:            | FileCheck %s
>> > +
>> > +namespace test1 {
>> > +
>> > +struct A {
>> > +  int a;
>> > +};
>> > +struct B {
>> > +  int b;
>> > +};
>> > +struct C {};
>> > +struct __declspec(align(16)) D {};
>> > +struct __declspec(empty_bases) X : A, D, B, C {
>> > +};
>> > +
>> > +// CHECK: *** Dumping AST Record Layout
>> > +// CHECK-NEXT:          0 | struct test1::A
>> > +// CHECK-NEXT:          0 |   int a
>> > +// CHECK-NEXT:            | [sizeof=4, align=4,
>> > +// CHECK-NEXT:            |  nvsize=4, nvalign=4]
>> > +
>> > +// CHECK: *** Dumping AST Record Layout
>> > +// CHECK-NEXT:          0 | struct test1::D (empty)
>> > +// CHECK-NEXT:            | [sizeof=16, align=16,
>> > +// CHECK-NEXT:            |  nvsize=0, nvalign=16]
>> > +
>> > +// CHECK: *** Dumping AST Record Layout
>> > +// CHECK-NEXT:          0 | struct test1::B
>> > +// CHECK-NEXT:          0 |   int b
>> > +// CHECK-NEXT:            | [sizeof=4, align=4,
>> > +// CHECK-NEXT:            |  nvsize=4, nvalign=4]
>> > +
>> > +// CHECK: *** Dumping AST Record Layout
>> > +// CHECK-NEXT:          0 | struct test1::C (empty)
>> > +// CHECK-NEXT:            | [sizeof=1, align=1,
>> > +// CHECK-NEXT:            |  nvsize=0, nvalign=1]
>> > +
>> > +// CHECK: *** Dumping AST Record Layout
>> > +// CHECK-NEXT:          0 | struct test1::X
>> > +// CHECK-NEXT:          0 |   struct test1::A (base)
>> > +// CHECK-NEXT:          0 |     int a
>> > +// CHECK-NEXT:          0 |   struct test1::D (base) (empty)
>> > +// CHECK-NEXT:          0 |   struct test1::C (base) (empty)
>> > +// CHECK-NEXT:          4 |   struct test1::B (base)
>> > +// CHECK-NEXT:          4 |     int b
>> > +// CHECK-NEXT:            | [sizeof=16, align=16,
>> > +// CHECK-NEXT:            |  nvsize=16, nvalign=16]
>> > +
>> > +int _ = sizeof(X);
>> > +}
>> > +
>> > +namespace test2 {
>> > +struct A {
>> > +  int a;
>> > +};
>> > +struct __declspec(empty_bases) B {};
>> > +struct C : A {
>> > +  B b;
>> > +};
>> > +
>> > +struct D {};
>> > +struct E {
>> > +  int e;
>> > +};
>> > +struct F : D, E {};
>> > +
>> > +struct G : C, F {};
>> > +
>> > +int _ = sizeof(G);
>> > +
>> > +// CHECK: *** Dumping AST Record Layout
>> > +// CHECK-NEXT:          0 | struct test2::A
>> > +// CHECK-NEXT:          0 |   int a
>> > +// CHECK-NEXT:            | [sizeof=4, align=4,
>> > +// CHECK-NEXT:            |  nvsize=4, nvalign=4]
>> > +
>> > +// CHECK: *** Dumping AST Record Layout
>> > +// CHECK-NEXT:          0 | struct test2::B (empty)
>> > +// CHECK-NEXT:            | [sizeof=1, align=1,
>> > +// CHECK-NEXT:            |  nvsize=0, nvalign=1]
>> > +
>> > +// CHECK: *** Dumping AST Record Layout
>> > +// CHECK-NEXT:          0 | struct test2::C
>> > +// CHECK-NEXT:          0 |   struct test2::A (base)
>> > +// CHECK-NEXT:          0 |     int a
>> > +// CHECK-NEXT:          4 |   struct test2::B b (empty)
>> > +// CHECK-NEXT:            | [sizeof=8, align=4,
>> > +// CHECK-NEXT:            |  nvsize=8, nvalign=4]
>> > +
>> > +// CHECK: *** Dumping AST Record Layout
>> > +// CHECK-NEXT:          0 | struct test2::D (empty)
>> > +// CHECK-NEXT:            | [sizeof=1, align=1,
>> > +// CHECK-NEXT:            |  nvsize=0, nvalign=1]
>> > +
>> > +// CHECK: *** Dumping AST Record Layout
>> > +// CHECK-NEXT:          0 | struct test2::E
>> > +// CHECK-NEXT:          0 |   int e
>> > +// CHECK-NEXT:            | [sizeof=4, align=4,
>> > +// CHECK-NEXT:            |  nvsize=4, nvalign=4]
>> > +
>> > +// CHECK: *** Dumping AST Record Layout
>> > +// CHECK-NEXT:          0 | struct test2::F
>> > +// CHECK-NEXT:          0 |   struct test2::D (base) (empty)
>> > +// CHECK-NEXT:          0 |   struct test2::E (base)
>> > +// CHECK-NEXT:          0 |     int e
>> > +// CHECK-NEXT:            | [sizeof=4, align=4,
>> > +// CHECK-NEXT:            |  nvsize=4, nvalign=4]
>> > +
>> > +// CHECK: *** Dumping AST Record Layout
>> > +// CHECK-NEXT:          0 | struct test2::G
>> > +// CHECK-NEXT:          0 |   struct test2::C (base)
>> > +// CHECK-NEXT:          0 |     struct test2::A (base)
>> > +// CHECK-NEXT:          0 |       int a
>> > +// CHECK-NEXT:          4 |     struct test2::B b (empty)
>> > +// CHECK-NEXT:          8 |   struct test2::F (base)
>> > +// CHECK-NEXT:          8 |     struct test2::D (base) (empty)
>> > +// CHECK-NEXT:          8 |     struct test2::E (base)
>> > +// CHECK-NEXT:          8 |       int e
>> > +// CHECK-NEXT:            | [sizeof=12, align=4,
>> > +// CHECK-NEXT:            |  nvsize=12, nvalign=4]
>> > +}
>> > +
>> > +namespace test3 {
>> > +struct A {
>> > +  int a;
>> > +};
>> > +struct B {};
>> > +struct C : A {
>> > +  B b;
>> > +};
>> > +
>> > +struct D {};
>> > +struct E {
>> > +  int e;
>> > +};
>> > +struct F : D, E {};
>> > +
>> > +struct __declspec(empty_bases) G : C, F {};
>> > +
>> > +int _ = sizeof(G);
>> > +
>> > +// CHECK: *** Dumping AST Record Layout
>> > +// CHECK-NEXT:          0 | struct test3::A
>> > +// CHECK-NEXT:          0 |   int a
>> > +// CHECK-NEXT:            | [sizeof=4, align=4,
>> > +// CHECK-NEXT:            |  nvsize=4, nvalign=4]
>> > +
>> > +// CHECK: *** Dumping AST Record Layout
>> > +// CHECK-NEXT:          0 | struct test3::B (empty)
>> > +// CHECK-NEXT:            | [sizeof=1, align=1,
>> > +// CHECK-NEXT:            |  nvsize=0, nvalign=1]
>> > +
>> > +// CHECK: *** Dumping AST Record Layout
>> > +// CHECK-NEXT:          0 | struct test3::C
>> > +// CHECK-NEXT:          0 |   struct test3::A (base)
>> > +// CHECK-NEXT:          0 |     int a
>> > +// CHECK-NEXT:          4 |   struct test3::B b (empty)
>> > +// CHECK-NEXT:            | [sizeof=8, align=4,
>> > +// CHECK-NEXT:            |  nvsize=8, nvalign=4]
>> > +
>> > +// CHECK: *** Dumping AST Record Layout
>> > +// CHECK-NEXT:          0 | struct test3::D (empty)
>> > +// CHECK-NEXT:            | [sizeof=1, align=1,
>> > +// CHECK-NEXT:            |  nvsize=0, nvalign=1]
>> > +
>> > +// CHECK: *** Dumping AST Record Layout
>> > +// CHECK-NEXT:          0 | struct test3::E
>> > +// CHECK-NEXT:          0 |   int e
>> > +// CHECK-NEXT:            | [sizeof=4, align=4,
>> > +// CHECK-NEXT:            |  nvsize=4, nvalign=4]
>> > +
>> > +// CHECK: *** Dumping AST Record Layout
>> > +// CHECK-NEXT:          0 | struct test3::F
>> > +// CHECK-NEXT:          0 |   struct test3::D (base) (empty)
>> > +// CHECK-NEXT:          0 |   struct test3::E (base)
>> > +// CHECK-NEXT:          0 |     int e
>> > +// CHECK-NEXT:            | [sizeof=4, align=4,
>> > +// CHECK-NEXT:            |  nvsize=4, nvalign=4]
>> > +
>> > +// CHECK: *** Dumping AST Record Layout
>> > +// CHECK-NEXT:          0 | struct test3::G
>> > +// CHECK-NEXT:          0 |   struct test3::C (base)
>> > +// CHECK-NEXT:          0 |     struct test3::A (base)
>> > +// CHECK-NEXT:          0 |       int a
>> > +// CHECK-NEXT:          4 |     struct test3::B b (empty)
>> > +// CHECK-NEXT:          8 |   struct test3::F (base)
>> > +// CHECK-NEXT:          8 |     struct test3::D (base) (empty)
>> > +// CHECK-NEXT:          8 |     struct test3::E (base)
>> > +// CHECK-NEXT:          8 |       int e
>> > +// CHECK-NEXT:            | [sizeof=12, align=4,
>> > +// CHECK-NEXT:            |  nvsize=12, nvalign=4]
>> > +}
>> > +
>> > +namespace test4 {
>> > +struct A {
>> > +  int a;
>> > +};
>> > +struct B {};
>> > +struct C : A {
>> > +  B b;
>> > +};
>> > +
>> > +struct __declspec(empty_bases) D {};
>> > +struct E {
>> > +  int e;
>> > +};
>> > +struct F : D, E {};
>> > +
>> > +struct G : C, F {};
>> > +
>> > +int _ = sizeof(G);
>> > +
>> > +// CHECK: *** Dumping AST Record Layout
>> > +// CHECK-NEXT:          0 | struct test4::A
>> > +// CHECK-NEXT:          0 |   int a
>> > +// CHECK-NEXT:            | [sizeof=4, align=4,
>> > +// CHECK-NEXT:            |  nvsize=4, nvalign=4]
>> > +
>> > +// CHECK: *** Dumping AST Record Layout
>> > +// CHECK-NEXT:          0 | struct test4::B (empty)
>> > +// CHECK-NEXT:            | [sizeof=1, align=1,
>> > +// CHECK-NEXT:            |  nvsize=0, nvalign=1]
>> > +
>> > +// CHECK: *** Dumping AST Record Layout
>> > +// CHECK-NEXT:          0 | struct test4::C
>> > +// CHECK-NEXT:          0 |   struct test4::A (base)
>> > +// CHECK-NEXT:          0 |     int a
>> > +// CHECK-NEXT:          4 |   struct test4::B b (empty)
>> > +// CHECK-NEXT:            | [sizeof=8, align=4,
>> > +// CHECK-NEXT:            |  nvsize=8, nvalign=4]
>> > +
>> > +// CHECK: *** Dumping AST Record Layout
>> > +// CHECK-NEXT:          0 | struct test4::D (empty)
>> > +// CHECK-NEXT:            | [sizeof=1, align=1,
>> > +// CHECK-NEXT:            |  nvsize=0, nvalign=1]
>> > +
>> > +// CHECK: *** Dumping AST Record Layout
>> > +// CHECK-NEXT:          0 | struct test4::E
>> > +// CHECK-NEXT:          0 |   int e
>> > +// CHECK-NEXT:            | [sizeof=4, align=4,
>> > +// CHECK-NEXT:            |  nvsize=4, nvalign=4]
>> > +
>> > +// CHECK: *** Dumping AST Record Layout
>> > +// CHECK-NEXT:          0 | struct test4::F
>> > +// CHECK-NEXT:          0 |   struct test4::D (base) (empty)
>> > +// CHECK-NEXT:          0 |   struct test4::E (base)
>> > +// CHECK-NEXT:          0 |     int e
>> > +// CHECK-NEXT:            | [sizeof=4, align=4,
>> > +// CHECK-NEXT:            |  nvsize=4, nvalign=4]
>> > +
>> > +// CHECK: *** Dumping AST Record Layout
>> > +// CHECK-NEXT:          0 | struct test4::G
>> > +// CHECK-NEXT:          0 |   struct test4::C (base)
>> > +// CHECK-NEXT:          0 |     struct test4::A (base)
>> > +// CHECK-NEXT:          0 |       int a
>> > +// CHECK-NEXT:          4 |     struct test4::B b (empty)
>> > +// CHECK-NEXT:          8 |   struct test4::F (base)
>> > +// CHECK-NEXT:          8 |     struct test4::D (base) (empty)
>> > +// CHECK-NEXT:          8 |     struct test4::E (base)
>> > +// CHECK-NEXT:          8 |       int e
>> > +// CHECK-NEXT:            | [sizeof=12, align=4,
>> > +// CHECK-NEXT:            |  nvsize=12, nvalign=4]
>> > +}
>> >
>> > Added: cfe/trunk/test/SemaCXX/ms-empty_bases.cpp
>> > URL:
>> > http://llvm.org/viewvc/llvm-project/cfe/trunk/test/SemaCXX/ms-empty_bases.cpp?rev=270457&view=auto
>> >
>> > ==============================================================================
>> > --- cfe/trunk/test/SemaCXX/ms-empty_bases.cpp (added)
>> > +++ cfe/trunk/test/SemaCXX/ms-empty_bases.cpp Mon May 23 12:16:12 2016
>> > @@ -0,0 +1,7 @@
>> > +// RUN: %clang_cc1 -triple i386-pc-win32 %s -fsyntax-only -verify
>> > -fms-extensions -Wno-microsoft -std=c++11
>> > +
>> > +struct __declspec(empty_bases) S {};
>> > +enum __declspec(empty_bases) E {}; // expected-warning{{'empty_bases'
>> > attribute only applies to classes}}
>> > +int __declspec(empty_bases) I; // expected-warning{{'empty_bases'
>> > attribute only applies to classes}}
>> > +typedef struct T __declspec(empty_bases) U; //
>> > expected-warning{{'empty_bases' attribute only applies to classes}}
>> > +auto z = []() __declspec(empty_bases) { return nullptr; }; //
>> > expected-warning{{'empty_bases' attribute only applies to classes}}
>>
>> Missing a test that ensures the attribute accepts no arguments.
>
>
> Sure, I'll add one.
>
>>
>>
>> >
>> > Added: cfe/trunk/test/SemaCXX/ms-layout_version.cpp
>> > URL:
>> > http://llvm.org/viewvc/llvm-project/cfe/trunk/test/SemaCXX/ms-layout_version.cpp?rev=270457&view=auto
>> >
>> > ==============================================================================
>> > --- cfe/trunk/test/SemaCXX/ms-layout_version.cpp (added)
>> > +++ cfe/trunk/test/SemaCXX/ms-layout_version.cpp Mon May 23 12:16:12
>> > 2016
>> > @@ -0,0 +1,10 @@
>> > +// RUN: %clang_cc1 -triple i386-pc-win32 %s -fsyntax-only -verify
>> > -fms-extensions -Wno-microsoft -std=c++11
>> > +
>> > +struct __declspec(layout_version(19)) S {};
>> > +enum __declspec(layout_version(19)) E {}; //
>> > expected-warning{{'layout_version' attribute only applies to classes}}
>> > +int __declspec(layout_version(19)) I; //
>> > expected-warning{{'layout_version' attribute only applies to classes}}
>> > +typedef struct T __declspec(layout_version(19)) U; //
>> > expected-warning{{'layout_version' attribute only applies to classes}}
>> > +auto z = []() __declspec(layout_version(19)) { return nullptr; }; //
>> > expected-warning{{'layout_version' attribute only applies to classes}}
>> > +
>> > +struct __declspec(layout_version(18)) X {}; //
>> > expected-error{{'layout_version' attribute parameter 18 is out of bounds}}
>> > +struct __declspec(layout_version(20)) Y {}; //
>> > expected-error{{'layout_version' attribute parameter 20 is out of bounds}}
>>
>> Missing a test that ensures the attribute still fails when given no
>> arguments.
>
>
> Sure, I'll add one.

Thanks!


More information about the cfe-commits mailing list