[lld] 9b61fff - Revert D120626 "[ELF] Move section assignment from initializeSymbols to postParse"
Fangrui Song via llvm-commits
llvm-commits at lists.llvm.org
Tue Mar 15 19:01:00 PDT 2022
Author: Fangrui Song
Date: 2022-03-15T19:00:54-07:00
New Revision: 9b61fff0eb9332553ecdbe00e01e3d08ad392768
URL: https://github.com/llvm/llvm-project/commit/9b61fff0eb9332553ecdbe00e01e3d08ad392768
DIFF: https://github.com/llvm/llvm-project/commit/9b61fff0eb9332553ecdbe00e01e3d08ad392768.diff
LOG: Revert D120626 "[ELF] Move section assignment from initializeSymbols to postParse"
This reverts commit c30e6447c0225f675773d07f2b6d4e3c2b962155.
It exposed brittle support for __x86.get_pc_thunk.bx.
Need to think a bit how to support __x86.get_pc_thunk.bx.
Added:
lld/test/ELF/Inputs/i386-linkonce.s
Modified:
lld/ELF/Config.h
lld/ELF/Driver.cpp
lld/ELF/InputFiles.cpp
lld/ELF/Relocations.cpp
lld/ELF/Symbols.cpp
lld/ELF/Symbols.h
lld/docs/ReleaseNotes.rst
lld/test/ELF/comdat-binding.s
lld/test/ELF/comdat-discarded-error.s
lld/test/ELF/comdat-discarded-lazy.s
lld/test/ELF/duplicated-synthetic-sym.s
lld/test/ELF/exclude-discarded-error2.s
lld/test/ELF/lto/comdat-mixed-archive.test
Removed:
################################################################################
diff --git a/lld/ELF/Config.h b/lld/ELF/Config.h
index 82aa217af089a..7b7265c786724 100644
--- a/lld/ELF/Config.h
+++ b/lld/ELF/Config.h
@@ -30,7 +30,6 @@ namespace elf {
class InputFile;
class InputSectionBase;
-class Symbol;
enum ELFKind : uint8_t {
ELFNoneKind,
@@ -350,24 +349,6 @@ struct Configuration {
// The only instance of Configuration struct.
extern std::unique_ptr<Configuration> config;
-struct DuplicateSymbol {
- const Symbol *sym;
- const InputFile *file;
- InputSectionBase *section;
- uint64_t value;
-};
-
-struct Ctx {
- // Duplicate symbol candidates.
- SmallVector<DuplicateSymbol, 0> duplicates;
- // Symbols in a non-prevailing COMDAT group which should be changed to an
- // Undefined.
- SmallVector<std::pair<Symbol *, unsigned>, 0> nonPrevailingSyms;
-};
-
-// The only instance of Ctx struct.
-extern std::unique_ptr<Ctx> ctx;
-
// The first two elements of versionDefinitions represent VER_NDX_LOCAL and
// VER_NDX_GLOBAL. This helper returns other elements.
static inline ArrayRef<VersionDefinition> namedVersionDefs() {
diff --git a/lld/ELF/Driver.cpp b/lld/ELF/Driver.cpp
index 72eff694acb10..9f5d550e2c297 100644
--- a/lld/ELF/Driver.cpp
+++ b/lld/ELF/Driver.cpp
@@ -74,7 +74,6 @@ using namespace lld;
using namespace lld::elf;
std::unique_ptr<Configuration> elf::config;
-std::unique_ptr<Ctx> elf::ctx;
std::unique_ptr<LinkerDriver> elf::driver;
static void setConfigs(opt::InputArgList &args);
@@ -118,7 +117,6 @@ bool elf::link(ArrayRef<const char *> args, llvm::raw_ostream &stdoutOS,
"--error-limit=0 to see all errors)";
config = std::make_unique<Configuration>();
- elf::ctx = std::make_unique<Ctx>();
driver = std::make_unique<LinkerDriver>();
script = std::make_unique<LinkerScript>();
symtab = std::make_unique<SymbolTable>();
@@ -2488,20 +2486,6 @@ void LinkerDriver::link(opt::InputArgList &args) {
parallelForEach(objectFiles, initializeLocalSymbols);
parallelForEach(objectFiles, postParseObjectFile);
parallelForEach(bitcodeFiles, [](BitcodeFile *file) { file->postParse(); });
- for (auto &it : ctx->nonPrevailingSyms) {
- Symbol &sym = *it.first;
- // See the FIXME in InputFiles.cpp.
- if (sym.getName() == "__x86.get_pc_thunk.bx" ||
- sym.getName() == "__i686.get_pc_thunk.bx")
- continue;
- sym.replace(Undefined{sym.file, sym.getName(), sym.binding, sym.stOther,
- sym.type, it.second});
- cast<Undefined>(sym).nonPrevailing = true;
- }
- ctx->nonPrevailingSyms.clear();
- for (const DuplicateSymbol &d : ctx->duplicates)
- reportDuplicate(*d.sym, d.file, d.section, d.value);
- ctx->duplicates.clear();
// Return if there were name resolution errors.
if (errorCount())
@@ -2574,8 +2558,6 @@ void LinkerDriver::link(opt::InputArgList &args) {
auto newObjectFiles = makeArrayRef(objectFiles).slice(numObjsBeforeLTO);
parallelForEach(newObjectFiles, initializeLocalSymbols);
parallelForEach(newObjectFiles, postParseObjectFile);
- for (const DuplicateSymbol &d : ctx->duplicates)
- reportDuplicate(*d.sym, d.file, d.section, d.value);
// Handle --exclude-libs again because lto.tmp may reference additional
// libcalls symbols defined in an excluded archive. This may override
diff --git a/lld/ELF/InputFiles.cpp b/lld/ELF/InputFiles.cpp
index 778258ff18c8c..0df7552dd658e 100644
--- a/lld/ELF/InputFiles.cpp
+++ b/lld/ELF/InputFiles.cpp
@@ -1016,6 +1016,7 @@ InputSectionBase *ObjFile<ELFT>::createInputSection(uint32_t idx,
// its corresponding ELF symbol table.
template <class ELFT>
void ObjFile<ELFT>::initializeSymbols(const object::ELFFile<ELFT> &obj) {
+ ArrayRef<InputSectionBase *> sections(this->sections);
SymbolTable &symtab = *elf::symtab;
ArrayRef<Elf_Sym> eSyms = this->getELFSyms<ELFT>();
@@ -1043,6 +1044,13 @@ void ObjFile<ELFT>::initializeSymbols(const object::ELFFile<ELFT> &obj) {
continue;
}
+ if (LLVM_UNLIKELY(secIdx == SHN_XINDEX))
+ secIdx = check(getExtendedSymbolTableIndex<ELFT>(eSym, i, shndxTable));
+ else if (secIdx >= SHN_LORESERVE)
+ secIdx = 0;
+ if (LLVM_UNLIKELY(secIdx >= sections.size()))
+ fatal(toString(this) + ": invalid section index: " + Twine(secIdx));
+ InputSectionBase *sec = sections[secIdx];
uint8_t stOther = eSym.st_other;
uint8_t type = eSym.getType();
uint64_t value = eSym.st_value;
@@ -1060,11 +1068,30 @@ void ObjFile<ELFT>::initializeSymbols(const object::ELFFile<ELFT> &obj) {
continue;
}
- // Handle global defined symbols. Defined::section will be set in postParse.
+ // If a defined symbol is in a discarded section, handle it as if it
+ // were an undefined symbol. Such symbol doesn't comply with the
+ // standard, but in practice, a .eh_frame often directly refer
+ // COMDAT member sections, and if a comdat group is discarded, some
+ // defined symbol in a .eh_frame becomes dangling symbols.
+ if (sec == &InputSection::discarded) {
+ Undefined und{this, StringRef(), binding, stOther, type, secIdx};
+ // !LazyObjFile::lazy indicates that the file containing this object has
+ // not finished processing, i.e. this symbol is a result of a lazy symbol
+ // extract. We should demote the lazy symbol to an Undefined so that any
+ // relocations outside of the group to it will trigger a discarded section
+ // error.
+ if (sym->symbolKind == Symbol::LazyObjectKind && !sym->file->lazy)
+ sym->replace(und);
+ else
+ sym->resolve(und);
+ continue;
+ }
+
+ // Handle global defined symbols.
if (binding == STB_GLOBAL || binding == STB_WEAK ||
binding == STB_GNU_UNIQUE) {
- sym->resolve(Defined{this, StringRef(), binding, stOther, type, value,
- size, nullptr});
+ sym->resolve(
+ Defined{this, StringRef(), binding, stOther, type, value, size, sec});
continue;
}
@@ -1129,12 +1156,10 @@ template <class ELFT> void ObjFile<ELFT>::initializeLocalSymbols() {
// Called after all ObjFile::parse is called for all ObjFiles. This checks
// duplicate symbols and may do symbol property merge in the future.
template <class ELFT> void ObjFile<ELFT>::postParse() {
- static std::mutex mu;
ArrayRef<Elf_Sym> eSyms = this->getELFSyms<ELFT>();
for (size_t i = firstGlobal, end = eSyms.size(); i != end; ++i) {
const Elf_Sym &eSym = eSyms[i];
- Symbol &sym = *symbols[i];
- uint32_t secIdx = eSym.st_shndx;
+ const Symbol &sym = *symbols[i];
// st_value of STT_TLS represents the assigned offset, not the actual
// address which is used by STT_FUNC and STT_OBJECT. STT_TLS symbols can
@@ -1145,41 +1170,23 @@ template <class ELFT> void ObjFile<ELFT>::postParse() {
errorOrWarn("TLS attribute mismatch: " + toString(sym) + "\n>>> in " +
toString(sym.file) + "\n>>> in " + toString(this));
- // Handle non-COMMON defined symbol below. !sym.file allows a symbol
- // assignment to redefine a symbol without an error.
- if (!sym.file || !sym.isDefined() || secIdx == SHN_UNDEF ||
- secIdx == SHN_COMMON)
+ // !sym.file allows a symbol assignment redefines a symbol without an error.
+ if (sym.file == this || !sym.file || !sym.isDefined() ||
+ eSym.st_shndx == SHN_UNDEF || eSym.st_shndx == SHN_COMMON ||
+ eSym.getBinding() == STB_WEAK)
continue;
-
+ uint32_t secIdx = eSym.st_shndx;
if (LLVM_UNLIKELY(secIdx == SHN_XINDEX))
- secIdx = check(getExtendedSymbolTableIndex<ELFT>(eSym, i, shndxTable));
+ secIdx = cantFail(getExtendedSymbolTableIndex<ELFT>(eSym, i, shndxTable));
else if (secIdx >= SHN_LORESERVE)
secIdx = 0;
- if (LLVM_UNLIKELY(secIdx >= sections.size()))
- fatal(toString(this) + ": invalid section index: " + Twine(secIdx));
- InputSectionBase *sec = sections[secIdx];
- if (sec == &InputSection::discarded) {
- if (sym.traced) {
- printTraceSymbol(Undefined{this, sym.getName(), sym.binding,
- sym.stOther, sym.type, secIdx},
- sym.getName());
- }
- if (sym.file == this) {
- std::lock_guard<std::mutex> lock(mu);
- ctx->nonPrevailingSyms.emplace_back(&sym, secIdx);
- }
- continue;
- }
-
- if (sym.file == this) {
- cast<Defined>(sym).section = sec;
+ if (sections[secIdx] == &InputSection::discarded)
continue;
- }
-
- if (eSym.getBinding() == STB_WEAK)
+ // Allow absolute symbols with the same value for GNU ld compatibility.
+ if (!cast<Defined>(sym).section && !sections[secIdx] &&
+ cast<Defined>(sym).value == eSym.st_value)
continue;
- std::lock_guard<std::mutex> lock(mu);
- ctx->duplicates.push_back({&sym, this, sec, eSym.st_value});
+ reportDuplicate(sym, this, sections[secIdx], eSym.st_value);
}
}
diff --git a/lld/ELF/Relocations.cpp b/lld/ELF/Relocations.cpp
index ed1f2168170d8..fa4547555b968 100644
--- a/lld/ELF/Relocations.cpp
+++ b/lld/ELF/Relocations.cpp
@@ -545,16 +545,9 @@ static std::string maybeReportDiscarded(Undefined &sym) {
// If the discarded section is a COMDAT.
StringRef signature = file->getShtGroupSignature(objSections, elfSec);
if (const InputFile *prevailing =
- symtab->comdatGroups.lookup(CachedHashStringRef(signature))) {
+ symtab->comdatGroups.lookup(CachedHashStringRef(signature)))
msg += "\n>>> section group signature: " + signature.str() +
"\n>>> prevailing definition is in " + toString(prevailing);
- if (sym.nonPrevailing) {
- msg += "\n>>> or the symbol in the prevailing group had STB_WEAK "
- "binding and the symbol in a non-prevailing group had STB_GLOBAL "
- "binding. Mixing groups with STB_WEAK and STB_GLOBAL binding "
- "signature is not supported";
- }
- }
return msg;
}
diff --git a/lld/ELF/Symbols.cpp b/lld/ELF/Symbols.cpp
index ffd5469b4550e..e8ead0d81cbd0 100644
--- a/lld/ELF/Symbols.cpp
+++ b/lld/ELF/Symbols.cpp
@@ -537,20 +537,11 @@ bool Symbol::shouldReplace(const Defined &other) const {
return !isGlobal() && other.isGlobal();
}
-void elf::reportDuplicate(const Symbol &sym, const InputFile *newFile,
+void elf::reportDuplicate(const Symbol &sym, InputFile *newFile,
InputSectionBase *errSec, uint64_t errOffset) {
if (config->allowMultipleDefinition)
return;
- // A definition in a COMDAT may be reported as duplicate with a definition
- // relative to .gnu.linkonce.t.__x86.get_pc_thunk.bx.
- // .gnu.linkonce.t.__x86.get_pc_thunk.bx will be discarded, so there is
- // actually no duplicate.
- const Defined *d = dyn_cast<Defined>(&sym);
- if (!d || d->getName() == "__x86.get_pc_thunk.bx")
- return;
- // Allow absolute symbols with the same value for GNU ld compatibility.
- if (!d->section && !errSec && errOffset && d->value == errOffset)
- return;
+ const Defined *d = cast<Defined>(&sym);
if (!d->section || !errSec) {
error("duplicate symbol: " + toString(sym) + "\n>>> defined in " +
toString(sym.file) + "\n>>> defined in " + toString(newFile));
diff --git a/lld/ELF/Symbols.h b/lld/ELF/Symbols.h
index 766c89b259a42..cd0e54ddf3d30 100644
--- a/lld/ELF/Symbols.h
+++ b/lld/ELF/Symbols.h
@@ -377,7 +377,6 @@ class Undefined : public Symbol {
// The section index if in a discarded section, 0 otherwise.
uint32_t discardedSecIdx;
- bool nonPrevailing = false;
};
class SharedSymbol : public Symbol {
@@ -559,7 +558,7 @@ template <typename... T> Defined *makeDefined(T &&...args) {
Defined(std::forward<T>(args)...);
}
-void reportDuplicate(const Symbol &sym, const InputFile *newFile,
+void reportDuplicate(const Symbol &sym, InputFile *newFile,
InputSectionBase *errSec, uint64_t errOffset);
void maybeWarnUnorderableSymbol(const Symbol *sym);
bool computeIsPreemptible(const Symbol &sym);
diff --git a/lld/docs/ReleaseNotes.rst b/lld/docs/ReleaseNotes.rst
index dc74b8abd17b3..5878cddb4c88a 100644
--- a/lld/docs/ReleaseNotes.rst
+++ b/lld/docs/ReleaseNotes.rst
@@ -35,8 +35,6 @@ Breaking changes
* The GNU ld incompatible ``--no-define-common`` has been removed.
* The obscure ``-dc``/``-dp`` options have been removed.
* ``-d`` is now ignored.
-* If a prevailing COMDAT group defines STB_WEAK symbol, having a STB_GLOBAL symbol in a non-prevailing group is now rejected with a diagnostic.
- (`D120626 <https://reviews.llvm.org/D120626>`_)
COFF Improvements
-----------------
diff --git a/lld/test/ELF/Inputs/i386-linkonce.s b/lld/test/ELF/Inputs/i386-linkonce.s
new file mode 100644
index 0000000000000..b5906cc79657f
--- /dev/null
+++ b/lld/test/ELF/Inputs/i386-linkonce.s
@@ -0,0 +1,11 @@
+.section .gnu.linkonce.t.__i686.get_pc_thunk.bx
+.global __i686.get_pc_thunk.bx
+__i686.get_pc_thunk.bx:
+ mov (%esp),%ebx
+ ret
+
+.section .text
+.global _strchr1
+_strchr1:
+ call __i686.get_pc_thunk.bx
+ ret
diff --git a/lld/test/ELF/comdat-binding.s b/lld/test/ELF/comdat-binding.s
index 139a1db7e3fbd..6c6fd89b57495 100644
--- a/lld/test/ELF/comdat-binding.s
+++ b/lld/test/ELF/comdat-binding.s
@@ -8,22 +8,11 @@
# RUN: ld.lld -e 0 %t/u.o %t/w.o -o %t/u
# RUN: llvm-readelf -s %t/u | FileCheck %s --check-prefix=UNIQUE
-## We prefer STB_GLOBAL definition, then changing it to undefined since it is in
-## in a non-prevailing COMDAT. Ideally this should be defined, but our behavior
-## is fine because valid input cannot form this case.
-# RUN: ld.lld -e 0 %t/w.o %t/g.o -o %t/und --noinhibit-exec 2>&1 | FileCheck %s --check-prefix=WARN
-# RUN: llvm-readelf -s %t/und | FileCheck %s --check-prefix=UND
+# RUN: ld.lld -e 0 %t/w.o %t/g.o -o %t/w
+# RUN: llvm-readelf -s %t/w | FileCheck %s --check-prefix=WEAK
# WEAK: NOTYPE WEAK DEFAULT [[#]] _ZZ1fvE1x
# UNIQUE: OBJECT UNIQUE DEFAULT [[#]] _ZZ1fvE1x
-# UND: NOTYPE GLOBAL DEFAULT UND _ZZ1fvE1x
-
-# WARN: warning: relocation refers to a symbol in a discarded section: f()::x
-# WARN-NEXT: >>> defined in {{.*}}g.o
-# WARN-NEXT: >>> section group signature: _ZZ1fvE1x
-# WARN-NEXT: >>> prevailing definition is in {{.*}}w.o
-# WARN-NEXT: >>> or the symbol in the prevailing group had STB_WEAK binding and the symbol in a non-prevailing group had STB_GLOBAL binding. Mixing groups with STB_WEAK and STB_GLOBAL binding signature is not supported
-# WARN-NEXT: >>> referenced by {{.*}}g.o:(.text+0x3)
#--- g.s
movq _ZZ1fvE1x at gotpcrel(%rip), %rax
diff --git a/lld/test/ELF/comdat-discarded-error.s b/lld/test/ELF/comdat-discarded-error.s
index dec927d32f39d..b818b15d5efff 100644
--- a/lld/test/ELF/comdat-discarded-error.s
+++ b/lld/test/ELF/comdat-discarded-error.s
@@ -11,7 +11,6 @@
# CHECK-NEXT: >>> defined in {{.*}}3.o
# CHECK-NEXT: >>> section group signature: foo
# CHECK-NEXT: >>> prevailing definition is in {{.*}}2.o
-# CHECK-NEXT: >>> or the symbol in the prevailing group {{.*}}
# CHECK-NEXT: >>> referenced by {{.*}}1.o:(.text+0x1)
# CHECK: error: relocation refers to a discarded section: .text.foo
diff --git a/lld/test/ELF/comdat-discarded-lazy.s b/lld/test/ELF/comdat-discarded-lazy.s
index 23b922abb8d6a..e19e4b24fa8ed 100644
--- a/lld/test/ELF/comdat-discarded-lazy.s
+++ b/lld/test/ELF/comdat-discarded-lazy.s
@@ -26,7 +26,6 @@
# AA-NEXT: >>> defined in {{.*}}aa.o
# AA-NEXT: >>> section group signature: foo
# AA-NEXT: >>> prevailing definition is in {{.*}}1.o
-# AA-NEXT: >>> or the symbol in the prevailing group {{.*}}
# AA-NEXT: >>> referenced by {{.*}}aa.o:(.text+0x1)
## Test the case when the symbol causing a "discarded section" is ordered
@@ -46,15 +45,14 @@
# ZZ-NEXT: >>> defined in {{.*}}zz.o
# ZZ-NEXT: >>> section group signature: foo
# ZZ-NEXT: >>> prevailing definition is in {{.*}}1.o
-# ZZ-NEXT: >>> or the symbol in the prevailing group {{.*}}
# ZZ-NEXT: >>> referenced by {{.*}}zz.o:(.text+0x1)
-## The definition in %tdef.o is outside a group. Currently we give an error
-## because %tdef.o is not extracted.
+## Don't error if the symbol which would cause "discarded section"
+## was inserted before %tzz.o
# RUN: echo '.globl zz; zz:' | llvm-mc -filetype=obj -triple=x86_64 - -o %tdef.o
-# RUN: not ld.lld %t.o --start-lib %t1.o %tdef.o %tzz.o --end-lib -o /dev/null 2>&1 | FileCheck --check-prefix=ZZ %s
+# RUN: ld.lld %t.o --start-lib %t1.o %tdef.o %tzz.o --end-lib -o /dev/null
# RUN: rm -f %tdef.a && llvm-ar rc %tdef.a %tdef.o
-# RUN: not ld.lld %t.o --start-lib %t1.o %tdef.a %tzz.o --end-lib -o /dev/null 2>&1 | FileCheck --check-prefix=ZZ %s
+# RUN: ld.lld %t.o --start-lib %t1.o %tdef.a %tzz.o --end-lib -o /dev/null
.globl _start
_start:
diff --git a/lld/test/ELF/duplicated-synthetic-sym.s b/lld/test/ELF/duplicated-synthetic-sym.s
index 0bf7800eb0ae3..64e71614e6d60 100644
--- a/lld/test/ELF/duplicated-synthetic-sym.s
+++ b/lld/test/ELF/duplicated-synthetic-sym.s
@@ -9,8 +9,8 @@
// RUN: not ld.lld %t.o --format binary file.bin -o /dev/null 2>&1 | FileCheck %s
// CHECK: duplicate symbol: _binary_file_bin_start
-// CHECK-NEXT: defined in {{.*}}.o
-// CHECK-NEXT: defined in <internal>
+// CHECK-NEXT: defined at {{.*}}.o:(.text+0x0)
+// CHECK-NEXT: defined at file.bin:(.data+0x0)
.globl _binary_file_bin_start
_binary_file_bin_start:
diff --git a/lld/test/ELF/exclude-discarded-error2.s b/lld/test/ELF/exclude-discarded-error2.s
index cda7bc7150fb6..5474cfe8170fe 100644
--- a/lld/test/ELF/exclude-discarded-error2.s
+++ b/lld/test/ELF/exclude-discarded-error2.s
@@ -1,14 +1,14 @@
# REQUIRES: x86
# RUN: llvm-mc -filetype=obj -triple=x86_64 %s -o %t.o
-# RUN: echo '.section .foo,"aG", at progbits,zz,comdat; .weak foo; foo:' | \
+# RUN: echo '.section .foo,"ae"; .weak foo; foo:' | \
# RUN: llvm-mc -filetype=obj -triple=x86_64 - -o %t1.o
-# RUN: ld.lld %t.o %t1.o -o %t
-# RUN: llvm-readelf -s %t | FileCheck %s
+# RUN: not ld.lld %t.o %t1.o -o /dev/null 2>&1 | FileCheck %s
-# CHECK: NOTYPE WEAK DEFAULT UND foo
+# Because foo defined in %t1.o is weak, it does not override global undefined
+# in %t.o
+# CHECK-NOT: discarded section
+# CHECK: undefined symbol: foo
.globl _start
_start:
jmp foo
-
-.section .foo,"aG", at progbits,zz,comdat
diff --git a/lld/test/ELF/lto/comdat-mixed-archive.test b/lld/test/ELF/lto/comdat-mixed-archive.test
index d7ea4eb18c72e..ee644a8cbd983 100644
--- a/lld/test/ELF/lto/comdat-mixed-archive.test
+++ b/lld/test/ELF/lto/comdat-mixed-archive.test
@@ -31,8 +31,8 @@ TRACE: lib.a(obj.o): lazy definition of foo
TRACE-NEXT: lib.a(obj.o): lazy definition of bar
TRACE-NEXT: lib.a(bc.bc): definition of foo
TRACE-NEXT: lib.a(bc.bc): reference to bar
-TRACE-NEXT: lib.a(obj.o): definition of bar
TRACE-NEXT: lib.a(obj.o): reference to foo
+TRACE-NEXT: lib.a(obj.o): definition of bar
TRACE-NEXT: <internal>: reference to foo
;; The definition of "foo" is visible outside the LTO result.
TRACE-NEXT: lto.tmp: definition of foo
More information about the llvm-commits
mailing list