Skip to content

Use ARM64 VM image in PR/CI - #352

Merged
vmoroz merged 5 commits into
microsoft:mainfrom
vmoroz:PR/use-arm64-vm-image
Jul 27, 2026
Merged

Use ARM64 VM image in PR/CI#352
vmoroz merged 5 commits into
microsoft:mainfrom
vmoroz:PR/use-arm64-vm-image

Conversation

@vmoroz

@vmoroz vmoroz commented Jul 27, 2026

Copy link
Copy Markdown
Member

Summary

This PR moves the Windows build to Visual Studio 2026 (Clang 22 / MSVC 14.51),
switches ARM64EC from MSVC to Clang, and makes ARM64 and ARM64EC first-class
in CI
: both now build the full target set and run the complete test suite on a
native ARM64 agent pool instead of being cross-compiled without tests.

ARM64 and ARM64EC had never been fully compiled (only hermes.dll /
hermes-icu.dll were) and no test had ever run against them. Everything else in
this PR is what it took to get there.

Toolchain

  • Visual Studio 2026 is now required. build.js locates it with
    vswhere -version 18; there is no VS 2022 fallback.
  • vs2022-msvc-* CMake presets are replaced by vs2026-msvc-{x64,x86,arm64}
    (generator Visual Studio 18 2026). The Ninja presets are unchanged.
  • ARM64EC no longer forces MSVC. It builds with Clang like every other target;
    --msvc still selects MSVC explicitly.
  • Developer documentation is updated for the new required VS version and the
    correct component IDs.

ARM64EC on Clang

The MSVC force was one line; making ARM64EC actually compile and link with Clang
was the bulk of the source change. Reviewers will find one root cause behind
almost all of it:

ARM64EC deliberately advertises the x64 predefined macros
(__x86_64__, _M_X64, __SSE2__) so that x64-targeting source keeps
compiling. But Clang generates AArch64 code there and ships no x86
intrinsics
— MSVC papered over this with softintrin-backed emulated
intrinsics; Clang does not. Every guard keyed on an x64 macro must therefore
also test __arm64ec__.

  • Guard fixes (all mechanical, all in the shape above): Support/SIMD.h now
    selects NEON; VM/Profiler.h skips <x86intrin.h> / __rdtsc; llvh's
    Host.cpp skips the x86 cpuid / xgetbv inline asm; dragonbox drops the two
    _umul128 / _addcarry_u64 fast paths; folly reports FOLLY_SSE 0 /
    FOLLY_NEON 1 and emits an AArch64 yield for asm_volatile_pause.
  • Boost's fiber_winfib.hpp gets the BOOST_NO_EXCEPTIONS guards
    fiber_fcontext.hpp already had. ARM64EC is the only target that uses winfib,
    and Clang hard-errors on throw / try under -fno-exceptions where MSVC did
    not.
  • softintrin.lib is now linked for ARM64EC + Clang. MSVC emits a
    /DEFAULTLIB:softintrin directive automatically; Clang does not, so every link
    failed with undefined symbol: _mm_getcsr (EC symbol) from the UCRT
    floating-point objects. It is set in CMAKE_EXE/SHARED_LINKER_FLAGS so that
    CMake's configure-time try_compile checks link too, per-target for the
    .node test addons (MODULE libraries do not inherit those flags), and via
    SHERMES_CC_SYSLDFLAGS for the C that shermes generates and links.
  • The arm64ec-pc-windows-msvc target triple is now passed regardless of host
    architecture, for both the main build and shermes: on an ARM64 host Clang's
    default target is plain ARM64, and non-EC objects cannot link against
    ARM64EC output. The MSVC-only -arm64EC flag injection is now MSVC-only.

Static Hermes AOT: ARM64EC raw longjmp

lib/VM/StaticH.cpp is the one non-mechanical change. Static Hermes throws via
setjmp/longjmp, and the CRT's longjmp goes through RtlUnwindEx. The file
already used a raw longjmp on x64 and ARM64 to avoid unwinding across DLL
boundaries, but excluded ARM64EC — where the CRT path additionally trips
RtlUnwindEx's Control Flow Guard check
(FAST_FAIL_INVALID_LONGJUMP_TARGET, 0xC0000409) whenever the AOT module is
loaded into a CFG-enforced host process, which is exactly what shermes -exec
does.

