Skip to content

Commit 263345b

Browse files
authored
[CIR] Implement zero-initialization of padding in constant records (#162483)
This adds the zero initialization as required by the C standard.
1 parent 4c2b1d4 commit 263345b

File tree

3 files changed

+162
-9
lines changed

3 files changed

+162
-9
lines changed

clang/include/clang/CIR/MissingFeatures.h

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -137,7 +137,6 @@ struct MissingFeatures {
137137
// RecordType
138138
static bool skippedLayout() { return false; }
139139
static bool astRecordDeclAttr() { return false; }
140-
static bool recordZeroInitPadding() { return false; }
141140
static bool zeroSizeRecordMembers() { return false; }
142141

143142
// Coroutines

clang/lib/CIR/CodeGen/CIRGenExprConstant.cpp

Lines changed: 72 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -500,6 +500,26 @@ class ConstRecordBuilder {
500500
bool appendBitField(const FieldDecl *field, uint64_t fieldOffset,
501501
cir::IntAttr ci, bool allowOverwrite = false);
502502

503+
/// Applies zero-initialization to padding bytes before and within a field.
504+
/// \param layout The record layout containing field offset information.
505+
/// \param fieldNo The field index in the record.
506+
/// \param field The field declaration.
507+
/// \param allowOverwrite Whether to allow overwriting existing values.
508+
/// \param sizeSoFar The current size processed, updated by this function.
509+
/// \param zeroFieldSize Set to true if the field has zero size.
510+
/// \returns true on success, false if padding could not be applied.
511+
bool applyZeroInitPadding(const ASTRecordLayout &layout, unsigned fieldNo,
512+
const FieldDecl &field, bool allowOverwrite,
513+
CharUnits &sizeSoFar, bool &zeroFieldSize);
514+
515+
/// Applies zero-initialization to trailing padding bytes in a record.
516+
/// \param layout The record layout containing size information.
517+
/// \param allowOverwrite Whether to allow overwriting existing values.
518+
/// \param sizeSoFar The current size processed.
519+
/// \returns true on success, false if padding could not be applied.
520+
bool applyZeroInitPadding(const ASTRecordLayout &layout, bool allowOverwrite,
521+
CharUnits &sizeSoFar);
522+
503523
bool build(InitListExpr *ile, bool allowOverwrite);
504524
bool build(const APValue &val, const RecordDecl *rd, bool isPrimaryBase,
505525
const CXXRecordDecl *vTableClass, CharUnits baseOffset);
@@ -548,6 +568,49 @@ bool ConstRecordBuilder::appendBitField(const FieldDecl *field,
548568
allowOverwrite);
549569
}
550570

571+
bool ConstRecordBuilder::applyZeroInitPadding(
572+
const ASTRecordLayout &layout, unsigned fieldNo, const FieldDecl &field,
573+
bool allowOverwrite, CharUnits &sizeSoFar, bool &zeroFieldSize) {
574+
uint64_t startBitOffset = layout.getFieldOffset(fieldNo);
575+
CharUnits startOffset =
576+
cgm.getASTContext().toCharUnitsFromBits(startBitOffset);
577+
if (sizeSoFar < startOffset) {
578+
if (!appendBytes(sizeSoFar, computePadding(cgm, startOffset - sizeSoFar),
579+
allowOverwrite))
580+
return false;
581+
}
582+
583+
if (!field.isBitField()) {
584+
CharUnits fieldSize =
585+
cgm.getASTContext().getTypeSizeInChars(field.getType());
586+
sizeSoFar = startOffset + fieldSize;
587+
zeroFieldSize = fieldSize.isZero();
588+
} else {
589+
const CIRGenRecordLayout &rl =
590+
cgm.getTypes().getCIRGenRecordLayout(field.getParent());
591+
const CIRGenBitFieldInfo &info = rl.getBitFieldInfo(&field);
592+
uint64_t endBitOffset = startBitOffset + info.size;
593+
sizeSoFar = cgm.getASTContext().toCharUnitsFromBits(endBitOffset);
594+
if (endBitOffset % cgm.getASTContext().getCharWidth() != 0)
595+
sizeSoFar++;
596+
zeroFieldSize = info.size == 0;
597+
}
598+
return true;
599+
}
600+
601+
bool ConstRecordBuilder::applyZeroInitPadding(const ASTRecordLayout &layout,
602+
bool allowOverwrite,
603+
CharUnits &sizeSoFar) {
604+
CharUnits totalSize = layout.getSize();
605+
if (sizeSoFar < totalSize) {
606+
if (!appendBytes(sizeSoFar, computePadding(cgm, totalSize - sizeSoFar),
607+
allowOverwrite))
608+
return false;
609+
}
610+
sizeSoFar = totalSize;
611+
return true;
612+
}
613+
551614
bool ConstRecordBuilder::build(InitListExpr *ile, bool allowOverwrite) {
552615
RecordDecl *rd = ile->getType()
553616
->castAs<clang::RecordType>()
@@ -562,11 +625,9 @@ bool ConstRecordBuilder::build(InitListExpr *ile, bool allowOverwrite) {
562625
if (cxxrd->getNumBases())
563626
return false;
564627

565-
if (cgm.shouldZeroInitPadding()) {
566-
assert(!cir::MissingFeatures::recordZeroInitPadding());
567-
cgm.errorNYI(rd->getSourceRange(), "zero init padding");
568-
return false;
569-
}
628+
const bool zeroInitPadding = cgm.shouldZeroInitPadding();
629+
bool zeroFieldSize = false;
630+
CharUnits sizeSoFar = CharUnits::Zero();
570631

571632
unsigned elementNo = 0;
572633
for (auto [index, field] : llvm::enumerate(rd->fields())) {
@@ -596,7 +657,10 @@ bool ConstRecordBuilder::build(InitListExpr *ile, bool allowOverwrite) {
596657
continue;
597658
}
598659

599-
assert(!cir::MissingFeatures::recordZeroInitPadding());
660+
if (zeroInitPadding &&
661+
!applyZeroInitPadding(layout, index, *field, allowOverwrite, sizeSoFar,
662+
zeroFieldSize))
663+
return false;
600664

601665
// When emitting a DesignatedInitUpdateExpr, a nested InitListExpr
602666
// represents additional overwriting of our current constant value, and not
@@ -641,8 +705,8 @@ bool ConstRecordBuilder::build(InitListExpr *ile, bool allowOverwrite) {
641705
}
642706
}
643707

644-
assert(!cir::MissingFeatures::recordZeroInitPadding());
645-
return true;
708+
return !zeroInitPadding ||
709+
applyZeroInitPadding(layout, allowOverwrite, sizeSoFar);
646710
}
647711

648712
namespace {
Lines changed: 90 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,90 @@
1+
// RUN: %clang_cc1 -triple x86_64-unknown-linux-gnu -fclangir -emit-cir %s -o %t.cir
2+
// RUN: FileCheck --input-file=%t.cir %s -check-prefix=CIR
3+
// RUN: %clang_cc1 -triple x86_64-unknown-linux-gnu -fclangir -emit-llvm %s -o %t-cir.ll
4+
// RUN: FileCheck --input-file=%t-cir.ll %s -check-prefix=LLVM
5+
// RUN: %clang_cc1 -triple x86_64-unknown-linux-gnu -emit-llvm %s -o %t.ll
6+
// RUN: FileCheck --input-file=%t.ll %s -check-prefix=OGCG
7+
8+
struct padding_after_field {
9+
char c;
10+
int i;
11+
};
12+
13+
struct bitfield_with_padding {
14+
unsigned int a : 3;
15+
unsigned int b : 5;
16+
int c;
17+
};
18+
19+
struct tail_padding {
20+
int a;
21+
char b;
22+
};
23+
24+
struct multiple_padding {
25+
char a;
26+
short b;
27+
long long c;
28+
};
29+
30+
void test_zero_init_padding(void) {
31+
static const struct padding_after_field paf = {1, 42};
32+
static const struct bitfield_with_padding bfp = {1, 2, 99};
33+
static const struct tail_padding tp = {10, 20};
34+
static const struct multiple_padding mp = {5, 10, 100};
35+
}
36+
37+
// Type definitions for anonymous structs with padding
38+
// CIR-DAG: !rec_anon_struct = !cir.record<struct {!s8i, !u8i, !s16i, !cir.array<!u8i x 4>, !s64i}>
39+
// CIR-DAG: !rec_anon_struct1 = !cir.record<struct {!s32i, !s8i, !cir.array<!u8i x 3>}>
40+
// CIR-DAG: !rec_anon_struct2 = !cir.record<struct {!u8i, !cir.array<!u8i x 3>, !s32i}>
41+
// CIR-DAG: !rec_anon_struct3 = !cir.record<struct {!s8i, !cir.array<!u8i x 3>, !s32i}>
42+
43+
// paf: char + 3 bytes padding + int -> uses !rec_anon_struct3
44+
// CIR-DAG: cir.global "private" internal dso_local @test_zero_init_padding.paf = #cir.const_record<{
45+
// CIR-DAG-SAME: #cir.int<1> : !s8i,
46+
// CIR-DAG-SAME: #cir.const_array<[#cir.zero : !u8i, #cir.zero : !u8i, #cir.zero : !u8i]> : !cir.array<!u8i x 3>,
47+
// CIR-DAG-SAME: #cir.int<42> : !s32i
48+
// CIR-DAG-SAME: }> : !rec_anon_struct3
49+
50+
// bfp: unsigned bitfield byte + 3 bytes padding + int -> uses !rec_anon_struct2
51+
// CIR-DAG: cir.global "private" internal dso_local @test_zero_init_padding.bfp = #cir.const_record<{
52+
// CIR-DAG-SAME: #cir.int<17> : !u8i,
53+
// CIR-DAG-SAME: #cir.const_array<[#cir.zero : !u8i, #cir.zero : !u8i, #cir.zero : !u8i]> : !cir.array<!u8i x 3>,
54+
// CIR-DAG-SAME: #cir.int<99> : !s32i
55+
// CIR-DAG-SAME: }> : !rec_anon_struct2
56+
57+
// tp: int + char + 3 bytes tail padding -> uses !rec_anon_struct1
58+
// CIR-DAG: cir.global "private" internal dso_local @test_zero_init_padding.tp = #cir.const_record<{
59+
// CIR-DAG-SAME: #cir.int<10> : !s32i,
60+
// CIR-DAG-SAME: #cir.int<20> : !s8i,
61+
// CIR-DAG-SAME: #cir.const_array<[#cir.zero : !u8i, #cir.zero : !u8i, #cir.zero : !u8i]> : !cir.array<!u8i x 3>
62+
// CIR-DAG-SAME: }> : !rec_anon_struct1
63+
64+
// mp: char + 1 byte padding + short + 4 bytes padding + long long -> uses !rec_anon_struct
65+
// CIR-DAG: cir.global "private" internal dso_local @test_zero_init_padding.mp = #cir.const_record<{
66+
// CIR-DAG-SAME: #cir.int<5> : !s8i,
67+
// CIR-DAG-SAME: #cir.zero : !u8i,
68+
// CIR-DAG-SAME: #cir.int<10> : !s16i,
69+
// CIR-DAG-SAME: #cir.const_array<[#cir.zero : !u8i, #cir.zero : !u8i, #cir.zero : !u8i, #cir.zero : !u8i]> : !cir.array<!u8i x 4>,
70+
// CIR-DAG-SAME: #cir.int<100> : !s64i
71+
// CIR-DAG-SAME: }> : !rec_anon_struct
72+
73+
// CIR-LABEL: cir.func {{.*}}@test_zero_init_padding
74+
// CIR: cir.return
75+
76+
// LLVM-DAG: @test_zero_init_padding.paf = internal global { i8, [3 x i8], i32 } { i8 1, [3 x i8] zeroinitializer, i32 42 }
77+
// LLVM-DAG: @test_zero_init_padding.bfp = internal global { i8, [3 x i8], i32 } { i8 17, [3 x i8] zeroinitializer, i32 99 }
78+
// LLVM-DAG: @test_zero_init_padding.tp = internal global { i32, i8, [3 x i8] } { i32 10, i8 20, [3 x i8] zeroinitializer }
79+
// LLVM-DAG: @test_zero_init_padding.mp = internal global { i8, i8, i16, [4 x i8], i64 } { i8 5, i8 0, i16 10, [4 x i8] zeroinitializer, i64 100 }
80+
81+
// LLVM-LABEL: define{{.*}} void @test_zero_init_padding
82+
// LLVM: ret void
83+
84+
// OGCG-DAG: @test_zero_init_padding.paf = internal constant { i8, [3 x i8], i32 } { i8 1, [3 x i8] zeroinitializer, i32 42 }
85+
// OGCG-DAG: @test_zero_init_padding.bfp = internal constant { i8, [3 x i8], i32 } { i8 17, [3 x i8] zeroinitializer, i32 99 }
86+
// OGCG-DAG: @test_zero_init_padding.tp = internal constant { i32, i8, [3 x i8] } { i32 10, i8 20, [3 x i8] zeroinitializer }
87+
// OGCG-DAG: @test_zero_init_padding.mp = internal constant { i8, i8, i16, [4 x i8], i64 } { i8 5, i8 0, i16 10, [4 x i8] zeroinitializer, i64 100 }
88+
89+
// OGCG-LABEL: define{{.*}} void @test_zero_init_padding
90+
// OGCG: ret void

0 commit comments

Comments
 (0)