[PATCH] Microsoft C++ Record Layout
John McCall
rjmccall at apple.com
Thu Jul 25 16:54:33 PDT 2013
On Jul 18, 2013, at 1:32 PM, Warren Hunt <whunt at google.com> wrote:
> Fixed code-gen such that all lit tests pass! The patch should be pretty much good to go.
+ /// HasOwnVBPtr - Does this class provide a virtual function table
+ /// (vtable in Itanium, VBtbl in Microsoft) that is independent from
+ /// its base classes?
+ bool HasOwnVBPtr : 1;
+
You’ve copied the comment here from HasOwnVFPtr.
+// significantly different than those produced by the Itanium ABI. Here We note
+// the most important differences.
Capitalization.
+// * The alignment of bitfields in unions is ignored when computing the
+// alignment of the union
Period.
+// * The Itanium equivelant
equivalent
+// function pointer) and a vbptr (virtual base pointer). They can each be
+// shared with a non-virtual base and each can be shared with a distinct
+// non-virtual base.
I’d just say “, possibly different ones.”
+// * Virtual bases sometimes require a 'vtordisp' field that is layed out before
laid
+// * vtordisps are allocated in a block of memory with size and alignment equal
+// to the alignment of the completed structure (before applying __declspec(
+// align())). They always occur at the end of the allocation.
Er. Unless our existing tests are wildly wrong, vtordisps always occur
before a virtual base.
+// * vbptrs are allocated in a block of memory equal to the alignment of the
+// fields and non-virtual bases at a potentially unaligned offset.
This doesn’t seem to be true; the vbptr itself always seems to be aligned.
+// * The last zero sized non-virtual base is allocated after the placement of
+// vbprt
the vbptr
+ void Layout(const RecordDecl *RD);
Since this is all new code, please lowercase method names in accordance
with LLVM style conventions.
+ // Look at all of our methods to determine if we need a VFPtr. We need a
+ // vfptr if we define a new virtual function.
+ if (!HasVFPtr && RD->isDynamicClass())
+ for (clang::CXXRecordDecl::method_iterator i = RD->method_begin(),
+ e = RD->method_end();
+ !HasVFPtr && i != e; ++i)
+ HasVFPtr = i->isVirtual() && i->size_overridden_methods() == 0;
+ if (!HasVFPtr)
+ return;
I believe we also need a new vf-table if we have a non-trivial covariant
override of a virtual function.
+ UpdateAlignment(Layout.getAlignment());
If this really isn’t affected by whether the class is packed, that’s worth
a comment.
+ // Use LayoutFields to compute the alignment of the fields. The layout
+ // is discarded. This is the simplest way to get all of the bit-field
+ // behavior correct and is not actually very expensive.
+ LayoutFields(RD);
+ Size = CharUnits::Zero();
+ FieldOffsets.clear();
I feel like FieldOffsets and LastFieldIsNonZeroWidthBitfield should be reset
in the same place, probably here.
+ // MSVC potentially over-aligns the vb-table pointer by giving it
+ // the max alignment of all the non-virtual objects in the class.
+ // This is completely unnecessary, but we're not here to pass
+ // judgment.
Er, what follows is all the nvdata, not just the vbptr. What you’re saying
is that MSVC over-aligns following a new vfptr, basically as if the layout
used struct layout rules for this type:
{ vfptr, { nvdata } }
+ // Empty bases only consume space when followed by another empty base.
+ if (RD && Context.getASTRecordLayout(RD).getNonVirtualSize().isZero())
Please grab this layout once at the top of the method.
+ CharUnits LazyEmptyBaseOffset = CharUnits::Zero();
+
Dead variable?
+ // Handle strange padding rules. I have no explenation for why the
+ // virtual base is padded in such an odd way. My guess is that they
+ // allways Add 2 * Alignment and incorrectly round down to the appropriate
explanation
always
+ CharUnits x = Size + Alignment + Alignment;
This variable can be sunk to where it’s used.
+ if (!RD->field_empty())
+ Size += CharUnits::fromQuantity(
+ x % getAdjustedFieldInfo(*RD->field_begin()).second);
This is a really bizarre calculation, but if it’s what they do...
field_begin() and field_empty() do some non-trivial redundant
work; please re-use the result of field_begin().
+ if (FD->isBitField()) {
+ LayoutBitField(FD);
+ return;
+ }
+
+ std::pair<CharUnits, CharUnits> FieldInfo = getAdjustedFieldInfo(FD);
+ CharUnits FieldSize = FieldInfo.first;
+ CharUnits FieldAlign = FieldInfo.second;
+
+ LastFieldIsNonZeroWidthBitfield = false;
I feel that the invariants would be clearer if you put this line immediately
after the isBitField() block.
+ // Clamp the bitfield to a containable size for the sake of being able
+ // to lay them out. Sema will error later.
I’d expect Sema to have already errored, actually.
+ if (!IsUnion && LastFieldIsNonZeroWidthBitfield &&
+ CurrentBitfieldSize == FieldSize && Width <= RemainingBitsInField) {
+ PlaceFieldAtBitOffset(Context.toBits(Size) - RemainingBitsInField);
+ RemainingBitsInField -= Width;
+ return;
Most of these checks are obvious, but please add a comment pointing out
the unusual thing here: that MSVC refuses to pack bit-fields together if their
formal type has a different size.
+ // TODO (whunt): Add a Sema warning that MS ignores bitfield alignment
+ // in unions.
We kindof frown on personalized TODOs.
+ if (getTargetInfo().getTriple().getArch() == llvm::Triple::x86 &&
+ getTargetInfo().getTriple().getOS() == llvm::Triple::Win32 &&
+ !D->isMsStruct(*this)) {
+ NewEntry = BuildMicrosoftASTRecordLayout(D);
+ } else {
This should be checking the target C++ ABI, not the target triple directly.
Also, I don’t understand why it’s *filtering out* things with ms_struct?
Also, you’ve left the rest of this block mis-indented.
You should really just do this at the top level:
if (getCXXABI().isMicrosoft()) {
NewEntry = BuildMicrosoftASTRecordLayout(D);
} else if (const CXXRecordDecl *RD = dyn_cast<CXXRecordDecl>(D)) {
...
- // MSVC gives the vb-table pointer an alignment equal to that of
- // the non-virtual part of the structure. That's an inherently
- // multi-pass operation. If our first pass doesn't give us
- // adequate alignment, try again with the specified minimum
- // alignment. This is *much* more maintainable than computing the
- // alignment in advance in a separately-coded pass; it's also
- // significantly more efficient in the common case where the
- // vb-table doesn't need extra padding.
- if (Builder.VBPtrOffset != CharUnits::fromQuantity(-1) &&
- (Builder.VBPtrOffset % Builder.NonVirtualAlignment) != 0) {
- Builder.resetWithTargetAlignment(Builder.NonVirtualAlignment);
- Builder.Layout(RD);
- }
-
This is great to remove, but you should also remove the *rest* of the
code in this file which is now completely dead.
Index: test/Layout/ms-x86-aligned-tail-padding.cpp
===================================================================
--- /dev/null
+++ test/Layout/ms-x86-aligned-tail-padding.cpp
All of your new tests have carriage returns.
-// CHECK-NEXT: sizeof=20, dsize=20, align=4
+// CHECK-NEXT: sizeof=20, dsize=4, align=4
You have a bunch of changes like this. dsize doesn’t really apply to the
MS ABI, which never tail-allocates anything; if it’s at all convenient to do so,
maybe we just shouldn’t be printing it.
John.
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://lists.llvm.org/pipermail/cfe-commits/attachments/20130725/b7ac37f3/attachment.html>
More information about the cfe-commits
mailing list