Skip to content

Commit 39f27a9

Browse files
committed
[clang] Stub out gcc_struct attribute
This commit implements gcc_struct attribute with the behavior similar to one in GCC. Current behavior is as follows: When C++ ABI is not "Microsoft" (i. e. when ItaniumRecordLayoutBuilder is used), [[gcc_struct]] will locally cancel the effect of -mms-bitfields on a record. If -mms-bitfields is not supplied and is not a default behavior on a target, [[gcc_struct]] will be a no-op. This should provide enough compatibility with GCC. If C++ ABI is "Microsoft", [[gcc_struct]] will currently always produce a diagnostic, since support for it is not yet implemented in MicrosoftRecordLayoutBuilder. Note, however, that all the infrastructure is ready for the future implementation. In particular, check for default value of -mms-bitfields is moved from a driver to ASTContext, since it now non-trivially depends on other supplied flags. This also, unfortunately, makes it impossible to use usual argument parsing for `-m{no-,}ms-bitfields`. The patch doesn't introduce any backwards-incompatible changes, except for situations when cc1 is called directly with `-mms-bitfields` option.
1 parent 55b1410 commit 39f27a9

22 files changed

+137
-47
lines changed

clang/docs/ReleaseNotes.rst

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -590,6 +590,12 @@ Attribute Changes in Clang
590590
The attributes declare constraints about a function's behavior pertaining to blocking and
591591
heap memory allocation.
592592

593+
- On targets with Itanium C++ ABI, Clang now supports ``[[gnu:gcc_struct]]``
594+
with the behavior similar to one existing in GCC. In particular, whenever
595+
``-mms-bitfields`` command line option is provided (or if Microsoft-compatible
596+
structure layout is default on the target), ``[[gnu::gcc_struct]]`` requests
597+
the compiler to follow Itanium rules for the layout of an annotated structure.
598+
593599
Improvements to Clang's diagnostics
594600
-----------------------------------
595601
- Clang now applies syntax highlighting to the code snippets it

clang/include/clang/AST/ASTContext.h

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2510,6 +2510,14 @@ class ASTContext : public RefCountedBase<ASTContext> {
25102510
/// runtime, such as those using the Itanium C++ ABI.
25112511
CharUnits getExnObjectAlignment() const;
25122512

2513+
/// Return whether getASTRecordLayout will use MicrosoftRecordLayoutBuilder
2514+
/// or ItaniumRecordLayoutBuilder.
2515+
bool isMicrosoftLayout() const;
2516+
2517+
/// Return whether unannotated records are treated as if they have
2518+
/// [[gnu::ms_struct]].
2519+
bool defaultsToMsStruct() const;
2520+
25132521
/// Get or compute information about the layout of the specified
25142522
/// record (struct/union/class) \p D, which indicates its size and field
25152523
/// position information.

clang/include/clang/Basic/Attr.td

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3974,7 +3974,14 @@ def CFGuard : InheritableAttr, TargetSpecificAttr<TargetWindows> {
39743974
def MSStruct : InheritableAttr {
39753975
let Spellings = [GCC<"ms_struct">];
39763976
let Subjects = SubjectList<[Record]>;
3977-
let Documentation = [Undocumented];
3977+
let Documentation = [MSStructDocs];
3978+
let SimpleHandler = 1;
3979+
}
3980+
3981+
def GCCStruct : InheritableAttr {
3982+
let Spellings = [GCC<"gcc_struct">];
3983+
let Subjects = SubjectList<[Record]>;
3984+
let Documentation = [MSStructDocs]; // Covers this attribute too.
39783985
let SimpleHandler = 1;
39793986
}
39803987

clang/include/clang/Basic/AttrDocs.td

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8173,3 +8173,18 @@ of ``nonallocating`` by the compiler.
81738173
}];
81748174
}
81758175

