Skip to content

Commit 481337f

Browse files
committed
Implement [[msvc::no_unique_address]]
This attribute should match the behavior of MSVC's [[msvc::no_unique_address]] attribute. Bug: llvm#49358 Differential Revision: https://reviews.llvm.org/D157762
1 parent f5592f3 commit 481337f

File tree

7 files changed

+540
-27
lines changed

7 files changed

+540
-27
lines changed

clang/include/clang/Basic/Attr.td

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1798,11 +1798,13 @@ def ArmMveStrictPolymorphism : TypeAttr, TargetSpecificAttr<TargetARM> {
17981798
let Documentation = [ArmMveStrictPolymorphismDocs];
17991799
}
18001800

1801-
def NoUniqueAddress : InheritableAttr, TargetSpecificAttr<TargetItaniumCXXABI> {
1802-
let Spellings = [CXX11<"", "no_unique_address", 201803>];
1801+
def NoUniqueAddress : InheritableAttr {
1802+
let Spellings = [CXX11<"", "no_unique_address", 201803>,
1803+
CXX11<"msvc", "no_unique_address", 201803>];
1804+
let Accessors = [Accessor<"isDefault", [CXX11<"", "no_unique_address", 201803>]>,
1805+
Accessor<"isMSVC", [CXX11<"msvc", "no_unique_address", 201803>]>];
18031806
let Subjects = SubjectList<[NonBitField], ErrorDiag>;
18041807
let Documentation = [NoUniqueAddressDocs];
1805-
let SimpleHandler = 1;
18061808
}
18071809

18081810
def ReturnsTwice : InheritableAttr {

clang/include/clang/Basic/AttrDocs.td

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1405,6 +1405,10 @@ Example usage:
14051405

14061406
``[[no_unique_address]]`` is a standard C++20 attribute. Clang supports its use
14071407
in C++11 onwards.
1408+
1409+
On MSVC targets, ``[[no_unique_address]]`` is ignored; use
1410+
``[[msvc::no_unique_address]]`` instead. Currently there is no guarantee of ABI
1411+
compatibility or stability.
14081412
}];
14091413
}
14101414

