Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Implement [[msvc::no_unique_address]] #65675

Merged
merged 6 commits into from
Sep 22, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
19 changes: 16 additions & 3 deletions clang/include/clang/Basic/Attr.td
Original file line number Diff line number Diff line change
Expand Up @@ -1798,11 +1798,24 @@ def ArmMveStrictPolymorphism : TypeAttr, TargetSpecificAttr<TargetARM> {
let Documentation = [ArmMveStrictPolymorphismDocs];
}

def NoUniqueAddress : InheritableAttr, TargetSpecificAttr<TargetItaniumCXXABI> {
let Spellings = [CXX11<"", "no_unique_address", 201803>];
def NoUniqueAddress : InheritableAttr {
let Subjects = SubjectList<[NonBitField], ErrorDiag>;
// No spellings because instances of this attribute are created by
// MSNoUniqueAddress and ItaniumNoUniqueAddress
let Spellings = [];
let Documentation = [NoUniqueAddressDocs];
}

def MSNoUniqueAddress : InheritableAttr, TargetSpecificAttr<TargetMicrosoftCXXABI> {
let Subjects = SubjectList<[NonBitField], ErrorDiag>;
let Spellings = [CXX11<"msvc", "no_unique_address", 201803>];
let Documentation = [NoUniqueAddressDocs];
}

def ItaniumNoUniqueAddress : InheritableAttr, TargetSpecificAttr<TargetItaniumCXXABI> {
let Subjects = SubjectList<[NonBitField], ErrorDiag>;
let Spellings = [CXX11<"", "no_unique_address", 201803>];
let Documentation = [NoUniqueAddressDocs];
let SimpleHandler = 1;
}

def ReturnsTwice : InheritableAttr {
Expand Down
4 changes: 4 additions & 0 deletions clang/include/clang/Basic/AttrDocs.td
Original file line number Diff line number Diff line change
Expand Up @@ -1405,6 +1405,10 @@ Example usage:

``[[no_unique_address]]`` is a standard C++20 attribute. Clang supports its use
in C++11 onwards.

On MSVC targets, ``[[no_unique_address]]`` is ignored; use
``[[msvc::no_unique_address]]`` instead. Currently there is no guarantee of ABI
compatibility or stability.
}];
}

Expand Down
11 changes: 8 additions & 3 deletions clang/lib/AST/Decl.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -4507,9 +4507,14 @@ bool FieldDecl::isZeroSize(const ASTContext &Ctx) const {

// Otherwise, [...] the circumstances under which the object has zero size
// are implementation-defined.
// FIXME: This might be Itanium ABI specific; we don't yet know what the MS
// ABI will do.
return true;
if (!Ctx.getTargetInfo().getCXXABI().isMicrosoft())
return true;

// MS ABI: has nonzero size if it is a class type with class type fields,
// whether or not they have nonzero size
return !llvm::any_of(CXXRD->fields(), [](const FieldDecl *Field) {
return Field->getType()->getAs<RecordType>();
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

My point here was, the isEmpty test above means we already know that CXXRD has no non-static data members of class type: https://clang.llvm.org/doxygen/classclang_1_1CXXRecordDecl.html#ac570e2716d2cb020851575aa4e5fafe0

So this test doesn't seem to do anything.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Oh, I see. The comment above the isEmpty check specifies "-- has subobjects of nonzero size or bit-fields of nonzero length", so I was assuming that isEmpty is taking the zero size stuff into account. I found it mentioned in this comment as well

.

Also, in the last commit I added tests to verify that this check is working.

});
}

