[PATCH] D69779: -fmodules-codegen should not emit extern templates

2020-01-14 Thread Luboš Luňák via Phabricator via cfe-commits
This revision was automatically updated to reflect the committed changes.
Closed by commit rG729530f68fe1: -fmodules-codegen should not emit extern 
templates (authored by llunak).

Repository:
  rG LLVM Github Monorepo

CHANGES SINCE LAST ACTION
  https://reviews.llvm.org/D69779/new/

https://reviews.llvm.org/D69779

Files:
  clang/lib/Serialization/ASTWriterDecl.cpp
  clang/test/Modules/codegen-extern-template.cpp
  clang/test/Modules/codegen-extern-template.h
  clang/test/Modules/codegen-extern-template.modulemap


Index: clang/test/Modules/codegen-extern-template.modulemap
===
--- /dev/null
+++ clang/test/Modules/codegen-extern-template.modulemap
@@ -0,0 +1 @@
+module foo { header "codegen-extern-template.h" }
Index: clang/test/Modules/codegen-extern-template.h
===
--- /dev/null
+++ clang/test/Modules/codegen-extern-template.h
@@ -0,0 +1,12 @@
+// header for codegen-extern-template.cpp
+#ifndef CODEGEN_EXTERN_TEMPLATE_H
+#define CODEGEN_EXTERN_TEMPLATE_H
+
+template 
+inline T foo() { return 10; }
+
+extern template int foo();
+
+inline int bar() { return foo(); }
+
+#endif
Index: clang/test/Modules/codegen-extern-template.cpp
===
--- /dev/null
+++ clang/test/Modules/codegen-extern-template.cpp
@@ -0,0 +1,9 @@
+// RUN: %clang_cc1 -triple=x86_64-linux-gnu -fmodules -fmodules-codegen 
-emit-module -fmodule-name=foo %S/codegen-extern-template.modulemap -x c++ -o 
%t.pcm
+// RUN: %clang_cc1 -triple x86_64-linux-gnu -fmodules -fmodule-file=%t.pcm %s 
-emit-llvm -o - | FileCheck %s
+// expected-no-diagnostics
+
+#include "codegen-extern-template.h"
+
+template int foo();
+
+// CHECK: define weak_odr i32 @_Z3fooIiET_v
Index: clang/lib/Serialization/ASTWriterDecl.cpp
===
--- clang/lib/Serialization/ASTWriterDecl.cpp
+++ clang/lib/Serialization/ASTWriterDecl.cpp
@@ -2437,11 +2437,12 @@
 }
 if (Writer->Context->getLangOpts().ModulesCodegen) {
   // Under -fmodules-codegen, codegen is performed for all non-internal,
-  // non-always_inline functions.
+  // non-always_inline functions, unless they are available elsewhere.
   if (!FD->hasAttr()) {
 if (!Linkage)
   Linkage = Writer->Context->GetGVALinkageForFunction(FD);
-ModulesCodegen = *Linkage != GVA_Internal;
+ModulesCodegen =
+*Linkage != GVA_Internal && *Linkage != GVA_AvailableExternally;
   }
 }
   }


Index: clang/test/Modules/codegen-extern-template.modulemap
===
--- /dev/null
+++ clang/test/Modules/codegen-extern-template.modulemap
@@ -0,0 +1 @@
+module foo { header "codegen-extern-template.h" }
Index: clang/test/Modules/codegen-extern-template.h
===
--- /dev/null
+++ clang/test/Modules/codegen-extern-template.h
@@ -0,0 +1,12 @@
+// header for codegen-extern-template.cpp
+#ifndef CODEGEN_EXTERN_TEMPLATE_H
+#define CODEGEN_EXTERN_TEMPLATE_H
+
+template 
+inline T foo() { return 10; }
+
+extern template int foo();
+
+inline int bar() { return foo(); }
+
+#endif
Index: clang/test/Modules/codegen-extern-template.cpp
===
--- /dev/null
+++ clang/test/Modules/codegen-extern-template.cpp
@@ -0,0 +1,9 @@
+// RUN: %clang_cc1 -triple=x86_64-linux-gnu -fmodules -fmodules-codegen -emit-module -fmodule-name=foo %S/codegen-extern-template.modulemap -x c++ -o %t.pcm
+// RUN: %clang_cc1 -triple x86_64-linux-gnu -fmodules -fmodule-file=%t.pcm %s -emit-llvm -o - | FileCheck %s
+// expected-no-diagnostics
+
+#include "codegen-extern-template.h"
+
+template int foo();
+
+// CHECK: define weak_odr i32 @_Z3fooIiET_v
Index: clang/lib/Serialization/ASTWriterDecl.cpp
===
--- clang/lib/Serialization/ASTWriterDecl.cpp
+++ clang/lib/Serialization/ASTWriterDecl.cpp
@@ -2437,11 +2437,12 @@
 }
 if (Writer->Context->getLangOpts().ModulesCodegen) {
   // Under -fmodules-codegen, codegen is performed for all non-internal,
-  // non-always_inline functions.
+  // non-always_inline functions, unless they are available elsewhere.
   if (!FD->hasAttr()) {
 if (!Linkage)
   Linkage = Writer->Context->GetGVALinkageForFunction(FD);
-ModulesCodegen = *Linkage != GVA_Internal;
+ModulesCodegen =
+*Linkage != GVA_Internal && *Linkage != GVA_AvailableExternally;
   }
 }
   }
