diff --git a/clang/include/clang/Basic/Attr.td b/clang/include/clang/Basic/Attr.td --- a/clang/include/clang/Basic/Attr.td +++ b/clang/include/clang/Basic/Attr.td @@ -681,6 +681,13 @@ let Documentation = [Undocumented]; } +def AlignNatural : InheritableAttr { + // This attribute has no spellings as it is only ever created implicitly. + let Spellings = []; + let SemaHandler = 0; + let Documentation = [Undocumented]; +} + def AlwaysInline : InheritableAttr { let Spellings = [GCC<"always_inline">, Keyword<"__forceinline">]; let Subjects = SubjectList<[Function]>; diff --git a/clang/include/clang/Basic/DiagnosticSemaKinds.td b/clang/include/clang/Basic/DiagnosticSemaKinds.td --- a/clang/include/clang/Basic/DiagnosticSemaKinds.td +++ b/clang/include/clang/Basic/DiagnosticSemaKinds.td @@ -854,6 +854,8 @@ def warn_pragma_pack_invalid_alignment : Warning< "expected #pragma pack parameter to be '1', '2', '4', '8', or '16'">, InGroup; +def err_pragma_pack_invalid_alignment : Error< + warn_pragma_pack_invalid_alignment.Text>; def warn_pragma_pack_non_default_at_include : Warning< "non-default #pragma pack value changes the alignment of struct or union " "members in the included file">, InGroup, @@ -887,6 +889,8 @@ Warning<"ms_struct may not produce Microsoft-compatible layouts for classes " "with base classes or virtual functions">, DefaultError, InGroup; +def err_pragma_pack_identifer_not_supported : Error< + "specifying an identifier within `#pragma pack` is not supported on this target">; def err_section_conflict : Error<"%0 causes a section type conflict with %1">; def err_no_base_classes : Error<"invalid use of '__super', %0 has no base classes">; def err_invalid_super_scope : Error<"invalid use of '__super', " diff --git a/clang/include/clang/Basic/LangOptions.def b/clang/include/clang/Basic/LangOptions.def --- a/clang/include/clang/Basic/LangOptions.def +++ b/clang/include/clang/Basic/LangOptions.def @@ -354,6 +354,8 @@ LANGOPT(ApplePragmaPack, 1, 0, "Apple gcc-compatible #pragma pack handling") +LANGOPT(XLPragmaPack, 1, 0, "IBM XL #pragma pack handling") + LANGOPT(RetainCommentsFromSystemHeaders, 1, 0, "retain documentation comments from system headers in the AST") LANGOPT(SanitizeAddressFieldPadding, 2, 0, "controls how aggressive is ASan " diff --git a/clang/include/clang/Driver/Options.td b/clang/include/clang/Driver/Options.td --- a/clang/include/clang/Driver/Options.td +++ b/clang/include/clang/Driver/Options.td @@ -1026,6 +1026,8 @@ "LangOpts->ApplePragmaPack", DefaultsToFalse, ChangedBy, ResetBy>; +def fxl_pragma_pack : Flag<["-"], "fxl-pragma-pack">, Group, Flags<[CC1Option]>, + HelpText<"Enable IBM XL #pragma pack handling">; def shared_libsan : Flag<["-"], "shared-libsan">, HelpText<"Dynamically link the sanitizer runtime">; def static_libsan : Flag<["-"], "static-libsan">, @@ -1966,6 +1968,7 @@ def fmudflap : Flag<["-"], "fmudflap">, Group; def fnested_functions : Flag<["-"], "fnested-functions">, Group; def fnext_runtime : Flag<["-"], "fnext-runtime">, Group; +def fno_xl_pragma_pack : Flag<["-"], "fno-xl-pragma-pack">, Group; def fno_asm : Flag<["-"], "fno-asm">, Group; def fno_asynchronous_unwind_tables : Flag<["-"], "fno-asynchronous-unwind-tables">, Group; def fno_assume_sane_operator_new : Flag<["-"], "fno-assume-sane-operator-new">, Group, diff --git a/clang/include/clang/Sema/Sema.h b/clang/include/clang/Sema/Sema.h --- a/clang/include/clang/Sema/Sema.h +++ b/clang/include/clang/Sema/Sema.h @@ -473,6 +473,108 @@ PSK_Pop_Set = PSK_Pop | PSK_Set, // #pragma (pop[, id], value) }; + // #pragma pack and align. + class AlignPackInfo { + public: + // `Native` represents default align mode, which may vary based on the + // platform. + enum Mode : unsigned char { Native, Natural, Packed, Mac68k }; + + // #pragma pack info constructor + AlignPackInfo(AlignPackInfo::Mode M, unsigned Num, bool IsXL) + : PackAttr(true), AlignMode(M), PackNumber(Num), XLStack(IsXL) { + assert(Num == PackNumber && "The pack number has been truncated."); + } + + // #pragma align info constructor + AlignPackInfo(AlignPackInfo::Mode M, bool IsXL) + : PackAttr(false), AlignMode(M), + PackNumber(M == Packed ? 1 : UninitPackVal), XLStack(IsXL) {} + + explicit AlignPackInfo(bool IsXL) : AlignPackInfo(Native, IsXL) {} + + AlignPackInfo() : AlignPackInfo(Native, false) {} + + // When a AlignPackInfo itself cannot be used, this returns an 32-bit + // integer encoding for it. This should only be passed to + // AlignPackInfo::getFromRawEncoding, it should not be inspected directly. + static uint32_t getRawEncoding(const AlignPackInfo &Info) { + std::uint32_t Encoding{}; + if (Info.IsXLStack()) + Encoding |= IsXLMask; + + Encoding |= static_cast(Info.getAlignMode()) << 1; + + if (Info.IsPackAttr()) + Encoding |= PackAttrMask; + + Encoding |= static_cast(Info.getPackNumber()) << 4; + + return Encoding; + } + + static AlignPackInfo getFromRawEncoding(unsigned Encoding) { + bool IsXL = static_cast(Encoding & IsXLMask); + AlignPackInfo::Mode M = + static_cast((Encoding & AlignModeMask) >> 1); + int PackNumber = (Encoding & PackNumMask) >> 4; + + if (Encoding & PackAttrMask) + return AlignPackInfo(M, PackNumber, IsXL); + + return AlignPackInfo(M, IsXL); + } + + bool IsPackAttr() const { return PackAttr; } + + bool IsAlignAttr() const { return !PackAttr; } + + Mode getAlignMode() const { return AlignMode; } + + unsigned getPackNumber() const { return PackNumber; } + + bool IsPackSet() const { + // #pragma align, #pragma pack(), and #pragma pack(0) do not set the pack + // attriute on a decl. + return PackNumber != UninitPackVal && PackNumber != 0; + } + + bool IsXLStack() const { return XLStack; } + + bool operator==(const AlignPackInfo &Info) const { + return std::tie(AlignMode, PackNumber, PackAttr, XLStack) == + std::tie(Info.AlignMode, Info.PackNumber, Info.PackAttr, + Info.XLStack); + } + + bool operator!=(const AlignPackInfo &Info) const { + return !(*this == Info); + } + + private: + /// \brief True if this is a pragma pack attribute, + /// not a pragma align attribute. + bool PackAttr; + + /// \brief The alignment mode that is in effect. + Mode AlignMode; + + /// \brief The pack number of the stack. + unsigned char PackNumber; + + /// \brief True if it is a XL #pragma align/pack stack. + bool XLStack; + + /// \brief Uninitialized pack value. + static constexpr unsigned char UninitPackVal = -1; + + // Masks to encode and decode an AlignPackInfo. + static constexpr uint32_t IsXLMask{0x0000'0001}; + static constexpr uint32_t AlignModeMask{0x0000'0006}; + static constexpr uint32_t PackAttrMask{0x00000'0008}; + static constexpr uint32_t PackNumMask{0x0000'01F0}; + }; + template struct PragmaStack { struct Slot { @@ -565,13 +667,10 @@ /// 2: Always insert vtordisps to support RTTI on partially constructed /// objects PragmaStack VtorDispStack; - // #pragma pack. - // Sentinel to represent when the stack is set to mac68k alignment. - static const unsigned kMac68kAlignmentSentinel = ~0U; - PragmaStack AlignPackStack; + PragmaStack AlignPackStack; // The current #pragma align/pack values and locations at each #include. struct AlignPackIncludeState { - unsigned CurrentValue; + AlignPackInfo CurrentValue; SourceLocation CurrentPragmaLocation; bool HasNonDefaultValue, ShouldWarnOnInclude; }; @@ -12750,6 +12849,13 @@ /// The template function declaration to be late parsed. Decl *D; }; + +template <> +void Sema::PragmaStack::Act(SourceLocation PragmaLocation, + PragmaMsStackAction Action, + llvm::StringRef StackSlotLabel, + AlignPackInfo Value); + } // end namespace clang namespace llvm { diff --git a/clang/include/clang/Serialization/ASTReader.h b/clang/include/clang/Serialization/ASTReader.h --- a/clang/include/clang/Serialization/ASTReader.h +++ b/clang/include/clang/Serialization/ASTReader.h @@ -25,6 +25,7 @@ #include "clang/Lex/PreprocessingRecord.h" #include "clang/Sema/ExternalSemaSource.h" #include "clang/Sema/IdentifierResolver.h" +#include "clang/Sema/Sema.h" #include "clang/Serialization/ASTBitCodes.h" #include "clang/Serialization/ContinuousRangeMap.h" #include "clang/Serialization/ModuleFile.h" @@ -870,10 +871,10 @@ llvm::SmallVector FpPragmaStrings; /// The pragma align/pack state. - Optional PragmaAlignPackCurrentValue; + Optional PragmaAlignPackCurrentValue; SourceLocation PragmaAlignPackCurrentLocation; struct PragmaAlignPackStackEntry { - unsigned Value; + Sema::AlignPackInfo Value; SourceLocation Location; SourceLocation PushLocation; StringRef SlotLabel; @@ -2117,6 +2118,11 @@ /// Read the contents of a CXXCtorInitializer array. CXXCtorInitializer **GetExternalCXXCtorInitializers(uint64_t Offset) override; + /// Read a AlignPackInfo from raw form. + Sema::AlignPackInfo ReadAlignPackInfo(uint32_t Raw) const { + return Sema::AlignPackInfo::getFromRawEncoding(Raw); + } + /// Read a source location from raw form and return it in its /// originating module file's source location space. SourceLocation ReadUntranslatedSourceLocation(uint32_t Raw) const { diff --git a/clang/include/clang/Serialization/ASTWriter.h b/clang/include/clang/Serialization/ASTWriter.h --- a/clang/include/clang/Serialization/ASTWriter.h +++ b/clang/include/clang/Serialization/ASTWriter.h @@ -19,6 +19,7 @@ #include "clang/AST/Type.h" #include "clang/Basic/LLVM.h" #include "clang/Basic/SourceLocation.h" +#include "clang/Sema/Sema.h" #include "clang/Sema/SemaConsumer.h" #include "clang/Serialization/ASTBitCodes.h" #include "clang/Serialization/ASTDeserializationListener.h" @@ -589,6 +590,10 @@ /// Emit a token. void AddToken(const Token &Tok, RecordDataImpl &Record); + /// Emit a AlignPackInfo. + void AddAlignPackInfo(const Sema::AlignPackInfo &Info, + RecordDataImpl &Record); + /// Emit a source location. void AddSourceLocation(SourceLocation Loc, RecordDataImpl &Record); diff --git a/clang/lib/AST/RecordLayoutBuilder.cpp b/clang/lib/AST/RecordLayoutBuilder.cpp --- a/clang/lib/AST/RecordLayoutBuilder.cpp +++ b/clang/lib/AST/RecordLayoutBuilder.cpp @@ -615,6 +615,8 @@ unsigned IsMac68kAlign : 1; + unsigned IsNaturalAlign : 1; + unsigned IsMsStruct : 1; /// UnfilledBitsInLastUnit - If the last field laid out was a bitfield, @@ -693,7 +695,9 @@ UnpackedAlignment(CharUnits::One()), UnadjustedAlignment(CharUnits::One()), UseExternalLayout(false), InferAlignment(false), Packed(false), IsUnion(false), - IsMac68kAlign(false), IsMsStruct(false), UnfilledBitsInLastUnit(0), + IsMac68kAlign(false), + IsNaturalAlign(!Context.getTargetInfo().getTriple().isOSAIX()), + IsMsStruct(false), UnfilledBitsInLastUnit(0), LastBitfieldStorageUnitSize(0), MaxFieldAlignment(CharUnits::Zero()), DataSize(0), NonVirtualSize(CharUnits::Zero()), NonVirtualAlignment(CharUnits::One()), @@ -1243,7 +1247,7 @@ // By handling a base class that is not empty, we're handling the // "first (inherited) member". HandledFirstNonOverlappingEmptyField = true; - } else { + } else if (!IsNaturalAlign) { UnpackedPreferredBaseAlign = UnpackedBaseAlign; PreferredBaseAlign = BaseAlign; } @@ -1313,8 +1317,6 @@ } Packed = D->hasAttr(); - HandledFirstNonOverlappingEmptyField = - !Context.getTargetInfo().defaultsToAIXPowerAlignment(); // Honor the default struct packing maximum alignment flag. if (unsigned DefaultMaxFieldAlignment = Context.getLangOpts().PackStruct) { @@ -1326,11 +1328,17 @@ // allude to additional (more complicated) semantics, especially with regard // to bit-fields, but gcc appears not to follow that. if (D->hasAttr()) { + assert( + !D->hasAttr() && + "Having both mac68k and natural alignment on a decl is not allowed."); IsMac68kAlign = true; MaxFieldAlignment = CharUnits::fromQuantity(2); Alignment = CharUnits::fromQuantity(2); PreferredAlignment = CharUnits::fromQuantity(2); } else { + if (D->hasAttr()) + IsNaturalAlign = true; + if (const MaxFieldAlignmentAttr *MFAA = D->getAttr()) MaxFieldAlignment = Context.toCharUnitsFromBits(MFAA->getAlignment()); @@ -1338,6 +1346,9 @@ UpdateAlignment(Context.toCharUnitsFromBits(MaxAlign)); } + HandledFirstNonOverlappingEmptyField = + !Context.getTargetInfo().defaultsToAIXPowerAlignment() || IsNaturalAlign; + // If there is an external AST source, ask it for the various offsets. if (const RecordDecl *RD = dyn_cast(D)) if (ExternalASTSource *Source = Context.getExternalSource()) { @@ -1921,7 +1932,7 @@ // types marked `no_unique_address` are not considered to be prior members. CharUnits PreferredAlign = FieldAlign; if (DefaultsToAIXPowerAlignment && !AlignIsRequired && - FoundFirstNonOverlappingEmptyFieldForAIX) { + (FoundFirstNonOverlappingEmptyFieldForAIX || IsNaturalAlign)) { auto performBuiltinTypeAlignmentUpgrade = [&](const BuiltinType *BTy) { if (BTy->getKind() == BuiltinType::Double || BTy->getKind() == BuiltinType::LongDouble) { diff --git a/clang/lib/Driver/ToolChains/Clang.cpp b/clang/lib/Driver/ToolChains/Clang.cpp --- a/clang/lib/Driver/ToolChains/Clang.cpp +++ b/clang/lib/Driver/ToolChains/Clang.cpp @@ -6106,6 +6106,10 @@ options::OPT_fno_apple_pragma_pack, false)) CmdArgs.push_back("-fapple-pragma-pack"); + if (Args.hasFlag(options::OPT_fxl_pragma_pack, + options::OPT_fno_xl_pragma_pack, RawTriple.isOSAIX())) + CmdArgs.push_back("-fxl-pragma-pack"); + // Remarks can be enabled with any of the `-f.*optimization-record.*` flags. if (willEmitRemarks(Args) && checkRemarksOptions(D, Args, Triple)) renderRemarksOptions(Args, CmdArgs, Triple, Input, Output, JA); diff --git a/clang/lib/Frontend/CompilerInvocation.cpp b/clang/lib/Frontend/CompilerInvocation.cpp --- a/clang/lib/Frontend/CompilerInvocation.cpp +++ b/clang/lib/Frontend/CompilerInvocation.cpp @@ -2502,6 +2502,7 @@ || Args.hasArg(OPT_fdump_record_layouts); if (Opts.FastRelaxedMath) Opts.setDefaultFPContractMode(LangOptions::FPM_Fast); + Opts.XLPragmaPack = Args.hasArg(OPT_fxl_pragma_pack); llvm::sort(Opts.ModuleFeatures); Opts.NativeHalfType |= Args.hasArg(OPT_fnative_half_type); Opts.NativeHalfArgsAndReturns |= Args.hasArg(OPT_fnative_half_arguments_and_returns); diff --git a/clang/lib/Parse/ParsePragma.cpp b/clang/lib/Parse/ParsePragma.cpp --- a/clang/lib/Parse/ParsePragma.cpp +++ b/clang/lib/Parse/ParsePragma.cpp @@ -1803,9 +1803,10 @@ // In MSVC/gcc, #pragma pack(4) sets the alignment without affecting // the push/pop stack. - // In Apple gcc, #pragma pack(4) is equivalent to #pragma pack(push, 4) - Action = - PP.getLangOpts().ApplePragmaPack ? Sema::PSK_Push_Set : Sema::PSK_Set; + // In Apple gcc/XL, #pragma pack(4) is equivalent to #pragma pack(push, 4) + Action = (PP.getLangOpts().ApplePragmaPack || PP.getLangOpts().XLPragmaPack) + ? Sema::PSK_Push_Set + : Sema::PSK_Set; } else if (Tok.is(tok::identifier)) { const IdentifierInfo *II = Tok.getIdentifierInfo(); if (II->isStr("show")) { @@ -1853,10 +1854,12 @@ } } } - } else if (PP.getLangOpts().ApplePragmaPack) { + } else if (PP.getLangOpts().ApplePragmaPack || + PP.getLangOpts().XLPragmaPack) { // In MSVC/gcc, #pragma pack() resets the alignment without affecting // the push/pop stack. - // In Apple gcc #pragma pack() is equivalent to #pragma pack(pop). + // In Apple gcc and IBM XL, #pragma pack() is equivalent to #pragma + // pack(pop). Action = Sema::PSK_Pop; } @@ -1985,6 +1988,7 @@ // #pragma 'align' '=' {'native','natural','mac68k','power','reset'} // #pragma 'options 'align' '=' {'native','natural','mac68k','power','reset'} +// #pragma 'align' '(' {'native','natural','mac68k','power','reset'} ')' static void ParseAlignPragma(Preprocessor &PP, Token &FirstTok, bool IsOptions) { Token Tok; @@ -1999,7 +2003,12 @@ } PP.Lex(Tok); - if (Tok.isNot(tok::equal)) { + if (PP.getLangOpts().XLPragmaPack) { + if (Tok.isNot(tok::l_paren)) { + PP.Diag(Tok.getLocation(), diag::warn_pragma_expected_lparen) << "align"; + return; + } + } else if (Tok.isNot(tok::equal)) { PP.Diag(Tok.getLocation(), diag::warn_pragma_align_expected_equal) << IsOptions; return; @@ -2032,6 +2041,14 @@ return; } + if (PP.getLangOpts().XLPragmaPack) { + PP.Lex(Tok); + if (Tok.isNot(tok::r_paren)) { + PP.Diag(Tok.getLocation(), diag::warn_pragma_expected_rparen) << "align"; + return; + } + } + SourceLocation EndLoc = Tok.getLocation(); PP.Lex(Tok); if (Tok.isNot(tok::eod)) { diff --git a/clang/lib/Sema/Sema.cpp b/clang/lib/Sema/Sema.cpp --- a/clang/lib/Sema/Sema.cpp +++ b/clang/lib/Sema/Sema.cpp @@ -158,7 +158,8 @@ OriginalLexicalContext(nullptr), MSStructPragmaOn(false), MSPointerToMemberRepresentationMethod( LangOpts.getMSPointerToMemberRepresentationMethod()), - VtorDispStack(LangOpts.getVtorDispMode()), AlignPackStack(0), + VtorDispStack(LangOpts.getVtorDispMode()), + AlignPackStack(AlignPackInfo(getLangOpts().XLPragmaPack)), DataSegStack(nullptr), BSSSegStack(nullptr), ConstSegStack(nullptr), CodeSegStack(nullptr), FpPragmaStack(FPOptionsOverride()), CurInitSeg(nullptr), VisContext(nullptr), diff --git a/clang/lib/Sema/SemaAttr.cpp b/clang/lib/Sema/SemaAttr.cpp --- a/clang/lib/Sema/SemaAttr.cpp +++ b/clang/lib/Sema/SemaAttr.cpp @@ -48,22 +48,31 @@ } void Sema::AddAlignmentAttributesForRecord(RecordDecl *RD) { - // If there is no pack value, we don't need any attributes. - if (!AlignPackStack.CurrentValue) + AlignPackInfo InfoVal = AlignPackStack.CurrentValue; + AlignPackInfo::Mode M = InfoVal.getAlignMode(); + bool IsPackSet = InfoVal.IsPackSet(); + bool IsXLPragma = getLangOpts().XLPragmaPack; + + // If we are not under mac68k/natural alignment mode and also there is no pack + // value, we don't need any attributes. + if (!IsPackSet && M != AlignPackInfo::Mac68k && M != AlignPackInfo::Natural) return; - // Otherwise, check to see if we need a max field alignment attribute. - if (unsigned Alignment = AlignPackStack.CurrentValue) { - if (Alignment == Sema::kMac68kAlignmentSentinel) - RD->addAttr(AlignMac68kAttr::CreateImplicit(Context)); - else - RD->addAttr(MaxFieldAlignmentAttr::CreateImplicit(Context, - Alignment * 8)); + if (M == AlignPackInfo::Mac68k && (IsXLPragma || InfoVal.IsAlignAttr())) { + RD->addAttr(AlignMac68kAttr::CreateImplicit(Context)); + } else if (IsPackSet) { + // Check to see if we need a max field alignment attribute. + RD->addAttr(MaxFieldAlignmentAttr::CreateImplicit( + Context, InfoVal.getPackNumber() * 8)); } + + if (IsXLPragma && M == AlignPackInfo::Natural) + RD->addAttr(AlignNaturalAttr::CreateImplicit(Context)); + if (AlignPackIncludeStack.empty()) return; // The #pragma align/pack affected a record in an included file, so Clang - // should warn when that the pragma was written in a file that included the + // should warn when that pragma was written in a file that included the // included file. for (auto &AlignPackedInclude : llvm::reverse(AlignPackIncludeStack)) { if (AlignPackedInclude.CurrentPragmaLocation != @@ -206,23 +215,27 @@ void Sema::ActOnPragmaOptionsAlign(PragmaOptionsAlignKind Kind, SourceLocation PragmaLoc) { PragmaMsStackAction Action = Sema::PSK_Reset; - unsigned Alignment = 0; + AlignPackInfo::Mode ModeVal = AlignPackInfo::Native; + switch (Kind) { - // For all targets we support native and natural are the same. + // For most of the platforms we support, native and natural are the same. + // With XL, native is the same as power, natural means something else. // // FIXME: This is not true on Darwin/PPC. case POAK_Native: case POAK_Power: + Action = Sema::PSK_Push_Set; + break; case POAK_Natural: Action = Sema::PSK_Push_Set; - Alignment = 0; + ModeVal = AlignPackInfo::Natural; break; // Note that '#pragma options align=packed' is not equivalent to attribute // packed, it has a different precedence relative to attribute aligned. case POAK_Packed: Action = Sema::PSK_Push_Set; - Alignment = 1; + ModeVal = AlignPackInfo::Packed; break; case POAK_Mac68k: @@ -232,15 +245,15 @@ return; } Action = Sema::PSK_Push_Set; - Alignment = Sema::kMac68kAlignmentSentinel; + ModeVal = AlignPackInfo::Mac68k; break; - case POAK_Reset: // Reset just pops the top of the stack, or resets the current alignment to // default. Action = Sema::PSK_Pop; if (AlignPackStack.Stack.empty()) { - if (AlignPackStack.CurrentValue) { + if (AlignPackStack.CurrentValue.getAlignMode() != AlignPackInfo::Native || + AlignPackStack.CurrentValue.IsPackAttr()) { Action = Sema::PSK_Reset; } else { Diag(PragmaLoc, diag::warn_pragma_options_align_reset_failed) @@ -251,7 +264,9 @@ break; } - AlignPackStack.Act(PragmaLoc, Action, StringRef(), Alignment); + AlignPackInfo Info(ModeVal, getLangOpts().XLPragmaPack); + + AlignPackStack.Act(PragmaLoc, Action, StringRef(), Info); } void Sema::ActOnPragmaClangSection(SourceLocation PragmaLoc, PragmaClangSectionAction Action, @@ -296,46 +311,68 @@ void Sema::ActOnPragmaPack(SourceLocation PragmaLoc, PragmaMsStackAction Action, StringRef SlotLabel, Expr *alignment) { + bool IsXLPragma = getLangOpts().XLPragmaPack; + // XL pragma pack does not support identifier syntax. + if (IsXLPragma && !SlotLabel.empty()) { + Diag(PragmaLoc, diag::err_pragma_pack_identifer_not_supported); + return; + } + + const AlignPackInfo CurVal = AlignPackStack.CurrentValue; Expr *Alignment = static_cast(alignment); // If specified then alignment must be a "small" power of two. unsigned AlignmentVal = 0; + AlignPackInfo::Mode ModeVal = CurVal.getAlignMode(); + if (Alignment) { Optional Val; + Val = Alignment->getIntegerConstantExpr(Context); // pack(0) is like pack(), which just works out since that is what // we use 0 for in PackAttr. - if (Alignment->isTypeDependent() || Alignment->isValueDependent() || - !(Val = Alignment->getIntegerConstantExpr(Context)) || + if (Alignment->isTypeDependent() || Alignment->isValueDependent() || !Val || !(*Val == 0 || Val->isPowerOf2()) || Val->getZExtValue() > 16) { Diag(PragmaLoc, diag::warn_pragma_pack_invalid_alignment); return; // Ignore } + if (IsXLPragma && *Val == 0) { + // pack(0) does not work out with XL. + Diag(PragmaLoc, diag::err_pragma_pack_invalid_alignment); + return; // Ignore + } + AlignmentVal = (unsigned)Val->getZExtValue(); } + if (Action == Sema::PSK_Show) { // Show the current alignment, making sure to show the right value // for the default. // FIXME: This should come from the target. - AlignmentVal = AlignPackStack.CurrentValue; - if (AlignmentVal == 0) - AlignmentVal = 8; - if (AlignmentVal == Sema::kMac68kAlignmentSentinel) + AlignmentVal = CurVal.IsPackSet() ? CurVal.getPackNumber() : 8; + if (ModeVal == AlignPackInfo::Mac68k && + (IsXLPragma || CurVal.IsAlignAttr())) Diag(PragmaLoc, diag::warn_pragma_pack_show) << "mac68k"; else Diag(PragmaLoc, diag::warn_pragma_pack_show) << AlignmentVal; } + // MSDN, C/C++ Preprocessor Reference > Pragma Directives > pack: // "#pragma pack(pop, identifier, n) is undefined" if (Action & Sema::PSK_Pop) { if (Alignment && !SlotLabel.empty()) Diag(PragmaLoc, diag::warn_pragma_pack_pop_identifier_and_alignment); - if (AlignPackStack.Stack.empty()) + if (AlignPackStack.Stack.empty()) { + assert(CurVal.getAlignMode() == AlignPackInfo::Native && + "Empty pack stack can only be at Native alignment mode."); Diag(PragmaLoc, diag::warn_pragma_pop_failed) << "pack" << "stack empty"; + } } - AlignPackStack.Act(PragmaLoc, Action, SlotLabel, AlignmentVal); + AlignPackInfo Info(ModeVal, AlignmentVal, IsXLPragma); + + AlignPackStack.Act(PragmaLoc, Action, SlotLabel, Info); } void Sema::DiagnoseNonDefaultPragmaAlignPack(PragmaAlignPackDiagnoseKind Kind, @@ -493,6 +530,68 @@ VtorDispStack.Act(PragmaLoc, Action, StringRef(), Mode); } +template <> +void Sema::PragmaStack::Act(SourceLocation PragmaLocation, + PragmaMsStackAction Action, + llvm::StringRef StackSlotLabel, + AlignPackInfo Value) { + if (Action == PSK_Reset) { + CurrentValue = DefaultValue; + CurrentPragmaLocation = PragmaLocation; + return; + } + if (Action & PSK_Push) + Stack.emplace_back(Slot(StackSlotLabel, CurrentValue, CurrentPragmaLocation, + PragmaLocation)); + else if (Action & PSK_Pop) { + if (!StackSlotLabel.empty()) { + // If we've got a label, try to find it and jump there. + auto I = llvm::find_if(llvm::reverse(Stack), [&](const Slot &x) { + return x.StackSlotLabel == StackSlotLabel; + }); + // We found the label, so pop from there. + if (I != Stack.rend()) { + CurrentValue = I->Value; + CurrentPragmaLocation = I->PragmaLocation; + Stack.erase(std::prev(I.base()), Stack.end()); + } + } else if (Value.IsXLStack() && Value.IsAlignAttr() && + CurrentValue.IsPackAttr()) { + // XL '#pragma align(reset)' would pop the stack until + // a current in effect pragma align is popped. + auto I = llvm::find_if(llvm::reverse(Stack), [&](const Slot &x) { + return x.Value.IsAlignAttr(); + }); + // If we found pragma align so pop from there. + if (I != Stack.rend()) { + Stack.erase(std::prev(I.base()), Stack.end()); + if (Stack.empty()) { + CurrentValue = DefaultValue; + CurrentPragmaLocation = PragmaLocation; + } else { + CurrentValue = Stack.back().Value; + CurrentPragmaLocation = Stack.back().PragmaLocation; + Stack.pop_back(); + } + } + } else if (!Stack.empty()) { + // xl '#pragma align' sets the baseline, and `#pragma pack` cannot pop + // over the baseline. + if (Value.IsXLStack() && Value.IsPackAttr() && CurrentValue.IsAlignAttr()) + return; + + // We don't have a label, just pop the last entry. + CurrentValue = Stack.back().Value; + CurrentPragmaLocation = Stack.back().PragmaLocation; + Stack.pop_back(); + } + } + if (Action & PSK_Set) { + CurrentValue = Value; + CurrentPragmaLocation = PragmaLocation; + } +} + bool Sema::UnifySection(StringRef SectionName, int SectionFlags, NamedDecl *Decl) { SourceLocation PragmaLocation; diff --git a/clang/lib/Serialization/ASTReader.cpp b/clang/lib/Serialization/ASTReader.cpp --- a/clang/lib/Serialization/ASTReader.cpp +++ b/clang/lib/Serialization/ASTReader.cpp @@ -3747,7 +3747,7 @@ Error("invalid pragma pack record"); return Failure; } - PragmaAlignPackCurrentValue = Record[0]; + PragmaAlignPackCurrentValue = ReadAlignPackInfo(Record[0]); PragmaAlignPackCurrentLocation = ReadSourceLocation(F, Record[1]); unsigned NumStackEntries = Record[2]; unsigned Idx = 3; @@ -3755,7 +3755,7 @@ PragmaAlignPackStack.clear(); for (unsigned I = 0; I < NumStackEntries; ++I) { PragmaAlignPackStackEntry Entry; - Entry.Value = Record[Idx++]; + Entry.Value = ReadAlignPackInfo(Record[Idx++]); Entry.Location = ReadSourceLocation(F, Record[Idx++]); Entry.PushLocation = ReadSourceLocation(F, Record[Idx++]); PragmaAlignPackStrings.push_back(ReadString(Record, Idx)); @@ -7892,14 +7892,15 @@ PragmaAlignPackStack.front().PushLocation); DropFirst = true; } - for (const auto &Entry : - llvm::makeArrayRef(PragmaAlignPackStack).drop_front(DropFirst ? 1 : 0)) + for (const auto &Entry : llvm::makeArrayRef(PragmaAlignPackStack) + .drop_front(DropFirst ? 1 : 0)) { SemaObj->AlignPackStack.Stack.emplace_back( Entry.SlotLabel, Entry.Value, Entry.Location, Entry.PushLocation); + } if (PragmaAlignPackCurrentLocation.isInvalid()) { assert(*PragmaAlignPackCurrentValue == SemaObj->AlignPackStack.DefaultValue && - "Expected a default alignment value"); + "Expected a default align and pack value"); // Keep the current values. } else { SemaObj->AlignPackStack.CurrentValue = *PragmaAlignPackCurrentValue; diff --git a/clang/lib/Serialization/ASTWriter.cpp b/clang/lib/Serialization/ASTWriter.cpp --- a/clang/lib/Serialization/ASTWriter.cpp +++ b/clang/lib/Serialization/ASTWriter.cpp @@ -4159,11 +4159,11 @@ return; RecordData Record; - Record.push_back(SemaRef.AlignPackStack.CurrentValue); + AddAlignPackInfo(SemaRef.AlignPackStack.CurrentValue, Record); AddSourceLocation(SemaRef.AlignPackStack.CurrentPragmaLocation, Record); Record.push_back(SemaRef.AlignPackStack.Stack.size()); for (const auto &StackEntry : SemaRef.AlignPackStack.Stack) { - Record.push_back(StackEntry.Value); + AddAlignPackInfo(StackEntry.Value, Record); AddSourceLocation(StackEntry.PragmaLocation, Record); AddSourceLocation(StackEntry.PragmaPushLocation, Record); AddString(StackEntry.StackSlotLabel, Record); @@ -5107,6 +5107,12 @@ } } +void ASTWriter::AddAlignPackInfo(const Sema::AlignPackInfo &Info, + RecordDataImpl &Record) { + uint32_t Raw = Sema::AlignPackInfo::getRawEncoding(Info); + Record.push_back(Raw); +} + void ASTWriter::AddSourceLocation(SourceLocation Loc, RecordDataImpl &Record) { uint32_t Raw = Loc.getRawEncoding(); Record.push_back((Raw << 1) | (Raw >> 31)); diff --git a/clang/test/Driver/aix-pragma-pack.c b/clang/test/Driver/aix-pragma-pack.c new file mode 100644 --- /dev/null +++ b/clang/test/Driver/aix-pragma-pack.c @@ -0,0 +1,4 @@ +// RUN: %clang -### -target powerpc-ibm-aix-xcoff -c %s -o /dev/null 2>&1 | FileCheck %s +// RUN: %clang -### -target powerpc64-ibm-aix-xcoff -c %s -o /dev/null 2>&1 | FileCheck %s + +// CHECK: "-fxl-pragma-pack" diff --git a/clang/test/Layout/aix-double-struct-member.cpp b/clang/test/Layout/aix-double-struct-member.cpp --- a/clang/test/Layout/aix-double-struct-member.cpp +++ b/clang/test/Layout/aix-double-struct-member.cpp @@ -1,9 +1,9 @@ // RUN: %clang_cc1 -triple powerpc-ibm-aix-xcoff -fdump-record-layouts \ -// RUN: -fsyntax-only %s | \ +// RUN: -fsyntax-only -fxl-pragma-pack %s | \ // RUN: FileCheck %s // RUN: %clang_cc1 -triple powerpc64-ibm-aix-xcoff -fdump-record-layouts \ -// RUN: -fsyntax-only %s | \ +// RUN: -fsyntax-only -fxl-pragma-pack %s | \ // RUN: FileCheck %s namespace test1 { @@ -335,6 +335,7 @@ short j; double k; }; +#pragma pack(pop) #pragma pack(2) struct D { @@ -342,22 +343,26 @@ short j; int i; }; +#pragma pack(pop) #pragma pack(8) struct E { double __attribute__((aligned(4))) d; short s; }; +#pragma pack(pop) #pragma pack(4) struct F : public D { double d; }; +#pragma pack(pop) #pragma pack(2) struct G : public E { int i; }; +#pragma pack(pop) int a = sizeof(A); int b = sizeof(B); diff --git a/clang/test/Layout/aix-power-natural-interaction.cpp b/clang/test/Layout/aix-power-natural-interaction.cpp new file mode 100644 --- /dev/null +++ b/clang/test/Layout/aix-power-natural-interaction.cpp @@ -0,0 +1,146 @@ +// RUN: %clang_cc1 -triple powerpc-ibm-aix-xcoff -fdump-record-layouts \ +// RUN: -fxl-pragma-pack -fsyntax-only %s | \ +// RUN: FileCheck %s + +// RUN: %clang_cc1 -triple powerpc64-ibm-aix-xcoff -fdump-record-layouts \ +// RUN: -fxl-pragma-pack -fsyntax-only %s | \ +// RUN: FileCheck %s + +namespace test1 { +#pragma align(natural) +struct A { + int i1; +}; + +struct B { + double d1; +}; +#pragma align(reset) + +struct C : A, B { + double d2; +}; + +int a = sizeof(C); + +// CHECK: *** Dumping AST Record Layout +// CHECK-NEXT: 0 | struct test1::A +// CHECK-NEXT: 0 | int i1 +// CHECK-NEXT: | [sizeof=4, dsize=4, align=4, preferredalign=4, +// CHECK-NEXT: | nvsize=4, nvalign=4, preferrednvalign=4] + +// CHECK: *** Dumping AST Record Layout +// CHECK-NEXT: 0 | struct test1::B +// CHECK-NEXT: 0 | double d1 +// CHECK-NEXT: | [sizeof=8, dsize=8, align=4, preferredalign=8, +// CHECK-NEXT: | nvsize=8, nvalign=4, preferrednvalign=8] + +// CHECK: *** Dumping AST Record Layout +// CHECK-NEXT: 0 | struct test1::C +// CHECK-NEXT: 0 | struct test1::A (base) +// CHECK-NEXT: 0 | int i1 +// CHECK-NEXT: 4 | struct test1::B (base) +// CHECK-NEXT: 4 | double d1 +// CHECK-NEXT: 12 | double d2 +// CHECK-NEXT: | [sizeof=20, dsize=20, align=4, preferredalign=4, +// CHECK-NEXT: | nvsize=20, nvalign=4, preferrednvalign=4] + +} // namespace test1 + +namespace test2 { +struct A { + int i1; + double d; +}; + +#pragma align(natural) +struct B : A { + int i2; +}; +#pragma align(reset) + +int b = sizeof(B); + +// CHECK: *** Dumping AST Record Layout +// CHECK-NEXT: 0 | struct test2::A +// CHECK-NEXT: 0 | int i1 +// CHECK-NEXT: 4 | double d +// CHECK-NEXT: | [sizeof=12, dsize=12, align=4, preferredalign=4, +// CHECK-NEXT: | nvsize=12, nvalign=4, preferrednvalign=4] + +// CHECK: *** Dumping AST Record Layout +// CHECK-NEXT: 0 | struct test2::B +// CHECK-NEXT: 0 | struct test2::A (base) +// CHECK-NEXT: 0 | int i1 +// CHECK-NEXT: 4 | double d +// CHECK-NEXT: 12 | int i2 +// CHECK-NEXT: | [sizeof=16, dsize=16, align=4, preferredalign=4, +// CHECK-NEXT: | nvsize=16, nvalign=4, preferrednvalign=4] + +} // namespace test2 + +namespace test3 { +#pragma align(natural) +struct A { + int i1; + double d; +}; +#pragma align(reset) + +struct B { + struct A a; + int i2; +}; + +int c = sizeof(B); + +// CHECK: *** Dumping AST Record Layout +// CHECK-NEXT: 0 | struct test3::A +// CHECK-NEXT: 0 | int i1 +// CHECK-NEXT: 8 | double d +// CHECK-NEXT: | [sizeof=16, dsize=16, align=4, preferredalign=8, +// CHECK-NEXT: | nvsize=16, nvalign=4, preferrednvalign=8] + +// CHECK: *** Dumping AST Record Layout +// CHECK-NEXT: 0 | struct test3::B +// CHECK-NEXT: 0 | struct test3::A a +// CHECK-NEXT: 0 | int i1 +// CHECK-NEXT: 8 | double d +// CHECK-NEXT: 16 | int i2 +// CHECK-NEXT: | [sizeof=24, dsize=24, align=4, preferredalign=8, +// CHECK-NEXT: | nvsize=24, nvalign=4, preferrednvalign=8] + +} // namespace test3 + +namespace test4 { +struct A { + int i1; + double d; +}; + +#pragma align(natural) +struct B { + int i2; + struct A a; +}; +#pragma align(reset) + +int d = sizeof(B); + +// CHECK: *** Dumping AST Record Layout +// CHECK-NEXT: 0 | struct test4::A +// CHECK-NEXT: 0 | int i1 +// CHECK-NEXT: 4 | double d +// CHECK-NEXT: | [sizeof=12, dsize=12, align=4, preferredalign=4, +// CHECK-NEXT: | nvsize=12, nvalign=4, preferrednvalign=4] + +// CHECK: *** Dumping AST Record Layout +// CHECK-NEXT: 0 | struct test4::B +// CHECK-NEXT: 0 | int i2 +// CHECK-NEXT: 4 | struct test4::A a +// CHECK-NEXT: 4 | int i1 +// CHECK-NEXT: 8 | double d +// CHECK-NEXT: | [sizeof=16, dsize=16, align=4, preferredalign=4, +// CHECK-NEXT: | nvsize=16, nvalign=4, preferrednvalign=4] + +} // namespace test4 diff --git a/clang/test/PCH/aix-pragma-pack.c b/clang/test/PCH/aix-pragma-pack.c new file mode 100644 --- /dev/null +++ b/clang/test/PCH/aix-pragma-pack.c @@ -0,0 +1,119 @@ +// Test this without pch. +// RUN: %clang_cc1 -triple powerpc-unknown-aix -fxl-pragma-pack %s -include %s -verify -fsyntax-only -Wno-pragma-pack -DSET +// RUN: %clang_cc1 -triple powerpc-unknown-aix -fxl-pragma-pack %s -include %s -verify -fsyntax-only -Wno-pragma-pack -DRESET +// RUN: %clang_cc1 -triple powerpc-unknown-aix -fxl-pragma-pack %s -include %s -verify -fsyntax-only -Wno-pragma-pack -DPUSH +// RUN: %clang_cc1 -triple powerpc-unknown-aix -fxl-pragma-pack %s -include %s -verify -fsyntax-only \ +// RUN: -Wno-pragma-pack -DPUSH_POP +// RUN: %clang_cc1 -triple powerpc-unknown-aix -fxl-pragma-pack %s -include %s -fsyntax-only -fdump-record-layouts \ +// RUN: -Wno-pragma-pack -DALIGN_NATURAL | \ +// RUN: FileCheck %s + +// Test with pch. +// RUN: %clang_cc1 -triple powerpc-unknown-aix -fxl-pragma-pack %s -Wno-pragma-pack -DSET -emit-pch -o %t +// RUN: %clang_cc1 -triple powerpc-unknown-aix -fxl-pragma-pack %s -Wno-pragma-pack -DSET -verify -include-pch %t +// RUN: %clang_cc1 -triple powerpc-unknown-aix -fxl-pragma-pack %s -Wno-pragma-pack -DRESET -emit-pch -o %t +// RUN: %clang_cc1 -triple powerpc-unknown-aix -fxl-pragma-pack %s -Wno-pragma-pack -DRESET -verify -include-pch %t +// RUN: %clang_cc1 -triple powerpc-unknown-aix -fxl-pragma-pack %s -Wno-pragma-pack -DPUSH -emit-pch -o %t +// RUN: %clang_cc1 -triple powerpc-unknown-aix -fxl-pragma-pack %s -Wno-pragma-pack -DPUSH -verify -include-pch %t +// RUN: %clang_cc1 -triple powerpc-unknown-aix -fxl-pragma-pack %s -Wno-pragma-pack -DPUSH_POP -emit-pch -o %t +// RUN: %clang_cc1 -triple powerpc-unknown-aix -fxl-pragma-pack %s -Wno-pragma-pack -DPUSH_POP -verify -include-pch %t +// RUN: %clang_cc1 -triple powerpc-unknown-aix -fxl-pragma-pack %s -Wno-pragma-pack -DALIGN_NATURAL -emit-pch -o %t +// RUN: %clang_cc1 -triple powerpc-unknown-aix -fxl-pragma-pack %s -Wno-pragma-pack -DALIGN_NATURAL \ +// RUN: -fdump-record-layouts -include-pch %t | \ +// RUN: FileCheck %s + +// Test this without pch. +// RUN: %clang_cc1 -triple powerpc64-unknown-aix -fxl-pragma-pack %s -include %s -verify -fsyntax-only -Wno-pragma-pack -DSET +// RUN: %clang_cc1 -triple powerpc64-unknown-aix -fxl-pragma-pack %s -include %s -verify -fsyntax-only -Wno-pragma-pack -DRESET +// RUN: %clang_cc1 -triple powerpc64-unknown-aix -fxl-pragma-pack %s -include %s -verify -fsyntax-only -Wno-pragma-pack -DPUSH +// RUN: %clang_cc1 -triple powerpc64-unknown-aix -fxl-pragma-pack %s -include %s -verify -fsyntax-only \ +// RUN: -Wno-pragma-pack -DPUSH_POP +// RUN: %clang_cc1 -triple powerpc64-unknown-aix -fxl-pragma-pack %s -include %s -fsyntax-only -fdump-record-layouts \ +// RUN: -Wno-pragma-pack -DALIGN_NATURAL + +// Test with pch. +// RUN: %clang_cc1 -triple powerpc64-unknown-aix -fxl-pragma-pack %s -Wno-pragma-pack -DSET -emit-pch -o %t +// RUN: %clang_cc1 -triple powerpc64-unknown-aix -fxl-pragma-pack %s -Wno-pragma-pack -DSET -verify -include-pch %t +// RUN: %clang_cc1 -triple powerpc64-unknown-aix -fxl-pragma-pack %s -Wno-pragma-pack -DRESET -emit-pch -o %t +// RUN: %clang_cc1 -triple powerpc64-unknown-aix -fxl-pragma-pack %s -Wno-pragma-pack -DRESET -verify -include-pch %t +// RUN: %clang_cc1 -triple powerpc64-unknown-aix -fxl-pragma-pack %s -Wno-pragma-pack -DPUSH -emit-pch -o %t +// RUN: %clang_cc1 -triple powerpc64-unknown-aix -fxl-pragma-pack %s -Wno-pragma-pack -DPUSH -verify -include-pch %t +// RUN: %clang_cc1 -triple powerpc64-unknown-aix -fxl-pragma-pack %s -Wno-pragma-pack -DPUSH_POP -emit-pch -o %t +// RUN: %clang_cc1 -triple powerpc64-unknown-aix -fxl-pragma-pack %s -Wno-pragma-pack -DPUSH_POP -verify -include-pch %t +// RUN: %clang_cc1 -triple powerpc64-unknown-aix -fxl-pragma-pack %s -Wno-pragma-pack -DALIGN_NATURAL -emit-pch -o %t +// RUN: %clang_cc1 -triple powerpc64-unknown-aix -fxl-pragma-pack %s -Wno-pragma-pack -DALIGN_NATURAL \ +// RUN: -fdump-record-layouts -include-pch %t | \ +// RUN: FileCheck %s + +#ifndef HEADER +#define HEADER + +#ifdef SET +#pragma pack(1) +#endif + +#ifdef RESET +#pragma pack(2) +#pragma pack() +#endif + +#ifdef PUSH +#pragma pack(1) +#pragma pack(push, 2) +#endif + +#ifdef PUSH_POP +#pragma pack(push, 4) +#pragma pack(push, 2) +#pragma pack(pop) +#endif + +#ifdef ALIGN_NATURAL +#pragma align(natural) +#endif + +#else + +#ifdef SET +#pragma pack(show) // expected-warning {{value of #pragma pack(show) == 1}} +#pragma pack(pop) +#endif + +#ifdef RESET +#pragma pack(show) // expected-warning {{value of #pragma pack(show) == 8}} +#pragma() +#pragma pack(show) // expected-warning {{value of #pragma pack(show) == 8}} +#endif + +#ifdef PUSH +#pragma pack(show) // expected-warning {{value of #pragma pack(show) == 2}} +#pragma pack(pop) +#pragma pack(show) // expected-warning {{value of #pragma pack(show) == 1}} +#pragma pack() +#pragma pack(show) // expected-warning {{value of #pragma pack(show) == 8}} +#pragma pack(pop) // expected-warning {{#pragma pack(pop, ...) failed: stack empty}} +#endif + +#ifdef PUSH_POP +#pragma pack(show) // expected-warning {{value of #pragma pack(show) == 4}} +#pragma pack(pop) +#pragma pack(show) // expected-warning {{value of #pragma pack(show) == 8}} +#pragma pack(pop) // expected-warning {{#pragma pack(pop, ...) failed: stack empty}} +#endif + +#ifdef ALIGN_NATURAL +struct D { + int i; + double d; +} d; + +int s = sizeof(d); + +// CHECK: *** Dumping AST Record Layout +// CHECK: 0 | struct D +// CHECK: 0 | int i +// CHECK: 8 | double d +// CHECK: | [sizeof=16, align=4, preferredalign=8] +#endif + +#endif diff --git a/clang/test/Sema/aix-pragma-pack-and-align.c b/clang/test/Sema/aix-pragma-pack-and-align.c new file mode 100644 --- /dev/null +++ b/clang/test/Sema/aix-pragma-pack-and-align.c @@ -0,0 +1,229 @@ +// RUN: %clang_cc1 -triple powerpc-ibm-aix-xcoff -fdump-record-layouts \ +// RUN: -fxl-pragma-pack -verify -fsyntax-only -x c++ %s | \ +// RUN: FileCheck %s + +// RUN: %clang_cc1 -triple powerpc64-ibm-aix-xcoff -fdump-record-layouts \ +// RUN: -fxl-pragma-pack -verify -fsyntax-only -x c++ %s | \ +// RUN: FileCheck %s + +namespace test1 { +#pragma align(natural) +#pragma pack(4) +#pragma pack(2) +struct A { + int i; + double d; +}; + +int a = sizeof(A); +#pragma pack() +#pragma pack(show) // expected-warning {{value of #pragma pack(show) == 4}} +#pragma pack(pop) +#pragma pack(show) // expected-warning {{value of #pragma pack(show) == 8}} +struct B { + int i; + double d; +}; +#pragma align(reset) + +int b = sizeof(B); + +// CHECK: *** Dumping AST Record Layout +// CHECK-NEXT: 0 | struct test1::A +// CHECK-NEXT: 0 | int i +// CHECK-NEXT: 4 | double d +// CHECK-NEXT: | [sizeof=12, dsize=12, align=2, preferredalign=2, +// CHECK-NEXT: | nvsize=12, nvalign=2, preferrednvalign=2] + +// CHECK: *** Dumping AST Record Layout +// CHECK-NEXT: 0 | struct test1::B +// CHECK-NEXT: 0 | int i +// CHECK-NEXT: 8 | double d +// CHECK-NEXT: | [sizeof=16, dsize=16, align=4, preferredalign=8, +// CHECK-NEXT: | nvsize=16, nvalign=4, preferrednvalign=8] + +} // namespace test1 + +namespace test2 { +#pragma align(natural) +#pragma pack(2) +struct A { + int i; + double d; +}; + +int a = sizeof(A); +#pragma align(reset) + +struct B { + int i; + double d; +}; + +int b = sizeof(B); + +// CHECK: *** Dumping AST Record Layout +// CHECK-NEXT: 0 | struct test2::A +// CHECK-NEXT: 0 | int i +// CHECK-NEXT: 4 | double d +// CHECK-NEXT: | [sizeof=12, dsize=12, align=2, preferredalign=2, +// CHECK-NEXT: | nvsize=12, nvalign=2, preferrednvalign=2] + +// CHECK: *** Dumping AST Record Layout +// CHECK-NEXT: 0 | struct test2::B +// CHECK-NEXT: 0 | int i +// CHECK-NEXT: 4 | double d +// CHECK-NEXT: | [sizeof=12, dsize=12, align=4, preferredalign=4, +// CHECK-NEXT: | nvsize=12, nvalign=4, preferrednvalign=4] + +} // namespace test2 + +namespace test3 { +#pragma pack(2) +#pragma align(natural) +struct A { + double d; +}; +#pragma align(reset) +#pragma pack(pop) + +int a = sizeof(A); + +// CHECK: *** Dumping AST Record Layout +// CHECK-NEXT: 0 | struct test3::A +// CHECK-NEXT: 0 | double d +// CHECK-NEXT: | [sizeof=8, dsize=8, align=4, preferredalign=8, +// CHECK-NEXT: | nvsize=8, nvalign=4, preferrednvalign=8] + +} // namespace test3 + +namespace test4 { +#pragma pack(2) +#pragma align(natural) +#pragma pack(pop) + +struct A { + int i; + double d; +} a; +#pragma align(reset) +#pragma pack(pop) + +int i = sizeof(A); + +// CHECK: *** Dumping AST Record Layout +// CHECK-NEXT: 0 | struct test4::A +// CHECK-NEXT: 0 | int i +// CHECK-NEXT: 8 | double d +// CHECK-NEXT: | [sizeof=16, dsize=16, align=4, preferredalign=8, +// CHECK-NEXT: | nvsize=16, nvalign=4, preferrednvalign=8] + +} // namespace test4 + +namespace test5 { +#pragma align(power) +#pragma align(natural) +#pragma pack(2) +#pragma align(reset) +struct A { + int i; + double d; +}; +#pragma align(reset) + +int a = sizeof(A); + +// CHECK: *** Dumping AST Record Layout +// CHECK-NEXT: 0 | struct test5::A +// CHECK-NEXT: 0 | int i +// CHECK-NEXT: 4 | double d +// CHECK-NEXT: | [sizeof=12, dsize=12, align=4, preferredalign=4, +// CHECK-NEXT: | nvsize=12, nvalign=4, preferrednvalign=4] + +} // namespace test5 + +namespace test6 { +#pragma align(natural) +#pragma pack(0) // expected-error {{expected #pragma pack parameter to be '1', '2', '4', '8', or '16'}} +#pragma pack(show) // expected-warning {{value of #pragma pack(show) == 8}} + +struct A { + int i; + double d; +} a; +#pragma align(reset) + +int i = sizeof(a); + +// CHECK: *** Dumping AST Record Layout +// CHECK-NEXT: 0 | struct test6::A +// CHECK-NEXT: 0 | int i +// CHECK-NEXT: 8 | double d +// CHECK-NEXT: | [sizeof=16, dsize=16, align=4, preferredalign=8, +// CHECK-NEXT: | nvsize=16, nvalign=4, preferrednvalign=8] + +} // namespace test6 + +namespace test7 { +#pragma align = natural // expected-warning {{missing '(' after '#pragma align' - ignoring}} +#pragma align(reset) // expected-warning {{#pragma options align=reset failed: stack empty}} +} // namespace test7 + +namespace test8 { +#pragma align(packed) +#pragma pack(2) +#pragma pack(show) // expected-warning {{value of #pragma pack(show) == 2}} +struct A { + int i; + double d; +}; +#pragma align(reset) + +int a = sizeof(A); + +// CHECK: *** Dumping AST Record Layout +// CHECK-NEXT: 0 | struct test8::A +// CHECK-NEXT: 0 | int i +// CHECK-NEXT: 4 | double d +// CHECK-NEXT: | [sizeof=12, dsize=12, align=2, preferredalign=2, +// CHECK-NEXT: | nvsize=12, nvalign=2, preferrednvalign=2] + +} // namespace test8 + +namespace test9 { +#pragma pack(push, r1, 2) // expected-error {{specifying an identifier within `#pragma pack` is not supported on this target}} +struct A { + int i; + double d; +}; +#pragma pack(pop) // expected-warning {{#pragma pack(pop, ...) failed: stack empty}} + +int a = sizeof(A); + +// CHECK: *** Dumping AST Record Layout +// CHECK-NEXT: 0 | struct test9::A +// CHECK-NEXT: 0 | int i +// CHECK-NEXT: 4 | double d +// CHECK-NEXT: | [sizeof=12, dsize=12, align=4, preferredalign=4, +// CHECK-NEXT: | nvsize=12, nvalign=4, preferrednvalign=4] + +} // namespace test9 + +namespace test10 { +#pragma pack(2) +#pragma align(reset) +struct A { + int i; + double d; +}; + +int a = sizeof(A); + +// CHECK: *** Dumping AST Record Layout +// CHECK-NEXT: 0 | struct test10::A +// CHECK-NEXT: 0 | int i +// CHECK-NEXT: 4 | double d +// CHECK-NEXT: | [sizeof=12, dsize=12, align=4, preferredalign=4, +// CHECK-NEXT: | nvsize=12, nvalign=4, preferrednvalign=4] + +} // namespace test10