clang/lib/AST/Decl.cpp

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4505,6 +4505,14 @@ bool FieldDecl::isZeroSize(const ASTContext &Ctx) const {
45054505
if (!CXXRD->isEmpty())
45064506
return false;
45074507

4508+
// MS ABI: nonzero if class type with class type fields
4509+
if (Ctx.getTargetInfo().getCXXABI().isMicrosoft() &&
4510+
llvm::any_of(CXXRD->fields(), [](const FieldDecl *Field) {
4511+
return Field->getType()->getAs<RecordType>();
4512+
})) {
4513+
return false;
4514+
}
4515+
45084516
// Otherwise, [...] the circumstances under which the object has zero size
45094517
// are implementation-defined.
45104518
// FIXME: This might be Itanium ABI specific; we don't yet know what the MS

clang/lib/AST/RecordLayoutBuilder.cpp

Lines changed: 167 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -2545,7 +2545,10 @@ struct MicrosoftRecordLayoutBuilder {
25452545
CharUnits Alignment;
25462546
};
25472547
typedef llvm::DenseMap<const CXXRecordDecl *, CharUnits> BaseOffsetsMapTy;
2548-
MicrosoftRecordLayoutBuilder(const ASTContext &Context) : Context(Context) {}
2548+
MicrosoftRecordLayoutBuilder(const ASTContext &Context,
2549+
EmptySubobjectMap *EmptySubobjects)
2550+
: Context(Context), EmptySubobjects(EmptySubobjects) {}
2551+
25492552
private:
25502553
MicrosoftRecordLayoutBuilder(const MicrosoftRecordLayoutBuilder &) = delete;
25512554
void operator=(const MicrosoftRecordLayoutBuilder &) = delete;
@@ -2562,7 +2565,11 @@ struct MicrosoftRecordLayoutBuilder {
25622565
void layoutNonVirtualBase(const CXXRecordDecl *RD,
25632566
const CXXRecordDecl *BaseDecl,
25642567
const ASTRecordLayout &BaseLayout,
2565-
const ASTRecordLayout *&PreviousBaseLayout);
2568+
const ASTRecordLayout *&PreviousBaseLayout,
2569+
BaseSubobjectInfo *Base);
2570+
BaseSubobjectInfo *computeBaseSubobjectInfo(const CXXRecordDecl *RD,
2571+
bool IsVirtual,
2572+
BaseSubobjectInfo *Derived);
25662573
void injectVFPtr(const CXXRecordDecl *RD);
25672574
void injectVBPtr(const CXXRecordDecl *RD);
25682575
/// Lays out the fields of the record. Also rounds size up to
@@ -2595,6 +2602,12 @@ struct MicrosoftRecordLayoutBuilder {
25952602
llvm::SmallPtrSetImpl<const CXXRecordDecl *> &HasVtorDispSet,
25962603
const CXXRecordDecl *RD) const;
25972604
const ASTContext &Context;
2605+
EmptySubobjectMap *EmptySubobjects;
2606+
llvm::SpecificBumpPtrAllocator<BaseSubobjectInfo> BaseSubobjectInfoAllocator;
2607+
typedef llvm::DenseMap<const CXXRecordDecl *, BaseSubobjectInfo *>
2608+
BaseSubobjectInfoMapTy;
2609+
BaseSubobjectInfoMapTy VirtualBaseInfo;
2610+
25982611
/// The size of the record being laid out.
25992612
CharUnits Size;
26002613
/// The non-virtual size of the record layout.
@@ -2818,6 +2831,8 @@ MicrosoftRecordLayoutBuilder::layoutNonVirtualBases(const CXXRecordDecl *RD) {
28182831
// Iterate through the bases and lay out the non-virtual ones.
28192832
for (const CXXBaseSpecifier &Base : RD->bases()) {
28202833
const CXXRecordDecl *BaseDecl = Base.getType()->getAsCXXRecordDecl();
2834+
BaseSubobjectInfo *BaseInfo =
2835+
computeBaseSubobjectInfo(BaseDecl, Base.isVirtual(), nullptr);
28212836
HasPolymorphicBaseClass |= BaseDecl->isPolymorphic();
28222837
const ASTRecordLayout &BaseLayout = Context.getASTRecordLayout(BaseDecl);
28232838
// Mark and skip virtual bases.
@@ -2839,7 +2854,8 @@ MicrosoftRecordLayoutBuilder::layoutNonVirtualBases(const CXXRecordDecl *RD) {
28392854
LeadsWithZeroSizedBase = BaseLayout.leadsWithZeroSizedBase();
28402855
}
28412856
// Lay out the base.
2842-
layoutNonVirtualBase(RD, BaseDecl, BaseLayout, PreviousBaseLayout);
2857+
layoutNonVirtualBase(RD, BaseDecl, BaseLayout, PreviousBaseLayout,
2858+
BaseInfo);
28432859
}
28442860
// Figure out if we need a fresh VFPtr for this class.
28452861
if (RD->isPolymorphic()) {
@@ -2864,10 +2880,21 @@ MicrosoftRecordLayoutBuilder::layoutNonVirtualBases(const CXXRecordDecl *RD) {
28642880
bool CheckLeadingLayout = !PrimaryBase;
28652881
// Iterate through the bases and lay out the non-virtual ones.
28662882
for (const CXXBaseSpecifier &Base : RD->bases()) {
2867-
if (Base.isVirtual())
2868-
continue;
28692883
const CXXRecordDecl *BaseDecl = Base.getType()->getAsCXXRecordDecl();
28702884
const ASTRecordLayout &BaseLayout = Context.getASTRecordLayout(BaseDecl);
2885+
BaseSubobjectInfo *BaseInfo =
2886+
computeBaseSubobjectInfo(BaseDecl, Base.isVirtual(), nullptr);
2887+
2888+
if (Base.isVirtual()) {
2889+
// Mark offset for virtual base.
2890+
CharUnits Offset = CharUnits::Zero();
2891+
while (!EmptySubobjects->CanPlaceBaseAtOffset(BaseInfo, Offset)) {
2892+
ElementInfo Info = getAdjustedElementInfo(BaseLayout);
2893+
Offset += Info.Alignment;
2894+
}
2895+
continue;
2896+
}
2897+
28712898
// Only lay out bases without extendable VFPtrs on the second pass.
28722899
if (BaseLayout.hasExtendableVFPtr()) {
28732900
VBPtrOffset = Bases[BaseDecl] + BaseLayout.getNonVirtualSize();
@@ -2880,7 +2907,8 @@ MicrosoftRecordLayoutBuilder::layoutNonVirtualBases(const CXXRecordDecl *RD) {
28802907
LeadsWithZeroSizedBase = BaseLayout.leadsWithZeroSizedBase();
28812908
}
28822909
// Lay out the base.
2883-
layoutNonVirtualBase(RD, BaseDecl, BaseLayout, PreviousBaseLayout);
2910+
layoutNonVirtualBase(RD, BaseDecl, BaseLayout, PreviousBaseLayout,
2911+
BaseInfo);
28842912
VBPtrOffset = Bases[BaseDecl] + BaseLayout.getNonVirtualSize();
28852913
}
28862914
// Set our VBPtroffset if we know it at this point.
@@ -2908,10 +2936,9 @@ static bool recordUsesEBO(const RecordDecl *RD) {
29082936
}
29092937

29102938
void MicrosoftRecordLayoutBuilder::layoutNonVirtualBase(
2911-
const CXXRecordDecl *RD,
2912-
const CXXRecordDecl *BaseDecl,
2939+
const CXXRecordDecl *RD, const CXXRecordDecl *BaseDecl,
29132940
const ASTRecordLayout &BaseLayout,
2914-
const ASTRecordLayout *&PreviousBaseLayout) {
2941+
const ASTRecordLayout *&PreviousBaseLayout, BaseSubobjectInfo *Base) {
29152942
// Insert padding between two bases if the left first one is zero sized or
29162943
// contains a zero sized subobject and the right is zero sized or one leads
29172944
// with a zero sized base.
@@ -2927,7 +2954,7 @@ void MicrosoftRecordLayoutBuilder::layoutNonVirtualBase(
29272954
if (UseExternalLayout) {
29282955
FoundBase = External.getExternalNVBaseOffset(BaseDecl, BaseOffset);
29292956
if (BaseOffset > Size) {
2930-
Size = BaseOffset;
2957+
DataSize = Size = BaseOffset;
29312958
}
29322959
}
29332960

@@ -2937,14 +2964,97 @@ void MicrosoftRecordLayoutBuilder::layoutNonVirtualBase(
29372964
BaseOffset = CharUnits::Zero();
29382965
} else {
29392966
// Otherwise, lay the base out at the end of the MDC.
2940-
BaseOffset = Size = Size.alignTo(Info.Alignment);
2967+
BaseOffset = DataSize = Size = Size.alignTo(Info.Alignment);
29412968
}
2969+
2970+
// Place in EmptySubobjects map but don't check the position? MSVC seems to
2971+
// not allow fields to overlap at the end of a virtual base, but they can
2972+
// overlap with other bass.
2973+
EmptySubobjects->CanPlaceBaseAtOffset(Base, BaseOffset);
29422974
}
2975+
29432976
Bases.insert(std::make_pair(BaseDecl, BaseOffset));
29442977
Size += BaseLayout.getNonVirtualSize();
2978+
DataSize = Size;
29452979
PreviousBaseLayout = &BaseLayout;
29462980
}
29472981

2982+
BaseSubobjectInfo *MicrosoftRecordLayoutBuilder::computeBaseSubobjectInfo(
2983+
const CXXRecordDecl *RD, bool IsVirtual, BaseSubobjectInfo *Derived) {
2984+
// This is copied directly from ItaniumRecordLayoutBuilder::ComputeBaseSubobjectInfo.
2985+
BaseSubobjectInfo *Info;
2986+
2987+
if (IsVirtual) {
2988+
// Check if we already have info about this virtual base.
2989+
BaseSubobjectInfo *&InfoSlot = VirtualBaseInfo[RD];
2990+
if (InfoSlot) {
2991+
assert(InfoSlot->Class == RD && "Wrong class for virtual base info!");
2992+
return InfoSlot;
2993+
}
2994+
2995+
// We don't, create it.
2996+
InfoSlot = new (BaseSubobjectInfoAllocator.Allocate()) BaseSubobjectInfo;
2997+
Info = InfoSlot;
2998+
} else {
2999+
Info = new (BaseSubobjectInfoAllocator.Allocate()) BaseSubobjectInfo;
3000+
}
3001+
3002+
Info->Class = RD;
3003+
Info->IsVirtual = IsVirtual;
3004+
Info->Derived = nullptr;
3005+
Info->PrimaryVirtualBaseInfo = nullptr;
3006+
3007+
const CXXRecordDecl *PrimaryVirtualBase = nullptr;
3008+
BaseSubobjectInfo *PrimaryVirtualBaseInfo = nullptr;
3009+
3010+
// Check if this base has a primary virtual base.
3011+
if (RD->getNumVBases()) {
3012+
const ASTRecordLayout &Layout = Context.getASTRecordLayout(RD);
3013+
if (Layout.isPrimaryBaseVirtual()) {
3014+
// This base does have a primary virtual base.
3015+
PrimaryVirtualBase = Layout.getPrimaryBase();
3016+
assert(PrimaryVirtualBase && "Didn't have a primary virtual base!");
3017+
3018+
// Now check if we have base subobject info about this primary base.
3019+
PrimaryVirtualBaseInfo = VirtualBaseInfo.lookup(PrimaryVirtualBase);
3020+
3021+
if (PrimaryVirtualBaseInfo) {
3022+
if (PrimaryVirtualBaseInfo->Derived) {
3023+
// We did have info about this primary base, and it turns out that it
3024+
// has already been claimed as a primary virtual base for another
3025+
// base.
3026+
PrimaryVirtualBase = nullptr;
3027+
} else {
3028+
// We can claim this base as our primary base.
3029+
Info->PrimaryVirtualBaseInfo = PrimaryVirtualBaseInfo;
3030+
PrimaryVirtualBaseInfo->Derived = Info;
3031+
}
3032+
}
3033+
}
3034+
}
3035+
3036+
// Now go through all direct bases.
3037+
for (const auto &I : RD->bases()) {
3038+
bool IsVirtual = I.isVirtual();
3039+
3040+
const CXXRecordDecl *BaseDecl = I.getType()->getAsCXXRecordDecl();
3041+
3042+
Info->Bases.push_back(computeBaseSubobjectInfo(BaseDecl, IsVirtual, Info));
3043+
}
3044+
3045+
if (PrimaryVirtualBase && !PrimaryVirtualBaseInfo) {
3046+
// Traversing the bases must have created the base info for our primary
3047+
// virtual base.
3048+
PrimaryVirtualBaseInfo = VirtualBaseInfo.lookup(PrimaryVirtualBase);
3049+
assert(PrimaryVirtualBaseInfo && "Did not create a primary virtual base!");
3050+
3051+
// Claim the primary virtual base as our primary virtual base.
3052+
Info->PrimaryVirtualBaseInfo = PrimaryVirtualBaseInfo;
3053+
PrimaryVirtualBaseInfo->Derived = Info;
3054+
}
3055+
return Info;
3056+
}
3057+
29483058
void MicrosoftRecordLayoutBuilder::layoutFields(const RecordDecl *RD) {
29493059
LastFieldIsNonZeroWidthBitfield = false;
29503060
for (const FieldDecl *Field : RD->fields())
@@ -2956,18 +3066,48 @@ void MicrosoftRecordLayoutBuilder::layoutField(const FieldDecl *FD) {
29563066
layoutBitField(FD);
29573067
return;
29583068
}
3069+
29593070
LastFieldIsNonZeroWidthBitfield = false;
29603071
ElementInfo Info = getAdjustedElementInfo(FD);
29613072
Alignment = std::max(Alignment, Info.Alignment);
2962-
CharUnits FieldOffset;
2963-
if (UseExternalLayout)
3073+
3074+
const CXXRecordDecl *FieldClass = FD->getType()->getAsCXXRecordDecl();
3075+
bool IsOverlappingEmptyField = FD->isPotentiallyOverlapping() &&
3076+
FieldClass->isEmpty() &&
3077+
FieldClass->fields().empty();
3078+
const CXXRecordDecl *ParentClass = cast<CXXRecordDecl>(FD->getParent());
3079+
bool HasBases =
3080+
!ParentClass->bases().empty() || !ParentClass->vbases().empty();
3081+
3082+
CharUnits FieldOffset = CharUnits::Zero();
3083+
3084+
if (UseExternalLayout) {
29643085
FieldOffset =
29653086
Context.toCharUnitsFromBits(External.getExternalFieldOffset(FD));
2966-
else if (IsUnion)
3087+
} else if (IsUnion) {
29673088
FieldOffset = CharUnits::Zero();
2968-
else
3089+
} else if (EmptySubobjects) {
3090+
if (!IsOverlappingEmptyField)
3091+
FieldOffset = DataSize.alignTo(Info.Alignment);
3092+
3093+
while (!EmptySubobjects->CanPlaceFieldAtOffset(FD, FieldOffset)) {
3094+
if (FieldOffset == CharUnits::Zero() && DataSize != CharUnits::Zero() &&
3095+
HasBases) {
3096+
// MSVC appears to only do this when there are base classes;
3097+
// otherwise it overlaps no_unique_address fields in non-zero offsets.
3098+
FieldOffset = DataSize.alignTo(Info.Alignment);
3099+
} else {
3100+
FieldOffset += Info.Alignment;
3101+
}
3102+
}
3103+
} else {
29693104
FieldOffset = Size.alignTo(Info.Alignment);
3105+
}
29703106
placeFieldAtOffset(FieldOffset);
3107+
3108+
if (!IsOverlappingEmptyField)
3109+
DataSize = std::max(DataSize, FieldOffset + Info.Size);
3110+
29713111
Size = std::max(Size, FieldOffset + Info.Size);
29723112
}
29733113

@@ -2999,17 +3139,17 @@ void MicrosoftRecordLayoutBuilder::layoutBitField(const FieldDecl *FD) {
29993139
auto NewSize = Context.toCharUnitsFromBits(
30003140
llvm::alignDown(FieldBitOffset, Context.toBits(Info.Alignment)) +
30013141
Context.toBits(Info.Size));
3002-
Size = std::max(Size, NewSize);
3142+
DataSize = Size = std::max(Size, NewSize);
30033143
Alignment = std::max(Alignment, Info.Alignment);
30043144
} else if (IsUnion) {
30053145
placeFieldAtOffset(CharUnits::Zero());
3006-
Size = std::max(Size, Info.Size);
3146+
DataSize = Size = std::max(Size, Info.Size);
30073147
// TODO: Add a Sema warning that MS ignores bitfield alignment in unions.
30083148
} else {
30093149
// Allocate a new block of memory and place the bitfield in it.
30103150
CharUnits FieldOffset = Size.alignTo(Info.Alignment);
30113151
placeFieldAtOffset(FieldOffset);
3012-
Size = FieldOffset + Info.Size;
3152+
DataSize = Size = FieldOffset + Info.Size;
30133153
Alignment = std::max(Alignment, Info.Alignment);
30143154
RemainingBitsInField = Context.toBits(Info.Size) - Width;
30153155
}
@@ -3029,13 +3169,13 @@ MicrosoftRecordLayoutBuilder::layoutZeroWidthBitField(const FieldDecl *FD) {
30293169
ElementInfo Info = getAdjustedElementInfo(FD);
30303170
if (IsUnion) {
30313171
placeFieldAtOffset(CharUnits::Zero());
3032-
Size = std::max(Size, Info.Size);
3172+
DataSize = Size = std::max(Size, Info.Size);
30333173
// TODO: Add a Sema warning that MS ignores bitfield alignment in unions.
30343174
} else {
30353175
// Round up the current record size to the field's alignment boundary.
30363176
CharUnits FieldOffset = Size.alignTo(Info.Alignment);
30373177
placeFieldAtOffset(FieldOffset);
3038-
Size = FieldOffset;
3178+
DataSize = Size = FieldOffset;
30393179
Alignment = std::max(Alignment, Info.Alignment);
30403180
}
30413181
}
@@ -3055,7 +3195,7 @@ void MicrosoftRecordLayoutBuilder::injectVBPtr(const CXXRecordDecl *RD) {
30553195
// It is possible that there were no fields or bases located after vbptr,
30563196
// so the size was not adjusted before.
30573197
if (Size < FieldStart)
3058-
Size = FieldStart;
3198+
DataSize = Size = FieldStart;
30593199
return;
30603200
}
30613201
// Make sure that the amount we push the fields back by is a multiple of the
@@ -3103,6 +3243,7 @@ void MicrosoftRecordLayoutBuilder::injectVFPtr(const CXXRecordDecl *RD) {
31033243
void MicrosoftRecordLayoutBuilder::layoutVirtualBases(const CXXRecordDecl *RD) {
31043244
if (!HasVBPtr)
31053245
return;
3246+
31063247
// Vtordisps are always 4 bytes (even in 64-bit mode)
31073248
CharUnits VtorDispSize = CharUnits::fromQuantity(4);
31083249
CharUnits VtorDispAlignment = VtorDispSize;
@@ -3136,7 +3277,7 @@ void MicrosoftRecordLayoutBuilder::layoutVirtualBases(const CXXRecordDecl *RD) {
31363277
if ((PreviousBaseLayout && PreviousBaseLayout->endsWithZeroSizedObject() &&
31373278
BaseLayout.leadsWithZeroSizedBase() && !recordUsesEBO(RD)) ||
31383279
HasVtordisp) {
3139-
Size = Size.alignTo(VtorDispAlignment) + VtorDispSize;
3280+
DataSize = Size = Size.alignTo(VtorDispAlignment) + VtorDispSize;
31403281
Alignment = std::max(VtorDispAlignment, Alignment);
31413282
}
31423283
// Insert the virtual base.
@@ -3154,7 +3295,7 @@ void MicrosoftRecordLayoutBuilder::layoutVirtualBases(const CXXRecordDecl *RD) {
31543295

31553296
VBases.insert(std::make_pair(BaseDecl,
31563297
ASTRecordLayout::VBaseInfo(BaseOffset, HasVtordisp)));
3157-
Size = BaseOffset + BaseLayout.getNonVirtualSize();
3298+
DataSize = Size = BaseOffset + BaseLayout.getNonVirtualSize();
31583299
PreviousBaseLayout = &BaseLayout;
31593300
}
31603301
}
@@ -3304,8 +3445,9 @@ ASTContext::getASTRecordLayout(const RecordDecl *D) const {
33043445
const ASTRecordLayout *NewEntry = nullptr;
33053446

33063447
if (isMsLayout(*this)) {
3307-
MicrosoftRecordLayoutBuilder Builder(*this);
33083448
if (const auto *RD = dyn_cast<CXXRecordDecl>(D)) {
3449+
EmptySubobjectMap EmptySubobjects(*this, RD);
3450+
MicrosoftRecordLayoutBuilder Builder(*this, &EmptySubobjects);
33093451
Builder.cxxLayout(RD);
33103452
NewEntry = new (*this) ASTRecordLayout(
33113453
*this, Builder.Size, Builder.Alignment, Builder.Alignment,
@@ -3317,6 +3459,7 @@ ASTContext::getASTRecordLayout(const RecordDecl *D) const {
33173459
Builder.EndsWithZeroSizedObject, Builder.LeadsWithZeroSizedBase,
33183460
Builder.Bases, Builder.VBases);
33193461
} else {
3462+
MicrosoftRecordLayoutBuilder Builder(*this, /*EmptySubobjects*/ nullptr);
33203463
Builder.layout(D);
33213464
NewEntry = new (*this) ASTRecordLayout(
33223465
*this, Builder.Size, Builder.Alignment, Builder.Alignment,

0 commit comments

Comments
 (0)