8176+
def MSStructDocs : Documentation {
8177+
let Category = DocCatDecl;
8178+
let Content = [{
8179+
The ``ms_struct`` and ``gcc_struct`` attributes request the compiler to enter a
8180+
special record layout compatibility mode which mimics the layout of Microsoft or
8181+
Itanium C++ ABI respectively. Obviously, if the current C++ ABI matches the
8182+
requested ABI, the attribute does nothing. However, if it does not, annotated
8183+
structure or class is laid out in a special compatibility mode, which slightly
8184+
changes offsets for fields and bit-fields. The intention is to match the layout
8185+
of the requested ABI for structures which only use C features.
8186+
8187+
Note that the default behavior can be controlled by ``-mms-bitfields`` and
8188+
``-mno-ms-bitfields`` switches and via ``#pragma ms_struct``.
8189+
}];
8190+
}

clang/include/clang/Basic/DiagnosticASTKinds.td

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1000,6 +1000,9 @@ def warn_npot_ms_struct : Warning<
10001000
"data types with sizes that aren't a power of two">,
10011001
DefaultError, InGroup<IncompatibleMSStruct>;
10021002

1003+
def err_itanium_layout_unimplemented : Error<
1004+
"Itanium-compatible layout for the Microsoft C++ ABI is not yet supported">;
1005+
10031006
// -Wpadded-bitfield
10041007
def warn_padded_struct_bitfield : Warning<
10051008
"padding %select{struct|interface|class}0 %1 with %2 "

