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
