Skip to content

Commit 5d8aaad

Browse files
committed
[C2x] Implement support for empty brace initialization (WG14 N2900 and WG14 N3011)
This implements support for allowing {} to consistently zero initialize objects. We already supported most of this work as a GNU extension, but the C2x feature goes beyond what the GNU extension allowed. The changes in this patch are: * Removed the -Wgnu-empty-initializer warning group. The extension is now a C2x extension warning instead. Note that use of `-Wno-gnu-empty-initializer seems` to be quite low in the wild (https://sourcegraph.com/search?q=context%3Aglobal+-file%3A.*test.*+%22-Wno-gnu-empty-initializer%22&patternType=standard&sm=1&groupBy=repo which currently only gives 8 hits total), so this is not expected to be an overly disruptive change. But I'm adding the clang vendors review group just in case this expectation is wrong. * Reworded the diagnostic wording to be about a C2x extension, added a pre-C2x compat warning. * Allow {} to zero initialize a VLA This functionality is exposed as an extension in all older C modes (same as the GNU extension was), but does *not* allow the extension for VLA initialization in C++ due to concern about handling non-trivially constructible types. Differential Revision: https://reviews.llvm.org/D147349
1 parent a71bc5f commit 5d8aaad

20 files changed

+303
-86
lines changed

clang/docs/ReleaseNotes.rst

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -121,6 +121,18 @@ C2x Feature Support
121121
which introduces the ``bool``, ``static_assert``, ``alignas``, ``alignof``,
122122
and ``thread_local`` keywords in C2x.
123123

124+
- Implemented `WG14 N2900 <https://www.open-std.org/jtc1/sc22/wg14/www/docs/n2900.htm>`_
125+
and `WG14 N3011 <https://www.open-std.org/jtc1/sc22/wg14/www/docs/n3011.htm>`_
126+
which allows for empty braced initialization in C.
127+
128+
.. code-block:: c
129+
130+
struct S { int x, y } s = {}; // Initializes s.x and s.y to 0
131+
132+
As part of this change, the ``-Wgnu-empty-initializer`` warning group was
133+
removed, as this is no longer a GNU extension but a C2x extension. You can
134+
use ``-Wno-c2x-extensions`` to silence the extension warning instead.
135+
124136
Non-comprehensive list of changes in this release
125137
-------------------------------------------------
126138
- Clang now saves the address of ABI-indirect function parameters on the stack,

clang/include/clang/Basic/DiagnosticCommonKinds.td

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -130,6 +130,12 @@ def warn_cxx20_compat_consteval : Warning<
130130
def warn_missing_type_specifier : Warning<
131131
"type specifier missing, defaults to 'int'">,
132132
InGroup<ImplicitInt>, DefaultIgnore;
133+
134+
def ext_c_empty_initializer : Extension<
135+
"use of an empty initializer is a C2x extension">, InGroup<C2x>;
136+
def warn_c2x_compat_empty_initializer : Warning<
137+
"use of an empty initializer is incompatible with C standards before C2x">,
138+
InGroup<CPre2xCompat>, DefaultIgnore;
133139
}
134140

