Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
246 changes: 246 additions & 0 deletions .github/workflows/pgo-windows.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,246 @@
name: Windows PGO profdata generation

on:
workflow_dispatch:
inputs:
source_ref:
description: Commit SHA, branch, or tag in this fork to build and profile
required: true
type: string
architecture:
description: Target architecture to build and profile
required: true
type: choice
default: arm64
options:
- arm64
- x64
committer_name:
description: Your full name for the commit and DCO sign-off
required: true
type: string
committer_email:
description: Your email for the commit and DCO sign-off
required: true
type: string

permissions:
contents: read

concurrency:
group: ${{ github.workflow }}-${{ inputs.architecture }}
cancel-in-progress: false

env:
PGO_ARCH: ${{ inputs.architecture }}
PYTHON_VERSION: '3.15'
RUSTC_VERSION: '1.88'

jobs:
build:
if: github.repository == 'JaneaSystems/node'
# windows-2022 comes with VS2022, windows-2025 comes with VS2026
# node.profdata from VS2026 is not compatible with VS2022, but vice versa is
# after moving to VS2026, the profdata can still be used with VS2022, but should be updated
runs-on: windows-2022
timeout-minutes: 300
outputs:
source_sha: ${{ steps.source.outputs.sha }}
source_date: ${{ steps.source.outputs.date }}
publish_branch: ${{ steps.source.outputs.branch }}
steps:
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
with:
ref: ${{ inputs.source_ref }}
persist-credentials: false
- name: Resolve the source commit and date
id: source
shell: pwsh
run: |
$sourceSha = git rev-parse --verify 'HEAD^{commit}'
if ($LASTEXITCODE -ne 0) { exit $LASTEXITCODE }
$timestamp = git show --no-patch --format=%ct $sourceSha
if ($LASTEXITCODE -ne 0) { exit $LASTEXITCODE }
$sourceDate = [DateTimeOffset]::FromUnixTimeSeconds([long]$timestamp).UtcDateTime.ToString('yyyy-MM-dd')
"sha=$sourceSha" >> $env:GITHUB_OUTPUT
"date=$sourceDate" >> $env:GITHUB_OUTPUT
"branch=pgo-windows-$env:PGO_ARCH-$sourceSha-$sourceDate" >> $env:GITHUB_OUTPUT
"Source commit: $sourceSha" >> $env:GITHUB_STEP_SUMMARY
"Source commit date (UTC): $sourceDate" >> $env:GITHUB_STEP_SUMMARY
if (!(Test-Path vcbuild.bat) -or !(Test-Path tools/pgo/pgo.py)) {
throw 'The selected commit must contain vcbuild.bat and tools/pgo/pgo.py for Windows PGO support.'
}
- name: Check whether the profile was already published
shell: pwsh
env:
GH_TOKEN: ${{ github.token }}
PGO_BRANCH: ${{ steps.source.outputs.branch }}
run: |
gh auth setup-git
if ($LASTEXITCODE -ne 0) { exit $LASTEXITCODE }
$existingBranch = git ls-remote --heads origin "refs/heads/$env:PGO_BRANCH"
if ($LASTEXITCODE -ne 0) { exit $LASTEXITCODE }
if ($existingBranch) {
$message = "This action was already done previously: branch $env:PGO_BRANCH already exists."
Write-Output "::error::$message"
$message >> $env:GITHUB_STEP_SUMMARY
exit 1
}
- name: Set up Python
uses: actions/setup-python@5fda3b95a4ea91299a34e894583c3862153e4b97 # v7.0.0
with:
python-version: ${{ env.PYTHON_VERSION }}
- name: Set up Rust for the target architecture
shell: pwsh
run: |
if ($env:PGO_ARCH -cnotin @('arm64', 'x64')) {
throw 'The target architecture must be arm64 or x64.'
}
rustup override set $env:RUSTC_VERSION
if ($LASTEXITCODE -ne 0) { exit $LASTEXITCODE }
if ($env:PGO_ARCH -eq 'arm64') {
rustup target add aarch64-pc-windows-msvc
if ($LASTEXITCODE -ne 0) { exit $LASTEXITCODE }
}
- name: Install NASM for x64
if: inputs.architecture == 'x64'
shell: pwsh
run: choco install nasm --yes --no-progress
- name: Build the instrumented ${{ inputs.architecture }} release
shell: cmd
run: |
if not "%PROCESSOR_ARCHITECTURE%"=="AMD64" exit /b 1
call vcbuild.bat build-release %PGO_ARCH% pgo-generate nosign
if errorlevel 1 exit /b 1
if not exist Release\node.exe exit /b 1
echo LLVM_BIN=%VCINSTALLDIR%\Tools\Llvm\x64\bin>>"%GITHUB_ENV%"
- name: Stage the binary and matching profile tool
if: inputs.architecture == 'arm64'
shell: pwsh
run: |
New-Item -ItemType Directory -Path pgo-artifact/Release -Force | Out-Null
Copy-Item Release/node.exe pgo-artifact/Release/
Copy-Item "$env:LLVM_BIN/llvm-profdata.exe" pgo-artifact/
Get-ChildItem $env:LLVM_BIN -Filter '*.dll' | Copy-Item -Destination pgo-artifact/
- name: Upload the instrumented build
if: inputs.architecture == 'arm64'
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
with:
name: node-pgo-windows-arm64
path: pgo-artifact/
if-no-files-found: error
retention-days: 7
- name: Generate the x64 profile on the build runner
if: inputs.architecture == 'x64'
shell: pwsh
timeout-minutes: 30
env:
LLVM_PROFILE_FILE: ${{ runner.temp }}\node-check-%p.profraw
run: |
$env:LLVM_PROFDATA = Join-Path $env:LLVM_BIN 'llvm-profdata.exe'
.\Release\node.exe -e "require('node:assert/strict').equal(process.arch, 'x64')"
if ($LASTEXITCODE -ne 0) { exit $LASTEXITCODE }
python tools\pgo\pgo.py
if ($LASTEXITCODE -ne 0) { exit $LASTEXITCODE }
if ((Get-Item node.profdata).Length -eq 0) {
throw 'The generated profile is empty.'
}
- name: Upload the x64 profile
if: inputs.architecture == 'x64'
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
with:
name: profdata-windows-${{ inputs.architecture }}
path: node.profdata
if-no-files-found: error
retention-days: 7

