dblaikie created this revision.

The best semantics I can come up with (based on discussions with
Richard) for inline asm and namespace scope internal linkage variables
in modular headers is to emit them into all users and never into modular
objects.

The reason for this is that such asm might create important symbols
(like the iostreams global initializer) that can only be produced if the
submodule is used (doesn't strictly apply to inline asm, as such - which
is currently emitted if the module is used at all (regardless of
submodules) I think). This does mean that if any modular code does use
these entities it will be ill formed/NDR (link error, generally) for
now. It could be diagnosed at parse/module-creation time.

This change has a bit of refactoring to allow this to work for inline
asm (& to work more efficiently for internal linkage namespace-scope
variables). Making the MODULAR_CODEGEN_DECLS and
EAGERLY_DESERIALIZED_DECLS distinct. Not only does this allow the
modular codegen not to needlessly deserialize and then ignore the
eagerly deserialized decls, it also allows there to be eagerly
deserialized decls that modular codegen never sees - such as inline asm.

(if this isn't the right design - if the absence of an entry in
eagerly/modular codegen shouldn't produce different behavior (ie: it
should only be an optimization) happy to make this work
differently/harder by making sure inline asm can be filtered, even if it
were loaded)

This change does a little work to preserve a scenario that it probably
doesn't need to: 'classic' AST codegen. There's a few tests
(test/Frontend/ast-*) that verify that the codegen of a serialized AST
file (without -fmodules-codegen) produces basically the same code as if
the AST hadn't been serialized. Is this important? Should we kill off
those tests & not worry about it? (this behavior is preserved by still
respecting the EAGERLY_DESERIALIZED_DECLS when deserializing an AST file
if it doesn't contain MODULAR_CODEGEN_DECLS).


https://reviews.llvm.org/D29432

Files:
  include/clang/AST/ASTContext.h
  include/clang/Serialization/ASTReader.h
  lib/AST/ASTContext.cpp
  lib/Serialization/ASTReader.cpp
  lib/Serialization/ASTWriterDecl.cpp
  test/CodeGenCXX/modules-ts.cppm
  test/Modules/Inputs/codegen/foo.h
  test/Modules/codegen.test

Index: test/Modules/codegen.test
===================================================================
--- test/Modules/codegen.test
+++ test/Modules/codegen.test
@@ -12,14 +12,12 @@
 RUN: %clang_cc1 -triple x86_64-linux-gnu -emit-llvm -o - -O2 -disable-llvm-passes %t/bar.pcm -fmodule-file=%t/foo.pcm | FileCheck --check-prefix=BAR-CMN --check-prefix=BAR-OPT %s
 RUN: %clang_cc1 -triple x86_64-linux-gnu -emit-llvm -o - -O2 -disable-llvm-passes -fmodules -fmodule-file=%t/foo.pcm -fmodule-file=%t/bar.pcm %S/Inputs/codegen/use.cpp | FileCheck --check-prefix=USE-CMN --check-prefix=USE-OPT %s
 
-FOO-NOT: comdat
+FOO-NOT: {{comdat|module asm}}
 FOO: $_Z3foov = comdat any
 FOO: $_Z4foo2v = comdat any
 FOO: $_ZZ3foovE1i = comdat any
 FOO: @_ZZ3foovE1i = linkonce_odr global i32 0, comdat
 FOO-NOT: {{comdat|define|declare}}
-FOO: define void @_Z7foo_extv()
-FOO-NOT: {{define|declare}}
 FOO: define weak_odr void @_Z3foov() #{{[0-9]+}} comdat
 FOO-NOT: {{define|declare}}
 FOO: declare void @_Z2f1Ri(i32*
@@ -31,26 +29,37 @@
 
 FOO: define weak_odr void @_Z4foo2v() #{{[0-9]+}} comdat
 FOO-NOT: {{define|declare}}
+FOO: define void @_Z7foo_extv()
+FOO-NOT: {{define|declare}}
 
 
-BAR-CMN-NOT: comdat
+BAR-CMN-NOT: {{comdat|module asm}}
 BAR-CMN: $_Z3barv = comdat any
+BAR-CMN-NOT: comdat
+BAR-OPT: $_ZZ3foovE1i = comdat any
+BAR-OPT-NOT: comdat
 BAR-OPT: @_ZZ3foovE1i = linkonce_odr global i32 0, comdat
-BAR-CMN-NOT: {{comdat|define|declare}}
+BAR-CMN-NOT: {{define|declare}}
 BAR-CMN: define weak_odr void @_Z3barv() #{{[0-9]+}} comdat
 BAR-CMN-NOT: {{define|declare}}
 BAR: declare void @_Z3foov()
+BAR-NOT: {{define|declare}}
 Include all the available_externally definitions required for bar (foo -> f2)
 BAR-OPT: define available_externally void @_Z3foov()
-BAR-CMN-NOT: {{define|declare}}
+BAR-OPT-NOT: {{define|declare}}
 BAR-OPT: declare void @_Z2f1Ri(i32*
 BAR-OPT-NOT: {{define|declare}}
 BAR-OPT: define available_externally void @_ZL2f2v()
 BAR-OPT-NOT: {{define|declare}}
 
 
+USE-CMN-NOT: {{comdat|module asm}}
+USE-CMN: module asm ".byte 42"
+USE-CMN-NOT: {{comdat|module asm}}
+USE-OPT: $_ZZ3foovE1i = comdat any
+USE-OPT-CMN-NOT: {{comdat}}
 USE-OPT: @_ZZ3foovE1i = linkonce_odr global i32 0, comdat
-USE-CMN-NOT: {{comdat|define|declare}}
+USE-CMN-NOT: {{define|declare}}
 USE-CMN: define i32 @main()
 USE-CMN-NOT: {{define|declare}}
 USE: declare void @_Z3barv()
Index: test/Modules/Inputs/codegen/foo.h
===================================================================
--- test/Modules/Inputs/codegen/foo.h
+++ test/Modules/Inputs/codegen/foo.h
@@ -1,3 +1,4 @@
+asm(".byte 42");
 void f1(int &);
 static void f2() {}
 inline void foo() {
Index: test/CodeGenCXX/modules-ts.cppm
===================================================================
--- test/CodeGenCXX/modules-ts.cppm
+++ test/CodeGenCXX/modules-ts.cppm
@@ -3,14 +3,14 @@
 
 module FooBar;
 
+// CHECK-LABEL: define weak_odr void @_Z2f2v(
+inline void f2() { }
+
 export {
   // CHECK-LABEL: define i32 @_Z1fv(
   int f() { return 0; }
 }
 
-// CHECK-LABEL: define weak_odr void @_Z2f2v(
-inline void f2() { }
-
 // FIXME: Emit global variables and their initializers with this TU.
 // Emit an initialization function that other TUs can call, with guard variable.
 
Index: lib/Serialization/ASTWriterDecl.cpp
===================================================================
--- lib/Serialization/ASTWriterDecl.cpp
+++ lib/Serialization/ASTWriterDecl.cpp
@@ -2153,7 +2153,8 @@
 /// relatively painless since they would presumably only do it for top-level
 /// decls.
 static bool isRequiredDecl(const Decl *D, ASTContext &Context,
-                           bool WritingModule, bool ModularCode) {
+                           bool WritingModule, bool *ForModuleFile) {
+  assert(!ForModuleFile || !*ForModuleFile);
   // An ObjCMethodDecl is never considered as "required" because its
   // implementation container always is.
 
@@ -2169,7 +2170,7 @@
     return false;
   }
 
-  return Context.DeclMustBeEmitted(D, ModularCode);
+  return Context.DeclMustBeEmitted(D, ForModuleFile);
 }
 
 void ASTWriter::WriteDecl(ASTContext &Context, Decl *D) {
@@ -2213,11 +2214,13 @@
 
   // Note declarations that should be deserialized eagerly so that we can add
   // them to a record in the AST file later.
-  if (isRequiredDecl(D, Context, WritingModule, false))
-    EagerlyDeserializedDecls.push_back(ID);
-  else if (Context.getLangOpts().ModularCodegen && WritingModule &&
-           isRequiredDecl(D, Context, true, true))
-    ModularCodegenDecls.push_back(ID);
+  bool ForModuleFile = false;
+  if (isRequiredDecl(D, Context, WritingModule,
+                     Context.getLangOpts().ModularCodegen && WritingModule
+                         ? &ForModuleFile
+                         : nullptr))
+    (ForModuleFile ? ModularCodegenDecls : EagerlyDeserializedDecls)
+        .push_back(ID);
 }
 
 void ASTRecordWriter::AddFunctionDefinition(const FunctionDecl *FD) {
Index: lib/Serialization/ASTReader.cpp
===================================================================
--- lib/Serialization/ASTReader.cpp
+++ lib/Serialization/ASTReader.cpp
@@ -2777,9 +2777,11 @@
     case MODULAR_CODEGEN_DECLS:
       // FIXME: Skip reading this record if our ASTConsumer doesn't care about
       // them (ie: if we're not codegenerating this module).
-      if (F.Kind == MK_MainFile)
+      if (F.Kind == MK_MainFile) {
+        ModularCodegenDecls.emplace();
         for (unsigned I = 0, N = Record.size(); I != N; ++I)
-          EagerlyDeserializedDecls.push_back(getGlobalDeclID(F, Record[I]));
+          ModularCodegenDecls->push_back(getGlobalDeclID(F, Record[I]));
+      }
       break;
 
     case SPECIAL_TYPES:
@@ -7001,8 +7003,15 @@
 
   // Ensure that we've loaded all potentially-interesting declarations
   // that need to be eagerly loaded.
-  for (auto ID : EagerlyDeserializedDecls)
-    GetDecl(ID);
+  auto GetDecls = [&](const SmallVector<uint64_t, 16> &V) {
+    for (auto ID : V)
+      GetDecl(ID);
+  };
+  if (ModularCodegenDecls)
+    GetDecls(*ModularCodegenDecls);
+  else
+    GetDecls(EagerlyDeserializedDecls);
+  ModularCodegenDecls = None;
   EagerlyDeserializedDecls.clear();
 
   while (!InterestingDecls.empty()) {
Index: lib/AST/ASTContext.cpp
===================================================================
--- lib/AST/ASTContext.cpp
+++ lib/AST/ASTContext.cpp
@@ -8982,7 +8982,8 @@
       *this, basicGVALinkageForVariable(*this, VD), VD);
 }
 
-bool ASTContext::DeclMustBeEmitted(const Decl *D, bool ForModularCodegen) {
+bool ASTContext::DeclMustBeEmitted(const Decl *D, bool *ForModuleFile) {
+  assert(!ForModuleFile || !*ForModuleFile);
   if (const VarDecl *VD = dyn_cast<VarDecl>(D)) {
     if (!VD->isFileVarDecl())
       return false;
@@ -9048,8 +9049,11 @@
 
     GVALinkage Linkage = GetGVALinkageForFunction(FD);
 
-    if (Linkage == GVA_DiscardableODR && ForModularCodegen)
+    if ((Linkage == GVA_DiscardableODR || Linkage == GVA_StrongExternal) &&
+        ForModuleFile) {
+      *ForModuleFile = true;
       return true;
+    }
 
     // static, static inline, always_inline, and extern inline functions can
     // always be deferred.  Normal inline functions can be deferred in C99/C++.
Index: include/clang/Serialization/ASTReader.h
===================================================================
--- include/clang/Serialization/ASTReader.h
+++ include/clang/Serialization/ASTReader.h
@@ -715,7 +715,7 @@
   /// the consumer eagerly.
   SmallVector<uint64_t, 16> EagerlyDeserializedDecls;
 
-  SmallVector<uint64_t, 16> ModularCodegenDecls;
+  Optional<SmallVector<uint64_t, 16>> ModularCodegenDecls;
 
   /// \brief The IDs of all tentative definitions stored in the chain.
   ///
Index: include/clang/AST/ASTContext.h
===================================================================
--- include/clang/AST/ASTContext.h
+++ include/clang/AST/ASTContext.h
@@ -2494,7 +2494,7 @@
   ///
   /// \returns true if the function/var must be CodeGen'ed/deserialized even if
   /// it is not used.
-  bool DeclMustBeEmitted(const Decl *D, bool ForModularCodegen = false);
+  bool DeclMustBeEmitted(const Decl *D, bool *ForModuleFile = nullptr);
 
   const CXXConstructorDecl *
   getCopyConstructorForExceptionObject(CXXRecordDecl *RD);
_______________________________________________
cfe-commits mailing list
cfe-commits@lists.llvm.org
http://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits

Reply via email to