[clang] [lld] [llvm] [llvm][lld][clang] Delay initializing TargetOptions in LTO builds until a Triple is available (PR #179509)

Paul Kirth via cfe-commits cfe-commits at lists.llvm.org
Wed Apr 8 10:49:38 PDT 2026


https://github.com/ilovepi updated https://github.com/llvm/llvm-project/pull/179509

>From fef2f7baa68ee834cf75befaad54efd70595af4b Mon Sep 17 00:00:00 2001
From: Paul Kirth <paulkirth at google.com>
Date: Tue, 3 Feb 2026 09:02:48 -0800
Subject: [PATCH 01/27] [llvm] Force TLSDESC for all TLS access in Fuchsia code

Fuchsia no longer supports TLS access via __tls_get_addr, and only
supports the TLSDESC ABI on all target machines.

Though we already set Fuchsia as enabling TLSDESC by default, LLD's LTO
pipeline is initialized with an empty target triple, and thus does not
correctly select the correct codegen options for Fuchsia's ABI. Instead,
we can additionally check if Fuchsia is the actual target if the option
isn't set, since useTLSDESC() is only called later, when a non-default
target triple will be available.

The alternative is to rework how LLD initializes the LTO code generation
options, so that it selects the correct target, and initializes them
correctly. However, that's a more invasive change, and would need some
discussion to make sure that is handled correctly across all of LLDs
supported formats (e.g. ELF, Mach-O, COFF, etc.).
---
 llvm/lib/Target/TargetMachine.cpp     |  4 +-
 llvm/test/CodeGen/RISCV/tls-models.ll | 69 +++++++++++++++++++++++++++
 llvm/test/CodeGen/X86/tls-desc.ll     | 43 +++++++++++++++++
 3 files changed, 115 insertions(+), 1 deletion(-)

diff --git a/llvm/lib/Target/TargetMachine.cpp b/llvm/lib/Target/TargetMachine.cpp
index 9243cdad43e1e..dbc4fb7a9e2af 100644
--- a/llvm/lib/Target/TargetMachine.cpp
+++ b/llvm/lib/Target/TargetMachine.cpp
@@ -255,7 +255,9 @@ bool TargetMachine::shouldAssumeDSOLocal(const GlobalValue *GV) const {
 }
 
 bool TargetMachine::useEmulatedTLS() const { return Options.EmulatedTLS; }
-bool TargetMachine::useTLSDESC() const { return Options.EnableTLSDESC; }
+bool TargetMachine::useTLSDESC() const {
+  return Options.EnableTLSDESC || TargetTriple.isOSFuchsia();
+}
 
 TLSModel::Model TargetMachine::getTLSModel(const GlobalValue *GV) const {
   bool IsPIE = GV->getParent()->getPIELevel() != PIELevel::Default;
diff --git a/llvm/test/CodeGen/RISCV/tls-models.ll b/llvm/test/CodeGen/RISCV/tls-models.ll
index 52c2c31d8fa81..8caa6108ffbe9 100644
--- a/llvm/test/CodeGen/RISCV/tls-models.ll
+++ b/llvm/test/CodeGen/RISCV/tls-models.ll
@@ -7,10 +7,13 @@
 ; RUN:     | FileCheck -check-prefix=RV64-PIC %s
 ; RUN: llc -mtriple=riscv64 -relocation-model=pic -enable-tlsdesc < %s \
 ; RUN:     | FileCheck -check-prefix=RV64-PIC-TLSDESC %s
+; RUN: llc -mtriple=riscv64-unknown-fuchsia -relocation-model=pic < %s \
+; RUN:     | FileCheck -check-prefix=RV64-PIC-FUCHSIA %s
 ; RUN: llc -mtriple=riscv32 < %s | FileCheck -check-prefix=RV32-NOPIC %s
 ; RUN: llc -mtriple=riscv32 < %s -enable-tlsdesc | FileCheck -check-prefix=RV32-NOPIC-TLSDESC %s
 ; RUN: llc -mtriple=riscv64 < %s | FileCheck -check-prefix=RV64-NOPIC %s
 ; RUN: llc -mtriple=riscv64 < %s -enable-tlsdesc | FileCheck -check-prefix=RV64-NOPIC-TLSDESC %s
+; RUN: llc -mtriple=riscv64-unknown-fuchsia < %s | FileCheck -check-prefix=RV64-NOPIC-FUCHSIA %s
 
 ; Check that TLS symbols are lowered correctly based on the specified
 ; model. Make sure they're external to avoid them all being optimised to Local
@@ -69,6 +72,16 @@ define ptr @f1() nounwind {
 ; RV64-PIC-TLSDESC-NEXT:    add a0, a0, tp
 ; RV64-PIC-TLSDESC-NEXT:    ret
 ;
+; RV64-PIC-FUCHSIA-LABEL: f1:
+; RV64-PIC-FUCHSIA:       # %bb.0: # %entry
+; RV64-PIC-FUCHSIA-NEXT:  .Ltlsdesc_hi0:
+; RV64-PIC-FUCHSIA-NEXT:    auipc a0, %tlsdesc_hi(unspecified)
+; RV64-PIC-FUCHSIA-NEXT:    ld a1, %tlsdesc_load_lo(.Ltlsdesc_hi0)(a0)
+; RV64-PIC-FUCHSIA-NEXT:    addi a0, a0, %tlsdesc_add_lo(.Ltlsdesc_hi0)
+; RV64-PIC-FUCHSIA-NEXT:    jalr t0, 0(a1), %tlsdesc_call(.Ltlsdesc_hi0)
+; RV64-PIC-FUCHSIA-NEXT:    add a0, a0, tp
+; RV64-PIC-FUCHSIA-NEXT:    ret
+;
 ; RV32-NOPIC-LABEL: f1:
 ; RV32-NOPIC:       # %bb.0: # %entry
 ; RV32-NOPIC-NEXT:  .Lpcrel_hi0:
@@ -100,6 +113,14 @@ define ptr @f1() nounwind {
 ; RV64-NOPIC-TLSDESC-NEXT:    ld a0, %pcrel_lo(.Lpcrel_hi0)(a0)
 ; RV64-NOPIC-TLSDESC-NEXT:    add a0, a0, tp
 ; RV64-NOPIC-TLSDESC-NEXT:    ret
+;
+; RV64-NOPIC-FUCHSIA-LABEL: f1:
+; RV64-NOPIC-FUCHSIA:       # %bb.0: # %entry
+; RV64-NOPIC-FUCHSIA-NEXT:  .Lpcrel_hi0:
+; RV64-NOPIC-FUCHSIA-NEXT:    auipc a0, %tls_ie_pcrel_hi(unspecified)
+; RV64-NOPIC-FUCHSIA-NEXT:    ld a0, %pcrel_lo(.Lpcrel_hi0)(a0)
+; RV64-NOPIC-FUCHSIA-NEXT:    add a0, a0, tp
+; RV64-NOPIC-FUCHSIA-NEXT:    ret
 entry:
   ret ptr @unspecified
 }
@@ -152,6 +173,16 @@ define ptr @f2() nounwind {
 ; RV64-PIC-TLSDESC-NEXT:    add a0, a0, tp
 ; RV64-PIC-TLSDESC-NEXT:    ret
 ;
+; RV64-PIC-FUCHSIA-LABEL: f2:
+; RV64-PIC-FUCHSIA:       # %bb.0: # %entry
+; RV64-PIC-FUCHSIA-NEXT:  .Ltlsdesc_hi1:
+; RV64-PIC-FUCHSIA-NEXT:    auipc a0, %tlsdesc_hi(ld)
+; RV64-PIC-FUCHSIA-NEXT:    ld a1, %tlsdesc_load_lo(.Ltlsdesc_hi1)(a0)
+; RV64-PIC-FUCHSIA-NEXT:    addi a0, a0, %tlsdesc_add_lo(.Ltlsdesc_hi1)
+; RV64-PIC-FUCHSIA-NEXT:    jalr t0, 0(a1), %tlsdesc_call(.Ltlsdesc_hi1)
+; RV64-PIC-FUCHSIA-NEXT:    add a0, a0, tp
+; RV64-PIC-FUCHSIA-NEXT:    ret
+;
 ; RV32-NOPIC-LABEL: f2:
 ; RV32-NOPIC:       # %bb.0: # %entry
 ; RV32-NOPIC-NEXT:  .Lpcrel_hi1:
@@ -183,6 +214,14 @@ define ptr @f2() nounwind {
 ; RV64-NOPIC-TLSDESC-NEXT:    ld a0, %pcrel_lo(.Lpcrel_hi1)(a0)
 ; RV64-NOPIC-TLSDESC-NEXT:    add a0, a0, tp
 ; RV64-NOPIC-TLSDESC-NEXT:    ret
+;
+; RV64-NOPIC-FUCHSIA-LABEL: f2:
+; RV64-NOPIC-FUCHSIA:       # %bb.0: # %entry
+; RV64-NOPIC-FUCHSIA-NEXT:  .Lpcrel_hi1:
+; RV64-NOPIC-FUCHSIA-NEXT:    auipc a0, %tls_ie_pcrel_hi(ld)
+; RV64-NOPIC-FUCHSIA-NEXT:    ld a0, %pcrel_lo(.Lpcrel_hi1)(a0)
+; RV64-NOPIC-FUCHSIA-NEXT:    add a0, a0, tp
+; RV64-NOPIC-FUCHSIA-NEXT:    ret
 entry:
   ret ptr @ld
 }
@@ -223,6 +262,14 @@ define ptr @f3() nounwind {
 ; RV64-PIC-TLSDESC-NEXT:    add a0, a0, tp
 ; RV64-PIC-TLSDESC-NEXT:    ret
 ;
+; RV64-PIC-FUCHSIA-LABEL: f3:
+; RV64-PIC-FUCHSIA:       # %bb.0: # %entry
+; RV64-PIC-FUCHSIA-NEXT:  .Lpcrel_hi0:
+; RV64-PIC-FUCHSIA-NEXT:    auipc a0, %tls_ie_pcrel_hi(ie)
+; RV64-PIC-FUCHSIA-NEXT:    ld a0, %pcrel_lo(.Lpcrel_hi0)(a0)
+; RV64-PIC-FUCHSIA-NEXT:    add a0, a0, tp
+; RV64-PIC-FUCHSIA-NEXT:    ret
+;
 ; RV32-NOPIC-LABEL: f3:
 ; RV32-NOPIC:       # %bb.0: # %entry
 ; RV32-NOPIC-NEXT:  .Lpcrel_hi2:
@@ -254,6 +301,14 @@ define ptr @f3() nounwind {
 ; RV64-NOPIC-TLSDESC-NEXT:    ld a0, %pcrel_lo(.Lpcrel_hi2)(a0)
 ; RV64-NOPIC-TLSDESC-NEXT:    add a0, a0, tp
 ; RV64-NOPIC-TLSDESC-NEXT:    ret
+;
+; RV64-NOPIC-FUCHSIA-LABEL: f3:
+; RV64-NOPIC-FUCHSIA:       # %bb.0: # %entry
+; RV64-NOPIC-FUCHSIA-NEXT:  .Lpcrel_hi2:
+; RV64-NOPIC-FUCHSIA-NEXT:    auipc a0, %tls_ie_pcrel_hi(ie)
+; RV64-NOPIC-FUCHSIA-NEXT:    ld a0, %pcrel_lo(.Lpcrel_hi2)(a0)
+; RV64-NOPIC-FUCHSIA-NEXT:    add a0, a0, tp
+; RV64-NOPIC-FUCHSIA-NEXT:    ret
 entry:
   ret ptr @ie
 }
@@ -290,6 +345,13 @@ define ptr @f4() nounwind {
 ; RV64-PIC-TLSDESC-NEXT:    addi a0, a0, %tprel_lo(le)
 ; RV64-PIC-TLSDESC-NEXT:    ret
 ;
+; RV64-PIC-FUCHSIA-LABEL: f4:
+; RV64-PIC-FUCHSIA:       # %bb.0: # %entry
+; RV64-PIC-FUCHSIA-NEXT:    lui a0, %tprel_hi(le)
+; RV64-PIC-FUCHSIA-NEXT:    add a0, a0, tp, %tprel_add(le)
+; RV64-PIC-FUCHSIA-NEXT:    addi a0, a0, %tprel_lo(le)
+; RV64-PIC-FUCHSIA-NEXT:    ret
+;
 ; RV32-NOPIC-LABEL: f4:
 ; RV32-NOPIC:       # %bb.0: # %entry
 ; RV32-NOPIC-NEXT:    lui a0, %tprel_hi(le)
@@ -317,6 +379,13 @@ define ptr @f4() nounwind {
 ; RV64-NOPIC-TLSDESC-NEXT:    add a0, a0, tp, %tprel_add(le)
 ; RV64-NOPIC-TLSDESC-NEXT:    addi a0, a0, %tprel_lo(le)
 ; RV64-NOPIC-TLSDESC-NEXT:    ret
+;
+; RV64-NOPIC-FUCHSIA-LABEL: f4:
+; RV64-NOPIC-FUCHSIA:       # %bb.0: # %entry
+; RV64-NOPIC-FUCHSIA-NEXT:    lui a0, %tprel_hi(le)
+; RV64-NOPIC-FUCHSIA-NEXT:    add a0, a0, tp, %tprel_add(le)
+; RV64-NOPIC-FUCHSIA-NEXT:    addi a0, a0, %tprel_lo(le)
+; RV64-NOPIC-FUCHSIA-NEXT:    ret
 entry:
   ret ptr @le
 }
diff --git a/llvm/test/CodeGen/X86/tls-desc.ll b/llvm/test/CodeGen/X86/tls-desc.ll
index c73986e69e791..f7dc0ceeeeb32 100644
--- a/llvm/test/CodeGen/X86/tls-desc.ll
+++ b/llvm/test/CodeGen/X86/tls-desc.ll
@@ -2,6 +2,7 @@
 ; RUN: llc < %s -mtriple=i686 --relocation-model=pic -enable-tlsdesc | FileCheck %s --check-prefix=X86
 ; RUN: llc < %s -mtriple=x86_64-pc-linux-gnux32 --relocation-model=pic -enable-tlsdesc | FileCheck %s --check-prefix=X32
 ; RUN: llc < %s -mtriple=x86_64 --relocation-model=pic -enable-tlsdesc | FileCheck %s --check-prefix=X64
+; RUN: llc < %s -mtriple=x86_64-unknown-fuchsia --relocation-model=pic | FileCheck %s --check-prefix=X64-FUCHSIA
 
 @x = thread_local global i32 0, align 4
 @y = internal thread_local global i32 1, align 4
@@ -62,6 +63,19 @@ define ptr @f1() nounwind {
 ; X64-NEXT:    #NO_APP
 ; X64-NEXT:    popq %rcx
 ; X64-NEXT:    retq
+;
+; X64-FUCHSIA-LABEL: f1:
+; X64-FUCHSIA:       # %bb.0:
+; X64-FUCHSIA-NEXT:    pushq %rax
+; X64-FUCHSIA-NEXT:    #APP
+; X64-FUCHSIA-NEXT:    #NO_APP
+; X64-FUCHSIA-NEXT:    leaq x at tlsdesc(%rip), %rax
+; X64-FUCHSIA-NEXT:    callq *x at tlscall(%rax)
+; X64-FUCHSIA-NEXT:    addq %fs:0, %rax
+; X64-FUCHSIA-NEXT:    #APP
+; X64-FUCHSIA-NEXT:    #NO_APP
+; X64-FUCHSIA-NEXT:    popq %rcx
+; X64-FUCHSIA-NEXT:    retq
   %a = call { i32, i32, i32, i32, i32, i32 } asm sideeffect "", "=r,=r,=r,=r,=r,=r,~{dirflag},~{fpsr},~{flags}"()
   %b = call ptr @llvm.threadlocal.address.p0(ptr @x)
   %a.0 = extractvalue { i32, i32, i32, i32, i32, i32 } %a, 0
@@ -109,6 +123,15 @@ define i32 @f2() nounwind {
 ; X64-NEXT:    movl (%rax,%rcx), %eax
 ; X64-NEXT:    popq %rcx
 ; X64-NEXT:    retq
+;
+; X64-FUCHSIA-LABEL: f2:
+; X64-FUCHSIA:       # %bb.0:
+; X64-FUCHSIA-NEXT:    pushq %rax
+; X64-FUCHSIA-NEXT:    leaq x at tlsdesc(%rip), %rax
+; X64-FUCHSIA-NEXT:    callq *x at tlscall(%rax)
+; X64-FUCHSIA-NEXT:    movl %fs:(%rax), %eax
+; X64-FUCHSIA-NEXT:    popq %rcx
+; X64-FUCHSIA-NEXT:    retq
   %1 = tail call ptr @llvm.threadlocal.address.p0(ptr @x)
   %2 = load i32, ptr %1
   ret i32 %2
@@ -147,6 +170,15 @@ define ptr @f3() nounwind {
 ; X64-NEXT:    addq %fs:0, %rax
 ; X64-NEXT:    popq %rcx
 ; X64-NEXT:    retq
+;
+; X64-FUCHSIA-LABEL: f3:
+; X64-FUCHSIA:       # %bb.0:
+; X64-FUCHSIA-NEXT:    pushq %rax
+; X64-FUCHSIA-NEXT:    leaq x at tlsdesc(%rip), %rax
+; X64-FUCHSIA-NEXT:    callq *x at tlscall(%rax)
+; X64-FUCHSIA-NEXT:    addq %fs:0, %rax
+; X64-FUCHSIA-NEXT:    popq %rcx
+; X64-FUCHSIA-NEXT:    retq
   %1 = tail call ptr @llvm.threadlocal.address.p0(ptr @x)
   ret ptr %1
 }
@@ -192,6 +224,17 @@ define i32 @f4() nounwind {
 ; X64-NEXT:    movl %ecx, %eax
 ; X64-NEXT:    popq %rcx
 ; X64-NEXT:    retq
+;
+; X64-FUCHSIA-LABEL: f4:
+; X64-FUCHSIA:       # %bb.0:
+; X64-FUCHSIA-NEXT:    pushq %rax
+; X64-FUCHSIA-NEXT:    leaq _TLS_MODULE_BASE_ at tlsdesc(%rip), %rax
+; X64-FUCHSIA-NEXT:    callq *_TLS_MODULE_BASE_ at tlscall(%rax)
+; X64-FUCHSIA-NEXT:    movl %fs:y at DTPOFF(%rax), %ecx
+; X64-FUCHSIA-NEXT:    addl %fs:z at DTPOFF(%rax), %ecx
+; X64-FUCHSIA-NEXT:    movl %ecx, %eax
+; X64-FUCHSIA-NEXT:    popq %rcx
+; X64-FUCHSIA-NEXT:    retq
   %1 = load i32, ptr @y, align 4
   %2 = load i32, ptr @z, align 4
   %3 = add nsw i32 %1, %2

>From 0b43a49ee618540252ae526919bb6dde2476858b Mon Sep 17 00:00:00 2001
From: Paul Kirth <paulkirth at google.com>
Date: Thu, 5 Feb 2026 14:29:26 -0800
Subject: [PATCH 02/27] Redesign how TargetOptions are used in LTO

---
 clang/lib/CodeGen/BackendUtil.cpp             |  7 +-
 .../ClangNVLinkWrapper.cpp                    |  2 -
 lld/COFF/LTO.cpp                              | 24 +++---
 lld/ELF/LTO.cpp                               | 79 ++++++++++---------
 lld/MachO/LTO.cpp                             |  8 +-
 lld/wasm/LTO.cpp                              | 10 +--
 llvm/include/llvm/LTO/Config.h                |  4 +-
 llvm/include/llvm/LTO/LTO.h                   |  2 +-
 .../llvm/LTO/legacy/LTOCodeGenerator.h        |  1 +
 llvm/lib/LTO/LTO.cpp                          | 50 ++++++++----
 llvm/lib/LTO/LTOBackend.cpp                   | 19 +++--
 llvm/lib/LTO/LTOCodeGenerator.cpp             |  8 +-
 llvm/lib/LTO/ThinLTOCodeGenerator.cpp         |  3 +-
 llvm/lib/Target/TargetMachine.cpp             |  4 +-
 llvm/tools/llvm-lto2/llvm-lto2.cpp            |  2 +-
 15 files changed, 135 insertions(+), 88 deletions(-)

diff --git a/clang/lib/CodeGen/BackendUtil.cpp b/clang/lib/CodeGen/BackendUtil.cpp
index 74c8bf1c670da..2595a95e4df13 100644
--- a/clang/lib/CodeGen/BackendUtil.cpp
+++ b/clang/lib/CodeGen/BackendUtil.cpp
@@ -25,6 +25,7 @@
 #include "llvm/Bitcode/BitcodeReader.h"
 #include "llvm/Bitcode/BitcodeWriter.h"
 #include "llvm/Bitcode/BitcodeWriterPass.h"
+#include "llvm/CodeGen/CommandFlags.h"
 #include "llvm/CodeGen/TargetSubtargetInfo.h"
 #include "llvm/Config/llvm-config.h"
 #include "llvm/Frontend/Driver/CodeGenOptions.h"
@@ -51,6 +52,7 @@
 #include "llvm/Support/MemoryBuffer.h"
 #include "llvm/Support/PrettyStackTrace.h"
 #include "llvm/Support/Program.h"
+#include "llvm/Support/TargetSelect.h"
 #include "llvm/Support/TimeProfiler.h"
 #include "llvm/Support/Timer.h"
 #include "llvm/Support/ToolOutputFile.h"
@@ -1351,7 +1353,10 @@ runThinLTOBackend(CompilerInstance &CI, ModuleSummaryIndex *CombinedIndex,
   assert(OptLevelOrNone && "Invalid optimization level!");
   Conf.CGOptLevel = *OptLevelOrNone;
   Conf.OptLevel = CGOpts.OptimizationLevel;
-  initTargetOptions(CI, Diags, Conf.Options);
+  // llvm::codegen::RegisterCodeGenFlags F;
+  Conf.ModifyTargetOptions = [&](llvm::TargetOptions &TargetOpts)->void {
+    initTargetOptions(CI, Diags, TargetOpts);
+  };
   Conf.SampleProfile = std::move(SampleProfile);
   Conf.PTO.LoopUnrolling = CGOpts.UnrollLoops;
   Conf.PTO.LoopInterchange = CGOpts.InterchangeLoops;
diff --git a/clang/tools/clang-nvlink-wrapper/ClangNVLinkWrapper.cpp b/clang/tools/clang-nvlink-wrapper/ClangNVLinkWrapper.cpp
index 70178568f76c6..caa3b8a320b0c 100644
--- a/clang/tools/clang-nvlink-wrapper/ClangNVLinkWrapper.cpp
+++ b/clang/tools/clang-nvlink-wrapper/ClangNVLinkWrapper.cpp
@@ -358,8 +358,6 @@ Expected<std::unique_ptr<lto::LTO>> createLTO(const ArgList &Args) {
       lto::createInProcessThinBackend(heavyweight_hardware_concurrency(Jobs));
 
   Conf.CPU = Args.getLastArgValue(OPT_arch);
-  Conf.Options = codegen::InitTargetOptionsFromCodeGenFlags(Triple);
-
   Conf.RemarksFilename =
       Args.getLastArgValue(OPT_opt_remarks_filename, RemarksFilename);
   Conf.RemarksPasses =
diff --git a/lld/COFF/LTO.cpp b/lld/COFF/LTO.cpp
index 445ea52e995da..d4b3f5e73c98f 100644
--- a/lld/COFF/LTO.cpp
+++ b/lld/COFF/LTO.cpp
@@ -56,17 +56,24 @@ std::string BitcodeCompiler::getThinLTOOutputFile(StringRef path) {
 
 lto::Config BitcodeCompiler::createConfig() {
   lto::Config c;
-  c.Options = initTargetOptionsFromCodeGenFlags();
-  c.Options.EmitAddrsig = true;
+  bool emitAsm = ctx.config.emit == EmitKind::ASM;
+  c.ModifyTargetOptions = [emitAsm](TargetOptions &options) {
+    options.EmitAddrsig = true;
+    // Always emit a section per function/datum with LTO. LLVM LTO should get
+    // most of the benefit of linker GC, but there are still opportunities for
+    // ICF.
+    options.FunctionSections = true;
+    options.DataSections = true;
+
+    if (emitAsm) {
+      options.MCOptions.AsmVerbose = true;
+    }
+  };
+
   for (StringRef C : ctx.config.mllvmOpts)
     c.MllvmArgs.emplace_back(C.str());
 
-  // Always emit a section per function/datum with LTO. LLVM LTO should get most
-  // of the benefit of linker GC, but there are still opportunities for ICF.
-  c.Options.FunctionSections = true;
-  c.Options.DataSections = true;
-
-  // Use static reloc model on 32-bit x86 because it usually results in more
+    // Use static reloc model on 32-bit x86 because it usually results in more
   // compact code, and because there are also known code generation bugs when
   // using the PIC model (see PR34306).
   if (ctx.config.machine == COFF::IMAGE_FILE_MACHINE_I386)
@@ -105,7 +112,6 @@ lto::Config BitcodeCompiler::createConfig() {
     };
   } else if (ctx.config.emit == EmitKind::ASM) {
     c.CGFileType = CodeGenFileType::AssemblyFile;
-    c.Options.MCOptions.AsmVerbose = true;
   }
 
   if (!ctx.config.saveTempsArgs.empty())
diff --git a/lld/ELF/LTO.cpp b/lld/ELF/LTO.cpp
index 7b0fe2001439e..834e3ccbaeb2f 100644
--- a/lld/ELF/LTO.cpp
+++ b/lld/ELF/LTO.cpp
@@ -56,47 +56,52 @@ static std::string getThinLTOOutputFile(Ctx &ctx, StringRef modulePath) {
 static lto::Config createConfig(Ctx &ctx) {
   lto::Config c;
 
-  // LLD supports the new relocations and address-significance tables.
-  c.Options = initTargetOptionsFromCodeGenFlags();
-  c.Options.EmitAddrsig = true;
-  for (StringRef C : ctx.arg.mllvmOpts)
-    c.MllvmArgs.emplace_back(C.str());
-
-  // Always emit a section per function/datum with LTO.
-  c.Options.FunctionSections = true;
-  c.Options.DataSections = true;
-
-  // Check if basic block sections must be used.
-  // Allowed values for --lto-basic-block-sections are "all",
-  // "<file name specifying basic block ids>", or none.  This is the equivalent
-  // of -fbasic-block-sections= flag in clang.
-  if (!ctx.arg.ltoBasicBlockSections.empty()) {
-    if (ctx.arg.ltoBasicBlockSections == "all") {
-      c.Options.BBSections = BasicBlockSection::All;
-    } else if (ctx.arg.ltoBasicBlockSections == "labels") {
-      c.Options.BBAddrMap = true;
-      Warn(ctx)
-          << "'--lto-basic-block-sections=labels' is deprecated; Please use "
-             "'--lto-basic-block-address-map' instead";
-    } else if (ctx.arg.ltoBasicBlockSections == "none") {
-      c.Options.BBSections = BasicBlockSection::None;
-    } else {
-      ErrorOr<std::unique_ptr<MemoryBuffer>> MBOrErr =
-          MemoryBuffer::getFile(ctx.arg.ltoBasicBlockSections.str());
-      if (!MBOrErr) {
-        ErrAlways(ctx) << "cannot open " << ctx.arg.ltoBasicBlockSections << ":"
-                       << MBOrErr.getError().message();
+  // Set up the callback to modify TargetOptions.
+  c.ModifyTargetOptions = [&](TargetOptions &Options) -> void {
+    // LLD supports the new relocations and address-significance tables.
+    Options.EmitAddrsig = true;
+    // Always emit a section per function/datum with LTO.
+    Options.FunctionSections = true;
+    Options.DataSections = true;
+
+    // Check if basic block sections must be used.
+    // Allowed values for --lto-basic-block-sections are "all",
+    // "<file name specifying basic block ids>", or none.  This is the
+    // equivalent of -fbasic-block-sections= flag in clang.
+    if (!ctx.arg.ltoBasicBlockSections.empty()) {
+      if (ctx.arg.ltoBasicBlockSections == "all") {
+        Options.BBSections = BasicBlockSection::All;
+      } else if (ctx.arg.ltoBasicBlockSections == "labels") {
+        Options.BBAddrMap = true;
+        Warn(ctx)
+            << "'--lto-basic-block-sections=labels' is deprecated; Please use "
+               "'--lto-basic-block-address-map' instead";
+      } else if (ctx.arg.ltoBasicBlockSections == "none") {
+        Options.BBSections = BasicBlockSection::None;
       } else {
-        c.Options.BBSectionsFuncListBuf = std::move(*MBOrErr);
+        ErrorOr<std::unique_ptr<MemoryBuffer>> MBOrErr =
+            MemoryBuffer::getFile(ctx.arg.ltoBasicBlockSections.str());
+        if (!MBOrErr) {
+          ErrAlways(ctx) << "cannot open " << ctx.arg.ltoBasicBlockSections
+                         << ":" << MBOrErr.getError().message();
+        } else {
+          Options.BBSectionsFuncListBuf = std::move(*MBOrErr);
+        }
+        Options.BBSections = BasicBlockSection::List;
       }
-      c.Options.BBSections = BasicBlockSection::List;
     }
-  }
 
-  c.Options.BBAddrMap = ctx.arg.ltoBBAddrMap;
+    Options.BBAddrMap = ctx.arg.ltoBBAddrMap;
 
-  c.Options.UniqueBasicBlockSectionNames =
-      ctx.arg.ltoUniqueBasicBlockSectionNames;
+    Options.UniqueBasicBlockSectionNames =
+        ctx.arg.ltoUniqueBasicBlockSectionNames;
+    if (ctx.arg.ltoEmitAsm) {
+      Options.MCOptions.AsmVerbose = true;
+    }
+  };
+
+  for (StringRef C : ctx.arg.mllvmOpts)
+    c.MllvmArgs.emplace_back(C.str());
 
   if (auto relocModel = getRelocModelFromCMModel())
     c.RelocModel = *relocModel;
@@ -166,7 +171,7 @@ static lto::Config createConfig(Ctx &ctx) {
 
   if (ctx.arg.ltoEmitAsm) {
     c.CGFileType = CodeGenFileType::AssemblyFile;
-    c.Options.MCOptions.AsmVerbose = true;
+    // c.Options.MCOptions.AsmVerbose = true;
   }
 
   if (!ctx.arg.saveTempsArgs.empty())
diff --git a/lld/MachO/LTO.cpp b/lld/MachO/LTO.cpp
index c8b7a4e797250..19c1bf25c86d7 100644
--- a/lld/MachO/LTO.cpp
+++ b/lld/MachO/LTO.cpp
@@ -38,8 +38,12 @@ static std::string getThinLTOOutputFile(StringRef modulePath) {
 
 static lto::Config createConfig() {
   lto::Config c;
-  c.Options = initTargetOptionsFromCodeGenFlags();
-  c.Options.EmitAddrsig = config->icfLevel == ICFLevel::safe;
+
+  bool emitAddrsig = config->icfLevel == ICFLevel::safe;
+  c.ModifyTargetOptions = [emitAddrsig](TargetOptions &options) {
+    options.EmitAddrsig = emitAddrsig;
+  };
+
   for (StringRef C : config->mllvmOpts)
     c.MllvmArgs.emplace_back(C.str());
   for (StringRef pluginFn : config->passPlugins)
diff --git a/lld/wasm/LTO.cpp b/lld/wasm/LTO.cpp
index 2c068bf518905..8706e80a71df3 100644
--- a/lld/wasm/LTO.cpp
+++ b/lld/wasm/LTO.cpp
@@ -42,11 +42,11 @@ static std::string getThinLTOOutputFile(StringRef modulePath) {
 
 static lto::Config createConfig() {
   lto::Config c;
-  c.Options = initTargetOptionsFromCodeGenFlags();
-
-  // Always emit a section per function/data with LTO.
-  c.Options.FunctionSections = true;
-  c.Options.DataSections = true;
+  c.ModifyTargetOptions = [&](TargetOptions &options) {
+    // Always emit a section per function/data with LTO.
+    options.FunctionSections = true;
+    options.DataSections = true;
+  };
 
   c.DisableVerify = ctx.arg.disableVerify;
   c.DiagHandler = diagnosticHandler;
diff --git a/llvm/include/llvm/LTO/Config.h b/llvm/include/llvm/LTO/Config.h
index 2aeb902bcfccf..179e0bb0c4cc0 100644
--- a/llvm/include/llvm/LTO/Config.h
+++ b/llvm/include/llvm/LTO/Config.h
@@ -48,7 +48,8 @@ struct Config {
   // Note: when adding fields here, consider whether they need to be added to
   // computeLTOCacheKey in LTO.cpp.
   std::string CPU;
-  TargetOptions Options;
+  // Callback to modify the target options once they are instantiated.
+  std::function<void(TargetOptions& Options)> ModifyTargetOptions = [](TargetOptions&){};
   std::vector<std::string> MAttrs;
   std::vector<std::string> MllvmArgs;
   // LTO will register both lists of plugins, but
@@ -292,6 +293,7 @@ struct Config {
   LLVM_ABI Error addSaveTemps(std::string OutputFileName,
                               bool UseInputModulePath = false,
                               const DenseSet<StringRef> &SaveTempsArgs = {});
+
 };
 
 struct LTOLLVMDiagnosticHandler : public DiagnosticHandler {
diff --git a/llvm/include/llvm/LTO/LTO.h b/llvm/include/llvm/LTO/LTO.h
index aba2661e81c47..afb95c14bd070 100644
--- a/llvm/include/llvm/LTO/LTO.h
+++ b/llvm/include/llvm/LTO/LTO.h
@@ -75,7 +75,7 @@ LLVM_ABI std::string computeLTOCacheKey(
     StringRef ModuleID, const FunctionImporter::ImportMapTy &ImportList,
     const FunctionImporter::ExportSetTy &ExportList,
     const std::map<GlobalValue::GUID, GlobalValue::LinkageTypes> &ResolvedODR,
-    const GVSummaryMapTy &DefinedGlobals,
+    const GVSummaryMapTy &DefinedGlobals, const Triple &TT,
     const DenseSet<GlobalValue::GUID> &CfiFunctionDefs = {},
     const DenseSet<GlobalValue::GUID> &CfiFunctionDecls = {});
 
diff --git a/llvm/include/llvm/LTO/legacy/LTOCodeGenerator.h b/llvm/include/llvm/LTO/legacy/LTOCodeGenerator.h
index caff198358caa..117269c1b58a1 100644
--- a/llvm/include/llvm/LTO/legacy/LTOCodeGenerator.h
+++ b/llvm/include/llvm/LTO/legacy/LTOCodeGenerator.h
@@ -249,6 +249,7 @@ struct LTOCodeGenerator {
   std::string SaveIRBeforeOptPath;
 
   lto::Config Config;
+  llvm::TargetOptions TO;
 };
 
 /// A convenience function that calls cl::ParseCommandLineOptions on the given
diff --git a/llvm/lib/LTO/LTO.cpp b/llvm/lib/LTO/LTO.cpp
index 95faf3484c456..5286d92f767d7 100644
--- a/llvm/lib/LTO/LTO.cpp
+++ b/llvm/lib/LTO/LTO.cpp
@@ -24,6 +24,7 @@
 #include "llvm/Bitcode/BitcodeWriter.h"
 #include "llvm/CGData/CodeGenData.h"
 #include "llvm/CodeGen/Analysis.h"
+#include "llvm/CodeGen/CommandFlags.h"
 #include "llvm/Config/llvm-config.h"
 #include "llvm/IR/AutoUpgrade.h"
 #include "llvm/IR/DiagnosticPrinter.h"
@@ -140,6 +141,7 @@ std::string llvm::computeLTOCacheKey(
     const FunctionImporter::ExportSetTy &ExportList,
     const std::map<GlobalValue::GUID, GlobalValue::LinkageTypes> &ResolvedODR,
     const GVSummaryMapTy &DefinedGlobals,
+    const Triple& TT,
     const DenseSet<GlobalValue::GUID> &CfiFunctionDefs,
     const DenseSet<GlobalValue::GUID> &CfiFunctionDecls) {
   // Compute the unique hash for this entry.
@@ -173,14 +175,17 @@ std::string llvm::computeLTOCacheKey(
     Hasher.update(ArrayRef<uint8_t>(&I, 1));
   };
   AddString(Conf.CPU);
+  TargetOptions Opts = codegen::InitTargetOptionsFromCodeGenFlags(TT);
+  Conf.ModifyTargetOptions(Opts);
+
   // FIXME: Hash more of Options. For now all clients initialize Options from
   // command-line flags (which is unsupported in production), but may set
   // X86RelaxRelocations. The clang driver can also pass FunctionSections,
   // DataSections and DebuggerTuning via command line flags.
-  AddUnsigned(Conf.Options.MCOptions.X86RelaxRelocations);
-  AddUnsigned(Conf.Options.FunctionSections);
-  AddUnsigned(Conf.Options.DataSections);
-  AddUnsigned((unsigned)Conf.Options.DebuggerTuning);
+  AddUnsigned(Opts.MCOptions.X86RelaxRelocations);
+  AddUnsigned(Opts.FunctionSections);
+  AddUnsigned(Opts.DataSections);
+  AddUnsigned((unsigned)Opts.DebuggerTuning);
   for (auto &A : Conf.MAttrs)
     AddString(A);
   if (Conf.RelocModel)
@@ -1619,10 +1624,10 @@ class InProcessThinBackend : public CGThinBackend {
       const GVSummaryMapTy &DefinedGlobals,
       MapVector<StringRef, BitcodeModule> &ModuleMap) {
     auto ModuleID = BM.getModuleIdentifier();
+    LTOLLVMContext BackendContext(Conf);
     llvm::TimeTraceScope timeScope("Run ThinLTO backend thread (in-process)",
                                    ModuleID);
     auto RunThinBackend = [&](AddStreamFn AddStream) {
-      LTOLLVMContext BackendContext(Conf);
       Expected<std::unique_ptr<Module>> MOrErr = BM.parseModule(BackendContext);
       if (!MOrErr)
         return MOrErr.takeError();
@@ -1643,10 +1648,15 @@ class InProcessThinBackend : public CGThinBackend {
       // no module hash.
       return RunThinBackend(AddStream);
 
+    auto Mod = ModuleMap.front().second.parseModule(BackendContext);
+    if (!Mod)
+      return Mod.takeError();
+    Triple TT = (*Mod)->getTargetTriple();
+
     // The module may be cached, this helps handling it.
     std::string Key = computeLTOCacheKey(
         Conf, CombinedIndex, ModuleID, ImportList, ExportList, ResolvedODR,
-        DefinedGlobals, CfiFunctionDefs, CfiFunctionDecls);
+        DefinedGlobals, TT, CfiFunctionDefs, CfiFunctionDecls);
     Expected<AddStreamFn> CacheAddStreamOrErr = Cache(Task, Key, ModuleID);
     if (Error Err = CacheAddStreamOrErr.takeError())
       return Err;
@@ -1735,9 +1745,9 @@ class FirstRoundThinBackend : public InProcessThinBackend {
     auto ModuleID = BM.getModuleIdentifier();
     llvm::TimeTraceScope timeScope("Run ThinLTO backend thread (first round)",
                                    ModuleID);
+      LTOLLVMContext BackendContext(Conf);
     auto RunThinBackend = [&](AddStreamFn CGAddStream,
                               AddStreamFn IRAddStream) {
-      LTOLLVMContext BackendContext(Conf);
       Expected<std::unique_ptr<Module>> MOrErr = BM.parseModule(BackendContext);
       if (!MOrErr)
         return MOrErr.takeError();
@@ -1763,10 +1773,15 @@ class FirstRoundThinBackend : public InProcessThinBackend {
       // no module hash.
       return RunThinBackend(CGAddStream, IRAddStream);
 
+    auto Mod = ModuleMap.front().second.parseModule(BackendContext);
+    if (!Mod)
+      return Mod.takeError();
+    Triple TT = (*Mod)->getTargetTriple();
+
     // Get CGKey for caching object in CGCache.
     std::string CGKey = computeLTOCacheKey(
         Conf, CombinedIndex, ModuleID, ImportList, ExportList, ResolvedODR,
-        DefinedGlobals, CfiFunctionDefs, CfiFunctionDecls);
+        DefinedGlobals, TT, CfiFunctionDefs, CfiFunctionDecls);
     Expected<AddStreamFn> CacheCGAddStreamOrErr =
         CGCache(Task, CGKey, ModuleID);
     if (Error Err = CacheCGAddStreamOrErr.takeError())
@@ -1833,8 +1848,8 @@ class SecondRoundThinBackend : public InProcessThinBackend {
     auto ModuleID = BM.getModuleIdentifier();
     llvm::TimeTraceScope timeScope("Run ThinLTO backend thread (second round)",
                                    ModuleID);
+    LTOLLVMContext BackendContext(Conf);
     auto RunThinBackend = [&](AddStreamFn AddStream) {
-      LTOLLVMContext BackendContext(Conf);
       std::unique_ptr<Module> LoadedModule =
           cgdata::loadModuleForTwoRounds(BM, Task, BackendContext, *IRFiles);
 
@@ -1849,11 +1864,16 @@ class SecondRoundThinBackend : public InProcessThinBackend {
       // no module hash.
       return RunThinBackend(AddStream);
 
+    auto Mod = ModuleMap.front().second.parseModule(BackendContext);
+    if (!Mod)
+      return Mod.takeError();
+    Triple TT = (*Mod)->getTargetTriple();
+
     // Get Key for caching the final object file in Cache with the combined
     // CGData hash.
     std::string Key = computeLTOCacheKey(
         Conf, CombinedIndex, ModuleID, ImportList, ExportList, ResolvedODR,
-        DefinedGlobals, CfiFunctionDefs, CfiFunctionDecls);
+        DefinedGlobals, TT, CfiFunctionDefs, CfiFunctionDecls);
     Key = recomputeLTOCacheKey(Key,
                                /*ExtraID=*/std::to_string(CombinedCGDataHash));
     Expected<AddStreamFn> CacheAddStreamOrErr = Cache(Task, Key, ModuleID);
@@ -2467,7 +2487,7 @@ class OutOfProcessThinBackend : public CGThinBackend {
     // The module may be cached, this helps handling it.
     J.CacheKey = computeLTOCacheKey(Conf, CombinedIndex, J.ModuleID, ImportList,
                                     ExportList, ResolvedODR, DefinedGlobals,
-                                    CfiFunctionDefs, CfiFunctionDecls);
+                                    Triple, CfiFunctionDefs, CfiFunctionDecls);
 
     // The module may be cached, this helps handling it.
     auto CacheAddStreamExp = Cache(J.Task, J.CacheKey, J.ModuleID);
@@ -2563,12 +2583,14 @@ class OutOfProcessThinBackend : public CGThinBackend {
     auto &Ops = CodegenOptions;
 
     Ops.push_back(Saver.save("-O" + Twine(C.OptLevel)));
+    TargetOptions TO = codegen::InitTargetOptionsFromCodeGenFlags(Triple);
+    C.ModifyTargetOptions(TO);
 
-    if (C.Options.EmitAddrsig)
+    if (TO.EmitAddrsig)
       Ops.push_back("-faddrsig");
-    if (C.Options.FunctionSections)
+    if (TO.FunctionSections)
       Ops.push_back("-ffunction-sections");
-    if (C.Options.DataSections)
+    if (TO.DataSections)
       Ops.push_back("-fdata-sections");
 
     if (C.RelocModel == Reloc::PIC_)
diff --git a/llvm/lib/LTO/LTOBackend.cpp b/llvm/lib/LTO/LTOBackend.cpp
index 16a42e526eb8a..1105a546d190d 100644
--- a/llvm/lib/LTO/LTOBackend.cpp
+++ b/llvm/lib/LTO/LTOBackend.cpp
@@ -22,6 +22,7 @@
 #include "llvm/Bitcode/BitcodeReader.h"
 #include "llvm/Bitcode/BitcodeWriter.h"
 #include "llvm/CGData/CodeGenData.h"
+#include "llvm/CodeGen/CommandFlags.h"
 #include "llvm/IR/LLVMRemarkStreamer.h"
 #include "llvm/IR/LegacyPassManager.h"
 #include "llvm/IR/PassManager.h"
@@ -218,6 +219,9 @@ static void RegisterPassPlugins(const Config &Conf, PassBuilder &PB) {
     LoadedPlugin->registerPassBuilderCallbacks(PB);
 }
 
+// Required to call InitTargetOptionsFromCodeGenFlags().
+static  codegen::RegisterCodeGenFlags F;
+
 static std::unique_ptr<TargetMachine>
 createTargetMachine(const Config &Conf, const Target *TheTarget, Module &M) {
   const Triple &TheTriple = M.getTargetTriple();
@@ -239,7 +243,10 @@ createTargetMachine(const Config &Conf, const Target *TheTarget, Module &M) {
   else
     CodeModel = M.getCodeModel();
 
-  TargetOptions TargetOpts = Conf.Options;
+  TargetOptions TargetOpts =
+      codegen::InitTargetOptionsFromCodeGenFlags(TheTriple);
+  Conf.ModifyTargetOptions(TargetOpts);
+
   if (TargetOpts.MCOptions.ABIName.empty()) {
     TargetOpts.MCOptions.ABIName = M.getTargetABIFromMD();
   }
@@ -434,7 +441,7 @@ bool lto::opt(const Config &Conf, TargetMachine *TM, unsigned Task, Module &Mod,
   return !Conf.PostOptModuleHook || Conf.PostOptModuleHook(Task, Mod);
 }
 
-static void codegen(const Config &Conf, TargetMachine *TM,
+static void startCodegen(const Config &Conf, TargetMachine *TM,
                     AddStreamFn AddStream, unsigned Task, Module &Mod,
                     const ModuleSummaryIndex &CombinedIndex) {
   llvm::TimeTraceScope timeScope("codegen");
@@ -547,7 +554,7 @@ static void splitCodeGen(const Config &C, TargetMachine *TM,
               std::unique_ptr<TargetMachine> TM =
                   createTargetMachine(C, T, *MPartInCtx);
 
-              codegen(C, TM.get(), AddStream, ThreadId, *MPartInCtx,
+              startCodegen(C, TM.get(), AddStream, ThreadId, *MPartInCtx,
                       CombinedIndex);
             },
             // Pass BC using std::move to ensure that it get moved rather than
@@ -613,7 +620,7 @@ Error lto::backend(const Config &C, AddStreamFn AddStream,
   }
 
   if (ParallelCodeGenParallelismLevel == 1) {
-    codegen(C, TM.get(), AddStream, 0, Mod, CombinedIndex);
+    startCodegen(C, TM.get(), AddStream, 0, Mod, CombinedIndex);
   } else {
     splitCodeGen(C, TM.get(), AddStream, ParallelCodeGenParallelismLevel, Mod,
                  CombinedIndex);
@@ -675,7 +682,7 @@ Error lto::thinBackend(const Config &Conf, unsigned Task, AddStreamFn AddStream,
   if (CodeGenOnly) {
     // If CodeGenOnly is set, we only perform code generation and skip
     // optimization. This value may differ from Conf.CodeGenOnly.
-    codegen(Conf, TM.get(), AddStream, Task, Mod, CombinedIndex);
+    startCodegen(Conf, TM.get(), AddStream, Task, Mod, CombinedIndex);
     return finalizeOptimizationRemarks(std::move(DiagnosticOutputFile));
   }
 
@@ -698,7 +705,7 @@ Error lto::thinBackend(const Config &Conf, unsigned Task, AddStreamFn AddStream,
         if (IRAddStream)
           cgdata::saveModuleForTwoRounds(Mod, Task, IRAddStream);
 
-        codegen(Conf, TM, AddStream, Task, Mod, CombinedIndex);
+        startCodegen(Conf, TM, AddStream, Task, Mod, CombinedIndex);
         return finalizeOptimizationRemarks(std::move(DiagnosticOutputFile));
       };
 
diff --git a/llvm/lib/LTO/LTOCodeGenerator.cpp b/llvm/lib/LTO/LTOCodeGenerator.cpp
index 8b9bcfb1d4736..36b63803eec5e 100644
--- a/llvm/lib/LTO/LTOCodeGenerator.cpp
+++ b/llvm/lib/LTO/LTOCodeGenerator.cpp
@@ -166,7 +166,7 @@ void LTOCodeGenerator::setModule(std::unique_ptr<LTOModule> Mod) {
 }
 
 void LTOCodeGenerator::setTargetOptions(const TargetOptions &Options) {
-  Config.Options = Options;
+  TO = Options;
 }
 
 void LTOCodeGenerator::setDebugInfo(lto_debug_model Debug) {
@@ -230,7 +230,7 @@ bool LTOCodeGenerator::writeMergedModules(StringRef Path) {
 
 bool LTOCodeGenerator::useAIXSystemAssembler() {
   const auto &Triple = TargetMach->getTargetTriple();
-  return Triple.isOSAIX() && Config.Options.DisableIntegratedAS;
+  return Triple.isOSAIX() && TO.DisableIntegratedAS;
 }
 
 bool LTOCodeGenerator::runAIXSystemAssembler(SmallString<128> &AssemblyFile) {
@@ -397,7 +397,7 @@ bool LTOCodeGenerator::determineTarget() {
   // If data-sections is not explicitly set or unset, set data-sections by
   // default to match the behaviour of lld and gold plugin.
   if (!codegen::getExplicitDataSections())
-    Config.Options.DataSections = true;
+    TO.DataSections = true;
 
   TargetMach = createTargetMachine();
   assert(TargetMach && "Unable to create target machine");
@@ -408,7 +408,7 @@ bool LTOCodeGenerator::determineTarget() {
 std::unique_ptr<TargetMachine> LTOCodeGenerator::createTargetMachine() {
   assert(MArch && "MArch is not set!");
   return std::unique_ptr<TargetMachine>(MArch->createTargetMachine(
-      MergedModule->getTargetTriple(), Config.CPU, FeatureStr, Config.Options,
+      MergedModule->getTargetTriple(), Config.CPU, FeatureStr,TO,
       Config.RelocModel, std::nullopt, Config.CGOptLevel));
 }
 
diff --git a/llvm/lib/LTO/ThinLTOCodeGenerator.cpp b/llvm/lib/LTO/ThinLTOCodeGenerator.cpp
index 6397fdffed687..e92cf1f828f4c 100644
--- a/llvm/lib/LTO/ThinLTOCodeGenerator.cpp
+++ b/llvm/lib/LTO/ThinLTOCodeGenerator.cpp
@@ -376,7 +376,6 @@ class ModuleCacheEntry {
 
     llvm::lto::Config Conf;
     Conf.OptLevel = OptLevel;
-    Conf.Options = TMBuilder.Options;
     Conf.CPU = TMBuilder.MCpu;
     Conf.MAttrs.push_back(TMBuilder.MAttr);
     Conf.RelocModel = TMBuilder.RelocModel;
@@ -384,7 +383,7 @@ class ModuleCacheEntry {
     Conf.Freestanding = Freestanding;
     std::string Key =
         computeLTOCacheKey(Conf, Index, ModuleID, ImportList, ExportList,
-                           ResolvedODR, DefinedGVSummaries);
+                           ResolvedODR, DefinedGVSummaries, TMBuilder.TheTriple);
 
     // This choice of file name allows the cache to be pruned (see pruneCache()
     // in include/llvm/Support/CachePruning.h).
diff --git a/llvm/lib/Target/TargetMachine.cpp b/llvm/lib/Target/TargetMachine.cpp
index dbc4fb7a9e2af..9243cdad43e1e 100644
--- a/llvm/lib/Target/TargetMachine.cpp
+++ b/llvm/lib/Target/TargetMachine.cpp
@@ -255,9 +255,7 @@ bool TargetMachine::shouldAssumeDSOLocal(const GlobalValue *GV) const {
 }
 
 bool TargetMachine::useEmulatedTLS() const { return Options.EmulatedTLS; }
-bool TargetMachine::useTLSDESC() const {
-  return Options.EnableTLSDESC || TargetTriple.isOSFuchsia();
-}
+bool TargetMachine::useTLSDESC() const { return Options.EnableTLSDESC; }
 
 TLSModel::Model TargetMachine::getTLSModel(const GlobalValue *GV) const {
   bool IsPIE = GV->getParent()->getPIELevel() != PIELevel::Default;
diff --git a/llvm/tools/llvm-lto2/llvm-lto2.cpp b/llvm/tools/llvm-lto2/llvm-lto2.cpp
index 839324e396850..73a695bdb6382 100644
--- a/llvm/tools/llvm-lto2/llvm-lto2.cpp
+++ b/llvm/tools/llvm-lto2/llvm-lto2.cpp
@@ -347,7 +347,7 @@ static int run(int argc, char **argv) {
     Conf.TimeTraceGranularity = TimeTraceGranularity;
   }
   Conf.CPU = codegen::getMCPU();
-  Conf.Options = codegen::InitTargetOptionsFromCodeGenFlags(Triple());
+  Conf.ModifyTargetOptions = [](llvm::TargetOptions &TO) {};
   Conf.MAttrs = codegen::getMAttrs();
   if (auto RM = codegen::getExplicitRelocModel())
     Conf.RelocModel = *RM;

>From 4b8300c7ad6c519aa9bec63be492d8005a88fad5 Mon Sep 17 00:00:00 2001
From: Paul Kirth <paulkirth at google.com>
Date: Thu, 5 Feb 2026 14:36:34 -0800
Subject: [PATCH 03/27] clang format

---
 clang/lib/CodeGen/BackendUtil.cpp     | 2 +-
 lld/COFF/LTO.cpp                      | 2 +-
 llvm/include/llvm/LTO/Config.h        | 4 ++--
 llvm/lib/LTO/LTO.cpp                  | 5 ++---
 llvm/lib/LTO/LTOBackend.cpp           | 8 ++++----
 llvm/lib/LTO/LTOCodeGenerator.cpp     | 2 +-
 llvm/lib/LTO/ThinLTOCodeGenerator.cpp | 6 +++---
 7 files changed, 14 insertions(+), 15 deletions(-)

diff --git a/clang/lib/CodeGen/BackendUtil.cpp b/clang/lib/CodeGen/BackendUtil.cpp
index 2595a95e4df13..50838af3c647d 100644
--- a/clang/lib/CodeGen/BackendUtil.cpp
+++ b/clang/lib/CodeGen/BackendUtil.cpp
@@ -1354,7 +1354,7 @@ runThinLTOBackend(CompilerInstance &CI, ModuleSummaryIndex *CombinedIndex,
   Conf.CGOptLevel = *OptLevelOrNone;
   Conf.OptLevel = CGOpts.OptimizationLevel;
   // llvm::codegen::RegisterCodeGenFlags F;
-  Conf.ModifyTargetOptions = [&](llvm::TargetOptions &TargetOpts)->void {
+  Conf.ModifyTargetOptions = [&](llvm::TargetOptions &TargetOpts) -> void {
     initTargetOptions(CI, Diags, TargetOpts);
   };
   Conf.SampleProfile = std::move(SampleProfile);
diff --git a/lld/COFF/LTO.cpp b/lld/COFF/LTO.cpp
index d4b3f5e73c98f..bf12b1af78733 100644
--- a/lld/COFF/LTO.cpp
+++ b/lld/COFF/LTO.cpp
@@ -73,7 +73,7 @@ lto::Config BitcodeCompiler::createConfig() {
   for (StringRef C : ctx.config.mllvmOpts)
     c.MllvmArgs.emplace_back(C.str());
 
-    // Use static reloc model on 32-bit x86 because it usually results in more
+  // Use static reloc model on 32-bit x86 because it usually results in more
   // compact code, and because there are also known code generation bugs when
   // using the PIC model (see PR34306).
   if (ctx.config.machine == COFF::IMAGE_FILE_MACHINE_I386)
diff --git a/llvm/include/llvm/LTO/Config.h b/llvm/include/llvm/LTO/Config.h
index 179e0bb0c4cc0..bd4a92b9ba67e 100644
--- a/llvm/include/llvm/LTO/Config.h
+++ b/llvm/include/llvm/LTO/Config.h
@@ -49,7 +49,8 @@ struct Config {
   // computeLTOCacheKey in LTO.cpp.
   std::string CPU;
   // Callback to modify the target options once they are instantiated.
-  std::function<void(TargetOptions& Options)> ModifyTargetOptions = [](TargetOptions&){};
+  std::function<void(TargetOptions &Options)> ModifyTargetOptions =
+      [](TargetOptions &) {};
   std::vector<std::string> MAttrs;
   std::vector<std::string> MllvmArgs;
   // LTO will register both lists of plugins, but
@@ -293,7 +294,6 @@ struct Config {
   LLVM_ABI Error addSaveTemps(std::string OutputFileName,
                               bool UseInputModulePath = false,
                               const DenseSet<StringRef> &SaveTempsArgs = {});
-
 };
 
 struct LTOLLVMDiagnosticHandler : public DiagnosticHandler {
diff --git a/llvm/lib/LTO/LTO.cpp b/llvm/lib/LTO/LTO.cpp
index 5286d92f767d7..a91e377b89e92 100644
--- a/llvm/lib/LTO/LTO.cpp
+++ b/llvm/lib/LTO/LTO.cpp
@@ -140,8 +140,7 @@ std::string llvm::computeLTOCacheKey(
     const FunctionImporter::ImportMapTy &ImportList,
     const FunctionImporter::ExportSetTy &ExportList,
     const std::map<GlobalValue::GUID, GlobalValue::LinkageTypes> &ResolvedODR,
-    const GVSummaryMapTy &DefinedGlobals,
-    const Triple& TT,
+    const GVSummaryMapTy &DefinedGlobals, const Triple &TT,
     const DenseSet<GlobalValue::GUID> &CfiFunctionDefs,
     const DenseSet<GlobalValue::GUID> &CfiFunctionDecls) {
   // Compute the unique hash for this entry.
@@ -1745,7 +1744,7 @@ class FirstRoundThinBackend : public InProcessThinBackend {
     auto ModuleID = BM.getModuleIdentifier();
     llvm::TimeTraceScope timeScope("Run ThinLTO backend thread (first round)",
                                    ModuleID);
-      LTOLLVMContext BackendContext(Conf);
+    LTOLLVMContext BackendContext(Conf);
     auto RunThinBackend = [&](AddStreamFn CGAddStream,
                               AddStreamFn IRAddStream) {
       Expected<std::unique_ptr<Module>> MOrErr = BM.parseModule(BackendContext);
diff --git a/llvm/lib/LTO/LTOBackend.cpp b/llvm/lib/LTO/LTOBackend.cpp
index 1105a546d190d..1c05323974f99 100644
--- a/llvm/lib/LTO/LTOBackend.cpp
+++ b/llvm/lib/LTO/LTOBackend.cpp
@@ -220,7 +220,7 @@ static void RegisterPassPlugins(const Config &Conf, PassBuilder &PB) {
 }
 
 // Required to call InitTargetOptionsFromCodeGenFlags().
-static  codegen::RegisterCodeGenFlags F;
+static codegen::RegisterCodeGenFlags F;
 
 static std::unique_ptr<TargetMachine>
 createTargetMachine(const Config &Conf, const Target *TheTarget, Module &M) {
@@ -442,8 +442,8 @@ bool lto::opt(const Config &Conf, TargetMachine *TM, unsigned Task, Module &Mod,
 }
 
 static void startCodegen(const Config &Conf, TargetMachine *TM,
-                    AddStreamFn AddStream, unsigned Task, Module &Mod,
-                    const ModuleSummaryIndex &CombinedIndex) {
+                         AddStreamFn AddStream, unsigned Task, Module &Mod,
+                         const ModuleSummaryIndex &CombinedIndex) {
   llvm::TimeTraceScope timeScope("codegen");
   if (Conf.PreCodeGenModuleHook && !Conf.PreCodeGenModuleHook(Task, Mod))
     return;
@@ -555,7 +555,7 @@ static void splitCodeGen(const Config &C, TargetMachine *TM,
                   createTargetMachine(C, T, *MPartInCtx);
 
               startCodegen(C, TM.get(), AddStream, ThreadId, *MPartInCtx,
-                      CombinedIndex);
+                           CombinedIndex);
             },
             // Pass BC using std::move to ensure that it get moved rather than
             // copied into the thread's context.
diff --git a/llvm/lib/LTO/LTOCodeGenerator.cpp b/llvm/lib/LTO/LTOCodeGenerator.cpp
index 36b63803eec5e..dfa6fad4c715f 100644
--- a/llvm/lib/LTO/LTOCodeGenerator.cpp
+++ b/llvm/lib/LTO/LTOCodeGenerator.cpp
@@ -408,7 +408,7 @@ bool LTOCodeGenerator::determineTarget() {
 std::unique_ptr<TargetMachine> LTOCodeGenerator::createTargetMachine() {
   assert(MArch && "MArch is not set!");
   return std::unique_ptr<TargetMachine>(MArch->createTargetMachine(
-      MergedModule->getTargetTriple(), Config.CPU, FeatureStr,TO,
+      MergedModule->getTargetTriple(), Config.CPU, FeatureStr, TO,
       Config.RelocModel, std::nullopt, Config.CGOptLevel));
 }
 
diff --git a/llvm/lib/LTO/ThinLTOCodeGenerator.cpp b/llvm/lib/LTO/ThinLTOCodeGenerator.cpp
index e92cf1f828f4c..7346d987b1420 100644
--- a/llvm/lib/LTO/ThinLTOCodeGenerator.cpp
+++ b/llvm/lib/LTO/ThinLTOCodeGenerator.cpp
@@ -381,9 +381,9 @@ class ModuleCacheEntry {
     Conf.RelocModel = TMBuilder.RelocModel;
     Conf.CGOptLevel = TMBuilder.CGOptLevel;
     Conf.Freestanding = Freestanding;
-    std::string Key =
-        computeLTOCacheKey(Conf, Index, ModuleID, ImportList, ExportList,
-                           ResolvedODR, DefinedGVSummaries, TMBuilder.TheTriple);
+    std::string Key = computeLTOCacheKey(
+        Conf, Index, ModuleID, ImportList, ExportList, ResolvedODR,
+        DefinedGVSummaries, TMBuilder.TheTriple);
 
     // This choice of file name allows the cache to be pruned (see pruneCache()
     // in include/llvm/Support/CachePruning.h).

>From afb9414ef5d318222f30b48b7d6239008a136b7d Mon Sep 17 00:00:00 2001
From: Paul Kirth <paulkirth at google.com>
Date: Tue, 10 Feb 2026 14:09:09 -0800
Subject: [PATCH 04/27] Address minor review comments

Cleanup stale comments and formatting.
---
 clang/lib/CodeGen/BackendUtil.cpp                  | 1 -
 lld/COFF/LTO.cpp                                   | 3 +--
 lld/Common/TargetOptionsCommandFlags.cpp           | 4 ----
 lld/ELF/LTO.cpp                                    | 1 -
 lld/include/lld/Common/TargetOptionsCommandFlags.h | 1 -
 5 files changed, 1 insertion(+), 9 deletions(-)

diff --git a/clang/lib/CodeGen/BackendUtil.cpp b/clang/lib/CodeGen/BackendUtil.cpp
index 50838af3c647d..f1895f30ad76d 100644
--- a/clang/lib/CodeGen/BackendUtil.cpp
+++ b/clang/lib/CodeGen/BackendUtil.cpp
@@ -1353,7 +1353,6 @@ runThinLTOBackend(CompilerInstance &CI, ModuleSummaryIndex *CombinedIndex,
   assert(OptLevelOrNone && "Invalid optimization level!");
   Conf.CGOptLevel = *OptLevelOrNone;
   Conf.OptLevel = CGOpts.OptimizationLevel;
-  // llvm::codegen::RegisterCodeGenFlags F;
   Conf.ModifyTargetOptions = [&](llvm::TargetOptions &TargetOpts) -> void {
     initTargetOptions(CI, Diags, TargetOpts);
   };
diff --git a/lld/COFF/LTO.cpp b/lld/COFF/LTO.cpp
index bf12b1af78733..0733723ffe1d7 100644
--- a/lld/COFF/LTO.cpp
+++ b/lld/COFF/LTO.cpp
@@ -65,9 +65,8 @@ lto::Config BitcodeCompiler::createConfig() {
     options.FunctionSections = true;
     options.DataSections = true;
 
-    if (emitAsm) {
+    if (emitAsm)
       options.MCOptions.AsmVerbose = true;
-    }
   };
 
   for (StringRef C : ctx.config.mllvmOpts)
diff --git a/lld/Common/TargetOptionsCommandFlags.cpp b/lld/Common/TargetOptionsCommandFlags.cpp
index 0860e8ff8ef8e..5eb08e6e54e30 100644
--- a/lld/Common/TargetOptionsCommandFlags.cpp
+++ b/lld/Common/TargetOptionsCommandFlags.cpp
@@ -12,10 +12,6 @@
 #include "llvm/TargetParser/Triple.h"
 #include <optional>
 
-llvm::TargetOptions lld::initTargetOptionsFromCodeGenFlags() {
-  return llvm::codegen::InitTargetOptionsFromCodeGenFlags(llvm::Triple());
-}
-
 std::optional<llvm::Reloc::Model> lld::getRelocModelFromCMModel() {
   return llvm::codegen::getExplicitRelocModel();
 }
diff --git a/lld/ELF/LTO.cpp b/lld/ELF/LTO.cpp
index 834e3ccbaeb2f..e2631ecf90d91 100644
--- a/lld/ELF/LTO.cpp
+++ b/lld/ELF/LTO.cpp
@@ -171,7 +171,6 @@ static lto::Config createConfig(Ctx &ctx) {
 
   if (ctx.arg.ltoEmitAsm) {
     c.CGFileType = CodeGenFileType::AssemblyFile;
-    // c.Options.MCOptions.AsmVerbose = true;
   }
 
   if (!ctx.arg.saveTempsArgs.empty())
diff --git a/lld/include/lld/Common/TargetOptionsCommandFlags.h b/lld/include/lld/Common/TargetOptionsCommandFlags.h
index 9bc22d441e183..4897126c90a79 100644
--- a/lld/include/lld/Common/TargetOptionsCommandFlags.h
+++ b/lld/include/lld/Common/TargetOptionsCommandFlags.h
@@ -18,7 +18,6 @@
 #include <optional>
 
 namespace lld {
-llvm::TargetOptions initTargetOptionsFromCodeGenFlags();
 std::optional<llvm::Reloc::Model> getRelocModelFromCMModel();
 std::optional<llvm::CodeModel::Model> getCodeModelFromCMModel();
 std::string getCPUStr();

>From 8e83b2b43a45aa4ff6589afb6180d25ed4e27563 Mon Sep 17 00:00:00 2001
From: Paul Kirth <paulkirth at google.com>
Date: Tue, 10 Feb 2026 14:10:42 -0800
Subject: [PATCH 05/27] Plumb the Triple through runThinLTO APIs

---
 llvm/include/llvm/LTO/LTO.h |  2 +-
 llvm/lib/LTO/LTO.cpp        | 38 ++++++++++++-------------------------
 2 files changed, 13 insertions(+), 27 deletions(-)

diff --git a/llvm/include/llvm/LTO/LTO.h b/llvm/include/llvm/LTO/LTO.h
index afb95c14bd070..be8f8ed4574c5 100644
--- a/llvm/include/llvm/LTO/LTO.h
+++ b/llvm/include/llvm/LTO/LTO.h
@@ -276,7 +276,7 @@ class ThinBackendProc {
       const FunctionImporter::ImportMapTy &ImportList,
       const FunctionImporter::ExportSetTy &ExportList,
       const std::map<GlobalValue::GUID, GlobalValue::LinkageTypes> &ResolvedODR,
-      MapVector<StringRef, BitcodeModule> &ModuleMap) = 0;
+      MapVector<StringRef, BitcodeModule> &ModuleMap, Triple TheTriple) = 0;
   virtual Error wait() {
     BackendThreadPool.wait();
     if (Err)
diff --git a/llvm/lib/LTO/LTO.cpp b/llvm/lib/LTO/LTO.cpp
index a91e377b89e92..747b14005293c 100644
--- a/llvm/lib/LTO/LTO.cpp
+++ b/llvm/lib/LTO/LTO.cpp
@@ -1621,7 +1621,7 @@ class InProcessThinBackend : public CGThinBackend {
       const FunctionImporter::ExportSetTy &ExportList,
       const std::map<GlobalValue::GUID, GlobalValue::LinkageTypes> &ResolvedODR,
       const GVSummaryMapTy &DefinedGlobals,
-      MapVector<StringRef, BitcodeModule> &ModuleMap) {
+      MapVector<StringRef, BitcodeModule> &ModuleMap, Triple TheTriple) {
     auto ModuleID = BM.getModuleIdentifier();
     LTOLLVMContext BackendContext(Conf);
     llvm::TimeTraceScope timeScope("Run ThinLTO backend thread (in-process)",
@@ -1647,15 +1647,10 @@ class InProcessThinBackend : public CGThinBackend {
       // no module hash.
       return RunThinBackend(AddStream);
 
-    auto Mod = ModuleMap.front().second.parseModule(BackendContext);
-    if (!Mod)
-      return Mod.takeError();
-    Triple TT = (*Mod)->getTargetTriple();
-
     // The module may be cached, this helps handling it.
     std::string Key = computeLTOCacheKey(
         Conf, CombinedIndex, ModuleID, ImportList, ExportList, ResolvedODR,
-        DefinedGlobals, TT, CfiFunctionDefs, CfiFunctionDecls);
+        DefinedGlobals, TheTriple, CfiFunctionDefs, CfiFunctionDecls);
     Expected<AddStreamFn> CacheAddStreamOrErr = Cache(Task, Key, ModuleID);
     if (Error Err = CacheAddStreamOrErr.takeError())
       return Err;
@@ -1671,7 +1666,7 @@ class InProcessThinBackend : public CGThinBackend {
       const FunctionImporter::ImportMapTy &ImportList,
       const FunctionImporter::ExportSetTy &ExportList,
       const std::map<GlobalValue::GUID, GlobalValue::LinkageTypes> &ResolvedODR,
-      MapVector<StringRef, BitcodeModule> &ModuleMap) override {
+      MapVector<StringRef, BitcodeModule> &ModuleMap, Triple TheTriple) override {
     StringRef ModulePath = BM.getModuleIdentifier();
     assert(ModuleToDefinedGVSummaries.count(ModulePath));
     const GVSummaryMapTy &DefinedGlobals =
@@ -1689,7 +1684,7 @@ class InProcessThinBackend : public CGThinBackend {
                                         "thin backend");
           Error E = runThinLTOBackendThread(
               AddStream, Cache, Task, BM, CombinedIndex, ImportList, ExportList,
-              ResolvedODR, DefinedGlobals, ModuleMap);
+              ResolvedODR, DefinedGlobals, ModuleMap, TheTriple);
           if (E) {
             std::unique_lock<std::mutex> L(ErrMu);
             if (Err)
@@ -1740,7 +1735,7 @@ class FirstRoundThinBackend : public InProcessThinBackend {
       const FunctionImporter::ExportSetTy &ExportList,
       const std::map<GlobalValue::GUID, GlobalValue::LinkageTypes> &ResolvedODR,
       const GVSummaryMapTy &DefinedGlobals,
-      MapVector<StringRef, BitcodeModule> &ModuleMap) override {
+      MapVector<StringRef, BitcodeModule> &ModuleMap, Triple TheTriple) override {
     auto ModuleID = BM.getModuleIdentifier();
     llvm::TimeTraceScope timeScope("Run ThinLTO backend thread (first round)",
                                    ModuleID);
@@ -1772,15 +1767,10 @@ class FirstRoundThinBackend : public InProcessThinBackend {
       // no module hash.
       return RunThinBackend(CGAddStream, IRAddStream);
 
-    auto Mod = ModuleMap.front().second.parseModule(BackendContext);
-    if (!Mod)
-      return Mod.takeError();
-    Triple TT = (*Mod)->getTargetTriple();
-
     // Get CGKey for caching object in CGCache.
     std::string CGKey = computeLTOCacheKey(
         Conf, CombinedIndex, ModuleID, ImportList, ExportList, ResolvedODR,
-        DefinedGlobals, TT, CfiFunctionDefs, CfiFunctionDecls);
+        DefinedGlobals, TheTriple, CfiFunctionDefs, CfiFunctionDecls);
     Expected<AddStreamFn> CacheCGAddStreamOrErr =
         CGCache(Task, CGKey, ModuleID);
     if (Error Err = CacheCGAddStreamOrErr.takeError())
@@ -1843,7 +1833,7 @@ class SecondRoundThinBackend : public InProcessThinBackend {
       const FunctionImporter::ExportSetTy &ExportList,
       const std::map<GlobalValue::GUID, GlobalValue::LinkageTypes> &ResolvedODR,
       const GVSummaryMapTy &DefinedGlobals,
-      MapVector<StringRef, BitcodeModule> &ModuleMap) override {
+      MapVector<StringRef, BitcodeModule> &ModuleMap, Triple TheTriple) override {
     auto ModuleID = BM.getModuleIdentifier();
     llvm::TimeTraceScope timeScope("Run ThinLTO backend thread (second round)",
                                    ModuleID);
@@ -1863,16 +1853,11 @@ class SecondRoundThinBackend : public InProcessThinBackend {
       // no module hash.
       return RunThinBackend(AddStream);
 
-    auto Mod = ModuleMap.front().second.parseModule(BackendContext);
-    if (!Mod)
-      return Mod.takeError();
-    Triple TT = (*Mod)->getTargetTriple();
-
     // Get Key for caching the final object file in Cache with the combined
     // CGData hash.
     std::string Key = computeLTOCacheKey(
         Conf, CombinedIndex, ModuleID, ImportList, ExportList, ResolvedODR,
-        DefinedGlobals, TT, CfiFunctionDefs, CfiFunctionDecls);
+        DefinedGlobals, TheTriple, CfiFunctionDefs, CfiFunctionDecls);
     Key = recomputeLTOCacheKey(Key,
                                /*ExtraID=*/std::to_string(CombinedCGDataHash));
     Expected<AddStreamFn> CacheAddStreamOrErr = Cache(Task, Key, ModuleID);
@@ -1966,7 +1951,7 @@ class WriteIndexesThinBackend : public ThinBackendProc {
       const FunctionImporter::ImportMapTy &ImportList,
       const FunctionImporter::ExportSetTy &ExportList,
       const std::map<GlobalValue::GUID, GlobalValue::LinkageTypes> &ResolvedODR,
-      MapVector<StringRef, BitcodeModule> &ModuleMap) override {
+      MapVector<StringRef, BitcodeModule> &ModuleMap, Triple TheTriple) override {
     StringRef ModulePath = BM.getModuleIdentifier();
 
     // The contents of this file may be used as input to a native link, and must
@@ -2231,6 +2216,7 @@ Error LTO::runThinLTO(AddStreamFn AddStream, FileCache Cache,
       ThinLTO.ModulesToCompile ? *ThinLTO.ModulesToCompile : ThinLTO.ModuleMap;
 
   auto RunBackends = [&](ThinBackendProc *BackendProcess) -> Error {
+    Triple TheTriple = RegularLTO.CombinedModule->getTargetTriple();
     auto ProcessOneModule = [&](int I) -> Error {
       auto &Mod = *(ModuleMap.begin() + I);
       // Tasks 0 through ParallelCodeGenParallelismLevel-1 are reserved for
@@ -2238,7 +2224,7 @@ Error LTO::runThinLTO(AddStreamFn AddStream, FileCache Cache,
       return BackendProcess->start(
           RegularLTO.ParallelCodeGenParallelismLevel + I, Mod.second,
           ImportLists[Mod.first], ExportLists[Mod.first],
-          ResolvedODR[Mod.first], ThinLTO.ModuleMap);
+          ResolvedODR[Mod.first], ThinLTO.ModuleMap, TheTriple);
     };
 
     BackendProcess->setup(ModuleMap.size(),
@@ -2512,7 +2498,7 @@ class OutOfProcessThinBackend : public CGThinBackend {
       const FunctionImporter::ImportMapTy &ImportList,
       const FunctionImporter::ExportSetTy &ExportList,
       const std::map<GlobalValue::GUID, GlobalValue::LinkageTypes> &ResolvedODR,
-      MapVector<StringRef, BitcodeModule> &ModuleMap) override {
+      MapVector<StringRef, BitcodeModule> &ModuleMap, class Triple TheTriple) override {
 
     StringRef ModulePath = BM.getModuleIdentifier();
 

>From 87fa36c4f845b672b063d2fd1c764cbb2b2fe3ac Mon Sep 17 00:00:00 2001
From: Paul Kirth <paulkirth at google.com>
Date: Tue, 10 Feb 2026 14:11:15 -0800
Subject: [PATCH 06/27] Fix formatting

---
 .../lld/Common/TargetOptionsCommandFlags.h        |  2 +-
 llvm/lib/LTO/LTO.cpp                              | 15 ++++++++++-----
 2 files changed, 11 insertions(+), 6 deletions(-)

diff --git a/lld/include/lld/Common/TargetOptionsCommandFlags.h b/lld/include/lld/Common/TargetOptionsCommandFlags.h
index 4897126c90a79..8b8ecba11f796 100644
--- a/lld/include/lld/Common/TargetOptionsCommandFlags.h
+++ b/lld/include/lld/Common/TargetOptionsCommandFlags.h
@@ -22,6 +22,6 @@ std::optional<llvm::Reloc::Model> getRelocModelFromCMModel();
 std::optional<llvm::CodeModel::Model> getCodeModelFromCMModel();
 std::string getCPUStr();
 std::vector<std::string> getMAttrs();
-}
+} // namespace lld
 
 #endif
diff --git a/llvm/lib/LTO/LTO.cpp b/llvm/lib/LTO/LTO.cpp
index 747b14005293c..ee973a2b9617b 100644
--- a/llvm/lib/LTO/LTO.cpp
+++ b/llvm/lib/LTO/LTO.cpp
@@ -1666,7 +1666,8 @@ class InProcessThinBackend : public CGThinBackend {
       const FunctionImporter::ImportMapTy &ImportList,
       const FunctionImporter::ExportSetTy &ExportList,
       const std::map<GlobalValue::GUID, GlobalValue::LinkageTypes> &ResolvedODR,
-      MapVector<StringRef, BitcodeModule> &ModuleMap, Triple TheTriple) override {
+      MapVector<StringRef, BitcodeModule> &ModuleMap,
+      Triple TheTriple) override {
     StringRef ModulePath = BM.getModuleIdentifier();
     assert(ModuleToDefinedGVSummaries.count(ModulePath));
     const GVSummaryMapTy &DefinedGlobals =
@@ -1735,7 +1736,8 @@ class FirstRoundThinBackend : public InProcessThinBackend {
       const FunctionImporter::ExportSetTy &ExportList,
       const std::map<GlobalValue::GUID, GlobalValue::LinkageTypes> &ResolvedODR,
       const GVSummaryMapTy &DefinedGlobals,
-      MapVector<StringRef, BitcodeModule> &ModuleMap, Triple TheTriple) override {
+      MapVector<StringRef, BitcodeModule> &ModuleMap,
+      Triple TheTriple) override {
     auto ModuleID = BM.getModuleIdentifier();
     llvm::TimeTraceScope timeScope("Run ThinLTO backend thread (first round)",
                                    ModuleID);
@@ -1833,7 +1835,8 @@ class SecondRoundThinBackend : public InProcessThinBackend {
       const FunctionImporter::ExportSetTy &ExportList,
       const std::map<GlobalValue::GUID, GlobalValue::LinkageTypes> &ResolvedODR,
       const GVSummaryMapTy &DefinedGlobals,
-      MapVector<StringRef, BitcodeModule> &ModuleMap, Triple TheTriple) override {
+      MapVector<StringRef, BitcodeModule> &ModuleMap,
+      Triple TheTriple) override {
     auto ModuleID = BM.getModuleIdentifier();
     llvm::TimeTraceScope timeScope("Run ThinLTO backend thread (second round)",
                                    ModuleID);
@@ -1951,7 +1954,8 @@ class WriteIndexesThinBackend : public ThinBackendProc {
       const FunctionImporter::ImportMapTy &ImportList,
       const FunctionImporter::ExportSetTy &ExportList,
       const std::map<GlobalValue::GUID, GlobalValue::LinkageTypes> &ResolvedODR,
-      MapVector<StringRef, BitcodeModule> &ModuleMap, Triple TheTriple) override {
+      MapVector<StringRef, BitcodeModule> &ModuleMap,
+      Triple TheTriple) override {
     StringRef ModulePath = BM.getModuleIdentifier();
 
     // The contents of this file may be used as input to a native link, and must
@@ -2498,7 +2502,8 @@ class OutOfProcessThinBackend : public CGThinBackend {
       const FunctionImporter::ImportMapTy &ImportList,
       const FunctionImporter::ExportSetTy &ExportList,
       const std::map<GlobalValue::GUID, GlobalValue::LinkageTypes> &ResolvedODR,
-      MapVector<StringRef, BitcodeModule> &ModuleMap, class Triple TheTriple) override {
+      MapVector<StringRef, BitcodeModule> &ModuleMap,
+      class Triple TheTriple) override {
 
     StringRef ModulePath = BM.getModuleIdentifier();
 

>From 8ea8da82d6c080f841ccd0ec32588bf80e24ed10 Mon Sep 17 00:00:00 2001
From: Paul Kirth <paulkirth at google.com>
Date: Tue, 10 Feb 2026 14:53:45 -0800
Subject: [PATCH 07/27] Restore static API name in LTOBackend.cpp

---
 llvm/lib/LTO/LTOBackend.cpp | 10 +++++-----
 1 file changed, 5 insertions(+), 5 deletions(-)

diff --git a/llvm/lib/LTO/LTOBackend.cpp b/llvm/lib/LTO/LTOBackend.cpp
index 1c05323974f99..72820807afd11 100644
--- a/llvm/lib/LTO/LTOBackend.cpp
+++ b/llvm/lib/LTO/LTOBackend.cpp
@@ -441,7 +441,7 @@ bool lto::opt(const Config &Conf, TargetMachine *TM, unsigned Task, Module &Mod,
   return !Conf.PostOptModuleHook || Conf.PostOptModuleHook(Task, Mod);
 }
 
-static void startCodegen(const Config &Conf, TargetMachine *TM,
+static void codegen(const Config &Conf, TargetMachine *TM,
                          AddStreamFn AddStream, unsigned Task, Module &Mod,
                          const ModuleSummaryIndex &CombinedIndex) {
   llvm::TimeTraceScope timeScope("codegen");
@@ -554,7 +554,7 @@ static void splitCodeGen(const Config &C, TargetMachine *TM,
               std::unique_ptr<TargetMachine> TM =
                   createTargetMachine(C, T, *MPartInCtx);
 
-              startCodegen(C, TM.get(), AddStream, ThreadId, *MPartInCtx,
+              ::codegen(C, TM.get(), AddStream, ThreadId, *MPartInCtx,
                            CombinedIndex);
             },
             // Pass BC using std::move to ensure that it get moved rather than
@@ -620,7 +620,7 @@ Error lto::backend(const Config &C, AddStreamFn AddStream,
   }
 
   if (ParallelCodeGenParallelismLevel == 1) {
-    startCodegen(C, TM.get(), AddStream, 0, Mod, CombinedIndex);
+    ::codegen(C, TM.get(), AddStream, 0, Mod, CombinedIndex);
   } else {
     splitCodeGen(C, TM.get(), AddStream, ParallelCodeGenParallelismLevel, Mod,
                  CombinedIndex);
@@ -682,7 +682,7 @@ Error lto::thinBackend(const Config &Conf, unsigned Task, AddStreamFn AddStream,
   if (CodeGenOnly) {
     // If CodeGenOnly is set, we only perform code generation and skip
     // optimization. This value may differ from Conf.CodeGenOnly.
-    startCodegen(Conf, TM.get(), AddStream, Task, Mod, CombinedIndex);
+    ::codegen(Conf, TM.get(), AddStream, Task, Mod, CombinedIndex);
     return finalizeOptimizationRemarks(std::move(DiagnosticOutputFile));
   }
 
@@ -705,7 +705,7 @@ Error lto::thinBackend(const Config &Conf, unsigned Task, AddStreamFn AddStream,
         if (IRAddStream)
           cgdata::saveModuleForTwoRounds(Mod, Task, IRAddStream);
 
-        startCodegen(Conf, TM, AddStream, Task, Mod, CombinedIndex);
+        ::codegen(Conf, TM, AddStream, Task, Mod, CombinedIndex);
         return finalizeOptimizationRemarks(std::move(DiagnosticOutputFile));
       };
 

>From c530964955cb8bccac499dfa780569a416086259 Mon Sep 17 00:00:00 2001
From: Paul Kirth <paulkirth at google.com>
Date: Tue, 10 Feb 2026 15:30:01 -0800
Subject: [PATCH 08/27] Clang Format

---
 llvm/lib/LTO/LTOBackend.cpp | 6 +++---
 1 file changed, 3 insertions(+), 3 deletions(-)

diff --git a/llvm/lib/LTO/LTOBackend.cpp b/llvm/lib/LTO/LTOBackend.cpp
index 72820807afd11..536fa4c2a268d 100644
--- a/llvm/lib/LTO/LTOBackend.cpp
+++ b/llvm/lib/LTO/LTOBackend.cpp
@@ -442,8 +442,8 @@ bool lto::opt(const Config &Conf, TargetMachine *TM, unsigned Task, Module &Mod,
 }
 
 static void codegen(const Config &Conf, TargetMachine *TM,
-                         AddStreamFn AddStream, unsigned Task, Module &Mod,
-                         const ModuleSummaryIndex &CombinedIndex) {
+                    AddStreamFn AddStream, unsigned Task, Module &Mod,
+                    const ModuleSummaryIndex &CombinedIndex) {
   llvm::TimeTraceScope timeScope("codegen");
   if (Conf.PreCodeGenModuleHook && !Conf.PreCodeGenModuleHook(Task, Mod))
     return;
@@ -555,7 +555,7 @@ static void splitCodeGen(const Config &C, TargetMachine *TM,
                   createTargetMachine(C, T, *MPartInCtx);
 
               ::codegen(C, TM.get(), AddStream, ThreadId, *MPartInCtx,
-                           CombinedIndex);
+                        CombinedIndex);
             },
             // Pass BC using std::move to ensure that it get moved rather than
             // copied into the thread's context.

>From cfd1fa41f3285e5e2a87d8f250df085930904711 Mon Sep 17 00:00:00 2001
From: Paul Kirth <paulkirth at google.com>
Date: Wed, 11 Feb 2026 10:18:58 -0800
Subject: [PATCH 09/27] Use ModifyTargetOptions to update DataSections

---
 llvm/lib/LTO/LTOCodeGenerator.cpp | 10 ++++++----
 1 file changed, 6 insertions(+), 4 deletions(-)

diff --git a/llvm/lib/LTO/LTOCodeGenerator.cpp b/llvm/lib/LTO/LTOCodeGenerator.cpp
index dfa6fad4c715f..7d0171a138494 100644
--- a/llvm/lib/LTO/LTOCodeGenerator.cpp
+++ b/llvm/lib/LTO/LTOCodeGenerator.cpp
@@ -394,10 +394,12 @@ bool LTOCodeGenerator::determineTarget() {
   if (Config.CPU.empty())
     Config.CPU = lto::getThinLTODefaultCPU(MergedModule->getTargetTriple());
 
-  // If data-sections is not explicitly set or unset, set data-sections by
-  // default to match the behaviour of lld and gold plugin.
-  if (!codegen::getExplicitDataSections())
-    TO.DataSections = true;
+  Config.ModifyTargetOptions = [](TargetOptions &Options) {
+    // If data-sections is not explicitly set or unset, set data-sections by
+    // default to match the behaviour of lld and gold plugin.
+    if (!codegen::getExplicitDataSections())
+      Options.DataSections = true;
+  };
 
   TargetMach = createTargetMachine();
   assert(TargetMach && "Unable to create target machine");

>From 62a899b59ac7f8f5dd69aad773dccd267cbd30cf Mon Sep 17 00:00:00 2001
From: Paul Kirth <paulkirth at google.com>
Date: Wed, 11 Feb 2026 10:55:47 -0800
Subject: [PATCH 10/27] Save work

---
 lld/ELF/LTO.cpp | 29 ++++++++++++++++++++---------
 1 file changed, 20 insertions(+), 9 deletions(-)

diff --git a/lld/ELF/LTO.cpp b/lld/ELF/LTO.cpp
index e2631ecf90d91..b019a269f4f6c 100644
--- a/lld/ELF/LTO.cpp
+++ b/lld/ELF/LTO.cpp
@@ -56,8 +56,26 @@ static std::string getThinLTOOutputFile(Ctx &ctx, StringRef modulePath) {
 static lto::Config createConfig(Ctx &ctx) {
   lto::Config c;
 
+  std::shared_ptr<MemoryBuffer> MBPtr;
+  if (!ctx.arg.ltoBasicBlockSections.empty()) {
+    if (ctx.arg.ltoBasicBlockSections == "all") {
+    } else if (ctx.arg.ltoBasicBlockSections == "labels") {
+    } else if (ctx.arg.ltoBasicBlockSections == "none") {
+    } else {
+      ErrorOr<std::unique_ptr<MemoryBuffer>> MBOrErr =
+          MemoryBuffer::getFile(ctx.arg.ltoBasicBlockSections.str());
+      if (!MBOrErr) {
+        ErrAlways(ctx) << "cannot open " << ctx.arg.ltoBasicBlockSections << ":"
+                       << MBOrErr.getError().message();
+      } else {
+        MBPtr = std::move(*MBOrErr);
+      }
+    }
+  }
+
   // Set up the callback to modify TargetOptions.
-  c.ModifyTargetOptions = [&](TargetOptions &Options) -> void {
+  c.ModifyTargetOptions =
+      [&ctx, MB = std::move(MBPtr)](TargetOptions &Options) -> void {
     // LLD supports the new relocations and address-significance tables.
     Options.EmitAddrsig = true;
     // Always emit a section per function/datum with LTO.
@@ -79,14 +97,7 @@ static lto::Config createConfig(Ctx &ctx) {
       } else if (ctx.arg.ltoBasicBlockSections == "none") {
         Options.BBSections = BasicBlockSection::None;
       } else {
-        ErrorOr<std::unique_ptr<MemoryBuffer>> MBOrErr =
-            MemoryBuffer::getFile(ctx.arg.ltoBasicBlockSections.str());
-        if (!MBOrErr) {
-          ErrAlways(ctx) << "cannot open " << ctx.arg.ltoBasicBlockSections
-                         << ":" << MBOrErr.getError().message();
-        } else {
-          Options.BBSectionsFuncListBuf = std::move(*MBOrErr);
-        }
+        Options.BBSectionsFuncListBuf = std::move(MB);
         Options.BBSections = BasicBlockSection::List;
       }
     }

>From b1c4b1682d20259203c215d31be710a8900cc527 Mon Sep 17 00:00:00 2001
From: Paul Kirth <paulkirth at google.com>
Date: Wed, 11 Feb 2026 11:12:40 -0800
Subject: [PATCH 11/27] Preallocate MemoryBuffer for callback

---
 lld/ELF/LTO.cpp | 57 +++++++++++++++++++++++++------------------------
 1 file changed, 29 insertions(+), 28 deletions(-)

diff --git a/lld/ELF/LTO.cpp b/lld/ELF/LTO.cpp
index b019a269f4f6c..08d704eff181d 100644
--- a/lld/ELF/LTO.cpp
+++ b/lld/ELF/LTO.cpp
@@ -53,34 +53,35 @@ static std::string getThinLTOOutputFile(Ctx &ctx, StringRef modulePath) {
                                    ctx.arg.thinLTOPrefixReplaceNew);
 }
 
+static std::shared_ptr<MemoryBuffer> getMemoryBuffer(Ctx &ctx) {
+  if (ctx.arg.ltoBasicBlockSections.empty() ||
+      ctx.arg.ltoBasicBlockSections == "all" ||
+      ctx.arg.ltoBasicBlockSections == "labels" ||
+      ctx.arg.ltoBasicBlockSections == "none")
+    return nullptr;
+
+  ErrorOr<std::unique_ptr<MemoryBuffer>> mbOrErr =
+      MemoryBuffer::getFile(ctx.arg.ltoBasicBlockSections.str());
+  if (!mbOrErr) {
+    ErrAlways(ctx) << "cannot open " << ctx.arg.ltoBasicBlockSections << ":"
+                   << mbOrErr.getError().message();
+  }
+  return std::move(*mbOrErr);
+}
+
 static lto::Config createConfig(Ctx &ctx) {
   lto::Config c;
 
-  std::shared_ptr<MemoryBuffer> MBPtr;
-  if (!ctx.arg.ltoBasicBlockSections.empty()) {
-    if (ctx.arg.ltoBasicBlockSections == "all") {
-    } else if (ctx.arg.ltoBasicBlockSections == "labels") {
-    } else if (ctx.arg.ltoBasicBlockSections == "none") {
-    } else {
-      ErrorOr<std::unique_ptr<MemoryBuffer>> MBOrErr =
-          MemoryBuffer::getFile(ctx.arg.ltoBasicBlockSections.str());
-      if (!MBOrErr) {
-        ErrAlways(ctx) << "cannot open " << ctx.arg.ltoBasicBlockSections << ":"
-                       << MBOrErr.getError().message();
-      } else {
-        MBPtr = std::move(*MBOrErr);
-      }
-    }
-  }
+  std::shared_ptr<MemoryBuffer> mbPtr = getMemoryBuffer(ctx);
 
   // Set up the callback to modify TargetOptions.
   c.ModifyTargetOptions =
-      [&ctx, MB = std::move(MBPtr)](TargetOptions &Options) -> void {
+      [&ctx, mb = std::move(mbPtr)](TargetOptions &options) -> void {
     // LLD supports the new relocations and address-significance tables.
-    Options.EmitAddrsig = true;
+    options.EmitAddrsig = true;
     // Always emit a section per function/datum with LTO.
-    Options.FunctionSections = true;
-    Options.DataSections = true;
+    options.FunctionSections = true;
+    options.DataSections = true;
 
     // Check if basic block sections must be used.
     // Allowed values for --lto-basic-block-sections are "all",
@@ -88,26 +89,26 @@ static lto::Config createConfig(Ctx &ctx) {
     // equivalent of -fbasic-block-sections= flag in clang.
     if (!ctx.arg.ltoBasicBlockSections.empty()) {
       if (ctx.arg.ltoBasicBlockSections == "all") {
-        Options.BBSections = BasicBlockSection::All;
+        options.BBSections = BasicBlockSection::All;
       } else if (ctx.arg.ltoBasicBlockSections == "labels") {
-        Options.BBAddrMap = true;
+        options.BBAddrMap = true;
         Warn(ctx)
             << "'--lto-basic-block-sections=labels' is deprecated; Please use "
                "'--lto-basic-block-address-map' instead";
       } else if (ctx.arg.ltoBasicBlockSections == "none") {
-        Options.BBSections = BasicBlockSection::None;
+        options.BBSections = BasicBlockSection::None;
       } else {
-        Options.BBSectionsFuncListBuf = std::move(MB);
-        Options.BBSections = BasicBlockSection::List;
+        options.BBSectionsFuncListBuf = std::move(mb);
+        options.BBSections = BasicBlockSection::List;
       }
     }
 
-    Options.BBAddrMap = ctx.arg.ltoBBAddrMap;
+    options.BBAddrMap = ctx.arg.ltoBBAddrMap;
 
-    Options.UniqueBasicBlockSectionNames =
+    options.UniqueBasicBlockSectionNames =
         ctx.arg.ltoUniqueBasicBlockSectionNames;
     if (ctx.arg.ltoEmitAsm) {
-      Options.MCOptions.AsmVerbose = true;
+      options.MCOptions.AsmVerbose = true;
     }
   };
 

>From efbf967d1df408c4b7915bcfbefef7bca7c8da48 Mon Sep 17 00:00:00 2001
From: Paul Kirth <paulkirth at google.com>
Date: Wed, 11 Feb 2026 15:20:15 -0800
Subject: [PATCH 12/27] Update comment and reuse local variable

---
 llvm/lib/LTO/LTO.cpp | 15 +++++++++------
 1 file changed, 9 insertions(+), 6 deletions(-)

diff --git a/llvm/lib/LTO/LTO.cpp b/llvm/lib/LTO/LTO.cpp
index ee973a2b9617b..0df2abac0630d 100644
--- a/llvm/lib/LTO/LTO.cpp
+++ b/llvm/lib/LTO/LTO.cpp
@@ -177,10 +177,12 @@ std::string llvm::computeLTOCacheKey(
   TargetOptions Opts = codegen::InitTargetOptionsFromCodeGenFlags(TT);
   Conf.ModifyTargetOptions(Opts);
 
-  // FIXME: Hash more of Options. For now all clients initialize Options from
-  // command-line flags (which is unsupported in production), but may set
-  // X86RelaxRelocations. The clang driver can also pass FunctionSections,
-  // DataSections and DebuggerTuning via command line flags.
+  // FIXME: Hash more of TargetOptions. For now all clients initialize
+  // TargetOptions from command-line flags (which is unsupported in production),
+  // but may set X86RelaxRelocations. The clang driver can also pass
+  // FunctionSections, DataSections and DebuggerTuning via command line flags.
+  // Alternatively, we could store these options in the IR, instead, which has
+  // other trade-offs.
   AddUnsigned(Opts.MCOptions.X86RelaxRelocations);
   AddUnsigned(Opts.FunctionSections);
   AddUnsigned(Opts.DataSections);
@@ -2220,7 +2222,8 @@ Error LTO::runThinLTO(AddStreamFn AddStream, FileCache Cache,
       ThinLTO.ModulesToCompile ? *ThinLTO.ModulesToCompile : ThinLTO.ModuleMap;
 
   auto RunBackends = [&](ThinBackendProc *BackendProcess) -> Error {
-    Triple TheTriple = RegularLTO.CombinedModule->getTargetTriple();
+    const Triple &TheTriple = RegularLTO.CombinedModule->getTargetTriple();
+    assert(!TheTriple.getTriple().empty() && "Empty TargetTriple for ThinLTO!");
     auto ProcessOneModule = [&](int I) -> Error {
       auto &Mod = *(ModuleMap.begin() + I);
       // Tasks 0 through ParallelCodeGenParallelismLevel-1 are reserved for
@@ -2233,7 +2236,7 @@ Error LTO::runThinLTO(AddStreamFn AddStream, FileCache Cache,
 
     BackendProcess->setup(ModuleMap.size(),
                           RegularLTO.ParallelCodeGenParallelismLevel,
-                          RegularLTO.CombinedModule->getTargetTriple());
+                          TheTriple);
 
     if (BackendProcess->getThreadCount() == 1 ||
         BackendProcess->isSensitiveToInputOrder()) {

>From 1018a36c52777b78458007220bbf15ef11b76e43 Mon Sep 17 00:00:00 2001
From: Paul Kirth <paulkirth at google.com>
Date: Thu, 12 Feb 2026 13:33:19 -0800
Subject: [PATCH 13/27] Rename helper function for clarity

---
 lld/ELF/LTO.cpp | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/lld/ELF/LTO.cpp b/lld/ELF/LTO.cpp
index 08d704eff181d..d4e53c100e442 100644
--- a/lld/ELF/LTO.cpp
+++ b/lld/ELF/LTO.cpp
@@ -53,7 +53,7 @@ static std::string getThinLTOOutputFile(Ctx &ctx, StringRef modulePath) {
                                    ctx.arg.thinLTOPrefixReplaceNew);
 }
 
-static std::shared_ptr<MemoryBuffer> getMemoryBuffer(Ctx &ctx) {
+static std::shared_ptr<MemoryBuffer> getBBSectionsMemoryBuffer(Ctx &ctx) {
   if (ctx.arg.ltoBasicBlockSections.empty() ||
       ctx.arg.ltoBasicBlockSections == "all" ||
       ctx.arg.ltoBasicBlockSections == "labels" ||
@@ -72,7 +72,7 @@ static std::shared_ptr<MemoryBuffer> getMemoryBuffer(Ctx &ctx) {
 static lto::Config createConfig(Ctx &ctx) {
   lto::Config c;
 
-  std::shared_ptr<MemoryBuffer> mbPtr = getMemoryBuffer(ctx);
+  std::shared_ptr<MemoryBuffer> mbPtr = getBBSectionsMemoryBuffer(ctx);
 
   // Set up the callback to modify TargetOptions.
   c.ModifyTargetOptions =

>From 5567483495a9a38279b3288c3376d9852af8d2a8 Mon Sep 17 00:00:00 2001
From: Paul Kirth <paulkirth at google.com>
Date: Thu, 12 Feb 2026 13:34:13 -0800
Subject: [PATCH 14/27] Don't issue warning in callback

---
 lld/ELF/LTO.cpp | 11 +++++++----
 1 file changed, 7 insertions(+), 4 deletions(-)

diff --git a/lld/ELF/LTO.cpp b/lld/ELF/LTO.cpp
index d4e53c100e442..acdc3d26ffa1a 100644
--- a/lld/ELF/LTO.cpp
+++ b/lld/ELF/LTO.cpp
@@ -56,10 +56,16 @@ static std::string getThinLTOOutputFile(Ctx &ctx, StringRef modulePath) {
 static std::shared_ptr<MemoryBuffer> getBBSectionsMemoryBuffer(Ctx &ctx) {
   if (ctx.arg.ltoBasicBlockSections.empty() ||
       ctx.arg.ltoBasicBlockSections == "all" ||
-      ctx.arg.ltoBasicBlockSections == "labels" ||
       ctx.arg.ltoBasicBlockSections == "none")
     return nullptr;
 
+  if (ctx.arg.ltoBasicBlockSections == "labels") {
+    Warn(ctx)
+        << "'--lto-basic-block-sections=labels' is deprecated; Please use "
+           "'--lto-basic-block-address-map' instead";
+    return nullptr;
+  }
+
   ErrorOr<std::unique_ptr<MemoryBuffer>> mbOrErr =
       MemoryBuffer::getFile(ctx.arg.ltoBasicBlockSections.str());
   if (!mbOrErr) {
@@ -92,9 +98,6 @@ static lto::Config createConfig(Ctx &ctx) {
         options.BBSections = BasicBlockSection::All;
       } else if (ctx.arg.ltoBasicBlockSections == "labels") {
         options.BBAddrMap = true;
-        Warn(ctx)
-            << "'--lto-basic-block-sections=labels' is deprecated; Please use "
-               "'--lto-basic-block-address-map' instead";
       } else if (ctx.arg.ltoBasicBlockSections == "none") {
         options.BBSections = BasicBlockSection::None;
       } else {

>From 6b060ba8fbd2da22d6a05a67bded5157c334a174 Mon Sep 17 00:00:00 2001
From: Paul Kirth <paulkirth at google.com>
Date: Thu, 12 Feb 2026 13:35:37 -0800
Subject: [PATCH 15/27] Ensure callback behaves consistently across calls

Using std::move() could make this unsafe to call multiple times, so avoid that.
---
 lld/ELF/LTO.cpp | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/lld/ELF/LTO.cpp b/lld/ELF/LTO.cpp
index acdc3d26ffa1a..ac6a391d32830 100644
--- a/lld/ELF/LTO.cpp
+++ b/lld/ELF/LTO.cpp
@@ -101,7 +101,7 @@ static lto::Config createConfig(Ctx &ctx) {
       } else if (ctx.arg.ltoBasicBlockSections == "none") {
         options.BBSections = BasicBlockSection::None;
       } else {
-        options.BBSectionsFuncListBuf = std::move(mb);
+        options.BBSectionsFuncListBuf = mb;
         options.BBSections = BasicBlockSection::List;
       }
     }

>From 9491cb009676efc1b81e163c6bb56a1f771f9b12 Mon Sep 17 00:00:00 2001
From: Paul Kirth <paulkirth at google.com>
Date: Thu, 12 Feb 2026 13:38:20 -0800
Subject: [PATCH 16/27] Remove redundant code

---
 llvm/tools/llvm-lto2/llvm-lto2.cpp | 1 -
 1 file changed, 1 deletion(-)

diff --git a/llvm/tools/llvm-lto2/llvm-lto2.cpp b/llvm/tools/llvm-lto2/llvm-lto2.cpp
index 73a695bdb6382..b1e4fa0e760ab 100644
--- a/llvm/tools/llvm-lto2/llvm-lto2.cpp
+++ b/llvm/tools/llvm-lto2/llvm-lto2.cpp
@@ -347,7 +347,6 @@ static int run(int argc, char **argv) {
     Conf.TimeTraceGranularity = TimeTraceGranularity;
   }
   Conf.CPU = codegen::getMCPU();
-  Conf.ModifyTargetOptions = [](llvm::TargetOptions &TO) {};
   Conf.MAttrs = codegen::getMAttrs();
   if (auto RM = codegen::getExplicitRelocModel())
     Conf.RelocModel = *RM;

>From 1e86b83d193332934477423b0f761180910d0d94 Mon Sep 17 00:00:00 2001
From: Paul Kirth <paulkirth at google.com>
Date: Fri, 13 Feb 2026 09:31:37 -0800
Subject: [PATCH 17/27] Move BackendContext back into lambda

---
 llvm/lib/LTO/LTO.cpp | 8 ++++----
 1 file changed, 4 insertions(+), 4 deletions(-)

diff --git a/llvm/lib/LTO/LTO.cpp b/llvm/lib/LTO/LTO.cpp
index 0df2abac0630d..5f1a7ffac5e78 100644
--- a/llvm/lib/LTO/LTO.cpp
+++ b/llvm/lib/LTO/LTO.cpp
@@ -1625,10 +1625,10 @@ class InProcessThinBackend : public CGThinBackend {
       const GVSummaryMapTy &DefinedGlobals,
       MapVector<StringRef, BitcodeModule> &ModuleMap, Triple TheTriple) {
     auto ModuleID = BM.getModuleIdentifier();
-    LTOLLVMContext BackendContext(Conf);
     llvm::TimeTraceScope timeScope("Run ThinLTO backend thread (in-process)",
                                    ModuleID);
     auto RunThinBackend = [&](AddStreamFn AddStream) {
+      LTOLLVMContext BackendContext(Conf);
       Expected<std::unique_ptr<Module>> MOrErr = BM.parseModule(BackendContext);
       if (!MOrErr)
         return MOrErr.takeError();
@@ -1743,9 +1743,9 @@ class FirstRoundThinBackend : public InProcessThinBackend {
     auto ModuleID = BM.getModuleIdentifier();
     llvm::TimeTraceScope timeScope("Run ThinLTO backend thread (first round)",
                                    ModuleID);
-    LTOLLVMContext BackendContext(Conf);
     auto RunThinBackend = [&](AddStreamFn CGAddStream,
                               AddStreamFn IRAddStream) {
+      LTOLLVMContext BackendContext(Conf);
       Expected<std::unique_ptr<Module>> MOrErr = BM.parseModule(BackendContext);
       if (!MOrErr)
         return MOrErr.takeError();
@@ -1842,8 +1842,8 @@ class SecondRoundThinBackend : public InProcessThinBackend {
     auto ModuleID = BM.getModuleIdentifier();
     llvm::TimeTraceScope timeScope("Run ThinLTO backend thread (second round)",
                                    ModuleID);
-    LTOLLVMContext BackendContext(Conf);
     auto RunThinBackend = [&](AddStreamFn AddStream) {
+      LTOLLVMContext BackendContext(Conf);
       std::unique_ptr<Module> LoadedModule =
           cgdata::loadModuleForTwoRounds(BM, Task, BackendContext, *IRFiles);
 
@@ -2506,7 +2506,7 @@ class OutOfProcessThinBackend : public CGThinBackend {
       const FunctionImporter::ExportSetTy &ExportList,
       const std::map<GlobalValue::GUID, GlobalValue::LinkageTypes> &ResolvedODR,
       MapVector<StringRef, BitcodeModule> &ModuleMap,
-      class Triple TheTriple) override {
+      llvm::Triple TheTriple) override {
 
     StringRef ModulePath = BM.getModuleIdentifier();
 

>From d40f79bb5bf2e698c2029ff1a8c78001e0d25d70 Mon Sep 17 00:00:00 2001
From: Paul Kirth <paulkirth at google.com>
Date: Fri, 13 Feb 2026 16:14:15 -0800
Subject: [PATCH 18/27] Update callback to return TargetOptions

---
 clang/lib/CodeGen/BackendUtil.cpp | 5 ++++-
 lld/COFF/LTO.cpp                  | 5 ++++-
 lld/ELF/LTO.cpp                   | 6 ++++--
 lld/MachO/LTO.cpp                 | 4 +++-
 lld/wasm/LTO.cpp                  | 4 +++-
 llvm/include/llvm/LTO/Config.h    | 7 +++++--
 llvm/lib/LTO/LTO.cpp              | 6 ++----
 llvm/lib/LTO/LTOBackend.cpp       | 4 +---
 llvm/lib/LTO/LTOCodeGenerator.cpp | 6 ++++--
 9 files changed, 30 insertions(+), 17 deletions(-)

diff --git a/clang/lib/CodeGen/BackendUtil.cpp b/clang/lib/CodeGen/BackendUtil.cpp
index f1895f30ad76d..4422b5f2f7a4d 100644
--- a/clang/lib/CodeGen/BackendUtil.cpp
+++ b/clang/lib/CodeGen/BackendUtil.cpp
@@ -1353,8 +1353,11 @@ runThinLTOBackend(CompilerInstance &CI, ModuleSummaryIndex *CombinedIndex,
   assert(OptLevelOrNone && "Invalid optimization level!");
   Conf.CGOptLevel = *OptLevelOrNone;
   Conf.OptLevel = CGOpts.OptimizationLevel;
-  Conf.ModifyTargetOptions = [&](llvm::TargetOptions &TargetOpts) -> void {
+  Conf.InitTargetOptions = [&](llvm::Triple TheTriple) -> llvm::TargetOptions {
+    llvm::TargetOptions TargetOpts =
+        llvm::codegen::InitTargetOptionsFromCodeGenFlags(TheTriple);
     initTargetOptions(CI, Diags, TargetOpts);
+    return TargetOpts;
   };
   Conf.SampleProfile = std::move(SampleProfile);
   Conf.PTO.LoopUnrolling = CGOpts.UnrollLoops;
diff --git a/lld/COFF/LTO.cpp b/lld/COFF/LTO.cpp
index 0733723ffe1d7..44e3b0caeed7a 100644
--- a/lld/COFF/LTO.cpp
+++ b/lld/COFF/LTO.cpp
@@ -24,6 +24,7 @@
 #include "llvm/IR/DiagnosticPrinter.h"
 #include "llvm/LTO/Config.h"
 #include "llvm/LTO/LTO.h"
+#include "llvm/CodeGen/CommandFlags.h"
 #include "llvm/Support/Caching.h"
 #include "llvm/Support/CodeGen.h"
 #include "llvm/Support/MemoryBuffer.h"
@@ -57,7 +58,8 @@ std::string BitcodeCompiler::getThinLTOOutputFile(StringRef path) {
 lto::Config BitcodeCompiler::createConfig() {
   lto::Config c;
   bool emitAsm = ctx.config.emit == EmitKind::ASM;
-  c.ModifyTargetOptions = [emitAsm](TargetOptions &options) {
+  c.InitTargetOptions = [emitAsm](const Triple &TT) {
+    TargetOptions options = codegen::InitTargetOptionsFromCodeGenFlags(TT);
     options.EmitAddrsig = true;
     // Always emit a section per function/datum with LTO. LLVM LTO should get
     // most of the benefit of linker GC, but there are still opportunities for
@@ -67,6 +69,7 @@ lto::Config BitcodeCompiler::createConfig() {
 
     if (emitAsm)
       options.MCOptions.AsmVerbose = true;
+    return options;
   };
 
   for (StringRef C : ctx.config.mllvmOpts)
diff --git a/lld/ELF/LTO.cpp b/lld/ELF/LTO.cpp
index ac6a391d32830..4288c04710d42 100644
--- a/lld/ELF/LTO.cpp
+++ b/lld/ELF/LTO.cpp
@@ -81,8 +81,9 @@ static lto::Config createConfig(Ctx &ctx) {
   std::shared_ptr<MemoryBuffer> mbPtr = getBBSectionsMemoryBuffer(ctx);
 
   // Set up the callback to modify TargetOptions.
-  c.ModifyTargetOptions =
-      [&ctx, mb = std::move(mbPtr)](TargetOptions &options) -> void {
+  c.InitTargetOptions =
+      [&ctx, mb = std::move(mbPtr)](const Triple &TT) -> TargetOptions {
+    TargetOptions options = codegen::InitTargetOptionsFromCodeGenFlags(TT);
     // LLD supports the new relocations and address-significance tables.
     options.EmitAddrsig = true;
     // Always emit a section per function/datum with LTO.
@@ -113,6 +114,7 @@ static lto::Config createConfig(Ctx &ctx) {
     if (ctx.arg.ltoEmitAsm) {
       options.MCOptions.AsmVerbose = true;
     }
+    return options;
   };
 
   for (StringRef C : ctx.arg.mllvmOpts)
diff --git a/lld/MachO/LTO.cpp b/lld/MachO/LTO.cpp
index 19c1bf25c86d7..1d685134b55ab 100644
--- a/lld/MachO/LTO.cpp
+++ b/lld/MachO/LTO.cpp
@@ -40,8 +40,10 @@ static lto::Config createConfig() {
   lto::Config c;
 
   bool emitAddrsig = config->icfLevel == ICFLevel::safe;
-  c.ModifyTargetOptions = [emitAddrsig](TargetOptions &options) {
+  c.InitTargetOptions = [emitAddrsig](const Triple &TT) {
+    TargetOptions options = codegen::InitTargetOptionsFromCodeGenFlags(TT);
     options.EmitAddrsig = emitAddrsig;
+    return options;
   };
 
   for (StringRef C : config->mllvmOpts)
diff --git a/lld/wasm/LTO.cpp b/lld/wasm/LTO.cpp
index 8706e80a71df3..757859e31f0ef 100644
--- a/lld/wasm/LTO.cpp
+++ b/lld/wasm/LTO.cpp
@@ -42,10 +42,12 @@ static std::string getThinLTOOutputFile(StringRef modulePath) {
 
 static lto::Config createConfig() {
   lto::Config c;
-  c.ModifyTargetOptions = [&](TargetOptions &options) {
+  c.InitTargetOptions = [&](const Triple &TT) {
+    TargetOptions options = codegen::InitTargetOptionsFromCodeGenFlags(TT);
     // Always emit a section per function/data with LTO.
     options.FunctionSections = true;
     options.DataSections = true;
+    return options;
   };
 
   c.DisableVerify = ctx.arg.disableVerify;
diff --git a/llvm/include/llvm/LTO/Config.h b/llvm/include/llvm/LTO/Config.h
index bd4a92b9ba67e..bdfb09894368e 100644
--- a/llvm/include/llvm/LTO/Config.h
+++ b/llvm/include/llvm/LTO/Config.h
@@ -15,6 +15,7 @@
 #define LLVM_LTO_CONFIG_H
 
 #include "llvm/ADT/DenseSet.h"
+#include "llvm/CodeGen/CommandFlags.h"
 #include "llvm/Config/llvm-config.h"
 #include "llvm/IR/DiagnosticInfo.h"
 #include "llvm/IR/GlobalValue.h"
@@ -49,8 +50,10 @@ struct Config {
   // computeLTOCacheKey in LTO.cpp.
   std::string CPU;
   // Callback to modify the target options once they are instantiated.
-  std::function<void(TargetOptions &Options)> ModifyTargetOptions =
-      [](TargetOptions &) {};
+  std::function<TargetOptions(const Triple &TheTriple)> InitTargetOptions =
+      [](const Triple &TheTriple) {
+        return codegen::InitTargetOptionsFromCodeGenFlags(TheTriple);
+      };
   std::vector<std::string> MAttrs;
   std::vector<std::string> MllvmArgs;
   // LTO will register both lists of plugins, but
diff --git a/llvm/lib/LTO/LTO.cpp b/llvm/lib/LTO/LTO.cpp
index 5f1a7ffac5e78..07e815af72117 100644
--- a/llvm/lib/LTO/LTO.cpp
+++ b/llvm/lib/LTO/LTO.cpp
@@ -174,8 +174,7 @@ std::string llvm::computeLTOCacheKey(
     Hasher.update(ArrayRef<uint8_t>(&I, 1));
   };
   AddString(Conf.CPU);
-  TargetOptions Opts = codegen::InitTargetOptionsFromCodeGenFlags(TT);
-  Conf.ModifyTargetOptions(Opts);
+  TargetOptions Opts = Conf.InitTargetOptions(TT);
 
   // FIXME: Hash more of TargetOptions. For now all clients initialize
   // TargetOptions from command-line flags (which is unsupported in production),
@@ -2576,8 +2575,7 @@ class OutOfProcessThinBackend : public CGThinBackend {
     auto &Ops = CodegenOptions;
 
     Ops.push_back(Saver.save("-O" + Twine(C.OptLevel)));
-    TargetOptions TO = codegen::InitTargetOptionsFromCodeGenFlags(Triple);
-    C.ModifyTargetOptions(TO);
+    TargetOptions TO = C.InitTargetOptions(Triple);
 
     if (TO.EmitAddrsig)
       Ops.push_back("-faddrsig");
diff --git a/llvm/lib/LTO/LTOBackend.cpp b/llvm/lib/LTO/LTOBackend.cpp
index 536fa4c2a268d..cfd845a4fc86a 100644
--- a/llvm/lib/LTO/LTOBackend.cpp
+++ b/llvm/lib/LTO/LTOBackend.cpp
@@ -243,9 +243,7 @@ createTargetMachine(const Config &Conf, const Target *TheTarget, Module &M) {
   else
     CodeModel = M.getCodeModel();
 
-  TargetOptions TargetOpts =
-      codegen::InitTargetOptionsFromCodeGenFlags(TheTriple);
-  Conf.ModifyTargetOptions(TargetOpts);
+  TargetOptions TargetOpts = Conf.InitTargetOptions(TheTriple);
 
   if (TargetOpts.MCOptions.ABIName.empty()) {
     TargetOpts.MCOptions.ABIName = M.getTargetABIFromMD();
diff --git a/llvm/lib/LTO/LTOCodeGenerator.cpp b/llvm/lib/LTO/LTOCodeGenerator.cpp
index 7d0171a138494..28b359345337c 100644
--- a/llvm/lib/LTO/LTOCodeGenerator.cpp
+++ b/llvm/lib/LTO/LTOCodeGenerator.cpp
@@ -394,11 +394,13 @@ bool LTOCodeGenerator::determineTarget() {
   if (Config.CPU.empty())
     Config.CPU = lto::getThinLTODefaultCPU(MergedModule->getTargetTriple());
 
-  Config.ModifyTargetOptions = [](TargetOptions &Options) {
+  Config.InitTargetOptions = [](const Triple& TT) {
+    TargetOptions TO = codegen::InitTargetOptionsFromCodeGenFlags(TT);
     // If data-sections is not explicitly set or unset, set data-sections by
     // default to match the behaviour of lld and gold plugin.
     if (!codegen::getExplicitDataSections())
-      Options.DataSections = true;
+      TO.DataSections = true;
+    return TO;
   };
 
   TargetMach = createTargetMachine();

>From 135cbb0071a074b3ada82d7ff4f7f5ed0ee11438 Mon Sep 17 00:00:00 2001
From: Paul Kirth <paulkirth at google.com>
Date: Tue, 17 Feb 2026 12:41:18 -0800
Subject: [PATCH 19/27] Use const reference

---
 clang/lib/CodeGen/BackendUtil.cpp | 3 ++-
 1 file changed, 2 insertions(+), 1 deletion(-)

diff --git a/clang/lib/CodeGen/BackendUtil.cpp b/clang/lib/CodeGen/BackendUtil.cpp
index 4422b5f2f7a4d..e29982303d9a6 100644
--- a/clang/lib/CodeGen/BackendUtil.cpp
+++ b/clang/lib/CodeGen/BackendUtil.cpp
@@ -1353,7 +1353,8 @@ runThinLTOBackend(CompilerInstance &CI, ModuleSummaryIndex *CombinedIndex,
   assert(OptLevelOrNone && "Invalid optimization level!");
   Conf.CGOptLevel = *OptLevelOrNone;
   Conf.OptLevel = CGOpts.OptimizationLevel;
-  Conf.InitTargetOptions = [&](llvm::Triple TheTriple) -> llvm::TargetOptions {
+  Conf.InitTargetOptions =
+      [&](const llvm::Triple &TheTriple) -> llvm::TargetOptions {
     llvm::TargetOptions TargetOpts =
         llvm::codegen::InitTargetOptionsFromCodeGenFlags(TheTriple);
     initTargetOptions(CI, Diags, TargetOpts);

>From c0471a52facffa7db1dc4aee66b6490c7dec6975 Mon Sep 17 00:00:00 2001
From: Paul Kirth <paulkirth at google.com>
Date: Tue, 17 Feb 2026 12:48:28 -0800
Subject: [PATCH 20/27] Formatting

---
 lld/COFF/LTO.cpp                  | 2 +-
 lld/ELF/LTO.cpp                   | 1 -
 llvm/lib/LTO/LTO.cpp              | 3 +--
 llvm/lib/LTO/LTOBackend.cpp       | 1 -
 llvm/lib/LTO/LTOCodeGenerator.cpp | 2 +-
 5 files changed, 3 insertions(+), 6 deletions(-)

diff --git a/lld/COFF/LTO.cpp b/lld/COFF/LTO.cpp
index 44e3b0caeed7a..2bb25e79fdce4 100644
--- a/lld/COFF/LTO.cpp
+++ b/lld/COFF/LTO.cpp
@@ -20,11 +20,11 @@
 #include "llvm/ADT/StringRef.h"
 #include "llvm/ADT/Twine.h"
 #include "llvm/Bitcode/BitcodeWriter.h"
+#include "llvm/CodeGen/CommandFlags.h"
 #include "llvm/DTLTO/DTLTO.h"
 #include "llvm/IR/DiagnosticPrinter.h"
 #include "llvm/LTO/Config.h"
 #include "llvm/LTO/LTO.h"
-#include "llvm/CodeGen/CommandFlags.h"
 #include "llvm/Support/Caching.h"
 #include "llvm/Support/CodeGen.h"
 #include "llvm/Support/MemoryBuffer.h"
diff --git a/lld/ELF/LTO.cpp b/lld/ELF/LTO.cpp
index 4288c04710d42..253d897a2913c 100644
--- a/lld/ELF/LTO.cpp
+++ b/lld/ELF/LTO.cpp
@@ -77,7 +77,6 @@ static std::shared_ptr<MemoryBuffer> getBBSectionsMemoryBuffer(Ctx &ctx) {
 
 static lto::Config createConfig(Ctx &ctx) {
   lto::Config c;
-
   std::shared_ptr<MemoryBuffer> mbPtr = getBBSectionsMemoryBuffer(ctx);
 
   // Set up the callback to modify TargetOptions.
diff --git a/llvm/lib/LTO/LTO.cpp b/llvm/lib/LTO/LTO.cpp
index 07e815af72117..73e0022539613 100644
--- a/llvm/lib/LTO/LTO.cpp
+++ b/llvm/lib/LTO/LTO.cpp
@@ -2573,10 +2573,9 @@ class OutOfProcessThinBackend : public CGThinBackend {
   void buildCommonRemoteCompilerOptions() {
     const lto::Config &C = Conf;
     auto &Ops = CodegenOptions;
-
     Ops.push_back(Saver.save("-O" + Twine(C.OptLevel)));
-    TargetOptions TO = C.InitTargetOptions(Triple);
 
+    TargetOptions TO = C.InitTargetOptions(Triple);
     if (TO.EmitAddrsig)
       Ops.push_back("-faddrsig");
     if (TO.FunctionSections)
diff --git a/llvm/lib/LTO/LTOBackend.cpp b/llvm/lib/LTO/LTOBackend.cpp
index cfd845a4fc86a..af348aff787d1 100644
--- a/llvm/lib/LTO/LTOBackend.cpp
+++ b/llvm/lib/LTO/LTOBackend.cpp
@@ -244,7 +244,6 @@ createTargetMachine(const Config &Conf, const Target *TheTarget, Module &M) {
     CodeModel = M.getCodeModel();
 
   TargetOptions TargetOpts = Conf.InitTargetOptions(TheTriple);
-
   if (TargetOpts.MCOptions.ABIName.empty()) {
     TargetOpts.MCOptions.ABIName = M.getTargetABIFromMD();
   }
diff --git a/llvm/lib/LTO/LTOCodeGenerator.cpp b/llvm/lib/LTO/LTOCodeGenerator.cpp
index 28b359345337c..d6806dd1a03e0 100644
--- a/llvm/lib/LTO/LTOCodeGenerator.cpp
+++ b/llvm/lib/LTO/LTOCodeGenerator.cpp
@@ -394,7 +394,7 @@ bool LTOCodeGenerator::determineTarget() {
   if (Config.CPU.empty())
     Config.CPU = lto::getThinLTODefaultCPU(MergedModule->getTargetTriple());
 
-  Config.InitTargetOptions = [](const Triple& TT) {
+  Config.InitTargetOptions = [](const Triple &TT) {
     TargetOptions TO = codegen::InitTargetOptionsFromCodeGenFlags(TT);
     // If data-sections is not explicitly set or unset, set data-sections by
     // default to match the behaviour of lld and gold plugin.

>From c2a7d005a88e58d94edfd8684b160b27758e9c4f Mon Sep 17 00:00:00 2001
From: Paul Kirth <paulkirth at google.com>
Date: Tue, 17 Feb 2026 16:35:00 -0800
Subject: [PATCH 21/27] Fix gold plugin support for non-default Triple

---
 llvm/lib/LTO/LTO.cpp            | 10 +++++++++-
 llvm/tools/gold/gold-plugin.cpp | 29 +++++++++++++++++------------
 2 files changed, 26 insertions(+), 13 deletions(-)

diff --git a/llvm/lib/LTO/LTO.cpp b/llvm/lib/LTO/LTO.cpp
index 73e0022539613..c62e0f2c26957 100644
--- a/llvm/lib/LTO/LTO.cpp
+++ b/llvm/lib/LTO/LTO.cpp
@@ -2220,8 +2220,16 @@ Error LTO::runThinLTO(AddStreamFn AddStream, FileCache Cache,
   auto &ModuleMap =
       ThinLTO.ModulesToCompile ? *ThinLTO.ModulesToCompile : ThinLTO.ModuleMap;
 
+  auto GetTripleFromConf = [&]() -> Triple {
+    if (!Conf.OverrideTriple.empty())
+      return Triple(Conf.OverrideTriple);
+    if (!RegularLTO.CombinedModule->getTargetTriple().empty())
+      return RegularLTO.CombinedModule->getTargetTriple();
+    return Triple(Conf.DefaultTriple);
+  };
+
   auto RunBackends = [&](ThinBackendProc *BackendProcess) -> Error {
-    const Triple &TheTriple = RegularLTO.CombinedModule->getTargetTriple();
+    Triple TheTriple = GetTripleFromConf();
     assert(!TheTriple.getTriple().empty() && "Empty TargetTriple for ThinLTO!");
     auto ProcessOneModule = [&](int I) -> Error {
       auto &Mod = *(ModuleMap.begin() + I);
diff --git a/llvm/tools/gold/gold-plugin.cpp b/llvm/tools/gold/gold-plugin.cpp
index ba2a1699aaea9..342bcc1e2d2eb 100644
--- a/llvm/tools/gold/gold-plugin.cpp
+++ b/llvm/tools/gold/gold-plugin.cpp
@@ -878,17 +878,23 @@ static std::unique_ptr<LTO> createLTO(IndexWriteCallback OnIndexWrite,
   ThinBackend Backend;
 
   Conf.CPU = options::mcpu;
-  Conf.Options = codegen::InitTargetOptionsFromCodeGenFlags(Triple());
-
-  // Disable the new X86 relax relocations since gold might not support them.
-  // FIXME: Check the gold version or add a new option to enable them.
-  Conf.Options.MCOptions.X86RelaxRelocations = false;
-
-  // Toggle function/data sections.
-  if (!codegen::getExplicitFunctionSections())
-    Conf.Options.FunctionSections = SplitSections;
-  if (!codegen::getExplicitDataSections())
-    Conf.Options.DataSections = SplitSections;
+  Conf.InitTargetOptions = [](const Triple &TheTriple) {
+    TargetOptions Options =
+        codegen::InitTargetOptionsFromCodeGenFlags(TheTriple);
+
+    // Disable the new X86 relax relocations since gold might not support them.
+    // FIXME: Check the gold version or add a new option to enable them.
+    Options.MCOptions.X86RelaxRelocations = false;
+
+    // Toggle function/data sections.
+    if (!codegen::getExplicitFunctionSections())
+      Options.FunctionSections = SplitSections;
+    if (!codegen::getExplicitDataSections())
+      Options.DataSections = SplitSections;
+    if (options::TheOutputType == options::OT_ASM_ONLY)
+      Options.MCOptions.AsmVerbose = true;
+    return Options;
+  };
 
   Conf.MAttrs = codegen::getMAttrs();
   Conf.RelocModel = RelocationModel;
@@ -952,7 +958,6 @@ static std::unique_ptr<LTO> createLTO(IndexWriteCallback OnIndexWrite,
     break;
   case options::OT_ASM_ONLY:
     Conf.CGFileType = CodeGenFileType::AssemblyFile;
-    Conf.Options.MCOptions.AsmVerbose = true;
     break;
   }
 

>From 2c1565291b8b781a08a74b66468abcfbff16b7aa Mon Sep 17 00:00:00 2001
From: Paul Kirth <paulkirth at google.com>
Date: Thu, 19 Feb 2026 16:35:37 -0800
Subject: [PATCH 22/27] Add LTO tests that exercise TargetOptions

---
 llvm/test/LTO/X86/default-tlsdesc.ll | 26 ++++++++++++++++++++++++++
 1 file changed, 26 insertions(+)
 create mode 100644 llvm/test/LTO/X86/default-tlsdesc.ll

diff --git a/llvm/test/LTO/X86/default-tlsdesc.ll b/llvm/test/LTO/X86/default-tlsdesc.ll
new file mode 100644
index 0000000000000..c8a4e66fac03d
--- /dev/null
+++ b/llvm/test/LTO/X86/default-tlsdesc.ll
@@ -0,0 +1,26 @@
+;; Test that Fuchsia enables TLSDESC by default during LTO.
+; RUN: opt -mtriple=x86_64-unknown-fuchsia < %s > %t.bc
+; RUN: llvm-lto -exported-symbol=f -relocation-model=pic -o %t.o %t.bc
+; RUN: llvm-readelf -r %t.o | FileCheck %s --check-prefix=FUCHSIA
+
+; RUN: opt -mtriple=x86_64-unknown-fuchsia -module-summary -o %t.thin.bc %s
+; RUN: llvm-lto2 run -r %t.thin.bc,f,plx -r %t.thin.bc,x, -relocation-model=pic -o %t.thin.o %t.thin.bc
+; RUN: llvm-readelf -r %t.thin.o.1 | FileCheck %s --check-prefix=FUCHSIA
+
+;; Test that Linux does not enable TLSDESC by default during LTO.
+; RUN: llvm-lto2 run -r %t.thin.bc,f,plx -r %t.thin.bc,x, -override-triple=x86_64-unknown-linux -relocation-model=pic -o %t.linux.o %t.thin.bc
+; RUN: llvm-readelf -r %t.linux.o.1 | FileCheck %s --check-prefix=LINUX
+
+declare ptr @llvm.threadlocal.address.p0(ptr)
+
+ at x = external thread_local global i32, align 4
+
+define ptr @f() {
+entry:
+  %1 = tail call ptr @llvm.threadlocal.address.p0(ptr @x)
+  ; FUCHSIA: R_X86_64_GOTPC32_TLSDESC
+  ; FUCHSIA: R_X86_64_TLSDESC_CALL
+  ; LINUX: R_X86_64_TLSGD
+  ; LINUX: R_X86_64_PLT32 {{.*}}__tls_get_addr
+  ret ptr %1
+}

>From 546bf3064021f6c270cab9f4c95f62d4ba2fcaee Mon Sep 17 00:00:00 2001
From: Paul Kirth <paulkirth at google.com>
Date: Thu, 19 Feb 2026 16:51:27 -0800
Subject: [PATCH 23/27] Add missing test case for llvm-lto

---
 llvm/test/LTO/X86/default-tlsdesc.ll | 4 ++++
 1 file changed, 4 insertions(+)

diff --git a/llvm/test/LTO/X86/default-tlsdesc.ll b/llvm/test/LTO/X86/default-tlsdesc.ll
index c8a4e66fac03d..c17d1256c8084 100644
--- a/llvm/test/LTO/X86/default-tlsdesc.ll
+++ b/llvm/test/LTO/X86/default-tlsdesc.ll
@@ -3,6 +3,10 @@
 ; RUN: llvm-lto -exported-symbol=f -relocation-model=pic -o %t.o %t.bc
 ; RUN: llvm-readelf -r %t.o | FileCheck %s --check-prefix=FUCHSIA
 
+; RUN: opt -mtriple=x86_64-unknown-linux < %s > %t.linux.bc
+; RUN: llvm-lto -exported-symbol=f -relocation-model=pic -o %t.linux.o %t.linux.bc
+; RUN: llvm-readelf -r %t.linux.o | FileCheck %s --check-prefix=LINUX
+
 ; RUN: opt -mtriple=x86_64-unknown-fuchsia -module-summary -o %t.thin.bc %s
 ; RUN: llvm-lto2 run -r %t.thin.bc,f,plx -r %t.thin.bc,x, -relocation-model=pic -o %t.thin.o %t.thin.bc
 ; RUN: llvm-readelf -r %t.thin.o.1 | FileCheck %s --check-prefix=FUCHSIA

>From 2bc596af9eac14ddf8b3bbbfd5f0f058568b9553 Mon Sep 17 00:00:00 2001
From: Paul Kirth <paulkirth at google.com>
Date: Thu, 19 Feb 2026 16:53:54 -0800
Subject: [PATCH 24/27] Reorganize test code to be separated by prefix

---
 llvm/test/LTO/X86/default-tlsdesc.ll | 8 ++++----
 1 file changed, 4 insertions(+), 4 deletions(-)

diff --git a/llvm/test/LTO/X86/default-tlsdesc.ll b/llvm/test/LTO/X86/default-tlsdesc.ll
index c17d1256c8084..9a08c70205b64 100644
--- a/llvm/test/LTO/X86/default-tlsdesc.ll
+++ b/llvm/test/LTO/X86/default-tlsdesc.ll
@@ -3,15 +3,15 @@
 ; RUN: llvm-lto -exported-symbol=f -relocation-model=pic -o %t.o %t.bc
 ; RUN: llvm-readelf -r %t.o | FileCheck %s --check-prefix=FUCHSIA
 
-; RUN: opt -mtriple=x86_64-unknown-linux < %s > %t.linux.bc
-; RUN: llvm-lto -exported-symbol=f -relocation-model=pic -o %t.linux.o %t.linux.bc
-; RUN: llvm-readelf -r %t.linux.o | FileCheck %s --check-prefix=LINUX
-
 ; RUN: opt -mtriple=x86_64-unknown-fuchsia -module-summary -o %t.thin.bc %s
 ; RUN: llvm-lto2 run -r %t.thin.bc,f,plx -r %t.thin.bc,x, -relocation-model=pic -o %t.thin.o %t.thin.bc
 ; RUN: llvm-readelf -r %t.thin.o.1 | FileCheck %s --check-prefix=FUCHSIA
 
 ;; Test that Linux does not enable TLSDESC by default during LTO.
+; RUN: opt -mtriple=x86_64-unknown-linux < %s > %t.linux.bc
+; RUN: llvm-lto -exported-symbol=f -relocation-model=pic -o %t.linux.o %t.linux.bc
+; RUN: llvm-readelf -r %t.linux.o | FileCheck %s --check-prefix=LINUX
+
 ; RUN: llvm-lto2 run -r %t.thin.bc,f,plx -r %t.thin.bc,x, -override-triple=x86_64-unknown-linux -relocation-model=pic -o %t.linux.o %t.thin.bc
 ; RUN: llvm-readelf -r %t.linux.o.1 | FileCheck %s --check-prefix=LINUX
 

>From 8b1c37a0335be0cdb3053f68ebcef0b6380d7097 Mon Sep 17 00:00:00 2001
From: Paul Kirth <paulkirth at google.com>
Date: Mon, 6 Apr 2026 15:22:50 -0700
Subject: [PATCH 25/27] Address feedback

- add comment
- add triple to test file
---
 llvm/test/CodeGen/X86/tls-desc.ll                  | 1 +
 llvm/test/tools/gold/X86/thinlto_prefix_replace.ll | 1 +
 2 files changed, 2 insertions(+)

diff --git a/llvm/test/CodeGen/X86/tls-desc.ll b/llvm/test/CodeGen/X86/tls-desc.ll
index f7dc0ceeeeb32..c02213f59e7f2 100644
--- a/llvm/test/CodeGen/X86/tls-desc.ll
+++ b/llvm/test/CodeGen/X86/tls-desc.ll
@@ -2,6 +2,7 @@
 ; RUN: llc < %s -mtriple=i686 --relocation-model=pic -enable-tlsdesc | FileCheck %s --check-prefix=X86
 ; RUN: llc < %s -mtriple=x86_64-pc-linux-gnux32 --relocation-model=pic -enable-tlsdesc | FileCheck %s --check-prefix=X32
 ; RUN: llc < %s -mtriple=x86_64 --relocation-model=pic -enable-tlsdesc | FileCheck %s --check-prefix=X64
+;; Fuchsia only supports TLSDESC for its ABI, so check it is set correctly, without -enable-tlsdesc.
 ; RUN: llc < %s -mtriple=x86_64-unknown-fuchsia --relocation-model=pic | FileCheck %s --check-prefix=X64-FUCHSIA
 
 @x = thread_local global i32 0, align 4
diff --git a/llvm/test/tools/gold/X86/thinlto_prefix_replace.ll b/llvm/test/tools/gold/X86/thinlto_prefix_replace.ll
index 7e5dc1e41f9ee..46c0b494d248e 100644
--- a/llvm/test/tools/gold/X86/thinlto_prefix_replace.ll
+++ b/llvm/test/tools/gold/X86/thinlto_prefix_replace.ll
@@ -12,6 +12,7 @@
 ; RUN: ls %t/newpath/thinlto_prefix_replace.o.thinlto.bc
 
 target datalayout = "e-m:e-i64:64-f80:128-n8:16:32:64-S128"
+target triple = "x86_64-unknown-linux-gnu"
 
 define void @f() {
 entry:

>From ef7264bbb4eeace40017fd9e421762d1e6893a04 Mon Sep 17 00:00:00 2001
From: Paul Kirth <paulkirth at google.com>
Date: Mon, 6 Apr 2026 17:52:07 -0700
Subject: [PATCH 26/27] Return error over falling back

---
 llvm/lib/LTO/LTO.cpp | 8 ++++----
 1 file changed, 4 insertions(+), 4 deletions(-)

diff --git a/llvm/lib/LTO/LTO.cpp b/llvm/lib/LTO/LTO.cpp
index c62e0f2c26957..5a5e83a2cf18a 100644
--- a/llvm/lib/LTO/LTO.cpp
+++ b/llvm/lib/LTO/LTO.cpp
@@ -2223,14 +2223,14 @@ Error LTO::runThinLTO(AddStreamFn AddStream, FileCache Cache,
   auto GetTripleFromConf = [&]() -> Triple {
     if (!Conf.OverrideTriple.empty())
       return Triple(Conf.OverrideTriple);
-    if (!RegularLTO.CombinedModule->getTargetTriple().empty())
-      return RegularLTO.CombinedModule->getTargetTriple();
-    return Triple(Conf.DefaultTriple);
+    return Triple(RegularLTO.CombinedModule->getTargetTriple());
   };
 
   auto RunBackends = [&](ThinBackendProc *BackendProcess) -> Error {
     Triple TheTriple = GetTripleFromConf();
-    assert(!TheTriple.getTriple().empty() && "Empty TargetTriple for ThinLTO!");
+    if (TheTriple.getTriple().empty())
+      return make_error<StringError>("Empty TargetTriple for ThinLTO!",
+                                     inconvertibleErrorCode());
     auto ProcessOneModule = [&](int I) -> Error {
       auto &Mod = *(ModuleMap.begin() + I);
       // Tasks 0 through ParallelCodeGenParallelismLevel-1 are reserved for

>From be33a6acdb2b426e82ede6e107f7f698687b3e8b Mon Sep 17 00:00:00 2001
From: Paul Kirth <paulkirth at google.com>
Date: Wed, 8 Apr 2026 10:41:44 -0700
Subject: [PATCH 27/27] Fix shared build and disallow default triples

---
 llvm/include/llvm/LTO/Config.h        | 9 +++++----
 llvm/lib/LTO/ThinLTOCodeGenerator.cpp | 7 +++++++
 llvm/tools/llvm-lto2/llvm-lto2.cpp    | 4 ++++
 3 files changed, 16 insertions(+), 4 deletions(-)

diff --git a/llvm/include/llvm/LTO/Config.h b/llvm/include/llvm/LTO/Config.h
index bdfb09894368e..0263a888564b3 100644
--- a/llvm/include/llvm/LTO/Config.h
+++ b/llvm/include/llvm/LTO/Config.h
@@ -49,11 +49,12 @@ struct Config {
   // Note: when adding fields here, consider whether they need to be added to
   // computeLTOCacheKey in LTO.cpp.
   std::string CPU;
-  // Callback to modify the target options once they are instantiated.
+  // Callback to initialize TargetOptions. Callers must set this to
+  // incorporate target-specific options.
   std::function<TargetOptions(const Triple &TheTriple)> InitTargetOptions =
-      [](const Triple &TheTriple) {
-        return codegen::InitTargetOptionsFromCodeGenFlags(TheTriple);
-      };
+      [](const Triple &) -> TargetOptions {
+    llvm_unreachable("InitTargetOptions must be set by the LTO client");
+  };
   std::vector<std::string> MAttrs;
   std::vector<std::string> MllvmArgs;
   // LTO will register both lists of plugins, but
diff --git a/llvm/lib/LTO/ThinLTOCodeGenerator.cpp b/llvm/lib/LTO/ThinLTOCodeGenerator.cpp
index 7346d987b1420..f1a3d7966e109 100644
--- a/llvm/lib/LTO/ThinLTOCodeGenerator.cpp
+++ b/llvm/lib/LTO/ThinLTOCodeGenerator.cpp
@@ -12,6 +12,7 @@
 //===----------------------------------------------------------------------===//
 
 #include "llvm/LTO/legacy/ThinLTOCodeGenerator.h"
+#include "llvm/CodeGen/CommandFlags.h"
 #include "llvm/Support/CommandLine.h"
 
 #include "llvm/ADT/ScopeExit.h"
@@ -375,6 +376,9 @@ class ModuleCacheEntry {
       return;
 
     llvm::lto::Config Conf;
+    Conf.InitTargetOptions = [](const Triple &TT) {
+      return codegen::InitTargetOptionsFromCodeGenFlags(TT);
+    };
     Conf.OptLevel = OptLevel;
     Conf.CPU = TMBuilder.MCpu;
     Conf.MAttrs.push_back(TMBuilder.MAttr);
@@ -529,6 +533,9 @@ static void resolvePrevailingInIndex(
 
   // TODO Conf.VisibilityScheme can be lto::Config::ELF for ELF.
   lto::Config Conf;
+  Conf.InitTargetOptions = [](const Triple &TT) {
+    return codegen::InitTargetOptionsFromCodeGenFlags(TT);
+  };
   thinLTOResolvePrevailingInIndex(Conf, Index, isPrevailing, recordNewLinkage,
                                   GUIDPreservedSymbols);
 }
diff --git a/llvm/tools/llvm-lto2/llvm-lto2.cpp b/llvm/tools/llvm-lto2/llvm-lto2.cpp
index b1e4fa0e760ab..d85b2895c8916 100644
--- a/llvm/tools/llvm-lto2/llvm-lto2.cpp
+++ b/llvm/tools/llvm-lto2/llvm-lto2.cpp
@@ -352,6 +352,10 @@ static int run(int argc, char **argv) {
     Conf.RelocModel = *RM;
   Conf.CodeModel = codegen::getExplicitCodeModel();
 
+  Conf.InitTargetOptions = [](const Triple &TT) {
+    return codegen::InitTargetOptionsFromCodeGenFlags(TT);
+  };
+
   Conf.DebugPassManager = DebugPassManager;
 
   if (SaveTemps && !SelectSaveTemps.empty()) {



More information about the cfe-commits mailing list