[lld-macho] Unset ExportDynamic where possible for LTO
authorJez Ng <jezng@fb.com>
Sat, 12 Feb 2022 03:24:09 +0000 (22:24 -0500)
committerJez Ng <jezng@fb.com>
Sat, 12 Feb 2022 03:26:19 +0000 (22:26 -0500)
By unsetting this property, we are now able to internalize more symbols
during LTO. I compared the output of `-save-temps` for both LLD and
ld64, and we now match ld64's behavior as far as `lto-internalize.ll` is
concerned.

(Thanks @smeenai for working on an initial version of this diff!)

Fixes https://github.com/llvm/llvm-project/issues/50574.

Reviewed By: #lld-macho, thakis

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

lld/MachO/Driver.cpp
lld/MachO/LTO.cpp
lld/test/MachO/lto-internalize.ll

index 636c401..790f31a 100644 (file)
@@ -505,14 +505,6 @@ static void initLLVM() {
 }
 
 static void compileBitcodeFiles() {
-  // FIXME: Remove this once LTO.cpp honors config->exportDynamic.
-  if (config->exportDynamic)
-    for (InputFile *file : inputFiles)
-      if (isa<BitcodeFile>(file)) {
-        warn("the effect of -export_dynamic on LTO is not yet implemented");
-        break;
-      }
-
   TimeTraceScope timeScope("LTO");
   auto *lto = make<BitcodeCompiler>();
   for (InputFile *file : inputFiles)
index fd49a09..c2863c7 100644 (file)
@@ -64,6 +64,8 @@ void BitcodeCompiler::add(BitcodeFile &f) {
   resols.reserve(objSyms.size());
 
   // Provide a resolution to the LTO API for each symbol.
+  bool exportDynamic =
+      config->outputType != MH_EXECUTE || config->exportDynamic;
   auto symIt = f.symbols.begin();
   for (const lto::InputFile::Symbol &objSym : objSyms) {
     resols.emplace_back();
@@ -77,12 +79,14 @@ void BitcodeCompiler::add(BitcodeFile &f) {
     // be removed.
     r.Prevailing = !objSym.isUndefined() && sym->getFile() == &f;
 
-    // FIXME: What about other output types? And we can probably be less
-    // restrictive with -flat_namespace, but it's an infrequent use case.
-    // FIXME: Honor config->exportDynamic.
-    r.VisibleToRegularObj = config->outputType != MH_EXECUTE ||
-                            config->namespaceKind == NamespaceKind::flat ||
-                            sym->isUsedInRegularObj;
+    if (const auto *defined = dyn_cast<Defined>(sym))
+      r.ExportDynamic =
+          defined->isExternal() && !defined->privateExtern && exportDynamic;
+    else if (const auto *common = dyn_cast<CommonSymbol>(sym))
+      r.ExportDynamic = !common->privateExtern && exportDynamic;
+
+    r.VisibleToRegularObj =
+        sym->isUsedInRegularObj || (r.Prevailing && r.ExportDynamic);
 
     // Un-define the symbol so that we don't get duplicate symbol errors when we
     // load the ObjFile emitted by LTO compilation.
index c9bac63..755484c 100644 (file)
@@ -12,6 +12,9 @@
 ; RUN: llvm-dis < %t/test.0.2.internalize.bc | FileCheck %s
 ; RUN: llvm-objdump --macho --syms %t/test | FileCheck %s --check-prefix=SYMTAB
 
+; CHECK: @comm = internal global
+; CHECK: @comm_hide = internal global
+
 ;; Check that main is not internalized. This covers the case of bitcode symbols
 ;; referenced by undefined symbols that don't belong to any InputFile.
 ; CHECK: define void @main()
 ;; internalized.
 ; CHECK: define internal void @baz()
 
-; Check foo and bar are not emitted to the .symtab
+;; Check that all internalized symbols are not emitted to the symtab
 ; SYMTAB-LABEL: SYMBOL TABLE:
-; SYMTAB-NEXT:  g     F __TEXT,__text _main
-; SYMTAB-NEXT:  g     F __TEXT,__text _used_in_regular_obj
-; SYMTAB-NEXT:  g     F __TEXT,__text __mh_execute_header
-; SYMTAB-NEXT:          *UND* dyld_stub_binder
+; SYMTAB-DAG:   g     F __TEXT,__text _main
+; SYMTAB-DAG:   g     F __TEXT,__text _used_in_regular_obj
+; SYMTAB-DAG:   g     F __TEXT,__text __mh_execute_header
+; SYMTAB-DAG:           *UND* dyld_stub_binder
 ; SYMTAB-EMPTY:
 
+; RUN: %lld -lSystem -dylib %t/test.o %t/baz.o %t/regular.o -o %t/test.dylib -save-temps
+; RUN: llvm-dis < %t/test.dylib.0.2.internalize.bc | FileCheck %s --check-prefix=DYN
+; RUN: llvm-nm -m %t/test.dylib | FileCheck %s --check-prefix=DYN-SYMS \
+; RUN:   --implicit-check-not _foo
+
+; RUN: %lld -lSystem -export_dynamic %t/test.o %t/baz.o %t/regular.o -o %t/test.extdyn -save-temps
+; RUN: llvm-dis < %t/test.extdyn.0.2.internalize.bc
+; RUN: llvm-nm -m %t/test.extdyn | FileCheck %s --check-prefix=DYN-SYMS \
+; RUN:   --implicit-check-not _foo
+
+;; Note that only foo() gets internalized here; everything else that isn't
+;; hidden must be exported.
+; DYN: @comm = common global
+; DYN: @comm_hide = internal global
+; DYN: define void @main()
+; DYN: define void @bar()
+; DYN: define internal void @foo()
+; DYN: define void @used_in_regular_obj()
+; DYN: define void @baz()
+
+; DYN-SYMS-DAG: (__TEXT,__text) external _bar
+; DYN-SYMS-DAG: (__TEXT,__text) external _baz
+; DYN-SYMS-DAG: (__DATA,__common) external _comm
+; DYN-SYMS-DAG: (__TEXT,__text) external _main
+; DYN-SYMS-DAG: (__TEXT,__text) external _used_in_regular_obj
+
 ;--- test.s
 target triple = "x86_64-apple-macosx10.15.0"
 target datalayout = "e-m:o-p270:32:32-p271:32:32-p272:64:64-i64:64-f80:128-n8:16:32:64-S128"
 
+;; Common symbols are always external.
+@comm = common global i8 0, align 1
+@comm_hide = common hidden global i8 0, align 1
+
 declare void @baz()
 
 define void @main() {