https://github.com/erozenfeld updated 
https://github.com/llvm/llvm-project/pull/205390

>From 064278ac2af3cf050dc65baa58379b6e1eb8f6f6 Mon Sep 17 00:00:00 2001
From: Eugene Rozenfeld <[email protected]>
Date: Tue, 23 Jun 2026 10:27:10 -0700
Subject: [PATCH] [lld][COFF] Add optimization remarks options to lld-link

 Add support for emitting optimization remarks during LTO in the COFF
 linker, matching the existing ELF linker functionality. The following
 options are added:

   -opt-remarks-filename
   -opt-remarks-passes
   -opt-remarks-format
   -opt-remarks-with-hotness
   -opt-remarks-hotness-threshold

 These options are forwarded to the LTO backend via lto::Config and
 allow users to capture optimization decisions (e.g., inlining) as
 structured YAML output, optionally filtered by pass name or profile
 hotness threshold.

 The clang driver is also updated to call addLTOOptions when invoking
 lld-link, so that -fsave-optimization-record and related clang flags
 are forwarded correctly.
---
 clang/lib/Driver/ToolChain.cpp       |  8 +++
 clang/lib/Driver/ToolChains/MSVC.cpp |  3 +
 lld/COFF/Config.h                    |  6 ++
 lld/COFF/Driver.cpp                  | 14 +++++
 lld/COFF/LTO.cpp                     |  5 ++
 lld/COFF/Options.td                  | 28 +++++++++
 lld/test/COFF/lto-opt-remarks.ll     | 87 ++++++++++++++++++++++++++++
 7 files changed, 151 insertions(+)
 create mode 100644 lld/test/COFF/lto-opt-remarks.ll

diff --git a/clang/lib/Driver/ToolChain.cpp b/clang/lib/Driver/ToolChain.cpp
index 7d93e7f65daf5..a2e3ce21cdc73 100644
--- a/clang/lib/Driver/ToolChain.cpp
+++ b/clang/lib/Driver/ToolChain.cpp
@@ -1306,6 +1306,14 @@ std::string ToolChain::GetLinkerPath(bool *LinkerIsLLD) 
const {
         *LinkerIsLLD = UseLinker == "lld";
       return LinkerPath;
     }
+
+    // If ld.<name> was not found, try the name directly (e.g. lld-link).
+    LinkerPath = GetProgramPath(UseLinker.str().c_str());
+    if (llvm::sys::fs::can_execute(LinkerPath)) {
+      if (LinkerIsLLD)
+        *LinkerIsLLD = UseLinker.starts_with("lld");
+      return LinkerPath;
+    }
   }
 
   if (A)
diff --git a/clang/lib/Driver/ToolChains/MSVC.cpp 
b/clang/lib/Driver/ToolChains/MSVC.cpp
index eb81f1b4e142c..ffe77e379261d 100644
--- a/clang/lib/Driver/ToolChains/MSVC.cpp
+++ b/clang/lib/Driver/ToolChains/MSVC.cpp
@@ -490,6 +490,9 @@ void visualstudio::Linker::ConstructJob(Compilation &C, 
const JobAction &JA,
     linkPath = TC.GetProgramPath(Linker.str().c_str());
   }
 
+  if (auto LTO = TC.getLTOMode(Args); LTO != LTOK_None)
+    addLTOOptions(TC, Args, CmdArgs, Output, Inputs, LTO == LTOK_Thin);
+
   auto LinkCmd = std::make_unique<Command>(
       JA, *this, ResponseFileSupport::AtFileUTF16(),
       Args.MakeArgString(linkPath), CmdArgs, Inputs, Output);
diff --git a/lld/COFF/Config.h b/lld/COFF/Config.h
index f22d5800b62c4..7546da219c440 100644
--- a/lld/COFF/Config.h
+++ b/lld/COFF/Config.h
@@ -21,6 +21,7 @@
 #include "llvm/Support/VirtualFileSystem.h"
 #include <cstdint>
 #include <map>
