[clang] [clang][CodeGen] Zero init unspecified fields in initializers in C (PR #97121)
via cfe-commits
cfe-commits at lists.llvm.org
Thu Aug 29 14:34:44 PDT 2024
https://github.com/yabinc updated https://github.com/llvm/llvm-project/pull/97121
>From d7e0361ed91032154aafee3cf2f9a7cdc233a395 Mon Sep 17 00:00:00 2001
From: Yabin Cui <yabinc at google.com>
Date: Thu, 27 Jun 2024 17:11:52 -0700
Subject: [PATCH 1/2] [clang][CodeGen] Zero init unspecified fields in
initializers in C (#97459)
When an initializer is provided to a variable, the Linux kernel relied
on the compiler to zero-initialize unspecified fields, as clarified in
https://www.spinics.net/lists/netdev/msg1007244.html.
But clang doesn't guarantee this:
1. For a union type, if an empty initializer is given, clang only
initializes bytes for the first field, left bytes for other (larger)
fields are marked as undef. Accessing those undef bytes can lead
to undefined behaviors.
2. For a union type, if an initializer explicitly sets a field, left
bytes for other (larger) fields are marked as undef.
3. When an initializer is given, clang doesn't zero initialize padding.
So this patch makes the following change:
1. In C, when an initializer is provided for a variable, zero-initialize
undef and padding fields in the initializer.
2. Document the change in LanguageExtensions.rst.
As suggested in https://github.com/llvm/llvm-project/issues/78034#issuecomment-2183437928,
the change isn't required by C23, but it's standards conforming to do
so.
---
clang/docs/LanguageExtensions.rst | 21 ++
clang/lib/CodeGen/CGExprAgg.cpp | 50 ++++
clang/lib/CodeGen/CGExprConstant.cpp | 140 ++++++++++-
...07-22-bitfield-init-after-zero-len-array.c | 2 +-
clang/test/CodeGen/2008-08-07-AlignPadding1.c | 4 +-
.../CodeGen/2009-06-14-anonymous-union-init.c | 4 +-
clang/test/CodeGen/64bit-swiftcall.c | 12 +-
clang/test/CodeGen/arm-swiftcall.c | 4 +-
clang/test/CodeGen/const-init.c | 4 +-
clang/test/CodeGen/decl.c | 4 +-
clang/test/CodeGen/designated-initializers.c | 12 +-
clang/test/CodeGen/ext-int.c | 18 +-
clang/test/CodeGen/flexible-array-init.c | 24 +-
clang/test/CodeGen/global-init.c | 2 +-
clang/test/CodeGen/init.c | 19 --
.../linux-kernel-struct-union-initializer.c | 219 ++++++++++++++++++
.../linux-kernel-struct-union-initializer2.c | 75 ++++++
clang/test/CodeGen/mingw-long-double.c | 9 +-
clang/test/CodeGen/mms-bitfields.c | 4 +-
clang/test/CodeGen/union-init2.c | 4 +-
clang/test/CodeGen/windows-swiftcall.c | 12 +-
.../CodeGenObjC/designated-initializers.m | 2 +-
22 files changed, 551 insertions(+), 94 deletions(-)
create mode 100644 clang/test/CodeGen/linux-kernel-struct-union-initializer.c
create mode 100644 clang/test/CodeGen/linux-kernel-struct-union-initializer2.c
diff --git a/clang/docs/LanguageExtensions.rst b/clang/docs/LanguageExtensions.rst
index 4679dbb68b25e1..6eebb209df09f9 100644
--- a/clang/docs/LanguageExtensions.rst
+++ b/clang/docs/LanguageExtensions.rst
@@ -5819,3 +5819,24 @@ specify the starting offset to begin embedding from. The resources is treated
as being empty if the specified offset is larger than the number of bytes in
the resource. The offset will be applied *before* any ``limit`` parameters are
applied.
+
+Union and aggregate initialization in C
+=======================================
+
+In C23 (N2900), when an object is initialized from initializer ``= {}``, all
+elements of arrays, all members of structs, and the first members of unions are
+empty-initialized recursively. In addition, all padding bits are initialized to
+zero.
+
+Clang guarantees the following behaviors:
+
+* ``1:`` Clang supports initializer ``= {}`` mentioned above in all C
+ standards.
+
+* ``2:`` When unions are initialized from initializer ``= {}``, bytes outside
+ of the first members of unions are also initialized to zero.
+
+* ``3:`` When unions, structures and arrays are initialized from initializer
+ ``= { initializer-list }``, all members not explicitly initialized in
+ the initializer list are empty-initialized recursively. In addition, all
+ padding bits are initialized to zero.
diff --git a/clang/lib/CodeGen/CGExprAgg.cpp b/clang/lib/CodeGen/CGExprAgg.cpp
index d9f44f4be617e5..49648130a83e62 100644
--- a/clang/lib/CodeGen/CGExprAgg.cpp
+++ b/clang/lib/CodeGen/CGExprAgg.cpp
@@ -1717,6 +1717,9 @@ void AggExprEmitter::VisitCXXParenListOrInitListExpr(
// Prepare a 'this' for CXXDefaultInitExprs.
CodeGenFunction::FieldConstructionScope FCS(CGF, Dest.getAddress());
+ // See comment in getPadding() in CGExprConstant.cpp.
+ bool ZeroInitPadding = !CGF.getLangOpts().CPlusPlus && !Dest.isZeroed();
+
if (record->isUnion()) {
// Only initialize one field of a union. The field itself is
// specified by the initializer list.
@@ -1745,13 +1748,48 @@ void AggExprEmitter::VisitCXXParenListOrInitListExpr(
// Default-initialize to null.
EmitNullInitializationToLValue(FieldLoc);
}
+ if (ZeroInitPadding) {
+ CharUnits TotalSize =
+ Dest.getPreferredSize(CGF.getContext(), DestLV.getType());
+ CharUnits FieldSize =
+ CGF.getContext().getTypeSizeInChars(FieldLoc.getType());
+ if (FieldSize < TotalSize) {
+ CharUnits LeftSize = TotalSize - FieldSize;
+ llvm::Constant *LeftSizeVal =
+ CGF.Builder.getInt64(LeftSize.getQuantity());
+ Address BaseLoc = Dest.getAddress().withElementType(CGF.Int8Ty);
+ Address LeftLoc =
+ CGF.Builder.CreateConstGEP(BaseLoc, LeftSize.getQuantity());
+ CGF.Builder.CreateMemSet(LeftLoc, CGF.Builder.getInt8(0), LeftSizeVal,
+ false);
+ }
+ }
return;
}
// Here we iterate over the fields; this makes it simpler to both
// default-initialize fields and skip over unnamed fields.
+ const ASTRecordLayout &Layout = CGF.getContext().getASTRecordLayout(record);
+ Address BaseLoc = Dest.getAddress().withElementType(CGF.Int8Ty);
+ CharUnits SizeSoFar = CharUnits::Zero();
for (const auto *field : record->fields()) {
+ if (ZeroInitPadding) {
+ unsigned FieldNo = field->getFieldIndex();
+ CharUnits Offset =
+ CGF.getContext().toCharUnitsFromBits(Layout.getFieldOffset(FieldNo));
+ if (SizeSoFar < Offset) {
+ llvm::Constant *PaddingSizeVal =
+ CGF.Builder.getInt64((Offset - SizeSoFar).getQuantity());
+ Address PaddingLoc =
+ CGF.Builder.CreateConstGEP(BaseLoc, SizeSoFar.getQuantity());
+ CGF.Builder.CreateMemSet(PaddingLoc, CGF.Builder.getInt8(0),
+ PaddingSizeVal, false);
+ }
+ CharUnits FieldSize =
+ CGF.getContext().getTypeSizeInChars(field->getType());
+ SizeSoFar = Offset + FieldSize;
+ }
// We're done once we hit the flexible array member.
if (field->getType()->isIncompleteArrayType())
break;
@@ -1793,6 +1831,18 @@ void AggExprEmitter::VisitCXXParenListOrInitListExpr(
}
}
}
+ if (ZeroInitPadding) {
+ CharUnits TotalSize =
+ Dest.getPreferredSize(CGF.getContext(), DestLV.getType());
+ if (SizeSoFar < TotalSize) {
+ llvm::Constant *PaddingSizeVal =
+ CGF.Builder.getInt64((TotalSize - SizeSoFar).getQuantity());
+ Address PaddingLoc =
+ CGF.Builder.CreateConstGEP(BaseLoc, SizeSoFar.getQuantity());
+ CGF.Builder.CreateMemSet(PaddingLoc, CGF.Builder.getInt8(0),
+ PaddingSizeVal, false);
+ }
+ }
}
void AggExprEmitter::VisitArrayInitLoopExpr(const ArrayInitLoopExpr *E,
diff --git a/clang/lib/CodeGen/CGExprConstant.cpp b/clang/lib/CodeGen/CGExprConstant.cpp
index f22321f0e738a1..56c05717efe8f6 100644
--- a/clang/lib/CodeGen/CGExprConstant.cpp
+++ b/clang/lib/CodeGen/CGExprConstant.cpp
@@ -42,6 +42,66 @@ using namespace CodeGen;
namespace {
class ConstExprEmitter;
+llvm::Constant *getPadding(const CodeGenModule &CGM, CharUnits PadSize) {
+ if (!CGM.getLangOpts().CPlusPlus) {
+ // In C23 (N3096) $6.7.10:
+ // """
+ // If any object is initialized with an empty iniitializer, then it is
+ // subject to default initialization:
+ // - if it is an aggregate, every member is initialized (recursively)
+ // according to these rules, and any padding is initialized to zero bits;
+ // - if it is a union, the first named member is initialized (recursively)
+ // according to these rules, and any padding is initialized to zero bits.
+ //
+ // If the aggregate or union contains elements or members that are
+ // aggregates or unions, these rules apply recursively to the subaggregates
+ // or contained unions.
+ //
+ // If there are fewer initializers in a brace-enclosed list than there are
+ // elements or members of an aggregate, or fewer characters in a string
+ // literal used to initialize an array of known size than there are elements
+ // in the array, the remainder of the aggregate is subject to default
+ // initialization.
+ // """
+ //
+ // From my understanding, the standard is ambiguous in the following two
+ // areas:
+ // 1. For a union type with empty initializer, if the first named member is
+ // not the largest member, then the bytes comes after the first named member
+ // but before padding are left unspecified. An example is:
+ // union U { int a; long long b;};
+ // union U u = {}; // The first 4 bytes are 0, but 4-8 bytes are left
+ // unspecified.
+ //
+ // 2. It only mentions padding for empty initializer, but doesn't mention
+ // padding for a non empty initialization list. And if the aggregation or
+ // union contains elements or members that are aggregates or unions, and
+ // some are non empty initializers, while others are empty initiailizers,
+ // the padding initialization is unclear. An example is:
+ // struct S1 { int a; long long b; };
+ // struct S2 { char c; struct S1 s1; };
+ // // The values for paddings between s2.c and s2.s1.a, between s2.s1.a
+ // and s2.s1.b are unclear.
+ // struct S2 s2 = { 'c' };
+ //
+ // Here we choose to zero initiailize left bytes of a union type. Because
+ // projects like the Linux kernel are relying on this behavior. If we don't
+ // explicitly zero initialize them, the undef values can be optimized to
+ // return gabage data. We also choose to zero initialize paddings for
+ // aggregates and unions, no matter they are initialized by empty
+ // initializers or non empty initializers. This can provide a consistent
+ // behavior. So projects like the Linux kernel can rely on it.
+ if (PadSize == CharUnits::One())
+ return llvm::ConstantInt::get(CGM.CharTy, 0);
+ llvm::Type *Ty = llvm::ArrayType::get(CGM.CharTy, PadSize.getQuantity());
+ return llvm::ConstantAggregateZero::get(Ty);
+ }
+ llvm::Type *Ty = CGM.CharTy;
+ if (PadSize > CharUnits::One())
+ Ty = llvm::ArrayType::get(Ty, PadSize.getQuantity());
+ return llvm::UndefValue::get(Ty);
+}
+
struct ConstantAggregateBuilderUtils {
CodeGenModule &CGM;
@@ -61,10 +121,7 @@ struct ConstantAggregateBuilderUtils {
}
llvm::Constant *getPadding(CharUnits PadSize) const {
- llvm::Type *Ty = CGM.CharTy;
- if (PadSize > CharUnits::One())
- Ty = llvm::ArrayType::get(Ty, PadSize.getQuantity());
- return llvm::UndefValue::get(Ty);
+ return ::getPadding(CGM, PadSize);
}
llvm::Constant *getZeroes(CharUnits ZeroSize) const {
@@ -715,6 +772,11 @@ bool ConstStructBuilder::Build(const InitListExpr *ILE, bool AllowOverwrite) {
if (CXXRD->getNumBases())
return false;
+ // See comment in getPadding().
+ bool ZeroInitPadding = !CGM.getLangOpts().CPlusPlus;
+ CharUnits SizeSoFar = CharUnits::Zero();
+ bool IsFlexibleArray = false;
+
for (FieldDecl *Field : RD->fields()) {
++FieldNo;
@@ -732,8 +794,16 @@ bool ConstStructBuilder::Build(const InitListExpr *ILE, bool AllowOverwrite) {
const Expr *Init = nullptr;
if (ElementNo < ILE->getNumInits())
Init = ILE->getInit(ElementNo++);
- if (isa_and_nonnull<NoInitExpr>(Init))
+ if (isa_and_nonnull<NoInitExpr>(Init)) {
+ if (ZeroInitPadding) {
+ CharUnits Offset = CGM.getContext().toCharUnitsFromBits(
+ Layout.getFieldOffset(FieldNo));
+ CharUnits FieldSize =
+ CGM.getContext().getTypeSizeInChars(Field->getType());
+ SizeSoFar = Offset + FieldSize;
+ }
continue;
+ }
// Zero-sized fields are not emitted, but their initializers may still
// prevent emission of this struct as a constant.
@@ -743,6 +813,19 @@ bool ConstStructBuilder::Build(const InitListExpr *ILE, bool AllowOverwrite) {
continue;
}
+ if (ZeroInitPadding) {
+ CharUnits Offset =
+ CGM.getContext().toCharUnitsFromBits(Layout.getFieldOffset(FieldNo));
+ if (SizeSoFar < Offset)
+ if (!AppendBytes(SizeSoFar, getPadding(CGM, Offset - SizeSoFar),
+ AllowOverwrite))
+ return false;
+ CharUnits FieldSize =
+ CGM.getContext().getTypeSizeInChars(Field->getType());
+ SizeSoFar = Offset + FieldSize;
+ IsFlexibleArray = FieldSize.isZero();
+ }
+
// When emitting a DesignatedInitUpdateExpr, a nested InitListExpr
// represents additional overwriting of our current constant value, and not
// a new constant to emit independently.
@@ -768,6 +851,10 @@ bool ConstStructBuilder::Build(const InitListExpr *ILE, bool AllowOverwrite) {
if (!EltInit)
return false;
+ if (ZeroInitPadding && IsFlexibleArray)
+ SizeSoFar += CharUnits::fromQuantity(
+ CGM.getDataLayout().getTypeAllocSize(EltInit->getType()));
+
if (!Field->isBitField()) {
// Handle non-bitfield members.
if (!AppendField(Field, Layout.getFieldOffset(FieldNo), EltInit,
@@ -785,6 +872,14 @@ bool ConstStructBuilder::Build(const InitListExpr *ILE, bool AllowOverwrite) {
}
}
+ if (ZeroInitPadding) {
+ CharUnits TotalSize = Layout.getSize();
+ if (SizeSoFar < TotalSize)
+ if (!AppendBytes(SizeSoFar, getPadding(CGM, TotalSize - SizeSoFar),
+ AllowOverwrite))
+ return false;
+ }
+
return true;
}
@@ -849,6 +944,9 @@ bool ConstStructBuilder::Build(const APValue &Val, const RecordDecl *RD,
unsigned FieldNo = 0;
uint64_t OffsetBits = CGM.getContext().toBits(Offset);
+ // See comment in getPadding().
+ bool ZeroInitPadding = !CGM.getLangOpts().CPlusPlus;
+ CharUnits SizeSoFar = CharUnits::Zero();
bool AllowOverwrite = false;
for (RecordDecl::field_iterator Field = RD->field_begin(),
@@ -870,6 +968,21 @@ bool ConstStructBuilder::Build(const APValue &Val, const RecordDecl *RD,
if (!EltInit)
return false;
+ if (ZeroInitPadding) {
+ CharUnits Offset =
+ CGM.getContext().toCharUnitsFromBits(Layout.getFieldOffset(FieldNo));
+ if (SizeSoFar < Offset)
+ if (!AppendBytes(SizeSoFar, getPadding(CGM, Offset - SizeSoFar),
+ AllowOverwrite))
+ return false;
+ CharUnits FieldSize =
+ CGM.getContext().getTypeSizeInChars(Field->getType());
+ if (FieldSize.isZero())
+ FieldSize = CharUnits::fromQuantity(
+ CGM.getDataLayout().getTypeAllocSize(EltInit->getType()));
+ SizeSoFar = Offset + FieldSize;
+ }
+
if (!Field->isBitField()) {
// Handle non-bitfield members.
if (!AppendField(*Field, Layout.getFieldOffset(FieldNo) + OffsetBits,
@@ -886,6 +999,13 @@ bool ConstStructBuilder::Build(const APValue &Val, const RecordDecl *RD,
return false;
}
}
+ if (ZeroInitPadding) {
+ CharUnits TotalSize = Layout.getSize();
+ if (SizeSoFar < TotalSize)
+ if (!AppendBytes(SizeSoFar, getPadding(CGM, TotalSize - SizeSoFar),
+ AllowOverwrite))
+ return false;
+ }
return true;
}
@@ -1127,12 +1247,10 @@ class ConstExprEmitter
assert(CurSize <= TotalSize && "Union size mismatch!");
if (unsigned NumPadBytes = TotalSize - CurSize) {
- llvm::Type *Ty = CGM.CharTy;
- if (NumPadBytes > 1)
- Ty = llvm::ArrayType::get(Ty, NumPadBytes);
-
- Elts.push_back(llvm::UndefValue::get(Ty));
- Types.push_back(Ty);
+ llvm::Constant *Padding =
+ getPadding(CGM, CharUnits::fromQuantity(NumPadBytes));
+ Elts.push_back(Padding);
+ Types.push_back(Padding->getType());
}
llvm::StructType *STy = llvm::StructType::get(VMContext, Types, false);
diff --git a/clang/test/CodeGen/2008-07-22-bitfield-init-after-zero-len-array.c b/clang/test/CodeGen/2008-07-22-bitfield-init-after-zero-len-array.c
index b72d689659e602..b639734ef5d4b7 100644
--- a/clang/test/CodeGen/2008-07-22-bitfield-init-after-zero-len-array.c
+++ b/clang/test/CodeGen/2008-07-22-bitfield-init-after-zero-len-array.c
@@ -8,4 +8,4 @@ struct et7 {
52,
};
-// CHECK: @yv7 ={{.*}} global %struct.et7 { [0 x float] zeroinitializer, i8 52 }
+// CHECK: @yv7 ={{.*}} global { [0 x float], i8, [3 x i8] } { [0 x float] zeroinitializer, i8 52, [3 x i8] zeroinitializer }
diff --git a/clang/test/CodeGen/2008-08-07-AlignPadding1.c b/clang/test/CodeGen/2008-08-07-AlignPadding1.c
index 17e88ce02659f0..d69cbc22cc1dfb 100644
--- a/clang/test/CodeGen/2008-08-07-AlignPadding1.c
+++ b/clang/test/CodeGen/2008-08-07-AlignPadding1.c
@@ -20,9 +20,9 @@ struct gc_generation {
#define GEN_HEAD(n) (&generations[n].head)
-// The idea is that there are 6 undefs in this structure initializer to cover
+// The idea is that there are 6 zeroinitializers in this structure initializer to cover
// the padding between elements.
-// CHECK: @generations ={{.*}} global [3 x %struct.gc_generation] [%struct.gc_generation { %union._gc_head { %struct.anon { ptr @generations, ptr @generations, i64 0 }, [8 x i8] undef }, i32 700, i32 0, [8 x i8] undef }, %struct.gc_generation { %union._gc_head { %struct.anon { ptr getelementptr (i8, ptr @generations, i64 48), ptr getelementptr (i8, ptr @generations, i64 48), i64 0 }, [8 x i8] undef }, i32 10, i32 0, [8 x i8] undef }, %struct.gc_generation { %union._gc_head { %struct.anon { ptr getelementptr (i8, ptr @generations, i64 96), ptr getelementptr (i8, ptr @generations, i64 96), i64 0 }, [8 x i8] undef }, i32 10, i32 0, [8 x i8] undef }]
+// CHECK: @generations ={{.*}} global [3 x %struct.gc_generation] [%struct.gc_generation { %union._gc_head { %struct.anon { ptr @generations, ptr @generations, i64 0 }, [8 x i8] zeroinitializer }, i32 700, i32 0, [8 x i8] zeroinitializer }, %struct.gc_generation { %union._gc_head { %struct.anon { ptr getelementptr (i8, ptr @generations, i64 48), ptr getelementptr (i8, ptr @generations, i64 48), i64 0 }, [8 x i8] zeroinitializer }, i32 10, i32 0, [8 x i8] zeroinitializer }, %struct.gc_generation { %union._gc_head { %struct.anon { ptr getelementptr (i8, ptr @generations, i64 96), ptr getelementptr (i8, ptr @generations, i64 96), i64 0 }, [8 x i8] zeroinitializer }, i32 10, i32 0, [8 x i8] zeroinitializer }]
/* linked lists of container objects */
struct gc_generation generations[3] = {
/* PyGC_Head, threshold, count */
diff --git a/clang/test/CodeGen/2009-06-14-anonymous-union-init.c b/clang/test/CodeGen/2009-06-14-anonymous-union-init.c
index 13f6357f7966d9..a4375d7868f01d 100644
--- a/clang/test/CodeGen/2009-06-14-anonymous-union-init.c
+++ b/clang/test/CodeGen/2009-06-14-anonymous-union-init.c
@@ -7,7 +7,7 @@ struct sysfs_dirent {
};
struct sysfs_dirent sysfs_root = { {}, 16877 };
-// CHECK: @sysfs_root = {{.*}}global %struct.sysfs_dirent { %union.anon zeroinitializer, i16 16877 }
+// CHECK: @sysfs_root = {{.*}}global { %union.anon, i16, [2 x i8] } { %union.anon zeroinitializer, i16 16877, [2 x i8] zeroinitializer }
struct Foo {
union { struct empty {} x; };
@@ -16,4 +16,4 @@ struct Foo {
struct Foo foo = { {}, 16877 };
// EMPTY: @foo = {{.*}}global %struct.Foo { i16 16877 }
-// EMPTY-MSVC: @foo = {{.*}}global %struct.Foo { [4 x i8] undef, i16 16877 }
+// EMPTY-MSVC: @foo = {{.*}}global %struct.Foo { [4 x i8] zeroinitializer, i16 16877 }
diff --git a/clang/test/CodeGen/64bit-swiftcall.c b/clang/test/CodeGen/64bit-swiftcall.c
index 7af65ccf556a81..7f8aa02d97ce1f 100644
--- a/clang/test/CodeGen/64bit-swiftcall.c
+++ b/clang/test/CodeGen/64bit-swiftcall.c
@@ -14,8 +14,6 @@
// CHECK-DAG: %struct.atomic_padded = type { { %struct.packed, [7 x i8] } }
// CHECK-DAG: %struct.packed = type <{ i64, i8 }>
-//
-// CHECK: [[STRUCT2_RESULT:@.*]] = private {{.*}} constant [[STRUCT2_TYPE:%.*]] { i32 0, i8 0, i8 undef, i8 0, i32 0, i32 0 }
/*****************************************************************************/
/****************************** PARAMETER ABIS *******************************/
@@ -162,8 +160,8 @@ typedef struct {
} struct_2;
TEST(struct_2);
// CHECK-LABEL: define{{.*}} swiftcc { i64, i64 } @return_struct_2() {{.*}}{
-// CHECK: [[RET:%.*]] = alloca [[STRUCT2_TYPE]], align 4
-// CHECK: call void @llvm.memcpy{{.*}}({{.*}}[[RET]], {{.*}}[[STRUCT2_RESULT]]
+// CHECK: [[RET:%.*]] = alloca [[STRUCT2:%.*]], align 4
+// CHECK: call void @llvm.memset
// CHECK: [[GEP0:%.*]] = getelementptr inbounds nuw { i64, i64 }, ptr [[RET]], i32 0, i32 0
// CHECK: [[T0:%.*]] = load i64, ptr [[GEP0]], align 4
// CHECK: [[GEP1:%.*]] = getelementptr inbounds nuw { i64, i64 }, ptr [[RET]], i32 0, i32 1
@@ -173,7 +171,7 @@ TEST(struct_2);
// CHECK: ret { i64, i64 } [[R1]]
// CHECK: }
// CHECK-LABEL: define{{.*}} swiftcc void @take_struct_2(i64 %0, i64 %1) {{.*}}{
-// CHECK: [[V:%.*]] = alloca [[STRUCT:%.*]], align 4
+// CHECK: [[V:%.*]] = alloca [[STRUCT2]], align 4
// CHECK: [[GEP0:%.*]] = getelementptr inbounds nuw { i64, i64 }, ptr [[V]], i32 0, i32 0
// CHECK: store i64 %0, ptr [[GEP0]], align 4
// CHECK: [[GEP1:%.*]] = getelementptr inbounds nuw { i64, i64 }, ptr [[V]], i32 0, i32 1
@@ -181,7 +179,7 @@ TEST(struct_2);
// CHECK: ret void
// CHECK: }
// CHECK-LABEL: define{{.*}} void @test_struct_2() {{.*}} {
-// CHECK: [[TMP:%.*]] = alloca [[STRUCT2_TYPE]], align 4
+// CHECK: [[TMP:%.*]] = alloca [[STRUCT2]], align 4
// CHECK: [[CALL:%.*]] = call swiftcc { i64, i64 } @return_struct_2()
// CHECK: [[GEP:%.*]] = getelementptr inbounds nuw {{.*}} [[TMP]], i32 0, i32 0
// CHECK: [[T0:%.*]] = extractvalue { i64, i64 } [[CALL]], 0
@@ -254,7 +252,7 @@ typedef union {
TEST(union_het_fp)
// CHECK-LABEL: define{{.*}} swiftcc i64 @return_union_het_fp()
// CHECK: [[RET:%.*]] = alloca [[UNION:%.*]], align 8
-// CHECK: call void @llvm.memcpy{{.*}}(ptr align 8 [[RET]]
+// CHECK: call void @llvm.memset{{.*}}(ptr align 8 [[RET]]
// CHECK: [[GEP:%.*]] = getelementptr inbounds nuw { i64 }, ptr [[RET]], i32 0, i32 0
// CHECK: [[R0:%.*]] = load i64, ptr [[GEP]], align 8
// CHECK: ret i64 [[R0]]
diff --git a/clang/test/CodeGen/arm-swiftcall.c b/clang/test/CodeGen/arm-swiftcall.c
index ec0e3867909a86..677b878c6765da 100644
--- a/clang/test/CodeGen/arm-swiftcall.c
+++ b/clang/test/CodeGen/arm-swiftcall.c
@@ -172,7 +172,7 @@ typedef struct {
TEST(struct_2);
// CHECK-LABEL: define{{.*}} @return_struct_2()
// CHECK: [[RET:%.*]] = alloca [[REC:%.*]], align 4
-// CHECK: @llvm.memcpy
+// CHECK: @llvm.memset
// CHECK: [[T0:%.*]] = getelementptr inbounds nuw [[AGG:{ i32, i32, float, float }]], ptr [[RET]], i32 0, i32 0
// CHECK: [[FIRST:%.*]] = load i32, ptr [[T0]], align 4
// CHECK: [[T0:%.*]] = getelementptr inbounds nuw [[AGG]], ptr [[RET]], i32 0, i32 1
@@ -274,7 +274,7 @@ typedef union {
TEST(union_het_fp)
// CHECK-LABEL: define{{.*}} @return_union_het_fp()
// CHECK: [[RET:%.*]] = alloca [[REC:%.*]], align {{(4|8)}}
-// CHECK: @llvm.memcpy
+// CHECK: @llvm.memset
// CHECK: [[T0:%.*]] = getelementptr inbounds nuw [[AGG:{ i32, i32 }]], ptr [[RET]], i32 0, i32 0
// CHECK: [[FIRST:%.*]] = load i32, ptr [[T0]], align {{(4|8)}}
// CHECK: [[T0:%.*]] = getelementptr inbounds nuw [[AGG]], ptr [[RET]], i32 0, i32 1
diff --git a/clang/test/CodeGen/const-init.c b/clang/test/CodeGen/const-init.c
index ad3e9551199ac2..fc973cb983a80a 100644
--- a/clang/test/CodeGen/const-init.c
+++ b/clang/test/CodeGen/const-init.c
@@ -170,7 +170,7 @@ void g30(void) {
int : 1;
int x;
} a = {};
- // CHECK: @g30.a = internal global %struct.anon.1 <{ i8 undef, i32 0 }>, align 1
+ // CHECK: @g30.a = internal global %struct.anon.1 zeroinitializer, align 1
#pragma pack()
}
@@ -182,7 +182,7 @@ void g31(void) {
short z;
} a = {23122, -12312731, -312};
#pragma pack()
- // CHECK: @g31.a = internal global %struct.anon.2 { i16 23122, i32 -12312731, i16 -312 }, align 4
+ // CHECK: @g31.a = internal global { i16, [2 x i8], i32, i16, [2 x i8] } { i16 23122, [2 x i8] zeroinitializer, i32 -12312731, i16 -312, [2 x i8] zeroinitializer }, align 4
}
// Clang should evaluate this in constant context, so floating point mode should
diff --git a/clang/test/CodeGen/decl.c b/clang/test/CodeGen/decl.c
index a63846b3223da4..97446781fdbd2b 100644
--- a/clang/test/CodeGen/decl.c
+++ b/clang/test/CodeGen/decl.c
@@ -2,10 +2,10 @@
// CHECK: @test1.x = internal constant [12 x i32] [i32 1
// CHECK: @__const.test2.x = private unnamed_addr constant [13 x i32] [i32 1,
-// CHECK: @test5w = {{(dso_local )?}}global { i32, [4 x i8] } { i32 2, [4 x i8] undef }
+// CHECK: @test5w = {{(dso_local )?}}global { i32, [4 x i8] } { i32 2, [4 x i8] zeroinitializer }
// CHECK: @test5y = {{(dso_local )?}}global { double } { double 7.300000e+0{{[0]*}}1 }
-// CHECK: @__const.test6.x = private unnamed_addr constant %struct.SelectDest { i8 1, i8 2, i32 3, i32 0 }
+// CHECK: @__const.test6.x = private unnamed_addr constant { i8, i8, [2 x i8], i32, i32 } { i8 1, i8 2, [2 x i8] zeroinitializer, i32 3, i32 0 }
// CHECK: @test7 = {{(dso_local )?}}global [2 x %struct.test7s] [%struct.test7s { i32 1, i32 2 }, %struct.test7s { i32 4, i32 0 }]
diff --git a/clang/test/CodeGen/designated-initializers.c b/clang/test/CodeGen/designated-initializers.c
index 620b1b90d25758..ac7860db43be77 100644
--- a/clang/test/CodeGen/designated-initializers.c
+++ b/clang/test/CodeGen/designated-initializers.c
@@ -8,7 +8,7 @@ struct foo {
// CHECK: @u ={{.*}} global %union.anon zeroinitializer
union { int i; float f; } u = { };
-// CHECK: @u2 ={{.*}} global { i32, [4 x i8] } { i32 0, [4 x i8] undef }
+// CHECK: @u2 ={{.*}} global { i32, [4 x i8] } zeroinitializer
union { int i; double f; } u2 = { };
// CHECK: @u3 ={{.*}} global %union.anon.1 zeroinitializer
@@ -62,22 +62,22 @@ struct overwrite_string_struct2 {
char L[6];
int M;
} overwrite_string2[] = { { { "foo" }, 1 }, [0].L[2] = 'x'};
-// CHECK: [6 x i8] c"fox\00\00\00", i32 1
+// CHECK: [6 x i8] c"fox\00\00\00", [2 x i8] zeroinitializer, i32 1
struct overwrite_string_struct3 {
char L[3];
int M;
} overwrite_string3[] = { { { "foo" }, 1 }, [0].L[2] = 'x'};
-// CHECK: [3 x i8] c"fox", i32 1
+// CHECK: [3 x i8] c"fox", i8 0, i32 1
struct overwrite_string_struct4 {
char L[3];
int M;
} overwrite_string4[] = { { { "foobar" }, 1 }, [0].L[2] = 'x'};
-// CHECK: [3 x i8] c"fox", i32 1
+// CHECK: [3 x i8] c"fox", i8 0, i32 1
struct overwrite_string_struct5 {
char L[6];
int M;
} overwrite_string5[] = { { { "foo" }, 1 }, [0].L[4] = 'y'};
-// CHECK: [6 x i8] c"foo\00y\00", i32 1
+// CHECK: [6 x i8] c"foo\00y\00", [2 x i8] zeroinitializer, i32 1
// CHECK: @u1 = {{.*}} { i32 65535 }
@@ -138,7 +138,7 @@ union_16644_t union_16644_instance_4[2] =
[1].b[1] = 4
};
-// CHECK: @lab ={{.*}} global { [4 x i8], i32 } { [4 x i8] undef, i32 123 }
+// CHECK: @lab ={{.*}} global { [4 x i8], i32 } { [4 x i8] zeroinitializer, i32 123 }
struct leading_anon_bitfield { int : 32; int n; } lab = { .n = 123 };
struct Base {
diff --git a/clang/test/CodeGen/ext-int.c b/clang/test/CodeGen/ext-int.c
index 714b7e122a706f..513e09deb4c4c9 100644
--- a/clang/test/CodeGen/ext-int.c
+++ b/clang/test/CodeGen/ext-int.c
@@ -16,7 +16,7 @@
unsigned _BitInt(1) GlobSize1 = 0;
// CHECK: @GlobSize1 = {{.*}}global i8 0
-// CHECK64: @__const.foo.A = private unnamed_addr constant { i32, [4 x i8], <{ i8, [23 x i8] }> } { i32 1, [4 x i8] undef, <{ i8, [23 x i8] }> <{ i8 -86, [23 x i8] zeroinitializer }> }, align 8
+// CHECK64: @__const.foo.A = private unnamed_addr constant { i32, [4 x i8], <{ i8, [23 x i8] }> } { i32 1, [4 x i8] zeroinitializer, <{ i8, [23 x i8] }> <{ i8 -86, [23 x i8] zeroinitializer }> }, align 8
// @BigGlob = global [40 x i8] c"\FF\FF\FF\FF\FF\FF\FF\FF\FF\FF\FF\FF\FF\FF\FF\FF\FF\FF\FF\FF\FF\FF\FF\FF\FF\FF\FF\FF\FF\FF\FF\FF\FF\FF\FF\FF\FF\FF\FF\FF", align 8
// CHECK64: @f.p = internal global <{ i8, i8, [22 x i8] }> <{ i8 16, i8 39, [22 x i8] zeroinitializer }>, align 8
@@ -91,8 +91,8 @@ int foo(int a) {
// CHECK64: %B2 = getelementptr inbounds nuw %struct.S1, ptr %B, i32 0, i32 2
// WIN32: %B2 = getelementptr inbounds nuw %struct.S1, ptr %B, i32 0, i32 2
// LIN32: %B2 = getelementptr inbounds nuw %struct.S1, ptr %B, i32 0, i32 1
- // CHECK: %0 = load i32, ptr %a.addr, align 4
- // CHECK: %conv = sext i32 %0 to i129
+ // CHECK: %[[V1:.+]] = load i32, ptr %a.addr, align 4
+ // CHECK: %conv = sext i32 %[[V1]] to i129
// CHECK64: storedv = sext i129 %conv to i192
// WIN32: storedv = sext i129 %conv to i192
// LIN32: storedv = sext i129 %conv to i160
@@ -102,12 +102,12 @@ int foo(int a) {
// CHECK64: %B3 = getelementptr inbounds nuw %struct.S1, ptr %A, i32 0, i32 2
// WIN32: %B3 = getelementptr inbounds nuw %struct.S1, ptr %A, i32 0, i32 2
// LIN32: %B3 = getelementptr inbounds nuw %struct.S1, ptr %A, i32 0, i32 1
- // CHECK64: %1 = load i192, ptr %B3, align 8
- // WIN32: %1 = load i192, ptr %B3, align 8
- // LIN32: %1 = load i160, ptr %B3, align 4
- // CHECK64: %loadedv = trunc i192 %1 to i129
- // WIN32: %loadedv = trunc i192 %1 to i129
- // LIN32: %loadedv = trunc i160 %1 to i129
+ // CHECK64: %[[V2:.+]] = load i192, ptr %B3, align 8
+ // WIN32: %[[V2:.+]] = load i192, ptr %B3, align 8
+ // LIN32: %[[V2:.+]] = load i160, ptr %B3, align 4
+ // CHECK64: %loadedv = trunc i192 %[[V2]] to i129
+ // WIN32: %loadedv = trunc i192 %[[V2]] to i129
+ // LIN32: %loadedv = trunc i160 %[[V2]] to i129
// CHECK: %conv4 = trunc i129 %loadedv to i32
struct S1 A = {1, 170};
struct S1 B = {1, a};
diff --git a/clang/test/CodeGen/flexible-array-init.c b/clang/test/CodeGen/flexible-array-init.c
index 15a30c15ac966e..17b520fe830942 100644
--- a/clang/test/CodeGen/flexible-array-init.c
+++ b/clang/test/CodeGen/flexible-array-init.c
@@ -14,11 +14,11 @@ struct { int y[]; } b1 = { { 14, 16 } };
// sizeof(c) == 8, so this global should be at least 8 bytes.
struct { int x; char c; char y[]; } c = { 1, 2, { 13, 15 } };
-// CHECK: @c ={{.*}} global { i32, i8, [2 x i8] } { i32 1, i8 2, [2 x i8] c"\0D\0F" }
+// CHECK: @c ={{.*}} global { i32, i8, [2 x i8], i8 } { i32 1, i8 2, [2 x i8] c"\0D\0F", i8 0 }
// sizeof(d) == 8, so this global should be at least 8 bytes.
struct __attribute((packed, aligned(4))) { char a; int x; char z[]; } d = { 1, 2, { 13, 15 } };
-// CHECK: @d ={{.*}} <{ i8, i32, [2 x i8], i8 }> <{ i8 1, i32 2, [2 x i8] c"\0D\0F", i8 undef }>,
+// CHECK: @d ={{.*}} <{ i8, i32, [2 x i8], i8 }> <{ i8 1, i32 2, [2 x i8] c"\0D\0F", i8 0 }>,
// This global needs 9 bytes to hold all the flexible array members.
struct __attribute((packed, aligned(4))) { char a; int x; char z[]; } e = { 1, 2, { 13, 15, 17, 19 } };
@@ -55,21 +55,21 @@ struct { int a; union { int b; short x[]; }; int c; int d; } hf = {1, 2, {}, 3};
// First member is the potential flexible array, initialization requires braces.
struct { int a; union { short x; int b; }; int c; int d; } i = {1, 2, {}, 3};
-// CHECK: @i = global { i32, { i16, [2 x i8] }, i32, i32 } { i32 1, { i16, [2 x i8] } { i16 2, [2 x i8] undef }, i32 0, i32 3 }
+// CHECK: @i = global { i32, { i16, [2 x i8] }, i32, i32 } { i32 1, { i16, [2 x i8] } { i16 2, [2 x i8] zeroinitializer }, i32 0, i32 3 }
struct { int a; union { short x[0]; int b; }; int c; int d; } i0 = {1, {}, 2, 3};
-// CHECK: @i0 = global { i32, { [0 x i16], [4 x i8] }, i32, i32 } { i32 1, { [0 x i16], [4 x i8] } { [0 x i16] zeroinitializer, [4 x i8] undef }, i32 2, i32 3 }
+// CHECK: @i0 = global { i32, { [0 x i16], [4 x i8] }, i32, i32 } { i32 1, { [0 x i16], [4 x i8] } zeroinitializer, i32 2, i32 3 }
struct { int a; union { short x[1]; int b; }; int c; int d; } i1 = {1, {2}, {}, 3};
-// CHECK: @i1 = global { i32, { [1 x i16], [2 x i8] }, i32, i32 } { i32 1, { [1 x i16], [2 x i8] } { [1 x i16] [i16 2], [2 x i8] undef }, i32 0, i32 3 }
+// CHECK: @i1 = global { i32, { [1 x i16], [2 x i8] }, i32, i32 } { i32 1, { [1 x i16], [2 x i8] } { [1 x i16] [i16 2], [2 x i8] zeroinitializer }, i32 0, i32 3 }
struct { int a; union { short x[]; int b; }; int c; int d; } i_f = {4, {}, {}, 6};
-// CHECK: @i_f = global { i32, { [0 x i16], [4 x i8] }, i32, i32 } { i32 4, { [0 x i16], [4 x i8] } { [0 x i16] zeroinitializer, [4 x i8] undef }, i32 0, i32 6 }
+// CHECK: @i_f = global { i32, { [0 x i16], [4 x i8] }, i32, i32 } { i32 4, { [0 x i16], [4 x i8] } zeroinitializer, i32 0, i32 6 }
// Named initializers; order doesn't matter.
struct { int a; union { int b; short x; }; int c; int d; } hn = {.a = 1, .x = 2, .c = 3};
-// CHECK: @hn = global { i32, { i16, [2 x i8] }, i32, i32 } { i32 1, { i16, [2 x i8] } { i16 2, [2 x i8] undef }, i32 3, i32 0 }
+// CHECK: @hn = global { i32, { i16, [2 x i8] }, i32, i32 } { i32 1, { i16, [2 x i8] } { i16 2, [2 x i8] zeroinitializer }, i32 3, i32 0 }
struct { int a; union { int b; short x[0]; }; int c; int d; } hn0 = {.a = 1, .x = {2}, .c = 3};
-// CHECK: @hn0 = global { i32, { [0 x i16], [4 x i8] }, i32, i32 } { i32 1, { [0 x i16], [4 x i8] } { [0 x i16] zeroinitializer, [4 x i8] undef }, i32 3, i32 0 }
+// CHECK: @hn0 = global { i32, { [0 x i16], [4 x i8] }, i32, i32 } { i32 1, { [0 x i16], [4 x i8] } zeroinitializer, i32 3, i32 0 }
struct { int a; union { int b; short x[1]; }; int c; int d; } hn1 = {.a = 1, .x = {2}, .c = 3};
-// CHECK: @hn1 = global { i32, { [1 x i16], [2 x i8] }, i32, i32 } { i32 1, { [1 x i16], [2 x i8] } { [1 x i16] [i16 2], [2 x i8] undef }, i32 3, i32 0 }
+// CHECK: @hn1 = global { i32, { [1 x i16], [2 x i8] }, i32, i32 } { i32 1, { [1 x i16], [2 x i8] } { [1 x i16] [i16 2], [2 x i8] zeroinitializer }, i32 3, i32 0 }
struct { char a[]; } empty_struct = {};
// CHECK: @empty_struct ={{.*}} global %struct.anon{{.*}} zeroinitializer, align 1
@@ -96,10 +96,10 @@ union { char a[]; } only_in_union0 = {0};
// CHECK: @only_in_union0 = global { [1 x i8] } zeroinitializer, align 1
union { char a[]; int b; } first_in_union = {};
-// CHECK: @first_in_union = global { [0 x i8], [4 x i8] } { [0 x i8] zeroinitializer, [4 x i8] undef }, align 4
+// CHECK: @first_in_union = global { [0 x i8], [4 x i8] } zeroinitializer, align 4
union { char a[]; int b; } first_in_union0 = {0};
-// CHECK: @first_in_union0 = global { [1 x i8], [3 x i8] } { [1 x i8] zeroinitializer, [3 x i8] undef }, align 4
+// CHECK: @first_in_union0 = global { [1 x i8], [3 x i8] } zeroinitializer, align 4
union { char a[]; int b; } first_in_union123 = { {1, 2, 3} };
-// CHECK: @first_in_union123 = global { [3 x i8], i8 } { [3 x i8] c"\01\02\03", i8 undef }, align 4
+// CHECK: @first_in_union123 = global { [3 x i8], i8 } { [3 x i8] c"\01\02\03", i8 0 }, align 4
diff --git a/clang/test/CodeGen/global-init.c b/clang/test/CodeGen/global-init.c
index 7f1d675b97c09e..b156466dbaaffc 100644
--- a/clang/test/CodeGen/global-init.c
+++ b/clang/test/CodeGen/global-init.c
@@ -33,7 +33,7 @@ struct ManyFields {
int f;
};
-// CHECK: global %struct.ManyFields { i32 1, i32 2, i32 0, i8 0, i32 0, i32 0 }
+// CHECK: global { i32, i32, i32, i8, [3 x i8], i32, i32 } { i32 1, i32 2, i32 0, i8 0, [3 x i8] zeroinitializer, i32 0, i32 0 }
struct ManyFields FewInits = {1, 2};
diff --git a/clang/test/CodeGen/init.c b/clang/test/CodeGen/init.c
index cbf615bb9ddfea..27f427dff8f79e 100644
--- a/clang/test/CodeGen/init.c
+++ b/clang/test/CodeGen/init.c
@@ -187,25 +187,6 @@ void nonzeroMemsetf64(void) {
// CHECK: call void @llvm.memset.p0.i32(ptr {{.*}}, i8 68, i32 56, i1 false)
}
-void nonzeroPaddedUnionMemset(void) {
- union U { char c; int i; };
- union U arr[9] = { 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, };
- // CHECK-LABEL: @nonzeroPaddedUnionMemset(
- // CHECK-NOT: store
- // CHECK-NOT: memcpy
- // CHECK: call void @llvm.memset.p0.i32(ptr {{.*}}, i8 -16, i32 36, i1 false)
-}
-
-void nonzeroNestedMemset(void) {
- union U { char c; int i; };
- struct S { union U u; short i; };
- struct S arr[5] = { { {0xF0}, 0xF0F0 }, { {0xF0}, 0xF0F0 }, { {0xF0}, 0xF0F0 }, { {0xF0}, 0xF0F0 }, { {0xF0}, 0xF0F0 }, };
- // CHECK-LABEL: @nonzeroNestedMemset(
- // CHECK-NOT: store
- // CHECK-NOT: memcpy
- // CHECK: call void @llvm.memset.p0.i32(ptr {{.*}}, i8 -16, i32 40, i1 false)
-}
-
// PR9257
struct test11S {
int A[10];
diff --git a/clang/test/CodeGen/linux-kernel-struct-union-initializer.c b/clang/test/CodeGen/linux-kernel-struct-union-initializer.c
new file mode 100644
index 00000000000000..728d296066313b
--- /dev/null
+++ b/clang/test/CodeGen/linux-kernel-struct-union-initializer.c
@@ -0,0 +1,219 @@
+// RUN: %clang_cc1 -triple x86_64-unknown-unknown -std=gnu11 -verify -emit-llvm %s -o - | FileCheck %s
+// expected-no-diagnostics
+
+union U1 {
+ int x;
+ char y[16];
+};
+// CHECK: %union.U1 = type { i32, [12 x i8] }
+
+struct S1 {
+ int x;
+ union U1 y;
+};
+// CHECK: %struct.S1 = type { i32, %union.U1 }
+
+union U2 {
+ int x;
+ char y[16];
+} __attribute__((__aligned__(32)));
+// CHECK: %union.U2 = type { i32, [28 x i8] }
+
+struct S2 {
+ int x;
+ long long y;
+ char z[8];
+} __attribute__((__aligned__(32)));
+// CHECK: %struct.S2 = type { i32, i64, [8 x i8], [8 x i8] }
+
+union U1 global_u1 = {};
+// CHECK: @global_u1 ={{.*}} global %union.U1 zeroinitializer, align 4
+
+union U1 global_u2 = {3};
+// CHECK: @global_u2 ={{.*}} global %union.U1 { i32 3, [12 x i8] zeroinitializer }, align 4
+
+union U1 global_u2_from_cast = (union U1)3;
+// CHECK: @global_u2_from_cast ={{.*}} global { i32, [12 x i8] } { i32 3, [12 x i8] zeroinitializer }, align 4
+
+struct S1 global_s1 = {};
+// CHECK: @global_s1 ={{.*}} global %struct.S1 zeroinitializer, align 4
+
+struct S1 global_s2 = {
+ .x = 3,
+};
+// CHECK: @global_s2 ={{.*}} global %struct.S1 { i32 3, %union.U1 zeroinitializer }, align 4
+
+struct S1 global_s3 = {.x = 3, .y = {.x = 6}};
+// CHECK: @global_s3 ={{.*}} global %struct.S1 { i32 3, %union.U1 { i32 6, [12 x i8] zeroinitializer } }, align 4
+
+const union U1 global_const_u1 = {4};
+struct S1 global_s3_from_const_u1 = {.y = global_const_u1};
+// CHECK: @global_s3_from_const_u1 ={{.*}} global %struct.S1 { i32 0, %union.U1 { i32 4, [12 x i8] zeroinitializer } }, align 4
+
+union U2 global_u3 = {};
+// CHECK: @global_u3 ={{.*}} global %union.U2 zeroinitializer, align 32
+
+struct S2 global_s4 = {};
+// CHECK: @global_s4 ={{.*}} global { i32, [4 x i8], i64, [8 x i8], [8 x i8] } zeroinitializer, align 32
+
+struct S2 global_s5 = {.x = 1};
+// CHECK: @global_s5 ={{.*}}global { i32, [4 x i8], i64, [8 x i8], [8 x i8] } { i32 1, [4 x i8] zeroinitializer, i64 0, [8 x i8] zeroinitializer, [8 x i8] zeroinitializer }, align 32
+
+// CHECK: @test2.a ={{.*}} global %union.U1 zeroinitializer, align 4
+// CHECK: @__const.test3.a ={{.*}} constant %union.U1 { i32 3, [12 x i8] zeroinitializer }, align 4
+// CHECK: @test4.a ={{.*}} global %union.U1 { i32 3, [12 x i8] zeroinitializer }, align 4
+// CHECK: @test6.s ={{.*}} global %struct.S1 zeroinitializer, align 4
+// CHECK: @__const.test7.s ={{.*}} constant %struct.S1 { i32 3, %union.U1 zeroinitializer }, align 4
+// CHECK: @test8.s ={{.*}} global %struct.S1 { i32 3, %union.U1 zeroinitializer }, align 4
+// CHECK: @__const.test9.s ={{.*}} constant %struct.S1 { i32 3, %union.U1 { i32 6, [12 x i8] zeroinitializer } }, align 4
+// CHECK: @test10.s ={{.*}} global %struct.S1 { i32 3, %union.U1 { i32 6, [12 x i8] zeroinitializer } }, align 4
+// CHECK: @test12.a ={{.*}} global %union.U2 zeroinitializer, align 32
+// CHECK: @test14.s ={{.*}} global { i32, [4 x i8], i64, [8 x i8], [8 x i8] } zeroinitializer, align 32
+// CHECK: @__const.test15.s ={{.*}} constant { i32, [4 x i8], i64, [8 x i8], [8 x i8] } { i32 1, [4 x i8] zeroinitializer, i64 0, [8 x i8] zeroinitializer, [8 x i8] zeroinitializer }, align 32
+// CHECK: @test16.s = internal global { i32, [4 x i8], i64, [8 x i8], [8 x i8] } { i32 1, [4 x i8] zeroinitializer, i64 0, [8 x i8] zeroinitializer, [8 x i8] zeroinitializer }, align 32
+
+// Test empty initializer for union.
+void test1() {
+ union U1 a = {};
+ // CHECK-LABEL: @test1()
+ // CHECK-NEXT: entry:
+ // CHECK-NEXT: %[[A:.+]] = alloca %union.U1, align 4
+ // CHECK-NEXT: call void @llvm.memset.p0.i64({{.*}}%[[A]], i8 0, i64 16, i1 false)
+}
+
+// Test empty initializer for union. Use static variable.
+void test2() {
+ static union U1 a = {};
+ // CHECK-LABEL: @test2()
+ // CHECK-NEXT: entry:
+ // CHECK-NEXT: ret void
+}
+
+// Test only initializing a small field for union.
+void test3() {
+ union U1 a = {3};
+ // CHECK-LABEL: @test3()
+ // CHECK-NEXT: entry:
+ // CHECK-NEXT: %[[A:.+]] = alloca %union.U1
+ // CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64({{.*}}%[[A]], {{.*}}@__const.test3.a, i64 16, i1 false)
+}
+
+// Test only initializing a small field for union. Use static variable.
+void test4() {
+ static union U1 a = {3};
+ // CHECK-LABEL: @test4()
+ // CHECK-NEXT: entry:
+ // CHECK-NEXT: ret void
+}
+
+// Test union in struct. Use empty initializer for the struct.
+void test5() {
+ struct S1 s = {};
+ // CHECK-LABEL: @test5()
+ // CHECK-NEXT: entry:
+ // CHECK-NEXT: %[[S:.+]] = alloca %struct.S1
+ // CHECK-NEXT: call void @llvm.memset.p0.i64({{.*}}%[[S]], i8 0, i64 20, i1 false)
+}
+
+// Test union in struct. Use empty initializer for the struct. Use static variable.
+void test6() {
+ static struct S1 s = {};
+ // CHECK-LABEL: @test6()
+ // CHECK-NEXT: entry:
+ // CHECK-NEXT: ret void
+}
+
+// Test union in struct. Initialize other fields of the struct.
+void test7() {
+ struct S1 s = {
+ .x = 3,
+ };
+ // CHECK-LABEL: @test7()
+ // CHECK-NEXT: entry:
+ // CHECK-NEXT: %[[S:.+]] = alloca %struct.S1
+ // CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64({{.*}}%[[S]], {{.*}}@__const.test7.s, i64 20, i1 false)
+}
+
+// Test union in struct. Initialize other fields of the struct. Use static variable.
+void test8() {
+ static struct S1 s = {
+ .x = 3,
+ };
+ // CHECK-LABEL: @test8()
+ // CHECK-NEXT: entry:
+ // CHECK-NEXT: ret void
+}
+
+// Test union in struct. Initialize a small field for union.
+void test9() {
+ struct S1 s = {.x = 3,
+ .y = {
+ .x = 6,
+ }};
+ // CHECK-LABEL: @test9()
+ // CHECK-NEXT: entry:
+ // CHECK-NEXT: %[[S:.+]] = alloca %struct.S1
+ // CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64({{.*}}%[[S]], {{.*}}@__const.test9.s, i64 20, i1 false)
+}
+
+// Test union in struct. Initialize a small field for union. Use static variable.
+void test10() {
+ static struct S1 s = {.x = 3,
+ .y = {
+ .x = 6,
+ }};
+ // CHECK-LABEL: @test10()
+ // CHECK-NEXT: entry:
+ // CHECK-NEXT: ret void
+}
+
+// Test empty initializer for union with padding.
+void test11() {
+ union U2 a = {};
+ // CHECK-LABEL: @test11()
+ // CHECK-NEXT: entry:
+ // CHECK-NEXT: %[[A:.+]] = alloca %union.U2, align 32
+ // CHECK-NEXT: call void @llvm.memset.p0.i64({{.*}}%[[A]], i8 0, i64 32, i1 false)
+}
+
+// Test empty initializer for union with padding. Use static variable.
+void test12() {
+ static union U2 a = {};
+ // CHECK-LABEL: @test12()
+ // CHECK-NEXT: entry:
+ // CHECK-NEXT: ret void
+}
+
+// Test empty initializer for struct with padding.
+void test13() {
+ struct S2 s = {};
+ // CHECK-LABEL: @test13()
+ // CHECK-NEXT: entry:
+ // CHECK-NEXT: %[[S:.+]] = alloca %struct.S2, align 32
+ // CHECK-NEXT: call void @llvm.memset.p0.i64({{.*}}%[[S]], i8 0, i64 32, i1 false)
+}
+
+// Test empty initializer for struct with padding. Use static variable.
+void test14() {
+ static struct S2 s = {};
+ // CHECK-LABEL: @test14()
+ // CHECK-NEXT: entry:
+ // CHECK-NEXT: ret void
+}
+
+// Test partial initialization for struct with padding.
+void test15() {
+ struct S2 s = {.x = 1};
+ // CHECK-LABEL: @test15()
+ // CHECK-NEXT: entry:
+ // CHECK-NEXT: %[[S:.+]] = alloca %struct.S2, align 32
+ // CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64({{.*}}%[[S]], {{.*}}@__const.test15.s, i64 32, i1 false)
+}
+
+// Test partial initialization for struct with padding. Use static variable.
+void test16() {
+ static struct S2 s = {.x = 1};
+ // CHECK-LABEL: @test16()
+ // CHECK-NEXT: entry:
+ // CHECK-NEXT: ret void
+}
diff --git a/clang/test/CodeGen/linux-kernel-struct-union-initializer2.c b/clang/test/CodeGen/linux-kernel-struct-union-initializer2.c
new file mode 100644
index 00000000000000..cb521c2ed0eee0
--- /dev/null
+++ b/clang/test/CodeGen/linux-kernel-struct-union-initializer2.c
@@ -0,0 +1,75 @@
+// RUN: %clang_cc1 -triple x86_64-unknown-unknown -std=gnu11 -verify -emit-llvm %s -o - | FileCheck %s
+// expected-no-diagnostics
+
+union U1 {
+ int x;
+ char y[5];
+};
+
+struct S1 {
+ int x;
+ long long y;
+};
+
+struct S2 {
+ unsigned char b1 : 3; // 1st 3 bits (in 1st byte) are b1
+ unsigned char : 2; // next 2 bits (in 1st byte) are blocked out as unused
+ unsigned char b2 : 6; // 6 bits for b2 - doesn't fit into the 1st byte => starts a 2nd
+ unsigned char b3 : 2; // 2 bits for b3 - next (and final) bits in the 2nd byte
+ int i;
+};
+
+struct S3 {
+ int x;
+} __attribute__((__aligned__(8)));
+
+struct S4 {
+ int a;
+ union U1 b;
+};
+
+// Test non-const initializer for union with padding.
+void test1(int x) {
+ union U1 a = {x};
+ // CHECK-LABEL: @test1
+ // CHECK: %[[A:.+]] = alloca %union.U1
+ // CHECK: %[[B:.+]] = getelementptr i8, ptr %[[A]], i64 4
+ // CHECK-NEXT: call void @llvm.memset.p0.i64(ptr align 4 %[[B]], i8 0, i64 4, i1 false)
+}
+
+// Test non-const initializer for struct with padding.
+void test2(long long y) {
+ struct S1 s = {.y = y};
+ // CHECK-LABEL: @test2
+ // CHECK: %[[A:.+]] = alloca %struct.S1
+ // CHECK: %[[B:.+]] = getelementptr i8, ptr %[[A]], i64 4
+ // CHECK-NEXT: call void @llvm.memset.p0.i64(ptr align 4 %[[B]], i8 0, i64 4, i1 false)
+}
+
+// Test non-const initializer for struct with padding and bit fields.
+void test3(unsigned char b) {
+ struct S2 s = {.b1 = b};
+ // CHECK-LABEL: @test3
+ // CHECK: %[[A:.+]] = alloca %struct.S2
+ // CHECK: %[[B:.+]] = getelementptr i8, ptr %[[A]], i64 2
+ // CHECK-NEXT: call void @llvm.memset.p0.i64(ptr align 2 %[[B]], i8 0, i64 2, i1 false)
+}
+
+// Test non-const initializer for struct with padding at the end of the struct.
+void test4(int x) {
+ struct S3 s = {x};
+ // CHECK-LABEL: @test4
+ // CHECK: %[[A:.+]] = alloca %struct.S3
+ // CHECK: %[[B:.+]] = getelementptr i8, ptr %[[A]], i64 4
+ // CHECK-NEXT: call void @llvm.memset.p0.i64(ptr align 4 %[[B]], i8 0, i64 4, i1 false)
+}
+
+// Test non-const initializer for union in struct.
+void test5(int a, int b) {
+ struct S4 s = {a, {b}};
+ // CHECK-LABEL: @test5
+ // CHECK: %[[A:.+]] = alloca %struct.S4
+ // CHECK: %[[B:.+]] = getelementptr inbounds nuw %struct.S4, ptr %[[A]], i32 0, i32 1
+ // CHECK: %[[C:.+]] = getelementptr i8, ptr %[[B]], i64 4
+ // CHECK-NEXT: call void @llvm.memset.p0.i64(ptr align 4 %[[C]], i8 0, i64 4, i1 false)
+}
diff --git a/clang/test/CodeGen/mingw-long-double.c b/clang/test/CodeGen/mingw-long-double.c
index 4be97526f96319..0fc8f015096827 100644
--- a/clang/test/CodeGen/mingw-long-double.c
+++ b/clang/test/CodeGen/mingw-long-double.c
@@ -11,12 +11,9 @@ struct {
char c;
long double ldb;
} agggregate_LD = {};
-// GNU32: %struct.anon = type { i8, x86_fp80 }
-// GNU32: @agggregate_LD = dso_local global %struct.anon zeroinitializer, align 4
-// GNU64: %struct.anon = type { i8, x86_fp80 }
-// GNU64: @agggregate_LD = dso_local global %struct.anon zeroinitializer, align 16
-// MSC64: %struct.anon = type { i8, double }
-// MSC64: @agggregate_LD = dso_local global %struct.anon zeroinitializer, align 8
+// GNU32: @agggregate_LD = dso_local global { i8, [3 x i8], x86_fp80 } zeroinitializer, align 4
+// GNU64: @agggregate_LD = dso_local global { i8, [15 x i8], x86_fp80 } zeroinitializer, align 16
+// MSC64: @agggregate_LD = dso_local global { i8, [7 x i8], double } zeroinitializer, align 8
long double dataLD = 1.0L;
// GNU32: @dataLD = dso_local global x86_fp80 0xK3FFF8000000000000000, align 4
diff --git a/clang/test/CodeGen/mms-bitfields.c b/clang/test/CodeGen/mms-bitfields.c
index 49c5c1c3e7d40d..2ccce326c7131d 100644
--- a/clang/test/CodeGen/mms-bitfields.c
+++ b/clang/test/CodeGen/mms-bitfields.c
@@ -61,5 +61,5 @@ union HEADER {
struct Inner variable = { 1,0,1, 21 };
union HEADER hdr = {{1,2,3,4}};
-// CHECK: @variable ={{.*}} global { i8, [3 x i8], i8, i8, i8, i8 } { i8 5, [3 x i8] undef, i8 21, i8 0, i8 0, i8 0 }, align 1
-// CHECK: @hdr ={{.*}} global { { i8, i8, [2 x i8], i8, i8, i8, i8, i8, [3 x i8] } } { { i8, i8, [2 x i8], i8, i8, i8, i8, i8, [3 x i8] } { i8 8, i8 0, [2 x i8] undef, i8 2, i8 0, i8 0, i8 3, i8 4, [3 x i8] undef } }, align 1
+// CHECK: @variable ={{.*}} global { i8, [3 x i8], i8, i8, i8, i8 } { i8 5, [3 x i8] zeroinitializer, i8 21, i8 0, i8 0, i8 0 }, align 1
+// CHECK: @hdr ={{.*}} global { { i8, i8, [2 x i8], i8, i8, i8, i8, i8, [3 x i8] } } { { i8, i8, [2 x i8], i8, i8, i8, i8, i8, [3 x i8] } { i8 8, i8 0, [2 x i8] zeroinitializer, i8 2, i8 0, i8 0, i8 3, i8 4, [3 x i8] zeroinitializer } }, align 1
diff --git a/clang/test/CodeGen/union-init2.c b/clang/test/CodeGen/union-init2.c
index 2c167683c4e55d..64fea80a80d49b 100644
--- a/clang/test/CodeGen/union-init2.c
+++ b/clang/test/CodeGen/union-init2.c
@@ -1,11 +1,11 @@
// RUN: %clang_cc1 -emit-llvm %s -o - -triple i686-pc-linux-gnu | FileCheck %s
// Make sure we generate something sane instead of a ptrtoint
-// CHECK: @r, [4 x i8] undef
+// CHECK: @r, [4 x i8] zeroinitializer
union x {long long b;union x* a;} r = {.a = &r};
-// CHECK: global { [3 x i8], [5 x i8] } { [3 x i8] zeroinitializer, [5 x i8] undef }
+// CHECK: global { [3 x i8], [5 x i8] } zeroinitializer
union z {
char a[3];
long long b;
diff --git a/clang/test/CodeGen/windows-swiftcall.c b/clang/test/CodeGen/windows-swiftcall.c
index bc7832d9d3ac28..41569c2606622f 100644
--- a/clang/test/CodeGen/windows-swiftcall.c
+++ b/clang/test/CodeGen/windows-swiftcall.c
@@ -5,8 +5,6 @@
#define ERROR __attribute__((swift_error_result))
#define CONTEXT __attribute__((swift_context))
-// CHECK: [[STRUCT2_RESULT:@.*]] = private {{.*}} constant [[STRUCT2_TYPE:%.*]] { i32 0, i8 0, i8 undef, i8 0, i32 0, i32 0 }
-
/*****************************************************************************/
/****************************** PARAMETER ABIS *******************************/
/*****************************************************************************/
@@ -142,8 +140,8 @@ typedef struct {
} struct_2;
TEST(struct_2);
// CHECK-LABEL: define dso_local swiftcc { i64, i64 } @return_struct_2() {{.*}}{
-// CHECK: [[RET:%.*]] = alloca [[STRUCT2_TYPE]], align 4
-// CHECK: call void @llvm.memcpy{{.*}}({{.*}}[[RET]], {{.*}}[[STRUCT2_RESULT]]
+// CHECK: [[RET:%.*]] = alloca [[STRUCT2:%.*]], align 4
+// CHECK: call void @llvm.memset
// CHECK: [[GEP0:%.*]] = getelementptr inbounds nuw { i64, i64 }, ptr [[RET]], i32 0, i32 0
// CHECK: [[T0:%.*]] = load i64, ptr [[GEP0]], align 4
// CHECK: [[GEP1:%.*]] = getelementptr inbounds nuw { i64, i64 }, ptr [[RET]], i32 0, i32 1
@@ -153,7 +151,7 @@ TEST(struct_2);
// CHECK: ret { i64, i64 } [[R1]]
// CHECK: }
// CHECK-LABEL: define dso_local swiftcc void @take_struct_2(i64 %0, i64 %1) {{.*}}{
-// CHECK: [[V:%.*]] = alloca [[STRUCT:%.*]], align 4
+// CHECK: [[V:%.*]] = alloca [[STRUCT2]], align 4
// CHECK: [[GEP0:%.*]] = getelementptr inbounds nuw { i64, i64 }, ptr [[V]], i32 0, i32 0
// CHECK: store i64 %0, ptr [[GEP0]], align 4
// CHECK: [[GEP1:%.*]] = getelementptr inbounds nuw { i64, i64 }, ptr [[V]], i32 0, i32 1
@@ -161,7 +159,7 @@ TEST(struct_2);
// CHECK: ret void
// CHECK: }
// CHECK-LABEL: define dso_local void @test_struct_2() {{.*}} {
-// CHECK: [[TMP:%.*]] = alloca [[STRUCT2_TYPE]], align 4
+// CHECK: [[TMP:%.*]] = alloca [[STRUCT2]], align 4
// CHECK: [[CALL:%.*]] = call swiftcc { i64, i64 } @return_struct_2()
// CHECK: [[GEP:%.*]] = getelementptr inbounds nuw {{.*}} [[TMP]], i32 0, i32 0
// CHECK: [[T0:%.*]] = extractvalue { i64, i64 } [[CALL]], 0
@@ -234,7 +232,7 @@ typedef union {
TEST(union_het_fp)
// CHECK-LABEL: define dso_local swiftcc i64 @return_union_het_fp()
// CHECK: [[RET:%.*]] = alloca [[UNION:%.*]], align 8
-// CHECK: call void @llvm.memcpy{{.*}}(ptr align {{[0-9]+}} [[RET]]
+// CHECK: call void @llvm.memset{{.*}}(ptr align {{[0-9]+}} [[RET]]
// CHECK: [[GEP:%.*]] = getelementptr inbounds nuw { i64 }, ptr [[RET]], i32 0, i32 0
// CHECK: [[R0:%.*]] = load i64, ptr [[GEP]], align 8
// CHECK: ret i64 [[R0]]
diff --git a/clang/test/CodeGenObjC/designated-initializers.m b/clang/test/CodeGenObjC/designated-initializers.m
index a67f82e1afbea8..ce58f6c367338e 100644
--- a/clang/test/CodeGenObjC/designated-initializers.m
+++ b/clang/test/CodeGenObjC/designated-initializers.m
@@ -4,4 +4,4 @@
char L[3];
int M;
} overwrite_string[] = { { { @encode(void**) }, 1 }, [0].L[1] = 'x'};
-// CHECK: [3 x i8] c"^xv", i32 1
+// CHECK: [3 x i8] c"^xv", i8 0, i32 1
>From c2c2188f73cc44324d2368192bca80fbcd701263 Mon Sep 17 00:00:00 2001
From: Yabin Cui <yabinc at google.com>
Date: Thu, 29 Aug 2024 13:33:30 -0700
Subject: [PATCH 2/2] Update based on comments
---
clang/docs/LanguageExtensions.rst | 2 +
clang/lib/CodeGen/CGExprAgg.cpp | 58 ++---
clang/lib/CodeGen/CGExprConstant.cpp | 101 ++++-----
.../linux-kernel-struct-union-initializer.c | 214 +++++++++++-------
.../linux-kernel-struct-union-initializer2.c | 107 +++++++--
5 files changed, 291 insertions(+), 191 deletions(-)
diff --git a/clang/docs/LanguageExtensions.rst b/clang/docs/LanguageExtensions.rst
index 6eebb209df09f9..8dd1f00647fd1d 100644
--- a/clang/docs/LanguageExtensions.rst
+++ b/clang/docs/LanguageExtensions.rst
@@ -5840,3 +5840,5 @@ Clang guarantees the following behaviors:
``= { initializer-list }``, all members not explicitly initialized in
the initializer list are empty-initialized recursively. In addition, all
padding bits are initialized to zero.
+
+Currently, the above extension only applies to C source code, not C++.
diff --git a/clang/lib/CodeGen/CGExprAgg.cpp b/clang/lib/CodeGen/CGExprAgg.cpp
index 49648130a83e62..f4c245b9c72eb7 100644
--- a/clang/lib/CodeGen/CGExprAgg.cpp
+++ b/clang/lib/CodeGen/CGExprAgg.cpp
@@ -1718,7 +1718,15 @@ void AggExprEmitter::VisitCXXParenListOrInitListExpr(
CodeGenFunction::FieldConstructionScope FCS(CGF, Dest.getAddress());
// See comment in getPadding() in CGExprConstant.cpp.
- bool ZeroInitPadding = !CGF.getLangOpts().CPlusPlus && !Dest.isZeroed();
+ const bool ZeroInitPadding = !CGF.getLangOpts().CPlusPlus && !Dest.isZeroed();
+ const Address BaseLoc = Dest.getAddress().withElementType(CGF.Int8Ty);
+ auto DoZeroInitPadding = [&](CharUnits Offset, CharUnits Size) {
+ if (Size.isPositive()) {
+ Address Loc = CGF.Builder.CreateConstGEP(BaseLoc, Offset.getQuantity());
+ llvm::Constant *SizeVal = CGF.Builder.getInt64(Size.getQuantity());
+ CGF.Builder.CreateMemSet(Loc, CGF.Builder.getInt8(0), SizeVal, false);
+ }
+ };
if (record->isUnion()) {
// Only initialize one field of a union. The field itself is
@@ -1744,48 +1752,33 @@ void AggExprEmitter::VisitCXXParenListOrInitListExpr(
if (NumInitElements) {
// Store the initializer into the field
EmitInitializationToLValue(InitExprs[0], FieldLoc);
+ if (ZeroInitPadding) {
+ CharUnits TotalSize =
+ Dest.getPreferredSize(CGF.getContext(), DestLV.getType());
+ CharUnits FieldSize =
+ CGF.getContext().getTypeSizeInChars(FieldLoc.getType());
+ DoZeroInitPadding(FieldSize, TotalSize - FieldSize);
+ }
} else {
// Default-initialize to null.
- EmitNullInitializationToLValue(FieldLoc);
- }
- if (ZeroInitPadding) {
- CharUnits TotalSize =
- Dest.getPreferredSize(CGF.getContext(), DestLV.getType());
- CharUnits FieldSize =
- CGF.getContext().getTypeSizeInChars(FieldLoc.getType());
- if (FieldSize < TotalSize) {
- CharUnits LeftSize = TotalSize - FieldSize;
- llvm::Constant *LeftSizeVal =
- CGF.Builder.getInt64(LeftSize.getQuantity());
- Address BaseLoc = Dest.getAddress().withElementType(CGF.Int8Ty);
- Address LeftLoc =
- CGF.Builder.CreateConstGEP(BaseLoc, LeftSize.getQuantity());
- CGF.Builder.CreateMemSet(LeftLoc, CGF.Builder.getInt8(0), LeftSizeVal,
- false);
- }
+ if (ZeroInitPadding)
+ EmitNullInitializationToLValue(DestLV);
+ else
+ EmitNullInitializationToLValue(FieldLoc);
}
-
return;
}
// Here we iterate over the fields; this makes it simpler to both
// default-initialize fields and skip over unnamed fields.
const ASTRecordLayout &Layout = CGF.getContext().getASTRecordLayout(record);
- Address BaseLoc = Dest.getAddress().withElementType(CGF.Int8Ty);
CharUnits SizeSoFar = CharUnits::Zero();
for (const auto *field : record->fields()) {
if (ZeroInitPadding) {
unsigned FieldNo = field->getFieldIndex();
CharUnits Offset =
CGF.getContext().toCharUnitsFromBits(Layout.getFieldOffset(FieldNo));
- if (SizeSoFar < Offset) {
- llvm::Constant *PaddingSizeVal =
- CGF.Builder.getInt64((Offset - SizeSoFar).getQuantity());
- Address PaddingLoc =
- CGF.Builder.CreateConstGEP(BaseLoc, SizeSoFar.getQuantity());
- CGF.Builder.CreateMemSet(PaddingLoc, CGF.Builder.getInt8(0),
- PaddingSizeVal, false);
- }
+ DoZeroInitPadding(SizeSoFar, Offset - SizeSoFar);
CharUnits FieldSize =
CGF.getContext().getTypeSizeInChars(field->getType());
SizeSoFar = Offset + FieldSize;
@@ -1834,14 +1827,7 @@ void AggExprEmitter::VisitCXXParenListOrInitListExpr(
if (ZeroInitPadding) {
CharUnits TotalSize =
Dest.getPreferredSize(CGF.getContext(), DestLV.getType());
- if (SizeSoFar < TotalSize) {
- llvm::Constant *PaddingSizeVal =
- CGF.Builder.getInt64((TotalSize - SizeSoFar).getQuantity());
- Address PaddingLoc =
- CGF.Builder.CreateConstGEP(BaseLoc, SizeSoFar.getQuantity());
- CGF.Builder.CreateMemSet(PaddingLoc, CGF.Builder.getInt8(0),
- PaddingSizeVal, false);
- }
+ DoZeroInitPadding(SizeSoFar, TotalSize - SizeSoFar);
}
}
diff --git a/clang/lib/CodeGen/CGExprConstant.cpp b/clang/lib/CodeGen/CGExprConstant.cpp
index 56c05717efe8f6..810ebc3acec32e 100644
--- a/clang/lib/CodeGen/CGExprConstant.cpp
+++ b/clang/lib/CodeGen/CGExprConstant.cpp
@@ -43,6 +43,9 @@ namespace {
class ConstExprEmitter;
llvm::Constant *getPadding(const CodeGenModule &CGM, CharUnits PadSize) {
+ llvm::Type *Ty = CGM.CharTy;
+ if (PadSize > CharUnits::One())
+ Ty = llvm::ArrayType::get(Ty, PadSize.getQuantity());
if (!CGM.getLangOpts().CPlusPlus) {
// In C23 (N3096) $6.7.10:
// """
@@ -91,14 +94,8 @@ llvm::Constant *getPadding(const CodeGenModule &CGM, CharUnits PadSize) {
// aggregates and unions, no matter they are initialized by empty
// initializers or non empty initializers. This can provide a consistent
// behavior. So projects like the Linux kernel can rely on it.
- if (PadSize == CharUnits::One())
- return llvm::ConstantInt::get(CGM.CharTy, 0);
- llvm::Type *Ty = llvm::ArrayType::get(CGM.CharTy, PadSize.getQuantity());
- return llvm::ConstantAggregateZero::get(Ty);
+ return llvm::Constant::getNullValue(Ty);
}
- llvm::Type *Ty = CGM.CharTy;
- if (PadSize > CharUnits::One())
- Ty = llvm::ArrayType::get(Ty, PadSize.getQuantity());
return llvm::UndefValue::get(Ty);
}
@@ -648,6 +645,9 @@ class ConstStructBuilder {
bool Build(const InitListExpr *ILE, bool AllowOverwrite);
bool Build(const APValue &Val, const RecordDecl *RD, bool IsPrimaryBase,
const CXXRecordDecl *VTableClass, CharUnits BaseOffset);
+ bool DoZeroInitPadding(const ASTRecordLayout &Layout, unsigned FieldNo,
+ const FieldDecl *Field, CharUnits &SizeSoFar,
+ bool &IsFlexibleArray, bool AllowOverwrite);
llvm::Constant *Finalize(QualType Ty);
};
@@ -773,7 +773,7 @@ bool ConstStructBuilder::Build(const InitListExpr *ILE, bool AllowOverwrite) {
return false;
// See comment in getPadding().
- bool ZeroInitPadding = !CGM.getLangOpts().CPlusPlus;
+ const bool ZeroInitPadding = !CGM.getLangOpts().CPlusPlus;
CharUnits SizeSoFar = CharUnits::Zero();
bool IsFlexibleArray = false;
@@ -795,13 +795,10 @@ bool ConstStructBuilder::Build(const InitListExpr *ILE, bool AllowOverwrite) {
if (ElementNo < ILE->getNumInits())
Init = ILE->getInit(ElementNo++);
if (isa_and_nonnull<NoInitExpr>(Init)) {
- if (ZeroInitPadding) {
- CharUnits Offset = CGM.getContext().toCharUnitsFromBits(
- Layout.getFieldOffset(FieldNo));
- CharUnits FieldSize =
- CGM.getContext().getTypeSizeInChars(Field->getType());
- SizeSoFar = Offset + FieldSize;
- }
+ if (ZeroInitPadding &&
+ !DoZeroInitPadding(Layout, FieldNo, Field, SizeSoFar, IsFlexibleArray,
+ AllowOverwrite))
+ return false;
continue;
}
@@ -813,18 +810,9 @@ bool ConstStructBuilder::Build(const InitListExpr *ILE, bool AllowOverwrite) {
continue;
}
- if (ZeroInitPadding) {
- CharUnits Offset =
- CGM.getContext().toCharUnitsFromBits(Layout.getFieldOffset(FieldNo));
- if (SizeSoFar < Offset)
- if (!AppendBytes(SizeSoFar, getPadding(CGM, Offset - SizeSoFar),
- AllowOverwrite))
- return false;
- CharUnits FieldSize =
- CGM.getContext().getTypeSizeInChars(Field->getType());
- SizeSoFar = Offset + FieldSize;
- IsFlexibleArray = FieldSize.isZero();
- }
+ if (ZeroInitPadding && !DoZeroInitPadding(Layout, FieldNo, Field, SizeSoFar,
+ IsFlexibleArray, AllowOverwrite))
+ return false;
// When emitting a DesignatedInitUpdateExpr, a nested InitListExpr
// represents additional overwriting of our current constant value, and not
@@ -872,13 +860,9 @@ bool ConstStructBuilder::Build(const InitListExpr *ILE, bool AllowOverwrite) {
}
}
- if (ZeroInitPadding) {
- CharUnits TotalSize = Layout.getSize();
- if (SizeSoFar < TotalSize)
- if (!AppendBytes(SizeSoFar, getPadding(CGM, TotalSize - SizeSoFar),
- AllowOverwrite))
- return false;
- }
+ if (ZeroInitPadding && !DoZeroInitPadding(Layout, FieldNo, nullptr, SizeSoFar,
+ IsFlexibleArray, AllowOverwrite))
+ return false;
return true;
}
@@ -945,8 +929,9 @@ bool ConstStructBuilder::Build(const APValue &Val, const RecordDecl *RD,
unsigned FieldNo = 0;
uint64_t OffsetBits = CGM.getContext().toBits(Offset);
// See comment in getPadding().
- bool ZeroInitPadding = !CGM.getLangOpts().CPlusPlus;
+ const bool ZeroInitPadding = !CGM.getLangOpts().CPlusPlus;
CharUnits SizeSoFar = CharUnits::Zero();
+ bool IsFlexibleArray = false;
bool AllowOverwrite = false;
for (RecordDecl::field_iterator Field = RD->field_begin(),
@@ -969,18 +954,12 @@ bool ConstStructBuilder::Build(const APValue &Val, const RecordDecl *RD,
return false;
if (ZeroInitPadding) {
- CharUnits Offset =
- CGM.getContext().toCharUnitsFromBits(Layout.getFieldOffset(FieldNo));
- if (SizeSoFar < Offset)
- if (!AppendBytes(SizeSoFar, getPadding(CGM, Offset - SizeSoFar),
- AllowOverwrite))
- return false;
- CharUnits FieldSize =
- CGM.getContext().getTypeSizeInChars(Field->getType());
- if (FieldSize.isZero())
- FieldSize = CharUnits::fromQuantity(
+ if (!DoZeroInitPadding(Layout, FieldNo, *Field, SizeSoFar,
+ IsFlexibleArray, AllowOverwrite))
+ return false;
+ if (IsFlexibleArray)
+ SizeSoFar += CharUnits::fromQuantity(
CGM.getDataLayout().getTypeAllocSize(EltInit->getType()));
- SizeSoFar = Offset + FieldSize;
}
if (!Field->isBitField()) {
@@ -999,14 +978,34 @@ bool ConstStructBuilder::Build(const APValue &Val, const RecordDecl *RD,
return false;
}
}
- if (ZeroInitPadding) {
- CharUnits TotalSize = Layout.getSize();
- if (SizeSoFar < TotalSize)
- if (!AppendBytes(SizeSoFar, getPadding(CGM, TotalSize - SizeSoFar),
+ if (ZeroInitPadding && !DoZeroInitPadding(Layout, FieldNo, nullptr, SizeSoFar,
+ IsFlexibleArray, AllowOverwrite))
+ return false;
+
+ return true;
+}
+
+bool ConstStructBuilder::DoZeroInitPadding(
+ const ASTRecordLayout &Layout, unsigned FieldNo, const FieldDecl *Field,
+ CharUnits &SizeSoFar, bool &IsFlexibleArray, bool AllowOverwrite) {
+ if (Field != nullptr) {
+ CharUnits Offset =
+ CGM.getContext().toCharUnitsFromBits(Layout.getFieldOffset(FieldNo));
+ if (SizeSoFar < Offset)
+ if (!AppendBytes(SizeSoFar, getPadding(CGM, Offset - SizeSoFar),
AllowOverwrite))
return false;
+ CharUnits FieldSize = CGM.getContext().getTypeSizeInChars(Field->getType());
+ SizeSoFar = Offset + FieldSize;
+ IsFlexibleArray = FieldSize.isZero();
+ return true;
}
-
+ CharUnits TotalSize = Layout.getSize();
+ if (SizeSoFar < TotalSize)
+ if (!AppendBytes(SizeSoFar, getPadding(CGM, TotalSize - SizeSoFar),
+ AllowOverwrite))
+ return false;
+ SizeSoFar = TotalSize;
return true;
}
diff --git a/clang/test/CodeGen/linux-kernel-struct-union-initializer.c b/clang/test/CodeGen/linux-kernel-struct-union-initializer.c
index 728d296066313b..dc68cc0f454c8c 100644
--- a/clang/test/CodeGen/linux-kernel-struct-union-initializer.c
+++ b/clang/test/CodeGen/linux-kernel-struct-union-initializer.c
@@ -1,3 +1,4 @@
+// NOTE: Assertions have been autogenerated by utils/update_cc_test_checks.py UTC_ARGS: --check-globals all --version 5
// RUN: %clang_cc1 -triple x86_64-unknown-unknown -std=gnu11 -verify -emit-llvm %s -o - | FileCheck %s
// expected-no-diagnostics
@@ -5,215 +6,262 @@ union U1 {
int x;
char y[16];
};
-// CHECK: %union.U1 = type { i32, [12 x i8] }
struct S1 {
int x;
union U1 y;
};
-// CHECK: %struct.S1 = type { i32, %union.U1 }
union U2 {
int x;
char y[16];
} __attribute__((__aligned__(32)));
-// CHECK: %union.U2 = type { i32, [28 x i8] }
struct S2 {
int x;
long long y;
char z[8];
} __attribute__((__aligned__(32)));
-// CHECK: %struct.S2 = type { i32, i64, [8 x i8], [8 x i8] }
union U1 global_u1 = {};
-// CHECK: @global_u1 ={{.*}} global %union.U1 zeroinitializer, align 4
union U1 global_u2 = {3};
-// CHECK: @global_u2 ={{.*}} global %union.U1 { i32 3, [12 x i8] zeroinitializer }, align 4
union U1 global_u2_from_cast = (union U1)3;
-// CHECK: @global_u2_from_cast ={{.*}} global { i32, [12 x i8] } { i32 3, [12 x i8] zeroinitializer }, align 4
struct S1 global_s1 = {};
-// CHECK: @global_s1 ={{.*}} global %struct.S1 zeroinitializer, align 4
struct S1 global_s2 = {
.x = 3,
};
-// CHECK: @global_s2 ={{.*}} global %struct.S1 { i32 3, %union.U1 zeroinitializer }, align 4
struct S1 global_s3 = {.x = 3, .y = {.x = 6}};
-// CHECK: @global_s3 ={{.*}} global %struct.S1 { i32 3, %union.U1 { i32 6, [12 x i8] zeroinitializer } }, align 4
const union U1 global_const_u1 = {4};
struct S1 global_s3_from_const_u1 = {.y = global_const_u1};
-// CHECK: @global_s3_from_const_u1 ={{.*}} global %struct.S1 { i32 0, %union.U1 { i32 4, [12 x i8] zeroinitializer } }, align 4
union U2 global_u3 = {};
-// CHECK: @global_u3 ={{.*}} global %union.U2 zeroinitializer, align 32
struct S2 global_s4 = {};
-// CHECK: @global_s4 ={{.*}} global { i32, [4 x i8], i64, [8 x i8], [8 x i8] } zeroinitializer, align 32
struct S2 global_s5 = {.x = 1};
-// CHECK: @global_s5 ={{.*}}global { i32, [4 x i8], i64, [8 x i8], [8 x i8] } { i32 1, [4 x i8] zeroinitializer, i64 0, [8 x i8] zeroinitializer, [8 x i8] zeroinitializer }, align 32
-
-// CHECK: @test2.a ={{.*}} global %union.U1 zeroinitializer, align 4
-// CHECK: @__const.test3.a ={{.*}} constant %union.U1 { i32 3, [12 x i8] zeroinitializer }, align 4
-// CHECK: @test4.a ={{.*}} global %union.U1 { i32 3, [12 x i8] zeroinitializer }, align 4
-// CHECK: @test6.s ={{.*}} global %struct.S1 zeroinitializer, align 4
-// CHECK: @__const.test7.s ={{.*}} constant %struct.S1 { i32 3, %union.U1 zeroinitializer }, align 4
-// CHECK: @test8.s ={{.*}} global %struct.S1 { i32 3, %union.U1 zeroinitializer }, align 4
-// CHECK: @__const.test9.s ={{.*}} constant %struct.S1 { i32 3, %union.U1 { i32 6, [12 x i8] zeroinitializer } }, align 4
-// CHECK: @test10.s ={{.*}} global %struct.S1 { i32 3, %union.U1 { i32 6, [12 x i8] zeroinitializer } }, align 4
-// CHECK: @test12.a ={{.*}} global %union.U2 zeroinitializer, align 32
-// CHECK: @test14.s ={{.*}} global { i32, [4 x i8], i64, [8 x i8], [8 x i8] } zeroinitializer, align 32
-// CHECK: @__const.test15.s ={{.*}} constant { i32, [4 x i8], i64, [8 x i8], [8 x i8] } { i32 1, [4 x i8] zeroinitializer, i64 0, [8 x i8] zeroinitializer, [8 x i8] zeroinitializer }, align 32
-// CHECK: @test16.s = internal global { i32, [4 x i8], i64, [8 x i8], [8 x i8] } { i32 1, [4 x i8] zeroinitializer, i64 0, [8 x i8] zeroinitializer, [8 x i8] zeroinitializer }, align 32
+
// Test empty initializer for union.
+//.
+// CHECK: @global_u1 = global %union.U1 zeroinitializer, align 4
+// CHECK: @global_u2 = global %union.U1 { i32 3, [12 x i8] zeroinitializer }, align 4
+// CHECK: @global_u2_from_cast = global { i32, [12 x i8] } { i32 3, [12 x i8] zeroinitializer }, align 4
+// CHECK: @global_s1 = global %struct.S1 zeroinitializer, align 4
+// CHECK: @global_s2 = global %struct.S1 { i32 3, %union.U1 zeroinitializer }, align 4
+// CHECK: @global_s3 = global %struct.S1 { i32 3, %union.U1 { i32 6, [12 x i8] zeroinitializer } }, align 4
+// CHECK: @global_const_u1 = constant %union.U1 { i32 4, [12 x i8] zeroinitializer }, align 4
+// CHECK: @global_s3_from_const_u1 = global %struct.S1 { i32 0, %union.U1 { i32 4, [12 x i8] zeroinitializer } }, align 4
+// CHECK: @global_u3 = global %union.U2 zeroinitializer, align 32
+// CHECK: @global_s4 = global { i32, [4 x i8], i64, [8 x i8], [8 x i8] } zeroinitializer, align 32
+// CHECK: @global_s5 = global { i32, [4 x i8], i64, [8 x i8], [8 x i8] } { i32 1, [4 x i8] zeroinitializer, i64 0, [8 x i8] zeroinitializer, [8 x i8] zeroinitializer }, align 32
+// CHECK: @test2.a = internal global %union.U1 zeroinitializer, align 4
+// CHECK: @__const.test3.a = private unnamed_addr constant %union.U1 { i32 3, [12 x i8] zeroinitializer }, align 4
+// CHECK: @test4.a = internal global %union.U1 { i32 3, [12 x i8] zeroinitializer }, align 4
+// CHECK: @test6.s = internal global %struct.S1 zeroinitializer, align 4
+// CHECK: @__const.test7.s = private unnamed_addr constant %struct.S1 { i32 3, %union.U1 zeroinitializer }, align 4
+// CHECK: @test8.s = internal global %struct.S1 { i32 3, %union.U1 zeroinitializer }, align 4
+// CHECK: @__const.test9.s = private unnamed_addr constant %struct.S1 { i32 3, %union.U1 { i32 6, [12 x i8] zeroinitializer } }, align 4
+// CHECK: @test10.s = internal global %struct.S1 { i32 3, %union.U1 { i32 6, [12 x i8] zeroinitializer } }, align 4
+// CHECK: @test12.a = internal global %union.U2 zeroinitializer, align 32
+// CHECK: @test14.s = internal global { i32, [4 x i8], i64, [8 x i8], [8 x i8] } zeroinitializer, align 32
+// CHECK: @__const.test15.s = private unnamed_addr constant { i32, [4 x i8], i64, [8 x i8], [8 x i8] } { i32 1, [4 x i8] zeroinitializer, i64 0, [8 x i8] zeroinitializer, [8 x i8] zeroinitializer }, align 32
+// CHECK: @test16.s = internal global { i32, [4 x i8], i64, [8 x i8], [8 x i8] } { i32 1, [4 x i8] zeroinitializer, i64 0, [8 x i8] zeroinitializer, [8 x i8] zeroinitializer }, align 32
+//.
+// CHECK-LABEL: define dso_local void @test1(
+// CHECK-SAME: ) #[[ATTR0:[0-9]+]] {
+// CHECK-NEXT: [[ENTRY:.*:]]
+// CHECK-NEXT: [[A:%.*]] = alloca [[UNION_U1:%.*]], align 4
+// CHECK-NEXT: call void @llvm.memset.p0.i64(ptr align 4 [[A]], i8 0, i64 16, i1 false)
+// CHECK-NEXT: ret void
+//
void test1() {
union U1 a = {};
- // CHECK-LABEL: @test1()
- // CHECK-NEXT: entry:
- // CHECK-NEXT: %[[A:.+]] = alloca %union.U1, align 4
- // CHECK-NEXT: call void @llvm.memset.p0.i64({{.*}}%[[A]], i8 0, i64 16, i1 false)
}
// Test empty initializer for union. Use static variable.
+// CHECK-LABEL: define dso_local void @test2(
+// CHECK-SAME: ) #[[ATTR0]] {
+// CHECK-NEXT: [[ENTRY:.*:]]
+// CHECK-NEXT: ret void
+//
void test2() {
static union U1 a = {};
- // CHECK-LABEL: @test2()
- // CHECK-NEXT: entry:
- // CHECK-NEXT: ret void
}
// Test only initializing a small field for union.
+// CHECK-LABEL: define dso_local void @test3(
+// CHECK-SAME: ) #[[ATTR0]] {
+// CHECK-NEXT: [[ENTRY:.*:]]
+// CHECK-NEXT: [[A:%.*]] = alloca [[UNION_U1:%.*]], align 4
+// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 4 [[A]], ptr align 4 @__const.test3.a, i64 16, i1 false)
+// CHECK-NEXT: ret void
+//
void test3() {
union U1 a = {3};
- // CHECK-LABEL: @test3()
- // CHECK-NEXT: entry:
- // CHECK-NEXT: %[[A:.+]] = alloca %union.U1
- // CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64({{.*}}%[[A]], {{.*}}@__const.test3.a, i64 16, i1 false)
}
// Test only initializing a small field for union. Use static variable.
+// CHECK-LABEL: define dso_local void @test4(
+// CHECK-SAME: ) #[[ATTR0]] {
+// CHECK-NEXT: [[ENTRY:.*:]]
+// CHECK-NEXT: ret void
+//
void test4() {
static union U1 a = {3};
- // CHECK-LABEL: @test4()
- // CHECK-NEXT: entry:
- // CHECK-NEXT: ret void
}
// Test union in struct. Use empty initializer for the struct.
+// CHECK-LABEL: define dso_local void @test5(
+// CHECK-SAME: ) #[[ATTR0]] {
+// CHECK-NEXT: [[ENTRY:.*:]]
+// CHECK-NEXT: [[S:%.*]] = alloca [[STRUCT_S1:%.*]], align 4
+// CHECK-NEXT: call void @llvm.memset.p0.i64(ptr align 4 [[S]], i8 0, i64 20, i1 false)
+// CHECK-NEXT: ret void
+//
void test5() {
struct S1 s = {};
- // CHECK-LABEL: @test5()
- // CHECK-NEXT: entry:
- // CHECK-NEXT: %[[S:.+]] = alloca %struct.S1
- // CHECK-NEXT: call void @llvm.memset.p0.i64({{.*}}%[[S]], i8 0, i64 20, i1 false)
}
// Test union in struct. Use empty initializer for the struct. Use static variable.
+// CHECK-LABEL: define dso_local void @test6(
+// CHECK-SAME: ) #[[ATTR0]] {
+// CHECK-NEXT: [[ENTRY:.*:]]
+// CHECK-NEXT: ret void
+//
void test6() {
static struct S1 s = {};
- // CHECK-LABEL: @test6()
- // CHECK-NEXT: entry:
- // CHECK-NEXT: ret void
}
// Test union in struct. Initialize other fields of the struct.
+// CHECK-LABEL: define dso_local void @test7(
+// CHECK-SAME: ) #[[ATTR0]] {
+// CHECK-NEXT: [[ENTRY:.*:]]
+// CHECK-NEXT: [[S:%.*]] = alloca [[STRUCT_S1:%.*]], align 4
+// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 4 [[S]], ptr align 4 @__const.test7.s, i64 20, i1 false)
+// CHECK-NEXT: ret void
+//
void test7() {
struct S1 s = {
.x = 3,
};
- // CHECK-LABEL: @test7()
- // CHECK-NEXT: entry:
- // CHECK-NEXT: %[[S:.+]] = alloca %struct.S1
- // CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64({{.*}}%[[S]], {{.*}}@__const.test7.s, i64 20, i1 false)
}
// Test union in struct. Initialize other fields of the struct. Use static variable.
+// CHECK-LABEL: define dso_local void @test8(
+// CHECK-SAME: ) #[[ATTR0]] {
+// CHECK-NEXT: [[ENTRY:.*:]]
+// CHECK-NEXT: ret void
+//
void test8() {
static struct S1 s = {
.x = 3,
};
- // CHECK-LABEL: @test8()
- // CHECK-NEXT: entry:
- // CHECK-NEXT: ret void
}
// Test union in struct. Initialize a small field for union.
+// CHECK-LABEL: define dso_local void @test9(
+// CHECK-SAME: ) #[[ATTR0]] {
+// CHECK-NEXT: [[ENTRY:.*:]]
+// CHECK-NEXT: [[S:%.*]] = alloca [[STRUCT_S1:%.*]], align 4
+// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 4 [[S]], ptr align 4 @__const.test9.s, i64 20, i1 false)
+// CHECK-NEXT: ret void
+//
void test9() {
struct S1 s = {.x = 3,
.y = {
.x = 6,
}};
- // CHECK-LABEL: @test9()
- // CHECK-NEXT: entry:
- // CHECK-NEXT: %[[S:.+]] = alloca %struct.S1
- // CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64({{.*}}%[[S]], {{.*}}@__const.test9.s, i64 20, i1 false)
}
// Test union in struct. Initialize a small field for union. Use static variable.
+// CHECK-LABEL: define dso_local void @test10(
+// CHECK-SAME: ) #[[ATTR0]] {
+// CHECK-NEXT: [[ENTRY:.*:]]
+// CHECK-NEXT: ret void
+//
void test10() {
static struct S1 s = {.x = 3,
.y = {
.x = 6,
}};
- // CHECK-LABEL: @test10()
- // CHECK-NEXT: entry:
- // CHECK-NEXT: ret void
}
// Test empty initializer for union with padding.
+// CHECK-LABEL: define dso_local void @test11(
+// CHECK-SAME: ) #[[ATTR0]] {
+// CHECK-NEXT: [[ENTRY:.*:]]
+// CHECK-NEXT: [[A:%.*]] = alloca [[UNION_U2:%.*]], align 32
+// CHECK-NEXT: call void @llvm.memset.p0.i64(ptr align 32 [[A]], i8 0, i64 32, i1 false)
+// CHECK-NEXT: ret void
+//
void test11() {
union U2 a = {};
- // CHECK-LABEL: @test11()
- // CHECK-NEXT: entry:
- // CHECK-NEXT: %[[A:.+]] = alloca %union.U2, align 32
- // CHECK-NEXT: call void @llvm.memset.p0.i64({{.*}}%[[A]], i8 0, i64 32, i1 false)
}
// Test empty initializer for union with padding. Use static variable.
+// CHECK-LABEL: define dso_local void @test12(
+// CHECK-SAME: ) #[[ATTR0]] {
+// CHECK-NEXT: [[ENTRY:.*:]]
+// CHECK-NEXT: ret void
+//
void test12() {
static union U2 a = {};
- // CHECK-LABEL: @test12()
- // CHECK-NEXT: entry:
- // CHECK-NEXT: ret void
}
// Test empty initializer for struct with padding.
+// CHECK-LABEL: define dso_local void @test13(
+// CHECK-SAME: ) #[[ATTR0]] {
+// CHECK-NEXT: [[ENTRY:.*:]]
+// CHECK-NEXT: [[S:%.*]] = alloca [[STRUCT_S2:%.*]], align 32
+// CHECK-NEXT: call void @llvm.memset.p0.i64(ptr align 32 [[S]], i8 0, i64 32, i1 false)
+// CHECK-NEXT: ret void
+//
void test13() {
struct S2 s = {};
- // CHECK-LABEL: @test13()
- // CHECK-NEXT: entry:
- // CHECK-NEXT: %[[S:.+]] = alloca %struct.S2, align 32
- // CHECK-NEXT: call void @llvm.memset.p0.i64({{.*}}%[[S]], i8 0, i64 32, i1 false)
}
// Test empty initializer for struct with padding. Use static variable.
+// CHECK-LABEL: define dso_local void @test14(
+// CHECK-SAME: ) #[[ATTR0]] {
+// CHECK-NEXT: [[ENTRY:.*:]]
+// CHECK-NEXT: ret void
+//
void test14() {
static struct S2 s = {};
- // CHECK-LABEL: @test14()
- // CHECK-NEXT: entry:
- // CHECK-NEXT: ret void
}
// Test partial initialization for struct with padding.
+// CHECK-LABEL: define dso_local void @test15(
+// CHECK-SAME: ) #[[ATTR0]] {
+// CHECK-NEXT: [[ENTRY:.*:]]
+// CHECK-NEXT: [[S:%.*]] = alloca [[STRUCT_S2:%.*]], align 32
+// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 32 [[S]], ptr align 32 @__const.test15.s, i64 32, i1 false)
+// CHECK-NEXT: ret void
+//
void test15() {
struct S2 s = {.x = 1};
- // CHECK-LABEL: @test15()
- // CHECK-NEXT: entry:
- // CHECK-NEXT: %[[S:.+]] = alloca %struct.S2, align 32
- // CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64({{.*}}%[[S]], {{.*}}@__const.test15.s, i64 32, i1 false)
}
// Test partial initialization for struct with padding. Use static variable.
+// CHECK-LABEL: define dso_local void @test16(
+// CHECK-SAME: ) #[[ATTR0]] {
+// CHECK-NEXT: [[ENTRY:.*:]]
+// CHECK-NEXT: ret void
+//
void test16() {
static struct S2 s = {.x = 1};
- // CHECK-LABEL: @test16()
- // CHECK-NEXT: entry:
- // CHECK-NEXT: ret void
}
+//.
+// CHECK: attributes #[[ATTR0]] = { noinline nounwind optnone "min-legal-vector-width"="0" "no-trapping-math"="true" "stack-protector-buffer-size"="8" "target-features"="+cx8,+mmx,+sse,+sse2,+x87" }
+// CHECK: attributes #[[ATTR1:[0-9]+]] = { nocallback nofree nounwind willreturn memory(argmem: write) }
+// CHECK: attributes #[[ATTR2:[0-9]+]] = { nocallback nofree nounwind willreturn memory(argmem: readwrite) }
+//.
+// CHECK: [[META0:![0-9]+]] = !{i32 1, !"wchar_size", i32 4}
+// CHECK: [[META1:![0-9]+]] = !{!"{{.*}}clang version {{.*}}"}
+//.
diff --git a/clang/test/CodeGen/linux-kernel-struct-union-initializer2.c b/clang/test/CodeGen/linux-kernel-struct-union-initializer2.c
index cb521c2ed0eee0..0a1ad3a369eacc 100644
--- a/clang/test/CodeGen/linux-kernel-struct-union-initializer2.c
+++ b/clang/test/CodeGen/linux-kernel-struct-union-initializer2.c
@@ -1,3 +1,4 @@
+// NOTE: Assertions have been autogenerated by utils/update_cc_test_checks.py UTC_ARGS: --version 5
// RUN: %clang_cc1 -triple x86_64-unknown-unknown -std=gnu11 -verify -emit-llvm %s -o - | FileCheck %s
// expected-no-diagnostics
@@ -29,47 +30,111 @@ struct S4 {
};
// Test non-const initializer for union with padding.
+// CHECK-LABEL: define dso_local void @test1(
+// CHECK-SAME: i32 noundef [[X:%.*]]) #[[ATTR0:[0-9]+]] {
+// CHECK-NEXT: [[ENTRY:.*:]]
+// CHECK-NEXT: [[X_ADDR:%.*]] = alloca i32, align 4
+// CHECK-NEXT: [[A:%.*]] = alloca [[UNION_U1:%.*]], align 4
+// CHECK-NEXT: store i32 [[X]], ptr [[X_ADDR]], align 4
+// CHECK-NEXT: [[TMP0:%.*]] = load i32, ptr [[X_ADDR]], align 4
+// CHECK-NEXT: store i32 [[TMP0]], ptr [[A]], align 4
+// CHECK-NEXT: [[TMP1:%.*]] = getelementptr i8, ptr [[A]], i64 4
+// CHECK-NEXT: call void @llvm.memset.p0.i64(ptr align 4 [[TMP1]], i8 0, i64 4, i1 false)
+// CHECK-NEXT: ret void
+//
void test1(int x) {
union U1 a = {x};
- // CHECK-LABEL: @test1
- // CHECK: %[[A:.+]] = alloca %union.U1
- // CHECK: %[[B:.+]] = getelementptr i8, ptr %[[A]], i64 4
- // CHECK-NEXT: call void @llvm.memset.p0.i64(ptr align 4 %[[B]], i8 0, i64 4, i1 false)
}
// Test non-const initializer for struct with padding.
+// CHECK-LABEL: define dso_local void @test2(
+// CHECK-SAME: i64 noundef [[Y:%.*]]) #[[ATTR0]] {
+// CHECK-NEXT: [[ENTRY:.*:]]
+// CHECK-NEXT: [[Y_ADDR:%.*]] = alloca i64, align 8
+// CHECK-NEXT: [[S:%.*]] = alloca [[STRUCT_S1:%.*]], align 8
+// CHECK-NEXT: store i64 [[Y]], ptr [[Y_ADDR]], align 8
+// CHECK-NEXT: [[X:%.*]] = getelementptr inbounds nuw [[STRUCT_S1]], ptr [[S]], i32 0, i32 0
+// CHECK-NEXT: store i32 0, ptr [[X]], align 8
+// CHECK-NEXT: [[TMP0:%.*]] = getelementptr i8, ptr [[S]], i64 4
+// CHECK-NEXT: call void @llvm.memset.p0.i64(ptr align 4 [[TMP0]], i8 0, i64 4, i1 false)
+// CHECK-NEXT: [[Y1:%.*]] = getelementptr inbounds nuw [[STRUCT_S1]], ptr [[S]], i32 0, i32 1
+// CHECK-NEXT: [[TMP1:%.*]] = load i64, ptr [[Y_ADDR]], align 8
+// CHECK-NEXT: store i64 [[TMP1]], ptr [[Y1]], align 8
+// CHECK-NEXT: ret void
+//
void test2(long long y) {
struct S1 s = {.y = y};
- // CHECK-LABEL: @test2
- // CHECK: %[[A:.+]] = alloca %struct.S1
- // CHECK: %[[B:.+]] = getelementptr i8, ptr %[[A]], i64 4
- // CHECK-NEXT: call void @llvm.memset.p0.i64(ptr align 4 %[[B]], i8 0, i64 4, i1 false)
}
// Test non-const initializer for struct with padding and bit fields.
+// CHECK-LABEL: define dso_local void @test3(
+// CHECK-SAME: i8 noundef zeroext [[B:%.*]]) #[[ATTR0]] {
+// CHECK-NEXT: [[ENTRY:.*:]]
+// CHECK-NEXT: [[B_ADDR:%.*]] = alloca i8, align 1
+// CHECK-NEXT: [[S:%.*]] = alloca [[STRUCT_S2:%.*]], align 4
+// CHECK-NEXT: store i8 [[B]], ptr [[B_ADDR]], align 1
+// CHECK-NEXT: [[TMP0:%.*]] = load i8, ptr [[B_ADDR]], align 1
+// CHECK-NEXT: [[TMP1:%.*]] = zext i8 [[TMP0]] to i16
+// CHECK-NEXT: [[BF_LOAD:%.*]] = load i16, ptr [[S]], align 4
+// CHECK-NEXT: [[BF_VALUE:%.*]] = and i16 [[TMP1]], 7
+// CHECK-NEXT: [[BF_CLEAR:%.*]] = and i16 [[BF_LOAD]], -8
+// CHECK-NEXT: [[BF_SET:%.*]] = or i16 [[BF_CLEAR]], [[BF_VALUE]]
+// CHECK-NEXT: store i16 [[BF_SET]], ptr [[S]], align 4
+// CHECK-NEXT: [[BF_LOAD1:%.*]] = load i16, ptr [[S]], align 4
+// CHECK-NEXT: [[BF_CLEAR2:%.*]] = and i16 [[BF_LOAD1]], -16129
+// CHECK-NEXT: [[BF_SET3:%.*]] = or i16 [[BF_CLEAR2]], 0
+// CHECK-NEXT: store i16 [[BF_SET3]], ptr [[S]], align 4
+// CHECK-NEXT: [[BF_LOAD4:%.*]] = load i16, ptr [[S]], align 4
+// CHECK-NEXT: [[BF_CLEAR5:%.*]] = and i16 [[BF_LOAD4]], 16383
+// CHECK-NEXT: [[BF_SET6:%.*]] = or i16 [[BF_CLEAR5]], 0
+// CHECK-NEXT: store i16 [[BF_SET6]], ptr [[S]], align 4
+// CHECK-NEXT: [[TMP2:%.*]] = getelementptr i8, ptr [[S]], i64 2
+// CHECK-NEXT: call void @llvm.memset.p0.i64(ptr align 2 [[TMP2]], i8 0, i64 2, i1 false)
+// CHECK-NEXT: [[I:%.*]] = getelementptr inbounds nuw [[STRUCT_S2]], ptr [[S]], i32 0, i32 1
+// CHECK-NEXT: store i32 0, ptr [[I]], align 4
+// CHECK-NEXT: ret void
+//
void test3(unsigned char b) {
struct S2 s = {.b1 = b};
- // CHECK-LABEL: @test3
- // CHECK: %[[A:.+]] = alloca %struct.S2
- // CHECK: %[[B:.+]] = getelementptr i8, ptr %[[A]], i64 2
- // CHECK-NEXT: call void @llvm.memset.p0.i64(ptr align 2 %[[B]], i8 0, i64 2, i1 false)
}
// Test non-const initializer for struct with padding at the end of the struct.
+// CHECK-LABEL: define dso_local void @test4(
+// CHECK-SAME: i32 noundef [[X:%.*]]) #[[ATTR0]] {
+// CHECK-NEXT: [[ENTRY:.*:]]
+// CHECK-NEXT: [[X_ADDR:%.*]] = alloca i32, align 4
+// CHECK-NEXT: [[S:%.*]] = alloca [[STRUCT_S3:%.*]], align 8
+// CHECK-NEXT: store i32 [[X]], ptr [[X_ADDR]], align 4
+// CHECK-NEXT: [[X1:%.*]] = getelementptr inbounds nuw [[STRUCT_S3]], ptr [[S]], i32 0, i32 0
+// CHECK-NEXT: [[TMP0:%.*]] = load i32, ptr [[X_ADDR]], align 4
+// CHECK-NEXT: store i32 [[TMP0]], ptr [[X1]], align 8
+// CHECK-NEXT: [[TMP1:%.*]] = getelementptr i8, ptr [[S]], i64 4
+// CHECK-NEXT: call void @llvm.memset.p0.i64(ptr align 4 [[TMP1]], i8 0, i64 4, i1 false)
+// CHECK-NEXT: ret void
+//
void test4(int x) {
struct S3 s = {x};
- // CHECK-LABEL: @test4
- // CHECK: %[[A:.+]] = alloca %struct.S3
- // CHECK: %[[B:.+]] = getelementptr i8, ptr %[[A]], i64 4
- // CHECK-NEXT: call void @llvm.memset.p0.i64(ptr align 4 %[[B]], i8 0, i64 4, i1 false)
}
// Test non-const initializer for union in struct.
+// CHECK-LABEL: define dso_local void @test5(
+// CHECK-SAME: i32 noundef [[A:%.*]], i32 noundef [[B:%.*]]) #[[ATTR0]] {
+// CHECK-NEXT: [[ENTRY:.*:]]
+// CHECK-NEXT: [[A_ADDR:%.*]] = alloca i32, align 4
+// CHECK-NEXT: [[B_ADDR:%.*]] = alloca i32, align 4
+// CHECK-NEXT: [[S:%.*]] = alloca [[STRUCT_S4:%.*]], align 4
+// CHECK-NEXT: store i32 [[A]], ptr [[A_ADDR]], align 4
+// CHECK-NEXT: store i32 [[B]], ptr [[B_ADDR]], align 4
+// CHECK-NEXT: [[A1:%.*]] = getelementptr inbounds nuw [[STRUCT_S4]], ptr [[S]], i32 0, i32 0
+// CHECK-NEXT: [[TMP0:%.*]] = load i32, ptr [[A_ADDR]], align 4
+// CHECK-NEXT: store i32 [[TMP0]], ptr [[A1]], align 4
+// CHECK-NEXT: [[B2:%.*]] = getelementptr inbounds nuw [[STRUCT_S4]], ptr [[S]], i32 0, i32 1
+// CHECK-NEXT: [[TMP1:%.*]] = load i32, ptr [[B_ADDR]], align 4
+// CHECK-NEXT: store i32 [[TMP1]], ptr [[B2]], align 4
+// CHECK-NEXT: [[TMP2:%.*]] = getelementptr i8, ptr [[B2]], i64 4
+// CHECK-NEXT: call void @llvm.memset.p0.i64(ptr align 4 [[TMP2]], i8 0, i64 4, i1 false)
+// CHECK-NEXT: ret void
+//
void test5(int a, int b) {
struct S4 s = {a, {b}};
- // CHECK-LABEL: @test5
- // CHECK: %[[A:.+]] = alloca %struct.S4
- // CHECK: %[[B:.+]] = getelementptr inbounds nuw %struct.S4, ptr %[[A]], i32 0, i32 1
- // CHECK: %[[C:.+]] = getelementptr i8, ptr %[[B]], i64 4
- // CHECK-NEXT: call void @llvm.memset.p0.i64(ptr align 4 %[[C]], i8 0, i64 4, i1 false)
}
More information about the cfe-commits
mailing list