Skip to content

Commit fe87409

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: In C, when an initializer is provided for a variable, zero-initialize undef and padding fields in the initializer. As suggested in #78034 (comment), the change isn't required by C23, but it's standards conforming to do so.
1 parent 8431170 commit fe87409

18 files changed

+307
-90
lines changed

clang/lib/CodeGen/CGDecl.cpp

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

337+
enum class IsPattern { No, Yes };
338+
339+
static llvm::Constant *replaceUndef(CodeGenModule &CGM, IsPattern isPattern,
340+
llvm::Constant *constant);
341+
static llvm::Constant *constWithPadding(CodeGenModule &CGM, IsPattern isPattern,
342+
llvm::Constant *constant);
343+
337344
/// AddInitializerToStaticVarDecl - Add the initializer for 'D' to the
338345
/// global variable that has already been created for it. If the initializer
339346
/// has a different type than GV does, this may free GV and return a different
@@ -360,6 +367,13 @@ CodeGenFunction::AddInitializerToStaticVarDecl(const VarDecl &D,
360367
}
361368
return GV;
362369
}
370+
if (getLangOpts().C99) {
371+
// In C, static initialization guarantees that padding is initialized to
372+
// zero bits. And the Linux kernel relies on clang to zero-initialize
373+
// unspecified fields.
374+
Init = constWithPadding(CGM, IsPattern::No,
375+
replaceUndef(CGM, IsPattern::No, Init));
376+
}
363377

364378
#ifndef NDEBUG
365379
CharUnits VarSize = CGM.getContext().getTypeSizeInChars(D.getType()) +
@@ -1037,8 +1051,6 @@ static bool shouldSplitConstantStore(CodeGenModule &CGM,
10371051
return false;
10381052
}
10391053

