forked from OSchip/llvm-project
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:
parent
ebd3e4a69c
commit
4ae767ba3d
|
@ -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
|
||||
|
|
|
@ -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()) &&
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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)
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue