Skip to content
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
6 changes: 6 additions & 0 deletions clang/docs/ReleaseNotes.rst
Original file line number Diff line number Diff line change
Expand Up @@ -379,6 +379,12 @@ Attribute Changes in Clang
multilibs for printf features. Requires cooperation with the libc
implementation.

- On targets with Itanium C++ ABI, Clang now supports ``[[gnu:gcc_struct]]``
with the behavior similar to one existing in GCC. In particular, whenever
``-mms-bitfields`` command line option is provided (or if Microsoft-compatible
structure layout is default on the target), ``[[gnu::gcc_struct]]`` requests
the compiler to follow Itanium rules for the layout of an annotated structure.

Improvements to Clang's diagnostics
-----------------------------------
- Diagnostics messages now refer to ``structured binding`` instead of ``decomposition``,
Expand Down
4 changes: 4 additions & 0 deletions clang/include/clang/AST/ASTContext.h
Original file line number Diff line number Diff line change
Expand Up @@ -2810,6 +2810,10 @@ class ASTContext : public RefCountedBase<ASTContext> {
/// runtime, such as those using the Itanium C++ ABI.
CharUnits getExnObjectAlignment() const;

/// Return whether unannotated records are treated as if they have
/// [[gnu::ms_struct]].
bool defaultsToMsStruct() const;

/// Get or compute information about the layout of the specified
/// record (struct/union/class) \p D, which indicates its size and field
/// position information.
Expand Down
9 changes: 7 additions & 2 deletions clang/include/clang/Basic/Attr.td
Original file line number Diff line number Diff line change
Expand Up @@ -4387,8 +4387,13 @@ def CFGuard : InheritableAttr, TargetSpecificAttr<TargetWindows> {
def MSStruct : InheritableAttr {
let Spellings = [GCC<"ms_struct">];
let Subjects = SubjectList<[Record]>;
let Documentation = [Undocumented];
let SimpleHandler = 1;
let Documentation = [MSStructDocs];
}

def GCCStruct : InheritableAttr {
let Spellings = [GCC<"gcc_struct">];
let Subjects = SubjectList<[Record]>;
let Documentation = [MSStructDocs]; // Covers this attribute too.
}

def DLLExport : InheritableAttr, TargetSpecificAttr<TargetHasDLLImportExport> {
Expand Down
21 changes: 21 additions & 0 deletions clang/include/clang/Basic/AttrDocs.td
Original file line number Diff line number Diff line change
Expand Up @@ -9682,3 +9682,24 @@ The following aspects are currently supported:
- ``float``: The call has a floating point argument
}];
}

def MSStructDocs : Documentation {
let Category = DocCatDecl;
let Content = [{
The ``ms_struct`` and ``gcc_struct`` attributes request the compiler to enter a
special record layout compatibility mode which mimics the layout of Microsoft or
Itanium C++ ABI respectively. Obviously, if the current C++ ABI matches the
requested ABI, the attribute does nothing. However, if it does not, annotated
structure or class is laid out in a special compatibility mode, which slightly
changes offsets for fields and bit-fields. The intention is to match the layout
of the requested ABI for structures which only use C features.

Note that the default behavior can be controlled by ``-mms-bitfields`` and
Copy link
Collaborator

Choose a reason for hiding this comment

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

It would be worth expanding on this documentation to include WHAT the differences (at least at the high level) between the two layouts is, and why one might want to use it.

``-mno-ms-bitfields`` switches and via ``#pragma ms_struct``.

The primary difference is for bitfields, where the MS variant only packs
adjacent fields into the same allocation unit if they have integral types
of the same size, while the GCC/Itanium variant packs all fields in a bitfield
tightly.
}];
}
3 changes: 3 additions & 0 deletions clang/include/clang/Basic/DiagnosticASTKinds.td
Original file line number Diff line number Diff line change
Expand Up @@ -1040,6 +1040,9 @@ def warn_npot_ms_struct : Warning<
"data types with sizes that aren't a power of two">,
DefaultError, InGroup<IncompatibleMSStruct>;

