[ThinLTO] Import composite types as declarations

Summary:
When reading the metadata bitcode, create a type declaration when
possible for composite types when we are importing. Doing this in
the bitcode reader saves memory. Also it works naturally in the case
when the type ODR map contains a definition for the same composite type
because it was used in the importing module (buildODRType will
automatically use the existing definition and not create a type
declaration).

For Chromium built with -g2, this reduces the aggregate size of the
generated native object files by 66% (from 31G to 10G). It reduced
the time through the ThinLTO link and backend phases by about 20% on
my machine.

Reviewers: mehdi_amini, dblaikie, aprantl

Subscribers: llvm-commits

Differential Revision: https://reviews.llvm.org/D27775

llvm-svn: 289993
diff --git a/llvm/lib/Bitcode/Reader/BitcodeReader.cpp b/llvm/lib/Bitcode/Reader/BitcodeReader.cpp
index ad4d9ce..4812b2c 100644
--- a/llvm/lib/Bitcode/Reader/BitcodeReader.cpp
+++ b/llvm/lib/Bitcode/Reader/BitcodeReader.cpp
@@ -488,7 +488,8 @@
 
   /// \brief Main interface to parsing a bitcode buffer.
   /// \returns true if an error occurred.
-  Error parseBitcodeInto(Module *M, bool ShouldLazyLoadMetadata = false);
+  Error parseBitcodeInto(Module *M, bool ShouldLazyLoadMetadata = false,
+                         bool IsImporting = false);
 
   static uint64_t decodeSignRotatedValue(uint64_t V);
 
@@ -3084,9 +3085,10 @@
   }
 }
 