bool FieldDecl::isPotentiallyOverlapping() const {
Expand Down
53 changes: 45 additions & 8 deletions clang/lib/AST/RecordLayoutBuilder.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2545,7 +2545,10 @@ struct MicrosoftRecordLayoutBuilder {
CharUnits Alignment;
};
typedef llvm::DenseMap<const CXXRecordDecl *, CharUnits> BaseOffsetsMapTy;
MicrosoftRecordLayoutBuilder(const ASTContext &Context) : Context(Context) {}
MicrosoftRecordLayoutBuilder(const ASTContext &Context,
EmptySubobjectMap *EmptySubobjects)
: Context(Context), EmptySubobjects(EmptySubobjects) {}

private:
MicrosoftRecordLayoutBuilder(const MicrosoftRecordLayoutBuilder &) = delete;
void operator=(const MicrosoftRecordLayoutBuilder &) = delete;
Expand Down Expand Up @@ -2595,6 +2598,8 @@ struct MicrosoftRecordLayoutBuilder {
llvm::SmallPtrSetImpl<const CXXRecordDecl *> &HasVtorDispSet,
const CXXRecordDecl *RD) const;
const ASTContext &Context;
EmptySubobjectMap *EmptySubobjects;

/// The size of the record being laid out.
CharUnits Size;
/// The non-virtual size of the record layout.
Expand Down Expand Up @@ -2908,8 +2913,7 @@ static bool recordUsesEBO(const RecordDecl *RD) {
}

void MicrosoftRecordLayoutBuilder::layoutNonVirtualBase(
const CXXRecordDecl *RD,
const CXXRecordDecl *BaseDecl,
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
Expand Down Expand Up @@ -2942,6 +2946,7 @@ void MicrosoftRecordLayoutBuilder::layoutNonVirtualBase(
}
Bases.insert(std::make_pair(BaseDecl, BaseOffset));
Size += BaseLayout.getNonVirtualSize();
DataSize = Size;
PreviousBaseLayout = &BaseLayout;
}

Expand All @@ -2959,15 +2964,43 @@ void MicrosoftRecordLayoutBuilder::layoutField(const FieldDecl *FD) {
LastFieldIsNonZeroWidthBitfield = false;
ElementInfo Info = getAdjustedElementInfo(FD);
Alignment = std::max(Alignment, Info.Alignment);
CharUnits FieldOffset;
if (UseExternalLayout)

const CXXRecordDecl *FieldClass = FD->getType()->getAsCXXRecordDecl();
bool IsOverlappingEmptyField = FD->isPotentiallyOverlapping() &&
FieldClass->isEmpty() &&
FieldClass->fields().empty();
CharUnits FieldOffset = CharUnits::Zero();

if (UseExternalLayout) {
FieldOffset =
Context.toCharUnitsFromBits(External.getExternalFieldOffset(FD));
else if (IsUnion)
} else if (IsUnion) {
FieldOffset = CharUnits::Zero();
else
} else if (EmptySubobjects) {
if (!IsOverlappingEmptyField)
FieldOffset = DataSize.alignTo(Info.Alignment);

while (!EmptySubobjects->CanPlaceFieldAtOffset(FD, FieldOffset)) {
const CXXRecordDecl *ParentClass = cast<CXXRecordDecl>(FD->getParent());
bool HasBases = ParentClass && (!ParentClass->bases().empty() ||
!ParentClass->vbases().empty());
if (FieldOffset == CharUnits::Zero() && DataSize != CharUnits::Zero() &&
HasBases) {
// MSVC appears to only do this when there are base classes;
// otherwise it overlaps no_unique_address fields in non-zero offsets.
FieldOffset = DataSize.alignTo(Info.Alignment);
} else {
FieldOffset += Info.Alignment;
}
}
} else {
FieldOffset = Size.alignTo(Info.Alignment);
}
placeFieldAtOffset(FieldOffset);

if (!IsOverlappingEmptyField)
DataSize = std::max(DataSize, FieldOffset + Info.Size);

Size = std::max(Size, FieldOffset + Info.Size);
}

Expand Down Expand Up @@ -3013,6 +3046,7 @@ void MicrosoftRecordLayoutBuilder::layoutBitField(const FieldDecl *FD) {
Alignment = std::max(Alignment, Info.Alignment);
RemainingBitsInField = Context.toBits(Info.Size) - Width;
}
DataSize = Size;
}

void
Expand All @@ -3038,6 +3072,7 @@ MicrosoftRecordLayoutBuilder::layoutZeroWidthBitField(const FieldDecl *FD) {
Size = FieldOffset;
Alignment = std::max(Alignment, Info.Alignment);
}
DataSize = Size;
}

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

if (isMsLayout(*this)) {
MicrosoftRecordLayoutBuilder Builder(*this);
if (const auto *RD = dyn_cast<CXXRecordDecl>(D)) {
EmptySubobjectMap EmptySubobjects(*this, RD);
MicrosoftRecordLayoutBuilder Builder(*this, &EmptySubobjects);
Builder.cxxLayout(RD);
NewEntry = new (*this) ASTRecordLayout(
*this, Builder.Size, Builder.Alignment, Builder.Alignment,
Expand All @@ -3317,6 +3353,7 @@ ASTContext::getASTRecordLayout(const RecordDecl *D) const {
Builder.EndsWithZeroSizedObject, Builder.LeadsWithZeroSizedBase,
Builder.Bases, Builder.VBases);
} else {
MicrosoftRecordLayoutBuilder Builder(*this, /*EmptySubobjects=*/nullptr);
Builder.layout(D);
NewEntry = new (*this) ASTRecordLayout(
*this, Builder.Size, Builder.Alignment, Builder.Alignment,
Expand Down
3 changes: 2 additions & 1 deletion clang/lib/Parse/ParseDeclCXX.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -4364,7 +4364,8 @@ static bool IsBuiltInOrStandardCXX11Attribute(IdentifierInfo *AttrName,
case ParsedAttr::AT_Deprecated:
case ParsedAttr::AT_FallThrough:
case ParsedAttr::AT_CXX11NoReturn:
case ParsedAttr::AT_NoUniqueAddress:
case ParsedAttr::AT_MSNoUniqueAddress:
case ParsedAttr::AT_ItaniumNoUniqueAddress:
case ParsedAttr::AT_Likely:
case ParsedAttr::AT_Unlikely:
return true;
Expand Down
10 changes: 10 additions & 0 deletions clang/lib/Sema/SemaDeclAttr.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -8371,6 +8371,10 @@ static void handleNoMergeAttr(Sema &S, Decl *D, const ParsedAttr &AL) {
D->addAttr(NoMergeAttr::Create(S.Context, AL));
}

static void handleNoUniqueAddressAttr(Sema &S, Decl *D, const ParsedAttr &AL) {
D->addAttr(NoUniqueAddressAttr::Create(S.Context, AL));
}

static void handleSYCLKernelAttr(Sema &S, Decl *D, const ParsedAttr &AL) {
// The 'sycl_kernel' attribute applies only to function templates.
const auto *FD = cast<FunctionDecl>(D);
Expand Down Expand Up @@ -9276,6 +9280,12 @@ ProcessDeclAttribute(Sema &S, Scope *scope, Decl *D, const ParsedAttr &AL,
case ParsedAttr::AT_NoMerge:
handleNoMergeAttr(S, D, AL);
break;
case ParsedAttr::AT_MSNoUniqueAddress:
handleNoUniqueAddressAttr(S, D, AL);
break;
case ParsedAttr::AT_ItaniumNoUniqueAddress:
handleNoUniqueAddressAttr(S, D, AL);
break;

case ParsedAttr::AT_AvailableOnlyInDefaultEvalMethod:
handleAvailableOnlyInDefaultEvalMethod(S, D, AL);
Expand Down
Loading