135141
let CategoryName = "Nullability Issue" in {

clang/include/clang/Basic/DiagnosticGroups.td

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -256,7 +256,6 @@ def EmptyBody : DiagGroup<"empty-body">;
256256
def Exceptions : DiagGroup<"exceptions">;
257257
def DeclarationAfterStatement : DiagGroup<"declaration-after-statement">;
258258

259-
def GNUEmptyInitializer : DiagGroup<"gnu-empty-initializer">;
260259
def GNUEmptyStruct : DiagGroup<"gnu-empty-struct">;
261260
def ExtraTokens : DiagGroup<"extra-tokens">;
262261
def CXX98CompatExtraSemi : DiagGroup<"c++98-compat-extra-semi">;
@@ -1135,7 +1134,7 @@ def GNU : DiagGroup<"gnu", [GNUAlignofExpression, GNUAnonymousStruct,
11351134
GNUBinaryLiteral, GNUCaseRange,
11361135
GNUComplexInteger, GNUCompoundLiteralInitializer,
11371136
GNUConditionalOmittedOperand, GNUDesignator,
1138-
GNUEmptyInitializer, GNUEmptyStruct,
1137+
GNUEmptyStruct,
11391138
VLAExtension, GNUFlexibleArrayInitializer,
11401139
GNUFlexibleArrayUnionMember, GNUFoldingConstant,
11411140
GNUImaginaryConstant, GNUIncludeNext,

clang/include/clang/Basic/DiagnosticParseKinds.td

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -182,8 +182,6 @@ def ext_gnu_statement_expr_macro : Extension<
182182
InGroup<GNUStatementExpressionFromMacroExpansion>;
183183
def ext_gnu_conditional_expr : Extension<
184184
"use of GNU ?: conditional expression extension, omitting middle operand">, InGroup<GNUConditionalOmittedOperand>;
185-
def ext_gnu_empty_initializer : Extension<
186-
"use of GNU empty initializer extension">, InGroup<GNUEmptyInitializer>;
187185
def ext_gnu_array_range : Extension<"use of GNU array range extension">,
188186
InGroup<GNUDesignator>;
189187
def ext_gnu_missing_equal_designator : ExtWarn<

clang/lib/CodeGen/CGExprAgg.cpp

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1658,11 +1658,19 @@ void AggExprEmitter::VisitCXXParenListOrInitListExpr(
16581658
LValue DestLV = CGF.MakeAddrLValue(Dest.getAddress(), ExprToVisit->getType());
16591659

16601660
// Handle initialization of an array.
1661-
if (ExprToVisit->getType()->isArrayType()) {
1661+
if (ExprToVisit->getType()->isConstantArrayType()) {
16621662
auto AType = cast<llvm::ArrayType>(Dest.getAddress().getElementType());
16631663
EmitArrayInit(Dest.getAddress(), AType, ExprToVisit->getType(), ExprToVisit,
16641664
InitExprs, ArrayFiller);
16651665
return;
1666+
} else if (ExprToVisit->getType()->isVariableArrayType()) {
1667+
// A variable array type that has an initializer can only do empty
1668+
// initialization. And because this feature is not exposed as an extension
1669+
// in C++, we can safely memset the array memory to zero.
1670+
assert(InitExprs.size() == 0 &&
1671+
"you can only use an empty initializer with VLAs");
1672+
CGF.EmitNullInitialization(Dest.getAddress(), ExprToVisit->getType());
1673+
return;
16661674
}
16671675

16681676
assert(ExprToVisit->getType()->isRecordType() &&

clang/lib/Parse/ParseInit.cpp

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -429,7 +429,7 @@ ExprResult Parser::ParseInitializerWithPotentialDesignator(
429429
/// initializer: [C99 6.7.8]
430430
/// '{' initializer-list '}'
431431
/// '{' initializer-list ',' '}'
432-
/// [GNU] '{' '}'
432+
/// [C2x] '{' '}'
433433
///
434434
/// initializer-list:
435435
/// designation[opt] initializer ...[opt]
@@ -447,9 +447,12 @@ ExprResult Parser::ParseBraceInitializer() {
447447
ExprVector InitExprs;
448448

449449
if (Tok.is(tok::r_brace)) {
450-
// Empty initializers are a C++ feature and a GNU extension to C.
451-
if (!getLangOpts().CPlusPlus)
452-
Diag(LBraceLoc, diag::ext_gnu_empty_initializer);
450+
// Empty initializers are a C++ feature and a GNU extension to C before C2x.
451+
if (!getLangOpts().CPlusPlus) {
452+
Diag(LBraceLoc, getLangOpts().C2x
453+
? diag::warn_c2x_compat_empty_initializer
454+
: diag::ext_c_empty_initializer);
455+
}
453456
// Match the '}'.
454457
return Actions.ActOnInitList(LBraceLoc, std::nullopt, ConsumeBrace());
455458
}

clang/lib/Sema/SemaExpr.cpp

Lines changed: 16 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -7491,10 +7491,23 @@ Sema::BuildCompoundLiteralExpr(SourceLocation LParenLoc, TypeSourceInfo *TInfo,
74917491
SourceRange(LParenLoc, LiteralExpr->getSourceRange().getEnd())))
74927492
return ExprError();
74937493
if (literalType->isVariableArrayType()) {
7494-
if (!tryToFixVariablyModifiedVarType(TInfo, literalType, LParenLoc,
7495-
diag::err_variable_object_no_init)) {
7494+
// C2x 6.7.9p4: An entity of variable length array type shall not be
7495+
// initialized except by an empty initializer.
7496+
//
7497+
// The C extension warnings are issued from ParseBraceInitializer() and
7498+
// do not need to be issued here. However, we continue to issue an error
7499+
// in the case there are initializers or we are compiling C++. We allow
7500+
// use of VLAs in C++, but it's not clear we want to allow {} to zero
7501+
// init a VLA in C++ in all cases (such as with non-trivial constructors).
7502+
// FIXME: should we allow this construct in C++ when it makes sense to do
7503+
// so?
7504+
std::optional<unsigned> NumInits;
7505+
if (const auto *ILE = dyn_cast<InitListExpr>(LiteralExpr))
7506+
NumInits = ILE->getNumInits();
7507+
if ((LangOpts.CPlusPlus || NumInits.value_or(0)) &&
7508+
!tryToFixVariablyModifiedVarType(TInfo, literalType, LParenLoc,
7509+
diag::err_variable_object_no_init))
74967510
return ExprError();
7497-
}
74987511
}
74997512
} else if (!literalType->isDependentType() &&
75007513
RequireCompleteType(LParenLoc, literalType,

clang/lib/Sema/SemaInit.cpp

Lines changed: 34 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -1565,20 +1565,23 @@ void InitListChecker::CheckScalarType(const InitializedEntity &Entity,
15651565
unsigned &StructuredIndex) {
15661566
if (Index >= IList->getNumInits()) {
15671567
if (!VerifyOnly) {
1568-
if (DeclType->isSizelessBuiltinType())
1569-
SemaRef.Diag(IList->getBeginLoc(),
1570-
SemaRef.getLangOpts().CPlusPlus11
1571-
? diag::warn_cxx98_compat_empty_sizeless_initializer
1572-
: diag::err_empty_sizeless_initializer)
1573-
<< DeclType << IList->getSourceRange();
1574-
else
1575-
SemaRef.Diag(IList->getBeginLoc(),
1576-
SemaRef.getLangOpts().CPlusPlus11
1577-
? diag::warn_cxx98_compat_empty_scalar_initializer
1578-
: diag::err_empty_scalar_initializer)
1579-
<< IList->getSourceRange();
1568+
if (SemaRef.getLangOpts().CPlusPlus) {
1569+
if (DeclType->isSizelessBuiltinType())
1570+
SemaRef.Diag(IList->getBeginLoc(),
1571+
SemaRef.getLangOpts().CPlusPlus11
1572+
? diag::warn_cxx98_compat_empty_sizeless_initializer
1573+
: diag::err_empty_sizeless_initializer)
1574+
<< DeclType << IList->getSourceRange();
1575+
else
1576+
SemaRef.Diag(IList->getBeginLoc(),
1577+
SemaRef.getLangOpts().CPlusPlus11
1578+
? diag::warn_cxx98_compat_empty_scalar_initializer
1579+
: diag::err_empty_scalar_initializer)
1580+
<< IList->getSourceRange();
1581+
}
15801582
}
1581-
hadError = !SemaRef.getLangOpts().CPlusPlus11;
1583+
hadError =
1584+
SemaRef.getLangOpts().CPlusPlus && !SemaRef.getLangOpts().CPlusPlus11;
15821585
++Index;
15831586
++StructuredIndex;
15841587
return;
@@ -1908,11 +1911,24 @@ void InitListChecker::CheckArrayType(const InitializedEntity &Entity,
19081911
// Check for VLAs; in standard C it would be possible to check this
19091912
// earlier, but I don't know where clang accepts VLAs (gcc accepts
19101913
// them in all sorts of strange places).
1911-
if (!VerifyOnly)
1912-
SemaRef.Diag(VAT->getSizeExpr()->getBeginLoc(),
1913-
diag::err_variable_object_no_init)
1914-
<< VAT->getSizeExpr()->getSourceRange();
1915-
hadError = true;
1914+
bool HasErr = IList->getNumInits() != 0 || SemaRef.getLangOpts().CPlusPlus;
1915+
if (!VerifyOnly) {
1916+
// C2x 6.7.9p4: An entity of variable length array type shall not be
1917+
// initialized except by an empty initializer.
1918+
//
1919+
// The C extension warnings are issued from ParseBraceInitializer() and
1920+
// do not need to be issued here. However, we continue to issue an error
1921+
// in the case there are initializers or we are compiling C++. We allow
1922+
// use of VLAs in C++, but it's not clear we want to allow {} to zero
1923+
// init a VLA in C++ in all cases (such as with non-trivial constructors).
1924+
// FIXME: should we allow this construct in C++ when it makes sense to do
1925+
// so?
1926+
if (HasErr)
1927+
SemaRef.Diag(VAT->getSizeExpr()->getBeginLoc(),
1928+
diag::err_variable_object_no_init)
1929+
<< VAT->getSizeExpr()->getSourceRange();
1930+
}
1931+
hadError = HasErr;
19161932
++Index;
19171933
++StructuredIndex;
19181934
return;

clang/test/C/C2x/n2900_n3011.c

Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
// RUN: %clang_cc1 -std=c2x -fsyntax-only -Wpre-c2x-compat -verify=compat %s
2+
// RUN: %clang_cc1 -std=c17 -fsyntax-only -pedantic -Wno-comment -verify=pedantic %s
3+
4+
/* WG14 N2900: yes
5+
* Consistent, Warningless, and Intuitive Initialization with {}
6+
*/
7+
8+
/* WG14 N3011: yes
9+
* Consistent, Warningless, and Intuitive Initialization with {}
10+
*/
11+
void test(void) {
12+
struct S { int x, y; } s = {}; // compat-warning {{use of an empty initializer is incompatible with C standards before C2x}} \
13+
pedantic-warning {{use of an empty initializer is a C2x extension}}
14+
int i = {}; // compat-warning {{use of an empty initializer is incompatible with C standards before C2x}} \
15+
pedantic-warning {{use of an empty initializer is a C2x extension}}
16+
int j = (int){}; // compat-warning {{use of an empty initializer is incompatible with C standards before C2x}} \
17+
pedantic-warning {{use of an empty initializer is a C2x extension}}
18+
int unknown_size[] = {}; // pedantic-warning {{zero size arrays are an extension}} \
19+
pedantic-warning {{use of an empty initializer is a C2x extension}} \
20+
compat-warning {{use of an empty initializer is incompatible with C standards before C2x}}
21+
int vla[i] = {}; // compat-warning {{use of an empty initializer is incompatible with C standards before C2x}} \
22+
pedantic-warning {{use of an empty initializer is a C2x extension}}
23+
int *compound_literal_vla = (int[i]){}; // compat-warning {{use of an empty initializer is incompatible with C standards before C2x}} \
24+
pedantic-warning {{use of an empty initializer is a C2x extension}}
25+
26+
struct T {
27+
int i;
28+
struct S s;
29+
} t1 = { 1, {} }; // compat-warning {{use of an empty initializer is incompatible with C standards before C2x}} \
30+
pedantic-warning {{use of an empty initializer is a C2x extension}}
31+
32+
struct T t2 = {
33+
1, {
34+
2, {} // compat-warning {{use of an empty initializer is incompatible with C standards before C2x}} \
35+
pedantic-warning {{use of an empty initializer is a C2x extension}}
36+
}
37+
};
38+
39+
struct T t3 = {
40+
(int){}, // compat-warning {{use of an empty initializer is incompatible with C standards before C2x}} \
41+
pedantic-warning {{use of an empty initializer is a C2x extension}}
42+
{} // compat-warning {{use of an empty initializer is incompatible with C standards before C2x}} \
43+
pedantic-warning {{use of an empty initializer is a C2x extension}}
44+
};
45+
46+
// Ensure that zero initialization does what you'd expect in a constant expr.
47+
// FIXME: the "not an ICE" warning is incorrect for C2x, but we don't yet
48+
// implement WG14 N3038.
49+
_Static_assert((int){} == 0, "what?"); // compat-warning {{use of an empty initializer is incompatible with C standards before C2x}} \
50+
pedantic-warning {{use of an empty initializer is a C2x extension}} \
51+
pedantic-warning {{expression is not an integer constant expression; folding it to a constant is a GNU extension}}
52+
}
53+

clang/test/C/C2x/n2900_n3011_2.c

Lines changed: 122 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,122 @@
1+
// RUN: %clang_cc1 -triple x86_64-unknown-unknown -std=c2x -verify -emit-llvm %s -o - | FileCheck %s
2+
// expected-no-diagnostics
3+
4+
struct S { int x, y; };
5+
struct T {
6+
int i;
7+
struct S s;
8+
};
9+
10+
// CHECK: @[[CONST_T1:.+]] = private unnamed_addr constant %struct.T { i32 1, %struct.S zeroinitializer }
11+
// CHECK: @[[CONST_T2:.+]] = private unnamed_addr constant %struct.T { i32 1, %struct.S { i32 2, i32 0 } }
12+
13+
void test_struct() {
14+
struct S s = {};
15+
// CHECK: define {{.*}} void @test_struct
16+
// CHECK-NEXT: entry:
17+
// CHECK-NEXT: %[[S:.+]] = alloca %struct.S
18+
// CHECK-NEXT: call void @llvm.memset.p0.i64({{.*}}%[[S]], i8 0, i64 8, i1 false)
19+
}
20+
21+
void test_var() {
22+
int i = {};
23+
// CHECK: define {{.*}} void @test_var
24+
// CHECK-NEXT: entry:
25+
// CHECK-NEXT: %[[I:.+]] = alloca i32
26+
// CHECK-NEXT: store i32 0, ptr %[[I]]
27+
}
28+
29+
void test_simple_compound_literal() {
30+
int j = (int){};
31+
// CHECK: define {{.*}} void @test_simple_compound_literal
32+
// CHECK-NEXT: entry:
33+
// CHECK-NEXT: %[[J:.+]] = alloca i32
34+
// CHECK-NEXT: %[[COMPOUND:.+]] = alloca i32
35+
// CHECK-NEXT: store i32 0, ptr %[[COMPOUND]]
36+
// CHECK-NEXT: %[[MEM:.+]] = load i32, ptr %[[COMPOUND]]
37+
// CHECK-NEXT: store i32 %[[MEM]], ptr %[[J]]
38+
}
39+
40+
void test_zero_size_array() {
41+
int unknown_size[] = {};
42+
// CHECK: define {{.*}} void @test_zero_size_array
43+
// CHECK-NEXT: entry:
44+
// CHECK-NEXT: %[[UNKNOWN:.+]] = alloca [0 x i32]
45+
}
46+
47+
void test_vla() {
48+
int num_elts = 12;
49+
int vla[num_elts] = {};
50+
// CHECK: define {{.*}} void @test_vla
51+
// CHECK-NEXT: entry:
52+
// CHECK-NEXT: %[[NUM_ELTS_PTR:.+]] = alloca i32
53+
// CHECK: %[[VLA_EXPR:.+]] = alloca i64
54+
// CHECK-NEXT: store i32 12, ptr %[[NUM_ELTS_PTR]]
55+
// CHECK-NEXT: %[[NUM_ELTS:.+]] = load i32, ptr %[[NUM_ELTS_PTR]]
56+
// CHECK-NEXT: %[[NUM_ELTS_EXT:.+]] = zext i32 %[[NUM_ELTS]] to i64
57+
// CHECK: %[[VLA:.+]] = alloca i32, i64 %[[NUM_ELTS_EXT]]
58+
// CHECK-NEXT: store i64 %[[NUM_ELTS_EXT]], ptr %[[VLA_EXPR]]
59+
// CHECK-NEXT: %[[BYTES_TO_COPY:.+]] = mul nuw i64 %[[NUM_ELTS_EXT]], 4
60+
// CHECK-NEXT: call void @llvm.memset.p0.i64(ptr {{.*}} %[[VLA]], i8 0, i64 %[[BYTES_TO_COPY]], i1 false)
61+
}
62+
63+
void test_zero_size_vla() {
64+
int num_elts = 0;
65+
int vla[num_elts] = {};
66+
// CHECK: define {{.*}} void @test_zero_size_vla
67+
// CHECK-NEXT: entry:
68+
// CHECK-NEXT: %[[NUM_ELTS_PTR:.+]] = alloca i32
69+
// CHECK: %[[VLA_EXPR:.+]] = alloca i64
70+
// CHECK-NEXT: store i32 0, ptr %[[NUM_ELTS_PTR]]
71+
// CHECK-NEXT: %[[NUM_ELTS:.+]] = load i32, ptr %[[NUM_ELTS_PTR]]
72+
// CHECK-NEXT: %[[NUM_ELTS_EXT:.+]] = zext i32 %[[NUM_ELTS]] to i64
73+
// CHECK: %[[VLA:.+]] = alloca i32, i64 %[[NUM_ELTS_EXT]]
74+
// CHECK-NEXT: store i64 %[[NUM_ELTS_EXT]], ptr %[[VLA_EXPR]]
75+
// CHECK-NEXT: %[[BYTES_TO_COPY:.+]] = mul nuw i64 %[[NUM_ELTS_EXT]], 4
76+
// CHECK-NEXT: call void @llvm.memset.p0.i64(ptr {{.*}} %[[VLA]], i8 0, i64 %[[BYTES_TO_COPY]], i1 false)
77+
}
78+
79+
void test_compound_literal_vla() {
80+
int num_elts = 12;
81+
int *compound_literal_vla = (int[num_elts]){};
82+
// CHECK: define {{.*}} void @test_compound_literal_vla
83+
// CHECK-NEXT: entry:
84+
// CHECK-NEXT: %[[NUM_ELTS_PTR:.+]] = alloca i32
85+
// CHECK-NEXT: %[[COMP_LIT_VLA:.+]] = alloca ptr
86+
// CHECK-NEXT: %[[COMP_LIT:.+]] = alloca i32
87+
// CHECK-NEXT: store i32 12, ptr %[[NUM_ELTS_PTR]]
88+
// CHECK-NEXT: %[[NUM_ELTS:.+]] = load i32, ptr %[[NUM_ELTS_PTR]]
89+
// CHECK-NEXT: %[[NUM_ELTS_EXT:.+]] = zext i32 %[[NUM_ELTS]] to i64
90+
// CHECK-NEXT: %[[BYTES_TO_COPY:.+]] = mul nuw i64 %[[NUM_ELTS_EXT]], 4
91+
// CHECK-NEXT: call void @llvm.memset.p0.i64(ptr {{.*}} %[[COMP_LIT]], i8 0, i64 %[[BYTES_TO_COPY]], i1 false)
92+
// CHECK-NEXT: store ptr %[[COMP_LIT]], ptr %[[COMP_LIT_VLA]]
93+
}
94+
95+
void test_nested_structs() {
96+
struct T t1 = { 1, {} };
97+
struct T t2 = { 1, { 2, {} } };
98+
struct T t3 = { (int){}, {} };
99+
// CHECK: define {{.*}} void @test_nested_structs
100+
// CHECK-NEXT: entry:
101+
// CHECK-NEXT: %[[T1:.+]] = alloca %struct.T
102+
// CHECK-NEXT: %[[T2:.+]] = alloca %struct.T
103+
// CHECK-NEXT: %[[T3:.+]] = alloca %struct.T
104+
// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr {{.*}} %[[T1]], ptr {{.*}} @[[CONST_T1]], i64 12, i1 false)
105+
// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr {{.*}} %[[T2]], ptr {{.*}} @[[CONST_T2]], i64 12, i1 false)
106+
// CHECK-NEXT: call void @llvm.memset.p0.i64(ptr {{.*}} %[[T3]], i8 0, i64 12, i1 false)
107+
}
108+
109+
void test_vla_of_nested_structs(int num_elts) {
110+
struct T t3[num_elts] = {};
111+
// CHECK: define {{.*}} void @test_vla_of_nested_structs(i32 noundef %[[NUM_ELTS_PARAM:.+]])
112+
// CHECK-NEXT: entry:
113+
// CHECK-NEXT: %[[NUM_ELTS_PTR:.+]] = alloca i32
114+
// CHECK: %[[VLA_EXPR:.+]] = alloca i64
115+
// CHECK-NEXT: store i32 %[[NUM_ELTS_PARAM]], ptr %[[NUM_ELTS_PTR]]
116+
// CHECK-NEXT: %[[NUM_ELTS_LOCAL:.+]] = load i32, ptr %[[NUM_ELTS_PTR]]
117+
// CHECK-NEXT: %[[NUM_ELTS_EXT:.+]] = zext i32 %[[NUM_ELTS_LOCAL]] to i64
118+
// CHECK: %[[VLA:.+]] = alloca %struct.T, i64 %[[NUM_ELTS_EXT]]
119+
// CHECK-NEXT: store i64 %[[NUM_ELTS_EXT]], ptr %[[VLA_EXPR]]
120+
// CHECK-NEXT: %[[COPY_BYTES:.+]] = mul nuw i64 %[[NUM_ELTS_EXT]], 12
121+
// CHECK-NEXT: call void @llvm.memset.p0.i64(ptr {{.*}} %[[VLA]], i8 0, i64 %[[COPY_BYTES]], i1 false)
122+
}

0 commit comments

Comments
 (0)