___
cfe-commits mailing list
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits


[PATCH] D69779: -fmodules-codegen should not emit extern templates

2020-01-14 Thread David Blaikie via Phabricator via cfe-commits
dblaikie accepted this revision.
dblaikie added a comment.
This revision is now accepted and ready to land.

Looks good - thanks for your patience!


CHANGES SINCE LAST ACTION
  https://reviews.llvm.org/D69779/new/

https://reviews.llvm.org/D69779



___
cfe-commits mailing list
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits


[PATCH] D69779: -fmodules-codegen should not emit extern templates

2020-01-14 Thread Luboš Luňák via Phabricator via cfe-commits
llunak updated this revision to Diff 238089.
llunak added a comment.

This version uses a module based on the code posted above.


CHANGES SINCE LAST ACTION
  https://reviews.llvm.org/D69779/new/

https://reviews.llvm.org/D69779

Files:
  clang/lib/Serialization/ASTWriterDecl.cpp
  clang/test/Modules/codegen-extern-template.cpp
  clang/test/Modules/codegen-extern-template.h
  clang/test/Modules/codegen-extern-template.modulemap


Index: clang/test/Modules/codegen-extern-template.modulemap
===
--- /dev/null
+++ clang/test/Modules/codegen-extern-template.modulemap
@@ -0,0 +1 @@
+module foo { header "codegen-extern-template.h" }
Index: clang/test/Modules/codegen-extern-template.h
===
--- /dev/null
+++ clang/test/Modules/codegen-extern-template.h
@@ -0,0 +1,12 @@
+// header for codegen-extern-template.cpp
+#ifndef CODEGEN_EXTERN_TEMPLATE_H
+#define CODEGEN_EXTERN_TEMPLATE_H
+
+template 
+inline T foo() { return 10; }
+
+extern template int foo();
+
+inline int bar() { return foo(); }
+
+#endif
Index: clang/test/Modules/codegen-extern-template.cpp
===
--- /dev/null
+++ clang/test/Modules/codegen-extern-template.cpp
@@ -0,0 +1,9 @@
+// RUN: %clang_cc1 -triple=x86_64-linux-gnu -fmodules -fmodules-codegen 
-emit-module -fmodule-name=foo %S/codegen-extern-template.modulemap -x c++ -o 
%t.pcm
+// RUN: %clang_cc1 -triple x86_64-linux-gnu -fmodules -fmodule-file=%t.pcm %s 
-emit-llvm -o - | FileCheck %s
+// expected-no-diagnostics
+
+#include "codegen-extern-template.h"
+
+template int foo();
+
+// CHECK: define weak_odr i32 @_Z3fooIiET_v
Index: clang/lib/Serialization/ASTWriterDecl.cpp
===
--- clang/lib/Serialization/ASTWriterDecl.cpp
+++ clang/lib/Serialization/ASTWriterDecl.cpp
@@ -2412,11 +2412,12 @@
 }
 if (Writer->Context->getLangOpts().ModulesCodegen) {
   // Under -fmodules-codegen, codegen is performed for all non-internal,
-  // non-always_inline functions.
+  // non-always_inline functions, unless they are available elsewhere.
   if (!FD->hasAttr()) {
 if (!Linkage)
   Linkage = Writer->Context->GetGVALinkageForFunction(FD);
-ModulesCodegen = *Linkage != GVA_Internal;
+ModulesCodegen =
+*Linkage != GVA_Internal && *Linkage != GVA_AvailableExternally;
   }
 }
   }


