From 702de33e4171235e9c479b3c5fa14d6d3522e1fd Mon Sep 17 00:00:00 2001 From: Morris Hafner Date: Wed, 8 Oct 2025 15:45:50 +0200 Subject: [PATCH 1/3] [CIR] Implement zero-initialization of padding in constant records This adds the zero initialization as required by the C standard. --- clang/include/clang/CIR/MissingFeatures.h | 1 - clang/lib/CIR/CodeGen/CIRGenExprConstant.cpp | 79 ++++++++++++++-- .../CIR/CodeGen/record-zero-init-padding.c | 90 +++++++++++++++++++ 3 files changed, 161 insertions(+), 9 deletions(-) create mode 100644 clang/test/CIR/CodeGen/record-zero-init-padding.c diff --git a/clang/include/clang/CIR/MissingFeatures.h b/clang/include/clang/CIR/MissingFeatures.h index f79580083cf9f..b0b825707aa7a 100644 --- a/clang/include/clang/CIR/MissingFeatures.h +++ b/clang/include/clang/CIR/MissingFeatures.h @@ -138,7 +138,6 @@ struct MissingFeatures { // RecordType static bool skippedLayout() { return false; } static bool astRecordDeclAttr() { return false; } - static bool recordZeroInitPadding() { return false; } static bool zeroSizeRecordMembers() { return false; } // Coroutines diff --git a/clang/lib/CIR/CodeGen/CIRGenExprConstant.cpp b/clang/lib/CIR/CodeGen/CIRGenExprConstant.cpp index 59aa25726a749..f577e23d7093e 100644 --- a/clang/lib/CIR/CodeGen/CIRGenExprConstant.cpp +++ b/clang/lib/CIR/CodeGen/CIRGenExprConstant.cpp @@ -500,6 +500,26 @@ class ConstRecordBuilder { bool appendBitField(const FieldDecl *field, uint64_t fieldOffset, cir::IntAttr ci, bool allowOverwrite = false); + /// Applies zero-initialization to padding bytes before and within a field. + /// \param layout The record layout containing field offset information. + /// \param fieldNo The field index in the record. + /// \param field The field declaration. + /// \param allowOverwrite Whether to allow overwriting existing values. + /// \param sizeSoFar The current size processed, updated by this function. + /// \param zeroFieldSize Set to true if the field has zero size. + /// \returns true on success, false if padding could not be applied. + bool applyZeroInitPadding(const ASTRecordLayout &layout, unsigned fieldNo, + const FieldDecl &field, bool allowOverwrite, + CharUnits &sizeSoFar, bool &zeroFieldSize); + + /// Applies zero-initialization to trailing padding bytes in a record. + /// \param layout The record layout containing size information. + /// \param allowOverwrite Whether to allow overwriting existing values. + /// \param sizeSoFar The current size processed. + /// \returns true on success, false if padding could not be applied. + bool applyZeroInitPadding(const ASTRecordLayout &layout, bool allowOverwrite, + CharUnits &sizeSoFar); + bool build(InitListExpr *ile, bool allowOverwrite); bool build(const APValue &val, const RecordDecl *rd, bool isPrimaryBase, const CXXRecordDecl *vTableClass, CharUnits baseOffset); @@ -548,6 +568,48 @@ bool ConstRecordBuilder::appendBitField(const FieldDecl *field, allowOverwrite); } +bool ConstRecordBuilder::applyZeroInitPadding( + const ASTRecordLayout &layout, unsigned fieldNo, const FieldDecl &field, + bool allowOverwrite, CharUnits &sizeSoFar, bool &zeroFieldSize) { + uint64_t startBitOffset = layout.getFieldOffset(fieldNo); + CharUnits startOffset = + cgm.getASTContext().toCharUnitsFromBits(startBitOffset); + if (sizeSoFar < startOffset) { + if (!appendBytes(sizeSoFar, computePadding(cgm, startOffset - sizeSoFar), + allowOverwrite)) + return false; + } + + if (!field.isBitField()) { + CharUnits fieldSize = + cgm.getASTContext().getTypeSizeInChars(field.getType()); + sizeSoFar = startOffset + fieldSize; + zeroFieldSize = fieldSize.isZero(); + } else { + const CIRGenRecordLayout &rl = + cgm.getTypes().getCIRGenRecordLayout(field.getParent()); + const CIRGenBitFieldInfo &info = rl.getBitFieldInfo(&field); + uint64_t endBitOffset = startBitOffset + info.size; + sizeSoFar = cgm.getASTContext().toCharUnitsFromBits(endBitOffset); + if (endBitOffset % cgm.getASTContext().getCharWidth() != 0) + sizeSoFar++; + zeroFieldSize = info.size == 0; + } + return true; +} + +bool ConstRecordBuilder::applyZeroInitPadding(const ASTRecordLayout &layout, + bool allowOverwrite, + CharUnits &sizeSoFar) { + CharUnits totalSize = layout.getSize(); + if (sizeSoFar < totalSize) { + if (!appendBytes(sizeSoFar, computePadding(cgm, totalSize - sizeSoFar), + allowOverwrite)) + return false; + } + return true; +} + bool ConstRecordBuilder::build(InitListExpr *ile, bool allowOverwrite) { RecordDecl *rd = ile->getType() ->castAs() @@ -562,11 +624,9 @@ bool ConstRecordBuilder::build(InitListExpr *ile, bool allowOverwrite) { if (cxxrd->getNumBases()) return false; - if (cgm.shouldZeroInitPadding()) { - assert(!cir::MissingFeatures::recordZeroInitPadding()); - cgm.errorNYI(rd->getSourceRange(), "zero init padding"); - return false; - } + const bool zeroInitPadding = cgm.shouldZeroInitPadding(); + bool zeroFieldSize = false; + CharUnits sizeSoFar = CharUnits::Zero(); unsigned elementNo = 0; for (auto [index, field] : llvm::enumerate(rd->fields())) { @@ -596,7 +656,10 @@ bool ConstRecordBuilder::build(InitListExpr *ile, bool allowOverwrite) { continue; } - assert(!cir::MissingFeatures::recordZeroInitPadding()); + if (zeroInitPadding && + !applyZeroInitPadding(layout, index, *field, allowOverwrite, sizeSoFar, + zeroFieldSize)) + return false; // When emitting a DesignatedInitUpdateExpr, a nested InitListExpr // represents additional overwriting of our current constant value, and not @@ -641,8 +704,8 @@ bool ConstRecordBuilder::build(InitListExpr *ile, bool allowOverwrite) { } } - assert(!cir::MissingFeatures::recordZeroInitPadding()); - return true; + return !zeroInitPadding || + applyZeroInitPadding(layout, allowOverwrite, sizeSoFar); } namespace { diff --git a/clang/test/CIR/CodeGen/record-zero-init-padding.c b/clang/test/CIR/CodeGen/record-zero-init-padding.c new file mode 100644 index 0000000000000..240dabba3c5d9 --- /dev/null +++ b/clang/test/CIR/CodeGen/record-zero-init-padding.c @@ -0,0 +1,90 @@ +// RUN: %clang_cc1 -triple x86_64-unknown-linux-gnu -fclangir -emit-cir %s -o %t.cir +// RUN: FileCheck --input-file=%t.cir %s -check-prefix=CIR +// RUN: %clang_cc1 -triple x86_64-unknown-linux-gnu -fclangir -emit-llvm %s -o %t-cir.ll +// RUN: FileCheck --input-file=%t-cir.ll %s -check-prefix=LLVM +// RUN: %clang_cc1 -triple x86_64-unknown-linux-gnu -emit-llvm %s -o %t.ll +// RUN: FileCheck --input-file=%t.ll %s -check-prefix=OGCG + +struct padding_after_field { + char c; + int i; +}; + +struct bitfield_with_padding { + unsigned int a : 3; + unsigned int b : 5; + int c; +}; + +struct tail_padding { + int a; + char b; +}; + +struct multiple_padding { + char a; + short b; + long long c; +}; + +void test_zero_init_padding(void) { + static const struct padding_after_field paf = {1, 42}; + static const struct bitfield_with_padding bfp = {1, 2, 99}; + static const struct tail_padding tp = {10, 20}; + static const struct multiple_padding mp = {5, 10, 100}; +} + +// Type definitions for anonymous structs with padding +// CIR-DAG: !rec_anon_struct = !cir.record, !s64i}> +// CIR-DAG: !rec_anon_struct1 = !cir.record}> +// CIR-DAG: !rec_anon_struct2 = !cir.record, !s32i}> +// CIR-DAG: !rec_anon_struct3 = !cir.record, !s32i}> + +// paf: char + 3 bytes padding + int -> uses !rec_anon_struct3 +// CIR-DAG: cir.global "private" internal dso_local @test_zero_init_padding.paf = #cir.const_record<{ +// CIR-DAG-SAME: #cir.int<1> : !s8i, +// CIR-DAG-SAME: #cir.const_array<[#cir.zero : !u8i, #cir.zero : !u8i, #cir.zero : !u8i]> : !cir.array, +// CIR-DAG-SAME: #cir.int<42> : !s32i +// CIR-DAG-SAME: }> : !rec_anon_struct3 + +// bfp: unsigned bitfield byte + 3 bytes padding + int -> uses !rec_anon_struct2 +// CIR-DAG: cir.global "private" internal dso_local @test_zero_init_padding.bfp = #cir.const_record<{ +// CIR-DAG-SAME: #cir.int<17> : !u8i, +// CIR-DAG-SAME: #cir.const_array<[#cir.zero : !u8i, #cir.zero : !u8i, #cir.zero : !u8i]> : !cir.array, +// CIR-DAG-SAME: #cir.int<99> : !s32i +// CIR-DAG-SAME: }> : !rec_anon_struct2 + +// tp: int + char + 3 bytes tail padding -> uses !rec_anon_struct1 +// CIR-DAG: cir.global "private" internal dso_local @test_zero_init_padding.tp = #cir.const_record<{ +// CIR-DAG-SAME: #cir.int<10> : !s32i, +// CIR-DAG-SAME: #cir.int<20> : !s8i, +// CIR-DAG-SAME: #cir.const_array<[#cir.zero : !u8i, #cir.zero : !u8i, #cir.zero : !u8i]> : !cir.array +// CIR-DAG-SAME: }> : !rec_anon_struct1 + +// mp: char + 1 byte padding + short + 4 bytes padding + long long -> uses !rec_anon_struct +// CIR-DAG: cir.global "private" internal dso_local @test_zero_init_padding.mp = #cir.const_record<{ +// CIR-DAG-SAME: #cir.int<5> : !s8i, +// CIR-DAG-SAME: #cir.zero : !u8i, +// CIR-DAG-SAME: #cir.int<10> : !s16i, +// CIR-DAG-SAME: #cir.const_array<[#cir.zero : !u8i, #cir.zero : !u8i, #cir.zero : !u8i, #cir.zero : !u8i]> : !cir.array, +// CIR-DAG-SAME: #cir.int<100> : !s64i +// CIR-DAG-SAME: }> : !rec_anon_struct + +// CIR-LABEL: cir.func {{.*}}@test_zero_init_padding +// CIR: cir.return + +// LLVM-DAG: @test_zero_init_padding.paf = internal global { i8, [3 x i8], i32 } { i8 1, [3 x i8] zeroinitializer, i32 42 } +// LLVM-DAG: @test_zero_init_padding.bfp = internal global { i8, [3 x i8], i32 } { i8 17, [3 x i8] zeroinitializer, i32 99 } +// LLVM-DAG: @test_zero_init_padding.tp = internal global { i32, i8, [3 x i8] } { i32 10, i8 20, [3 x i8] zeroinitializer } +// 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 } + +// LLVM-LABEL: define{{.*}} void @test_zero_init_padding +// LLVM: ret void + +// OGCG-DAG: @test_zero_init_padding.paf = internal constant { i8, [3 x i8], i32 } { i8 1, [3 x i8] zeroinitializer, i32 42 } +// OGCG-DAG: @test_zero_init_padding.bfp = internal constant { i8, [3 x i8], i32 } { i8 17, [3 x i8] zeroinitializer, i32 99 } +// OGCG-DAG: @test_zero_init_padding.tp = internal constant { i32, i8, [3 x i8] } { i32 10, i8 20, [3 x i8] zeroinitializer } +// 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 } + +// OGCG-LABEL: define{{.*}} void @test_zero_init_padding +// OGCG: ret void From 62c84ae4fb311b4793deba5d29bad0e381d2feac Mon Sep 17 00:00:00 2001 From: Morris Hafner Date: Wed, 8 Oct 2025 19:05:30 +0200 Subject: [PATCH 2/3] readd missing sizeSoFar assignment --- clang/lib/CIR/CodeGen/CIRGenExprConstant.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/clang/lib/CIR/CodeGen/CIRGenExprConstant.cpp b/clang/lib/CIR/CodeGen/CIRGenExprConstant.cpp index f577e23d7093e..89e9ec4e9d613 100644 --- a/clang/lib/CIR/CodeGen/CIRGenExprConstant.cpp +++ b/clang/lib/CIR/CodeGen/CIRGenExprConstant.cpp @@ -607,6 +607,7 @@ bool ConstRecordBuilder::applyZeroInitPadding(const ASTRecordLayout &layout, allowOverwrite)) return false; } + sizeSoFar = totalSize; return true; } From 8956e7c0174aef3e6765f95129be826baf0ccd58 Mon Sep 17 00:00:00 2001 From: Morris Hafner Date: Thu, 9 Oct 2025 18:13:52 +0200 Subject: [PATCH 3/3] indentation --- .../CIR/CodeGen/record-zero-init-padding.c | 28 +++++++++---------- 1 file changed, 14 insertions(+), 14 deletions(-) diff --git a/clang/test/CIR/CodeGen/record-zero-init-padding.c b/clang/test/CIR/CodeGen/record-zero-init-padding.c index 240dabba3c5d9..f131c9bbd069f 100644 --- a/clang/test/CIR/CodeGen/record-zero-init-padding.c +++ b/clang/test/CIR/CodeGen/record-zero-init-padding.c @@ -6,32 +6,32 @@ // RUN: FileCheck --input-file=%t.ll %s -check-prefix=OGCG struct padding_after_field { - char c; - int i; + char c; + int i; }; struct bitfield_with_padding { - unsigned int a : 3; - unsigned int b : 5; - int c; + unsigned int a : 3; + unsigned int b : 5; + int c; }; struct tail_padding { - int a; - char b; + int a; + char b; }; struct multiple_padding { - char a; - short b; - long long c; + char a; + short b; + long long c; }; void test_zero_init_padding(void) { - static const struct padding_after_field paf = {1, 42}; - static const struct bitfield_with_padding bfp = {1, 2, 99}; - static const struct tail_padding tp = {10, 20}; - static const struct multiple_padding mp = {5, 10, 100}; + static const struct padding_after_field paf = {1, 42}; + static const struct bitfield_with_padding bfp = {1, 2, 99}; + static const struct tail_padding tp = {10, 20}; + static const struct multiple_padding mp = {5, 10, 100}; } // Type definitions for anonymous structs with padding