Skip to content

Commit 4a55d42

Browse files
authored
Implement [[msvc::no_unique_address]] (#65675)
This implements the [[msvc::no_unique_address]] attribute. There is not ABI compatibility in this patch because the attribute is relatively new and there's still some uncertainty in the MSVC version. Bug: #49358 Also see https://reviews.llvm.org/D157762.
1 parent d9f8316 commit 4a55d42

File tree

9 files changed

+489
-16
lines changed

9 files changed

+489
-16
lines changed

clang/include/clang/Basic/Attr.td

Lines changed: 16 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1798,11 +1798,24 @@ 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 {
18031802
let Subjects = SubjectList<[NonBitField], ErrorDiag>;
1803+
// No spellings because instances of this attribute are created by
1804+
// MSNoUniqueAddress and ItaniumNoUniqueAddress
1805+
let Spellings = [];
1806+
let Documentation = [NoUniqueAddressDocs];
1807+
}
1808+
1809+
def MSNoUniqueAddress : InheritableAttr, TargetSpecificAttr<TargetMicrosoftCXXABI> {
1810+
let Subjects = SubjectList<[NonBitField], ErrorDiag>;
1811+
let Spellings = [CXX11<"msvc", "no_unique_address", 201803>];
1812+
let Documentation = [NoUniqueAddressDocs];
1813+
}
1814+
1815+
def ItaniumNoUniqueAddress : InheritableAttr, TargetSpecificAttr<TargetItaniumCXXABI> {
1816+
let Subjects = SubjectList<[NonBitField], ErrorDiag>;
1817+
let Spellings = [CXX11<"", "no_unique_address", 201803>];
18041818
let Documentation = [NoUniqueAddressDocs];
1805-
let SimpleHandler = 1;
18061819
}
18071820

18081821
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 & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4507,9 +4507,14 @@ bool FieldDecl::isZeroSize(const ASTContext &Ctx) const {
45074507

45084508
// Otherwise, [...] the circumstances under which the object has zero size
45094509
// are implementation-defined.
4510-
// FIXME: This might be Itanium ABI specific; we don't yet know what the MS
4511-
// ABI will do.
4512-
return true;
4510+
if (!Ctx.getTargetInfo().getCXXABI().isMicrosoft())
4511+
return true;
4512+
4513+
// MS ABI: has nonzero size if it is a class type with class type fields,
4514+
// whether or not they have nonzero size
4515+
return !llvm::any_of(CXXRD->fields(), [](const FieldDecl *Field) {
4516+
return Field->getType()->getAs<RecordType>();
4517+
});
45134518
}
45144519

45154520
bool FieldDecl::isPotentiallyOverlapping() const {

clang/lib/AST/RecordLayoutBuilder.cpp

Lines changed: 45 additions & 8 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;
@@ -2595,6 +2598,8 @@ struct MicrosoftRecordLayoutBuilder {
25952598
llvm::SmallPtrSetImpl<const CXXRecordDecl *> &HasVtorDispSet,
25962599
const CXXRecordDecl *RD) const;
25972600
const ASTContext &Context;
2601+
EmptySubobjectMap *EmptySubobjects;
2602+
25982603
/// The size of the record being laid out.
25992604
CharUnits Size;
26002605
/// The non-virtual size of the record layout.
@@ -2908,8 +2913,7 @@ static bool recordUsesEBO(const RecordDecl *RD) {
29082913
}
29092914

29102915
void MicrosoftRecordLayoutBuilder::layoutNonVirtualBase(
2911-
const CXXRecordDecl *RD,
2912-
const CXXRecordDecl *BaseDecl,
2916+
const CXXRecordDecl *RD, const CXXRecordDecl *BaseDecl,
29132917
const ASTRecordLayout &BaseLayout,
29142918
const ASTRecordLayout *&PreviousBaseLayout) {
29152919
// Insert padding between two bases if the left first one is zero sized or
@@ -2942,6 +2946,7 @@ void MicrosoftRecordLayoutBuilder::layoutNonVirtualBase(
29422946
}
29432947
Bases.insert(std::make_pair(BaseDecl, BaseOffset));
29442948
Size += BaseLayout.getNonVirtualSize();
2949+
DataSize = Size;
29452950
PreviousBaseLayout = &BaseLayout;
29462951
}
29472952

@@ -2959,15 +2964,43 @@ void MicrosoftRecordLayoutBuilder::layoutField(const FieldDecl *FD) {
29592964
LastFieldIsNonZeroWidthBitfield = false;
29602965
ElementInfo Info = getAdjustedElementInfo(FD);
29612966
Alignment = std::max(Alignment, Info.Alignment);
2962-
CharUnits FieldOffset;
2963-
if (UseExternalLayout)
2967+
2968+
const CXXRecordDecl *FieldClass = FD->getType()->getAsCXXRecordDecl();
2969+
bool IsOverlappingEmptyField = FD->isPotentiallyOverlapping() &&
2970+
FieldClass->isEmpty() &&
2971+
FieldClass->fields().empty();
2972+
CharUnits FieldOffset = CharUnits::Zero();
2973+
2974+
if (UseExternalLayout) {
29642975
FieldOffset =
29652976
Context.toCharUnitsFromBits(External.getExternalFieldOffset(FD));
2966-
else if (IsUnion)
2977+
} else if (IsUnion) {
29672978
FieldOffset = CharUnits::Zero();
2968-
else
2979+
} else if (EmptySubobjects) {
2980+
if (!IsOverlappingEmptyField)
2981+
FieldOffset = DataSize.alignTo(Info.Alignment);
2982+
2983+
while (!EmptySubobjects->CanPlaceFieldAtOffset(FD, FieldOffset)) {
2984+
const CXXRecordDecl *ParentClass = cast<CXXRecordDecl>(FD->getParent());
2985+
bool HasBases = ParentClass && (!ParentClass->bases().empty() ||
2986+
!ParentClass->vbases().empty());
2987+
if (FieldOffset == CharUnits::Zero() && DataSize != CharUnits::Zero() &&
2988+
HasBases) {
2989+
// MSVC appears to only do this when there are base classes;
2990+
// otherwise it overlaps no_unique_address fields in non-zero offsets.
2991+
FieldOffset = DataSize.alignTo(Info.Alignment);
2992+
} else {
2993+
FieldOffset += Info.Alignment;
2994+
}
2995+
}
2996+
} else {
29692997
FieldOffset = Size.alignTo(Info.Alignment);
2998+
}
29702999
placeFieldAtOffset(FieldOffset);
3000+
3001+
if (!IsOverlappingEmptyField)
3002+
DataSize = std::max(DataSize, FieldOffset + Info.Size);
3003+
29713004
Size = std::max(Size, FieldOffset + Info.Size);
29723005
}
29733006

@@ -3013,6 +3046,7 @@ void MicrosoftRecordLayoutBuilder::layoutBitField(const FieldDecl *FD) {
30133046
Alignment = std::max(Alignment, Info.Alignment);
30143047
RemainingBitsInField = Context.toBits(Info.Size) - Width;
30153048
}
3049+
DataSize = Size;
30163050
}
30173051

30183052
void
@@ -3038,6 +3072,7 @@ MicrosoftRecordLayoutBuilder::layoutZeroWidthBitField(const FieldDecl *FD) {
30383072
Size = FieldOffset;
30393073
Alignment = std::max(Alignment, Info.Alignment);
30403074
}
3075+
DataSize = Size;
30413076
}
30423077

