Skip to content

[LTO] Fix a crash with thin LTO caching and asm output #138203

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 2 commits into from
May 14, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
48 changes: 27 additions & 21 deletions llvm/lib/LTO/LTOBackend.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -439,27 +439,33 @@ static void codegen(const Config &Conf, TargetMachine *TM,
std::unique_ptr<CachedFileStream> &Stream = *StreamOrErr;
TM->Options.ObjectFilenameForDebug = Stream->ObjectPathName;

legacy::PassManager CodeGenPasses;
TargetLibraryInfoImpl TLII(Mod.getTargetTriple());
CodeGenPasses.add(new TargetLibraryInfoWrapperPass(TLII));
// No need to make index available if the module is empty.
// In theory these passes should not use the index for an empty
// module, however, this guards against doing any unnecessary summary-based
// analysis in the case of a ThinLTO build where this might be an empty
// regular LTO combined module, with a large combined index from ThinLTO.
if (!isEmptyModule(Mod))
CodeGenPasses.add(
createImmutableModuleSummaryIndexWrapperPass(&CombinedIndex));
if (Conf.PreCodeGenPassesHook)
Conf.PreCodeGenPassesHook(CodeGenPasses);
if (TM->addPassesToEmitFile(CodeGenPasses, *Stream->OS,
DwoOut ? &DwoOut->os() : nullptr,
Conf.CGFileType))
report_fatal_error("Failed to setup codegen");
CodeGenPasses.run(Mod);

if (DwoOut)
DwoOut->keep();
// Create the codegen pipeline in its own scope so it gets deleted before
// Stream->commit() is called. The commit function of CacheStream deletes
// the raw stream, which is too early as streamers (e.g. MCAsmStreamer)
// keep the pointer and may use it until their destruction. See #138194.
{
legacy::PassManager CodeGenPasses;
TargetLibraryInfoImpl TLII(Mod.getTargetTriple());
CodeGenPasses.add(new TargetLibraryInfoWrapperPass(TLII));
// No need to make index available if the module is empty.
// In theory these passes should not use the index for an empty
// module, however, this guards against doing any unnecessary summary-based
// analysis in the case of a ThinLTO build where this might be an empty
// regular LTO combined module, with a large combined index from ThinLTO.
if (!isEmptyModule(Mod))
CodeGenPasses.add(
createImmutableModuleSummaryIndexWrapperPass(&CombinedIndex));
if (Conf.PreCodeGenPassesHook)
Conf.PreCodeGenPassesHook(CodeGenPasses);
if (TM->addPassesToEmitFile(CodeGenPasses, *Stream->OS,
DwoOut ? &DwoOut->os() : nullptr,
Conf.CGFileType))
report_fatal_error("Failed to setup codegen");
CodeGenPasses.run(Mod);

if (DwoOut)
DwoOut->keep();
}

if (Error Err = Stream->commit())
report_fatal_error(std::move(Err));
Expand Down
15 changes: 15 additions & 0 deletions llvm/test/ThinLTO/X86/cache-emit-asm.ll
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
;; This test runs thin LTO with cache only to look for memory errors, either
;; as crashes or sanitizer errors. MCAsmStreamer has specific assumptions about
;; the lifetime of the output stream that are easy to overlook (see #138194).

; RUN: rm -rf %t.cache
; RUN: opt -module-hash -module-summary -thinlto-bc %s -o %t1.bc
; RUN: ld.lld --thinlto-cache-dir=%t.cache --lto-emit-asm %t1.bc

target datalayout = "e-m:e-i64:64-f80:128-n8:16:32:64-S128"
target triple = "x86_64-unknown-linux-gnu"

define void @globalfunc() {
entry:
ret void
}