Index: lib/CodeGen/CodeGenModule.h =================================================================== --- lib/CodeGen/CodeGenModule.h +++ lib/CodeGen/CodeGenModule.h @@ -390,6 +390,7 @@ bool isTriviallyRecursive(const FunctionDecl *F); bool shouldEmitFunction(GlobalDecl GD); + bool shouldEmitGlobalVariable(const VarDecl *D); /// @name Cache for Blocks Runtime Globals /// @{ Index: lib/CodeGen/CodeGenModule.cpp =================================================================== --- lib/CodeGen/CodeGenModule.cpp +++ lib/CodeGen/CodeGenModule.cpp @@ -1275,6 +1275,16 @@ return !isTriviallyRecursive(F); } +bool CodeGenModule::shouldEmitGlobalVariable(const VarDecl *VD) { + if (GetLLVMLinkageVarDefinition(VD, /*isConstant=*/true) == + llvm::Function::AvailableExternallyLinkage) { + assert(isTypeConstant(VD->getType(), /*ExcludeCtor=*/true) && + "available_externally variable should be constant!"); + return CodeGenOpts.OptimizationLevel != 0; + } + return true; +} + /// If the type for the method's class was generated by /// CGDebugInfo::createContextChain(), the cache contains only a /// limited DIType without any declarations. Since EmitFunctionStart() @@ -1325,8 +1335,13 @@ return EmitGlobalFunctionDefinition(GD); } - if (const VarDecl *VD = dyn_cast(D)) + if (const VarDecl *VD = dyn_cast(D)) { + // At -O0, don't generate IR for vars with available_externally linkage. + if (!shouldEmitGlobalVariable(VD)) + return; + return EmitGlobalVarDefinition(VD); + } llvm_unreachable("Invalid argument to EmitGlobalDefinition()"); } @@ -1569,6 +1584,16 @@ CXXThreadLocals.push_back(std::make_pair(D, GV)); setTLSMode(GV, *D); } + + // If the variable has an initializer but no definition we can emit it with + // available_externally linkage. + if (D->hasInit() && !D->hasDefinition(Context)) { + assert(!D->getType().isVolatileQualified()); + assert(GetLLVMLinkageVarDefinition(D, /*isConstant=*/true) == + llvm::GlobalValue::AvailableExternallyLinkage && + "Global variable should be available_externally!"); + DeferredDeclsToEmit.push_back(D); + } } if (AddrSpace != Ty->getAddressSpace()) @@ -1851,6 +1876,16 @@ llvm::GlobalValue::LinkageTypes CodeGenModule::GetLLVMLinkageVarDefinition(const VarDecl *D, bool isConstant) { + // Check for definitionless vars. We can emit them with available_externally + // linkage. We check this before calling GetGVALinkageForVariable because it + // can happen for explicit template instantiations that would cause an + // assertion failure otherwise. + if (D->hasInit() && !D->hasDefinition(Context)) { + assert(isTypeConstant(D->getType(), /*ExcludeCtor=*/true) && + "Variable with init but no definition should be constant!"); + return llvm::GlobalValue::AvailableExternallyLinkage; + } + GVALinkage Linkage = getContext().GetGVALinkageForVariable(D); if (Linkage == GVA_Internal) return llvm::Function::InternalLinkage; Index: test/CodeGenCXX/constexpr-available-externally.cpp =================================================================== --- /dev/null +++ test/CodeGenCXX/constexpr-available-externally.cpp @@ -0,0 +1,50 @@ +// RUN: %clang_cc1 -emit-llvm -O1 -std=c++11 -disable-llvm-optzns -o - %s | FileCheck %s + +// PR17612 +// CHECK: @_ZN6Object3fooE = available_externally constant %struct.Literal { i32 1 } +// CHECK: @_ZN6Object3barE = available_externally constant i32 42 +// CHECK: @_ZN3tplIiE4nposE = available_externally constant i32 -1 +// CHECK: @_ZN6Holder11threadlocalE = available_externally thread_local constant i32 1 + +struct Literal { + int i; + constexpr Literal(int i) : i(i) {} +}; + +struct Object { + static constexpr Literal foo = Literal(1); + static const int bar = 42; +}; + +void bar(const int &); + +int f() { + Literal l = Object::foo; + bar(Object::bar); + return l.i; + +// CHECK-LABEL: @_Z1fv +// CHECK: call void @llvm.memcpy{{[^(]*}}(i8* %{{[^,]*}}, i8* bitcast (%struct.Literal* @_ZN6Object3fooE to i8*) +// CHECK: call void @_Z3barRKi(i32* @_ZN6Object3barE) +} + +template +struct tpl { + static const T npos = -1; +}; + +template struct tpl; + +void g() { + bar(tpl::npos); +// CHECK-LABEL: @_Z1gv +// CHECK: call void @_Z3barRKi(i32* @_ZN3tplIiE4nposE) +} + +constexpr int constone() { return 1; } +struct Holder { + static const int thread_local threadlocal = constone(); +}; +const int *h() { + return &Holder::threadlocal; +}