30433078
void MicrosoftRecordLayoutBuilder::injectVBPtr(const CXXRecordDecl *RD) {
@@ -3304,8 +3339,9 @@ ASTContext::getASTRecordLayout(const RecordDecl *D) const {
33043339
const ASTRecordLayout *NewEntry = nullptr;
33053340

33063341
if (isMsLayout(*this)) {
3307-
MicrosoftRecordLayoutBuilder Builder(*this);
33083342
if (const auto *RD = dyn_cast<CXXRecordDecl>(D)) {
3343+
EmptySubobjectMap EmptySubobjects(*this, RD);
3344+
MicrosoftRecordLayoutBuilder Builder(*this, &EmptySubobjects);
33093345
Builder.cxxLayout(RD);
33103346
NewEntry = new (*this) ASTRecordLayout(
33113347
*this, Builder.Size, Builder.Alignment, Builder.Alignment,
@@ -3317,6 +3353,7 @@ ASTContext::getASTRecordLayout(const RecordDecl *D) const {
33173353
Builder.EndsWithZeroSizedObject, Builder.LeadsWithZeroSizedBase,
33183354
Builder.Bases, Builder.VBases);
33193355
} else {
3356+
MicrosoftRecordLayoutBuilder Builder(*this, /*EmptySubobjects=*/nullptr);
33203357
Builder.layout(D);
33213358
NewEntry = new (*this) ASTRecordLayout(
33223359
*this, Builder.Size, Builder.Alignment, Builder.Alignment,

clang/lib/Parse/ParseDeclCXX.cpp

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4374,7 +4374,8 @@ static bool IsBuiltInOrStandardCXX11Attribute(IdentifierInfo *AttrName,
43744374
case ParsedAttr::AT_Deprecated:
43754375
case ParsedAttr::AT_FallThrough:
43764376
case ParsedAttr::AT_CXX11NoReturn:
4377-
case ParsedAttr::AT_NoUniqueAddress:
4377+
case ParsedAttr::AT_MSNoUniqueAddress:
4378+
case ParsedAttr::AT_ItaniumNoUniqueAddress:
43784379
case ParsedAttr::AT_Likely:
43794380
case ParsedAttr::AT_Unlikely:
43804381
return true;

clang/lib/Sema/SemaDeclAttr.cpp

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8372,6 +8372,10 @@ static void handleNoMergeAttr(Sema &S, Decl *D, const ParsedAttr &AL) {
83728372
D->addAttr(NoMergeAttr::Create(S.Context, AL));
83738373
}
83748374

8375+
static void handleNoUniqueAddressAttr(Sema &S, Decl *D, const ParsedAttr &AL) {
8376+
D->addAttr(NoUniqueAddressAttr::Create(S.Context, AL));
8377+
}
8378+
83758379
static void handleSYCLKernelAttr(Sema &S, Decl *D, const ParsedAttr &AL) {
83768380
// The 'sycl_kernel' attribute applies only to function templates.
83778381
const auto *FD = cast<FunctionDecl>(D);
@@ -9277,6 +9281,12 @@ ProcessDeclAttribute(Sema &S, Scope *scope, Decl *D, const ParsedAttr &AL,
92779281
case ParsedAttr::AT_NoMerge:
92789282
handleNoMergeAttr(S, D, AL);
92799283
break;
9284+
case ParsedAttr::AT_MSNoUniqueAddress:
9285+
handleNoUniqueAddressAttr(S, D, AL);
9286+
break;
9287+
case ParsedAttr::AT_ItaniumNoUniqueAddress:
9288+
handleNoUniqueAddressAttr(S, D, AL);
9289+
break;
92809290

92819291
case ParsedAttr::AT_AvailableOnlyInDefaultEvalMethod:
92829292
handleAvailableOnlyInDefaultEvalMethod(S, D, AL);

0 commit comments

Comments
 (0)