]> granicus.if.org Git - clang/commitdiff
[CodeGen] Recognize more cases of zero initialization
authorSerge Pavlov <sepavloff@gmail.com>
Mon, 21 May 2018 16:09:54 +0000 (16:09 +0000)
committerSerge Pavlov <sepavloff@gmail.com>
Mon, 21 May 2018 16:09:54 +0000 (16:09 +0000)
If a variable has an initializer, codegen tries to build its value. If
the variable is large in size, building its value requires substantial
resources. It causes strange behavior from user viewpoint: compilation
of huge zero initialized arrays like:

    char data_1[2147483648u] = { 0 };

consumes enormous amount of time and memory.

With this change codegen tries to determine if variable initializer is
equivalent to zero initializer. In this case variable value is not
constructed.

This change fixes PR18978.

Differential Revision: https://reviews.llvm.org/D46241

git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@332847 91177308-0d34-0410-b5e6-96231b3b80d8

include/clang/AST/Expr.h
lib/AST/ExprConstant.cpp
lib/CodeGen/CGExprConstant.cpp
test/CodeGen/const-init.c
test/CodeGen/designated-initializers.c
test/CodeGen/union-init2.c
test/CodeGenCXX/cxx11-initializer-aggregate.cpp
test/CodeGenCXX/cxx1z-initializer-aggregate.cpp
test/SemaCXX/large-array-init.cpp [deleted file]

index 47e8261abf15e5798194cc9031b79ee562a2fd4a..a44e98b1d1e27285268ffc369d30ccd505b3bb7e 100644 (file)
@@ -537,6 +537,13 @@ public:
   bool isConstantInitializer(ASTContext &Ctx, bool ForRef,
                              const Expr **Culprit = nullptr) const;
 
+  enum SideEffectsKind {
+    SE_NoSideEffects,          ///< Strictly evaluate the expression.
+    SE_AllowUndefinedBehavior, ///< Allow UB that we can give a value, but not
+                               ///< arbitrary unmodeled side effects.
+    SE_AllowSideEffects        ///< Allow any unmodeled side effect.
+  };
+
   /// EvalStatus is a struct with detailed info about an evaluation in progress.
   struct EvalStatus {
     /// Whether the evaluated expression has side effects.
@@ -565,6 +572,11 @@ public:
     bool hasSideEffects() const {
       return HasSideEffects;
     }
+
+    bool hasUnacceptableSideEffect(SideEffectsKind SEK) {
+      return (SEK < SE_AllowSideEffects && HasSideEffects) ||
+             (SEK < SE_AllowUndefinedBehavior && HasUndefinedBehavior);
+    }
   };
 
   /// EvalResult is a struct with detailed info about an evaluated expression.
@@ -591,13 +603,6 @@ public:
   /// side-effects.
   bool EvaluateAsBooleanCondition(bool &Result, const ASTContext &Ctx) const;
 