Index: clang/test/Modules/codegen-extern-template.modulemap
===
--- /dev/null
+++ clang/test/Modules/codegen-extern-template.modulemap
@@ -0,0 +1 @@
+module foo { header "codegen-extern-template.h" }
Index: clang/test/Modules/codegen-extern-template.h
===
--- /dev/null
+++ clang/test/Modules/codegen-extern-template.h
@@ -0,0 +1,12 @@
+// header for codegen-extern-template.cpp
+#ifndef CODEGEN_EXTERN_TEMPLATE_H
+#define CODEGEN_EXTERN_TEMPLATE_H
+
+template 
+inline T foo() { return 10; }
+
+extern template int foo();
+
+inline int bar() { return foo(); }
+
+#endif
Index: clang/test/Modules/codegen-extern-template.cpp
===
--- /dev/null
+++ clang/test/Modules/codegen-extern-template.cpp
@@ -0,0 +1,9 @@
+// RUN: %clang_cc1 -triple=x86_64-linux-gnu -fmodules -fmodules-codegen -emit-module -fmodule-name=foo %S/codegen-extern-template.modulemap -x c++ -o %t.pcm
+// RUN: %clang_cc1 -triple x86_64-linux-gnu -fmodules -fmodule-file=%t.pcm %s -emit-llvm -o - | FileCheck %s
+// expected-no-diagnostics
+
+#include "codegen-extern-template.h"
+
+template int foo();
+
+// CHECK: define weak_odr i32 @_Z3fooIiET_v
Index: clang/lib/Serialization/ASTWriterDecl.cpp
===
--- clang/lib/Serialization/ASTWriterDecl.cpp
+++ clang/lib/Serialization/ASTWriterDecl.cpp
@@ -2412,11 +2412,12 @@
 }
 if (Writer->Context->getLangOpts().ModulesCodegen) {
   // Under -fmodules-codegen, codegen is performed for all non-internal,
-  // non-always_inline functions.
+  // non-always_inline functions, unless they are available elsewhere.
   if (!FD->hasAttr()) {
 if (!Linkage)
   Linkage = Writer->Context->GetGVALinkageForFunction(FD);
-ModulesCodegen = *Linkage != GVA_Internal;
+ModulesCodegen =
+*Linkage != GVA_Internal && *Linkage != GVA_AvailableExternally;
   }
 }
   }
___
cfe-commits mailing list
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits


[PATCH] D69779: -fmodules-codegen should not emit extern templates

2020-01-14 Thread Luboš Luňák via Phabricator via cfe-commits
llunak updated this revision to Diff 238086.
llunak added a comment.

I've updated the test as requested.

However I've noticed that a PCH-based test for this relies on 
https://reviews.llvm.org/D69585 . The fix works (of course), but it requires a 
PCH/module with the instantiation.


CHANGES SINCE LAST ACTION
  https://reviews.llvm.org/D69779/new/

https://reviews.llvm.org/D69779

Files:
  clang/lib/Serialization/ASTWriterDecl.cpp
  clang/test/PCH/codegen-extern-template.cpp
  clang/test/PCH/codegen-extern-template.h


Index: clang/test/PCH/codegen-extern-template.h
===
--- /dev/null
+++ clang/test/PCH/codegen-extern-template.h
@@ -0,0 +1,12 @@
+// header for codegen-extern-template.cpp
+#ifndef CODEGEN_EXTERN_TEMPLATE_H
+#define CODEGEN_EXTERN_TEMPLATE_H
+
+template 
+inline T foo() { return 10; }
+
+extern template int foo();
+
+inline int bar() { return foo(); }
+
+#endif
Index: clang/test/PCH/codegen-extern-template.cpp
===
--- /dev/null
+++ clang/test/PCH/codegen-extern-template.cpp
@@ -0,0 +1,9 @@
+// RUN: %clang_cc1 -triple=x86_64-linux-gnu -x c++-header 
-building-pch-with-obj -pch-instantiate-templates -fmodules-codegen -emit-pch 
%S/codegen-extern-template.h -o %t.pch
+// RUN: %clang_cc1 -triple x86_64-linux-gnu -emit-llvm -o - %s -include-pch 
%t.pch | FileCheck %s
+// expected-no-diagnostics
+
+#include "codegen-extern-template.h"
+
+template int foo();
+
+// CHECK: define weak_odr i32 @_Z3fooIiET_v
Index: clang/lib/Serialization/ASTWriterDecl.cpp
===
--- clang/lib/Serialization/ASTWriterDecl.cpp
+++ clang/lib/Serialization/ASTWriterDecl.cpp
@@ -2412,11 +2412,12 @@
 }
 if (Writer->Context->getLangOpts().ModulesCodegen) {
   // Under -fmodules-codegen, codegen is performed for all non-internal,
-  // non-always_inline functions.
+  // non-always_inline functions, unless they are available elsewhere.
   if (!FD->hasAttr()) {
 if (!Linkage)
   Linkage = Writer->Context->GetGVALinkageForFunction(FD);
-ModulesCodegen = *Linkage != GVA_Internal;
+ModulesCodegen =
+*Linkage != GVA_Internal && *Linkage != GVA_AvailableExternally;
   }
 }
   }


