[clang] [clang][CodeGen] Zero init unspecified fields in initializers in C (#78034) (PR #97121)
via cfe-commits
cfe-commits at lists.llvm.org
Tue Jul 2 13:44:22 PDT 2024
https://github.com/yabinc updated https://github.com/llvm/llvm-project/pull/97121
>From d3aa71bfd9f4766e2aadc0d028d26ea7bd5e50a6 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] [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 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/lib/CodeGen/CGDecl.cpp | 63 ++++--
clang/lib/CodeGen/CodeGenModule.cpp | 6 +
clang/lib/CodeGen/CodeGenModule.h | 2 +
...07-22-bitfield-init-after-zero-len-array.c | 2 +-
clang/test/CodeGen/2008-08-07-AlignPadding1.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/designated-initializers.c | 12 +-
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 | 212 ++++++++++++++++++
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 +-
18 files changed, 307 insertions(+), 90 deletions(-)
create mode 100644 clang/test/CodeGen/linux-kernel-struct-union-initializer.c
diff --git a/clang/lib/CodeGen/CGDecl.cpp b/clang/lib/CodeGen/CGDecl.cpp
index 90aa4c0745a8a..960add12f323b 100644
--- a/clang/lib/CodeGen/CGDecl.cpp
+++ b/clang/lib/CodeGen/CGDecl.cpp
@@ -334,6 +334,13 @@ llvm::Constant *CodeGenModule::getOrCreateStaticVarDecl(
return Addr;
}
+enum class IsPattern { No, Yes };
+
+static llvm::Constant *replaceUndef(CodeGenModule &CGM, IsPattern isPattern,
+ llvm::Constant *constant);
+static llvm::Constant *constWithPadding(CodeGenModule &CGM, IsPattern isPattern,
+ llvm::Constant *constant);
+
/// AddInitializerToStaticVarDecl - Add the initializer for 'D' to the
/// global variable that has already been created for it. If the initializer
/// has a different type than GV does, this may free GV and return a different
@@ -360,6 +367,13 @@ CodeGenFunction::AddInitializerToStaticVarDecl(const VarDecl &D,
}
return GV;
}
+ if (getLangOpts().C99) {
+ // In C, static initialization guarantees that padding is initialized to
+ // zero bits. And the Linux kernel relies on clang to zero-initialize
+ // unspecified fields.
+ Init = constWithPadding(CGM, IsPattern::No,
+ replaceUndef(CGM, IsPattern::No, Init));
+ }
#ifndef NDEBUG
CharUnits VarSize = CGM.getContext().getTypeSizeInChars(D.getType()) +
@@ -1037,8 +1051,6 @@ static bool shouldSplitConstantStore(CodeGenModule &CGM,
return false;
}
-enum class IsPattern { No, Yes };
-
/// Generate a constant filled with either a pattern or zeroes.
static llvm::Constant *patternOrZeroFor(CodeGenModule &CGM, IsPattern isPattern,
llvm::Type *Ty) {
@@ -1048,9 +1060,6 @@ static llvm::Constant *patternOrZeroFor(CodeGenModule &CGM, IsPattern isPattern,
return llvm::Constant::getNullValue(Ty);
}
-static llvm::Constant *constWithPadding(CodeGenModule &CGM, IsPattern isPattern,
- llvm::Constant *constant);
-
/// Helper function for constWithPadding() to deal with padding in structures.
static llvm::Constant *constStructWithPadding(CodeGenModule &CGM,
IsPattern isPattern,
@@ -1108,6 +1117,9 @@ static llvm::Constant *constWithPadding(CodeGenModule &CGM, IsPattern isPattern,
if (ZeroInitializer) {
OpValue = llvm::Constant::getNullValue(ElemTy);
PaddedOp = constWithPadding(CGM, isPattern, OpValue);
+ // Avoid iterating large arrays with zero initializer when possible.
+ if (PaddedOp->getType() == ElemTy)
+ return constant;
}
for (unsigned Op = 0; Op != Size; ++Op) {
if (!ZeroInitializer) {
@@ -1953,21 +1965,26 @@ void CodeGenFunction::EmitAutoVarInit(const AutoVarEmission &emission) {
D.mightBeUsableInConstantExpressions(getContext())) {
assert(!capturedByInit && "constant init contains a capturing block?");
constant = ConstantEmitter(*this).tryEmitAbstractForInitializer(D);
- if (constant && !constant->isZeroValue() &&
- (trivialAutoVarInit !=
- LangOptions::TrivialAutoVarInitKind::Uninitialized)) {
- IsPattern isPattern =
- (trivialAutoVarInit == LangOptions::TrivialAutoVarInitKind::Pattern)
- ? IsPattern::Yes
- : IsPattern::No;
- // C guarantees that brace-init with fewer initializers than members in
- // the aggregate will initialize the rest of the aggregate as-if it were
- // static initialization. In turn static initialization guarantees that
- // padding is initialized to zero bits. We could instead pattern-init if D
- // has any ImplicitValueInitExpr, but that seems to be unintuitive
- // behavior.
- constant = constWithPadding(CGM, IsPattern::No,
- replaceUndef(CGM, isPattern, constant));
+ if (constant && !constant->isZeroValue()) {
+ if (getLangOpts().C99) {
+ // C guarantees that brace-init with fewer initializers than members in
+ // the aggregate will initialize the rest of the aggregate as-if it were
+ // static initialization. In turn static initialization guarantees that
+ // padding is initialized to zero bits. And the Linux kernel relies on
+ // clang to zero-initialize unspecified fields.
+ constant = constWithPadding(CGM, IsPattern::No,
+ replaceUndef(CGM, IsPattern::No, constant));
+ } else if (trivialAutoVarInit !=
+ LangOptions::TrivialAutoVarInitKind::Uninitialized) {
+ IsPattern isPattern =
+ (trivialAutoVarInit == LangOptions::TrivialAutoVarInitKind::Pattern)
+ ? IsPattern::Yes
+ : IsPattern::No;
+ // We could instead pattern-init padding if D has any
+ // ImplicitValueInitExpr, but that seems to be unintuitive behavior.
+ constant = constWithPadding(CGM, IsPattern::No,
+ replaceUndef(CGM, isPattern, constant));
+ }
}
}
@@ -2849,3 +2866,9 @@ CodeGenModule::getOMPAllocateAlignment(const VarDecl *VD) {
}
return std::nullopt;
}
+
+llvm::Constant *
+CodeGenModule::zeroInitGlobalVarInitializer(llvm::Constant *Init) {
+ return constWithPadding(*this, IsPattern::No,
+ replaceUndef(*this, IsPattern::No, Init));
+}
diff --git a/clang/lib/CodeGen/CodeGenModule.cpp b/clang/lib/CodeGen/CodeGenModule.cpp
index 652f519d82488..a29eafa2fad68 100644
--- a/clang/lib/CodeGen/CodeGenModule.cpp
+++ b/clang/lib/CodeGen/CodeGenModule.cpp
@@ -5433,6 +5433,12 @@ void CodeGenModule::EmitGlobalVarDefinition(const VarDecl *D,
Init = llvm::UndefValue::get(getTypes().ConvertType(T));
}
} else {
+ if (getLangOpts().C99) {
+ // In C, static initialization guarantees that padding is initialized
+ // to zero bits. And the Linux kernel relies on clang to
+ // zero-initialize unspecified fields.
+ Initializer = zeroInitGlobalVarInitializer(Initializer);
+ }
Init = Initializer;
// We don't need an initializer, so remove the entry for the delayed
// initializer position (just in case this entry was delayed) if we
diff --git a/clang/lib/CodeGen/CodeGenModule.h b/clang/lib/CodeGen/CodeGenModule.h
index 22b2b314c316c..3222de2a32e6d 100644
--- a/clang/lib/CodeGen/CodeGenModule.h
+++ b/clang/lib/CodeGen/CodeGenModule.h
@@ -1837,6 +1837,8 @@ class CodeGenModule : public CodeGenTypeCache {
llvm::Metadata *CreateMetadataIdentifierImpl(QualType T, MetadataTypeMap &Map,
StringRef Suffix);
+
+ llvm::Constant *zeroInitGlobalVarInitializer(llvm::Constant *Init);
};
} // end namespace CodeGen
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 b72d689659e60..b639734ef5d4b 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 17e88ce02659f..d69cbc22cc1df 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/64bit-swiftcall.c b/clang/test/CodeGen/64bit-swiftcall.c
index b1c42e3b0a657..4b0143e37afbf 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 { i64, i64 }, ptr [[RET]], i32 0, i32 0
// CHECK: [[T0:%.*]] = load i64, ptr [[GEP0]], align 4
// CHECK: [[GEP1:%.*]] = getelementptr inbounds { 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 { i64, i64 }, ptr [[V]], i32 0, i32 0
// CHECK: store i64 %0, ptr [[GEP0]], align 4
// CHECK: [[GEP1:%.*]] = getelementptr inbounds { 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 {{.*}} [[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 { 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 9fa607a968ccf..78ceed5926014 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 [[AGG:{ i32, i32, float, float }]], ptr [[RET]], i32 0, i32 0
// CHECK: [[FIRST:%.*]] = load i32, ptr [[T0]], align 4
// CHECK: [[T0:%.*]] = getelementptr inbounds [[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 [[AGG:{ i32, i32 }]], ptr [[RET]], i32 0, i32 0
// CHECK: [[FIRST:%.*]] = load i32, ptr [[T0]], align {{(4|8)}}
// CHECK: [[T0:%.*]] = getelementptr inbounds [[AGG]], ptr [[RET]], i32 0, i32 1
diff --git a/clang/test/CodeGen/const-init.c b/clang/test/CodeGen/const-init.c
index ad3e9551199ac..fc973cb983a80 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/designated-initializers.c b/clang/test/CodeGen/designated-initializers.c
index 620b1b90d2575..56600f39e8bb2 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", [1 x i8] zeroinitializer, 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", [1 x i8] zeroinitializer, 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/flexible-array-init.c b/clang/test/CodeGen/flexible-array-init.c
index 15a30c15ac966..08b5aaf63eaea 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], [1 x i8] } { i32 1, i8 2, [2 x i8] c"\0D\0F", [1 x i8] zeroinitializer }
// 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 7f1d675b97c09..b156466dbaaff 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 cbf615bb9ddfe..27f427dff8f79 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 0000000000000..a5497c7db84e9
--- /dev/null
+++ b/clang/test/CodeGen/linux-kernel-struct-union-initializer.c
@@ -0,0 +1,212 @@
+// 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
+
+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
+
+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/mingw-long-double.c b/clang/test/CodeGen/mingw-long-double.c
index 4be97526f9631..0fc8f01509682 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 49c5c1c3e7d40..2ccce326c7131 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 2c167683c4e55..64fea80a80d49 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 6c138a341835d..d8ebec4363e38 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 { i64, i64 }, ptr [[RET]], i32 0, i32 0
// CHECK: [[T0:%.*]] = load i64, ptr [[GEP0]], align 4
// CHECK: [[GEP1:%.*]] = getelementptr inbounds { 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 { i64, i64 }, ptr [[V]], i32 0, i32 0
// CHECK: store i64 %0, ptr [[GEP0]], align 4
// CHECK: [[GEP1:%.*]] = getelementptr inbounds { 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 {{.*}} [[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.p0.i64(ptr align 8 [[RET]], i8 0, i64 8
// CHECK: [[GEP:%.*]] = getelementptr inbounds { 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 a67f82e1afbea..98a1a3568cff3 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", [1 x i8] zeroinitializer, i32 1
More information about the cfe-commits
mailing list