From 54e3ba5ace52bcdc0d20d69f715c202a31d8c525 Mon Sep 17 00:00:00 2001 From: David Majnemer Date: Wed, 2 Apr 2014 23:17:29 +0000 Subject: [PATCH] CodeGen: Emit some functions as weak_odr under -fms-compatibility Summary: MSVC always emits inline functions marked with the extern storage class specifier. The result is something similar to the opposite of __attribute__((gnu_inline)). This extension is also available in C. This fixes PR19264. Reviewers: rnk, rsmith CC: cfe-commits Differential Revision: http://llvm-reviews.chandlerc.com/D3207 llvm-svn: 205485 --- clang/include/clang/AST/ASTContext.h | 2 +- clang/include/clang/AST/Decl.h | 2 + clang/include/clang/Basic/Linkage.h | 2 +- clang/lib/AST/ASTContext.cpp | 12 +++-- clang/lib/AST/Decl.cpp | 40 +++++++++++++++- clang/lib/CodeGen/CodeGenModule.cpp | 5 +- clang/test/CodeGen/inline.c | 5 +- clang/test/CodeGenCXX/inline-functions.cpp | 56 +++++++++++++++++++++- 8 files changed, 112 insertions(+), 12 deletions(-) diff --git a/clang/include/clang/AST/ASTContext.h b/clang/include/clang/AST/ASTContext.h index 82a022ade51d..ca1a9067c52f 100644 --- a/clang/include/clang/AST/ASTContext.h +++ b/clang/include/clang/AST/ASTContext.h @@ -2156,7 +2156,7 @@ public: /// when it is called. void AddDeallocation(void (*Callback)(void*), void *Data); - GVALinkage GetGVALinkageForFunction(const FunctionDecl *FD); + GVALinkage GetGVALinkageForFunction(const FunctionDecl *FD) const; GVALinkage GetGVALinkageForVariable(const VarDecl *VD); /// \brief Determines if the decl can be CodeGen'ed or deserialized from PCH diff --git a/clang/include/clang/AST/Decl.h b/clang/include/clang/AST/Decl.h index ceb8f2d2078f..526187e31bd0 100644 --- a/clang/include/clang/AST/Decl.h +++ b/clang/include/clang/AST/Decl.h @@ -1930,6 +1930,8 @@ public: bool isInlineDefinitionExternallyVisible() const; + bool isMSExternInline() const; + bool doesDeclarationForceExternallyVisibleDefinition() const; /// isOverloadedOperator - Whether this function declaration diff --git a/clang/include/clang/Basic/Linkage.h b/clang/include/clang/Basic/Linkage.h index 699620784ecd..247c6e714f47 100644 --- a/clang/include/clang/Basic/Linkage.h +++ b/clang/include/clang/Basic/Linkage.h @@ -63,7 +63,7 @@ enum GVALinkage { GVA_CXXInline, GVA_StrongExternal, GVA_TemplateInstantiation, - GVA_ExplicitTemplateInstantiation + GVA_StrongODR }; inline bool isExternallyVisible(Linkage L) { diff --git a/clang/lib/AST/ASTContext.cpp b/clang/lib/AST/ASTContext.cpp index 5aa4beeb292b..3ed2a9ff1cdc 100644 --- a/clang/lib/AST/ASTContext.cpp +++ b/clang/lib/AST/ASTContext.cpp @@ -7732,7 +7732,7 @@ QualType ASTContext::GetBuiltinType(unsigned Id, return getFunctionType(ResType, ArgTypes, EPI); } -GVALinkage ASTContext::GetGVALinkageForFunction(const FunctionDecl *FD) { +GVALinkage ASTContext::GetGVALinkageForFunction(const FunctionDecl *FD) const { if (!FD->isExternallyVisible()) return GVA_Internal; @@ -7744,7 +7744,7 @@ GVALinkage ASTContext::GetGVALinkageForFunction(const FunctionDecl *FD) { break; case TSK_ExplicitInstantiationDefinition: - return GVA_ExplicitTemplateInstantiation; + return GVA_StrongODR; case TSK_ExplicitInstantiationDeclaration: case TSK_ImplicitInstantiation: @@ -7776,6 +7776,12 @@ GVALinkage ASTContext::GetGVALinkageForFunction(const FunctionDecl *FD) { == TSK_ExplicitInstantiationDeclaration) return GVA_C99Inline; + // Functions specified with extern and inline in -fms-compatibility mode + // forcibly get emitted. While the body of the function cannot be later + // replaced, the function definition cannot be discarded. + if (FD->getMostRecentDecl()->isMSExternInline()) + return GVA_StrongODR; + return GVA_CXXInline; } @@ -7793,7 +7799,7 @@ GVALinkage ASTContext::GetGVALinkageForVariable(const VarDecl *VD) { // Fall through to treat this like any other instantiation. case TSK_ExplicitInstantiationDefinition: - return GVA_ExplicitTemplateInstantiation; + return GVA_StrongODR; case TSK_ImplicitInstantiation: return GVA_TemplateInstantiation; diff --git a/clang/lib/AST/Decl.cpp b/clang/lib/AST/Decl.cpp index 83cbb44f7f91..13681ddf5598 100644 --- a/clang/lib/AST/Decl.cpp +++ b/clang/lib/AST/Decl.cpp @@ -2546,6 +2546,37 @@ unsigned FunctionDecl::getMinRequiredArguments() const { return NumRequiredArgs; } +/// \brief The combination of the extern and inline keywords under MSVC forces +/// the function to be required. +/// +/// Note: This function assumes that we will only get called when isInlined() +/// would return true for this FunctionDecl. +bool FunctionDecl::isMSExternInline() const { + assert(isInlined() && "expected to get called on an inlined function!"); + + const ASTContext &Context = getASTContext(); + if (!Context.getLangOpts().MSVCCompat) + return false; + + for (const FunctionDecl *FD = this; FD; FD = FD->getPreviousDecl()) + if (FD->getStorageClass() == SC_Extern) + return true; + + return false; +} + +static bool redeclForcesDefMSVC(const FunctionDecl *Redecl) { + if (Redecl->getStorageClass() != SC_Extern) + return false; + + for (const FunctionDecl *FD = Redecl->getPreviousDecl(); FD; + FD = FD->getPreviousDecl()) + if (FD->getStorageClass() == SC_Extern) + return false; + + return true; +} + static bool RedeclForcesDefC99(const FunctionDecl *Redecl) { // Only consider file-scope declarations in this test. if (!Redecl->getLexicalDeclContext()->isTranslationUnit()) @@ -2565,7 +2596,7 @@ static bool RedeclForcesDefC99(const FunctionDecl *Redecl) { /// \brief For a function declaration in C or C++, determine whether this /// declaration causes the definition to be externally visible. /// -/// Specifically, this determines if adding the current declaration to the set +/// For instance, this determines if adding the current declaration to the set /// of redeclarations of the given functions causes /// isInlineDefinitionExternallyVisible to change from false to true. bool FunctionDecl::doesDeclarationForceExternallyVisibleDefinition() const { @@ -2574,6 +2605,13 @@ bool FunctionDecl::doesDeclarationForceExternallyVisibleDefinition() const { ASTContext &Context = getASTContext(); + if (Context.getLangOpts().MSVCCompat) { + const FunctionDecl *Definition; + if (hasBody(Definition) && Definition->isInlined() && + redeclForcesDefMSVC(this)) + return true; + } + if (Context.getLangOpts().GNUInline || hasAttr()) { // With GNU inlining, a declaration with 'inline' but not 'extern', forces // an externally visible definition. diff --git a/clang/lib/CodeGen/CodeGenModule.cpp b/clang/lib/CodeGen/CodeGenModule.cpp index d997a79f5b23..c26f76911821 100644 --- a/clang/lib/CodeGen/CodeGenModule.cpp +++ b/clang/lib/CodeGen/CodeGenModule.cpp @@ -589,7 +589,7 @@ CodeGenModule::getFunctionLinkage(GlobalDecl GD) { // explicit instantiations can occur in multiple translation units // and must all be equivalent. However, we are not allowed to // throw away these explicit instantiations. - if (Linkage == GVA_ExplicitTemplateInstantiation) + if (Linkage == GVA_StrongODR) return !Context.getLangOpts().AppleKext ? llvm::Function::WeakODRLinkage : llvm::Function::ExternalLinkage; @@ -1948,8 +1948,7 @@ CodeGenModule::GetLLVMLinkageVarDefinition(const VarDecl *D, bool isConstant) { return llvm::GlobalVariable::WeakODRLinkage; else return llvm::GlobalVariable::WeakAnyLinkage; - } else if (Linkage == GVA_TemplateInstantiation || - Linkage == GVA_ExplicitTemplateInstantiation) + } else if (Linkage == GVA_TemplateInstantiation || Linkage == GVA_StrongODR) return llvm::GlobalVariable::WeakODRLinkage; else if (!getLangOpts().CPlusPlus && ((!CodeGenOpts.NoCommon && !D->hasAttr()) || diff --git a/clang/test/CodeGen/inline.c b/clang/test/CodeGen/inline.c index 70ab696b64ee..96c9a86baecb 100644 --- a/clang/test/CodeGen/inline.c +++ b/clang/test/CodeGen/inline.c @@ -53,12 +53,13 @@ // RUN: echo "MS C Mode tests:" // RUN: %clang_cc1 %s -triple i386-unknown-unknown -O1 -disable-llvm-optzns -emit-llvm -o - -std=c99 -fms-compatibility | FileCheck %s --check-prefix=CHECK4 +// CHECK4-LABEL: define weak_odr i32 @ei() // CHECK4-LABEL: define i32 @bar() +// CHECK4-NOT: unreferenced1 +// CHECK4-LABEL: define weak_odr void @unreferenced2() // CHECK4-LABEL: define void @gnu_inline() // CHECK4-LABEL: define available_externally void @gnu_ei_inline() // CHECK4-LABEL: define linkonce_odr i32 @foo() -// CHECK4-NOT: unreferenced -// CHECK4-LABEL: define linkonce_odr i32 @ei() extern __inline int ei() { return 123; } diff --git a/clang/test/CodeGenCXX/inline-functions.cpp b/clang/test/CodeGenCXX/inline-functions.cpp index 7c645148ae6f..622cfa9536f3 100644 --- a/clang/test/CodeGenCXX/inline-functions.cpp +++ b/clang/test/CodeGenCXX/inline-functions.cpp @@ -1,4 +1,5 @@ -// RUN: %clang_cc1 %s -triple=x86_64-apple-darwin10 -emit-llvm -o - | FileCheck %s +// RUN: %clang_cc1 %s -std=c++11 -triple=x86_64-apple-darwin10 -emit-llvm -o - | FileCheck %s --check-prefix=CHECK --check-prefix=NORMAL +// RUN: %clang_cc1 %s -std=c++11 -fms-compatibility -triple=x86_64-apple-darwin10 -emit-llvm -o - | FileCheck %s --check-prefix=CHECK --check-prefix=MSVCCOMPAT // CHECK: ; ModuleID struct A { @@ -67,3 +68,56 @@ namespace test2 { } // CHECK-LABEL: define linkonce_odr void @_ZN5test21fERKNS_1AE } + +// MSVCCOMPAT-LABEL: define weak_odr void @_Z17ExternAndInlineFnv +// NORMAL-NOT: _Z17ExternAndInlineFnv +extern inline void ExternAndInlineFn() {} + +// MSVCCOMPAT-LABEL: define weak_odr void @_Z18InlineThenExternFnv +// NORMAL-NOT: _Z18InlineThenExternFnv +inline void InlineThenExternFn() {} +extern void InlineThenExternFn(); + +// CHECK-LABEL: define void @_Z18ExternThenInlineFnv +extern void ExternThenInlineFn() {} + +// MSVCCOMPAT-LABEL: define weak_odr void @_Z25ExternThenInlineThenDefFnv +// NORMAL-NOT: _Z25ExternThenInlineThenDefFnv +extern void ExternThenInlineThenDefFn(); +inline void ExternThenInlineThenDefFn(); +void ExternThenInlineThenDefFn() {} + +// MSVCCOMPAT-LABEL: define weak_odr void @_Z25InlineThenExternThenDefFnv +// NORMAL-NOT: _Z25InlineThenExternThenDefFnv +inline void InlineThenExternThenDefFn(); +extern void InlineThenExternThenDefFn(); +void InlineThenExternThenDefFn() {} + +// MSVCCOMPAT-LABEL: define weak_odr i32 @_Z20ExternAndConstexprFnv +// NORMAL-NOT: _Z17ExternAndConstexprFnv +extern constexpr int ExternAndConstexprFn() { return 0; } + +// CHECK-NOT: _Z11ConstexprFnv +constexpr int ConstexprFn() { return 0; } + +template +extern inline void ExternInlineOnPrimaryTemplate(T); + +// CHECK-LABEL: define void @_Z29ExternInlineOnPrimaryTemplateIiEvT_ +template <> +void ExternInlineOnPrimaryTemplate(int) {} + +template +extern inline void ExternInlineOnPrimaryTemplateAndSpecialization(T); + +// MSVCCOMPAT-LABEL: define weak_odr void @_Z46ExternInlineOnPrimaryTemplateAndSpecializationIiEvT_ +// NORMAL-NOT: _Z46ExternInlineOnPrimaryTemplateAndSpecializationIiEvT_ +template <> +extern inline void ExternInlineOnPrimaryTemplateAndSpecialization(int) {} + +struct TypeWithInlineMethods { + // CHECK-NOT: _ZN21TypeWithInlineMethods9StaticFunEv + static void StaticFun() {} + // CHECK-NOT: _ZN21TypeWithInlineMethods12NonStaticFunEv + void NonStaticFun() { StaticFun(); } +};