-  enum SideEffectsKind {
-    SE_NoSideEffects,          ///< Strictly evaluate the expression.
-    SE_AllowUndefinedBehavior, ///< Allow UB that we can give a value, but not
-                               ///< arbitrary unmodeled side effects.
-    SE_AllowSideEffects        ///< Allow any unmodeled side effect.
-  };
-
   /// EvaluateAsInt - Return true if this is a constant which we can fold and
   /// convert to an integer, using any crazy technique that we want to.
   bool EvaluateAsInt(llvm::APSInt &Result, const ASTContext &Ctx,
index 0e1fb98bf77f8701999e72a77fbf76d05edc36dd..3efd8dc6a60ef408095368eb921ec53d78cb8c72 100644 (file)
@@ -10312,12 +10312,6 @@ bool Expr::EvaluateAsBooleanCondition(bool &Result,
          HandleConversionToBool(Scratch.Val, Result);
 }
 
-static bool hasUnacceptableSideEffect(Expr::EvalStatus &Result,
-                                      Expr::SideEffectsKind SEK) {
-  return (SEK < Expr::SE_AllowSideEffects && Result.HasSideEffects) ||
-         (SEK < Expr::SE_AllowUndefinedBehavior && Result.HasUndefinedBehavior);
-}
-
 bool Expr::EvaluateAsInt(APSInt &Result, const ASTContext &Ctx,
                          SideEffectsKind AllowSideEffects) const {
   if (!getType()->isIntegralOrEnumerationType())
@@ -10325,7 +10319,7 @@ bool Expr::EvaluateAsInt(APSInt &Result, const ASTContext &Ctx,
 
   EvalResult ExprResult;
   if (!EvaluateAsRValue(ExprResult, Ctx) || !ExprResult.Val.isInt() ||
-      hasUnacceptableSideEffect(ExprResult, AllowSideEffects))
+      ExprResult.hasUnacceptableSideEffect(AllowSideEffects))
     return false;
 
   Result = ExprResult.Val.getInt();
@@ -10339,7 +10333,7 @@ bool Expr::EvaluateAsFloat(APFloat &Result, const ASTContext &Ctx,
 
   EvalResult ExprResult;
   if (!EvaluateAsRValue(ExprResult, Ctx) || !ExprResult.Val.isFloat() ||
-      hasUnacceptableSideEffect(ExprResult, AllowSideEffects))
+      ExprResult.hasUnacceptableSideEffect(AllowSideEffects))
     return false;
 
   Result = ExprResult.Val.getFloat();
@@ -10417,7 +10411,7 @@ bool Expr::EvaluateAsInitializer(APValue &Value, const ASTContext &Ctx,
 bool Expr::isEvaluatable(const ASTContext &Ctx, SideEffectsKind SEK) const {
   EvalResult Result;
   return EvaluateAsRValue(Result, Ctx) &&
-         !hasUnacceptableSideEffect(Result, SEK);
+         !Result.hasUnacceptableSideEffect(SEK);
 }
 
 APSInt Expr::EvaluateKnownConstInt(const ASTContext &Ctx,
index 7b076ea3e65eaac074686507cbe10d4f6cc59b26..ae05df1be58b5e48187d5b674b3eefbbafe1417c 100644 (file)
@@ -1392,20 +1392,40 @@ static QualType getNonMemoryType(CodeGenModule &CGM, QualType type) {
   return type;
 }
 
+/// Checks if the specified initializer is equivalent to zero initialization.
+static bool isZeroInitializer(ConstantEmitter &CE, const Expr *Init) {
+  if (auto *E = dyn_cast_or_null<CXXConstructExpr>(Init)) {
+    CXXConstructorDecl *CD = E->getConstructor();
+    return CD->isDefaultConstructor() && CD->isTrivial();
+  }
+
+  if (auto *IL = dyn_cast_or_null<InitListExpr>(Init)) {
+    for (auto I : IL->inits())
+      if (!isZeroInitializer(CE, I))
+        return false;
+    if (const Expr *Filler = IL->getArrayFiller())
+      return isZeroInitializer(CE, Filler);
+    return true;
+  }
+
+  QualType InitTy = Init->getType();
+  if (InitTy->isIntegralOrEnumerationType() || InitTy->isPointerType()) {
+    Expr::EvalResult Result;
+    if (Init->EvaluateAsRValue(Result, CE.CGM.getContext()) &&
+        !Result.hasUnacceptableSideEffect(Expr::SE_NoSideEffects))
+      return (Result.Val.isInt() && Result.Val.getInt().isNullValue()) ||
+             (Result.Val.isLValue() && Result.Val.isNullPointer());
+  }
+
+  return false;
+}
+
 llvm::Constant *ConstantEmitter::tryEmitPrivateForVarInit(const VarDecl &D) {
   // Make a quick check if variable can be default NULL initialized
   // and avoid going through rest of code which may do, for c++11,
   // initialization of memory to all NULLs.
-  if (!D.hasLocalStorage()) {
-    QualType Ty = CGM.getContext().getBaseElementType(D.getType());
-    if (Ty->isRecordType())
-      if (const CXXConstructExpr *E =
-          dyn_cast_or_null<CXXConstructExpr>(D.getInit())) {
-        const CXXConstructorDecl *CD = E->getConstructor();
-        if (CD->isTrivial() && CD->isDefaultConstructor())
-          return CGM.EmitNullConstant(D.getType());
-      }
-  }
+  if (!D.hasLocalStorage() && isZeroInitializer(*this, D.getInit()))
+    return CGM.EmitNullConstant(D.getType());
 
   QualType destType = D.getType();
 
index 3fd231b630ee7d1286770544c79c4599b674bcef..2d6999540317ee22a9a9f4a5e3d1c1a80b457985 100644 (file)
@@ -167,7 +167,7 @@ void g30() {
     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()
 }
 
index 74532c8fa5b438c3c618a87b5c8ac11e0206f423..21077b4c1474e28a2cd8e9ca9f8186ba1128d058 100644 (file)
@@ -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 %union.anon.0 zeroinitializer
 union { int i; double f; } u2 = { };
 
 // CHECK: @u3 = global  %union.anon.1 zeroinitializer
index bf20696a22c1ee79c6e38fbca694ff5ec77d88b4..0b68c211122c3217955f19aaa6f91b44fdf3e04b 100644 (file)
@@ -5,7 +5,7 @@
 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 %union.z zeroinitializer
 union z {
   char a[3];
   long long b;
index 8bf35966f5b99e2d9a86b744ffb972f4f6189468..0fb247a31c7b7227a91b8ce037f7313cd3d9c4d6 100644 (file)
@@ -51,3 +51,30 @@ namespace NonTrivialInit {
   // meaningful.
   B b[30] = {};
 }
+
+namespace ZeroInit {
+  enum { Zero, One };
+  constexpr int zero() { return 0; }
+  constexpr int *null() { return nullptr; }
+  struct Filler {
+    int x;
+    Filler();
+  };
+  struct S1 {
+    int x;
+  };
+
+  // These declarations, if implemented elementwise, require huge
+  // amout of memory and compiler time.
+  unsigned char data_1[1024 * 1024 * 1024 * 2u] = { 0 };
+  unsigned char data_2[1024 * 1024 * 1024 * 2u] = { Zero };
+  unsigned char data_3[1024][1024][1024] = {{{0}}};
+  unsigned char data_4[1024 * 1024 * 1024 * 2u] = { zero() };
+  int *data_5[1024 * 1024 * 512] = { nullptr };
+  int *data_6[1024 * 1024 * 512] = { null() };
+  struct S1 data_7[1024 * 1024 * 512] = {{0}};
+
+  // This variable must be initialized elementwise.
+  Filler data_e1[1024] = {};
+  // CHECK: getelementptr inbounds {{.*}} @_ZN8ZeroInit7data_e1E
+}
index f59ec51136fefb1c87d87d4a21870f1af1bcb835..ab1c6383bd31266d2d5e551a594430fe4d9dfc62 100644 (file)
@@ -17,14 +17,14 @@ namespace Constant {
 
   C c1 = {};
   C c2 = {1};
-  // CHECK: @_ZN8Constant2c1E = global { i8 } zeroinitializer, align 1
+  // CHECK: @_ZN8Constant2c1E = global %"struct.Constant::C" zeroinitializer, align 1
   // CHECK: @_ZN8Constant2c2E = global { i8 } { i8 1 }, align 1
 
   // Test packing bases into tail padding.
   D d1 = {};
   D d2 = {1, 2, 3};
   D d3 = {1};
-  // CHECK: @_ZN8Constant2d1E = global { i32, i8, i8 } zeroinitializer, align 4
+  // CHECK: @_ZN8Constant2d1E = global %"struct.Constant::D" zeroinitializer, align 4
   // CHECK: @_ZN8Constant2d2E = global { i32, i8, i8 } { i32 1, i8 2, i8 3 }, align 4
   // CHECK: @_ZN8Constant2d3E = global { i32, i8, i8 } { i32 1, i8 0, i8 0 }, align 4
 
diff --git a/test/SemaCXX/large-array-init.cpp b/test/SemaCXX/large-array-init.cpp
deleted file mode 100644 (file)
index ba73428..0000000
+++ /dev/null
@@ -1,10 +0,0 @@
-// RUN: %clang_cc1 -S -o %t.ll -mllvm -debug-only=exprconstant %s 2>&1 | \
-// RUN:     FileCheck %s
-// REQUIRES: asserts
-
-struct S { int i; };
-
-static struct S arr[100000000] = {{ 0 }};
-// CHECK: The number of elements to initialize: 1.
-
-struct S *foo() { return arr; }