train:
needs: build
if: inputs.architecture == 'arm64'
runs-on: windows-11-arm
timeout-minutes: 30
steps:
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
with:
ref: ${{ needs.build.outputs.source_sha }}
persist-credentials: false
- name: Set up Python
uses: actions/setup-python@5fda3b95a4ea91299a34e894583c3862153e4b97 # v7.0.0
with:
python-version: ${{ env.PYTHON_VERSION }}
architecture: arm64
- name: Download the instrumented build
uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1
with:
name: node-pgo-windows-arm64
- name: Generate the ARM64 profile
shell: pwsh
env:
LLVM_PROFDATA: ${{ github.workspace }}\llvm-profdata.exe
LLVM_PROFILE_FILE: ${{ runner.temp }}\node-check-%p.profraw
run: |
if ($env:RUNNER_ARCH -ne 'ARM64') {
throw 'PGO training requires a Windows ARM64 runner.'
}
.\Release\node.exe -e "require('node:assert/strict').equal(process.arch, 'arm64')"
if ($LASTEXITCODE -ne 0) { exit $LASTEXITCODE }
python tools\pgo\pgo.py
if ($LASTEXITCODE -ne 0) { exit $LASTEXITCODE }
if ((Get-Item node.profdata).Length -eq 0) {
throw 'The generated profile is empty.'
}
- name: Upload the profile
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
with:
name: profdata-windows-${{ inputs.architecture }}
path: node.profdata
if-no-files-found: error
retention-days: 7

