Skip to content

Commit 77f9935

Browse files
committed
[clang] Implement gcc_struct attribute on Itanium targets
This commit implements gcc_struct attribute with the behavior similar to one in GCC. Current behavior is as follows: 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. Work towards #24757
1 parent 5e4b41c commit 77f9935

22 files changed

+138
-47
lines changed

clang/docs/ReleaseNotes.rst

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -574,6 +574,12 @@ Attribute Changes in Clang
574574

575575
- Clang now disallows the use of attributes after the namespace name. (#GH121407)
576576

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

clang/include/clang/AST/ASTContext.h

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

2613+
/// Return whether getASTRecordLayout will use MicrosoftRecordLayoutBuilder
2614+
/// or ItaniumRecordLayoutBuilder.
2615+
bool isMicrosoftLayout() const;
2616+
2617+
/// Return whether unannotated records are treated as if they have
2618+
/// [[gnu::ms_struct]].
2619+
bool defaultsToMsStruct() const;
2620+
26132621
/// Get or compute information about the layout of the specified
26142622
/// record (struct/union/class) \p D, which indicates its size and field
26152623
/// position information.

clang/include/clang/Basic/Attr.td

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4130,7 +4130,14 @@ def CFGuard : InheritableAttr, TargetSpecificAttr<TargetWindows> {
41304130
def MSStruct : InheritableAttr {
41314131
let Spellings = [GCC<"ms_struct">];
41324132
let Subjects = SubjectList<[Record]>;
4133-
let Documentation = [Undocumented];
4133+
let Documentation = [MSStructDocs];
4134+
let SimpleHandler = 1;
4135+
}
4136+
4137+
def GCCStruct : InheritableAttr {
4138+
let Spellings = [GCC<"gcc_struct">];
4139+
let Subjects = SubjectList<[Record]>;
4140+
let Documentation = [MSStructDocs]; // Covers this attribute too.
41344141
let SimpleHandler = 1;
41354142
}
41364143

clang/include/clang/Basic/AttrDocs.td

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8943,3 +8943,19 @@ Declares that a function potentially allocates heap memory, and prevents any pot
89438943
of ``nonallocating`` by the compiler.
89448944
}];
89458945
}
8946+
8947+
def MSStructDocs : Documentation {
8948+
let Category = DocCatDecl;
8949+
let Content = [{
8950+
The ``ms_struct`` and ``gcc_struct`` attributes request the compiler to enter a
8951+
special record layout compatibility mode which mimics the layout of Microsoft or
8952+
Itanium C++ ABI respectively. Obviously, if the current C++ ABI matches the
8953+
requested ABI, the attribute does nothing. However, if it does not, annotated
8954+
structure or class is laid out in a special compatibility mode, which slightly
8955+
changes offsets for fields and bit-fields. The intention is to match the layout
8956+
of the requested ABI for structures which only use C features.
8957+
8958+
Note that the default behavior can be controlled by ``-mms-bitfields`` and
8959+
``-mno-ms-bitfields`` switches and via ``#pragma ms_struct``.
8960+
}];
8961+
}

clang/include/clang/Basic/DiagnosticASTKinds.td

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

