Skip to content

Commit e1d7577

Browse files
committed
[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 #78034 (comment), the change isn't required by C23, but it's standards conforming to do so.
1 parent 12189f8 commit e1d7577

24 files changed

+484
-103
lines changed

clang/docs/LanguageExtensions.rst

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5821,3 +5821,24 @@ specify the starting offset to begin embedding from. The resources is treated
58215821
as being empty if the specified offset is larger than the number of bytes in
58225822
the resource. The offset will be applied *before* any ``limit`` parameters are
58235823
applied.
5824+
5825+
Union and aggregate initialization in C
5826+
=======================================
5827+
5828+
In C23 (N2900), when an object is initialized from initializer ``= {}``, all
5829+
elements of arrays, all members of structs, and the first members of unions are
5830+
empty-initialized recursively. In addition, all padding bits are initialized to
5831+
zero.
5832+
5833+
Clang guarantees the following behaviors:
5834+
5835+
* ``1:`` Clang supports initializer ``= {}`` mentioned above in all C
5836+
standards.
5837+
5838+
* ``2:`` When unions are initialized from initializer ``= {}``, bytes outside
5839+
of the first members of unions are also initialized to zero.
5840+
5841+
* ``3:`` When unions, structures and arrays are initialized from initializer
5842+
``= { initializer-list }``, all members not explicitly initialized in
5843+
the initializer list are empty-initialized recursively. In addition, all
5844+
padding bits are initialized to zero.

clang/lib/CodeGen/CGDecl.cpp

Lines changed: 77 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -335,6 +335,13 @@ llvm::Constant *CodeGenModule::getOrCreateStaticVarDecl(
335335
return Addr;
336336
}
337337

338+
enum class IsPattern { No, Yes };
339+
340+
static llvm::Constant *replaceUndef(CodeGenModule &CGM, IsPattern isPattern,
341+
llvm::Constant *constant);
342+
static llvm::Constant *constWithPadding(CodeGenModule &CGM, IsPattern isPattern,
343+
llvm::Constant *constant);
344+
338345
/// AddInitializerToStaticVarDecl - Add the initializer for 'D' to the
339346
/// global variable that has already been created for it. If the initializer
340347
/// has a different type than GV does, this may free GV and return a different
@@ -361,6 +368,51 @@ CodeGenFunction::AddInitializerToStaticVarDecl(const VarDecl &D,
361368
}
362369
return GV;
363370
}
371+
if (!getLangOpts().CPlusPlus) {
372+
// In C23 (N3096) $6.7.10:
373+
// """
374+
// If any object is initialized with an empty iniitializer, then it is subject to default
375+
// initialization:
376+
// - if it is an aggregate, every member is initialized (recursively) according to these rules,
377+
// and any padding is initialized to zero bits;
378+
// - if it is a union, the first named member is initialized (recursively) according to these
379+
// rules, and any padding is initialized to zero bits.
380+
//
381+
// If the aggregate or union contains elements or members that are aggregates or unions, these
382+
// rules apply recursively to the subaggregates or contained unions.
383+
//
384+
// If there are fewer initializers in a brace-enclosed list than there are elements or members
385+
// of an aggregate, or fewer characters in a string literal used to initialize an array of
386+
// known size than there are elements in the array, the remainder of the aggregate is subject
387+
// to default initialization.
388+
// """
389+
//
390+
// From my understanding, the standard is ambiguous in the following two areas:
391+
// 1. For a union type with empty initializer, if the first named member is not the largest
392+
// member, then the bytes comes after the first named member but before padding are left
393+
// unspecified. An example is:
394+
// union U { int a; long long b;};
395+
// union U u = {}; // The first 4 bytes are 0, but 4-8 bytes are left unspecified.
396+
//
397+
// 2. It only mentions padding for empty initializer, but doesn't mention padding for a
398+
// non empty initialization list. And if the aggregation or union contains elements or members
399+
// that are aggregates or unions, and some are non empty initializers, while others are empty
400+
// initiailizers, the padding initialization is unclear. An example is:
401+
// struct S1 { int a; long long b; };
402+
// struct S2 { char c; struct S1 s1; };
403+
// // The values for paddings between s2.c and s2.s1.a, between s2.s1.a and s2.s1.b are
404+
// // unclear.
405+
// struct S2 s2 = { 'c' };
406+
//
407+
// Here we choose to zero initiailize left bytes of a union type. Because projects like the
408+
// Linux kernel are relying on this behavior. If we don't explicitly zero initialize them, the
409+
// undef values can be optimized to return gabage data.
410+
// We also choose to zero initialize paddings for aggregates and unions, no matter they are
411+
// initialized by empty initializers or non empty initializers. This can provide a consistent
412+
// behavior. So projects like the Linux kernel can rely on it.
413+
Init = constWithPadding(CGM, IsPattern::No,
414+
replaceUndef(CGM, IsPattern::No, Init));
415+
}
364416