publish:
needs: [build, train]
if: >-
${{
!cancelled() &&
github.repository == 'JaneaSystems/node' &&
needs.build.result == 'success' &&
(
(inputs.architecture == 'arm64' && needs.train.result == 'success') ||
(inputs.architecture == 'x64' && needs.train.result == 'skipped')
)
}}
runs-on: ubuntu-latest
timeout-minutes: 10
permissions:
contents: write
env:
PGO_BRANCH: ${{ needs.build.outputs.publish_branch }}
steps:
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
with:
repository: JaneaSystems/node
ref: ${{ needs.build.outputs.source_sha }}
persist-credentials: false
- name: Download the profile into the repository
uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1
with:
name: profdata-windows-${{ inputs.architecture }}
path: tools/pgo/profdata/windows/${{ inputs.architecture }}
- name: Commit and push the profile
shell: bash
env:
GH_TOKEN: ${{ github.token }}
GIT_AUTHOR_NAME: ${{ inputs.committer_name }}
GIT_AUTHOR_EMAIL: ${{ inputs.committer_email }}
GIT_COMMITTER_NAME: ${{ inputs.committer_name }}
GIT_COMMITTER_EMAIL: ${{ inputs.committer_email }}
run: |
profile="tools/pgo/profdata/windows/$PGO_ARCH/node.profdata"
test -s "$profile"
gh auth setup-git
git switch --create "$PGO_BRANCH"
git add --force -- "$profile"
git commit --allow-empty --signoff -m "win,build: add $PGO_ARCH pgo profdata" \
--trailer "Assisted-by: GitHub Copilot"
git push origin "HEAD:refs/heads/$PGO_BRANCH"
echo "Published branch: $PGO_BRANCH" >> "$GITHUB_STEP_SUMMARY"
51 changes: 51 additions & 0 deletions tools/pgo/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,57 @@ Clang installation.
python tools\pgo\pgo.py --duration=30
```

### GitHub Actions: Windows PGO profdata generation

The [Windows PGO workflow](../../.github/workflows/pgo-windows.yml)
is manually dispatched and only runs in `JaneaSystems/node`. Once it is on the
fork's default branch, open
**Actions > Windows PGO profdata generation > Run workflow** and select the
branch containing the workflow definition. Set `source_ref` to a full commit
SHA, branch, or tag to fetch from `JaneaSystems/node`; this input does not select
another fork. The repository condition checks where the workflow runs, not the
origin of the selected commit. Choose the target `architecture`: `arm64`
(the default) or `x64`. The selected source commit does not need to contain the
workflow file. Supply `committer_name` and `committer_email` with your full name
and email for the commit identity and `Signed-off-by` trailer, attesting to the
[Developer Certificate of Origin](https://developercertificate.org/).

The workflow builds with
`vcbuild.bat build-release <architecture> pgo-generate nosign` on `windows-2025`.
The `nosign` option avoids the Azure Trusted Signing credentials otherwise
required by `build-release`. The source ref is resolved once at checkout, and
every job uses that exact commit even if the source branch moves during the run.
The selected revision must support the Windows PGO build command and contain
`tools/pgo/pgo.py`; revisions predating that support cannot use this workflow.
The workflow sets up Python 3.15 and Rust 1.88, adds the
`aarch64-pc-windows-msvc` Rust target for ARM64, and installs NASM for x64.

For ARM64, the workflow transfers `Release/node.exe` and the matching
`llvm-profdata` tool to `windows-11-arm` for training. Only the x64 profile merge
tool runs under Windows emulation; Node.js and the workloads run natively on
ARM64. For x64, `pgo.py` runs directly after compilation in the same job on the
same machine. The separate ARM64 training job and binary transfer are skipped.

After `pgo.py` succeeds, the workflow places `node.profdata` in
`tools/pgo/profdata/windows/<architecture>/` and pushes a new branch named
`pgo-windows-<architecture>-<source_sha>-YYYY-MM-DD` to `JaneaSystems/node`, using
the full source commit SHA and its committer date converted to UTC. Immediately
after resolving the source commit, the build job checks whether this branch
exists, before setting up tools or compiling. If it does, the build job fails
and reports that the action was already done previously; training and publication
are skipped. The generated commit is based on that source commit.
Its subject is `win,build: add <architecture> pgo profdata`, with
your sign-off and an `Assisted-by: GitHub Copilot` trailer. The downloaded profile
replaces any existing file, and both new and modified profiles are staged. The
workflow commits and pushes even if the profile is unchanged, allowing an empty
commit in that case.

Only the publication job receives `contents: write`, using the fork's
`GITHUB_TOKEN`; no personal access token is needed. Review the generated commit
before using it in a contribution. Profiles are retained as workflow artifacts
for seven days. ARM64 runs also retain the instrumented build transferred to the
training runner.

## Quick Start: Linux with GCC

```bash
Expand Down
Loading