- Use getOrCreate* instead of GetAddr* and Create*
  - Emit constant initializers of local statics

http://reviews.llvm.org/D4787

Files:
  lib/CodeGen/CGDecl.cpp
  lib/CodeGen/CGExpr.cpp
  lib/CodeGen/CGExprConstant.cpp
  lib/CodeGen/CodeGenFunction.h
  lib/CodeGen/CodeGenModule.h
  test/CodeGenCXX/static-local-in-local-class.cpp
  test/CodeGenObjC/local-static-block.m
Index: lib/CodeGen/CGDecl.cpp
===================================================================
--- lib/CodeGen/CGDecl.cpp
+++ lib/CodeGen/CGDecl.cpp
@@ -146,74 +146,134 @@
   return EmitAutoVarDecl(D);
 }
 
-static std::string GetStaticDeclName(CodeGenFunction &CGF, const VarDecl &D) {
-  CodeGenModule &CGM = CGF.CGM;
-
-  if (CGF.getLangOpts().CPlusPlus)
+static std::string getStaticDeclName(CodeGenModule &CGM, const VarDecl &D) {
+  if (CGM.getLangOpts().CPlusPlus)
     return CGM.getMangledName(&D).str();
 
-  StringRef ContextName;
-  if (!CGF.CurFuncDecl) {
-    // Better be in a block declared in global scope.
-    const NamedDecl *ND = cast<NamedDecl>(&D);
-    const DeclContext *DC = ND->getDeclContext();
-    if (const BlockDecl *BD = dyn_cast<BlockDecl>(DC))
-      ContextName = CGM.getBlockMangledName(GlobalDecl(), BD);
-    else
-      llvm_unreachable("Unknown context for block static var decl");
-  } else if (const FunctionDecl *FD = dyn_cast<FunctionDecl>(CGF.CurFuncDecl))
+  // If this isn't C++, we don't need a mangled name, just a pretty one.
+  assert(!D.isExternallyVisible() && "name shouldn't matter");
+  std::string ContextName;
+  const DeclContext *DC = D.getDeclContext();
+  if (const auto *FD = dyn_cast<FunctionDecl>(DC))
     ContextName = CGM.getMangledName(FD);
-  else if (isa<ObjCMethodDecl>(CGF.CurFuncDecl))
-    ContextName = CGF.CurFn->getName();
+  else if (const auto *BD = dyn_cast<BlockDecl>(DC))
+    ContextName = CGM.getBlockMangledName(GlobalDecl(), BD);
+  else if (const auto *OMD = dyn_cast<ObjCMethodDecl>(DC))
+    ContextName = OMD->getSelector().getAsString();
   else
     llvm_unreachable("Unknown context for static var decl");
 
-  return ContextName.str() + "." + D.getNameAsString();
+  ContextName += "." + D.getNameAsString();
+  return ContextName;
+}
+
+static llvm::Constant *adjustAddrSpaceAndUpdateMap(CodeGenModule &CGM,
+                                                   const VarDecl &D,
+                                                   unsigned AddrSpace,
+                                                   unsigned ExpectedAddrSpace,
+                                                   llvm::Constant *Addr) {
+  // Insert an address space cast if we're placing this global into an address
+  // space other than the one expected by the type system.
+  if (AddrSpace != ExpectedAddrSpace) {
+    auto *OldPTy = cast<llvm::PointerType>(Addr->getType());
+    auto *PTy =
+        llvm::PointerType::get(OldPTy->getElementType(), ExpectedAddrSpace);
+    Addr = llvm::ConstantExpr::getAddrSpaceCast(Addr, PTy);
+  }
+
+  // Update the static local decl map before we try to emit the initializer in
+  // case there is a circular reference to this decl.
+  CGM.setStaticLocalDeclAddress(&D, Addr);
+
+  return Addr;
 }
 
-llvm::Constant *
-CodeGenFunction::CreateStaticVarDecl(const VarDecl &D,
-                                     llvm::GlobalValue::LinkageTypes Linkage) {
+llvm::Constant *CodeGenModule::getOrCreateStaticVarDecl(
+    const VarDecl &D, llvm::GlobalValue::LinkageTypes Linkage) {
+  // In general, we don't always emit static var decls once before we reference
+  // them. It is possible to reference them before emitting the function that
+  // contains them, and it is possible to emit the containing function multiple
+  // times.
+  if (llvm::Constant *ExistingGV = StaticLocalDeclMap[&D])
+    return ExistingGV;
+
   QualType Ty = D.getType();
   assert(Ty->isConstantSizeType() && "VLAs can't be static");
 
   // Use the label if the variable is renamed with the asm-label extension.
   std::string Name;
   if (D.hasAttr<AsmLabelAttr>())
-    Name = CGM.getMangledName(&D);
+    Name = getMangledName(&D);
   else
-    Name = GetStaticDeclName(*this, D);
+    Name = getStaticDeclName(*this, D);
 
-  llvm::Type *LTy = CGM.getTypes().ConvertTypeForMem(Ty);
-  unsigned AddrSpace =
-   CGM.GetGlobalVarAddressSpace(&D, CGM.getContext().getTargetAddressSpace(Ty));
+  llvm::Type *LTy = getTypes().ConvertTypeForMem(Ty);
+  unsigned ExpectedAddrSpace = getContext().getTargetAddressSpace(Ty);
+  unsigned AddrSpace = GetGlobalVarAddressSpace(&D, ExpectedAddrSpace);
   llvm::GlobalVariable *GV =
-    new llvm::GlobalVariable(CGM.getModule(), LTy,
-                             Ty.isConstant(getContext()), Linkage,
-                             CGM.EmitNullConstant(D.getType()), Name, nullptr,
-                             llvm::GlobalVariable::NotThreadLocal,
-                             AddrSpace);
+      new llvm::GlobalVariable(getModule(), LTy, Ty.isConstant(getContext()),
+                               Linkage, EmitNullConstant(Ty), Name, nullptr,
+                               llvm::GlobalVariable::NotThreadLocal, AddrSpace);
   GV->setAlignment(getContext().getDeclAlign(&D).getQuantity());
-  CGM.setGlobalVisibility(GV, &D);
+  setGlobalVisibility(GV, &D);
 
   if (D.getTLSKind())
-    CGM.setTLSMode(GV, D);
+    setTLSMode(GV, D);
 
   if (D.isExternallyVisible()) {
     if (D.hasAttr<DLLImportAttr>())
       GV->setDLLStorageClass(llvm::GlobalVariable::DLLImportStorageClass);
     else if (D.hasAttr<DLLExportAttr>())
       GV->setDLLStorageClass(llvm::GlobalVariable::DLLExportStorageClass);
   }
 
-  // Make sure the result is of the correct type.
-  unsigned ExpectedAddrSpace = CGM.getContext().getTargetAddressSpace(Ty);
-  if (AddrSpace != ExpectedAddrSpace) {
-    llvm::PointerType *PTy = llvm::PointerType::get(LTy, ExpectedAddrSpace);
-    return llvm::ConstantExpr::getAddrSpaceCast(GV, PTy);
+  llvm::Constant *Addr =
+      adjustAddrSpaceAndUpdateMap(*this, D, AddrSpace, ExpectedAddrSpace, GV);
+
+  // If there is an initializer, try to emit it as a constant. If it's not
+  // constant, leave the zero initializer so that var decl emission can
+  // overwrite it at runtime.
+  llvm::Constant *Init = nullptr;
+  if (D.getInit())
+    Init = EmitConstantInit(D);
+
+  if (Init) {
+    // The initializer may differ in type from the global. Rewrite
+    // the global to match the initializer.  (We have to do this
+    // because some types, like unions, can't be completely represented
+    // in the LLVM type system.)
+    if (GV->getType()->getElementType() != Init->getType()) {
+      llvm::GlobalVariable *OldGV = GV;
+
+      GV = new llvm::GlobalVariable(
+          getModule(), Init->getType(), OldGV->isConstant(),
+          OldGV->getLinkage(), Init, "",
+          /*InsertBefore*/ OldGV, OldGV->getThreadLocalMode(),
+          ExpectedAddrSpace);
+      GV->copyAttributesFrom(OldGV);
+
+      // Steal the name of the old global
+      GV->takeName(OldGV);
+
+      // Replace all uses of the old global with the new global
+      llvm::Constant *NewPtrForOldDecl =
+          llvm::ConstantExpr::getBitCast(GV, OldGV->getType());
+      OldGV->replaceAllUsesWith(NewPtrForOldDecl);
+
+      // We also have to change the value in the map and redo the address space
+      // casting, now that we've replaced the value.
+      Addr = adjustAddrSpaceAndUpdateMap(*this, D, AddrSpace, ExpectedAddrSpace,
+                                         NewPtrForOldDecl);
+
+      // Erase the old global, since it is no longer used.
+      OldGV->eraseFromParent();
+    }
+
+    GV->setConstant(isTypeConstant(Ty, true));
+    GV->setInitializer(Init);
   }
 
-  return GV;
+  return Addr;
 }
 
 /// hasNontrivialDestruction - Determine whether a type's destruction is
@@ -224,13 +284,9 @@
   return RD && !RD->hasTrivialDestructor();
 }
 
-/// AddInitializerToStaticVarDecl - Add the initializer for 'D' to the
-/// global variable that has already been created for it.  If the initializer
-/// has a different type than GV does, this may free GV and return a different
-/// one.  Otherwise it just returns GV.
-llvm::GlobalVariable *
-CodeGenFunction::AddInitializerToStaticVarDecl(const VarDecl &D,
-                                               llvm::GlobalVariable *GV) {
+void
+CodeGenFunction::EmitInitializerForStaticVarDecl(const VarDecl &D,
+                                                 llvm::GlobalVariable *GV) {
   llvm::Constant *Init = CGM.EmitConstantInit(D, this);
 
   // If constant emission failed, then this should be a C++ static
@@ -245,62 +301,32 @@
 
       EmitCXXGuardedInit(D, GV, /*PerformInit*/true);
     }
-    return GV;
-  }
-
-  // The initializer may differ in type from the global. Rewrite
-  // the global to match the initializer.  (We have to do this
-  // because some types, like unions, can't be completely represented
-  // in the LLVM type system.)
-  if (GV->getType()->getElementType() != Init->getType()) {
-    llvm::GlobalVariable *OldGV = GV;
-
-    GV = new llvm::GlobalVariable(CGM.getModule(), Init->getType(),
-                                  OldGV->isConstant(),
-                                  OldGV->getLinkage(), Init, "",
-                                  /*InsertBefore*/ OldGV,
-                                  OldGV->getThreadLocalMode(),
-                           CGM.getContext().getTargetAddressSpace(D.getType()));
-    GV->setVisibility(OldGV->getVisibility());
-
-    // Steal the name of the old global
-    GV->takeName(OldGV);
-
-    // Replace all uses of the old global with the new global
-    llvm::Constant *NewPtrForOldDecl =
-    llvm::ConstantExpr::getBitCast(GV, OldGV->getType());
-    OldGV->replaceAllUsesWith(NewPtrForOldDecl);
-
-    // Erase the old global, since it is no longer used.
-    OldGV->eraseFromParent();
+    return;
   }
 
-  GV->setConstant(CGM.isTypeConstant(D.getType(), true));
-  GV->setInitializer(Init);
+  // Constant evaluation might fail when referencing the static local from some
+  // other scope and succeed when we enter the context of the function. If so,
+  // update the initializer.
+  assert(GV->getType()->getElementType() == Init->getType() &&
+         "static var initializer has incorrect type");
+  if (GV->getInitializer() != Init)
+    GV->setInitializer(Init);
 
   if (hasNontrivialDestruction(D.getType())) {
     // We have a constant initializer, but a nontrivial destructor. We still
     // need to perform a guarded "initialization" in order to register the
     // destructor.
     EmitCXXGuardedInit(D, GV, /*PerformInit*/false);
   }
-
-  return GV;
 }
 
-void CodeGenFunction::EmitStaticVarDecl(const VarDecl &D,
-                                      llvm::GlobalValue::LinkageTypes Linkage) {
+void
+CodeGenFunction::EmitStaticVarDecl(const VarDecl &D,
+                                   llvm::GlobalValue::LinkageTypes Linkage) {
   llvm::Value *&DMEntry = LocalDeclMap[&D];
   assert(!DMEntry && "Decl already exists in localdeclmap!");
 
-  // Check to see if we already have a global variable for this
-  // declaration.  This can happen when double-emitting function
-  // bodies, e.g. with complete and base constructors.
-  llvm::Constant *addr =
-    CGM.getStaticLocalDeclAddress(&D);
-
-  if (!addr)
-    addr = CreateStaticVarDecl(D, Linkage);
+  llvm::Constant *addr = CGM.getOrCreateStaticVarDecl(D, Linkage);
 
   // Store into LocalDeclMap before generating initializer to handle
   // circular references.
@@ -320,7 +346,7 @@
     cast<llvm::GlobalVariable>(addr->stripPointerCasts());
   // If this value has an initializer, emit it.
   if (D.getInit())
-    var = AddInitializerToStaticVarDecl(D, var);
+    EmitInitializerForStaticVarDecl(D, var);
 
   var->setAlignment(getContext().getDeclAlign(&D).getQuantity());
 
@@ -1127,7 +1153,7 @@
   } else {
     // Otherwise, create a temporary global with the initializer then
     // memcpy from the global to the alloca.
-    std::string Name = GetStaticDeclName(*this, D);
+    std::string Name = getStaticDeclName(CGM, D);
     llvm::GlobalVariable *GV =
       new llvm::GlobalVariable(CGM.getModule(), constant->getType(), true,
                                llvm::GlobalValue::PrivateLinkage,
Index: lib/CodeGen/CGExpr.cpp
===================================================================
--- lib/CodeGen/CGExpr.cpp
+++ lib/CodeGen/CGExpr.cpp
@@ -1924,7 +1924,8 @@
 
     llvm::Value *V = LocalDeclMap.lookup(VD);
     if (!V && VD->isStaticLocal())
-      V = CGM.getStaticLocalDeclAddress(VD);
+      V = CGM.getOrCreateStaticVarDecl(
+          *VD, CGM.getLLVMLinkageVarDefinition(VD, /*isConstant=*/false));
 
     // Use special handling for lambdas.
     if (!V) {
Index: lib/CodeGen/CGExprConstant.cpp
===================================================================
--- lib/CodeGen/CGExprConstant.cpp
+++ lib/CodeGen/CGExprConstant.cpp
@@ -868,8 +868,10 @@
         if (!VD->hasLocalStorage()) {
           if (VD->isFileVarDecl() || VD->hasExternalStorage())
             return CGM.GetAddrOfGlobalVar(VD);
-          else if (VD->isLocalVarDecl())
-            return CGM.getStaticLocalDeclAddress(VD);
+          else if (VD->isLocalVarDecl()) {
+            return CGM.getOrCreateStaticVarDecl(
+                *VD, CGM.getLLVMLinkageVarDefinition(VD, /*isConstant=*/false));
+          }
         }
       }
       return nullptr;
@@ -916,7 +918,8 @@
       return CGM.GetAddrOfConstantCString("", ".tmp");
     }
     case Expr::AddrLabelExprClass: {
-      assert(CGF && "Invalid address of label expression outside function.");
+      if (!CGF)
+        return llvm::Constant::getNullValue(ConvertType(E->getType()));
       llvm::Constant *Ptr =
         CGF->GetAddrOfLabel(cast<AddrLabelExpr>(E)->getLabel());
       return llvm::ConstantExpr::getBitCast(Ptr, ConvertType(E->getType()));
Index: lib/CodeGen/CodeGenFunction.h
===================================================================
--- lib/CodeGen/CodeGenFunction.h
+++ lib/CodeGen/CodeGenFunction.h
@@ -2426,19 +2426,10 @@
   /// EmitLoadOfComplex - Load a complex number from the specified l-value.
   ComplexPairTy EmitLoadOfComplex(LValue src, SourceLocation loc);
 
-  /// CreateStaticVarDecl - Create a zero-initialized LLVM global for
-  /// a static local variable.
-  llvm::Constant *CreateStaticVarDecl(const VarDecl &D,
-                                      llvm::GlobalValue::LinkageTypes Linkage);
-
-  /// AddInitializerToStaticVarDecl - Add the initializer for 'D' to the
-  /// global variable that has already been created for it.  If the initializer
-  /// has a different type than GV does, this may free GV and return a different
-  /// one.  Otherwise it just returns GV.
-  llvm::GlobalVariable *
-  AddInitializerToStaticVarDecl(const VarDecl &D,
-                                llvm::GlobalVariable *GV);
-
+  /// EmitInitializerForStaticVarDecl - Emits an initializer for 'D' into the
+  /// global variable that has already been created for it.
+  void EmitInitializerForStaticVarDecl(const VarDecl &D,
+                                       llvm::GlobalVariable *GV);
 
   /// EmitCXXGlobalVarDeclInit - Create the initializer for a C++
   /// variable with global storage.
Index: lib/CodeGen/CodeGenModule.h
===================================================================
--- lib/CodeGen/CodeGenModule.h
+++ lib/CodeGen/CodeGenModule.h
@@ -540,9 +540,13 @@
     return CoverageMapping.get();
   }
 
-  llvm::Constant *getStaticLocalDeclAddress(const VarDecl *D) {
-    return StaticLocalDeclMap[D];
-  }
+  /// CreateStaticVarDecl - Get or create an LLVM global for a static local
+  /// variable. Constant variables will be initialized, and non-constant
+  /// variables will be zero-initialized.
+  llvm::Constant *
+  getOrCreateStaticVarDecl(const VarDecl &D,
+                           llvm::GlobalValue::LinkageTypes Linkage);
+
   void setStaticLocalDeclAddress(const VarDecl *D, 
                                  llvm::Constant *C) {
     StaticLocalDeclMap[D] = C;
Index: test/CodeGenCXX/static-local-in-local-class.cpp
===================================================================
--- test/CodeGenCXX/static-local-in-local-class.cpp
+++ test/CodeGenCXX/static-local-in-local-class.cpp
@@ -1,6 +1,12 @@
-// RUN: %clang_cc1 -emit-llvm -o %t %s
-// PR6769
+// RUN: %clang_cc1 -triple x86_64-linux -emit-llvm -o - %s -std=c++1y | FileCheck %s
 
+
+// CHECK: @_ZZL14deduced_returnvE1n = internal global i32 0
+// CHECK: @"_ZZZNK17pr18020_constexpr3$_1clEvENKUlvE_clEvE2l2" =
+// CHECK: internal global i32* @"_ZZNK17pr18020_constexpr3$_1clEvE2l1"
+
+
+namespace pr6769 {
 struct X {
   static void f();
 };
@@ -19,8 +25,9 @@
   }
   (void)i;
 }
+}
 
-// pr7101
+namespace pr7101 {
 void foo() {
     static int n = 0;
     struct Helper {
@@ -30,4 +37,77 @@
     };
     Helper::Execute();
 }
+}
+
+// These tests all break the assumption that the static var decl has to be
+// emitted before use of the var decl.  This happens because we defer emission
+// of variables with internal linkage and no initialization side effects, such
+// as 'x'.  Then we hit operator()() in 'f', and emit the callee before we emit
+// the arguments, so we emit the innermost function first.
+
+namespace pr18020_lambda {
+// Referring to l1 before emitting it used to crash.
+auto x = []() {
+  static int l1 = 0;
+  return [] { return l1; };
+};
+int f() { return x()(); }
+}
+
+// CHECK-LABEL: define internal i32 @"_ZZNK14pr18020_lambda3$_0clEvENKUlvE_clEv"
+// CHECK: load i32* @"_ZZNK14pr18020_lambda3$_0clEvE2l1"
+
+namespace pr18020_constexpr {
+// Taking the address of l1 in a constant expression used to crash.
+auto x = []() {
+  static int l1 = 0;
+  return [] {
+    static int *l2 = &l1;
+    return *l2;
+  };
+};
+int f() { return x()(); }
+}
+
+// CHECK-LABEL: define internal i32 @"_ZZNK17pr18020_constexpr3$_1clEvENKUlvE_clEv"
+// CHECK: load i32** @"_ZZZNK17pr18020_constexpr3$_1clEvENKUlvE_clEvE2l2"
+
+// Lambda-less reduction that references l1 before emitting it.  This didn't
+// crash if you put it in a namespace.
+struct pr18020_class {
+  auto operator()() {
+    static int l1 = 0;
+    struct U {
+      int operator()() { return l1; }
+    };
+    return U();
+  }
+};
+static pr18020_class x;
+int pr18020_f() { return x()(); }
+
+// CHECK-LABEL: define linkonce_odr i32 @_ZZN13pr18020_classclEvEN1UclEv
+// CHECK: load i32* @_ZZN13pr18020_classclEvE2l1
+
+// In this test case, the function containing the static local will not be
+// emitted because it is unneeded. However, the operator call of the inner class
+// is called, and the static local is referenced and must be emitted.
+static auto deduced_return() {
+  static int n;
+  struct S { int &operator()() { return n; } };
+  return S();
+}
+
+extern "C" int call_deduced_return_operator() {
+  return decltype(deduced_return())()();
+}
+
+// CHECK-LABEL: define i32 @call_deduced_return_operator()
+// CHECK: call dereferenceable(4) i32* @_ZZL14deduced_returnvEN1SclEv(%struct.S* %tmp)
+// CHECK: load i32* %
+// CHECK: ret i32 %
+
+// CHECK-LABEL: define internal dereferenceable(4) i32* @_ZZL14deduced_returnvEN1SclEv(%struct.S* %this)
+// CHECK: ret i32* @_ZZL14deduced_returnvE1n
 
+// FIXME: Surely we can exercise this edge case in C with -fblocks.
Index: test/CodeGenObjC/local-static-block.m
===================================================================
--- test/CodeGenObjC/local-static-block.m
+++ test/CodeGenObjC/local-static-block.m
@@ -53,5 +53,5 @@
 }
 // CHECK-LP64: @ArrayRecurs = internal global
 // CHECK-LP64: @FUNC.ArrayRecurs = internal global
-// CHECK-LP64: @FUNC.ArrayRecurs3 = internal global
+// CHECK-LP64: @FUNC.ArrayRecurs6 = internal global
 // CHECK-LP64: @FUNC1.ArrayRecurs = internal global
_______________________________________________
cfe-commits mailing list
[email protected]
http://lists.cs.uiuc.edu/mailman/listinfo/cfe-commits

Reply via email to