365417
#ifndef NDEBUG
366418
CharUnits VarSize = CGM.getContext().getTypeSizeInChars(D.getType()) +
@@ -1038,8 +1090,6 @@ static bool shouldSplitConstantStore(CodeGenModule &CGM,
10381090
return false;
10391091
}
10401092

1041-
enum class IsPattern { No, Yes };
1042-
10431093
/// Generate a constant filled with either a pattern or zeroes.
10441094
static llvm::Constant *patternOrZeroFor(CodeGenModule &CGM, IsPattern isPattern,
10451095
llvm::Type *Ty) {
@@ -1049,9 +1099,6 @@ static llvm::Constant *patternOrZeroFor(CodeGenModule &CGM, IsPattern isPattern,
10491099
return llvm::Constant::getNullValue(Ty);
10501100
}
10511101

1052-
static llvm::Constant *constWithPadding(CodeGenModule &CGM, IsPattern isPattern,
1053-
llvm::Constant *constant);
1054-
10551102
/// Helper function for constWithPadding() to deal with padding in structures.
10561103
static llvm::Constant *constStructWithPadding(CodeGenModule &CGM,
10571104
IsPattern isPattern,
@@ -1109,6 +1156,9 @@ static llvm::Constant *constWithPadding(CodeGenModule &CGM, IsPattern isPattern,
11091156
if (ZeroInitializer) {
11101157
OpValue = llvm::Constant::getNullValue(ElemTy);
11111158
PaddedOp = constWithPadding(CGM, isPattern, OpValue);
1159+
// Avoid iterating large arrays with zero initializer when possible.
1160+
if (PaddedOp->getType() == ElemTy)
1161+
return constant;
11121162
}
11131163
for (unsigned Op = 0; Op != Size; ++Op) {
11141164
if (!ZeroInitializer) {
@@ -1954,21 +2004,22 @@ void CodeGenFunction::EmitAutoVarInit(const AutoVarEmission &emission) {
19542004
D.mightBeUsableInConstantExpressions(getContext())) {
19552005
assert(!capturedByInit && "constant init contains a capturing block?");
19562006
constant = ConstantEmitter(*this).tryEmitAbstractForInitializer(D);
1957-
if (constant && !constant->isZeroValue() &&
1958-
(trivialAutoVarInit !=
1959-
LangOptions::TrivialAutoVarInitKind::Uninitialized)) {
1960-
IsPattern isPattern =
1961-
(trivialAutoVarInit == LangOptions::TrivialAutoVarInitKind::Pattern)
1962-
? IsPattern::Yes
1963-
: IsPattern::No;
1964-
// C guarantees that brace-init with fewer initializers than members in
1965-
// the aggregate will initialize the rest of the aggregate as-if it were
1966-
// static initialization. In turn static initialization guarantees that
1967-
// padding is initialized to zero bits. We could instead pattern-init if D
1968-
// has any ImplicitValueInitExpr, but that seems to be unintuitive
1969-
// behavior.
1970-
constant = constWithPadding(CGM, IsPattern::No,
1971-
replaceUndef(CGM, isPattern, constant));
2007+
if (constant && !constant->isZeroValue()) {
2008+
if (!getLangOpts().CPlusPlus) {
2009+
// See comment in CodeGenFunction::AddInitializerToStaticVarDecl().
2010+
constant = constWithPadding(CGM, IsPattern::No,
2011+
replaceUndef(CGM, IsPattern::No, constant));
2012+
} else if (trivialAutoVarInit !=
2013+
LangOptions::TrivialAutoVarInitKind::Uninitialized) {
2014+
IsPattern isPattern =
2015+
(trivialAutoVarInit == LangOptions::TrivialAutoVarInitKind::Pattern)
2016+
? IsPattern::Yes
2017+
: IsPattern::No;
2018+
// We could instead pattern-init padding if D has any
2019+
// ImplicitValueInitExpr, but that seems to be unintuitive behavior.
2020+
constant = constWithPadding(CGM, IsPattern::No,
2021+
replaceUndef(CGM, isPattern, constant));
2022+
}
19722023
}
19732024

19742025
if (D.getType()->isBitIntType() &&
@@ -2861,3 +2912,9 @@ CodeGenModule::getOMPAllocateAlignment(const VarDecl *VD) {
28612912
}
28622913
return std::nullopt;
28632914
}
2915+
2916+
llvm::Constant *
2917+
CodeGenModule::zeroInitGlobalVarInitializer(llvm::Constant *Init) {
2918+
return constWithPadding(*this, IsPattern::No,
2919+
replaceUndef(*this, IsPattern::No, Init));
2920+
}

clang/lib/CodeGen/CGExprAgg.cpp

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1720,6 +1720,9 @@ void AggExprEmitter::VisitCXXParenListOrInitListExpr(
17201720
// Prepare a 'this' for CXXDefaultInitExprs.
17211721
CodeGenFunction::FieldConstructionScope FCS(CGF, Dest.getAddress());
17221722

1723+
// See comment in CodeGenFunction::AddInitializerToStaticVarDecl().
1724+
bool ZeroInitPadding = !CGF.getLangOpts().CPlusPlus && !Dest.isZeroed();
1725+
17231726
if (record->isUnion()) {
17241727
// Only initialize one field of a union. The field itself is
17251728
// specified by the initializer list.
@@ -1748,13 +1751,38 @@ void AggExprEmitter::VisitCXXParenListOrInitListExpr(
17481751
// Default-initialize to null.
17491752
EmitNullInitializationToLValue(FieldLoc);
17501753
}
1754+
if (ZeroInitPadding) {
1755+
CharUnits TotalSize = Dest.getPreferredSize(CGF.getContext(), DestLV.getType());
1756+
CharUnits FieldSize = CGF.getContext().getTypeSizeInChars(FieldLoc.getType());
1757+
if (FieldSize < TotalSize) {
1758+
CharUnits LeftSize = TotalSize - FieldSize;
1759+
llvm::Constant *LeftSizeVal = CGF.Builder.getInt64(LeftSize.getQuantity());
1760+
Address BaseLoc = Dest.getAddress().withElementType(CGF.Int8Ty);
1761+
Address LeftLoc = CGF.Builder.CreateConstGEP(BaseLoc, LeftSize.getQuantity());
1762+
CGF.Builder.CreateMemSet(LeftLoc, CGF.Builder.getInt8(0), LeftSizeVal, false);
1763+
}
1764+
}
17511765

17521766
return;
17531767
}
17541768

17551769
// Here we iterate over the fields; this makes it simpler to both
17561770
// default-initialize fields and skip over unnamed fields.
1771+
const ASTRecordLayout &Layout = CGF.getContext().getASTRecordLayout(record);
1772+
Address BaseLoc = Dest.getAddress().withElementType(CGF.Int8Ty);
1773+
uint64_t SizeSoFar = 0;
17571774
for (const auto *field : record->fields()) {
1775+
if (ZeroInitPadding) {
1776+
unsigned FieldIndex = field->getFieldIndex();
1777+
uint64_t CurOff = Layout.getFieldOffset(FieldIndex) / CGF.getContext().getCharWidth();
1778+
if (SizeSoFar < CurOff) {
1779+
llvm::Constant* PaddingSizeVal = CGF.Builder.getInt64(CurOff - SizeSoFar);
1780+
Address PaddingLoc = CGF.Builder.CreateConstGEP(BaseLoc, SizeSoFar);
1781+
CGF.Builder.CreateMemSet(PaddingLoc, CGF.Builder.getInt8(0), PaddingSizeVal, false);
1782+
}
1783+
CharUnits FieldSize = CGF.getContext().getTypeSizeInChars(field->getType());
1784+
SizeSoFar = CurOff + FieldSize.getQuantity();
1785+
}
17581786
// We're done once we hit the flexible array member.
17591787
if (field->getType()->isIncompleteArrayType())
17601788
break;
@@ -1796,6 +1824,14 @@ void AggExprEmitter::VisitCXXParenListOrInitListExpr(
17961824
}
17971825
}
17981826
}
1827+
if (ZeroInitPadding) {
1828+
uint64_t TotalSize = Dest.getPreferredSize(CGF.getContext(), DestLV.getType()).getQuantity();
1829+
if (SizeSoFar < TotalSize) {
1830+
llvm::Constant* PaddingSizeVal = CGF.Builder.getInt64(TotalSize - SizeSoFar);
1831+
Address PaddingLoc = CGF.Builder.CreateConstGEP(BaseLoc, SizeSoFar);
1832+
CGF.Builder.CreateMemSet(PaddingLoc, CGF.Builder.getInt8(0), PaddingSizeVal, false);
1833+
}
1834+
}
17991835
}
18001836

18011837
void AggExprEmitter::VisitArrayInitLoopExpr(const ArrayInitLoopExpr *E,

clang/lib/CodeGen/CodeGenModule.cpp

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5473,6 +5473,10 @@ void CodeGenModule::EmitGlobalVarDefinition(const VarDecl *D,
54735473
Init = llvm::UndefValue::get(getTypes().ConvertType(T));
54745474
}
54755475
} else {
5476+
if (!getLangOpts().CPlusPlus) {
5477+
// See comment in CodeGenFunction::AddInitializerToStaticVarDecl().
5478+
Initializer = zeroInitGlobalVarInitializer(Initializer);
5479+
}
54765480
Init = Initializer;
54775481
// We don't need an initializer, so remove the entry for the delayed
54785482
// initializer position (just in case this entry was delayed) if we

clang/lib/CodeGen/CodeGenModule.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1866,6 +1866,8 @@ class CodeGenModule : public CodeGenTypeCache {
18661866

18671867
llvm::Metadata *CreateMetadataIdentifierImpl(QualType T, MetadataTypeMap &Map,
18681868
StringRef Suffix);
1869+
1870+
llvm::Constant *zeroInitGlobalVarInitializer(llvm::Constant *Init);
18691871
};
18701872

18711873
} // end namespace CodeGen

clang/test/CodeGen/2008-07-22-bitfield-init-after-zero-len-array.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,4 +8,4 @@ struct et7 {
88
52,
99
};
1010

11-
// CHECK: @yv7 ={{.*}} global %struct.et7 { [0 x float] zeroinitializer, i8 52 }
11+
// CHECK: @yv7 ={{.*}} global { [0 x float], i8, [3 x i8] } { [0 x float] zeroinitializer, i8 52, [3 x i8] zeroinitializer }

clang/test/CodeGen/2008-08-07-AlignPadding1.c

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -20,9 +20,9 @@ struct gc_generation {
2020

2121
#define GEN_HEAD(n) (&generations[n].head)
2222

23-
// The idea is that there are 6 undefs in this structure initializer to cover
23+
// The idea is that there are 6 zeroinitializers in this structure initializer to cover
2424
// the padding between elements.
25-
// 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 }]
25+
// 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 }]
2626
/* linked lists of container objects */
2727
struct gc_generation generations[3] = {
2828
/* PyGC_Head, threshold, count */

clang/test/CodeGen/2009-06-14-anonymous-union-init.c

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ struct sysfs_dirent {
77
};
88
struct sysfs_dirent sysfs_root = { {}, 16877 };
99

10-
// CHECK: @sysfs_root = {{.*}}global %struct.sysfs_dirent { %union.anon zeroinitializer, i16 16877 }
10+
// CHECK: @sysfs_root = {{.*}}global { %union.anon, i16, [2 x i8] } { %union.anon zeroinitializer, i16 16877, [2 x i8] zeroinitializer }
1111

1212
struct Foo {
1313
union { struct empty {} x; };
@@ -16,4 +16,4 @@ struct Foo {
1616
struct Foo foo = { {}, 16877 };
1717

1818
// EMPTY: @foo = {{.*}}global %struct.Foo { i16 16877 }
19-
// EMPTY-MSVC: @foo = {{.*}}global %struct.Foo { [4 x i8] undef, i16 16877 }
19+
// EMPTY-MSVC: @foo = {{.*}}global %struct.Foo { [4 x i8] zeroinitializer, i16 16877 }

clang/test/CodeGen/64bit-swiftcall.c

Lines changed: 5 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -14,8 +14,6 @@
1414

1515
// CHECK-DAG: %struct.atomic_padded = type { { %struct.packed, [7 x i8] } }
1616
// CHECK-DAG: %struct.packed = type <{ i64, i8 }>
17-
//
18-
// CHECK: [[STRUCT2_RESULT:@.*]] = private {{.*}} constant [[STRUCT2_TYPE:%.*]] { i32 0, i8 0, i8 undef, i8 0, i32 0, i32 0 }
1917

2018
/*****************************************************************************/
2119
/****************************** PARAMETER ABIS *******************************/
@@ -162,8 +160,8 @@ typedef struct {
162160
} struct_2;
163161
TEST(struct_2);
164162
// CHECK-LABEL: define{{.*}} swiftcc { i64, i64 } @return_struct_2() {{.*}}{
165-
// CHECK: [[RET:%.*]] = alloca [[STRUCT2_TYPE]], align 4
166-
// CHECK: call void @llvm.memcpy{{.*}}({{.*}}[[RET]], {{.*}}[[STRUCT2_RESULT]]
163+
// CHECK: [[RET:%.*]] = alloca [[STRUCT2:%.*]], align 4
164+
// CHECK: call void @llvm.memset
167165
// CHECK: [[GEP0:%.*]] = getelementptr inbounds { i64, i64 }, ptr [[RET]], i32 0, i32 0
168166
// CHECK: [[T0:%.*]] = load i64, ptr [[GEP0]], align 4
169167
// CHECK: [[GEP1:%.*]] = getelementptr inbounds { i64, i64 }, ptr [[RET]], i32 0, i32 1
@@ -173,15 +171,15 @@ TEST(struct_2);
173171
// CHECK: ret { i64, i64 } [[R1]]
174172
// CHECK: }
175173
// CHECK-LABEL: define{{.*}} swiftcc void @take_struct_2(i64 %0, i64 %1) {{.*}}{
176-
// CHECK: [[V:%.*]] = alloca [[STRUCT:%.*]], align 4
174+
// CHECK: [[V:%.*]] = alloca [[STRUCT2]], align 4
177175
// CHECK: [[GEP0:%.*]] = getelementptr inbounds { i64, i64 }, ptr [[V]], i32 0, i32 0
178176
// CHECK: store i64 %0, ptr [[GEP0]], align 4
179177
// CHECK: [[GEP1:%.*]] = getelementptr inbounds { i64, i64 }, ptr [[V]], i32 0, i32 1
180178
// CHECK: store i64 %1, ptr [[GEP1]], align 4
181179
// CHECK: ret void
182180
// CHECK: }
183181
// CHECK-LABEL: define{{.*}} void @test_struct_2() {{.*}} {
184-
// CHECK: [[TMP:%.*]] = alloca [[STRUCT2_TYPE]], align 4
182+
// CHECK: [[TMP:%.*]] = alloca [[STRUCT2]], align 4
185183
// CHECK: [[CALL:%.*]] = call swiftcc { i64, i64 } @return_struct_2()
186184
// CHECK: [[GEP:%.*]] = getelementptr inbounds {{.*}} [[TMP]], i32 0, i32 0
187185
// CHECK: [[T0:%.*]] = extractvalue { i64, i64 } [[CALL]], 0
@@ -254,7 +252,7 @@ typedef union {
254252
TEST(union_het_fp)
255253
// CHECK-LABEL: define{{.*}} swiftcc i64 @return_union_het_fp()
256254
// CHECK: [[RET:%.*]] = alloca [[UNION:%.*]], align 8
257-
// CHECK: call void @llvm.memcpy{{.*}}(ptr align 8 [[RET]]
255+
// CHECK: call void @llvm.memset{{.*}}(ptr align 8 [[RET]]
258256
// CHECK: [[GEP:%.*]] = getelementptr inbounds { i64 }, ptr [[RET]], i32 0, i32 0
259257
// CHECK: [[R0:%.*]] = load i64, ptr [[GEP]], align 8
260258
// CHECK: ret i64 [[R0]]

clang/test/CodeGen/arm-swiftcall.c

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -172,7 +172,7 @@ typedef struct {
172172
TEST(struct_2);
173173
// CHECK-LABEL: define{{.*}} @return_struct_2()
174174
// CHECK: [[RET:%.*]] = alloca [[REC:%.*]], align 4
175-
// CHECK: @llvm.memcpy
175+
// CHECK: @llvm.memset
176176
// CHECK: [[T0:%.*]] = getelementptr inbounds [[AGG:{ i32, i32, float, float }]], ptr [[RET]], i32 0, i32 0
177177
// CHECK: [[FIRST:%.*]] = load i32, ptr [[T0]], align 4
178178
// CHECK: [[T0:%.*]] = getelementptr inbounds [[AGG]], ptr [[RET]], i32 0, i32 1
@@ -274,7 +274,7 @@ typedef union {
274274
TEST(union_het_fp)
275275
// CHECK-LABEL: define{{.*}} @return_union_het_fp()
276276
// CHECK: [[RET:%.*]] = alloca [[REC:%.*]], align {{(4|8)}}
277-
// CHECK: @llvm.memcpy
277+
// CHECK: @llvm.memset
278278
// CHECK: [[T0:%.*]] = getelementptr inbounds [[AGG:{ i32, i32 }]], ptr [[RET]], i32 0, i32 0
279279
// CHECK: [[FIRST:%.*]] = load i32, ptr [[T0]], align {{(4|8)}}
280280
// CHECK: [[T0:%.*]] = getelementptr inbounds [[AGG]], ptr [[RET]], i32 0, i32 1

clang/test/CodeGen/const-init.c

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -170,7 +170,7 @@ void g30(void) {
170170
int : 1;
171171
int x;
172172
} a = {};
173-
// CHECK: @g30.a = internal global %struct.anon.1 <{ i8 undef, i32 0 }>, align 1
173+
// CHECK: @g30.a = internal global %struct.anon.1 zeroinitializer, align 1
174174
#pragma pack()
175175
}
176176

@@ -182,7 +182,7 @@ void g31(void) {
182182
short z;
183183
} a = {23122, -12312731, -312};
184184
#pragma pack()
185-
// CHECK: @g31.a = internal global %struct.anon.2 { i16 23122, i32 -12312731, i16 -312 }, align 4
185+
// 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
186186
}
187187

188188
// Clang should evaluate this in constant context, so floating point mode should

clang/test/CodeGen/decl.c

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,10 +2,10 @@
22

33
// CHECK: @test1.x = internal constant [12 x i32] [i32 1
44
// CHECK: @__const.test2.x = private unnamed_addr constant [13 x i32] [i32 1,
5-
// CHECK: @test5w = {{(dso_local )?}}global { i32, [4 x i8] } { i32 2, [4 x i8] undef }
5+
// CHECK: @test5w = {{(dso_local )?}}global { i32, [4 x i8] } { i32 2, [4 x i8] zeroinitializer }
66
// CHECK: @test5y = {{(dso_local )?}}global { double } { double 7.300000e+0{{[0]*}}1 }
77

8-
// CHECK: @__const.test6.x = private unnamed_addr constant %struct.SelectDest { i8 1, i8 2, i32 3, i32 0 }
8+
// 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 }
99

1010
// CHECK: @test7 = {{(dso_local )?}}global [2 x %struct.test7s] [%struct.test7s { i32 1, i32 2 }, %struct.test7s { i32 4, i32 0 }]
1111

0 commit comments

Comments
 (0)