Skip to content

Commit 401aca5

Browse files
amykhuanglegrosbuffle
authored andcommitted
Recommit "Implement [[msvc::no_unique_address]] (llvm#65675)" (llvm#67199)
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. The recommit changes the attribute definitions so that instead of making two separate attributes for no_unique_address and msvc::no_unique_address, it modifies the attributes tablegen emitter to allow spellings to be target-specific. This reverts commit 71f9e76.
1 parent e758aac commit 401aca5

File tree

12 files changed

+570
-16
lines changed

12 files changed

+570
-16
lines changed

clang/include/clang/Basic/Attr.td

Lines changed: 14 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -450,6 +450,12 @@ def TargetELF : TargetSpec {
450450
def TargetSupportsInitPriority : TargetSpec {
451451
let CustomCode = [{ !Target.getTriple().isOSzOS() }];
452452
}
453+
454+
class TargetSpecificSpelling<TargetSpec target, list<Spelling> spellings> {
455+
TargetSpec Target = target;
456+
list<Spelling> Spellings = spellings;
457+
}
458+
453459
// Attribute subject match rules that are used for #pragma clang attribute.
454460
//
455461
// A instance of AttrSubjectMatcherRule represents an individual match rule.
@@ -576,6 +582,8 @@ class Attr {
576582
list<Argument> Args = [];
577583
// Accessors which should be generated for the attribute.
578584
list<Accessor> Accessors = [];
585+
// Specify targets for spellings.
586+
list<TargetSpecificSpelling> TargetSpecificSpellings = [];
579587
// Set to true for attributes with arguments which require delayed parsing.
580588
bit LateParsed = 0;
581589
// Set to false to prevent an attribute from being propagated from a template
@@ -1798,11 +1806,14 @@ def ArmMveStrictPolymorphism : TypeAttr, TargetSpecificAttr<TargetARM> {
17981806
let Documentation = [ArmMveStrictPolymorphismDocs];
17991807
}
18001808

1801-
def NoUniqueAddress : InheritableAttr, TargetSpecificAttr<TargetItaniumCXXABI> {
1802-
let Spellings = [CXX11<"", "no_unique_address", 201803>];
1809+
def NoUniqueAddress : InheritableAttr {
18031810
let Subjects = SubjectList<[NonBitField], ErrorDiag>;
1811+
let Spellings = [CXX11<"", "no_unique_address", 201803>, CXX11<"msvc", "no_unique_address", 201803>];
1812+
let TargetSpecificSpellings = [
1813+
TargetSpecificSpelling<TargetItaniumCXXABI, [CXX11<"", "no_unique_address", 201803>]>,
1814+
TargetSpecificSpelling<TargetMicrosoftCXXABI, [CXX11<"msvc", "no_unique_address", 201803>]>,
1815+
];
18041816
let Documentation = [NoUniqueAddressDocs];
1805-
let SimpleHandler = 1;
18061817
}
18071818

18081819
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 with MSVC.
14081412
}];
14091413
}
14101414

clang/include/clang/Basic/ParsedAttrInfo.h

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -116,6 +116,14 @@ struct ParsedAttrInfo {
116116

117117
/// Check if this attribute is allowed when compiling for the given target.
118118
virtual bool existsInTarget(const TargetInfo &Target) const { return true; }
119+
120+
/// Check if this attribute's spelling is allowed when compiling for the given
121+
/// target.
122+
virtual bool spellingExistsInTarget(const TargetInfo &Target,
123+
const unsigned SpellingListIndex) const {
124+
return true;
125+
}
126+
119127
/// Convert the spelling index of Attr to a semantic spelling enum value.
120128
virtual unsigned
121129
spellingIndexToSemanticSpelling(const ParsedAttr &Attr) const {

clang/lib/AST/Decl.cpp

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4513,9 +4513,14 @@ bool FieldDecl::isZeroSize(const ASTContext &Ctx) const {
45134513

45144514
// Otherwise, [...] the circumstances under which the object has zero size
45154515
// are implementation-defined.
4516-
// FIXME: This might be Itanium ABI specific; we don't yet know what the MS
4517-
// ABI will do.
4518-
return true;
4516+
if (!Ctx.getTargetInfo().getCXXABI().isMicrosoft())
4517+
return true;
4518+
4519+
// MS ABI: has nonzero size if it is a class type with class type fields,
4520+
// whether or not they have nonzero size
4521+
return !llvm::any_of(CXXRD->fields(), [](const FieldDecl *Field) {
4522+
return Field->getType()->getAs<RecordType>();
4523+
});
45194524
}
45204525

45214526
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: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4466,6 +4466,14 @@ bool Parser::ParseCXX11AttributeArgs(
44664466
if (!Attrs.empty() &&
44674467
IsBuiltInOrStandardCXX11Attribute(AttrName, ScopeName)) {
44684468
ParsedAttr &Attr = Attrs.back();
4469+
4470+
// Ignore attributes that don't exist for the target.
4471+
if (!Attr.existsInTarget(getTargetInfo())) {
4472+
Diag(LParenLoc, diag::warn_unknown_attribute_ignored) << AttrName;
4473+
Attr.setInvalid(true);
4474+
return true;
4475+
}
4476+
44694477
// If the attribute is a standard or built-in attribute and we are
44704478
// parsing an argument list, we need to determine whether this attribute
44714479
// was allowed to have an argument list (such as [[deprecated]]), and how

clang/lib/Sema/ParsedAttr.cpp

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -193,7 +193,9 @@ bool ParsedAttr::isTypeAttr() const { return getInfo().IsType; }
193193
bool ParsedAttr::isStmtAttr() const { return getInfo().IsStmt; }
194194

195195
bool ParsedAttr::existsInTarget(const TargetInfo &Target) const {
196-
return getInfo().existsInTarget(Target);
196+
return getInfo().existsInTarget(Target) &&
197+
getInfo().spellingExistsInTarget(Target,
198+
getAttributeSpellingListIndex());
197199
}
198200

199201
bool ParsedAttr::isKnownToGCC() const { return getInfo().IsKnownToGCC; }

clang/lib/Sema/SemaDeclAttr.cpp

Lines changed: 7 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,9 @@ 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_NoUniqueAddress:
9285+
handleNoUniqueAddressAttr(S, D, AL);
9286+
break;
92809287

92819288
case ParsedAttr::AT_AvailableOnlyInDefaultEvalMethod:
92829289
handleAvailableOnlyInDefaultEvalMethod(S, D, AL);

0 commit comments

Comments
 (0)