Index: clang/test/PCH/codegen-extern-template.h
===
--- /dev/null
+++ clang/test/PCH/codegen-extern-template.h
@@ -0,0 +1,12 @@
+// header for codegen-extern-template.cpp
+#ifndef CODEGEN_EXTERN_TEMPLATE_H
+#define CODEGEN_EXTERN_TEMPLATE_H
+
+template 
+inline T foo() { return 10; }
+
+extern template int foo();
+
+inline int bar() { return foo(); }
+
+#endif
Index: clang/test/PCH/codegen-extern-template.cpp
===
--- /dev/null
+++ clang/test/PCH/codegen-extern-template.cpp
@@ -0,0 +1,9 @@
+// RUN: %clang_cc1 -triple=x86_64-linux-gnu -x c++-header -building-pch-with-obj -pch-instantiate-templates -fmodules-codegen -emit-pch %S/codegen-extern-template.h -o %t.pch
+// RUN: %clang_cc1 -triple x86_64-linux-gnu -emit-llvm -o - %s -include-pch %t.pch | FileCheck %s
+// expected-no-diagnostics
+
+#include "codegen-extern-template.h"
+
+template int foo();
+
+// CHECK: define weak_odr i32 @_Z3fooIiET_v
Index: clang/lib/Serialization/ASTWriterDecl.cpp
===
--- clang/lib/Serialization/ASTWriterDecl.cpp
+++ clang/lib/Serialization/ASTWriterDecl.cpp
@@ -2412,11 +2412,12 @@
 }
 if (Writer->Context->getLangOpts().ModulesCodegen) {
   // Under -fmodules-codegen, codegen is performed for all non-internal,
-  // non-always_inline functions.
+  // non-always_inline functions, unless they are available elsewhere.
   if (!FD->hasAttr()) {
 if (!Linkage)
   Linkage = Writer->Context->GetGVALinkageForFunction(FD);
-ModulesCodegen = *Linkage != GVA_Internal;
+ModulesCodegen =
+*Linkage != GVA_Internal && *Linkage != GVA_AvailableExternally;
   }
 }
   }
___
cfe-commits mailing list
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits


[PATCH] D69779: -fmodules-codegen should not emit extern templates

2020-01-13 Thread David Blaikie via Phabricator via cfe-commits
dblaikie added inline comments.



Comment at: clang/test/PCH/codegen-extern-template.cpp:1-9
+// Build the PCH with extern template.
+// RUN: %clang -x c++-header %S/codegen-extern-template.h -o %t.pch -Xclang 
-building-pch-with-obj -Xclang -fmodules-codegen
+// Build the PCH's object file.
+// RUN: %clang -c %s -include-pch %t.pch -Xclang -building-pch-with-obj 
-Xclang -fmodules-codegen -o %t.1.o
+// Build source with explicit template instantiation.
+// RUN: %clang -c %s -DMAIN -include-pch %t.pch -o %t.2.o
+// There should be no unresolved symbol.

Please reduce the test to not rely on linking - it should check for the IR 
output of clang (not object output) for the symbols that should/shouldn't be 
present.

Specifically the main function is probably unneeded - and only the explicit 
template specialization definition is needed. The test would then verify that 
the required function definition is present in the IR output.

Also - to simplify this, could you change this to use an inline function 
template - rather than a member function? It'd make it more clear that it's the 
function template with 'inline' (rather than relying on the implicit inline 
from member functions).



