PR37275 packed attribute should not apply to base classes

Clang incorrectly applied the packed attribute to base classes. Per GCC's
documentation and as can be observed from its behavior, packed only applies to
members, not base classes.

This change is conditioned behind -fclang-abi-compat so that an ABI break can
be avoided by users if desired.

Differential Revision: https://reviews.llvm.org/D46218

llvm-svn: 331136
This commit is contained in:
Richard Smith 2018-04-29 04:55:46 +00:00
parent ebd3e4a69c
commit 4ae767ba3d
4 changed files with 70 additions and 14 deletions

View File

@ -78,6 +78,12 @@ Non-comprehensive list of changes in this release
standard-layout if all base classes and the first data member (or bit-field)
can be laid out at offset zero.
- Clang's handling of the GCC ``packed`` class attribute in C++ has been fixed
to apply only to non-static data members and not to base classes. This fixes
an ABI difference between Clang and GCC, but creates an ABI difference between
Clang 7 and earlier versions. The old behavior can be restored by setting
``-fclang-abi-compat`` to ``6`` or earlier.
- ...
New Compiler Flags

View File

@ -967,7 +967,7 @@ void ItaniumRecordLayoutBuilder::ComputeBaseSubobjectInfo(
void ItaniumRecordLayoutBuilder::EnsureVTablePointerAlignment(
CharUnits UnpackedBaseAlign) {
CharUnits BaseAlign = (Packed) ? CharUnits::One() : UnpackedBaseAlign;
CharUnits BaseAlign = Packed ? CharUnits::One() : UnpackedBaseAlign;
// The maximum field alignment overrides base align.
if (!MaxFieldAlignment.isZero()) {
@ -1175,9 +1175,14 @@ ItaniumRecordLayoutBuilder::LayoutBase(const BaseSubobjectInfo *Base) {
HasExternalLayout = External.getExternalVBaseOffset(Base->Class, Offset);
}
// Clang <= 6 incorrectly applied the 'packed' attribute to base classes.
// Per GCC's documentation, it only applies to non-static data members.
CharUnits UnpackedBaseAlign = Layout.getNonVirtualAlignment();
CharUnits BaseAlign = (Packed) ? CharUnits::One() : UnpackedBaseAlign;
CharUnits BaseAlign = (Packed && Context.getLangOpts().getClangABICompat() <=
LangOptions::ClangABI::Ver6)
? CharUnits::One()
: UnpackedBaseAlign;
// If we have an empty base class, try to place it at offset 0.
if (Base->Class->isEmpty() &&
(!HasExternalLayout || Offset == CharUnits::Zero()) &&

View File

@ -1,4 +1,5 @@
// RUN: %clang_cc1 %s -emit-llvm -o - -triple=x86_64-apple-darwin10 | FileCheck %s
// RUN: %clang_cc1 %s -emit-llvm -o - -triple=x86_64-apple-darwin10 | FileCheck %s --check-prefix=CHECK --check-prefix=CHECK-NOCOMPAT
// RUN: %clang_cc1 %s -emit-llvm -o - -triple=x86_64-apple-darwin10 -fclang-abi-compat=6.0 | FileCheck %s --check-prefix=CHECK --check-prefix=CHECK-V6COMPAT
extern int int_source();
extern void int_sink(int x);
@ -54,11 +55,13 @@ namespace test0 {
// CHECK: [[B_P:%.*]] = bitcast i8* [[T1]] to [[B]]*
// CHECK: [[FIELD_P:%.*]] = bitcast [[B]]* [[B_P]] to i8*
// CHECK: [[TRUNC:%.*]] = trunc i32 [[CALL]] to i8
// CHECK: [[OLD_VALUE:%.*]] = load i8, i8* [[FIELD_P]], align 2
// CHECK-V6COMPAT: [[OLD_VALUE:%.*]] = load i8, i8* [[FIELD_P]], align 2
// CHECK-NOCOMPAT: [[OLD_VALUE:%.*]] = load i8, i8* [[FIELD_P]], align 4
// CHECK: [[T0:%.*]] = and i8 [[TRUNC]], 3
// CHECK: [[T1:%.*]] = and i8 [[OLD_VALUE]], -4
// CHECK: [[T2:%.*]] = or i8 [[T1]], [[T0]]
// CHECK: store i8 [[T2]], i8* [[FIELD_P]], align 2
// CHECK-V6COMPAT: store i8 [[T2]], i8* [[FIELD_P]], align 2
// CHECK-NOCOMPAT: store i8 [[T2]], i8* [[FIELD_P]], align 4
c.onebit = int_source();
// CHECK: [[C_P:%.*]] = load [[C]]*, [[C]]**
@ -66,7 +69,8 @@ namespace test0 {
// CHECK: [[T1:%.*]] = getelementptr inbounds i8, i8* [[T0]], i64 8
// CHECK: [[B_P:%.*]] = bitcast i8* [[T1]] to [[B]]*
// CHECK: [[FIELD_P:%.*]] = bitcast [[B]]* [[B_P]] to i8*
// CHECK: [[VALUE:%.*]] = load i8, i8* [[FIELD_P]], align 2
// CHECK-V6COMPAT: [[VALUE:%.*]] = load i8, i8* [[FIELD_P]], align 2
// CHECK-NOCOMPAT: [[VALUE:%.*]] = load i8, i8* [[FIELD_P]], align 4
// CHECK: [[T0:%.*]] = shl i8 [[VALUE]], 6
// CHECK: [[T1:%.*]] = ashr i8 [[T0]], 6
// CHECK: [[T2:%.*]] = sext i8 [[T1]] to i32
@ -83,11 +87,13 @@ namespace test0 {
// CHECK: [[B_P:%.*]] = bitcast i8* [[T1]] to [[B]]*
// CHECK: [[FIELD_P:%.*]] = bitcast [[B]]* [[B_P]] to i8*
// CHECK: [[TRUNC:%.*]] = trunc i32 [[CALL]] to i8
// CHECK: [[OLD_VALUE:%.*]] = load i8, i8* [[FIELD_P]], align 2
// CHECK-V6COMPAT: [[OLD_VALUE:%.*]] = load i8, i8* [[FIELD_P]], align 2
// CHECK-NOCOMPAT: [[OLD_VALUE:%.*]] = load i8, i8* [[FIELD_P]], align 4
// CHECK: [[T0:%.*]] = and i8 [[TRUNC]], 3
// CHECK: [[T1:%.*]] = and i8 [[OLD_VALUE]], -4
// CHECK: [[T2:%.*]] = or i8 [[T1]], [[T0]]
// CHECK: store i8 [[T2]], i8* [[FIELD_P]], align 2
// CHECK-V6COMPAT: store i8 [[T2]], i8* [[FIELD_P]], align 2
// CHECK-NOCOMPAT: store i8 [[T2]], i8* [[FIELD_P]], align 4
c->onebit = int_source();
// CHECK: [[C_P:%.*]] = load [[C:%.*]]*, [[C]]**
@ -95,7 +101,8 @@ namespace test0 {
// CHECK: [[T1:%.*]] = getelementptr inbounds i8, i8* [[T0]], i64 8
// CHECK: [[B_P:%.*]] = bitcast i8* [[T1]] to [[B:%.*]]*
// CHECK: [[FIELD_P:%.*]] = bitcast [[B]]* [[B_P]] to i8*
// CHECK: [[VALUE:%.*]] = load i8, i8* [[FIELD_P]], align 2
// CHECK-V6COMPAT: [[VALUE:%.*]] = load i8, i8* [[FIELD_P]], align 2
// CHECK-NOCOMPAT: [[VALUE:%.*]] = load i8, i8* [[FIELD_P]], align 4
// CHECK: [[T0:%.*]] = shl i8 [[VALUE]], 6
// CHECK: [[T1:%.*]] = ashr i8 [[T0]], 6
// CHECK: [[T2:%.*]] = sext i8 [[T1]] to i32
@ -107,7 +114,8 @@ namespace test0 {
// in an alignment-2 variable.
// CHECK-LABEL: @_ZN5test01dEv
void d() {
// CHECK: [[C_P:%.*]] = alloca [[C:%.*]], align 2
// CHECK-V6COMPAT: [[C_P:%.*]] = alloca [[C:%.*]], align 2
// CHECK-NOCOMPAT: [[C_P:%.*]] = alloca [[C:%.*]], align 4
C c;
// CHECK: [[CALL:%.*]] = call i32 @_Z10int_sourcev()
@ -116,18 +124,21 @@ namespace test0 {
// CHECK: [[B_P:%.*]] = bitcast i8* [[T1]] to [[B]]*
// CHECK: [[FIELD_P:%.*]] = bitcast [[B]]* [[B_P]] to i8*
// CHECK: [[TRUNC:%.*]] = trunc i32 [[CALL]] to i8
// CHECK: [[OLD_VALUE:%.*]] = load i8, i8* [[FIELD_P]], align 2
// CHECK-V6COMPAT: [[OLD_VALUE:%.*]] = load i8, i8* [[FIELD_P]], align 2
// CHECK-NOCOMPAT: [[OLD_VALUE:%.*]] = load i8, i8* [[FIELD_P]], align 4
// CHECK: [[T0:%.*]] = and i8 [[TRUNC]], 3
// CHECK: [[T1:%.*]] = and i8 [[OLD_VALUE]], -4
// CHECK: [[T2:%.*]] = or i8 [[T1]], [[T0]]
// CHECK: store i8 [[T2]], i8* [[FIELD_P]], align 2
// CHECK-V6COMPAT: store i8 [[T2]], i8* [[FIELD_P]], align 2
// CHECK-NOCOMPAT: store i8 [[T2]], i8* [[FIELD_P]], align 4
c.onebit = int_source();
// CHECK: [[T0:%.*]] = bitcast [[C]]* [[C_P]] to i8*
// CHECK: [[T1:%.*]] = getelementptr inbounds i8, i8* [[T0]], i64 8
// CHECK: [[B_P:%.*]] = bitcast i8* [[T1]] to [[B:%.*]]*
// CHECK: [[FIELD_P:%.*]] = bitcast [[B]]* [[B_P]] to i8*
// CHECK: [[VALUE:%.*]] = load i8, i8* [[FIELD_P]], align 2
// CHECK-V6COMPAT: [[VALUE:%.*]] = load i8, i8* [[FIELD_P]], align 2
// CHECK-NOCOMPAT: [[VALUE:%.*]] = load i8, i8* [[FIELD_P]], align 4
// CHECK: [[T0:%.*]] = shl i8 [[VALUE]], 6
// CHECK: [[T1:%.*]] = ashr i8 [[T0]], 6
// CHECK: [[T2:%.*]] = sext i8 [[T1]] to i32

View File

@ -1,5 +1,6 @@
// RUN: %clang_cc1 -triple x86_64-unknown-unknown %s -fsyntax-only -verify -std=c++98 -Wno-inaccessible-base
// RUN: %clang_cc1 -triple x86_64-unknown-unknown %s -fsyntax-only -verify -std=c++11 -Wno-inaccessible-base
// RUN: %clang_cc1 -triple x86_64-unknown-unknown %s -fsyntax-only -verify -std=c++11 -Wno-inaccessible-base -fclang-abi-compat=6 -DCLANG_ABI_COMPAT=6
// expected-no-diagnostics
#define SA(n, p) int a##n[(p) ? 1 : -1]
@ -570,3 +571,36 @@ namespace test18 {
SA(0, sizeof(might_use_tail_padding) == 80);
}
} // namespace PR16537
namespace PR37275 {
struct X { char c; };
struct A { int n; };
_Static_assert(_Alignof(A) == _Alignof(int), "");
// __attribute__((packed)) does not apply to base classes.
struct __attribute__((packed)) B : X, A {};
#if defined(CLANG_ABI_COMPAT) && CLANG_ABI_COMPAT <= 6
_Static_assert(_Alignof(B) == 1, "");
_Static_assert(__builtin_offsetof(B, n) == 1, "");
#else
_Static_assert(_Alignof(B) == _Alignof(int), "");
_Static_assert(__builtin_offsetof(B, n) == 4, "");
#endif
// #pragma pack does, though.
#pragma pack(push, 2)
struct C : X, A {};
_Static_assert(_Alignof(C) == 2, "");
_Static_assert(__builtin_offsetof(C, n) == 2, "");
struct __attribute__((packed)) D : X, A {};
#if defined(CLANG_ABI_COMPAT) && CLANG_ABI_COMPAT <= 6
_Static_assert(_Alignof(D) == 1, "");
_Static_assert(__builtin_offsetof(D, n) == 1, "");
#else
_Static_assert(_Alignof(D) == 2, "");
_Static_assert(__builtin_offsetof(D, n) == 2, "");
#endif
#pragma pack(pop)
}