Skip to content

[ARM,AArch64] Fix ABI bugs with over-sized bitfields #126774

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 4 commits into from
Feb 20, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 8 additions & 0 deletions clang/include/clang/Basic/TargetInfo.h
Original file line number Diff line number Diff line change
Expand Up @@ -199,6 +199,10 @@ struct TransferrableTargetInfo {
/// zero length bitfield, regardless of the zero length bitfield type.
unsigned ZeroLengthBitfieldBoundary;

/// The largest container size which should be used for an over-sized
/// bitfield, in bits.
unsigned LargestOverSizedBitfieldContainer;

/// If non-zero, specifies a maximum alignment to truncate alignment
/// specified in the aligned attribute of a static variable to this value.
unsigned MaxAlignedAttribute;
Expand Down Expand Up @@ -954,6 +958,10 @@ class TargetInfo : public TransferrableTargetInfo,
return ZeroLengthBitfieldBoundary;
}

unsigned getLargestOverSizedBitfieldContainer() const {
return LargestOverSizedBitfieldContainer;
}

/// Get the maximum alignment in bits for a static variable with
/// aligned attribute.
unsigned getMaxAlignedAttribute() const { return MaxAlignedAttribute; }
Expand Down
10 changes: 7 additions & 3 deletions clang/lib/AST/RecordLayoutBuilder.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1469,15 +1469,18 @@ void ItaniumRecordLayoutBuilder::LayoutWideBitField(uint64_t FieldSize,
// sizeof(T')*8 <= n.

QualType IntegralPODTypes[] = {
Context.UnsignedCharTy, Context.UnsignedShortTy, Context.UnsignedIntTy,
Context.UnsignedLongTy, Context.UnsignedLongLongTy
Context.UnsignedCharTy, Context.UnsignedShortTy,
Context.UnsignedIntTy, Context.UnsignedLongTy,
Context.UnsignedLongLongTy, Context.UnsignedInt128Ty,
};

QualType Type;
uint64_t MaxSize =
Context.getTargetInfo().getLargestOverSizedBitfieldContainer();
for (const QualType &QT : IntegralPODTypes) {
uint64_t Size = Context.getTypeSize(QT);

if (Size > FieldSize)
if (Size > FieldSize || Size > MaxSize)
break;

Type = QT;
Expand Down Expand Up @@ -1520,6 +1523,7 @@ void ItaniumRecordLayoutBuilder::LayoutWideBitField(uint64_t FieldSize,
setSize(std::max(getSizeInBits(), getDataSizeInBits()));

// Remember max struct/class alignment.
UnadjustedAlignment = std::max(UnadjustedAlignment, TypeAlign);
UpdateAlignment(TypeAlign);
}

Expand Down
1 change: 1 addition & 0 deletions clang/lib/Basic/TargetInfo.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -141,6 +141,7 @@ TargetInfo::TargetInfo(const llvm::Triple &T) : Triple(T) {
UseLeadingZeroLengthBitfield = true;
UseExplicitBitFieldAlignment = true;
ZeroLengthBitfieldBoundary = 0;
LargestOverSizedBitfieldContainer = 64;
MaxAlignedAttribute = 0;
HalfFormat = &llvm::APFloat::IEEEhalf();
FloatFormat = &llvm::APFloat::IEEEsingle();
Expand Down
4 changes: 4 additions & 0 deletions clang/lib/Basic/Targets/AArch64.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -261,6 +261,10 @@ AArch64TargetInfo::AArch64TargetInfo(const llvm::Triple &Triple,
assert(UseBitFieldTypeAlignment && "bitfields affect type alignment");
UseZeroLengthBitfieldAlignment = true;

// AAPCS64 allows any "fundamental integer data type" to be used for
// over-sized bitfields, which includes 128-bit integers.
LargestOverSizedBitfieldContainer = 128;

HasUnalignedAccess = true;

// AArch64 targets default to using the ARM C++ ABI.
Expand Down
43 changes: 43 additions & 0 deletions clang/test/CodeGen/aapcs-align.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,11 @@

extern "C" {

// CHECK: @sizeof_OverSizedBitfield ={{.*}} global i32 8
// CHECK: @alignof_OverSizedBitfield ={{.*}} global i32 8
// CHECK: @sizeof_VeryOverSizedBitfield ={{.*}} global i32 16
// CHECK: @alignof_VeryOverSizedBitfield ={{.*}} global i32 8

// Base case, nothing interesting.
struct S {
int x, y;
Expand Down Expand Up @@ -138,4 +143,42 @@ void g6() {
// CHECK: call void @f6m(i32 noundef 1, i32 noundef 2, i32 noundef 3, i32 noundef 4, i32 noundef 5, [4 x i32] [i32 6, i32 7, i32 0, i32 undef])
// CHECK: declare void @f6(i32 noundef, [4 x i32])
// CHECK: declare void @f6m(i32 noundef, i32 noundef, i32 noundef, i32 noundef, i32 noundef, [4 x i32])

// Over-sized bitfield, which results in a 64-bit container type, so 64-bit
// alignment.
struct OverSizedBitfield {
int x : 64;
};

unsigned sizeof_OverSizedBitfield = sizeof(OverSizedBitfield);
unsigned alignof_OverSizedBitfield = alignof(OverSizedBitfield);

// CHECK: define{{.*}} void @g7
// CHECK: call void @f7(i32 noundef 1, [1 x i64] [i64 42])
// CHECK: declare void @f7(i32 noundef, [1 x i64])
void f7(int a, OverSizedBitfield b);
void g7() {
OverSizedBitfield s = {42};
f7(1, s);
}

// There are no 128-bit fundamental data types defined by AAPCS32, so this gets
// a 64-bit container plus 64 bits of padding, giving it a size of 16 bytes and
// alignment of 8 bytes.
struct VeryOverSizedBitfield {
int x : 128;
};

unsigned sizeof_VeryOverSizedBitfield = sizeof(VeryOverSizedBitfield);
unsigned alignof_VeryOverSizedBitfield = alignof(VeryOverSizedBitfield);

// CHECK: define{{.*}} void @g8
// CHECK: call void @f8(i32 noundef 1, [2 x i64] [i64 42, i64 0])
// CHECK: declare void @f8(i32 noundef, [2 x i64])
void f8(int a, VeryOverSizedBitfield b);
void g8() {
VeryOverSizedBitfield s = {42};
f8(1, s);
}

}
64 changes: 64 additions & 0 deletions clang/test/CodeGen/aapcs64-align.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,13 @@

extern "C" {

// CHECK: @sizeof_OverSizedBitfield ={{.*}} global i32 8
// CHECK: @alignof_OverSizedBitfield ={{.*}} global i32 8
// CHECK: @sizeof_VeryOverSizedBitfield ={{.*}} global i32 16
// CHECK: @alignof_VeryOverSizedBitfield ={{.*}} global i32 16
// CHECK: @sizeof_RidiculouslyOverSizedBitfield ={{.*}} global i32 32
// CHECK: @alignof_RidiculouslyOverSizedBitfield ={{.*}} global i32 16

// Base case, nothing interesting.
struct S {
long x, y;
Expand Down Expand Up @@ -161,5 +168,62 @@ int test_bitint8(){
}
// CHECK: ret i32 1

// Over-sized bitfield, which results in a 64-bit container type, so 64-bit
// alignment.
struct OverSizedBitfield {
int x : 64;
};

unsigned sizeof_OverSizedBitfield = sizeof(OverSizedBitfield);
unsigned alignof_OverSizedBitfield = alignof(OverSizedBitfield);

// CHECK: define{{.*}} void @g7
// CHECK: call void @f7(i32 noundef 1, i64 42)
// CHECK: declare void @f7(i32 noundef, i64)
void f7(int a, OverSizedBitfield b);
void g7() {
OverSizedBitfield s = {42};
f7(1, s);
}

// AAPCS64 does have a 128-bit integer fundamental data type, so this gets a
// 128-bit container with 128-bit alignment. This is just within the limit of
// what can be passed directly.
struct VeryOverSizedBitfield {
int x : 128;
};

unsigned sizeof_VeryOverSizedBitfield = sizeof(VeryOverSizedBitfield);
unsigned alignof_VeryOverSizedBitfield = alignof(VeryOverSizedBitfield);

// CHECK: define{{.*}} void @g8
// CHECK: call void @f8(i32 noundef 1, i128 42)
// CHECK: declare void @f8(i32 noundef, i128)
void f8(int a, VeryOverSizedBitfield b);
void g8() {
VeryOverSizedBitfield s = {42};
f8(1, s);
}

// There are no bigger fundamental data types, so this gets a 128-bit container
// and 128 bits of padding, giving the struct a size of 32 bytes, and an
// alignment of 16 bytes. This is over the PCS size limit of 16 bytes, so it
// will be passed indirectly.
struct RidiculouslyOverSizedBitfield {
int x : 256;
};

unsigned sizeof_RidiculouslyOverSizedBitfield = sizeof(RidiculouslyOverSizedBitfield);
unsigned alignof_RidiculouslyOverSizedBitfield = alignof(RidiculouslyOverSizedBitfield);

// CHECK: define{{.*}} void @g9
// CHECK: call void @f9(i32 noundef 1, ptr noundef nonnull %agg.tmp)
// CHECK: declare void @f9(i32 noundef, ptr noundef)
void f9(int a, RidiculouslyOverSizedBitfield b);
void g9() {
RidiculouslyOverSizedBitfield s = {42};
f9(1, s);
}

}

Original file line number Diff line number Diff line change
Expand Up @@ -248,8 +248,8 @@ struct S15 {
};

// CHECK-LABEL: define dso_local void @_Z4fS15v
// CHECK: alloca %struct.S15, align 8
// CHECK-NEXT: [[TMP0:%.*]] = alloca %struct.S15, align 8
// CHECK: alloca %struct.S15, align 16
// CHECK-NEXT: [[TMP0:%.*]] = alloca %struct.S15, align 16
// CHECK: #dbg_declare(ptr [[TMP0]], [[S15_A:![0-9]+]], !DIExpression(DW_OP_LLVM_extract_bits_sext, 0, 32),
// CHECK-NEXT: #dbg_declare(ptr [[TMP0]], [[S15_B:![0-9]+]], !DIExpression(DW_OP_plus_uconst, 16, DW_OP_LLVM_extract_bits_zext, 0, 32),
//
Expand Down