From 21eb097e524b991f36ccdbec55caeef3a3a8304c Mon Sep 17 00:00:00 2001 From: StefanStojanovic Date: Fri, 27 Feb 2026 14:18:04 +0100 Subject: [PATCH 1/4] build,win: enable x64 PGO --- common.gypi | 24 ++++++++++++++++++++++++ configure.py | 25 +++++++++++++++---------- node.gyp | 32 +++++++++++++++++++++----------- vcbuild.bat | 18 +++++++++++++++++- 4 files changed, 77 insertions(+), 22 deletions(-) diff --git a/common.gypi b/common.gypi index 183d8707682e8e..dbc00c06aa95ba 100644 --- a/common.gypi +++ b/common.gypi @@ -242,6 +242,30 @@ },], ], },], + ['OS=="win"', { + 'conditions': [ + ['enable_pgo_generate=="true"', { + 'msvs_settings': { + 'VCCLCompilerTool': { + 'AdditionalOptions': ['-fprofile-generate'], + }, + 'VCLinkerTool': { + 'AdditionalOptions': ['-fprofile-generate'], + }, + }, + },], + ['enable_pgo_use=="true"', { + 'msvs_settings': { + 'VCCLCompilerTool': { + 'AdditionalOptions': ['-fprofile-use=$(SolutionDir)node.profdata'], + }, + 'VCLinkerTool': { + 'AdditionalOptions': ['-fprofile-use=$(SolutionDir)node.profdata'], + }, + }, + },], + ], + },], ['OS == "android"', { 'cflags': [ '-fPIC', '-I<(android_ndk_path)/sources/android/cpufeatures' ], 'ldflags': [ '-fPIC' ] diff --git a/configure.py b/configure.py index 995d800bf69461..ec271aa2847ce1 100755 --- a/configure.py +++ b/configure.py @@ -202,14 +202,14 @@ dest="enable_pgo_generate", default=None, help="Enable profiling with pgo of a binary. This feature is only available " - "on linux with gcc and g++ 5.4.1 or newer.") + "on linux with gcc and g++ 5.4.1 or newer and on windows.") parser.add_argument("--enable-pgo-use", action="store_true", dest="enable_pgo_use", default=None, help="Enable use of the profile generated with --enable-pgo-generate. This " - "feature is only available on linux with gcc and g++ 5.4.1 or newer.") + "feature is only available on linux with gcc and g++ 5.4.1 or newer and on windows.") parser.add_argument("--enable-lto", action="store_true", @@ -1909,9 +1909,9 @@ def configure_node(o): else: o['variables']['node_enable_v8_vtunejit'] = 'false' - if flavor != 'linux' and (options.enable_pgo_generate or options.enable_pgo_use): + if (flavor != 'linux' and flavor != 'win') and (options.enable_pgo_generate or options.enable_pgo_use): raise Exception( - 'The pgo option is supported only on linux.') + 'The pgo option is supported only on linux and windows.') if flavor == 'linux': if options.enable_pgo_generate or options.enable_pgo_use: @@ -1922,12 +1922,17 @@ def configure_node(o): 'The options --enable-pgo-generate and --enable-pgo-use ' f'are supported for gcc and gxx {version_checked_str} or newer only.') - if options.enable_pgo_generate and options.enable_pgo_use: - raise Exception( - 'Only one of the --enable-pgo-generate or --enable-pgo-use options ' - 'can be specified at a time. You would like to use ' - '--enable-pgo-generate first, profile node, and then recompile ' - 'with --enable-pgo-use') + if options.enable_pgo_generate and options.enable_pgo_use: + raise Exception( + 'Only one of the --enable-pgo-generate or --enable-pgo-use options ' + 'can be specified at a time. You would like to use ' + '--enable-pgo-generate first, profile node, and then recompile ' + 'with --enable-pgo-use') + + if flavor == 'win' and options.with_ltcg and (options.enable_pgo_generate or options.enable_pgo_use): + raise Exception( + 'The --with-ltcg option cannot be used with --enable-pgo-generate or --enable-pgo-use. ' + 'PGO uses /LTCG:PGInstrument or /LTCG:PGOptimize while LTCG uses /LTCG:INCREMENTAL.') o['variables']['enable_pgo_generate'] = b(options.enable_pgo_generate) o['variables']['enable_pgo_use'] = b(options.enable_pgo_use) diff --git a/node.gyp b/node.gyp index b62e4f6fe3404c..9f08b7b16455aa 100644 --- a/node.gyp +++ b/node.gyp @@ -730,25 +730,35 @@ 'Ws2_32.lib', ], }], - ['node_with_ltcg=="true"', { + # Whole-program optimization: either LTCG or PGO + ['node_with_ltcg=="true" or enable_pgo_generate=="true" or enable_pgo_use=="true"', { 'msvs_settings': { 'VCCLCompilerTool': { - 'WholeProgramOptimization': 'true' # /GL, whole program optimization, needed for LTCG - }, - 'VCLibrarianTool': { - 'AdditionalOptions': [ - '/LTCG:INCREMENTAL', # link time code generation - ], + 'WholeProgramOptimization': 'true' # /GL, whole program optimization, needed for both LTCG and PGO }, 'VCLinkerTool': { 'OptimizeReferences': 2, # /OPT:REF 'EnableCOMDATFolding': 2, # /OPT:ICF 'LinkIncremental': 1, # disable incremental linking - 'AdditionalOptions': [ - '/LTCG:INCREMENTAL', # incremental link-time code generation - ], } - } + }, + 'conditions': [ + # LTCG-specific settings (only when PGO not active) + ['node_with_ltcg=="true" and enable_pgo_generate!="true" and enable_pgo_use!="true"', { + 'msvs_settings': { + 'VCLibrarianTool': { + 'AdditionalOptions': [ + '/LTCG:INCREMENTAL', # link time code generation + ], + }, + 'VCLinkerTool': { + 'AdditionalOptions': [ + '/LTCG:INCREMENTAL', # incremental link-time code generation + ], + }, + }, + }], + ] }, { 'msvs_settings': { 'VCCLCompilerTool': { diff --git a/vcbuild.bat b/vcbuild.bat index e733d147f089a6..1b3c85df6c5faa 100644 --- a/vcbuild.bat +++ b/vcbuild.bat @@ -24,6 +24,8 @@ set config=Release set target=Build set target_arch=x64 set ltcg= +set pgo_generate= +set pgo_use= set target_env= set noprojgen= set projgen= @@ -104,6 +106,8 @@ if /i "%1"=="sign" set sign=1&goto arg-ok if /i "%1"=="nosnapshot" set nosnapshot=1&goto arg-ok if /i "%1"=="nonpm" set nonpm=1&goto arg-ok if /i "%1"=="ltcg" set ltcg=1&goto arg-ok +if /i "%1"=="pgo-generate" set pgo_generate=1&goto arg-ok +if /i "%1"=="pgo-use" set pgo_use=1&goto arg-ok if /i "%1"=="v8temporal" set v8temporal=1&goto arg-ok if /i "%1"=="v8windbg" set v8windbg=1&goto arg-ok if /i "%1"=="licensertf" set licensertf=1&goto arg-ok @@ -179,6 +183,14 @@ goto next-arg :args-done +:: PGO mutual exclusion +if defined pgo_generate if defined pgo_use ( + echo Error: Only one of 'pgo-generate' or 'pgo-use' can be specified. + echo pgo-generate : build instrumented binary, then profile it + echo pgo-use : rebuild using the collected profile data + exit /b 1 +) + if defined build_release ( set config=Release set package=1 @@ -212,6 +224,8 @@ if "%config%"=="Debug" set configure_flags=%configure_flags% --debug if defined nosnapshot set configure_flags=%configure_flags% --without-snapshot if defined nonpm set configure_flags=%configure_flags% --without-npm if defined ltcg set configure_flags=%configure_flags% --with-ltcg +if defined pgo_generate set configure_flags=%configure_flags% --enable-pgo-generate +if defined pgo_use set configure_flags=%configure_flags% --enable-pgo-use if defined release_urlbase set configure_flags=%configure_flags% --release-urlbase=%release_urlbase% if defined download_arg set configure_flags=%configure_flags% %download_arg% if defined enable_vtune_arg set configure_flags=%configure_flags% --enable-vtune-profiling @@ -875,7 +889,7 @@ set exit_code=1 goto exit :help -echo vcbuild.bat [debug/release] [msi] [doc] [test/test-all/test-addons/test-doc/test-js-native-api/test-node-api/test-internet/test-tick-processor/test-known-issues/test-node-inspect/test-check-deopts/test-npm/test-v8/test-v8-intl/test-v8-benchmarks/test-v8-all] [build-addons/build-js-native-api-tests/build-node-api-tests/build-ffi-tests] [ignore-flaky] [static/dll] [noprojgen] [projgen] [clang-cl] [ccache path-to-ccache] [small-icu/full-icu/without-intl] [nobuild] [nosnapshot] [nonpm] [ltcg] [licensetf] [sign] [x64/arm64] [vs2022/vs2026] [download-all] [enable-vtune] [lint/lint-ci/lint-js/lint-md] [lint-md-build] [format-md] [package] [build-release] [upload] [no-NODE-OPTIONS] [link-module path-to-module] [debug-http2] [debug-nghttp2] [clean] [cctest] [no-cctest] [openssl-no-asm] +echo vcbuild.bat [debug/release] [msi] [doc] [test/test-all/test-addons/test-doc/test-js-native-api/test-node-api/test-internet/test-tick-processor/test-known-issues/test-node-inspect/test-check-deopts/test-npm/test-v8/test-v8-intl/test-v8-benchmarks/test-v8-all] [build-addons/build-js-native-api-tests/build-node-api-tests/build-ffi-tests] [ignore-flaky] [static/dll] [noprojgen] [projgen] [clang-cl] [ccache path-to-ccache] [small-icu/full-icu/without-intl] [nobuild] [nosnapshot] [nonpm] [ltcg] [pgo-generate] [pgo-use] [licensetf] [sign] [x64/arm64] [vs2022/vs2026] [download-all] [enable-vtune] [lint/lint-ci/lint-js/lint-md] [lint-md-build] [format-md] [package] [build-release] [upload] [no-NODE-OPTIONS] [link-module path-to-module] [debug-http2] [debug-nghttp2] [clean] [cctest] [no-cctest] [openssl-no-asm] echo Examples: echo vcbuild.bat : builds release build echo vcbuild.bat debug : builds debug build @@ -887,6 +901,8 @@ echo vcbuild.bat link-module my_module.js : bundles my_module as built-in modu echo vcbuild.bat lint : runs the C++, documentation and JavaScript linter echo vcbuild.bat no-cctest : skip building cctest.exe echo vcbuild.bat ccache c:\ccache\ : use ccache to speed build +echo vcbuild.bat pgo-generate : builds instrumented binary for PGO (profile first, then rebuild with pgo-use) +echo vcbuild.bat pgo-use : builds optimized binary using PGO profile data goto exit :exit From c5f9f10eed5e11c2adaaea258900cb15397e30fd Mon Sep 17 00:00:00 2001 From: StefanStojanovic Date: Fri, 27 Feb 2026 16:13:38 +0100 Subject: [PATCH 2/4] build,win: change LTCG to thin LTO --- configure.py | 7 +------ node.gyp | 52 ++++++++++++++++++++++++++-------------------------- 2 files changed, 27 insertions(+), 32 deletions(-) diff --git a/configure.py b/configure.py index ec271aa2847ce1..d21887f61e9434 100755 --- a/configure.py +++ b/configure.py @@ -919,7 +919,7 @@ action='store_true', dest='with_ltcg', default=None, - help='Use Link Time Code Generation. This feature is only available on Windows.') + help='Use Thin LTO. This feature is only available on Windows.') parser.add_argument('--write-snapshot-as-array-literals', action='store_true', @@ -1929,11 +1929,6 @@ def configure_node(o): '--enable-pgo-generate first, profile node, and then recompile ' 'with --enable-pgo-use') - if flavor == 'win' and options.with_ltcg and (options.enable_pgo_generate or options.enable_pgo_use): - raise Exception( - 'The --with-ltcg option cannot be used with --enable-pgo-generate or --enable-pgo-use. ' - 'PGO uses /LTCG:PGInstrument or /LTCG:PGOptimize while LTCG uses /LTCG:INCREMENTAL.') - o['variables']['enable_pgo_generate'] = b(options.enable_pgo_generate) o['variables']['enable_pgo_use'] = b(options.enable_pgo_use) diff --git a/node.gyp b/node.gyp index 9f08b7b16455aa..e3c5b5efd7058c 100644 --- a/node.gyp +++ b/node.gyp @@ -730,45 +730,34 @@ 'Ws2_32.lib', ], }], - # Whole-program optimization: either LTCG or PGO - ['node_with_ltcg=="true" or enable_pgo_generate=="true" or enable_pgo_use=="true"', { + # Thin LTO for node_main.cc and linker (scoped to node_exe) + ['node_with_ltcg=="true"', { 'msvs_settings': { 'VCCLCompilerTool': { - 'WholeProgramOptimization': 'true' # /GL, whole program optimization, needed for both LTCG and PGO + 'AdditionalOptions': ['-flto=thin'], + }, + 'VCLinkerTool': { + 'AdditionalOptions': ['-flto=thin'], }, + }, + }], + # Whole-program optimization: either Thin LTO or PGO + ['node_with_ltcg=="true" or enable_pgo_generate=="true" or enable_pgo_use=="true"', { + 'msvs_settings': { 'VCLinkerTool': { 'OptimizeReferences': 2, # /OPT:REF 'EnableCOMDATFolding': 2, # /OPT:ICF 'LinkIncremental': 1, # disable incremental linking - } + }, }, - 'conditions': [ - # LTCG-specific settings (only when PGO not active) - ['node_with_ltcg=="true" and enable_pgo_generate!="true" and enable_pgo_use!="true"', { - 'msvs_settings': { - 'VCLibrarianTool': { - 'AdditionalOptions': [ - '/LTCG:INCREMENTAL', # link time code generation - ], - }, - 'VCLinkerTool': { - 'AdditionalOptions': [ - '/LTCG:INCREMENTAL', # incremental link-time code generation - ], - }, - }, - }], - ] }, { + # No whole-program optimization 'msvs_settings': { - 'VCCLCompilerTool': { - 'WholeProgramOptimization': 'false' - }, 'VCLinkerTool': { - 'LinkIncremental': 2 # enable incremental linking + 'LinkIncremental': 2, # enable incremental linking }, }, - }], + }], ['node_use_node_snapshot=="true"', { 'dependencies': [ 'node_mksnapshot', @@ -1171,6 +1160,17 @@ [ 'debug_nghttp2==1', { 'defines': [ 'NODE_DEBUG_NGHTTP2=1' ] }], + # Thin LTO for node sources (scoped to libnode, not global) + ['node_with_ltcg=="true"', { + 'msvs_settings': { + 'VCCLCompilerTool': { + 'AdditionalOptions': ['-flto=thin'], + }, + 'VCLibrarianTool': { + 'AdditionalOptions': ['-flto=thin'], + }, + }, + }], ], 'actions': [ { From b46b727fd8bbd6a1163038b994abd25f260a97ef Mon Sep 17 00:00:00 2001 From: StefanStojanovic Date: Thu, 19 Mar 2026 09:07:54 +0100 Subject: [PATCH 3/4] build,win: enable full LTO --- common.gypi | 26 ++++++++++++++ configure.py | 33 ++++++++++++++--- deps/openssl/openssl-cli.gypi | 10 ++++++ deps/openssl/openssl.gyp | 10 ++++++ node.gyp | 42 +++++++++++++++++++++- tools/icu/icu-generic.gyp | 40 +++++++++++++++++++++ tools/v8_gypfiles/d8.gyp | 10 ++++++ tools/v8_gypfiles/v8.gyp | 67 +++++++++++++++++++++++++++++++++++ vcbuild.bat | 23 +++++++++++- 9 files changed, 255 insertions(+), 6 deletions(-) diff --git a/common.gypi b/common.gypi index dbc00c06aa95ba..a174b74beee124 100644 --- a/common.gypi +++ b/common.gypi @@ -244,6 +244,32 @@ },], ['OS=="win"', { 'conditions': [ + ['enable_lto=="true"', { + 'msvs_settings': { + 'VCCLCompilerTool': { + 'AdditionalOptions': ['-flto=full'], + }, + 'VCLibrarianTool': { + 'AdditionalOptions': ['-flto=full'], + }, + 'VCLinkerTool': { + 'AdditionalOptions': ['-flto=full'], + }, + }, + },], + ['enable_thin_lto=="true"', { + 'msvs_settings': { + 'VCCLCompilerTool': { + 'AdditionalOptions': ['-flto=thin'], + }, + 'VCLibrarianTool': { + 'AdditionalOptions': ['-flto=thin'], + }, + 'VCLinkerTool': { + 'AdditionalOptions': ['-flto=thin'], + }, + }, + },], ['enable_pgo_generate=="true"', { 'msvs_settings': { 'VCCLCompilerTool': { diff --git a/configure.py b/configure.py index d21887f61e9434..c3259b5f764ea3 100755 --- a/configure.py +++ b/configure.py @@ -218,6 +218,13 @@ help="Enable compiling with lto of a binary. This feature is only available " "with gcc 5.4.1+ or clang 3.9.1+.") +parser.add_argument("--enable-thin-lto", + action="store_true", + dest="enable_thin_lto", + default=None, + help="Enable compiling with thin lto of a binary. This feature is only available " + "on windows.") + parser.add_argument("--link-module", action="append", dest="linked_module", @@ -919,7 +926,8 @@ action='store_true', dest='with_ltcg', default=None, - help='Use Thin LTO. This feature is only available on Windows.') + help='Use Thin LTO scoped to node.exe and libnode only. ' + 'This feature is only available on Windows.') parser.add_argument('--write-snapshot-as-array-literals', action='store_true', @@ -1932,11 +1940,27 @@ def configure_node(o): o['variables']['enable_pgo_generate'] = b(options.enable_pgo_generate) o['variables']['enable_pgo_use'] = b(options.enable_pgo_use) - if flavor == 'win' and (options.enable_lto): + if flavor != 'win' and options.enable_thin_lto: raise Exception( - 'Use Link Time Code Generation instead.') + 'Use --enable-lto instead.') + + # LTO mutual exclusion + if flavor == 'win': + lto_options = [] + if options.enable_lto: + lto_options.append('--enable-lto') + if options.enable_thin_lto: + lto_options.append('--enable-thin-lto') + if options.with_ltcg: + lto_options.append('--with-ltcg') + if len(lto_options) > 1: + raise Exception( + f'Only one LTO option can be specified at a time: {", ".join(lto_options)}. ' + 'Use --enable-lto for Full LTO (global), ' + '--enable-thin-lto for Thin LTO (global), ' + 'or --with-ltcg for Thin LTO (scoped to node.exe and libnode).') - if options.enable_lto: + if options.enable_lto and flavor != 'win': gcc_version_checked = (5, 4, 1) clang_version_checked = (3, 9, 1) if not gcc_version_ge(gcc_version_checked) and not clang_version_ge(clang_version_checked): @@ -1947,6 +1971,7 @@ def configure_node(o): f'or clang {clang_version_checked_str}+ only.') o['variables']['enable_lto'] = b(options.enable_lto) + o['variables']['enable_thin_lto'] = b(options.enable_thin_lto) if options.node_use_large_pages or options.node_use_large_pages_script_lld: warn('''The `--use-largepages` and `--use-largepages-script-lld` options diff --git a/deps/openssl/openssl-cli.gypi b/deps/openssl/openssl-cli.gypi index ae74be9a2b17d2..da03938f1d1a92 100644 --- a/deps/openssl/openssl-cli.gypi +++ b/deps/openssl/openssl-cli.gypi @@ -25,5 +25,15 @@ ['enable_lto=="true"', { 'ldflags': [ '-fno-lto' ], }], + ['node_with_ltcg=="true" or enable_lto=="true" or enable_thin_lto=="true"', { + 'msvs_settings': { + 'VCCLCompilerTool': { + 'AdditionalOptions': ['-fno-lto'], + }, + 'VCLinkerTool': { + 'AdditionalOptions': ['-fno-lto'], + }, + }, + }], ], } diff --git a/deps/openssl/openssl.gyp b/deps/openssl/openssl.gyp index ea3a2dc09ef29b..4d8d16f0e3c838 100644 --- a/deps/openssl/openssl.gyp +++ b/deps/openssl/openssl.gyp @@ -77,6 +77,16 @@ ['enable_lto=="true"', { 'ldflags': [ '-fno-lto' ], }], + ['node_with_ltcg=="true" or enable_lto=="true" or enable_thin_lto=="true"', { + 'msvs_settings': { + 'VCCLCompilerTool': { + 'AdditionalOptions': ['-fno-lto'], + }, + 'VCLinkerTool': { + 'AdditionalOptions': ['-fno-lto'], + }, + }, + }], ] }, { # openssl-fipsmodule target diff --git a/node.gyp b/node.gyp index e3c5b5efd7058c..a32a1a3b7b5813 100644 --- a/node.gyp +++ b/node.gyp @@ -742,7 +742,7 @@ }, }], # Whole-program optimization: either Thin LTO or PGO - ['node_with_ltcg=="true" or enable_pgo_generate=="true" or enable_pgo_use=="true"', { + ['node_with_ltcg=="true" or enable_lto=="true" or enable_thin_lto=="true" or enable_pgo_generate=="true" or enable_pgo_use=="true"', { 'msvs_settings': { 'VCLinkerTool': { 'OptimizeReferences': 2, # /OPT:REF @@ -1471,6 +1471,16 @@ ['enable_lto=="true"', { 'ldflags': [ '-fno-lto' ], }], + ['node_with_ltcg=="true" or enable_lto=="true" or enable_thin_lto=="true"', { + 'msvs_settings': { + 'VCCLCompilerTool': { + 'AdditionalOptions': ['-fno-lto'], + }, + 'VCLinkerTool': { + 'AdditionalOptions': ['-fno-lto'], + }, + }, + }], ], }, # cctest @@ -1535,6 +1545,16 @@ ['enable_lto=="true"', { 'ldflags': [ '-fno-lto' ], }], + ['node_with_ltcg=="true" or enable_lto=="true" or enable_thin_lto=="true"', { + 'msvs_settings': { + 'VCCLCompilerTool': { + 'AdditionalOptions': ['-fno-lto'], + }, + 'VCLinkerTool': { + 'AdditionalOptions': ['-fno-lto'], + }, + }, + }], ], }, # embedtest @@ -1612,6 +1632,16 @@ ['enable_lto=="true"', { 'ldflags': [ '-fno-lto' ], }], + ['node_with_ltcg=="true" or enable_lto=="true" or enable_thin_lto=="true"', { + 'msvs_settings': { + 'VCCLCompilerTool': { + 'AdditionalOptions': ['-fno-lto'], + }, + 'VCLinkerTool': { + 'AdditionalOptions': ['-fno-lto'], + }, + }, + }], ] }, # overlapped-checker { @@ -1738,6 +1768,16 @@ ['enable_lto=="true"', { 'ldflags': [ '-fno-lto' ], }], + ['node_with_ltcg=="true" or enable_lto=="true" or enable_thin_lto=="true"', { + 'msvs_settings': { + 'VCCLCompilerTool': { + 'AdditionalOptions': ['-fno-lto'], + }, + 'VCLinkerTool': { + 'AdditionalOptions': ['-fno-lto'], + }, + }, + }], ], }, # node_mksnapshot ], # end targets diff --git a/tools/icu/icu-generic.gyp b/tools/icu/icu-generic.gyp index f007c65232c0d6..f49b4ddba74a12 100644 --- a/tools/icu/icu-generic.gyp +++ b/tools/icu/icu-generic.gyp @@ -456,6 +456,16 @@ ['enable_lto=="true"', { 'ldflags': [ '-fno-lto' ], }], + ['node_with_ltcg=="true" or enable_lto=="true" or enable_thin_lto=="true"', { + 'msvs_settings': { + 'VCCLCompilerTool': { + 'AdditionalOptions': ['-fno-lto'], + }, + 'VCLinkerTool': { + 'AdditionalOptions': ['-fno-lto'], + }, + }, + }], ], }, # This tool is used to rebuild res_index.res manifests @@ -473,6 +483,16 @@ ['enable_lto=="true"', { 'ldflags': [ '-fno-lto' ], }], + ['node_with_ltcg=="true" or enable_lto=="true" or enable_thin_lto=="true"', { + 'msvs_settings': { + 'VCCLCompilerTool': { + 'AdditionalOptions': ['-fno-lto'], + }, + 'VCLinkerTool': { + 'AdditionalOptions': ['-fno-lto'], + }, + }, + }], ], }, # This tool is used to package, unpackage, repackage .dat files @@ -491,6 +511,16 @@ ['enable_lto=="true"', { 'ldflags': [ '-fno-lto' ], }], + ['node_with_ltcg=="true" or enable_lto=="true" or enable_thin_lto=="true"', { + 'msvs_settings': { + 'VCCLCompilerTool': { + 'AdditionalOptions': ['-fno-lto'], + }, + 'VCLinkerTool': { + 'AdditionalOptions': ['-fno-lto'], + }, + }, + }], ], }, # this is used to convert .dat directly into .obj @@ -508,6 +538,16 @@ ['enable_lto=="true"', { 'ldflags': [ '-fno-lto' ], }], + ['node_with_ltcg=="true" or enable_lto=="true" or enable_thin_lto=="true"', { + 'msvs_settings': { + 'VCCLCompilerTool': { + 'AdditionalOptions': ['-fno-lto'], + }, + 'VCLinkerTool': { + 'AdditionalOptions': ['-fno-lto'], + }, + }, + }], ], }, ], diff --git a/tools/v8_gypfiles/d8.gyp b/tools/v8_gypfiles/d8.gyp index f5f8a194318eb5..5b47ebf180dd88 100644 --- a/tools/v8_gypfiles/d8.gyp +++ b/tools/v8_gypfiles/d8.gyp @@ -67,6 +67,16 @@ ['enable_lto=="true"', { 'ldflags': [ '-fno-lto' ], }], + ['node_with_ltcg=="true" or enable_lto=="true" or enable_thin_lto=="true"', { + 'msvs_settings': { + 'VCCLCompilerTool': { + 'AdditionalOptions': ['-fno-lto'], + }, + 'VCLinkerTool': { + 'AdditionalOptions': ['-fno-lto'], + }, + }, + }], ], }, ], diff --git a/tools/v8_gypfiles/v8.gyp b/tools/v8_gypfiles/v8.gyp index e09fcc1ce8c59f..5dddc2c220f258 100644 --- a/tools/v8_gypfiles/v8.gyp +++ b/tools/v8_gypfiles/v8.gyp @@ -1791,6 +1791,16 @@ ['enable_lto=="true"', { 'ldflags': [ '-fno-lto' ], }], + ['node_with_ltcg=="true" or enable_lto=="true" or enable_thin_lto=="true"', { + 'msvs_settings': { + 'VCCLCompilerTool': { + 'AdditionalOptions': ['-fno-lto'], + }, + 'VCLinkerTool': { + 'AdditionalOptions': ['-fno-lto'], + }, + }, + }], ], 'defines!': [ 'BUILDING_V8_SHARED=1', @@ -1854,6 +1864,16 @@ ['enable_lto=="true"', { 'ldflags': [ '-fno-lto' ], }], + ['node_with_ltcg=="true" or enable_lto=="true" or enable_thin_lto=="true"', { + 'msvs_settings': { + 'VCCLCompilerTool': { + 'AdditionalOptions': ['-fno-lto'], + }, + 'VCLinkerTool': { + 'AdditionalOptions': ['-fno-lto'], + }, + }, + }], ], }, # mksnapshot { @@ -1872,6 +1892,16 @@ ['enable_lto=="true"', { 'ldflags': [ '-fno-lto' ], }], + ['node_with_ltcg=="true" or enable_lto=="true" or enable_thin_lto=="true"', { + 'msvs_settings': { + 'VCCLCompilerTool': { + 'AdditionalOptions': ['-fno-lto'], + }, + 'VCLinkerTool': { + 'AdditionalOptions': ['-fno-lto'], + }, + }, + }], ], 'defines!': [ '_HAS_EXCEPTIONS=0', @@ -1911,6 +1941,16 @@ ['enable_lto=="true"', { 'ldflags': [ '-fno-lto' ], }], + ['node_with_ltcg=="true" or enable_lto=="true" or enable_thin_lto=="true"', { + 'msvs_settings': { + 'VCCLCompilerTool': { + 'AdditionalOptions': ['-fno-lto'], + }, + 'VCLinkerTool': { + 'AdditionalOptions': ['-fno-lto'], + }, + }, + }], ], 'dependencies': [ 'torque_base', @@ -1949,6 +1989,16 @@ ['enable_lto=="true"', { 'ldflags': [ '-fno-lto' ], }], + ['node_with_ltcg=="true" or enable_lto=="true" or enable_thin_lto=="true"', { + 'msvs_settings': { + 'VCCLCompilerTool': { + 'AdditionalOptions': ['-fno-lto'], + }, + 'VCLinkerTool': { + 'AdditionalOptions': ['-fno-lto'], + }, + }, + }], ], 'sources': [ "<(V8_ROOT)/src/regexp/gen-regexp-special-case.cc", @@ -1969,6 +2019,16 @@ ['enable_lto=="true"', { 'ldflags': [ '-fno-lto' ], }], + ['node_with_ltcg=="true" or enable_lto=="true" or enable_thin_lto=="true"', { + 'msvs_settings': { + 'VCCLCompilerTool': { + 'AdditionalOptions': ['-fno-lto'], + }, + 'VCLinkerTool': { + 'AdditionalOptions': ['-fno-lto'], + }, + }, + }], ], 'actions': [ { @@ -2037,6 +2097,13 @@ ['enable_lto=="true"', { 'cflags_cc': [ '-fno-lto' ], }], + ['node_with_ltcg=="true" or enable_lto=="true" or enable_thin_lto=="true"', { + 'msvs_settings': { + 'VCCLCompilerTool': { + 'AdditionalOptions': ['-fno-lto'], + }, + }, + }], # Changes in push_registers_asm.cc in V8 v12.8 requires using # push_registers_masm on Windows even with ClangCL on x64 ['OS=="win"', { diff --git a/vcbuild.bat b/vcbuild.bat index 1b3c85df6c5faa..2a3340c473f8eb 100644 --- a/vcbuild.bat +++ b/vcbuild.bat @@ -24,6 +24,8 @@ set config=Release set target=Build set target_arch=x64 set ltcg= +set thin_lto= +set lto= set pgo_generate= set pgo_use= set target_env= @@ -106,6 +108,8 @@ if /i "%1"=="sign" set sign=1&goto arg-ok if /i "%1"=="nosnapshot" set nosnapshot=1&goto arg-ok if /i "%1"=="nonpm" set nonpm=1&goto arg-ok if /i "%1"=="ltcg" set ltcg=1&goto arg-ok +if /i "%1"=="thin-lto" set thin_lto=1&goto arg-ok +if /i "%1"=="lto" set lto=1&goto arg-ok if /i "%1"=="pgo-generate" set pgo_generate=1&goto arg-ok if /i "%1"=="pgo-use" set pgo_use=1&goto arg-ok if /i "%1"=="v8temporal" set v8temporal=1&goto arg-ok @@ -183,6 +187,19 @@ goto next-arg :args-done +:: LTO mutual exclusion +set lto_count=0 +if defined ltcg set /a lto_count+=1 +if defined thin_lto set /a lto_count+=1 +if defined lto set /a lto_count+=1 +if %lto_count% gtr 1 ( + echo Error: Only one of 'ltcg', 'thin-lto', or 'lto' can be specified. + echo ltcg : Thin LTO scoped to node.exe and libnode only + echo thin-lto : Thin LTO applied globally to all targets + echo lto : Full LTO applied globally to all targets + exit /b 1 +) + :: PGO mutual exclusion if defined pgo_generate if defined pgo_use ( echo Error: Only one of 'pgo-generate' or 'pgo-use' can be specified. @@ -224,6 +241,8 @@ if "%config%"=="Debug" set configure_flags=%configure_flags% --debug if defined nosnapshot set configure_flags=%configure_flags% --without-snapshot if defined nonpm set configure_flags=%configure_flags% --without-npm if defined ltcg set configure_flags=%configure_flags% --with-ltcg +if defined thin_lto set configure_flags=%configure_flags% --enable-thin-lto +if defined lto set configure_flags=%configure_flags% --enable-lto if defined pgo_generate set configure_flags=%configure_flags% --enable-pgo-generate if defined pgo_use set configure_flags=%configure_flags% --enable-pgo-use if defined release_urlbase set configure_flags=%configure_flags% --release-urlbase=%release_urlbase% @@ -889,7 +908,7 @@ set exit_code=1 goto exit :help -echo vcbuild.bat [debug/release] [msi] [doc] [test/test-all/test-addons/test-doc/test-js-native-api/test-node-api/test-internet/test-tick-processor/test-known-issues/test-node-inspect/test-check-deopts/test-npm/test-v8/test-v8-intl/test-v8-benchmarks/test-v8-all] [build-addons/build-js-native-api-tests/build-node-api-tests/build-ffi-tests] [ignore-flaky] [static/dll] [noprojgen] [projgen] [clang-cl] [ccache path-to-ccache] [small-icu/full-icu/without-intl] [nobuild] [nosnapshot] [nonpm] [ltcg] [pgo-generate] [pgo-use] [licensetf] [sign] [x64/arm64] [vs2022/vs2026] [download-all] [enable-vtune] [lint/lint-ci/lint-js/lint-md] [lint-md-build] [format-md] [package] [build-release] [upload] [no-NODE-OPTIONS] [link-module path-to-module] [debug-http2] [debug-nghttp2] [clean] [cctest] [no-cctest] [openssl-no-asm] +echo vcbuild.bat [debug/release] [msi] [doc] [test/test-all/test-addons/test-doc/test-js-native-api/test-node-api/test-internet/test-tick-processor/test-known-issues/test-node-inspect/test-check-deopts/test-npm/test-v8/test-v8-intl/test-v8-benchmarks/test-v8-all] [build-addons/build-js-native-api-tests/build-node-api-tests/build-ffi-tests] [ignore-flaky] [static/dll] [noprojgen] [projgen] [clang-cl] [ccache path-to-ccache] [small-icu/full-icu/without-intl] [nobuild] [nosnapshot] [nonpm] [ltcg] [thin-lto] [lto] [pgo-generate] [pgo-use] [licensetf] [sign] [x64/arm64] [vs2022/vs2026] [download-all] [enable-vtune] [lint/lint-ci/lint-js/lint-md] [lint-md-build] [format-md] [package] [build-release] [upload] [no-NODE-OPTIONS] [link-module path-to-module] [debug-http2] [debug-nghttp2] [clean] [cctest] [no-cctest] [openssl-no-asm] echo Examples: echo vcbuild.bat : builds release build echo vcbuild.bat debug : builds debug build @@ -901,6 +920,8 @@ echo vcbuild.bat link-module my_module.js : bundles my_module as built-in modu echo vcbuild.bat lint : runs the C++, documentation and JavaScript linter echo vcbuild.bat no-cctest : skip building cctest.exe echo vcbuild.bat ccache c:\ccache\ : use ccache to speed build +echo vcbuild.bat thin-lto : builds with Thin LTO applied globally to all targets +echo vcbuild.bat lto : builds with Full LTO applied globally to all targets echo vcbuild.bat pgo-generate : builds instrumented binary for PGO (profile first, then rebuild with pgo-use) echo vcbuild.bat pgo-use : builds optimized binary using PGO profile data goto exit From c58fb1f05b3e0a73424f909a1179b3e552a5abd0 Mon Sep 17 00:00:00 2001 From: StefanStojanovic Date: Thu, 26 Mar 2026 11:41:44 +0100 Subject: [PATCH 4/4] build,win: enable ARM64 PGO --- common.gypi | 48 +++++++++++++++++++++++++++++------------------- configure.py | 18 ++++++++++++++++++ 2 files changed, 47 insertions(+), 19 deletions(-) diff --git a/common.gypi b/common.gypi index a174b74beee124..f0ce6ea13eea43 100644 --- a/common.gypi +++ b/common.gypi @@ -12,6 +12,7 @@ 'msvs_multi_core_compile': '0', # we do enable multicore compiles, but not using the V8 way 'enable_pgo_generate%': '0', 'enable_pgo_use%': '0', + 'clang_profile_lib%': '', 'python%': 'python', 'node_shared%': 'false', @@ -270,25 +271,34 @@ }, }, },], - ['enable_pgo_generate=="true"', { - 'msvs_settings': { - 'VCCLCompilerTool': { - 'AdditionalOptions': ['-fprofile-generate'], - }, - 'VCLinkerTool': { - 'AdditionalOptions': ['-fprofile-generate'], - }, - }, - },], - ['enable_pgo_use=="true"', { - 'msvs_settings': { - 'VCCLCompilerTool': { - 'AdditionalOptions': ['-fprofile-use=$(SolutionDir)node.profdata'], - }, - 'VCLinkerTool': { - 'AdditionalOptions': ['-fprofile-use=$(SolutionDir)node.profdata'], - }, - }, + ], + 'target_conditions': [ + ['_toolset=="target"', { + 'conditions': [ + ['enable_pgo_generate=="true"', { + 'msvs_settings': { + 'VCCLCompilerTool': { + 'AdditionalOptions': ['-fprofile-generate'], + }, + 'VCLinkerTool': { + 'AdditionalOptions': [ + '/NODEFAULTLIB:clang_rt.profile.lib', + '"<(clang_profile_lib)"', + ], + }, + }, + },], + ['enable_pgo_use=="true"', { + 'msvs_settings': { + 'VCCLCompilerTool': { + 'AdditionalOptions': ['-fprofile-use=$(SolutionDir)node.profdata'], + }, + 'VCLinkerTool': { + 'AdditionalOptions': ['-fprofile-use=$(SolutionDir)node.profdata'], + }, + }, + },], + ], },], ], },], diff --git a/configure.py b/configure.py index c3259b5f764ea3..85c7fd6e129c5e 100755 --- a/configure.py +++ b/configure.py @@ -1940,6 +1940,24 @@ def configure_node(o): o['variables']['enable_pgo_generate'] = b(options.enable_pgo_generate) o['variables']['enable_pgo_use'] = b(options.enable_pgo_use) + if flavor == 'win' and (options.enable_pgo_generate or options.enable_pgo_use): + lib_suffix = 'aarch64' if target_arch == 'arm64' else 'x86_64' + lib_name = f'clang_rt.profile-{lib_suffix}.lib' + msvc_dir = target_arch # 'x64' or 'arm64' + + vc_tools_dir = os.environ.get('VCToolsInstallDir', '') + if vc_tools_dir: + clang_profile_lib = os.path.join(vc_tools_dir, 'lib', msvc_dir, lib_name) + if os.path.isfile(clang_profile_lib): + o['variables']['clang_profile_lib'] = clang_profile_lib + else: + raise Exception( + f'PGO profile runtime library not found at {clang_profile_lib}. ' + 'Ensure the ClangCL toolset is installed.') + else: + raise Exception( + 'VCToolsInstallDir not set. Run from a Visual Studio command prompt.') + if flavor != 'win' and options.enable_thin_lto: raise Exception( 'Use --enable-lto instead.')