-Error BitcodeReader::parseBitcodeInto(Module *M, bool ShouldLazyLoadMetadata) {
+Error BitcodeReader::parseBitcodeInto(Module *M, bool ShouldLazyLoadMetadata,
+                                      bool IsImporting) {
   TheModule = M;
-  MDLoader = MetadataLoader(Stream, *M, ValueList,
+  MDLoader = MetadataLoader(Stream, *M, ValueList, IsImporting,
                             [&](unsigned ID) { return getTypeByID(ID); });
   return parseModule(0, ShouldLazyLoadMetadata);
 }
@@ -5220,7 +5222,7 @@
 /// everything.
 Expected<std::unique_ptr<Module>>
 BitcodeModule::getModuleImpl(LLVMContext &Context, bool MaterializeAll,
-                             bool ShouldLazyLoadMetadata) {
+                             bool ShouldLazyLoadMetadata, bool IsImporting) {
   BitstreamCursor Stream(Buffer);
 
   std::string ProducerIdentification;
@@ -5243,7 +5245,8 @@
   M->setMaterializer(R);
 
   // Delay parsing Metadata if ShouldLazyLoadMetadata is true.
-  if (Error Err = R->parseBitcodeInto(M.get(), ShouldLazyLoadMetadata))
+  if (Error Err =
+          R->parseBitcodeInto(M.get(), ShouldLazyLoadMetadata, IsImporting))
     return std::move(Err);
 
   if (MaterializeAll) {
@@ -5259,9 +5262,9 @@
 }
 
 Expected<std::unique_ptr<Module>>
-BitcodeModule::getLazyModule(LLVMContext &Context,
-                             bool ShouldLazyLoadMetadata) {
-  return getModuleImpl(Context, false, ShouldLazyLoadMetadata);
+BitcodeModule::getLazyModule(LLVMContext &Context, bool ShouldLazyLoadMetadata,
+                             bool IsImporting) {
+  return getModuleImpl(Context, false, ShouldLazyLoadMetadata, IsImporting);
 }
 
 // Parse the specified bitcode buffer, returning the function info index.
@@ -5323,20 +5326,20 @@
 }
 
 Expected<std::unique_ptr<Module>>
-llvm::getLazyBitcodeModule(MemoryBufferRef Buffer,
-                           LLVMContext &Context, bool ShouldLazyLoadMetadata) {
+llvm::getLazyBitcodeModule(MemoryBufferRef Buffer, LLVMContext &Context,
+                           bool ShouldLazyLoadMetadata, bool IsImporting) {
   Expected<BitcodeModule> BM = getSingleModule(Buffer);
   if (!BM)
     return BM.takeError();
 
-  return BM->getLazyModule(Context, ShouldLazyLoadMetadata);
+  return BM->getLazyModule(Context, ShouldLazyLoadMetadata, IsImporting);
 }
 
-Expected<std::unique_ptr<Module>>
-llvm::getOwningLazyBitcodeModule(std::unique_ptr<MemoryBuffer> &&Buffer,
-                                 LLVMContext &Context,
-                                 bool ShouldLazyLoadMetadata) {
-  auto MOrErr = getLazyBitcodeModule(*Buffer, Context, ShouldLazyLoadMetadata);
+Expected<std::unique_ptr<Module>> llvm::getOwningLazyBitcodeModule(
+    std::unique_ptr<MemoryBuffer> &&Buffer, LLVMContext &Context,
+    bool ShouldLazyLoadMetadata, bool IsImporting) {
+  auto MOrErr = getLazyBitcodeModule(*Buffer, Context, ShouldLazyLoadMetadata,
+                                     IsImporting);
   if (MOrErr)
     (*MOrErr)->setOwnedMemoryBuffer(std::move(Buffer));
   return MOrErr;
@@ -5344,7 +5347,7 @@
 
 Expected<std::unique_ptr<Module>>
 BitcodeModule::parseModule(LLVMContext &Context) {
-  return getModuleImpl(Context, true, false);
+  return getModuleImpl(Context, true, false, false);
   // TODO: Restore the use-lists to the in-memory state when the bitcode was
   // written.  We must defer until the Module has been fully materialized.
 }
diff --git a/llvm/lib/Bitcode/Reader/MetadataLoader.cpp b/llvm/lib/Bitcode/Reader/MetadataLoader.cpp
index 1e28411d..eb24ac6 100644
--- a/llvm/lib/Bitcode/Reader/MetadataLoader.cpp
+++ b/llvm/lib/Bitcode/Reader/MetadataLoader.cpp
@@ -86,6 +86,12 @@
 
 using namespace llvm;
 
+/// Flag whether we need to import full type definitions for ThinLTO.
+/// Currently needed for Darwin and LLDB.
+static cl::opt<bool> ImportFullTypeDefinitions(
+    "import-full-type-definitions", cl::init(false), cl::Hidden,
+    cl::desc("Import full type definitions for ThinLTO."));
+
 namespace {
 
 static int64_t unrotateSign(uint64_t U) { return U & 1 ? ~(U >> 1) : U >> 1; }
@@ -399,7 +405,7 @@
         Stream(Stream), Context(TheModule.getContext()), TheModule(TheModule),
         getTypeByID(getTypeByID) {}
 
-  Error parseMetadata(bool ModuleLevel);
+  Error parseMetadata(bool ModuleLevel, bool IsImporting);
 
   bool hasFwdRefs() const { return MetadataList.hasFwdRefs(); }
   Metadata *getMetadataFwdRef(unsigned Idx) {
@@ -435,7 +441,8 @@
 
 /// Parse a METADATA_BLOCK. If ModuleLevel is true then we are parsing
 /// module level metadata.
-Error MetadataLoader::MetadataLoaderImpl::parseMetadata(bool ModuleLevel) {
+Error MetadataLoader::MetadataLoaderImpl::parseMetadata(bool ModuleLevel,
+                                                        bool IsImporting) {
   if (!ModuleLevel && MetadataList.hasFwdRefs())
     return error("Invalid metadata: fwd refs into function blocks");
 
@@ -709,18 +716,38 @@
       Metadata *File = getMDOrNull(Record[3]);
       unsigned Line = Record[4];
       Metadata *Scope = getDITypeRefOrNull(Record[5]);
-      Metadata *BaseType = getDITypeRefOrNull(Record[6]);
+      Metadata *BaseType = nullptr;
       uint64_t SizeInBits = Record[7];
       if (Record[8] > (uint64_t)std::numeric_limits<uint32_t>::max())
         return error("Alignment value is too large");
       uint32_t AlignInBits = Record[8];
-      uint64_t OffsetInBits = Record[9];
+      uint64_t OffsetInBits = 0;
       DINode::DIFlags Flags = static_cast<DINode::DIFlags>(Record[10]);
-      Metadata *Elements = getMDOrNull(Record[11]);
+      Metadata *Elements = nullptr;
       unsigned RuntimeLang = Record[12];
-      Metadata *VTableHolder = getDITypeRefOrNull(Record[13]);
-      Metadata *TemplateParams = getMDOrNull(Record[14]);
+      Metadata *VTableHolder = nullptr;
+      Metadata *TemplateParams = nullptr;
       auto *Identifier = getMDString(Record[15]);
+      // If this module is being parsed so that it can be ThinLTO imported
+      // into another module, composite types only need to be imported
+      // as type declarations (unless full type definitions requested).
+      // Create type declarations up front to save memory. Also, buildODRType
+      // handles the case where this is type ODRed with a definition needed
+      // by the importing module, in which case the existing definition is
+      // used.
+      if (IsImporting && !ImportFullTypeDefinitions &&
+          (Tag == dwarf::DW_TAG_enumeration_type ||
+           Tag == dwarf::DW_TAG_class_type ||
+           Tag == dwarf::DW_TAG_structure_type ||
+           Tag == dwarf::DW_TAG_union_type)) {
+        Flags = Flags | DINode::FlagFwdDecl;
+      } else {
+        BaseType = getDITypeRefOrNull(Record[6]);
+        OffsetInBits = Record[9];
+        Elements = getMDOrNull(Record[11]);
+        VTableHolder = getDITypeRefOrNull(Record[13]);
+        TemplateParams = getMDOrNull(Record[14]);
+      }
       DICompositeType *CT = nullptr;
       if (Identifier)
         CT = DICompositeType::buildODRType(
@@ -1281,17 +1308,19 @@
   return *this;
 }
 MetadataLoader::MetadataLoader(MetadataLoader &&RHS)
-    : Pimpl(std::move(RHS.Pimpl)) {}
+    : Pimpl(std::move(RHS.Pimpl)), IsImporting(RHS.IsImporting) {}
 
 MetadataLoader::~MetadataLoader() = default;
 MetadataLoader::MetadataLoader(BitstreamCursor &Stream, Module &TheModule,
                                BitcodeReaderValueList &ValueList,
+                               bool IsImporting,
                                std::function<Type *(unsigned)> getTypeByID)
     : Pimpl(llvm::make_unique<MetadataLoaderImpl>(Stream, TheModule, ValueList,
-                                                  getTypeByID)) {}
+                                                  getTypeByID)),
+      IsImporting(IsImporting) {}
 
 Error MetadataLoader::parseMetadata(bool ModuleLevel) {
-  return Pimpl->parseMetadata(ModuleLevel);
+  return Pimpl->parseMetadata(ModuleLevel, IsImporting);
 }
 
 bool MetadataLoader::hasFwdRefs() const { return Pimpl->hasFwdRefs(); }
diff --git a/llvm/lib/Bitcode/Reader/MetadataLoader.h b/llvm/lib/Bitcode/Reader/MetadataLoader.h
index 7d1027e..351857e 100644
--- a/llvm/lib/Bitcode/Reader/MetadataLoader.h
+++ b/llvm/lib/Bitcode/Reader/MetadataLoader.h
@@ -36,12 +36,14 @@
 class MetadataLoader {
   class MetadataLoaderImpl;
   std::unique_ptr<MetadataLoaderImpl> Pimpl;
+  /// True if metadata is being parsed for a module being ThinLTO imported.
+  bool IsImporting = false;
   Error parseMetadata(bool ModuleLevel);
 
 public:
   ~MetadataLoader();
   MetadataLoader(BitstreamCursor &Stream, Module &TheModule,
-                 BitcodeReaderValueList &ValueList,
+                 BitcodeReaderValueList &ValueList, bool IsImporting,
                  std::function<Type *(unsigned)> getTypeByID);
   MetadataLoader &operator=(MetadataLoader &&);
   MetadataLoader(MetadataLoader &&);