1040-
enum class IsPattern { No, Yes };
1041-
10421054
/// Generate a constant filled with either a pattern or zeroes.
10431055
static llvm::Constant *patternOrZeroFor(CodeGenModule &CGM, IsPattern isPattern,
10441056
llvm::Type *Ty) {
@@ -1048,9 +1060,6 @@ static llvm::Constant *patternOrZeroFor(CodeGenModule &CGM, IsPattern isPattern,
10481060
return llvm::Constant::getNullValue(Ty);
10491061
}
10501062

1051-
static llvm::Constant *constWithPadding(CodeGenModule &CGM, IsPattern isPattern,
1052-
llvm::Constant *constant);
1053-
10541063
/// Helper function for constWithPadding() to deal with padding in structures.
10551064
static llvm::Constant *constStructWithPadding(CodeGenModule &CGM,
10561065
IsPattern isPattern,
@@ -1108,6 +1117,9 @@ static llvm::Constant *constWithPadding(CodeGenModule &CGM, IsPattern isPattern,
11081117
if (ZeroInitializer) {
11091118
OpValue = llvm::Constant::getNullValue(ElemTy);
11101119
PaddedOp = constWithPadding(CGM, isPattern, OpValue);
1120+
// Avoid iterating large arrays with zero initializer when possible.
1121+
if (PaddedOp->getType() == ElemTy)
1122+
return constant;
11111123
}
11121124
for (unsigned Op = 0; Op != Size; ++Op) {
11131125
if (!ZeroInitializer) {
@@ -1953,21 +1965,26 @@ void CodeGenFunction::EmitAutoVarInit(const AutoVarEmission &emission) {
19531965
D.mightBeUsableInConstantExpressions(getContext())) {
19541966
assert(!capturedByInit && "constant init contains a capturing block?");
19551967
constant = ConstantEmitter(*this).tryEmitAbstractForInitializer(D);
1956-
if (constant && !constant->isZeroValue() &&
1957-
(trivialAutoVarInit !=
1958-
LangOptions::TrivialAutoVarInitKind::Uninitialized)) {
1959-
IsPattern isPattern =
1960-
(trivialAutoVarInit == LangOptions::TrivialAutoVarInitKind::Pattern)
1961-
? IsPattern::Yes
1962-
: IsPattern::No;
1963-
// C guarantees that brace-init with fewer initializers than members in
1964-
// the aggregate will initialize the rest of the aggregate as-if it were
1965-
// static initialization. In turn static initialization guarantees that
1966-
// padding is initialized to zero bits. We could instead pattern-init if D
1967-
// has any ImplicitValueInitExpr, but that seems to be unintuitive
1968-
// behavior.
1969-
constant = constWithPadding(CGM, IsPattern::No,
1970-
replaceUndef(CGM, isPattern, constant));
1968+
if (constant && !constant->isZeroValue()) {
1969+
if (getLangOpts().C99) {
1970+
// C guarantees that brace-init with fewer initializers than members in
1971+
// the aggregate will initialize the rest of the aggregate as-if it were
1972+
// static initialization. In turn static initialization guarantees that
1973+
// padding is initialized to zero bits. And the Linux kernel relies on
1974+
// clang to zero-initialize unspecified fields.
1975+
constant = constWithPadding(CGM, IsPattern::No,
1976+
replaceUndef(CGM, IsPattern::No, constant));
1977+
} else if (trivialAutoVarInit !=
1978+
LangOptions::TrivialAutoVarInitKind::Uninitialized) {
1979+
IsPattern isPattern =
1980+
(trivialAutoVarInit == LangOptions::TrivialAutoVarInitKind::Pattern)
1981+
? IsPattern::Yes
1982+
: IsPattern::No;
1983+
// We could instead pattern-init padding if D has any
1984+
// ImplicitValueInitExpr, but that seems to be unintuitive behavior.
1985+
constant = constWithPadding(CGM, IsPattern::No,
1986+
replaceUndef(CGM, isPattern, constant));
1987+
}
19711988
}
19721989
}
19731990

@@ -2849,3 +2866,9 @@ CodeGenModule::getOMPAllocateAlignment(const VarDecl *VD) {
28492866
}
28502867
return std::nullopt;
28512868
}
2869+
2870+
llvm::Constant *
2871+
CodeGenModule::zeroInitGlobalVarInitializer(llvm::Constant *Init) {
2872+
return constWithPadding(*this, IsPattern::No,
2873+
replaceUndef(*this, IsPattern::No, Init));
2874+
}

clang/lib/CodeGen/CodeGenModule.cpp

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5441,6 +5441,12 @@ void CodeGenModule::EmitGlobalVarDefinition(const VarDecl *D,
54415441
Init = llvm::UndefValue::get(getTypes().ConvertType(T));
54425442
}
54435443
} else {
5444+
if (getLangOpts().C99) {
5445+
// In C, static initialization guarantees that padding is initialized
5446+
// to zero bits. And the Linux kernel relies on clang to
5447+
// zero-initialize unspecified fields.
5448+
Initializer = zeroInitGlobalVarInitializer(Initializer);
5449+
}
54445450
Init = Initializer;
54455451
// We don't need an initializer, so remove the entry for the delayed
54465452
// 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
@@ -1837,6 +1837,8 @@ class CodeGenModule : public CodeGenTypeCache {
18371837

18381838
llvm::Metadata *CreateMetadataIdentifierImpl(QualType T, MetadataTypeMap &Map,
18391839
StringRef Suffix);
1840+
1841+
llvm::Constant *zeroInitGlobalVarInitializer(llvm::Constant *Init);
18401842
};
18411843

18421844
} // 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/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/designated-initializers.c

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ struct foo {
88
// CHECK: @u ={{.*}} global %union.anon zeroinitializer
99
union { int i; float f; } u = { };
1010

11-
// CHECK: @u2 ={{.*}} global { i32, [4 x i8] } { i32 0, [4 x i8] undef }
11+
// CHECK: @u2 ={{.*}} global { i32, [4 x i8] } zeroinitializer
1212
union { int i; double f; } u2 = { };
1313

1414
// CHECK: @u3 ={{.*}} global %union.anon.1 zeroinitializer
@@ -62,22 +62,22 @@ struct overwrite_string_struct2 {
6262
char L[6];
6363
int M;
6464
} overwrite_string2[] = { { { "foo" }, 1 }, [0].L[2] = 'x'};
65-
// CHECK: [6 x i8] c"fox\00\00\00", i32 1
65+
// CHECK: [6 x i8] c"fox\00\00\00", [2 x i8] zeroinitializer, i32 1
6666
struct overwrite_string_struct3 {
6767
char L[3];
6868
int M;
6969
} overwrite_string3[] = { { { "foo" }, 1 }, [0].L[2] = 'x'};
70-
// CHECK: [3 x i8] c"fox", i32 1
70+
// CHECK: [3 x i8] c"fox", [1 x i8] zeroinitializer, i32 1
7171
struct overwrite_string_struct4 {
7272
char L[3];
7373
int M;
7474
} overwrite_string4[] = { { { "foobar" }, 1 }, [0].L[2] = 'x'};
75-
// CHECK: [3 x i8] c"fox", i32 1
75+
// CHECK: [3 x i8] c"fox", [1 x i8] zeroinitializer, i32 1
7676
struct overwrite_string_struct5 {
7777
char L[6];
7878
int M;
7979
} overwrite_string5[] = { { { "foo" }, 1 }, [0].L[4] = 'y'};
80-
// CHECK: [6 x i8] c"foo\00y\00", i32 1
80+
// CHECK: [6 x i8] c"foo\00y\00", [2 x i8] zeroinitializer, i32 1
8181

8282

8383
// CHECK: @u1 = {{.*}} { i32 65535 }
@@ -138,7 +138,7 @@ union_16644_t union_16644_instance_4[2] =
138138
[1].b[1] = 4
139139
};
140140

141-
// CHECK: @lab ={{.*}} global { [4 x i8], i32 } { [4 x i8] undef, i32 123 }
141+
// CHECK: @lab ={{.*}} global { [4 x i8], i32 } { [4 x i8] zeroinitializer, i32 123 }
142142
struct leading_anon_bitfield { int : 32; int n; } lab = { .n = 123 };
143143

144144
struct Base {

clang/test/CodeGen/flexible-array-init.c

Lines changed: 12 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -14,11 +14,11 @@ struct { int y[]; } b1 = { { 14, 16 } };
1414

1515
// sizeof(c) == 8, so this global should be at least 8 bytes.
1616
struct { int x; char c; char y[]; } c = { 1, 2, { 13, 15 } };
17-
// CHECK: @c ={{.*}} global { i32, i8, [2 x i8] } { i32 1, i8 2, [2 x i8] c"\0D\0F" }
17+
// CHECK: @c ={{.*}} global { i32, i8, [2 x i8], [1 x i8] } { i32 1, i8 2, [2 x i8] c"\0D\0F", [1 x i8] zeroinitializer }
1818

1919
// sizeof(d) == 8, so this global should be at least 8 bytes.
2020
struct __attribute((packed, aligned(4))) { char a; int x; char z[]; } d = { 1, 2, { 13, 15 } };
21-
// CHECK: @d ={{.*}} <{ i8, i32, [2 x i8], i8 }> <{ i8 1, i32 2, [2 x i8] c"\0D\0F", i8 undef }>,
21+
// CHECK: @d ={{.*}} <{ i8, i32, [2 x i8], i8 }> <{ i8 1, i32 2, [2 x i8] c"\0D\0F", i8 0 }>,
2222

2323
// This global needs 9 bytes to hold all the flexible array members.
2424
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};
5555

5656
// First member is the potential flexible array, initialization requires braces.
5757
struct { int a; union { short x; int b; }; int c; int d; } i = {1, 2, {}, 3};
58-
// 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 }
58+
// 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 }
5959
struct { int a; union { short x[0]; int b; }; int c; int d; } i0 = {1, {}, 2, 3};
60-
// 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 }
60+
// 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 }
6161
struct { int a; union { short x[1]; int b; }; int c; int d; } i1 = {1, {2}, {}, 3};
62-
// 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 }
62+
// 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 }
6363
struct { int a; union { short x[]; int b; }; int c; int d; } i_f = {4, {}, {}, 6};
64-
// 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 }
64+
// 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 }
6565

6666
// Named initializers; order doesn't matter.
6767
struct { int a; union { int b; short x; }; int c; int d; } hn = {.a = 1, .x = 2, .c = 3};
68-
// 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 }
68+
// 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 }
6969
struct { int a; union { int b; short x[0]; }; int c; int d; } hn0 = {.a = 1, .x = {2}, .c = 3};
70-
// 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 }
70+
// 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 }
7171
struct { int a; union { int b; short x[1]; }; int c; int d; } hn1 = {.a = 1, .x = {2}, .c = 3};
72-
// 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 }
72+
// 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 }
7373

7474
struct { char a[]; } empty_struct = {};
7575
// CHECK: @empty_struct ={{.*}} global %struct.anon{{.*}} zeroinitializer, align 1
@@ -96,10 +96,10 @@ union { char a[]; } only_in_union0 = {0};
9696
// CHECK: @only_in_union0 = global { [1 x i8] } zeroinitializer, align 1
9797

9898
union { char a[]; int b; } first_in_union = {};
99-
// CHECK: @first_in_union = global { [0 x i8], [4 x i8] } { [0 x i8] zeroinitializer, [4 x i8] undef }, align 4
99+
// CHECK: @first_in_union = global { [0 x i8], [4 x i8] } zeroinitializer, align 4
100100

101101
union { char a[]; int b; } first_in_union0 = {0};
102-
// CHECK: @first_in_union0 = global { [1 x i8], [3 x i8] } { [1 x i8] zeroinitializer, [3 x i8] undef }, align 4
102+
// CHECK: @first_in_union0 = global { [1 x i8], [3 x i8] } zeroinitializer, align 4
103103

104104
union { char a[]; int b; } first_in_union123 = { {1, 2, 3} };
105-
// CHECK: @first_in_union123 = global { [3 x i8], i8 } { [3 x i8] c"\01\02\03", i8 undef }, align 4
105+
// CHECK: @first_in_union123 = global { [3 x i8], i8 } { [3 x i8] c"\01\02\03", i8 0 }, align 4

clang/test/CodeGen/global-init.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,7 @@ struct ManyFields {
3333
int f;
3434
};
3535

36-
// CHECK: global %struct.ManyFields { i32 1, i32 2, i32 0, i8 0, i32 0, i32 0 }
36+
// 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 }
3737
struct ManyFields FewInits = {1, 2};
3838

3939

clang/test/CodeGen/init.c

Lines changed: 0 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -187,25 +187,6 @@ void nonzeroMemsetf64(void) {
187187
// CHECK: call void @llvm.memset.p0.i32(ptr {{.*}}, i8 68, i32 56, i1 false)
188188
}
189189

190-
void nonzeroPaddedUnionMemset(void) {
191-
union U { char c; int i; };
192-
union U arr[9] = { 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, };
193-
// CHECK-LABEL: @nonzeroPaddedUnionMemset(
194-
// CHECK-NOT: store
195-
// CHECK-NOT: memcpy
196-
// CHECK: call void @llvm.memset.p0.i32(ptr {{.*}}, i8 -16, i32 36, i1 false)
197-
}
198-
199-
void nonzeroNestedMemset(void) {
200-
union U { char c; int i; };
201-
struct S { union U u; short i; };
202-
struct S arr[5] = { { {0xF0}, 0xF0F0 }, { {0xF0}, 0xF0F0 }, { {0xF0}, 0xF0F0 }, { {0xF0}, 0xF0F0 }, { {0xF0}, 0xF0F0 }, };
203-
// CHECK-LABEL: @nonzeroNestedMemset(
204-
// CHECK-NOT: store
205-
// CHECK-NOT: memcpy
206-
// CHECK: call void @llvm.memset.p0.i32(ptr {{.*}}, i8 -16, i32 40, i1 false)
207-
}
208-
209190
// PR9257
210191
struct test11S {
211192
int A[10];

0 commit comments

Comments
 (0)