[clang] 0cf1e66 - [clang] Skip auto-init on scalar vars that have a non-constant Init and no self-ref (#94642)

via cfe-commits cfe-commits at lists.llvm.org
Mon Jun 24 05:46:36 PDT 2024


Author: Jan Voung
Date: 2024-06-24T08:46:31-04:00
New Revision: 0cf1e662b13fb20d3897c5b9055c60b0de50beca

URL: https://github.com/llvm/llvm-project/commit/0cf1e662b13fb20d3897c5b9055c60b0de50beca
DIFF: https://github.com/llvm/llvm-project/commit/0cf1e662b13fb20d3897c5b9055c60b0de50beca.diff

LOG: [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.

Added: 
    clang/test/CodeGenCXX/trivial-auto-var-init-skip-scalar-with-nonconst-init.cpp

Modified: 
    clang/lib/CodeGen/CGDecl.cpp
    clang/test/CodeGenCXX/auto-var-init-max-size.cpp
    clang/test/CodeGenCXX/auto-var-init-stop-after.cpp
    clang/test/CodeGenCXX/auto-var-init.cpp

Removed: 
    


################################################################################
diff  --git a/clang/lib/CodeGen/CGDecl.cpp b/clang/lib/CodeGen/CGDecl.cpp
index 4a213990d1e36..90aa4c0745a8a 100644
--- a/clang/lib/CodeGen/CGDecl.cpp
+++ b/clang/lib/CodeGen/CGDecl.cpp
@@ -1972,7 +1972,21 @@ 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() || capturedByInit ||
+          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..25c30c383332b 100644
--- a/clang/test/CodeGenCXX/auto-var-init-max-size.cpp
+++ b/clang/test/CodeGenCXX/auto-var-init-max-size.cpp
@@ -15,7 +15,9 @@ struct Foo {
 
 int foo(unsigned n) {
   bool var_size_1;
-  long var_size_8 = 123;
+  long var_size_8_init = 123;
+  long var_size_8_noinit;
+  long var_size_8_init_later;
   void *var_size_8p;
   int var_size_1024[256];
   Foo var_size_1028;
@@ -24,29 +26,39 @@ int foo(unsigned n) {
   int var_vla[n];
   // builtin, non-constant size
   var_size_8p = __builtin_alloca(sizeof(unsigned long long) * n);
-  // There are 8 variables: var_size_1, var_size_8, var_size_8p, var_size_1024,
+  var_size_8_init_later = 456;
+  // There are 10 variables: var_size_1, var_size_8_init, var_size_8_noinit,
+  // var_size_8_init_later, var_size_8p, var_size_1024,
   // var_size_1028, var_size_4096, var_vla, and a builtin anonymous var ("%5").
+  // - Doesn't need auto-init: var_size_8_init
   // - COMMON (auto-init regardless of the max size): "var_vla", and "%5"
   // - Max size 1: "var_size_1"
-  // - Max size 1024: "var_size_1", "var_size_8", "var_size_8p", "var_size_1024"
-  // - Max size 4096: "var_size_1", "var_size_8", "var_size_8p", "var_size_1024", "var_size_1028", "var_size_4096"
+  // - Max size 1024: "var_size_1", "var_size_8_noinit", "var_size_8_init_later", "var_size_8p", "var_size_1024"
+  // - Max size 4096: "var_size_1", "var_size_8_noinit", "var_size_8_init_later", "var_size_8p", "var_size_1024",
+  //                  "var_size_1028", "var_size_4096"
   //
   // PATTERN-MAX-1: store i8 -86, ptr %var_size_1, align 1, !annotation [[AUTO_INIT:!.+]]
-  // PATTERN-MAX-1-NOT: store i64 -6148914691236517206, ptr %var_size_8, align 8, !annotation [[AUTO_INIT:!.+]]
+  // PATTERN-MAX-1-NEXT: store i64 123, ptr %var_size_8_init, align 8
+  // PATTERN-MAX-1-NOT: store i64 -6148914691236517206, ptr %var_size_8_noinit, align 8, !annotation [[AUTO_INIT:!.+]]
+  // PATTERN-MAX-1-NOT: store i64 -6148914691236517206, ptr %var_size_8_init_later, align 8, !annotation [[AUTO_INIT:!.+]]
   // PATTERN-MAX-1-NOT: store ptr inttoptr (i64 -6148914691236517206 to ptr), ptr %var_size_8p, align 8, !annotation [[AUTO_INIT:!.+]]
   // 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:!.+]]
   // 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:!.+]]
   // 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:!.+]]
 
   // PATTERN-MAX-1024: store i8 -86, ptr %var_size_1, align 1, !annotation [[AUTO_INIT:!.+]]
-  // PATTERN-MAX-1024: store i64 -6148914691236517206, ptr %var_size_8, align 8, !annotation [[AUTO_INIT:!.+]]
+  // PATTERN-MAX-1024-NEXT: store i64 123, ptr %var_size_8_init, align 8
+  // PATTERN-MAX-1024: store i64 -6148914691236517206, ptr %var_size_8_noinit, align 8, !annotation [[AUTO_INIT:!.+]]
+  // PATTERN-MAX-1024: store i64 -6148914691236517206, ptr %var_size_8_init_later, align 8, !annotation [[AUTO_INIT:!.+]]
   // PATTERN-MAX-1024: store ptr inttoptr (i64 -6148914691236517206 to ptr), ptr %var_size_8p, align 8, !annotation [[AUTO_INIT:!.+]]
   // PATTERN-MAX-1024: call void @llvm.memset.p0.i64(ptr align 16 %var_size_1024, i8 -86, i64 1024, i1 false), !annotation [[AUTO_INIT:!.+]]
   // 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:!.+]]
   // 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:!.+]]
 
   // PATTERN-MAX-4096: store i8 -86, ptr %var_size_1, align 1, !annotation [[AUTO_INIT:!.+]]
-  // PATTERN-MAX-4096: store i64 -6148914691236517206, ptr %var_size_8, align 8, !annotation [[AUTO_INIT:!.+]]
+  // PATTERN-MAX-4096-NEXT: store i64 123, ptr %var_size_8_init, align 8
+  // PATTERN-MAX-4096: store i64 -6148914691236517206, ptr %var_size_8_noinit, align 8, !annotation [[AUTO_INIT:!.+]]
+  // PATTERN-MAX-4096: store i64 -6148914691236517206, ptr %var_size_8_init_later, align 8, !annotation [[AUTO_INIT:!.+]]
   // PATTERN-MAX-4096: store ptr inttoptr (i64 -6148914691236517206 to ptr), ptr %var_size_8p, align 8, !annotation [[AUTO_INIT:!.+]]
   // PATTERN-MAX-4096: call void @llvm.memset.p0.i64(ptr align 16 %var_size_1024, i8 -86, i64 1024, i1 false), !annotation [[AUTO_INIT:!.+]]
   // 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) {
   // PATTERN-COMMON: call void @llvm.memset.p0.i64(ptr align 16 %5, i8 -86, i64 %mul, i1 false), !annotation [[AUTO_INIT:!.+]]
 
   // ZERO-MAX-1: store i8 0, ptr %var_size_1, align 1, !annotation [[AUTO_INIT:!.+]]
-  // ZERO-MAX-1-NOT: store i64 0, ptr %var_size_8, align 8, !annotation [[AUTO_INIT:!.+]]
+  // ZERO-MAX-1-NEXT: store i64 123, ptr %var_size_8_init, align 8
+  // ZERO-MAX-1-NOT: store i64 0, ptr %var_size_8_noinit, align 8, !annotation [[AUTO_INIT:!.+]]
+  // ZERO-MAX-1-NOT: store i64 0, ptr %var_size_8_init_later, align 8, !annotation [[AUTO_INIT:!.+]]
   // ZERO-MAX-1-NOT: store ptr null, ptr %var_size_8p, align 8, !annotation [[AUTO_INIT:!.+]]
   // 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:!.+]]
   // 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:!.+]]
   // 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:!.+]]
 
   // ZERO-MAX-1024: store i8 0, ptr %var_size_1, align 1, !annotation [[AUTO_INIT:!.+]]
-  // ZERO-MAX-1024: store i64 0, ptr %var_size_8, align 8, !annotation [[AUTO_INIT:!.+]]
+  // ZERO-MAX-1024-NEXT: store i64 123, ptr %var_size_8_init, align 8
+  // ZERO-MAX-1024: store i64 0, ptr %var_size_8_noinit, align 8, !annotation [[AUTO_INIT:!.+]]
+  // ZERO-MAX-1024: store i64 0, ptr %var_size_8_init_later, align 8, !annotation [[AUTO_INIT:!.+]]
   // ZERO-MAX-1024: store ptr null, ptr %var_size_8p, align 8, !annotation [[AUTO_INIT:!.+]]
   // ZERO-MAX-1024: call void @llvm.memset.p0.i64(ptr align 16 %var_size_1024, i8 0, i64 1024, i1 false), !annotation [[AUTO_INIT:!.+]]
   // 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:!.+]]
   // 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:!.+]]
 
   // ZERO-MAX-4096: store i8 0, ptr %var_size_1, align 1, !annotation [[AUTO_INIT:!.+]]
-  // ZERO-MAX-4096: store i64 0, ptr %var_size_8, align 8, !annotation [[AUTO_INIT:!.+]]
+  // ZERO-MAX-4096-NEXT: store i64 123, ptr %var_size_8_init, align 8
+  // ZERO-MAX-4096: store i64 0, ptr %var_size_8_noinit, align 8, !annotation [[AUTO_INIT:!.+]]
+  // ZERO-MAX-4096: store i64 0, ptr %var_size_8_init_later, align 8, !annotation [[AUTO_INIT:!.+]]
   // ZERO-MAX-4096: store ptr null, ptr %var_size_8p, align 8, !annotation [[AUTO_INIT:!.+]]
   // ZERO-MAX-4096: call void @llvm.memset.p0.i64(ptr align 16 %var_size_1024, i8 0, i64 1024, i1 false), !annotation [[AUTO_INIT:!.+]]
   // ZERO-MAX-4096: call void @llvm.memset.p0.i64(ptr align 4 %var_size_1028, i8 0, i64 1028, i1 false), !annotation [[AUTO_INIT:!.+]]

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..ba4df6ebe115f
--- /dev/null
+++ b/clang/test/CodeGenCXX/trivial-auto-var-init-skip-scalar-with-nonconst-init.cpp
@@ -0,0 +1,121 @@
+// 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);
+}
+
+// Scalar with a self-reference: does need auto-init.
+// UNINIT-LABEL:  test_selfinit_gnu_stmt_expression(
+// ZERO-LABEL:    test_selfinit_gnu_stmt_expression(
+// ZERO: store i32 0, ptr %self, align 4, !annotation [[AUTO_INIT:!.+]]
+// PATTERN-LABEL: test_selfinit_gnu_stmt_expression(
+// PATTERN: store i32 -1431655766, ptr %self, align 4, !annotation [[AUTO_INIT:!.+]]
+void test_selfinit_gnu_stmt_expression() {
+  int self = ({int x = self; x + 1; });
+  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-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-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-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-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"
+
+// ZERO: [[AUTO_INIT]] = !{!"auto-init"}
+// PATTERN: [[AUTO_INIT]] = !{!"auto-init"}
+


        


More information about the cfe-commits mailing list