Skip to content

Commit 0cf1e66

Browse files
authored
[clang] Skip auto-init on scalar vars that have a non-constant Init and no self-ref (#94642)
In that scalar case, the Init should initialize the auto var before use. The Init might use uninitialized memory from other sources (e.g., heap) but auto-init did not help us in that case because the auto-init would have been overwritten by the Init before use. For non-scalars e.g., classes, the Init expr might be a ctor call that leaves uninitialized members, so we leave the auto-init there. The motivation is to have less IR for the optimizer to later remove, which may not be until a fairly late pass (DSE) or may not get optimized in lower optimization levels like O1 (no DSE) or sometimes due to derefinement. This is ~10% less left-over auto-init in O1 in a few examples checked.
1 parent 6e3725d commit 0cf1e66

File tree

5 files changed

+165
-20
lines changed

5 files changed

+165
-20
lines changed

clang/lib/CodeGen/CGDecl.cpp

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1972,7 +1972,21 @@ void CodeGenFunction::EmitAutoVarInit(const AutoVarEmission &emission) {
19721972
}
19731973

19741974
if (!constant) {
1975-
initializeWhatIsTechnicallyUninitialized(Loc);
1975+
if (trivialAutoVarInit !=
1976+
LangOptions::TrivialAutoVarInitKind::Uninitialized) {
1977+
// At this point, we know D has an Init expression, but isn't a constant.
1978+
// - If D is not a scalar, auto-var-init conservatively (members may be
1979+
// left uninitialized by constructor Init expressions for example).
1980+
// - If D is a scalar, we only need to auto-var-init if there is a
1981+
// self-reference. Otherwise, the Init expression should be sufficient.
1982+
// It may be that the Init expression uses other uninitialized memory,
1983+
// but auto-var-init here would not help, as auto-init would get
1984+
// overwritten by Init.
1985+
if (!D.getType()->isScalarType() || capturedByInit ||
1986+
isAccessedBy(D, Init)) {
1987+
initializeWhatIsTechnicallyUninitialized(Loc);
1988+
}
1989+
}
19761990
LValue lv = MakeAddrLValue(Loc, type);
19771991
lv.setNonGC(true);
19781992
return EmitExprAsInit(Init, &D, lv, capturedByInit);

clang/test/CodeGenCXX/auto-var-init-max-size.cpp

Lines changed: 28 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,9 @@ struct Foo {
1515

1616
int foo(unsigned n) {
1717
bool var_size_1;
18-
long var_size_8 = 123;
18+
long var_size_8_init = 123;
19+
long var_size_8_noinit;
20+
long var_size_8_init_later;
1921
void *var_size_8p;
2022
int var_size_1024[256];
2123
Foo var_size_1028;
@@ -24,29 +26,39 @@ int foo(unsigned n) {
2426
int var_vla[n];
2527
// builtin, non-constant size
2628
var_size_8p = __builtin_alloca(sizeof(unsigned long long) * n);
27-
// There are 8 variables: var_size_1, var_size_8, var_size_8p, var_size_1024,
29+
var_size_8_init_later = 456;
30+
// There are 10 variables: var_size_1, var_size_8_init, var_size_8_noinit,
31+
// var_size_8_init_later, var_size_8p, var_size_1024,
2832
// var_size_1028, var_size_4096, var_vla, and a builtin anonymous var ("%5").
33+
// - Doesn't need auto-init: var_size_8_init
2934
// - COMMON (auto-init regardless of the max size): "var_vla", and "%5"
3035
// - Max size 1: "var_size_1"
31-
// - Max size 1024: "var_size_1", "var_size_8", "var_size_8p", "var_size_1024"
32-
// - Max size 4096: "var_size_1", "var_size_8", "var_size_8p", "var_size_1024", "var_size_1028", "var_size_4096"
36+
// - Max size 1024: "var_size_1", "var_size_8_noinit", "var_size_8_init_later", "var_size_8p", "var_size_1024"
37+
// - Max size 4096: "var_size_1", "var_size_8_noinit", "var_size_8_init_later", "var_size_8p", "var_size_1024",
38+
// "var_size_1028", "var_size_4096"
3339
//
3440
// PATTERN-MAX-1: store i8 -86, ptr %var_size_1, align 1, !annotation [[AUTO_INIT:!.+]]
35-
// PATTERN-MAX-1-NOT: store i64 -6148914691236517206, ptr %var_size_8, align 8, !annotation [[AUTO_INIT:!.+]]
41+
// PATTERN-MAX-1-NEXT: store i64 123, ptr %var_size_8_init, align 8
42+
// PATTERN-MAX-1-NOT: store i64 -6148914691236517206, ptr %var_size_8_noinit, align 8, !annotation [[AUTO_INIT:!.+]]
43+
// PATTERN-MAX-1-NOT: store i64 -6148914691236517206, ptr %var_size_8_init_later, align 8, !annotation [[AUTO_INIT:!.+]]
3644
// PATTERN-MAX-1-NOT: store ptr inttoptr (i64 -6148914691236517206 to ptr), ptr %var_size_8p, align 8, !annotation [[AUTO_INIT:!.+]]
3745
// PATTERN-MAX-1-NOT: call void @llvm.memset.p0.i64(ptr align 16 %var_size_1024, i8 -86, i64 1024, i1 false), !annotation [[AUTO_INIT:!.+]]
3846
// PATTERN-MAX-1-NOT: call void @llvm.memset.p0.i64(ptr align 4 %var_size_1028, i8 -86, i64 1028, i1 false), !annotation [[AUTO_INIT:!.+]]
3947
// PATTERN-MAX-1-NOT: call void @llvm.memset.p0.i64(ptr align 16 %var_size_4096, i8 -86, i64 4096, i1 false), !annotation [[AUTO_INIT:!.+]]
4048

4149
// PATTERN-MAX-1024: store i8 -86, ptr %var_size_1, align 1, !annotation [[AUTO_INIT:!.+]]
42-
// PATTERN-MAX-1024: store i64 -6148914691236517206, ptr %var_size_8, align 8, !annotation [[AUTO_INIT:!.+]]
50+
// PATTERN-MAX-1024-NEXT: store i64 123, ptr %var_size_8_init, align 8
51+
// PATTERN-MAX-1024: store i64 -6148914691236517206, ptr %var_size_8_noinit, align 8, !annotation [[AUTO_INIT:!.+]]
52+
// PATTERN-MAX-1024: store i64 -6148914691236517206, ptr %var_size_8_init_later, align 8, !annotation [[AUTO_INIT:!.+]]
4353
// PATTERN-MAX-1024: store ptr inttoptr (i64 -6148914691236517206 to ptr), ptr %var_size_8p, align 8, !annotation [[AUTO_INIT:!.+]]
4454
// PATTERN-MAX-1024: call void @llvm.memset.p0.i64(ptr align 16 %var_size_1024, i8 -86, i64 1024, i1 false), !annotation [[AUTO_INIT:!.+]]
4555
// PATTERN-MAX-1024-NOT: call void @llvm.memset.p0.i64(ptr align 4 %var_size_1028, i8 -86, i64 1028, i1 false), !annotation [[AUTO_INIT:!.+]]
4656
// PATTERN-MAX-1024-NOT: call void @llvm.memset.p0.i64(ptr align 16 %var_size_4096, i8 -86, i64 4096, i1 false), !annotation [[AUTO_INIT:!.+]]
4757

4858
// PATTERN-MAX-4096: store i8 -86, ptr %var_size_1, align 1, !annotation [[AUTO_INIT:!.+]]
49-
// PATTERN-MAX-4096: store i64 -6148914691236517206, ptr %var_size_8, align 8, !annotation [[AUTO_INIT:!.+]]
59+
// PATTERN-MAX-4096-NEXT: store i64 123, ptr %var_size_8_init, align 8
60+
// PATTERN-MAX-4096: store i64 -6148914691236517206, ptr %var_size_8_noinit, align 8, !annotation [[AUTO_INIT:!.+]]
61+
// PATTERN-MAX-4096: store i64 -6148914691236517206, ptr %var_size_8_init_later, align 8, !annotation [[AUTO_INIT:!.+]]
5062
// PATTERN-MAX-4096: store ptr inttoptr (i64 -6148914691236517206 to ptr), ptr %var_size_8p, align 8, !annotation [[AUTO_INIT:!.+]]
5163
// PATTERN-MAX-4096: call void @llvm.memset.p0.i64(ptr align 16 %var_size_1024, i8 -86, i64 1024, i1 false), !annotation [[AUTO_INIT:!.+]]
5264
// PATTERN-MAX-4096: call void @llvm.memset.p0.i64(ptr align 4 %var_size_1028, i8 -86, i64 1028, i1 false), !annotation [[AUTO_INIT:!.+]]
@@ -56,21 +68,27 @@ int foo(unsigned n) {
5668
// PATTERN-COMMON: call void @llvm.memset.p0.i64(ptr align 16 %5, i8 -86, i64 %mul, i1 false), !annotation [[AUTO_INIT:!.+]]
5769

5870
// ZERO-MAX-1: store i8 0, ptr %var_size_1, align 1, !annotation [[AUTO_INIT:!.+]]
59-
// ZERO-MAX-1-NOT: store i64 0, ptr %var_size_8, align 8, !annotation [[AUTO_INIT:!.+]]
71+
// ZERO-MAX-1-NEXT: store i64 123, ptr %var_size_8_init, align 8
72+
// ZERO-MAX-1-NOT: store i64 0, ptr %var_size_8_noinit, align 8, !annotation [[AUTO_INIT:!.+]]
73+
// ZERO-MAX-1-NOT: store i64 0, ptr %var_size_8_init_later, align 8, !annotation [[AUTO_INIT:!.+]]
6074
// ZERO-MAX-1-NOT: store ptr null, ptr %var_size_8p, align 8, !annotation [[AUTO_INIT:!.+]]
6175
// ZERO-MAX-1-NOT: call void @llvm.memset.p0.i64(ptr align 16 %var_size_1024, i8 0, i64 1024, i1 false), !annotation [[AUTO_INIT:!.+]]
6276
// ZERO-MAX-1-NOT: call void @llvm.memset.p0.i64(ptr align 4 %var_size_1028, i8 0, i64 1028, i1 false), !annotation [[AUTO_INIT:!.+]]
6377
// ZERO-MAX-1-NOT: call void @llvm.memset.p0.i64(ptr align 16 %var_size_4096, i8 0, i64 4096, i1 false), !annotation [[AUTO_INIT:!.+]]
6478

6579
// ZERO-MAX-1024: store i8 0, ptr %var_size_1, align 1, !annotation [[AUTO_INIT:!.+]]
66-
// ZERO-MAX-1024: store i64 0, ptr %var_size_8, align 8, !annotation [[AUTO_INIT:!.+]]
80+
// ZERO-MAX-1024-NEXT: store i64 123, ptr %var_size_8_init, align 8
81+
// ZERO-MAX-1024: store i64 0, ptr %var_size_8_noinit, align 8, !annotation [[AUTO_INIT:!.+]]
82+
// ZERO-MAX-1024: store i64 0, ptr %var_size_8_init_later, align 8, !annotation [[AUTO_INIT:!.+]]
6783
// ZERO-MAX-1024: store ptr null, ptr %var_size_8p, align 8, !annotation [[AUTO_INIT:!.+]]
6884
// ZERO-MAX-1024: call void @llvm.memset.p0.i64(ptr align 16 %var_size_1024, i8 0, i64 1024, i1 false), !annotation [[AUTO_INIT:!.+]]
6985
// ZERO-MAX-1024-NOT: call void @llvm.memset.p0.i64(ptr align 4 %var_size_1028, i8 0, i64 1028, i1 false), !annotation [[AUTO_INIT:!.+]]
7086
// ZERO-MAX-1024-NOT: call void @llvm.memset.p0.i64(ptr align 16 %var_size_4096, i8 0, i64 4096, i1 false), !annotation [[AUTO_INIT:!.+]]
7187

7288
// ZERO-MAX-4096: store i8 0, ptr %var_size_1, align 1, !annotation [[AUTO_INIT:!.+]]
73-
// ZERO-MAX-4096: store i64 0, ptr %var_size_8, align 8, !annotation [[AUTO_INIT:!.+]]
89+
// ZERO-MAX-4096-NEXT: store i64 123, ptr %var_size_8_init, align 8
90+
// ZERO-MAX-4096: store i64 0, ptr %var_size_8_noinit, align 8, !annotation [[AUTO_INIT:!.+]]
91+
// ZERO-MAX-4096: store i64 0, ptr %var_size_8_init_later, align 8, !annotation [[AUTO_INIT:!.+]]
7492
// ZERO-MAX-4096: store ptr null, ptr %var_size_8p, align 8, !annotation [[AUTO_INIT:!.+]]
7593
// ZERO-MAX-4096: call void @llvm.memset.p0.i64(ptr align 16 %var_size_1024, i8 0, i64 1024, i1 false), !annotation [[AUTO_INIT:!.+]]
7694
// ZERO-MAX-4096: call void @llvm.memset.p0.i64(ptr align 4 %var_size_1028, i8 0, i64 1028, i1 false), !annotation [[AUTO_INIT:!.+]]

clang/test/CodeGenCXX/auto-var-init-stop-after.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ typedef struct {
1818

1919
int foo(unsigned n) {
2020
// scalar variable
21-
long a = 888;
21+
long a;
2222
// array
2323
S arr[ARRLEN];
2424
// VLA

clang/test/CodeGenCXX/auto-var-init.cpp

Lines changed: 0 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -146,16 +146,8 @@ struct notlockfree { long long a[4]; };
146146
// PATTERN-O1-NOT: @__const.test_atomictailpad_uninit.uninit
147147
// PATTERN-O0: @__const.test_complexfloat_uninit.uninit = private unnamed_addr constant { float, float } { float 0xFFFFFFFFE0000000, float 0xFFFFFFFFE0000000 }, align 4
148148
// PATTERN-O1-NOT: @__const.test_complexfloat_uninit.uninit
149-
// PATTERN-O0: @__const.test_complexfloat_braces.braces = private unnamed_addr constant { float, float } { float 0xFFFFFFFFE0000000, float 0xFFFFFFFFE0000000 }, align 4
150-
// PATTERN-O1-NOT: @__const.test_complexfloat_braces.braces
151-
// PATTERN-O0: @__const.test_complexfloat_custom.custom = private unnamed_addr constant { float, float } { float 0xFFFFFFFFE0000000, float 0xFFFFFFFFE0000000 }, align 4
152-
// PATTERN-O1-NOT: @__const.test_complexfloat_custom.custom
153149
// PATTERN-O0: @__const.test_complexdouble_uninit.uninit = private unnamed_addr constant { double, double } { double 0xFFFFFFFFFFFFFFFF, double 0xFFFFFFFFFFFFFFFF }, align 8
154150
// PATTERN-O1-NOT: @__const.test_complexdouble_uninit.uninit
155-
// PATTERN-O0: @__const.test_complexdouble_braces.braces = private unnamed_addr constant { double, double } { double 0xFFFFFFFFFFFFFFFF, double 0xFFFFFFFFFFFFFFFF }, align 8
156-
// PATTERN-O1-NOT: @__const.test_complexdouble_braces.braces
157-
// PATTERN-O0: @__const.test_complexdouble_custom.custom = private unnamed_addr constant { double, double } { double 0xFFFFFFFFFFFFFFFF, double 0xFFFFFFFFFFFFFFFF }, align 8
158-
// PATTERN-O1-NOT: @__const.test_complexdouble_custom.custom
159151
// PATTERN-O0: @__const.test_semivolatile_uninit.uninit = private unnamed_addr constant %struct.semivolatile { i32 [[I32]], i32 [[I32]] }, align 4
160152
// PATTERN-O0: @__const.test_semivolatile_custom.custom = private unnamed_addr constant %struct.semivolatile { i32 1145324612, i32 1145324612 }, align 4
161153
// PATTERN-O1-NOT: @__const.test_semivolatile_custom.custom
Lines changed: 121 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,121 @@
1+
// RUN: %clang_cc1 -triple x86_64-unknown-unknown %s -emit-llvm -o - | FileCheck %s -check-prefix=UNINIT
2+
// RUN: %clang_cc1 -triple x86_64-unknown-unknown -ftrivial-auto-var-init=pattern %s -emit-llvm -o - | FileCheck %s -check-prefix=PATTERN
3+
// RUN: %clang_cc1 -triple x86_64-unknown-unknown -ftrivial-auto-var-init=zero %s -emit-llvm -o - | FileCheck %s -check-prefix=ZERO
4+
5+
template<typename T> void used(T &) noexcept;
6+
7+
extern "C" {
8+
9+
extern int get_int(int) noexcept;
10+
struct C {
11+
int x;
12+
int y;
13+
};
14+
extern C make_c() noexcept;
15+
16+
// Scalar with a self-reference: does need auto-init.
17+
// UNINIT-LABEL: test_selfinit_call(
18+
// ZERO-LABEL: test_selfinit_call(
19+
// ZERO: store i32 0, ptr %self, align 4, !annotation [[AUTO_INIT:!.+]]
20+
// PATTERN-LABEL: test_selfinit_call(
21+
// PATTERN: store i32 -1431655766, ptr %self, align 4, !annotation [[AUTO_INIT:!.+]]
22+
void test_selfinit_call() {
23+
int self = get_int(self);
24+
used(self);
25+
}
26+
27+
// Scalar without a self-reference: no auto-init needed.
28+
// UNINIT-LABEL: test_nonself_call(
29+
// ZERO-LABEL: test_nonself_call(
30+
// ZERO-NOT: !annotation [[AUTO_INIT:!.+]]
31+
// PATTERN-LABEL: test_nonself_call(
32+
// PATTERN-NOT: !annotation [[AUTO_INIT:!.+]]
33+
void test_nonself_call() {
34+
int x = get_int(2);
35+
used(x);
36+
}
37+
38+
// Scalar with a self-reference: does need auto-init.
39+
// UNINIT-LABEL: test_selfinit_lambda_call(
40+
// ZERO-LABEL: test_selfinit_lambda_call(
41+
// ZERO: store i32 0, ptr %self, align 4, !annotation [[AUTO_INIT:!.+]]
42+
// PATTERN-LABEL: test_selfinit_lambda_call(
43+
// PATTERN: store i32 -1431655766, ptr %self, align 4, !annotation [[AUTO_INIT:!.+]]
44+
void test_selfinit_lambda_call() {
45+
int self = [&](){ return self; }();
46+
used(self);
47+
}
48+
49+
// Scalar with a self-reference: does need auto-init.
50+
// UNINIT-LABEL: test_selfinit_gnu_stmt_expression(
51+
// ZERO-LABEL: test_selfinit_gnu_stmt_expression(
52+
// ZERO: store i32 0, ptr %self, align 4, !annotation [[AUTO_INIT:!.+]]
53+
// PATTERN-LABEL: test_selfinit_gnu_stmt_expression(
54+
// PATTERN: store i32 -1431655766, ptr %self, align 4, !annotation [[AUTO_INIT:!.+]]
55+
void test_selfinit_gnu_stmt_expression() {
56+
int self = ({int x = self; x + 1; });
57+
used(self);
58+
}
59+
60+
// Not a scalar: auto-init just in case
61+
// UNINIT-LABEL: test_nonscalar_call(
62+
// ZERO-LABEL: test_nonscalar_call(
63+
// ZERO: call void @llvm.memset{{.*}}, i8 0, i64 8, {{.*}} !annotation [[AUTO_INIT:!.+]]
64+
// PATTERN-LABEL: test_nonscalar_call(
65+
// PATTERN: call void @llvm.memcpy{{.*}}, i64 8, {{.*}} !annotation [[AUTO_INIT:!.+]]
66+
void test_nonscalar_call() {
67+
C c = make_c();
68+
used(c);
69+
}
70+
71+
// Scalar with a self-reference: does need auto-init.
72+
// UNINIT-LABEL: test_self_ptr(
73+
// ZERO-LABEL: test_self_ptr(
74+
// ZERO: store ptr null, ptr %self, align 8, !annotation [[AUTO_INIT:!.+]]
75+
// PATTERN-LABEL: test_self_ptr(
76+
// PATTERN: store ptr inttoptr (i64 -6148914691236517206 to ptr), ptr %self, align 8, !annotation [[AUTO_INIT:!.+]]
77+
void test_self_ptr() {
78+
void* self = self;
79+
used(self);
80+
}
81+
82+
// Scalar without a self-reference: no auto-init needed.
83+
// UNINIT-LABEL: test_nonself_ptr(
84+
// ZERO-LABEL: test_nonself_ptr(
85+
// ZERO-NOT: !annotation [[AUTO_INIT:!.+]]
86+
// PATTERN-LABEL: test_nonself_ptr(
87+
// PATTERN-NOT: !annotation [[AUTO_INIT:!.+]]
88+
void test_nonself_ptr() {
89+
int y = 0;
90+
void* x = &y;
91+
used(x);
92+
}
93+
94+
// Scalar with a self-reference: does need auto-init.
95+
// UNINIT-LABEL: test_self_complex(
96+
// ZERO-LABEL: test_self_complex(
97+
// ZERO: call void @llvm.memset{{.*}} !annotation [[AUTO_INIT:!.+]]
98+
// PATTERN-LABEL: test_self_complex(
99+
// PATTERN: call void @llvm.memcpy{{.*}} !annotation [[AUTO_INIT:!.+]]
100+
void test_self_complex() {
101+
_Complex float self = 3.0 * 3.0 * self;
102+
used(self);
103+
}
104+
105+
// Scalar without a self-reference: no auto-init needed.
106+
// UNINIT-LABEL: test_nonself_complex(
107+
// ZERO-LABEL: test_nonself_complex(
108+
// ZERO-NOT: !annotation [[AUTO_INIT:!.+]]
109+
// PATTERN-LABEL: test_nonself_complex(
110+
// PATTERN-NOT: !annotation [[AUTO_INIT:!.+]]
111+
void test_nonself_complex() {
112+
_Complex float y = 0.0;
113+
_Complex float x = 3.0 * 3.0 * y;
114+
used(x);
115+
}
116+
117+
} // extern "C"
118+
119+
// ZERO: [[AUTO_INIT]] = !{!"auto-init"}
120+
// PATTERN: [[AUTO_INIT]] = !{!"auto-init"}
121+

0 commit comments

Comments
 (0)