[clang] Skip auto-init on scalar vars that have a non-constant Init and no self-ref (PR #94642)
Jan Voung via cfe-commits
cfe-commits at lists.llvm.org
Thu Jun 6 10:16:29 PDT 2024
https://github.com/jvoung updated https://github.com/llvm/llvm-project/pull/94642
>From 23ee93af279360dc94cc34f47f9bbef2ba40f815 Mon Sep 17 00:00:00 2001
From: Jan Voung <jvoung at gmail.com>
Date: Thu, 6 Jun 2024 16:32:20 +0000
Subject: [PATCH 1/2] Skip auto-init on scalar vars that have a non-constant
Init and no self-ref
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 reduce, which
may be in a fairly late pass (DSE) or may not get optimized in lower
optimization levels like O1 (no DSe). This is ~10% less left-over
auto-init in O1 in a few examples checked.
---
clang/lib/CodeGen/CGDecl.cpp | 15 ++-
.../CodeGenCXX/auto-var-init-max-size.cpp | 2 +-
.../CodeGenCXX/auto-var-init-stop-after.cpp | 2 +-
clang/test/CodeGenCXX/auto-var-init.cpp | 8 --
...ar-init-skip-scalar-with-nonconst-init.cpp | 112 ++++++++++++++++++
5 files changed, 128 insertions(+), 11 deletions(-)
create mode 100644 clang/test/CodeGenCXX/trivial-auto-var-init-skip-scalar-with-nonconst-init.cpp
diff --git a/clang/lib/CodeGen/CGDecl.cpp b/clang/lib/CodeGen/CGDecl.cpp
index 4a213990d1e36..49e97a23cb0a9 100644
--- a/clang/lib/CodeGen/CGDecl.cpp
+++ b/clang/lib/CodeGen/CGDecl.cpp
@@ -1972,7 +1972,20 @@ void CodeGenFunction::EmitAutoVarInit(const AutoVarEmission &emission) {
}
if (!constant) {
- initializeWhatIsTechnicallyUninitialized(Loc);
+ if (trivialAutoVarInit !=
+ LangOptions::TrivialAutoVarInitKind::Uninitialized) {
+ // At this point, we know D has an Init expression, but isn't a constant.
+ // - If D is not a scalar, auto-var-init conservatively (members may be
+ // left uninitialized by constructor Init expressions for example).
+ // - If D is a scalar, we only need to auto-var-init if there is a
+ // self-reference. Otherwise, the Init expression should be sufficient.
+ // It may be that the Init expression uses other uninitialized memory,
+ // but auto-var-init here would not help, as auto-init would get
+ // overwritten by Init.
+ if (!D.getType()->isScalarType() || isAccessedBy(D, Init)) {
+ initializeWhatIsTechnicallyUninitialized(Loc);
+ }
+ }
LValue lv = MakeAddrLValue(Loc, type);
lv.setNonGC(true);
return EmitExprAsInit(Init, &D, lv, capturedByInit);
diff --git a/clang/test/CodeGenCXX/auto-var-init-max-size.cpp b/clang/test/CodeGenCXX/auto-var-init-max-size.cpp
index ef38b8227a9a1..f4db297a07be8 100644
--- a/clang/test/CodeGenCXX/auto-var-init-max-size.cpp
+++ b/clang/test/CodeGenCXX/auto-var-init-max-size.cpp
@@ -15,7 +15,7 @@ struct Foo {
int foo(unsigned n) {
bool var_size_1;
- long var_size_8 = 123;
+ long var_size_8;
void *var_size_8p;
int var_size_1024[256];
Foo var_size_1028;
diff --git a/clang/test/CodeGenCXX/auto-var-init-stop-after.cpp b/clang/test/CodeGenCXX/auto-var-init-stop-after.cpp
index a782692d0127e..f1dc0e3a068e7 100644
--- a/clang/test/CodeGenCXX/auto-var-init-stop-after.cpp
+++ b/clang/test/CodeGenCXX/auto-var-init-stop-after.cpp
@@ -18,7 +18,7 @@ typedef struct {
int foo(unsigned n) {
// scalar variable
- long a = 888;
+ long a;
// array
S arr[ARRLEN];
// VLA
diff --git a/clang/test/CodeGenCXX/auto-var-init.cpp b/clang/test/CodeGenCXX/auto-var-init.cpp
index e1568bee136e5..e697731b0cdf1 100644
--- a/clang/test/CodeGenCXX/auto-var-init.cpp
+++ b/clang/test/CodeGenCXX/auto-var-init.cpp
@@ -146,16 +146,8 @@ struct notlockfree { long long a[4]; };
// PATTERN-O1-NOT: @__const.test_atomictailpad_uninit.uninit
// PATTERN-O0: @__const.test_complexfloat_uninit.uninit = private unnamed_addr constant { float, float } { float 0xFFFFFFFFE0000000, float 0xFFFFFFFFE0000000 }, align 4
// PATTERN-O1-NOT: @__const.test_complexfloat_uninit.uninit
-// PATTERN-O0: @__const.test_complexfloat_braces.braces = private unnamed_addr constant { float, float } { float 0xFFFFFFFFE0000000, float 0xFFFFFFFFE0000000 }, align 4
-// PATTERN-O1-NOT: @__const.test_complexfloat_braces.braces
-// PATTERN-O0: @__const.test_complexfloat_custom.custom = private unnamed_addr constant { float, float } { float 0xFFFFFFFFE0000000, float 0xFFFFFFFFE0000000 }, align 4
-// PATTERN-O1-NOT: @__const.test_complexfloat_custom.custom
// PATTERN-O0: @__const.test_complexdouble_uninit.uninit = private unnamed_addr constant { double, double } { double 0xFFFFFFFFFFFFFFFF, double 0xFFFFFFFFFFFFFFFF }, align 8
// PATTERN-O1-NOT: @__const.test_complexdouble_uninit.uninit
-// PATTERN-O0: @__const.test_complexdouble_braces.braces = private unnamed_addr constant { double, double } { double 0xFFFFFFFFFFFFFFFF, double 0xFFFFFFFFFFFFFFFF }, align 8
-// PATTERN-O1-NOT: @__const.test_complexdouble_braces.braces
-// PATTERN-O0: @__const.test_complexdouble_custom.custom = private unnamed_addr constant { double, double } { double 0xFFFFFFFFFFFFFFFF, double 0xFFFFFFFFFFFFFFFF }, align 8
-// PATTERN-O1-NOT: @__const.test_complexdouble_custom.custom
// PATTERN-O0: @__const.test_semivolatile_uninit.uninit = private unnamed_addr constant %struct.semivolatile { i32 [[I32]], i32 [[I32]] }, align 4
// PATTERN-O0: @__const.test_semivolatile_custom.custom = private unnamed_addr constant %struct.semivolatile { i32 1145324612, i32 1145324612 }, align 4
// PATTERN-O1-NOT: @__const.test_semivolatile_custom.custom
diff --git a/clang/test/CodeGenCXX/trivial-auto-var-init-skip-scalar-with-nonconst-init.cpp b/clang/test/CodeGenCXX/trivial-auto-var-init-skip-scalar-with-nonconst-init.cpp
new file mode 100644
index 0000000000000..a653bd761879c
--- /dev/null
+++ b/clang/test/CodeGenCXX/trivial-auto-var-init-skip-scalar-with-nonconst-init.cpp
@@ -0,0 +1,112 @@
+// RUN: %clang_cc1 -triple x86_64-unknown-unknown %s -emit-llvm -o - | FileCheck %s -check-prefix=UNINIT
+// RUN: %clang_cc1 -triple x86_64-unknown-unknown -ftrivial-auto-var-init=pattern %s -emit-llvm -o - | FileCheck %s -check-prefix=PATTERN
+// RUN: %clang_cc1 -triple x86_64-unknown-unknown -ftrivial-auto-var-init=zero %s -emit-llvm -o - | FileCheck %s -check-prefix=ZERO
+
+template<typename T> void used(T &) noexcept;
+
+extern "C" {
+
+extern int get_int(int) noexcept;
+struct C {
+ int x;
+ int y;
+};
+extern C make_c() noexcept;
+
+// Scalar with a self-reference: does need auto-init.
+// UNINIT-LABEL: test_selfinit_call(
+// ZERO-LABEL: test_selfinit_call(
+// ZERO: store i32 0, ptr %self, align 4, !annotation [[AUTO_INIT:!.+]]
+// PATTERN-LABEL: test_selfinit_call(
+// PATTERN: store i32 -1431655766, ptr %self, align 4, !annotation [[AUTO_INIT:!.+]]
+void test_selfinit_call() {
+ int self = get_int(self);
+ used(self);
+}
+
+// Scalar without a self-reference: no auto-init needed.
+// UNINIT-LABEL: test_nonself_call(
+// ZERO-LABEL: test_nonself_call(
+// ZERO-NOT: !annotation [[AUTO_INIT:!.+]]
+// PATTERN-LABEL: test_nonself_call(
+// PATTERN-NOT: !annotation [[AUTO_INIT:!.+]]
+void test_nonself_call() {
+ int x = get_int(2);
+ used(x);
+}
+
+// Scalar with a self-reference: does need auto-init.
+// UNINIT-LABEL: test_selfinit_lambda_call(
+// ZERO-LABEL: test_selfinit_lambda_call(
+// ZERO: store i32 0, ptr %self, align 4, !annotation [[AUTO_INIT:!.+]]
+// PATTERN-LABEL: test_selfinit_lambda_call(
+// PATTERN: store i32 -1431655766, ptr %self, align 4, !annotation [[AUTO_INIT:!.+]]
+void test_selfinit_lambda_call() {
+ int self = [&](){ return self; }();
+ used(self);
+}
+
+// Not a scalar: auto-init just in case
+// UNINIT-LABEL: test_nonscalar_call(
+// ZERO-LABEL: test_nonscalar_call(
+// ZERO: call void @llvm.memset{{.*}}, i8 0, i64 8, {{.*}} !annotation [[AUTO_INIT:!.+]]
+// PATTERN-LABEL: test_nonscalar_call(
+// PATTERN: call void @llvm.memcpy{{.*}}, i64 8, {{.*}} !annotation [[AUTO_INIT:!.+]]
+void test_nonscalar_call() {
+ C c = make_c();
+ used(c);
+}
+
+// Scalar with a self-reference: does need auto-init.
+// UNINIT
+// UNINIT-LABEL: test_self_ptr(
+// ZERO-LABEL: test_self_ptr(
+// ZERO: store ptr null, ptr %self, align 8, !annotation [[AUTO_INIT:!.+]]
+// PATTERN-LABEL: test_self_ptr(
+// PATTERN: store ptr inttoptr (i64 -6148914691236517206 to ptr), ptr %self, align 8, !annotation [[AUTO_INIT:!.+]]
+void test_self_ptr() {
+ void* self = self;
+ used(self);
+}
+
+// Scalar without a self-reference: no auto-init needed.
+// UNINIT
+// UNINIT-LABEL: test_nonself_ptr(
+// ZERO-LABEL: test_nonself_ptr(
+// ZERO-NOT: !annotation [[AUTO_INIT:!.+]]
+// PATTERN-LABEL: test_nonself_ptr(
+// PATTERN-NOT: !annotation [[AUTO_INIT:!.+]]
+void test_nonself_ptr() {
+ int y = 0;
+ void* x = &y;
+ used(x);
+}
+
+// Scalar with a self-reference: does need auto-init.
+// UNINIT
+// UNINIT-LABEL: test_self_complex(
+// ZERO-LABEL: test_self_complex(
+// ZERO: call void @llvm.memset{{.*}} !annotation [[AUTO_INIT:!.+]]
+// PATTERN-LABEL: test_self_complex(
+// PATTERN: call void @llvm.memcpy{{.*}} !annotation [[AUTO_INIT:!.+]]
+void test_self_complex() {
+ _Complex float self = 3.0 * 3.0 * self;
+ used(self);
+}
+
+// Scalar without a self-reference: no auto-init needed.
+// UNINIT
+// UNINIT-LABEL: test_nonself_complex(
+// ZERO-LABEL: test_nonself_complex(
+// ZERO-NOT: !annotation [[AUTO_INIT:!.+]]
+// PATTERN-LABEL: test_nonself_complex(
+// PATTERN-NOT: !annotation [[AUTO_INIT:!.+]]
+void test_nonself_complex() {
+ _Complex float y = 0.0;
+ _Complex float x = 3.0 * 3.0 * y;
+ used(x);
+}
+
+} // extern "C"
+
+// CHECK: [[AUTO_INIT]] = !{ !"auto-init" }
>From 3c09d218cdca3f85f1552dda48541492cb0959ea Mon Sep 17 00:00:00 2001
From: Jan Voung <jvoung at gmail.com>
Date: Thu, 6 Jun 2024 16:32:20 +0000
Subject: [PATCH 2/2] Skip auto-init on scalar vars that have a non-constant
Init and no self-ref
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 reduce, 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.
---
...ivial-auto-var-init-skip-scalar-with-nonconst-init.cpp | 8 +++-----
1 file changed, 3 insertions(+), 5 deletions(-)
diff --git a/clang/test/CodeGenCXX/trivial-auto-var-init-skip-scalar-with-nonconst-init.cpp b/clang/test/CodeGenCXX/trivial-auto-var-init-skip-scalar-with-nonconst-init.cpp
index a653bd761879c..4a7b5d2be5799 100644
--- a/clang/test/CodeGenCXX/trivial-auto-var-init-skip-scalar-with-nonconst-init.cpp
+++ b/clang/test/CodeGenCXX/trivial-auto-var-init-skip-scalar-with-nonconst-init.cpp
@@ -58,7 +58,6 @@ void test_nonscalar_call() {
}
// Scalar with a self-reference: does need auto-init.
-// UNINIT
// UNINIT-LABEL: test_self_ptr(
// ZERO-LABEL: test_self_ptr(
// ZERO: store ptr null, ptr %self, align 8, !annotation [[AUTO_INIT:!.+]]
@@ -70,7 +69,6 @@ void test_self_ptr() {
}
// Scalar without a self-reference: no auto-init needed.
-// UNINIT
// UNINIT-LABEL: test_nonself_ptr(
// ZERO-LABEL: test_nonself_ptr(
// ZERO-NOT: !annotation [[AUTO_INIT:!.+]]
@@ -83,7 +81,6 @@ void test_nonself_ptr() {
}
// Scalar with a self-reference: does need auto-init.
-// UNINIT
// UNINIT-LABEL: test_self_complex(
// ZERO-LABEL: test_self_complex(
// ZERO: call void @llvm.memset{{.*}} !annotation [[AUTO_INIT:!.+]]
@@ -95,7 +92,6 @@ void test_self_complex() {
}
// Scalar without a self-reference: no auto-init needed.
-// UNINIT
// UNINIT-LABEL: test_nonself_complex(
// ZERO-LABEL: test_nonself_complex(
// ZERO-NOT: !annotation [[AUTO_INIT:!.+]]
@@ -109,4 +105,6 @@ void test_nonself_complex() {
} // extern "C"
-// CHECK: [[AUTO_INIT]] = !{ !"auto-init" }
+// ZERO: [[AUTO_INIT]] = !{!"auto-init"}
+// PATTERN: [[AUTO_INIT]] = !{!"auto-init"}
+
More information about the cfe-commits
mailing list