+#include <optional>
 #include <string>
 
 namespace lld::coff {
@@ -357,6 +358,11 @@ struct Configuration {
   EmitKind emit = EmitKind::Obj;
   bool allowDuplicateWeak = false;
   BuildIDHash buildIDHash = BuildIDHash::None;
+  llvm::StringRef optRemarksFilename;
+  llvm::StringRef optRemarksPasses;
+  llvm::StringRef optRemarksFormat;
+  bool optRemarksWithHotness = false;
+  std::optional<uint64_t> optRemarksHotnessThreshold = 0;
 };
 
 struct COFFSyncStream : SyncStream {
diff --git a/lld/COFF/Driver.cpp b/lld/COFF/Driver.cpp
index 024cb2c95cd20..da5ee7f5c04af 100644
--- a/lld/COFF/Driver.cpp
+++ b/lld/COFF/Driver.cpp
@@ -33,6 +33,7 @@
 #include "llvm/Option/Arg.h"
 #include "llvm/Option/ArgList.h"
 #include "llvm/Option/Option.h"
+#include "llvm/Remarks/HotnessThresholdParser.h"
 #include "llvm/Support/BinaryStreamReader.h"
 #include "llvm/Support/CommandLine.h"
 #include "llvm/Support/Debug.h"
@@ -2295,6 +2296,19 @@ void LinkerDriver::linkerMain(ArrayRef<const char *> 
argsArr) {
   if (args.hasFlag(OPT_prefetch_inputs, OPT_prefetch_inputs_no, false))
     config->prefetchInputs = true;
 
+  config->optRemarksFilename = args.getLastArgValue(OPT_opt_remarks_filename);
+  config->optRemarksPasses = args.getLastArgValue(OPT_opt_remarks_passes);
+  config->optRemarksFormat = args.getLastArgValue(OPT_opt_remarks_format);
+  config->optRemarksWithHotness = args.hasArg(OPT_opt_remarks_with_hotness);
+  if (auto *arg = args.getLastArg(OPT_opt_remarks_hotness_threshold)) {
+    auto resultOrErr = remarks::parseHotnessThresholdOption(arg->getValue());
+    if (!resultOrErr)
+      Err(ctx) << arg->getSpelling() << ": invalid argument '"
+               << arg->getValue() << "', only integer or 'auto' is supported";
+    else
+      config->optRemarksHotnessThreshold = *resultOrErr;
+  }
+
   if (errCount(ctx))
     return;
 
diff --git a/lld/COFF/LTO.cpp b/lld/COFF/LTO.cpp
index 0329f6c2e9cea..8dcf4dd1e7392 100644
--- a/lld/COFF/LTO.cpp
+++ b/lld/COFF/LTO.cpp
@@ -95,6 +95,11 @@ lto::Config BitcodeCompiler::createConfig() {
   c.SampleProfile = ctx.config.ltoSampleProfileName;
   c.TimeTraceEnabled = ctx.config.timeTraceEnabled;
   c.TimeTraceGranularity = ctx.config.timeTraceGranularity;
+  c.RemarksFilename = std::string(ctx.config.optRemarksFilename);
+  c.RemarksPasses = std::string(ctx.config.optRemarksPasses);
+  c.RemarksWithHotness = ctx.config.optRemarksWithHotness;
+  c.RemarksHotnessThreshold = ctx.config.optRemarksHotnessThreshold;
+  c.RemarksFormat = std::string(ctx.config.optRemarksFormat);
 
   if (ctx.config.emit == EmitKind::LLVM) {
     c.PreCodeGenModuleHook = [this](size_t task, const Module &m) {
diff --git a/lld/COFF/Options.td b/lld/COFF/Options.td
index bc1902d093a0f..5822cecb11605 100644
--- a/lld/COFF/Options.td
+++ b/lld/COFF/Options.td
@@ -360,6 +360,34 @@ defm prefetch_inputs : B<"prefetch-inputs",
                          "possible, to improve link times",
                          "Do not prefetch input files (default)">;
 
+def opt_remarks_filename: Separate<["-"], "opt-remarks-filename">,
+  HelpText<"YAML output file for optimization remarks">;
+def opt_remarks_passes: Separate<["-"], "opt-remarks-passes">,
+  HelpText<"Regex for the passes that need to be serialized to the output 
file">;
+def opt_remarks_format: Separate<["-"], "opt-remarks-format">,
+  HelpText<"The format used for serializing remarks (default: YAML)">;
+def opt_remarks_with_hotness: F<"opt-remarks-with-hotness">,
+  HelpText<"Include hotness information in the optimization remarks file">;
+def opt_remarks_hotness_threshold: P<"opt-remarks-hotness-threshold",
+  "Minimum profile count required for an optimization remark to be output. "
+  "Use 'auto' to apply the threshold from profile summary.">;
+
+def: F<"plugin-opt=opt-remarks-filename=">,
+  Alias<opt_remarks_filename>,
+  HelpText<"Alias for -opt-remarks-filename">;
+def: F<"plugin-opt=opt-remarks-passes=">,
+  Alias<opt_remarks_passes>,
+  HelpText<"Alias for -opt-remarks-passes">;
+def: F<"plugin-opt=opt-remarks-format=">,
+  Alias<opt_remarks_format>,
+  HelpText<"Alias for -opt-remarks-format">;
+def: F<"plugin-opt=opt-remarks-with-hotness">,
+  Alias<opt_remarks_with_hotness>,
+  HelpText<"Alias for -opt-remarks-with-hotness">;
+def: F<"plugin-opt=opt-remarks-hotness-threshold=">,
+  Alias<opt_remarks_hotness_threshold>,
+  HelpText<"Alias for -opt-remarks-hotness-threshold">;
+
 // Flags for debugging
 def lldmap : F<"lldmap">;
 def lldmap_file : P_priv<"lldmap">;
diff --git a/lld/test/COFF/lto-opt-remarks.ll b/lld/test/COFF/lto-opt-remarks.ll
new file mode 100644
index 0000000000000..7446093ec4d66
--- /dev/null
+++ b/lld/test/COFF/lto-opt-remarks.ll
@@ -0,0 +1,87 @@
+; REQUIRES: x86
+; RUN: llvm-as %s -o %t.obj
+
+; RUN: rm -f %t.yaml %t.pass.yaml %t.hot.yaml %t.t300.yaml %t.t301.yaml
+; RUN: lld-link -opt-remarks-filename %t.yaml %t.obj -entry:main -nodefaultlib 
\
+; RUN:   -out:%t.exe -force:unresolved
+; RUN: cat %t.yaml | FileCheck %s -check-prefix=YAML
+
+; RUN: lld-link -opt-remarks-filename %t.pass.yaml -opt-remarks-passes inline \
+; RUN:   %t.obj -entry:main -nodefaultlib -out:%t.exe -force:unresolved
+; RUN: cat %t.pass.yaml | FileCheck %s -check-prefix=YAML-PASSES
+
+; RUN: lld-link -opt-remarks-with-hotness -opt-remarks-filename %t.hot.yaml \
+; RUN:   %t.obj -entry:main -nodefaultlib -out:%t.exe -force:unresolved
+; RUN: cat %t.hot.yaml | FileCheck %s -check-prefix=YAML-HOT
+
+; RUN: lld-link -opt-remarks-with-hotness \
+; RUN:   -opt-remarks-hotness-threshold:300 \
+; RUN:   -opt-remarks-filename %t.t300.yaml %t.obj -entry:main -nodefaultlib \
+; RUN:   -out:%t.exe -force:unresolved
+; RUN: FileCheck %s -check-prefix=YAML-HOT < %t.t300.yaml
+
+; RUN: lld-link -opt-remarks-with-hotness \
+; RUN:   -opt-remarks-hotness-threshold:301 \
+; RUN:   -opt-remarks-filename %t.t301.yaml %t.obj -entry:main -nodefaultlib \
+; RUN:   -out:%t.exe -force:unresolved
+; RUN: count 0 < %t.t301.yaml
+
+; RUN: lld-link -opt-remarks-filename %t.yaml -opt-remarks-format yaml \
+; RUN:   %t.obj -entry:main -nodefaultlib -out:%t.exe -force:unresolved
+; RUN: FileCheck %s -check-prefix=YAML < %t.yaml
+
+; YAML:      --- !Passed
+; YAML-NEXT: Pass:            inline
+; YAML-NEXT: Name:            Inlined
+; YAML-NEXT: Function:        main
+; YAML-NEXT: Args:
+; YAML-NEXT:   - String:          ''''
+; YAML-NEXT:   - Callee:          tinkywinky
+; YAML-NEXT:   - String:          ''' inlined into '''
+; YAML-NEXT:   - Caller:          main
+; YAML-NEXT:   - String:          ''''
+; YAML-NEXT:   - String:          ' with '
+; YAML-NEXT:   - String:          '(cost='
+; YAML-NEXT:   - Cost:
+; YAML-NEXT:   - String:          ', threshold='
+; YAML-NEXT:   - Threshold:
+; YAML-NEXT:   - String:          ')'
+; YAML-NEXT: ...
+
+; YAML-HOT:      --- !Passed
+; YAML-HOT-NEXT: Pass:            inline
+; YAML-HOT-NEXT: Name:            Inlined
+; YAML-HOT-NEXT: Function:        main
+; YAML-HOT-NEXT: Hotness:         300
+; YAML-HOT-NEXT: Args:
+; YAML-HOT-NEXT:   - String:          ''''
+; YAML-HOT-NEXT:   - Callee:          tinkywinky
+; YAML-HOT-NEXT:   - String:          ''' inlined into '''
+; YAML-HOT-NEXT:   - Caller:          main
+; YAML-HOT-NEXT:   - String:          ''''
+; YAML-HOT-NEXT:   - String:          ' with '
+; YAML-HOT-NEXT:   - String:          '(cost='
+; YAML-HOT-NEXT:   - Cost:
+; YAML-HOT-NEXT:   - String:          ', threshold='
+; YAML-HOT-NEXT:   - Threshold:
+; YAML-HOT-NEXT:   - String:          ')'
+; YAML-HOT-NEXT: ...
+
+; YAML-PASSES: Pass:            inline
+
+target datalayout = 
"e-m:w-p270:32:32-p271:32:32-p272:64:64-i64:64-f80:128-n8:16:32:64-S128"
+target triple = "x86_64-pc-windows-msvc19.14.26433"
+
+declare i32 @patatino()
+
+define i32 @tinkywinky() {
+  %a = call i32 @patatino()
+  ret i32 %a
+}
+
+define i32 @main() !prof !0 {
+  %i = call i32 @tinkywinky()
+  ret i32 %i
+}
+
+!0 = !{!"function_entry_count", i64 300}

_______________________________________________
cfe-commits mailing list
[email protected]
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits

Reply via email to