Comment at: clang/test/PCH/codegen-extern-template.h:1-16
+// header for codegen-extern-template.cpp
+#pragma once
+
+template< typename T >
+struct A
+{
+   T foo() { return 10;}

Please run this (& the .cpp file) through clang-format, and change the #pragma 
once to standard header include guards (#define/etc).


Repository:
  rC Clang

CHANGES SINCE LAST ACTION
  https://reviews.llvm.org/D69779/new/

https://reviews.llvm.org/D69779



___
cfe-commits mailing list
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits


[PATCH] D69779: -fmodules-codegen should not emit extern templates

2020-01-13 Thread Luboš Luňák via Phabricator via cfe-commits
llunak added a comment.

Do you need some more information about the patch? It'd be nice if this could 
make it into 10.0.


Repository:
  rC Clang

CHANGES SINCE LAST ACTION
  https://reviews.llvm.org/D69779/new/

https://reviews.llvm.org/D69779



___
cfe-commits mailing list
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits


[PATCH] D69779: -fmodules-codegen should not emit extern templates

2020-01-07 Thread Luboš Luňák via Phabricator via cfe-commits
llunak added a comment.

In D69779#1801771 , @dblaikie wrote:

> Ah, if I mark the standalone function template 'inline' (the implicit linkage 
> of member functions) then I get the same failure for both. Haven't tested 
> whether the fix is the same fix for both yet.


With my patch there are no errors with your testcase, even with the 'inline' 
added.


Repository:
  rC Clang

CHANGES SINCE LAST ACTION
  https://reviews.llvm.org/D69779/new/

https://reviews.llvm.org/D69779



___
cfe-commits mailing list
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits


[PATCH] D69779: -fmodules-codegen should not emit extern templates

2020-01-02 Thread David Blaikie via Phabricator via cfe-commits
dblaikie added a comment.

Hey - thanks for this! Does look like it reproduces in modules:

foo.h:

  #pragma once
  
  template 
  struct outer {
  static void func() {
  }
  };
  
  template
  void func() {
  }
  
  extern template struct outer;
  extern template void func();
  
  inline void caller() {
outer::func();
func();
  }

foo.modulemap:

  module foo { header "foo.h" }

use.cpp:

  #include "foo.h"
  template struct outer;
  template void func();
  int main() {
  }

Commands:

  clang-tot -cc1 -fmodules-codegen -fmodules -emit-module -fmodule-name=foo 
foo.modulemap  -x c++ -o foo.pcm
  clang-tot -cc1 -fmodules -fmodule-file=foo.pcm use.cpp -emit-obj
  clang-tot -c foo.pcm
  clang-tot foo.o use.o

This test case exercises both a member function of a class template, and a 
standalone function template. I'd like to better understand why this shows up 
for function templates but not member functions of class templates - perhaps 
there's somewhere the solution to both cases can be unified.

Ah, if I mark the standalone function template 'inline' (the implicit linkage 
of member functions) then I get the same failure for both. Haven't tested 
whether the fix is the same fix for both yet.


Repository:
  rC Clang

CHANGES SINCE LAST ACTION
  https://reviews.llvm.org/D69779/new/

https://reviews.llvm.org/D69779



___
cfe-commits mailing list
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits


[PATCH] D69779: -fmodules-codegen should not emit extern templates

2019-12-05 Thread Luboš Luňák via Phabricator via cfe-commits
llunak added a comment.

Ping.


Repository:
  rC Clang

CHANGES SINCE LAST ACTION
  https://reviews.llvm.org/D69779/new/

https://reviews.llvm.org/D69779



___
cfe-commits mailing list
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits


[PATCH] D69779: -fmodules-codegen should not emit extern templates

2019-11-03 Thread Luboš Luňák via Phabricator via cfe-commits
llunak created this revision.
llunak added a reviewer: dblaikie.
llunak added a project: clang.
Herald added a subscriber: cfe-commits.

See the test for a testcase. If a header contains 'extern template', then the 
template should be provided somewhere by an explicit instantiation, so it is 
not necessary to generate a copy. Worse (at least with PCH 
-building-pch-with-obj), the current code actually leads to an unresolved 
symbol, because the PCH's object file will not actually contain functions from 
such a template (because of the GVA_AvailableExternally?), but the object file 
for the explicit instantiation will not contain them either because it will be 
blocked by the information provided by the PCH. I don't know if the same 
problem exists with modules (nor I know how to check for sure), all tests pass. 
If the problem is not there for modules, I can make the change PCH-specific.


Repository:
  rC Clang

https://reviews.llvm.org/D69779

Files:
  clang/lib/Serialization/ASTWriterDecl.cpp
  clang/test/PCH/codegen-extern-template.cpp
  clang/test/PCH/codegen-extern-template.h


Index: clang/test/PCH/codegen-extern-template.h
===
--- /dev/null
+++ clang/test/PCH/codegen-extern-template.h
@@ -0,0 +1,16 @@
+// header for codegen-extern-template.cpp
+#pragma once
+
+template< typename T >
+struct A
+{
+   T foo() { return 10;}
+};
+
+extern template struct A< int >;
+
+inline
+int bar(A* p)
+{
+return p->foo();
+}
Index: clang/test/PCH/codegen-extern-template.cpp
===
--- /dev/null
+++ clang/test/PCH/codegen-extern-template.cpp
@@ -0,0 +1,21 @@
+// Build the PCH with extern template.
+// RUN: %clang -x c++-header %S/codegen-extern-template.h -o %t.pch -Xclang 
-building-pch-with-obj -Xclang -fmodules-codegen
+// Build the PCH's object file.
+// RUN: %clang -c %s -include-pch %t.pch -Xclang -building-pch-with-obj 
-Xclang -fmodules-codegen -o %t.1.o
+// Build source with explicit template instantiation.
+// RUN: %clang -c %s -DMAIN -include-pch %t.pch -o %t.2.o
+// There should be no unresolved symbol.
+// RUN: %clang %t.1.o %t.2.o
+// expected-no-diagnostics
+
+#include "codegen-extern-template.h"
+
+#ifdef MAIN
+template struct A< int >;
+int main()
+{
+A< int > a;
+return bar(&a);
+}
+
+#endif
Index: clang/lib/Serialization/ASTWriterDecl.cpp
===
--- clang/lib/Serialization/ASTWriterDecl.cpp
+++ clang/lib/Serialization/ASTWriterDecl.cpp
@@ -2412,11 +2412,12 @@
 }
 if (Writer->Context->getLangOpts().ModulesCodegen) {
   // Under -fmodules-codegen, codegen is performed for all non-internal,
-  // non-always_inline functions.
+  // non-always_inline functions, unless they are available elsewhere.
   if (!FD->hasAttr()) {
 if (!Linkage)
   Linkage = Writer->Context->GetGVALinkageForFunction(FD);
-ModulesCodegen = *Linkage != GVA_Internal;
+ModulesCodegen =
+*Linkage != GVA_Internal && *Linkage != GVA_AvailableExternally;
   }
 }
   }