def err_itanium_layout_unimplemented : Error<
"Itanium-compatible layout for the Microsoft C++ ABI is not yet supported">;

// -Wpadded-bitfield
def warn_padded_struct_bitfield : Warning<
"padding %select{struct|interface|class}0 %1 with %2 "
Expand Down
4 changes: 3 additions & 1 deletion clang/include/clang/Basic/LangOptions.def
Original file line number Diff line number Diff line change
Expand Up @@ -104,7 +104,9 @@ LANGOPT(AssumeNothrowExceptionDtor , 1, 0, NotCompatible, "Assume exception obje
LANGOPT(TraditionalCPP , 1, 0, NotCompatible, "traditional CPP emulation")
LANGOPT(RTTI , 1, 1, NotCompatible, "run-time type information")
LANGOPT(RTTIData , 1, 1, NotCompatible, "emit run-time type information data")
LANGOPT(MSBitfields , 1, 0, NotCompatible, "Microsoft-compatible structure layout")
ENUM_LANGOPT(LayoutCompatibility, LayoutCompatibilityKind, 2,
LayoutCompatibilityKind::Default, NotCompatible,
"Microsoft-compatible structure layout")
LANGOPT(MSVolatile , 1, 0, NotCompatible, "Microsoft-compatible volatile loads and stores")
LANGOPT(Freestanding , 1, 0, NotCompatible, "freestanding implementation")
LANGOPT(NoBuiltin , 1, 0, NotCompatible, "disable builtin functions")
Expand Down
10 changes: 10 additions & 0 deletions clang/include/clang/Basic/LangOptions.h
Original file line number Diff line number Diff line change
Expand Up @@ -421,6 +421,16 @@ class LangOptionsBase {
None,
};

enum class LayoutCompatibilityKind {
/// Use default layout rules of the target.
Default = 0,
/// Use Itanium rules for bit-field layout and fundamental types alignment.
Itanium = 1,
/// Use Microsoft C++ ABI rules for bit-field layout and fundamental types
/// alignment.
Microsoft = 2,
};

// Define simple language options (with no accessors).
#define LANGOPT(Name, Bits, Default, Compatibility, Description) \
unsigned Name : Bits;
Expand Down
10 changes: 7 additions & 3 deletions clang/include/clang/Options/Options.td
Original file line number Diff line number Diff line change
Expand Up @@ -5219,9 +5219,7 @@ def : Joined<["-"], "mmacosx-version-min=">,
Visibility<[ClangOption, CC1Option, FC1Option, FlangOption]>,
Group<m_Group>, Alias<mmacos_version_min_EQ>;
def mms_bitfields : Flag<["-"], "mms-bitfields">, Group<m_Group>,
Visibility<[ClangOption, CC1Option]>,
HelpText<"Set the default structure layout to be compatible with the Microsoft compiler standard">,
MarshallingInfoFlag<LangOpts<"MSBitfields">>;
HelpText<"Set the default structure layout to be compatible with the Microsoft compiler standard">;
def moutline : Flag<["-"], "moutline">, Group<f_clang_Group>,
Visibility<[ClangOption, CC1Option]>,
HelpText<"Enable function outlining (AArch64 only)">;
Expand All @@ -5230,6 +5228,12 @@ def mno_outline : Flag<["-"], "mno-outline">, Group<f_clang_Group>,
HelpText<"Disable function outlining (AArch64 only)">;
def mno_ms_bitfields : Flag<["-"], "mno-ms-bitfields">, Group<m_Group>,
HelpText<"Do not set the default structure layout to be compatible with the Microsoft compiler standard">;
def fms_layout_compatibility_EQ : Joined<["-"], "fms-layout-compatibility=">,
Visibility<[CC1Option]>,
HelpText<"Structure layout compatibility with Microsoft C++ ABI">,
Values<"default,itanium,microsoft">,
NormalizedValues<["Default", "Itanium", "Microsoft"]>, NormalizedValuesScope<"LangOptions::LayoutCompatibilityKind">,
MarshallingInfoEnum<LangOpts<"LayoutCompatibility">, "Default">;
def mskip_rax_setup : Flag<["-"], "mskip-rax-setup">, Group<m_Group>,
Visibility<[ClangOption, CC1Option]>,
HelpText<"Skip setting up RAX register when passing variable arguments (x86 only)">,
Expand Down
9 changes: 8 additions & 1 deletion clang/lib/AST/Decl.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -5244,7 +5244,14 @@ void RecordDecl::completeDefinition() {
/// This which can be turned on with an attribute, pragma, or the
/// -mms-bitfields command-line option.
bool RecordDecl::isMsStruct(const ASTContext &C) const {
return hasAttr<MSStructAttr>() || C.getLangOpts().MSBitfields == 1;
if (hasAttr<GCCStructAttr>())
return false;
if (hasAttr<MSStructAttr>())
return true;
auto LayoutCompatibility = C.getLangOpts().getLayoutCompatibility();
if (LayoutCompatibility == LangOptions::LayoutCompatibilityKind::Default)
return C.defaultsToMsStruct();
return LayoutCompatibility == LangOptions::LayoutCompatibilityKind::Microsoft;
}

void RecordDecl::reorderDecls(const SmallVectorImpl<Decl *> &Decls) {
Expand Down
12 changes: 12 additions & 0 deletions clang/lib/AST/RecordLayoutBuilder.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2794,6 +2794,13 @@ void MicrosoftRecordLayoutBuilder::initializeLayout(const RecordDecl *RD) {
UseExternalLayout = Source->layoutRecordType(
RD, External.Size, External.Align, External.FieldOffsets,
External.BaseOffsets, External.VirtualBaseOffsets);

if (!RD->isMsStruct(Context)) {
auto Location = RD->getLocation();
if (Location.isValid())
Context.getDiagnostics().Report(Location,
diag::err_itanium_layout_unimplemented);
}
}

void
Expand Down Expand Up @@ -3358,6 +3365,11 @@ void MicrosoftRecordLayoutBuilder::computeVtorDispSet(
}
}

bool ASTContext::defaultsToMsStruct() const {
return getTargetInfo().hasMicrosoftRecordLayout() ||
getTargetInfo().getTriple().isWindowsGNUEnvironment();
}

/// getASTRecordLayout - Get or compute information about the layout of the
/// specified record (struct/union/class), which indicates its size and field
/// position information.
Expand Down
9 changes: 6 additions & 3 deletions clang/lib/Driver/ToolChains/Clang.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -5889,9 +5889,12 @@ void Clang::ConstructJob(Compilation &C, const JobAction &JA,
if (KernelOrKext && RawTriple.isOSDarwin())
CmdArgs.push_back("-fforbid-guard-variables");

if (Args.hasFlag(options::OPT_mms_bitfields, options::OPT_mno_ms_bitfields,
Triple.isWindowsGNUEnvironment())) {
CmdArgs.push_back("-mms-bitfields");
if (Arg *A = Args.getLastArg(options::OPT_mms_bitfields,
options::OPT_mno_ms_bitfields)) {
if (A->getOption().matches(options::OPT_mms_bitfields))
CmdArgs.push_back("-fms-layout-compatibility=microsoft");
else
CmdArgs.push_back("-fms-layout-compatibility=itanium");
}

if (Triple.isOSCygMing()) {
Expand Down
4 changes: 1 addition & 3 deletions clang/lib/Sema/SemaDecl.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -18992,9 +18992,7 @@ ExprResult Sema::VerifyBitField(SourceLocation FieldLoc,
// ABI.
bool CStdConstraintViolation =
BitfieldIsOverwide && !getLangOpts().CPlusPlus;
bool MSBitfieldViolation =
Value.ugt(TypeStorageSize) &&
(IsMsStruct || Context.getTargetInfo().getCXXABI().isMicrosoft());
bool MSBitfieldViolation = Value.ugt(TypeStorageSize) && IsMsStruct;
if (CStdConstraintViolation || MSBitfieldViolation) {
unsigned DiagWidth =
CStdConstraintViolation ? TypeWidth : TypeStorageSize;
Expand Down
38 changes: 38 additions & 0 deletions clang/lib/Sema/SemaDeclAttr.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -6173,6 +6173,36 @@ static void handleMSConstexprAttr(Sema &S, Decl *D, const ParsedAttr &AL) {
D->addAttr(::new (S.Context) MSConstexprAttr(S.Context, AL));
}

static void handleMSStructAttr(Sema &S, Decl *D, const ParsedAttr &AL) {
if (const auto *First = D->getAttr<GCCStructAttr>()) {
S.Diag(AL.getLoc(), diag::err_attributes_are_not_compatible)
<< AL << First << 0;
S.Diag(First->getLocation(), diag::note_conflicting_attribute);
return;
}
if (const auto *Preexisting = D->getAttr<MSStructAttr>()) {
if (Preexisting->isImplicit())
D->dropAttr<MSStructAttr>();
}

D->addAttr(::new (S.Context) MSStructAttr(S.Context, AL));
}

static void handleGCCStructAttr(Sema &S, Decl *D, const ParsedAttr &AL) {
if (const auto *First = D->getAttr<MSStructAttr>()) {
if (First->isImplicit()) {
D->dropAttr<MSStructAttr>();
} else {
S.Diag(AL.getLoc(), diag::err_attributes_are_not_compatible)
<< AL << First << 0;
S.Diag(First->getLocation(), diag::note_conflicting_attribute);
return;
}
}

D->addAttr(::new (S.Context) GCCStructAttr(S.Context, AL));
}

static void handleAbiTagAttr(Sema &S, Decl *D, const ParsedAttr &AL) {
SmallVector<StringRef, 4> Tags;
for (unsigned I = 0, E = AL.getNumArgs(); I != E; ++I) {
Expand Down Expand Up @@ -7983,6 +8013,14 @@ ProcessDeclAttribute(Sema &S, Scope *scope, Decl *D, const ParsedAttr &AL,
case ParsedAttr::AT_ModularFormat:
handleModularFormat(S, D, AL);
break;

case ParsedAttr::AT_MSStruct:
handleMSStructAttr(S, D, AL);
break;

case ParsedAttr::AT_GCCStruct:
handleGCCStructAttr(S, D, AL);
break;
}
}

Expand Down
27 changes: 17 additions & 10 deletions clang/lib/Sema/SemaDeclCXX.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -7288,20 +7288,27 @@ void Sema::CheckCompletedCXXClass(Scope *S, CXXRecordDecl *Record) {
CheckCompletedMemberFunction(MD);
}

// ms_struct is a request to use the same ABI rules as MSVC. Check
// whether this class uses any C++ features that are implemented
// completely differently in MSVC, and if so, emit a diagnostic.
// That diagnostic defaults to an error, but we allow projects to
// map it down to a warning (or ignore it). It's a fairly common
// practice among users of the ms_struct pragma to mass-annotate
// headers, sweeping up a bunch of types that the project doesn't
// really rely on MSVC-compatible layout for. We must therefore
// support "ms_struct except for C++ stuff" as a secondary ABI.
// {ms,gcc}_struct is a request to change ABI rules to either follow
// Microsoft or Itanium C++ ABI. However, even if these attributes are
// present, we do not layout classes following foreign ABI rules, but
// instead enter a special "compatibility mode", which only changes
// alignments of fundamental types and layout of bit fields.
// Check whether this class uses any C++ features that are implemented
// completely differently in the requested ABI, and if so, emit a
// diagnostic. That diagnostic defaults to an error, but we allow
// projects to map it down to a warning (or ignore it). It's a fairly
// common practice among users of the ms_struct pragma to
// mass-annotate headers, sweeping up a bunch of types that the
// project doesn't really rely on MSVC-compatible layout for. We must
// therefore support "ms_struct except for C++ stuff" as a secondary
// ABI.
// Don't emit this diagnostic if the feature was enabled as a
// language option (as opposed to via a pragma or attribute), as
// the option -mms-bitfields otherwise essentially makes it impossible
// to build C++ code, unless this diagnostic is turned off.
if (Record->isMsStruct(Context) && !Context.getLangOpts().MSBitfields &&
if (Context.getLangOpts().getLayoutCompatibility() ==
LangOptions::LayoutCompatibilityKind::Default &&
Record->isMsStruct(Context) != Context.defaultsToMsStruct() &&
(Record->isPolymorphic() || Record->getNumBases())) {
Diag(Record->getLocation(), diag::warn_cxx_ms_struct);
}
Expand Down
6 changes: 6 additions & 0 deletions clang/test/CodeGen/gcc_struct-msvc-todo-1.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
// RUN: %clang_cc1 -emit-llvm-only -triple x86_64-pc-windows-msvc -verify %s

// expected-error@+1 {{Itanium-compatible layout for the Microsoft C++ ABI is not yet supported}}
struct {
int a;
} __attribute__((gcc_struct)) t1;
6 changes: 6 additions & 0 deletions clang/test/CodeGen/gcc_struct-msvc-todo-2.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
// RUN: %clang_cc1 -emit-llvm-only -triple x86_64-pc-windows-msvc -fms-layout-compatibility=itanium -verify %s

// expected-error@+1 {{Itanium-compatible layout for the Microsoft C++ ABI is not yet supported}}
struct {
int a;
} t1;
18 changes: 18 additions & 0 deletions clang/test/CodeGen/gcc_struct.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
// RUN: %clang_cc1 -emit-llvm-only -triple x86_64-pc-linux-gnu %s
// RUN: %clang_cc1 -emit-llvm-only -triple x86_64-pc-linux-gnu -fms-layout-compatibility=microsoft %s
// RUN: %clang_cc1 -emit-llvm-only -triple x86_64-pc-windows-gnu %s
// RUN: %clang_cc1 -emit-llvm-only -triple x86_64-pc-windows-gnu -fms-layout-compatibility=itanium %s

struct {
int a : 24;
char b : 8;
} __attribute__((gcc_struct)) t1;
_Static_assert(sizeof(t1) == 4, "");

#pragma ms_struct on
struct {
int a : 24;
char b : 8;
} __attribute__((gcc_struct)) t2;
_Static_assert(sizeof(t2) == 4, "");
#pragma ms_struct off
2 changes: 1 addition & 1 deletion clang/test/CodeGen/mingw-long-double.c
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
// RUN: %clang_cc1 -triple i686-windows-gnu -emit-llvm -o - %s \
// RUN: | FileCheck %s --check-prefix=GNU32
// RUN: %clang_cc1 -triple i686-windows-gnu -emit-llvm -o - %s -mms-bitfields \
// RUN: %clang_cc1 -triple i686-windows-gnu -emit-llvm -o - %s -fms-layout-compatibility=microsoft \
// RUN: | FileCheck %s --check-prefix=GNU32
// RUN: %clang_cc1 -triple x86_64-windows-gnu -emit-llvm -o - %s \
// RUN: | FileCheck %s --check-prefix=GNU64
Expand Down
2 changes: 1 addition & 1 deletion clang/test/CodeGen/mms-bitfields.c
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
// RUN: %clang_cc1 -triple i386-apple-darwin10 -mms-bitfields -emit-llvm %s -o - | FileCheck %s
// RUN: %clang_cc1 -triple i386-apple-darwin10 -fms-layout-compatibility=microsoft -emit-llvm %s -o - | FileCheck %s

struct s1 {
int f32;
Expand Down
19 changes: 13 additions & 6 deletions clang/test/Driver/ms-bitfields.c
Original file line number Diff line number Diff line change
@@ -1,8 +1,15 @@
// RUN: %clang -### --target=x86_64-linux-gnu %s 2>&1 | FileCheck %s -check-prefix=NO-MSBITFIELDS
// RUN: %clang -### --target=x86_64-windows-gnu %s 2>&1 | FileCheck %s -check-prefix=MSBITFIELDS
// RUN: %clang -### -mno-ms-bitfields -mms-bitfields %s 2>&1 | FileCheck %s -check-prefix=MSBITFIELDS
// RUN: %clang -### -mms-bitfields -mno-ms-bitfields %s 2>&1 | FileCheck %s -check-prefix=NO-MSBITFIELDS
// RUN: %clang -### --target=x86_64-linux-gnu %s 2>&1 | FileCheck %s -check-prefix=DEFAULT-LAYOUT
// RUN: %clang -### --target=x86_64-windows-gnu %s 2>&1 | FileCheck %s -check-prefix=DEFAULT-LAYOUT
// RUN: %clang -### --target=x86_64-windows-msvc %s 2>&1 | FileCheck %s -check-prefix=DEFAULT-LAYOUT
// RUN: %clang -### -mms-bitfields %s 2>&1 | FileCheck %s -check-prefix=MICROSOFT-LAYOUT
// RUN: %clang -### -mno-ms-bitfields %s 2>&1 | FileCheck %s -check-prefix=ITANIUM-LAYOUT
// RUN: %clang -### -mno-ms-bitfields -mms-bitfields %s 2>&1 | FileCheck %s -check-prefix=MICROSOFT-LAYOUT
// RUN: %clang -### -mms-bitfields -mno-ms-bitfields %s 2>&1 | FileCheck %s -check-prefix=ITANIUM-LAYOUT

// MSBITFIELDS: -mms-bitfields
// NO-MSBITFIELDS-NOT: -mms-bitfields
// DEFAULT-LAYOUT-NOT: -fms-layout-compatibility=itanium
// DEFAULT-LAYOUT-NOT: -fms-layout-compatibility=microsoft
// MICROSOFT-LAYOUT: -fms-layout-compatibility=microsoft
// MICROSOFT-LAYOUT-NOT: -fms-layout-compatibility=itanium
// ITANIUM-LAYOUT: -fms-layout-compatibility=itanium
// ITANIUM-LAYOUT-NOT: -fms-layout-compatibility=microsoft

2 changes: 1 addition & 1 deletion clang/test/Layout/itanium-union-bitfield.cpp
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
// RUN: %clang_cc1 -emit-llvm-only -triple %itanium_abi_triple -fdump-record-layouts %s 2>/dev/null \
// RUN: %clang_cc1 -emit-llvm-only -triple %itanium_abi_triple -fms-layout-compatibility=itanium -fdump-record-layouts %s 2>/dev/null \
// RUN: | FileCheck %s

// On z/OS, a bit-field has single byte alignment. Add aligned(4) on z/OS so the union has
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -87,6 +87,7 @@
// CHECK-NEXT: FlagEnum (SubjectMatchRule_enum)
// CHECK-NEXT: Flatten (SubjectMatchRule_function)
// CHECK-NEXT: FunctionReturnThunks (SubjectMatchRule_function)
// CHECK-NEXT: GCCStruct (SubjectMatchRule_record)
// CHECK-NEXT: GNUInline (SubjectMatchRule_function)
// CHECK-NEXT: HIPManaged (SubjectMatchRule_variable)
// CHECK-NEXT: HLSLVkLocation (SubjectMatchRule_variable_is_parameter, SubjectMatchRule_field, SubjectMatchRule_function)
Expand Down
Loading