]> granicus.if.org Git - clang/commitdiff
Defer codegen of inline method definitions to the end of current top level declaration
authorHans Wennborg <hans@hanshq.net>
Fri, 6 Jun 2014 17:36:17 +0000 (17:36 +0000)
committerHans Wennborg <hans@hanshq.net>
Fri, 6 Jun 2014 17:36:17 +0000 (17:36 +0000)
We would previously fail to emit a definition of bar() for the following code:

  struct __declspec(dllexport) S {
    void foo() {
      t->bar();
    }
    struct T {
      void bar() {}
    };
    T *t;
  };

Note that foo() is an exported method, but bar() is not. However, foo() refers
to bar() so we need to emit its definition. We would previously fail to
realise that bar() is used.

By deferring the method definitions until the end of the top level declaration,
we can simply call EmitTopLevelDecl on them and rely on the usual mechanisms
to decide whether the method should be emitted or not.

Differential Revision: http://reviews.llvm.org/D4038

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

lib/CodeGen/ModuleBuilder.cpp
test/CodeGenCXX/attr-used.cpp
test/CodeGenCXX/dllexport-members.cpp
test/CodeGenCXX/dllexport.cpp

index e5bdae93fe7ed8da452c53237a1283ead60713a4..c4a0e5c063d461978131372ae90f8724fecae42f 100644 (file)
@@ -93,6 +93,12 @@ namespace {
       // Make sure to emit all elements of a Decl.
       for (DeclGroupRef::iterator I = DG.begin(), E = DG.end(); I != E; ++I)
         Builder->EmitTopLevelDecl(*I);
+
+      // Emit any deferred inline method definitions.
+      for (CXXMethodDecl *MD : DeferredInlineMethodDefinitions)
+        Builder->EmitTopLevelDecl(MD);
+      DeferredInlineMethodDefinitions.clear();
+
       return true;
     }
 
@@ -102,12 +108,15 @@ namespace {
 
       assert(D->doesThisDeclarationHaveABody());
 
-      // We may have member functions that need to be emitted at this point.
-      if (!D->isDependentContext() &&
-          (D->hasAttr<UsedAttr>() || D->hasAttr<ConstructorAttr>() ||
-           D->hasAttr<DLLExportAttr>())) {
-        Builder->EmitTopLevelDecl(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,
+      // e.g.
+      //   typedef struct {
+      //     void bar();
+      //     void foo() { bar(); }
+      //   } A;
+      DeferredInlineMethodDefinitions.push_back(D);
     }
 
     /// HandleTagDeclDefinition - This callback is invoked each time a TagDecl
@@ -168,6 +177,9 @@ namespace {
     void HandleDependentLibrary(llvm::StringRef Lib) override {
       Builder->AddDependentLib(Lib);
     }
+
+  private:
+    std::vector<CXXMethodDecl *> DeferredInlineMethodDefinitions;
   };
 }
 
index 26c1597b5897d71c1e512a0ca6ade1fab6e08863..86173466735752cd8bf4689e692a6f8a37ad89a9 100644 (file)
@@ -15,3 +15,13 @@ struct X1 {
     void __attribute__((used)) f() {}
   };
 };
+
+struct X2 {
+  // We must delay emission of bar() until foo() has had its body parsed,
+  // otherwise foo() would not be emitted.
+  void __attribute__((used)) bar() { foo(); }
+  void foo() { }
+
+  // CHECK: define linkonce_odr {{.*}} @_ZN2X23barEv
+  // CHECK: define linkonce_odr {{.*}} @_ZN2X23fooEv
+};
index 481e005264a2f3a3737b711163c1559e42f8b1d0..353b952cffe6c69b887361c35206f4c8a2b1656d 100644 (file)
@@ -40,10 +40,12 @@ struct ExportMembers {
   // G64-DAG: define weak_odr dllexport                void @_ZN13ExportMembers15normalInlineDefEv(%struct.ExportMembers* %this)
   // G32-DAG: define weak_odr dllexport x86_thiscallcc void @_ZN13ExportMembers16normalInlineDeclEv(%struct.ExportMembers* %this)
   // G64-DAG: define weak_odr dllexport                void @_ZN13ExportMembers16normalInlineDeclEv(%struct.ExportMembers* %this)
+  // M32-DAG: define linkonce_odr       x86_thiscallcc void @"\01?referencedNonExportedInClass@ExportMembers@@QAEXXZ"
   __declspec(dllexport)                void normalDef();
-  __declspec(dllexport)                void normalInclass() {}
+  __declspec(dllexport)                void normalInclass() { referencedNonExportedInClass(); }
   __declspec(dllexport)                void normalInlineDef();
   __declspec(dllexport)         inline void normalInlineDecl();
+                                       void referencedNonExportedInClass() {}
 
   // M32-DAG: define          dllexport x86_thiscallcc void @"\01?virtualDef@ExportMembers@@UAEXXZ"(%struct.ExportMembers* %this)
   // M64-DAG: define          dllexport                void @"\01?virtualDef@ExportMembers@@UEAAXXZ"(%struct.ExportMembers* %this)
index adb856c89d854c88bc4494fab00602a49044307b..d393bc53d1b2a0943fd4c59b35278a5b4c10839f 100644 (file)
@@ -538,3 +538,17 @@ struct __declspec(dllexport) DefaultedCtorsDtors {
   ~DefaultedCtorsDtors() = default;
   // M32-DAG: define weak_odr dllexport x86_thiscallcc void @"\01??1DefaultedCtorsDtors@@QAE@XZ"
 };
+
+namespace ReferencedInlineMethodInNestedClass {
+  struct __declspec(dllexport) S {
+    void foo() {
+      t->bar();
+    }
+    struct T {
+      void bar() {}
+    };
+    T *t;
+  };
+  // M32-DAG: define weak_odr dllexport x86_thiscallcc void @"\01?foo@S@ReferencedInlineMethodInNestedClass@@QAEXXZ"
+  // M32-DAG: define linkonce_odr x86_thiscallcc void @"\01?bar@T@S@ReferencedInlineMethodInNestedClass@@QAEXXZ"
+}