The ARM64EC _sh_raw_longjmp restores the AArch64 callee-saved state from the
x64-layout jmp_buf (ARM64EC has no _M_ARM64EC branch in setjmp.h, and
_M_ARM64EC implies _M_X64), following the fixed ARM64EC x64↔AArch64 register
map. The mapping, the registers that are intentionally not restored (x23/x24/x28
are never used by ARM64EC codegen) and the MxCsr slot decision are documented in
the code. This turned 22 failing AOT lit tests into a clean run.

CI and PR pipelines

  • The top-level pool now demands ImageOverride -equals windows-2025-1espt,
    which is what moves x64, x86 and all UWP cells onto the VS 2026 image.
  • win32_arm64 and win32_arm64ec run on the windows-2025-1espt-arm64 pool
    (single-image, so no ImageOverride) with CanRunTest: true. On a native host
    both stop being cross builds, so the tools, the unit-test binaries and the lit
    drivers are built, and the C++ unit tests, JS regression tests and Test262 Intl
    tests all run. The pool is selected by a per-cell UseArm64Pool flag rather
    than by TargetCPU, which keeps uwp_arm64 on the x64 pool — UWP binaries are
    not executed by the pipeline on any architecture.
  • ARM64EC is tested, not just built: the test executables are themselves ARM64EC
    and load the ARM64EC hermes.dll / hermes-icu.dll, so the shipped artifacts
    are what gets exercised.
  • New .ado/ensure-node10-shim.yml, referenced first in the ARM64 jobs. ARM64
    agents ship no externals\node10, but the mandatory 1ES build-retention
    post-job still runs on the Node 10 task handler and would otherwise fail.
  • New .ado/image/README.md documenting the pool ↔ image mapping. The image
    definitions themselves are shared with microsoft/v8-jsi and are not
    duplicated here.

Agent environment

The new images differ from the old ones in ways the pipeline and the build
script had silently depended on:

  • UseNode@1 is removed. The images ship Node.js 24 on PATH; the task tried
    to provision a runtime instead, which the network-isolated PR pipeline blocks.
  • The diagnostic file listing used ls -R, which is not on cmd.exe's PATH
    on these images. It is now a pwsh step.
  • Git Bash discovery in build.js is fixed. It used to take the first git.exe
    on PATH and rewrite cmd to bin in that path; on the new agents the first
    Git is the agent's own bundled copy, which has no bash.exe, so every lit
    test failed with "Running Hermes LIT tests in CMD.exe is not supported". It
    now scans all git.exe locations plus the default install directories and
    picks one that actually contains bash.exe.

Package contents

Because the ARM64 cells are now native builds, they also stage hermes.exe and
hermesc.exe. The NuGet package therefore carries tools for every target
platform (tools\native\release\{x64,x86,arm64,arm64ec}\) instead of only x64
and x86. The existing signing patterns and nuspec globs pick them up unchanged.

Feed compliance

A repo-level NuGet.config pins package restore to the ADO feed, and the local
BinSkim download now uses that feed instead of nuget.org.

Validation

All runs are Release, on VS 2026 18.8 / MSVC 14.51.36231 / Clang 22.1.3 /
Windows SDK 10.0.28000. ARM64 and ARM64EC were validated on a native ARM64 host.

Target Build C++ unit tests JS regression tests Test262 Intl BinSkim
x64 0 errors 16/16 3064 pass, 0 unexpected 100% (348/348)
x86 0 errors 16/16 3063 pass, 0 unexpected 100% (348/348)
arm64 0 errors 16/16 3064 pass, 0 unexpected 100% (348/348) all rules pass
arm64ec 0 errors 16/16 3064 pass, 0 unexpected 100% (348/348) all rules pass

JS regression runs additionally report the pre-existing 9 expected failures and
78 unsupported tests, unchanged from main.

Machine types verified with dumpbin /headers for hermes.dll, hermes-icu.dll
and the test executables: AA64 machine (ARM64) for arm64, and
8664 machine (x64) (ARM64X) for arm64ec.

Notes for reviewers

  • Breaking for developers: VS 2022 no longer builds this repo, and the
    vs2022-msvc-* presets are gone.
  • ARM64 CI jobs are now real builds with three test suites, so those cells take
    substantially longer than the previous library-only cross builds.
  • Vendored-dependency edits (llvh, folly, boost, dragonbox) are kept as small,
    additive __arm64ec__ guards to minimize friction when syncing upstream.

