From 17d7d145717cc9eedee5f9650f5188a0bf32a65f Mon Sep 17 00:00:00 2001 From: Stephan Bergmann Date: Wed, 30 Mar 2016 06:27:31 +0000 Subject: [PATCH] For MS ABI, emit dllexport friend functions defined inline in class ...as that is apparently what MSVC does. This is an updated version of r263738, which had to be reverted in r263740 due to test failures. The original version had erroneously emitted functions that are defined in class templates, too (see the updated "Handle friend functions" code in EmitDeferredDecls, lib/CodeGen/ModuleBuilder.cpp). (The updated tests needed to be split out into their own dllexport-ms-friend.cpp because of the CHECK-NOTs which would have interfered with subsequent CHECK-DAGs in dllexport.cpp.) Differential Revision: http://reviews.llvm.org/D18430 llvm-svn: 264841 --- clang/include/clang/AST/ASTConsumer.h | 6 +++--- clang/include/clang/Frontend/MultiplexConsumer.h | 2 +- clang/include/clang/Sema/Sema.h | 2 +- clang/lib/CodeGen/CodeGenAction.cpp | 6 +++--- clang/lib/CodeGen/ModuleBuilder.cpp | 19 +++++++++++++++---- clang/lib/Frontend/MultiplexConsumer.cpp | 4 ++-- clang/lib/Parse/ParseCXXInlineMethods.cpp | 6 ++++-- clang/lib/Sema/SemaDecl.cpp | 4 ++-- clang/test/CodeGenCXX/dllexport-ms-friend.cpp | 22 ++++++++++++++++++++++ 9 files changed, 53 insertions(+), 18 deletions(-) create mode 100644 clang/test/CodeGenCXX/dllexport-ms-friend.cpp diff --git a/clang/include/clang/AST/ASTConsumer.h b/clang/include/clang/AST/ASTConsumer.h index 7b5dd9b..0b1f006 100644 --- a/clang/include/clang/AST/ASTConsumer.h +++ b/clang/include/clang/AST/ASTConsumer.h @@ -55,9 +55,9 @@ public: /// \returns true to continue parsing, or false to abort parsing. virtual bool HandleTopLevelDecl(DeclGroupRef D); - /// \brief This callback is invoked each time an inline method definition is - /// completed. - virtual void HandleInlineMethodDefinition(CXXMethodDecl *D) {} + /// \brief This callback is invoked each time an inline (method or friend) + /// function definition in a class is completed. + virtual void HandleInlineFunctionDefinition(FunctionDecl *D) {} /// HandleInterestingDecl - Handle the specified interesting declaration. This /// is called by the AST reader when deserializing things that might interest diff --git a/clang/include/clang/Frontend/MultiplexConsumer.h b/clang/include/clang/Frontend/MultiplexConsumer.h index da14526..d13565c 100644 --- a/clang/include/clang/Frontend/MultiplexConsumer.h +++ b/clang/include/clang/Frontend/MultiplexConsumer.h @@ -36,7 +36,7 @@ public: void Initialize(ASTContext &Context) override; void HandleCXXStaticMemberVarInstantiation(VarDecl *VD) override; bool HandleTopLevelDecl(DeclGroupRef D) override; - void HandleInlineMethodDefinition(CXXMethodDecl *D) override; + void HandleInlineFunctionDefinition(FunctionDecl *D) override; void HandleInterestingDecl(DeclGroupRef D) override; void HandleTranslationUnit(ASTContext &Ctx) override; void HandleTagDeclDefinition(TagDecl *D) override; diff --git a/clang/include/clang/Sema/Sema.h b/clang/include/clang/Sema/Sema.h index 6fb797d..eb234c4 100644 --- a/clang/include/clang/Sema/Sema.h +++ b/clang/include/clang/Sema/Sema.h @@ -1773,7 +1773,7 @@ public: Decl *ActOnFinishFunctionBody(Decl *Decl, Stmt *Body); Decl *ActOnFinishFunctionBody(Decl *Decl, Stmt *Body, bool IsInstantiation); Decl *ActOnSkippedFunctionBody(Decl *Decl); - void ActOnFinishInlineMethodDef(CXXMethodDecl *D); + void ActOnFinishInlineFunctionDef(FunctionDecl *D); /// ActOnFinishDelayedAttribute - Invoked when we have finished parsing an /// attribute for which parsing is delayed. diff --git a/clang/lib/CodeGen/CodeGenAction.cpp b/clang/lib/CodeGen/CodeGenAction.cpp index 638f79a..edd258d3 100644 --- a/clang/lib/CodeGen/CodeGenAction.cpp +++ b/clang/lib/CodeGen/CodeGenAction.cpp @@ -123,14 +123,14 @@ namespace clang { return true; } - void HandleInlineMethodDefinition(CXXMethodDecl *D) override { + void HandleInlineFunctionDefinition(FunctionDecl *D) override { PrettyStackTraceDecl CrashInfo(D, SourceLocation(), Context->getSourceManager(), - "LLVM IR generation of inline method"); + "LLVM IR generation of inline function"); if (llvm::TimePassesIsEnabled) LLVMIRGeneration.startTimer(); - Gen->HandleInlineMethodDefinition(D); + Gen->HandleInlineFunctionDefinition(D); if (llvm::TimePassesIsEnabled) LLVMIRGeneration.stopTimer(); diff --git a/clang/lib/CodeGen/ModuleBuilder.cpp b/clang/lib/CodeGen/ModuleBuilder.cpp index bd59332..5b7201e 100644 --- a/clang/lib/CodeGen/ModuleBuilder.cpp +++ b/clang/lib/CodeGen/ModuleBuilder.cpp @@ -143,12 +143,23 @@ namespace { DeferredInlineMethodDefinitions.clear(); } - void HandleInlineMethodDefinition(CXXMethodDecl *D) override { + void HandleInlineFunctionDefinition(FunctionDecl *D) override { if (Diags.hasErrorOccurred()) return; assert(D->doesThisDeclarationHaveABody()); + // Handle friend functions. + if (D->isInIdentifierNamespace(Decl::IDNS_OrdinaryFriend)) { + if (Ctx->getTargetInfo().getCXXABI().isMicrosoft() + && !D->getLexicalDeclContext()->isDependentContext()) + Builder->EmitTopLevelDecl(D); + return; + } + + // Otherwise, must be a method. + auto MD = cast(D); + // We may want to emit this definition. However, that decision might be // based on computing the linkage, and we have to defer that in case we // are inside of something that will change the method's final linkage, @@ -157,13 +168,13 @@ namespace { // void bar(); // void foo() { bar(); } // } A; - DeferredInlineMethodDefinitions.push_back(D); + DeferredInlineMethodDefinitions.push_back(MD); // Provide some coverage mapping even for methods that aren't emitted. // Don't do this for templated classes though, as they may not be // instantiable. - if (!D->getParent()->getDescribedClassTemplate()) - Builder->AddDeferredUnusedCoverageMapping(D); + if (!MD->getParent()->getDescribedClassTemplate()) + Builder->AddDeferredUnusedCoverageMapping(MD); } /// HandleTagDeclDefinition - This callback is invoked each time a TagDecl diff --git a/clang/lib/Frontend/MultiplexConsumer.cpp b/clang/lib/Frontend/MultiplexConsumer.cpp index 31c9b5f..ccda8c3 100644 --- a/clang/lib/Frontend/MultiplexConsumer.cpp +++ b/clang/lib/Frontend/MultiplexConsumer.cpp @@ -272,9 +272,9 @@ bool MultiplexConsumer::HandleTopLevelDecl(DeclGroupRef D) { return Continue; } -void MultiplexConsumer::HandleInlineMethodDefinition(CXXMethodDecl *D) { +void MultiplexConsumer::HandleInlineFunctionDefinition(FunctionDecl *D) { for (auto &Consumer : Consumers) - Consumer->HandleInlineMethodDefinition(D); + Consumer->HandleInlineFunctionDefinition(D); } void MultiplexConsumer::HandleCXXStaticMemberVarInstantiation(VarDecl *VD) { diff --git a/clang/lib/Parse/ParseCXXInlineMethods.cpp b/clang/lib/Parse/ParseCXXInlineMethods.cpp index 07e32b7..3db75c7 100644 --- a/clang/lib/Parse/ParseCXXInlineMethods.cpp +++ b/clang/lib/Parse/ParseCXXInlineMethods.cpp @@ -564,8 +564,10 @@ void Parser::ParseLexedMethodDef(LexedMethod &LM) { if (Tok.is(tok::eof) && Tok.getEofData() == LM.D) ConsumeAnyToken(); - if (CXXMethodDecl *MD = dyn_cast_or_null(LM.D)) - Actions.ActOnFinishInlineMethodDef(MD); + if (auto *FD = dyn_cast_or_null(LM.D)) + if (isa(FD) || + FD->isInIdentifierNamespace(Decl::IDNS_OrdinaryFriend)) + Actions.ActOnFinishInlineFunctionDef(FD); } /// ParseLexedMemberInitializers - We finished parsing the member specification diff --git a/clang/lib/Sema/SemaDecl.cpp b/clang/lib/Sema/SemaDecl.cpp index 1f03735..66abb65 100644 --- a/clang/lib/Sema/SemaDecl.cpp +++ b/clang/lib/Sema/SemaDecl.cpp @@ -10875,8 +10875,8 @@ Sema::ActOnStartOfFunctionDef(Scope *FnBodyScope, Declarator &D, return ActOnStartOfFunctionDef(FnBodyScope, DP, SkipBody); } -void Sema::ActOnFinishInlineMethodDef(CXXMethodDecl *D) { - Consumer.HandleInlineMethodDefinition(D); +void Sema::ActOnFinishInlineFunctionDef(FunctionDecl *D) { + Consumer.HandleInlineFunctionDefinition(D); } static bool ShouldWarnAboutMissingPrototype(const FunctionDecl *FD, diff --git a/clang/test/CodeGenCXX/dllexport-ms-friend.cpp b/clang/test/CodeGenCXX/dllexport-ms-friend.cpp new file mode 100644 index 0000000..7bcf590 --- /dev/null +++ b/clang/test/CodeGenCXX/dllexport-ms-friend.cpp @@ -0,0 +1,22 @@ +// RUN: %clang_cc1 -triple %ms_abi_triple -fms-extensions -emit-llvm -O0 -o - %s | FileCheck %s + +// Friend functions defined in classes are emitted. +// CHECK: define weak_odr dllexport void @"\01?friend1@@YAXXZ"() +struct FuncFriend1 { + friend __declspec(dllexport) void friend1() {} +}; + +// But function templates and functions defined in class templates are not +// emitted. +// CHECK-NOT: friend2 +// CHECK-NOT: friend3 +// CHECK-NOT: friend4 +struct FuncFriend2 { + template friend __declspec(dllexport) void friend2() {} +}; +template struct FuncFriend3 { + friend __declspec(dllexport) void friend3() {} + struct Inner { + friend __declspec(dllexport) void friend4() {} + }; +}; -- 2.7.4