Index: clang/test/PCH/codegen-extern-template.h
===
--- /dev/null
+++ clang/test/PCH/codegen-extern-template.h
@@ -0,0 +1,16 @@
+// header for codegen-extern-template.cpp
+#pragma once
+
+template< typename T >
+struct A
+{
+   T foo() { return 10;}
+};
+
+extern template struct A< int >;
+
+inline
+int bar(A* p)
+{
+return p->foo();
+}
Index: clang/test/PCH/codegen-extern-template.cpp
===
--- /dev/null
+++ clang/test/PCH/codegen-extern-template.cpp
@@ -0,0 +1,21 @@
+// Build the PCH with extern template.
+// RUN: %clang -x c++-header %S/codegen-extern-template.h -o %t.pch -Xclang -building-pch-with-obj -Xclang -fmodules-codegen
+// Build the PCH's object file.
+// RUN: %clang -c %s -include-pch %t.pch -Xclang -building-pch-with-obj -Xclang -fmodules-codegen -o %t.1.o
+// Build source with explicit template instantiation.
+// RUN: %clang -c %s -DMAIN -include-pch %t.pch -o %t.2.o
+// There should be no unresolved symbol.
+// RUN: %clang %t.1.o %t.2.o
+// expected-no-diagnostics
+
+#include "codegen-extern-template.h"
+
+#ifdef MAIN
+template struct A< int >;
+int main()
+{
+A< int > a;
+return bar(&a);
+}
+
+#endif
Index: clang/lib/Serialization/ASTWriterDecl.cpp
===
--- clang/lib/Serialization/ASTWriterDecl.cpp
+++ clang/lib/Serialization/ASTWriterDecl.cpp
@@ -2412,11 +2412,12 @@
 }
 if (Writer->Context->getLangOpts().ModulesCodegen) {
   // Under -fmodules-codegen, codegen is performed for all non-internal,
-  // non-always_inline functions.
+  // non-always_inline functions, unless they are available elsewhere.
   if (!FD->hasAttr()) {
 if (!Linkage)
   Linkag