Skip to content

Commit

Permalink
Recommit "Implement [[msvc::no_unique_address]] (#65675)" (#67199)
Browse files Browse the repository at this point in the history
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.
  • Loading branch information
amykhuang committed Sep 28, 2023
1 parent aa5a1b2 commit 0faee97
Show file tree
Hide file tree
Showing 12 changed files with 570 additions and 16 deletions.
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 @@ -4513,9 +4513,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 @@ -4466,6 +4466,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

0 comments on commit 0faee97

Please sign in to comment.