clang/include/clang/Basic/LangOptions.def

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -150,7 +150,8 @@ LANGOPT(AssumeNothrowExceptionDtor , 1, 0, "Assume exception object's destructor
150150
LANGOPT(TraditionalCPP , 1, 0, "traditional CPP emulation")
151151
LANGOPT(RTTI , 1, 1, "run-time type information")
152152
LANGOPT(RTTIData , 1, 1, "emit run-time type information data")
153-
LANGOPT(MSBitfields , 1, 0, "Microsoft-compatible structure layout")
153+
ENUM_LANGOPT(LayoutCompatibility, LayoutCompatibilityKind, 2,
154+
LayoutCompatibilityKind::Default, "Microsoft-compatible structure layout")
154155
LANGOPT(MSVolatile , 1, 0, "Microsoft-compatible volatile loads and stores")
155156
LANGOPT(Freestanding, 1, 0, "freestanding implementation")
156157
LANGOPT(NoBuiltin , 1, 0, "disable builtin functions")

clang/include/clang/Basic/LangOptions.h

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -441,6 +441,16 @@ class LangOptionsBase {
441441
CX_None
442442
};
443443

444+
enum class LayoutCompatibilityKind {
445+
/// Use default layout rules of the target.
446+
Default = 0,
447+
/// Use Itanium rules for bit-field layout and fundamental types alignment.
448+
Itanium = 1,
449+
/// Use Microsoft C++ ABI rules for bit-field layout and fundamental types
450+
/// alignment.
451+
Microsoft = 2,
452+
};
453+
444454
// Define simple language options (with no accessors).
445455
#define LANGOPT(Name, Bits, Default, Description) unsigned Name : Bits;
446456
#define ENUM_LANGOPT(Name, Type, Bits, Default, Description)

clang/include/clang/Driver/Options.td

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4706,9 +4706,7 @@ def mmacos_version_min_EQ : Joined<["-"], "mmacos-version-min=">,
47064706
def : Joined<["-"], "mmacosx-version-min=">,
47074707
Group<m_Group>, Alias<mmacos_version_min_EQ>;
47084708
def mms_bitfields : Flag<["-"], "mms-bitfields">, Group<m_Group>,
4709-
Visibility<[ClangOption, CC1Option]>,
4710-
HelpText<"Set the default structure layout to be compatible with the Microsoft compiler standard">,
4711-
MarshallingInfoFlag<LangOpts<"MSBitfields">>;
4709+
HelpText<"Set the default structure layout to be compatible with the Microsoft compiler standard">;
47124710
def moutline : Flag<["-"], "moutline">, Group<f_clang_Group>,
47134711
Visibility<[ClangOption, CC1Option]>,
47144712
HelpText<"Enable function outlining (AArch64 only)">;
@@ -4717,6 +4715,12 @@ def mno_outline : Flag<["-"], "mno-outline">, Group<f_clang_Group>,
47174715
HelpText<"Disable function outlining (AArch64 only)">;
47184716
def mno_ms_bitfields : Flag<["-"], "mno-ms-bitfields">, Group<m_Group>,
47194717
HelpText<"Do not set the default structure layout to be compatible with the Microsoft compiler standard">;
4718+
def fms_layout_compatibility_EQ : Joined<["-"], "fms-layout-compatibility=">,
4719+
Visibility<[CC1Option]>,
4720+
HelpText<"Structure layout compatibility with Microsoft C++ ABI">,
4721+
Values<"default,itanium,microsoft">,
4722+
NormalizedValues<["Default", "Itanium", "Microsoft"]>, NormalizedValuesScope<"LangOptions::LayoutCompatibilityKind">,
4723+
MarshallingInfoEnum<LangOpts<"LayoutCompatibility">, "Default">;
47204724
def mskip_rax_setup : Flag<["-"], "mskip-rax-setup">, Group<m_Group>,
47214725
Visibility<[ClangOption, CC1Option]>,
47224726
HelpText<"Skip setting up RAX register when passing variable arguments (x86 only)">,

clang/lib/AST/Decl.cpp

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5095,7 +5095,14 @@ void RecordDecl::completeDefinition() {
50955095
/// This which can be turned on with an attribute, pragma, or the
50965096
/// -mms-bitfields command-line option.
50975097
bool RecordDecl::isMsStruct(const ASTContext &C) const {
5098-
return hasAttr<MSStructAttr>() || C.getLangOpts().MSBitfields == 1;
5098+
if (hasAttr<MSStructAttr>())
5099+
return true;
5100+
if (hasAttr<GCCStructAttr>())
5101+
return false;
5102+
auto LayoutCompatibility = C.getLangOpts().getLayoutCompatibility();
5103+
if (LayoutCompatibility == LangOptions::LayoutCompatibilityKind::Default)
5104+
return C.defaultsToMsStruct();
5105+
return LayoutCompatibility == LangOptions::LayoutCompatibilityKind::Microsoft;
50995106
}
51005107

51015108
void RecordDecl::reorderDecls(const SmallVectorImpl<Decl *> &Decls) {

clang/lib/AST/RecordLayoutBuilder.cpp

Lines changed: 24 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -2457,15 +2457,6 @@ static bool mustSkipTailPadding(TargetCXXABI ABI, const CXXRecordDecl *RD) {
24572457
llvm_unreachable("bad tail-padding use kind");
24582458
}
24592459

2460-
static bool isMsLayout(const ASTContext &Context) {
2461-
// Check if it's CUDA device compilation; ensure layout consistency with host.
2462-
if (Context.getLangOpts().CUDA && Context.getLangOpts().CUDAIsDevice &&
2463-
Context.getAuxTargetInfo())
2464-
return Context.getAuxTargetInfo()->getCXXABI().isMicrosoft();
2465-
2466-
return Context.getTargetInfo().getCXXABI().isMicrosoft();
2467-
}
2468-
24692460
// This section contains an implementation of struct layout that is, up to the
24702461
// included tests, compatible with cl.exe (2013). The layout produced is
24712462
// significantly different than those produced by the Itanium ABI. Here we note
@@ -2807,6 +2798,13 @@ void MicrosoftRecordLayoutBuilder::initializeLayout(const RecordDecl *RD) {
28072798
UseExternalLayout = Source->layoutRecordType(
28082799
RD, External.Size, External.Align, External.FieldOffsets,
28092800
External.BaseOffsets, External.VirtualBaseOffsets);
2801+
2802+
if (!RD->isMsStruct(Context)) {
2803+
auto Location = RD->getLocation();
2804+
if (Location.isValid())
2805+
Context.getDiagnostics().Report(Location,
2806+
diag::err_itanium_layout_unimplemented);
2807+
}
28102808
}
28112809

28122810
void
@@ -3328,6 +3326,19 @@ void MicrosoftRecordLayoutBuilder::computeVtorDispSet(
33283326
}
33293327
}
33303328

3329+
bool ASTContext::isMicrosoftLayout() const {
3330+
// Check if it's CUDA device compilation; ensure layout consistency with host.
3331+
if (getLangOpts().CUDA && getLangOpts().CUDAIsDevice && getAuxTargetInfo())
3332+
return getAuxTargetInfo()->getCXXABI().isMicrosoft();
3333+
3334+
return getTargetInfo().getCXXABI().isMicrosoft();
3335+
}
3336+
3337+
bool ASTContext::defaultsToMsStruct() const {
3338+
return isMicrosoftLayout() ||
3339+
getTargetInfo().getTriple().isWindowsGNUEnvironment();
3340+
}
3341+
33313342
/// getASTRecordLayout - Get or compute information about the layout of the
33323343
/// specified record (struct/union/class), which indicates its size and field
33333344
/// position information.
@@ -3356,7 +3367,7 @@ ASTContext::getASTRecordLayout(const RecordDecl *D) const {
33563367

33573368
const ASTRecordLayout *NewEntry = nullptr;
33583369

3359-
if (isMsLayout(*this)) {
3370+
if (isMicrosoftLayout()) {
33603371
if (const auto *RD = dyn_cast<CXXRecordDecl>(D)) {
33613372
EmptySubobjectMap EmptySubobjects(*this, RD);
33623373
MicrosoftRecordLayoutBuilder Builder(*this, &EmptySubobjects);
@@ -3632,7 +3643,7 @@ static void DumpRecordLayout(raw_ostream &OS, const RecordDecl *RD,
36323643
bool HasOwnVBPtr = Layout.hasOwnVBPtr();
36333644

36343645
// Vtable pointer.
3635-
if (CXXRD->isDynamicClass() && !PrimaryBase && !isMsLayout(C)) {
3646+
if (CXXRD->isDynamicClass() && !PrimaryBase && !C.isMicrosoftLayout()) {
36363647
PrintOffset(OS, Offset, IndentLevel);
36373648
OS << '(' << *RD << " vtable pointer)\n";
36383649
} else if (HasOwnVFPtr) {
@@ -3732,7 +3743,7 @@ static void DumpRecordLayout(raw_ostream &OS, const RecordDecl *RD,
37323743

37333744
PrintIndentNoOffset(OS, IndentLevel - 1);
37343745
OS << "[sizeof=" << Layout.getSize().getQuantity();
3735-
if (CXXRD && !isMsLayout(C))
3746+
if (CXXRD && !C.isMicrosoftLayout())
37363747
OS << ", dsize=" << Layout.getDataSize().getQuantity();
37373748
OS << ", align=" << Layout.getAlignment().getQuantity();
37383749
if (C.getTargetInfo().defaultsToAIXPowerAlignment())
@@ -3771,7 +3782,7 @@ void ASTContext::DumpRecordLayout(const RecordDecl *RD, raw_ostream &OS,
37713782
OS << "\nLayout: ";
37723783
OS << "<ASTRecordLayout\n";
37733784
OS << " Size:" << toBits(Info.getSize()) << "\n";
3774-
if (!isMsLayout(*this))
3785+
if (!isMicrosoftLayout())
37753786
OS << " DataSize:" << toBits(Info.getDataSize()) << "\n";
37763787
OS << " Alignment:" << toBits(Info.getAlignment()) << "\n";
37773788
if (Target->defaultsToAIXPowerAlignment())

0 commit comments

Comments
 (0)