Index: include/clang/Basic/TargetInfo.h =================================================================== --- include/clang/Basic/TargetInfo.h +++ include/clang/Basic/TargetInfo.h @@ -204,6 +204,11 @@ /// not for language specific address spaces bool UseAddrSpaceMapMangling; + /// \brief Specify if globals of a struct type containing bitfields should + /// have their alignment explicitly specified so as to ensure that LLVM uses + /// the correct alignment + bool EnforceBitfieldContainerAlignment; + public: IntType getSizeType() const { return SizeType; } IntType getIntMaxType() const { return IntMaxType; } @@ -454,6 +459,11 @@ return HasAlignMac68kSupport; } + /// \brief Check whether the alignment of bitfield struct should be enforced + bool enforceBitfieldContainerAlignment() const { + return EnforceBitfieldContainerAlignment; + } + /// \brief Return the user string for the specified integer type enum. /// /// For example, SignedShort -> "short". Index: lib/Basic/TargetInfo.cpp =================================================================== --- lib/Basic/TargetInfo.cpp +++ lib/Basic/TargetInfo.cpp @@ -75,6 +75,7 @@ RegParmMax = 0; SSERegParmMax = 0; HasAlignMac68kSupport = false; + EnforceBitfieldContainerAlignment = false; // Default to no types using fpret. RealTypeUsesObjCFPRet = 0; Index: lib/Basic/Targets.cpp =================================================================== --- lib/Basic/Targets.cpp +++ lib/Basic/Targets.cpp @@ -3983,6 +3983,9 @@ ZeroLengthBitfieldBoundary = 0; + // Enforce the alignment of bitfield structs + EnforceBitfieldContainerAlignment = true; + // Thumb1 add sp, #imm requires the immediate value be multiple of 4, // so set preferred for small types to 32. if (T.isOSBinFormatMachO()) { @@ -4825,6 +4828,9 @@ UseBitFieldTypeAlignment = true; UseZeroLengthBitfieldAlignment = true; + // Enforce the alignment of bitfield structs + EnforceBitfieldContainerAlignment = true; + // AArch64 targets default to using the ARM C++ ABI. TheCXXABI.set(TargetCXXABI::GenericAArch64); } Index: lib/CodeGen/CodeGenModule.cpp =================================================================== --- lib/CodeGen/CodeGenModule.cpp +++ lib/CodeGen/CodeGenModule.cpp @@ -1799,6 +1799,24 @@ D->getType().isConstant(Context) && isExternallyVisible(D->getLinkageAndVisibility().getLinkage())) GV->setSection(".cp.rodata"); + + // The ARM/AArch64 ABI expects structs with bitfields to respect the proper + // container alignment, hence we have to enfore this in the IR so as to + // work around clang combining bitfields into one large type. + llvm::Triple::ArchType Arch = Context.getTargetInfo().getTriple().getArch(); + if (getContext().getTargetInfo().enforceBitfieldContainerAlignment()) { + if (const auto *RT = D->getType()->getAs()) { + const RecordDecl *RD = RT->getDecl(); + + for (auto I = RD->field_begin(), End = RD->field_end(); I != End; ++I) { + if ((*I)->isBitField()) { + const ASTRecordLayout &Info = getContext().getASTRecordLayout(RD); + GV->setAlignment(Info.getAlignment().getQuantity()); + break; + } + } + } + } } if (AddrSpace != Ty->getAddressSpace()) Index: test/CodeGen/arm-bitfield-alignment.c =================================================================== --- /dev/null +++ test/CodeGen/arm-bitfield-alignment.c @@ -0,0 +1,15 @@ +// RUN: %clang_cc1 -triple arm-none-eabi -ffreestanding -emit-llvm -o - -O3 %s | FileCheck %s +// RUN: %clang_cc1 -triple aarch64 -ffreestanding -emit-llvm -o - -O3 %s | FileCheck %s + +extern struct T { + int b0 : 8; + int b1 : 24; + int b2 : 1; +} g; + +int func() { + return g.b1; +} + +// CHECK: @g = external global %struct.T, align 4 +// CHECK: %{{.*}} = load i64, i64* bitcast (%struct.T* @g to i64*), align 4