1000+
def err_itanium_layout_unimplemented : Error<
1001+
"Itanium-compatible layout for the Microsoft C++ ABI is not yet supported">;
1002+
10001003
// -Wpadded-bitfield
10011004
def warn_padded_struct_bitfield : Warning<
10021005
"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
@@ -151,7 +151,8 @@ LANGOPT(AssumeNothrowExceptionDtor , 1, 0, "Assume exception object's destructor
151151
LANGOPT(TraditionalCPP , 1, 0, "traditional CPP emulation")
152152
LANGOPT(RTTI , 1, 1, "run-time type information")
153153
LANGOPT(RTTIData , 1, 1, "emit run-time type information data")
154-
LANGOPT(MSBitfields , 1, 0, "Microsoft-compatible structure layout")
154+
ENUM_LANGOPT(LayoutCompatibility, LayoutCompatibilityKind, 2,
155+
LayoutCompatibilityKind::Default, "Microsoft-compatible structure layout")
155156
LANGOPT(MSVolatile , 1, 0, "Microsoft-compatible volatile loads and stores")
156157
LANGOPT(Freestanding, 1, 0, "freestanding implementation")
157158
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
@@ -479,6 +479,16 @@ class LangOptionsBase {
479479
None,
480480
};
481481

482+
enum class LayoutCompatibilityKind {
483+
/// Use default layout rules of the target.
484+
Default = 0,
485+
/// Use Itanium rules for bit-field layout and fundamental types alignment.
486+
Itanium = 1,
487+
/// Use Microsoft C++ ABI rules for bit-field layout and fundamental types
488+
/// alignment.
489+
Microsoft = 2,
490+
};
491+
482492
// Define simple language options (with no accessors).
483493
#define LANGOPT(Name, Bits, Default, Description) unsigned Name : Bits;
484494
#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
@@ -4861,9 +4861,7 @@ def mmacos_version_min_EQ : Joined<["-"], "mmacos-version-min=">,
48614861
def : Joined<["-"], "mmacosx-version-min=">,
48624862
Group<m_Group>, Alias<mmacos_version_min_EQ>;
48634863
def mms_bitfields : Flag<["-"], "mms-bitfields">, Group<m_Group>,
4864-
Visibility<[ClangOption, CC1Option]>,
4865-
HelpText<"Set the default structure layout to be compatible with the Microsoft compiler standard">,
4866-
MarshallingInfoFlag<LangOpts<"MSBitfields">>;
4864+
HelpText<"Set the default structure layout to be compatible with the Microsoft compiler standard">;
48674865
def moutline : Flag<["-"], "moutline">, Group<f_clang_Group>,
48684866
Visibility<[ClangOption, CC1Option]>,
48694867
HelpText<"Enable function outlining (AArch64 only)">;
@@ -4872,6 +4870,12 @@ def mno_outline : Flag<["-"], "mno-outline">, Group<f_clang_Group>,
48724870
HelpText<"Disable function outlining (AArch64 only)">;
48734871
def mno_ms_bitfields : Flag<["-"], "mno-ms-bitfields">, Group<m_Group>,
48744872
HelpText<"Do not set the default structure layout to be compatible with the Microsoft compiler standard">;
4873+
def fms_layout_compatibility_EQ : Joined<["-"], "fms-layout-compatibility=">,
4874+
Visibility<[CC1Option]>,
4875+
HelpText<"Structure layout compatibility with Microsoft C++ ABI">,
4876+
Values<"default,itanium,microsoft">,
4877+
NormalizedValues<["Default", "Itanium", "Microsoft"]>, NormalizedValuesScope<"LangOptions::LayoutCompatibilityKind">,
4878+
MarshallingInfoEnum<LangOpts<"LayoutCompatibility">, "Default">;
48754879
def mskip_rax_setup : Flag<["-"], "mskip-rax-setup">, Group<m_Group>,
48764880
Visibility<[ClangOption, CC1Option]>,
48774881
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
@@ -5125,7 +5125,14 @@ void RecordDecl::completeDefinition() {
51255125
/// This which can be turned on with an attribute, pragma, or the
51265126
/// -mms-bitfields command-line option.
51275127
bool RecordDecl::isMsStruct(const ASTContext &C) const {
5128-
return hasAttr<MSStructAttr>() || C.getLangOpts().MSBitfields == 1;
5128+
if (hasAttr<MSStructAttr>())
5129+
return true;
5130+
if (hasAttr<GCCStructAttr>())
5131+
return false;
5132+
auto LayoutCompatibility = C.getLangOpts().getLayoutCompatibility();
5133+
if (LayoutCompatibility == LangOptions::LayoutCompatibilityKind::Default)
5134+
return C.defaultsToMsStruct();
5135+
return LayoutCompatibility == LangOptions::LayoutCompatibilityKind::Microsoft;
51295136
}
51305137

51315138
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
@@ -2443,15 +2443,6 @@ static bool mustSkipTailPadding(TargetCXXABI ABI, const CXXRecordDecl *RD) {
24432443
llvm_unreachable("bad tail-padding use kind");
24442444
}
24452445

2446-
static bool isMsLayout(const ASTContext &Context) {
2447-
// Check if it's CUDA device compilation; ensure layout consistency with host.
2448-
if (Context.getLangOpts().CUDA && Context.getLangOpts().CUDAIsDevice &&
2449-
Context.getAuxTargetInfo())
2450-
return Context.getAuxTargetInfo()->getCXXABI().isMicrosoft();
2451-
2452-
return Context.getTargetInfo().getCXXABI().isMicrosoft();
2453-
}
2454-
24552446
// This section contains an implementation of struct layout that is, up to the
24562447
// included tests, compatible with cl.exe (2013). The layout produced is
24572448
// significantly different than those produced by the Itanium ABI. Here we note
@@ -2793,6 +2784,13 @@ void MicrosoftRecordLayoutBuilder::initializeLayout(const RecordDecl *RD) {
27932784
UseExternalLayout = Source->layoutRecordType(
27942785
RD, External.Size, External.Align, External.FieldOffsets,
27952786
External.BaseOffsets, External.VirtualBaseOffsets);
2787+
2788+
if (!RD->isMsStruct(Context)) {
2789+
auto Location = RD->getLocation();
2790+
if (Location.isValid())
2791+
Context.getDiagnostics().Report(Location,
2792+
diag::err_itanium_layout_unimplemented);
2793+
}
27962794
}
27972795

27982796
void
@@ -3314,6 +3312,19 @@ void MicrosoftRecordLayoutBuilder::computeVtorDispSet(
33143312
}
33153313
}
33163314

3315+
bool ASTContext::isMicrosoftLayout() const {
3316+
// Check if it's CUDA device compilation; ensure layout consistency with host.
3317+
if (getLangOpts().CUDA && getLangOpts().CUDAIsDevice && getAuxTargetInfo())
3318+
return getAuxTargetInfo()->getCXXABI().isMicrosoft();
3319+
3320+
return getTargetInfo().getCXXABI().isMicrosoft();
3321+
}
3322+
3323+
bool ASTContext::defaultsToMsStruct() const {
3324+
return isMicrosoftLayout() ||
3325+
getTargetInfo().getTriple().isWindowsGNUEnvironment();
3326+
}
3327+
33173328
/// getASTRecordLayout - Get or compute information about the layout of the
33183329
/// specified record (struct/union/class), which indicates its size and field
33193330
/// position information.
@@ -3342,7 +3353,7 @@ ASTContext::getASTRecordLayout(const RecordDecl *D) const {
33423353

33433354
const ASTRecordLayout *NewEntry = nullptr;
33443355

3345-
if (isMsLayout(*this)) {
3356+
if (isMicrosoftLayout()) {
33463357
if (const auto *RD = dyn_cast<CXXRecordDecl>(D)) {
33473358
EmptySubobjectMap EmptySubobjects(*this, RD);
33483359
MicrosoftRecordLayoutBuilder Builder(*this, &EmptySubobjects);
@@ -3618,7 +3629,7 @@ static void DumpRecordLayout(raw_ostream &OS, const RecordDecl *RD,
36183629
bool HasOwnVBPtr = Layout.hasOwnVBPtr();
36193630

36203631
// Vtable pointer.
3621-
if (CXXRD->isDynamicClass() && !PrimaryBase && !isMsLayout(C)) {
3632+
if (CXXRD->isDynamicClass() && !PrimaryBase && !C.isMicrosoftLayout()) {
36223633
PrintOffset(OS, Offset, IndentLevel);
36233634
OS << '(' << *RD << " vtable pointer)\n";
36243635
} else if (HasOwnVFPtr) {
@@ -3716,7 +3727,7 @@ static void DumpRecordLayout(raw_ostream &OS, const RecordDecl *RD,
37163727

37173728
PrintIndentNoOffset(OS, IndentLevel - 1);
37183729
OS << "[sizeof=" << Layout.getSize().getQuantity();
3719-
if (CXXRD && !isMsLayout(C))
3730+
if (CXXRD && !C.isMicrosoftLayout())
37203731
OS << ", dsize=" << Layout.getDataSize().getQuantity();
37213732
OS << ", align=" << Layout.getAlignment().getQuantity();
37223733
if (C.getTargetInfo().defaultsToAIXPowerAlignment())
@@ -3755,7 +3766,7 @@ void ASTContext::DumpRecordLayout(const RecordDecl *RD, raw_ostream &OS,
37553766
OS << "\nLayout: ";
37563767
OS << "<ASTRecordLayout\n";
37573768
OS << " Size:" << toBits(Info.getSize()) << "\n";
3758-
if (!isMsLayout(*this))
3769+
if (!isMicrosoftLayout())
37593770
OS << " DataSize:" << toBits(Info.getDataSize()) << "\n";
37603771
OS << " Alignment:" << toBits(Info.getAlignment()) << "\n";
37613772
if (Target->defaultsToAIXPowerAlignment())

0 commit comments

Comments
 (0)