Skip to content

Recommit "Implement [[msvc::no_unique_address]] (#65675)" #67199

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

Merged
merged 4 commits into from
Sep 28, 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
17 changes: 14 additions & 3 deletions clang/include/clang/Basic/Attr.td
Original file line number Diff line number Diff line change
Expand Up @@ -450,6 +450,12 @@ def TargetELF : TargetSpec {
def TargetSupportsInitPriority : TargetSpec {
let CustomCode = [{ !Target.getTriple().isOSzOS() }];
}

class TargetSpecificSpelling<TargetSpec target, list<Spelling> spellings> {
TargetSpec Target = target;
list<Spelling> Spellings = spellings;
}

// Attribute subject match rules that are used for #pragma clang attribute.
//
// A instance of AttrSubjectMatcherRule represents an individual match rule.
Expand Down Expand Up @@ -576,6 +582,8 @@ class Attr {
list<Argument> Args = [];
// Accessors which should be generated for the attribute.
list<Accessor> Accessors = [];
// Specify targets for spellings.
list<TargetSpecificSpelling> TargetSpecificSpellings = [];
// Set to true for attributes with arguments which require delayed parsing.
bit LateParsed = 0;
// Set to false to prevent an attribute from being propagated from a template
Expand Down Expand Up @@ -1798,11 +1806,14 @@ 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>;
let Spellings = [CXX11<"", "no_unique_address", 201803>, CXX11<"msvc", "no_unique_address", 201803>];
let TargetSpecificSpellings = [
TargetSpecificSpelling<TargetItaniumCXXABI, [CXX11<"", "no_unique_address", 201803>]>,
TargetSpecificSpelling<TargetMicrosoftCXXABI, [CXX11<"msvc", "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 with MSVC.
}];
}

Expand Down
8 changes: 8 additions & 0 deletions clang/include/clang/Basic/ParsedAttrInfo.h
Original file line number Diff line number Diff line change
Expand Up @@ -116,6 +116,14 @@ struct ParsedAttrInfo {

/// Check if this attribute is allowed when compiling for the given target.
virtual bool existsInTarget(const TargetInfo &Target) const { return true; }

/// Check if this attribute's spelling is allowed when compiling for the given
/// target.
virtual bool spellingExistsInTarget(const TargetInfo &Target,
const unsigned SpellingListIndex) const {
return true;
}

/// Convert the spelling index of Attr to a semantic spelling enum value.
virtual unsigned
spellingIndexToSemanticSpelling(const ParsedAttr &Attr) const {
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>();
});
}

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
8 changes: 8 additions & 0 deletions clang/lib/Parse/ParseDeclCXX.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -4464,6 +4464,14 @@ bool Parser::ParseCXX11AttributeArgs(
if (!Attrs.empty() &&
IsBuiltInOrStandardCXX11Attribute(AttrName, ScopeName)) {
ParsedAttr &Attr = Attrs.back();

// Ignore attributes that don't exist for the target.
if (!Attr.existsInTarget(getTargetInfo())) {
Diag(LParenLoc, diag::warn_unknown_attribute_ignored) << AttrName;
Attr.setInvalid(true);
return true;
}

// If the attribute is a standard or built-in attribute and we are
// parsing an argument list, we need to determine whether this attribute
// was allowed to have an argument list (such as [[deprecated]]), and how
Expand Down
4 changes: 3 additions & 1 deletion clang/lib/Sema/ParsedAttr.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -193,7 +193,9 @@ bool ParsedAttr::isTypeAttr() const { return getInfo().IsType; }
bool ParsedAttr::isStmtAttr() const { return getInfo().IsStmt; }

bool ParsedAttr::existsInTarget(const TargetInfo &Target) const {
return getInfo().existsInTarget(Target);
return getInfo().existsInTarget(Target) &&
getInfo().spellingExistsInTarget(Target,
getAttributeSpellingListIndex());
}

bool ParsedAttr::isKnownToGCC() const { return getInfo().IsKnownToGCC; }
Expand Down
7 changes: 7 additions & 0 deletions clang/lib/Sema/SemaDeclAttr.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -8372,6 +8372,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 @@ -9277,6 +9281,9 @@ ProcessDeclAttribute(Sema &S, Scope *scope, Decl *D, const ParsedAttr &AL,
case ParsedAttr::AT_NoMerge:
handleNoMergeAttr(S, D, AL);
break;
case ParsedAttr::AT_NoUniqueAddress:
handleNoUniqueAddressAttr(S, D, AL);
break;

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