@vmoroz
vmoroz requested a review from a team as a code owner July 27, 2026 15:55
@github-actions

github-actions Bot commented Jul 27, 2026

Copy link
Copy Markdown

Benchmark Results

Total benchmarks: 204

Inputs: baseline/baseline.json, bench_result.json

v8 (6176ms, 4857ms) -21.4%
v8 hermes (CI) hermes (CI)
v8-crypto 622.6ms 480ms
v8-deltablue 739.4ms 610.2ms
v8-raytrace 127.8ms 95.2ms
v8-regexp 593ms 475.2ms
v8-richards 937ms 713.6ms
v8-splay 225.2ms 188.8ms
v8-crypto (static) 452ms 339.2ms
v8-deltablue (static) 661.6ms 519.8ms
v8-raytrace (static) 91.8ms 73.6ms
v8-regexp (static) 749.2ms 596.8ms
v8-richards (static) 781.4ms 590.2ms
v8-splay (static) 195ms 174.8ms
test-suites (317886ms, 254627ms) -19.9%
test-suites hermes (CI) hermes (CI)
box2d 2949.8ms 1922.8ms
earley-boyer 2564.8ms 1983.4ms
navier-stokes 5743.8ms 3455.4ms
pdfjs 1038.2ms 748.8ms
gbemu 2296ms 1713ms
code-load 4472.8ms 3705.4ms
typescript 3381.8ms 2375.6ms
simpleSum 8855.4ms 5010.4ms
propAccess 2691.2ms 1761.8ms
allocObj 252ms 172.2ms
allocObjLit 6371.8ms 5111.4ms
allocNewObj 20053ms 15476.8ms
allocArray 249ms 171.4ms
allocNewArray 34956.8ms 27916ms
arrayRead 115.6ms 76.4ms
arrayReadByIndex 550ms 394.8ms
largeArrayRead 827.2ms 409ms
arrayWrite 261.8ms 196ms
largeArrayWrite 2218ms 1615ms
interp-dispatch 2880ms 1754.4ms
wb-perf 8327ms 7077ms
arrayReverse 40.6ms 31.8ms
arrayMap 1472.6ms 1469ms
arrayIndexOf 143.2ms 108ms
arrayLastIndexOf 148.6ms 117ms
arrayEvery 2308.8ms 2263ms
arraySome 2174.4ms 2239.4ms
arrayFill 2401.2ms 1900.2ms
arrayFilter 1866.4ms 1689.6ms
arrayFind 3207ms 2767.2ms
arrayFindIndex 3322ms 2749.6ms
arrayPop 1031.4ms 820.8ms
arrayReduce 1994.2ms 2014.4ms
arrayReduceRight 2075.8ms 2025ms
arrayShift 2065.8ms 1834.6ms
arrayUnshift 2109.4ms 1853.8ms
arrayIncludes 1216.6ms 901.6ms
arrayFrom 1102.8ms 859.8ms
arrayCopyWithin 1487ms 1240.8ms
stringFromCharCode 92.6ms 73.4ms
arraySlice 786.2ms 637.2ms
arraySplice 26.8ms 24ms
arrayOf 1048.8ms 773ms
stringCharAt 1346.6ms 995ms
stringMatch 3192.4ms 2249.8ms
stringSearch 3508.2ms 2228.4ms
stringStartsWith 701ms 466.2ms
stringEndsWith 650.4ms 416.8ms
stringIncludes 1520.6ms 1454.2ms
stringIndexOf 1555.6ms 1808.2ms
stringLastIndexOf 1821.4ms 1386.8ms
stringSplit 822ms 611.2ms
stringSlice 490ms 397ms
stringPadStart 2910.8ms 2325ms
stringPadEnd 2910ms 2322.4ms
regExpMatch 1506.2ms 1182.4ms
regExpSearch 1227ms 1060.4ms
regExpToString 1204ms 831ms
stringReplace 1394.8ms 1418.6ms
regExpReplace 3309.2ms 707ms
regExpFlags 956.8ms 761.6ms
regExpSplit 1307.8ms 995.8ms
numberArrayReadWrite 2477.8ms 1856.2ms
protoCache 3594.6ms 1826.4ms
box2d (static) 1792.6ms 1385.4ms
earley-boyer (static) 1875.6ms 1452.8ms
navier-stokes (static) 3410.2ms 2739.4ms
pdfjs (static) 812.8ms 627.8ms
gbemu (static) 1797ms 1374.2ms
code-load (static) 4199.2ms 3416.2ms
typescript (static) 2685.6ms 2061.4ms
simpleSum (static) 936.2ms 847.2ms
propAccess (static) 2298ms 1823.6ms
allocObj (static) 0.2ms 0.2ms
allocObjLit (static) 4154.4ms 3620.6ms
allocNewObj (static) 15749.2ms 14031.8ms
allocArray (static) 0.6ms 0.4ms
allocNewArray (static) 28494.2ms 25112ms
arrayRead (static) 74.2ms 61.2ms
arrayReadByIndex (static) 453.2ms 390.4ms
largeArrayRead (static) 552.2ms 447.2ms
arrayWrite (static) 178.4ms 158ms
largeArrayWrite (static) 1568.6ms 1415.8ms
interp-dispatch (static) 1918.8ms 1644ms
wb-perf (static) 8991.2ms 7390.2ms
arrayReverse (static) 37ms 33.2ms
arrayMap (static) 1064.8ms 849ms
arrayIndexOf (static) 128.2ms 103.8ms
arrayLastIndexOf (static) 138.8ms 98.2ms
arrayEvery (static) 1397.6ms 1258.4ms
arraySome (static) 1406.6ms 1242.2ms
arrayFill (static) 1969.2ms 1735ms
arrayFilter (static) 1061ms 923.8ms
arrayFind (static) 2242.2ms 2001.2ms
arrayFindIndex (static) 2235.8ms 1998.2ms
arrayPop (static) 878ms 727.6ms
arrayReduce (static) 1311.8ms 1149.6ms
arrayReduceRight (static) 1322.6ms 1132.6ms
arrayShift (static) 1512ms 1250ms
arrayUnshift (static) 1568.2ms 1282.6ms
arrayIncludes (static) 963.4ms 780ms
arrayFrom (static) 1030.6ms 788.4ms
arrayCopyWithin (static) 1109.6ms 902.6ms
stringFromCharCode (static) 77ms 58.6ms
arraySlice (static) 571.6ms 483.2ms
arraySplice (static) 26ms 23.2ms
arrayOf (static) 868.8ms 644.2ms
stringCharAt (static) 1100.6ms 909ms
stringMatch (static) 2227.6ms 1898.6ms
stringSearch (static) 2219.2ms 1836ms
stringStartsWith (static) 494.2ms 401.8ms
stringEndsWith (static) 457.8ms 385.2ms
stringIncludes (static) 1437.4ms 1687ms
stringIndexOf (static) 1440.8ms 1839.4ms
stringLastIndexOf (static) 1956.6ms 1393.6ms
stringSplit (static) 651.2ms 529.6ms
stringSlice (static) 445.6ms 360.6ms
stringPadStart (static) 2294.4ms 1879.4ms
stringPadEnd (static) 2499.2ms 1889.4ms
regExpMatch (static) 1483.2ms 1080.4ms
regExpSearch (static) 1296.2ms 1080.8ms
regExpToString (static) 1326.6ms 1012.6ms
stringReplace (static) 1455.6ms 1405.8ms
regExpReplace (static) 851.8ms 640.8ms
regExpFlags (static) 870.6ms 665.8ms
regExpSplit (static) 1200ms 848.4ms
numberArrayReadWrite (static) 2132.8ms 1740.6ms
protoCache (static) 3595.8ms 2936.6ms
micros (60876ms, 49293ms) -19.0%
micros hermes (CI) hermes (CI)
getNodeById.js 5273ms 4387.2ms
setInsert.js 2864.8ms 2482.4ms
stringify-number.js 1823.4ms 1458.6ms
typed-array-sort.js 22381ms 17501.4ms
getNodeById.js (static) 3941ms 3230.8ms
setInsert.js (static) 2517.6ms 2305.2ms
stringify-number.js (static) 1663.6ms 1227.8ms
typed-array-sort.js (static) 20411.4ms 16699.8ms
jit-benches (8563ms, 5377ms) -37.2%
jit-benches hermes (CI) hermes (CI)
idisp.js 2810.2ms 1722.2ms
idispn.js 3395.8ms 1763.2ms
idisp.js (static) 1906ms 1588.4ms
idispn.js (static) 450.8ms 303.4ms
many-subclasses (73437ms, 61769ms) -15.9%
many-subclasses hermes (CI) hermes (CI)
many.js 19104.8ms 14664ms
many-sh-1.js 7322.8ms 6252ms
many-sh-2.js 7329ms 6266.4ms
many-sh-3.js 7083.6ms 5901.8ms
many-sh-4.js 7235ms 5911.8ms
many.js (static) 15996ms 14954.6ms
many-sh-1.js (static) 2285.2ms 1866.2ms
many-sh-2.js (static) 2387ms 2037.4ms
many-sh-3.js (static) 2347.2ms 1964.8ms
many-sh-4.js (static) 2346ms 1949.8ms
map-objects (3627ms, 2796ms) -22.9%
map-objects hermes (CI) hermes (CI)
map-objects-untyped.js 1023.6ms 780.8ms
map-objects-typed.js 957.6ms 715.2ms
map-objects-untyped.js (static) 917.2ms 725.2ms
map-objects-typed.js (static) 728.8ms 574.6ms
map-strings (4335ms, 3228ms) -25.5%
map-strings hermes (CI) hermes (CI)
map-strings-untyped.js 1196.8ms 897.6ms
map-strings-typed.js 1139.2ms 818.4ms
map-strings-untyped.js (static) 1092.2ms 824.2ms
map-strings-typed.js (static) 906.8ms 688.2ms
nbody (3448ms, 2395ms) -30.5%
nbody hermes (CI) hermes (CI)
original/nbody.js 839.6ms 568.6ms
fully-typed/nbody.js 710ms 455.4ms
fully-typed/nbody.ts 865.2ms 551.4ms
original/nbody.js (static) 449.2ms 357.4ms
fully-typed/nbody.js (static) 135ms 100.4ms
fully-typed/nbody.ts (static) 449.2ms 362ms
string-switch (6503ms, 4573ms) -29.7%
string-switch (string-switch/plain) hermes (CI) hermes (CI)
bench.js 1316ms 1074.4ms
bench.js (static) 5187ms 3498.8ms
raytracer (5608ms, 4399ms) -21.6%
raytracer (raytracer/original) hermes (CI) hermes (CI)
bench-raytracer.js 1550.4ms 1179.8ms
raytracer.ts 1667.6ms 1227.4ms
bench-raytracer.js (static) 1198.4ms 992ms
raytracer.ts (static) 1191.8ms 999.6ms
MiniReact (30332ms, 22843ms) -24.7%
MiniReact hermes (CI) hermes (CI)
no-objects/out/simple-stripped.js 2239.2ms 1555.8ms
no-objects/out/simple-lowered.js 2270.8ms 1580.4ms
no-objects/out/music-stripped.js 42.6ms 32.6ms
no-objects/out/music-lowered.js 47.2ms 37.4ms
no-deps/stripped/MiniReact.js 5004ms 3788ms
no-deps/MiniReact.js 5256.2ms 3747.4ms
no-objects/out/simple.js 2260.4ms 1564ms
no-objects/out/music.js 45.6ms 36.4ms
no-objects/out/simple-stripped.js (static) 1725.2ms 1381.4ms
no-objects/out/simple-lowered.js (static) 1725.4ms 1367.2ms
no-objects/out/music-stripped.js (static) 18.8ms 17ms
no-objects/out/music-lowered.js (static) 19.4ms 17.8ms
no-deps/stripped/MiniReact.js (static) 4024.8ms 3218ms
no-deps/MiniReact.js (static) 3938.2ms 3142.4ms
no-objects/out/simple.js (static) 1695.2ms 1341ms
no-objects/out/music.js (static) 19ms 16.2ms
widgets (12934ms, 10289ms) -20.4%
widgets hermes (CI) hermes (CI)
simple-classes/widgets.js 1758.2ms 1323.4ms
original/es5/widgets.js 2798.8ms 2245.8ms
single-file/es5/widgets.js 2775.2ms 2247.8ms
simple-classes/widgets.js (static) 1012ms 806.8ms
original/es5/widgets.js (static) 2283.2ms 1843.6ms
single-file/es5/widgets.js (static) 2306.2ms 1821.8ms

@vmoroz
vmoroz enabled auto-merge (squash) July 27, 2026 19:35
@vmoroz
vmoroz merged commit 49e9f51 into microsoft:main Jul 27, 2026
26 checks passed
@vmoroz
vmoroz deleted the PR/use-arm64-vm-image branch July 27, 2026 21:35
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants