Skip to content

Commit 4166d86

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 ae96bde commit 4166d86

22 files changed

+331
-95
lines changed

clang/docs/LanguageExtensions.rst

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

clang/lib/CodeGen/CGDecl.cpp

Lines changed: 41 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,13 @@ CodeGenFunction::AddInitializerToStaticVarDecl(const VarDecl &D,
361368
}
362369
return GV;
363370
}
371+
if (!getLangOpts().CPlusPlus) {
372+
// In C, when an initializer is given, the Linux kernel relies on clang to
373+
// zero-initialize all members not explicitly initialized, including padding
374+
// bits.
375+
Init = constWithPadding(CGM, IsPattern::No,
376+
replaceUndef(CGM, IsPattern::No, Init));
377+
}
364378

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

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

1052-
static llvm::Constant *constWithPadding(CodeGenModule &CGM, IsPattern isPattern,
1053-
llvm::Constant *constant);
1054-
10551064
/// Helper function for constWithPadding() to deal with padding in structures.
10561065
static llvm::Constant *constStructWithPadding(CodeGenModule &CGM,
10571066
IsPattern isPattern,
@@ -1109,6 +1118,9 @@ static llvm::Constant *constWithPadding(CodeGenModule &CGM, IsPattern isPattern,
11091118
if (ZeroInitializer) {
11101119
OpValue = llvm::Constant::getNullValue(ElemTy);
11111120
PaddedOp = constWithPadding(CGM, isPattern, OpValue);
1121+
// Avoid iterating large arrays with zero initializer when possible.
1122+
if (PaddedOp->getType() == ElemTy)
1123+
return constant;
11121124
}
11131125
for (unsigned Op = 0; Op != Size; ++Op) {
11141126
if (!ZeroInitializer) {
@@ -1954,21 +1966,24 @@ void CodeGenFunction::EmitAutoVarInit(const AutoVarEmission &emission) {
19541966
D.mightBeUsableInConstantExpressions(getContext())) {
19551967
assert(!capturedByInit && "constant init contains a capturing block?");
19561968
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));
1969+
if (constant && !constant->isZeroValue()) {
1970+
if (!getLangOpts().CPlusPlus) {
1971+
// In C, when an initializer is given, the Linux kernel relies on clang
1972+
// to zero-initialize all members not explicitly initialized, including
1973+
// padding bits.
1974+
constant = constWithPadding(CGM, IsPattern::No,
1975+
replaceUndef(CGM, IsPattern::No, constant));
1976+
} else if (trivialAutoVarInit !=
1977+
LangOptions::TrivialAutoVarInitKind::Uninitialized) {
1978+
IsPattern isPattern =
1979+
(trivialAutoVarInit == LangOptions::TrivialAutoVarInitKind::Pattern)
1980+
? IsPattern::Yes
1981+
: IsPattern::No;
1982+
// We could instead pattern-init padding if D has any
1983+
// ImplicitValueInitExpr, but that seems to be unintuitive behavior.
1984+
constant = constWithPadding(CGM, IsPattern::No,
1985+
replaceUndef(CGM, isPattern, constant));
1986+
}
19721987
}
19731988

19741989
if (D.getType()->isBitIntType() &&
@@ -2861,3 +2876,9 @@ CodeGenModule::getOMPAllocateAlignment(const VarDecl *VD) {
28612876
}
28622877
return std::nullopt;
28632878
}
2879+
2880+
llvm::Constant *
2881+
CodeGenModule::zeroInitGlobalVarInitializer(llvm::Constant *Init) {
2882+
return constWithPadding(*this, IsPattern::No,
2883+
replaceUndef(*this, IsPattern::No, Init));
2884+
}

clang/lib/CodeGen/CodeGenModule.cpp

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5473,6 +5473,12 @@ void CodeGenModule::EmitGlobalVarDefinition(const VarDecl *D,
54735473
Init = llvm::UndefValue::get(getTypes().ConvertType(T));
54745474
}
54755475
} else {
5476+
if (!getLangOpts().CPlusPlus) {
5477+
// In C, when an initializer is given, the Linux kernel relies on clang
5478+
// to zero-initialize all members not explicitly initialized, including
5479+
// padding bits.
5480+
Initializer = zeroInitGlobalVarInitializer(Initializer);
5481+
}
54765482
Init = Initializer;
54775483
// We don't need an initializer, so remove the entry for the delayed
54785484
// 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
@@ -1858,6 +1858,8 @@ class CodeGenModule : public CodeGenTypeCache {
18581858

18591859
llvm::Metadata *CreateMetadataIdentifierImpl(QualType T, MetadataTypeMap &Map,
18601860
StringRef Suffix);
1861+
1862+
llvm::Constant *zeroInitGlobalVarInitializer(llvm::Constant *Init);
18611863
};
18621864

18631865
} // 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

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/ext-int.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@
1616
unsigned _BitInt(1) GlobSize1 = 0;
1717
// CHECK: @GlobSize1 = {{.*}}global i8 0
1818

19-
// 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
19+
// 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
2020
// @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
2121
// CHECK64: @f.p = internal global <{ i8, i8, [22 x i8] }> <{ i8 16, i8 39, [22 x i8] zeroinitializer }>, align 8
2222

0 commit comments

Comments
 (0)