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
Original file line number Diff line number Diff line change
Expand Up @@ -885,10 +885,10 @@ index f85d1129..43c9a8be 100644
}
diff --git a/test/js/node/process/arraybuffer-accounting.fixture.mjs b/test/js/node/process/arraybuffer-accounting.fixture.mjs
new file mode 100644
index 0000000000000000000000000000000000000000..d2bce713c2bd0a7bc66262eb327e1a6cf52676b4
index 0000000000000000000000000000000000000000..8611deb06e3f23ad5605ac7693efcfb6c833a16d
--- /dev/null
+++ b/test/js/node/process/arraybuffer-accounting.fixture.mjs
@@ -0,0 +1,361 @@
@@ -0,0 +1,366 @@
+import assert from "node:assert/strict";
+import { once } from "node:events";
+import { readFileSync } from "node:fs";
Expand Down Expand Up @@ -1081,6 +1081,11 @@ index 0000000000000000000000000000000000000000..d2bce713c2bd0a7bc66262eb327e1a6c
+ return [before, retained, isolated, released];
+}
+
+// Keep JIT compilation of the busy loop separate from this large fixture.
+function spinUntilReleased(flag) {
+ while (!Atomics.load(flag, 0)) {}
+}
+
+const kind = isMainThread ? process.argv[2] : typeof workerData === "string" ? workerData : workerData.kind;
+if (!isMainThread && ["busy", "wait", "shutdown", "startup"].includes(kind)) {
+ // Finish startup work before comparing snapshots of a fresh allocation.
Expand All @@ -1091,7 +1096,7 @@ index 0000000000000000000000000000000000000000..d2bce713c2bd0a7bc66262eb327e1a6c
+ if (kind === "wait") {
+ assert.equal(Atomics.wait(flag, 0, 0), "ok");
+ } else {
+ while (!Atomics.load(flag, 0)) {}
+ spinUntilReleased(flag);
+ }
+ parentPort.postMessage("released");
+} else if (!isMainThread && kind === "transfer") {
Expand Down
41 changes: 41 additions & 0 deletions .github/openclaw/qualify-arm64.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
#!/usr/bin/env bash
set -euo pipefail
: "${ENGINE_BUILD_DIR:?}" "${QUALIFICATION_DIR:?}"
case "$(uname -m)" in arm64|aarch64) ;; *) echo 'Native ARM64 host required' >&2; exit 1;; esac
mkdir -p "$QUALIFICATION_DIR"
QUALIFICATION_DIR=$(cd "$QUALIFICATION_DIR" && pwd)
jsc="$ENGINE_BUILD_DIR/bin/jsc"
"$ENGINE_BUILD_DIR/bin/testmasm" > "$QUALIFICATION_DIR/testmasm.log" 2>&1
"$ENGINE_BUILD_DIR/bin/testFFI" > "$QUALIFICATION_DIR/testffi.log" 2>&1
Tools/Scripts/run-jsc-stress-tests JSTests/stress --jsc "$jsc" --child-processes "${ENGINE_TEST_JOBS:-4}" \
--filter 'promise|microtask|dynamic-import|bun-async-context|module-loader|top-level-await|async-module|module-namespace|intl-segmenter|callsite-syntax-positions|stack-position-regressions|error-stack' \
--output-dir "$QUALIFICATION_DIR/jsc-stress" --no-retry > "$QUALIFICATION_DIR/jsc-stress.log" 2>&1
python3 .github/openclaw/verify-jsc-results.py "$QUALIFICATION_DIR/jsc-stress" --require module-namespace-esmodule-marker.js --require callsite-syntax-positions.js --require stack-position-regressions.js
Tools/Scripts/run-jsc-stress-tests JSTests/modules.yaml --jsc "$jsc" --child-processes "${ENGINE_TEST_JOBS:-4}" \
--output-dir "$QUALIFICATION_DIR/jsc-modules" --no-retry > "$QUALIFICATION_DIR/jsc-modules.log" 2>&1
# Accounting requires generational GC; run its established explicit modes below.
for fixture in array-buffer-accounting heap-allocation-sampling; do
for tier in interpreter baseline dfg ftl concurrent; do
args=(--useGenerationalGC=true --forceMiniVMMode=false --useDollarVM=true)
case "$tier" in
interpreter) args+=(--useJIT=false);;
baseline) args+=(--useDFGJIT=false --useFTLJIT=false);;
dfg) args+=(--useFTLJIT=false --useConcurrentJIT=false --thresholdForJITAfterWarmUp=10 --thresholdForOptimizeAfterWarmUp=20);;
ftl) args+=(--useConcurrentJIT=false --thresholdForJITAfterWarmUp=10 --thresholdForOptimizeAfterWarmUp=20 --thresholdForFTLOptimizeAfterWarmUp=20);;
concurrent) args+=(--gcAtEnd=true --useConcurrentGC=true);;
esac
"$jsc" "${args[@]}" "JSTests/stress/$fixture.js" > "$QUALIFICATION_DIR/$fixture-$tier.log" 2>&1
done
done
python3 - "$QUALIFICATION_DIR" "$(git rev-parse HEAD)" <<'PY'
import json, platform, sys
from pathlib import Path
out = Path(sys.argv[1])
counts = {}
for suite, minimum in [('jsc-stress', 1400), ('jsc-modules', 1600)]:
lines = (out / suite / 'results').read_text().splitlines()
assert len(lines) >= minimum and all(line.endswith(': PASS') for line in lines), suite
counts[suite] = len(lines)
assert 'testAdd64RegisterAddress()' in (out / 'testmasm.log').read_text()
(out / 'arm64-gate.json').write_text(json.dumps({'passed': True, 'source': sys.argv[2], 'machine': platform.machine(), 'system': platform.system(), 'counts': counts, 'accounting_modes': 5, 'sampling_modes': 5, 'testmasm': True, 'testffi': True}, indent=2) + '\n')
PY
15 changes: 14 additions & 1 deletion .github/openclaw/qualify-windows-arm64.ps1
Original file line number Diff line number Diff line change
Expand Up @@ -36,5 +36,18 @@ $result = & $jsc -e $probe
if ($LASTEXITCODE -ne 0 -or $result -cne 'windows-arm64-jsc-ok') { throw 'Native JSC startup/JIT probe failed.' }
& $ffi
if ($LASTEXITCODE -ne 0) { throw 'Native testFFI failed.' }
@{ passed = $true; source = $Source; lane = $label; sha256 = $digest; architecture = 'arm64'; startup = $true; dfg = $true; ffi = $true } |
$fixture = Join-Path $PSScriptRoot '../../JSTests/stress/array-buffer-accounting.js'
$modes = @(
@{ Name = 'interpreter'; Flags = @('--useJIT=false') },
@{ Name = 'baseline'; Flags = @('--useDFGJIT=false', '--useFTLJIT=false') },
@{ Name = 'dfg'; Flags = @('--useFTLJIT=false', '--useConcurrentJIT=false') },
@{ Name = 'ftl'; Flags = @('--useConcurrentJIT=false', '--thresholdForJITAfterWarmUp=10', '--thresholdForOptimizeAfterWarmUp=20', '--thresholdForFTLOptimizeAfterWarmUp=20') },
@{ Name = 'concurrent'; Flags = @('--gcAtEnd=true', '--useConcurrentGC=true') }
)
foreach ($mode in $modes) {
$flags = @('--useGenerationalGC=true', '--forceMiniVMMode=false') + $mode.Flags
& $jsc @flags $fixture *> (Join-Path $OutputDirectory "accounting-$($mode.Name).log")
if ($LASTEXITCODE -ne 0) { throw "Native ARM64 ArrayBuffer accounting failed in $($mode.Name) mode." }
}
@{ passed = $true; source = $Source; lane = $label; sha256 = $digest; architecture = 'arm64'; startup = $true; dfg = $true; ffi = $true; arraybuffer_accounting = $true; arraybuffer_modes = $modes.Count } |
ConvertTo-Json | Set-Content -Encoding utf8 (Join-Path $OutputDirectory 'gate.json')
4 changes: 3 additions & 1 deletion .github/openclaw/release-notes.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
This release fixes ARM64 register-to-memory 64-bit addition, restoring DFG typed-array allocation accounting and preserving the source register. The testmasm regression checks the stored sum, source register, adjacent memory and 64-bit wraparound. Native macOS and Linux ARM64 qualification covers assembler tests and JSC interpreter, baseline, DFG, FTL and concurrent-GC modes.

This release adds the non-LTO Windows ARM64 artifact to the qualified OpenClaw engine on upstream `5718a6ec579b98362ea7276a426deedcc6281ef5`, with native Windows 11 ARM64 startup, DFG JIT and FFI gates bound to the archive and source.

- Build Windows ARM64 with the unchanged upstream cross-compilation recipe and require both JSC and testFFI to be native ARM64 PE files.
Expand All @@ -14,7 +16,7 @@ The previously released engine changes remain included:
- Synchronize the accounting-related parallel-helper assertion and visited-memory counters.
- Preserve syntax-selected stack positions for calls, constructors, property reads and async continuations, including live/captured stacks and cache replay; thanks @robobun for the upstream source-position work.

Qualification includes all ten artifact builds, Linux JSC module/promise/namespace/Segmenter and allocation-sampling regressions, the W113 paired Bun selection, a build of the exact prepared upstream-synced Bun runtime with its required namespace API adapter and candidate manifest with startup and unchanged upstream memory-release tests, and a separate build with feature adapters for the complete fork selection and patch regressions. Syntax-selected stack positions are checked across execution tiers, live/captured stacks, and cache replay. Other targets have build/provenance proof; native runtime qualification covers Linux x64 and Windows ARM64 JSC startup, DFG JIT and FFI probes. Bun consumer qualification is separate.
Qualification includes all ten artifact builds, Linux JSC module/promise/namespace/Segmenter and allocation-sampling regressions, the paired Bun selection, a build of the exact prepared upstream-synced Bun runtime with its required namespace API adapter and candidate manifest with startup and unchanged upstream memory-release tests, and a separate build with feature adapters for the complete fork selection and patch regressions. Syntax-selected stack positions are checked across execution tiers, live/captured stacks, and cache replay. Other targets have build/provenance proof; native runtime qualification additionally covers macOS and Linux ARM64 assembler/JSC regressions, and Windows ARM64 JSC startup, DFG JIT, FFI and five ArrayBuffer accounting modes. Bun consumer qualification is separate.

The new engine requires Bun's updated mimalloc idle hook and matching embedding adapters. A manifest-only update of the original sync tree does not compile: its old namespace marker access must be replaced by the namespace adapter (Bun #106), paired atomically with this engine. Consumers must rebuild with the manifest's exact headers and libraries; an older executable is not interchangeable.

Expand Down
3 changes: 2 additions & 1 deletion .github/openclaw/release.py
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,8 @@ def assemble(inputs,out,source):
if (windows.get('passed') is not True or windows.get('source')!=source
or windows.get('lane')!='bun-webkit-windows-arm64' or windows.get('architecture')!='arm64'
or windows.get('sha256')!=artifacts['bun-webkit-windows-arm64.tar.gz']['sha256']
or any(windows.get(k) is not True for k in ('startup','dfg','ffi'))):
or any(windows.get(k) is not True for k in ('startup','dfg','ffi','arraybuffer_accounting'))
or windows.get('arraybuffer_modes')!=5):
raise ValueError('missing or mismatched native Windows ARM64 qualification')
gate['windows_arm64']=windows
# Retain low-level provenance and test logs as a separate archive.
Expand Down
4 changes: 2 additions & 2 deletions .github/openclaw/test_release.py
Original file line number Diff line number Diff line change
Expand Up @@ -24,10 +24,10 @@ def setUp(self):
(d/(label+'.manifest.json')).write_text(json.dumps({'source':SOURCE,'lane':{'label':label},'files':{f.name:{'sha256':release.sha(f),'size':f.stat().st_size}}}))
w=self.inputs/'windows-arm64-qualification';w.mkdir()
archive=self.inputs/'bun-webkit-windows-arm64/bun-webkit-windows-arm64.tar.gz'
(w/'gate.json').write_text(json.dumps({'passed':True,'source':SOURCE,'lane':'bun-webkit-windows-arm64','architecture':'arm64','sha256':release.sha(archive),'startup':True,'dfg':True,'ffi':True}))
(w/'gate.json').write_text(json.dumps({'passed':True,'source':SOURCE,'lane':'bun-webkit-windows-arm64','architecture':'arm64','sha256':release.sha(archive),'startup':True,'dfg':True,'ffi':True,'arraybuffer_accounting':True,'arraybuffer_modes':5}))
def test_windows_gate_rejects_wrong_source_digest_or_failed_probe(self):
p=self.inputs/'windows-arm64-qualification/gate.json';original=json.loads(p.read_text())
for key,value in [('source','b'*40),('sha256','0'*64),('architecture','x64'),('startup',False),('dfg',False),('ffi',False)]:
for key,value in [('source','b'*40),('sha256','0'*64),('architecture','x64'),('startup',False),('dfg',False),('ffi',False),('arraybuffer_accounting',False),('arraybuffer_accounting',None),('arraybuffer_modes',4),('arraybuffer_modes',None)]:
with self.subTest(key=key):
p.write_text(json.dumps({**original,key:value}))
with self.assertRaisesRegex(ValueError,'native Windows ARM64'):self.assemble()
Expand Down
64 changes: 64 additions & 0 deletions .github/workflows/openclaw-arm64.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
name: OpenClaw ARM64 regressions
on:
pull_request:
branches: [openclaw/main, openclaw/release-5718a6ec]
workflow_dispatch:
permissions:
contents: read
concurrency:
group: openclaw-arm64-${{ github.ref }}
cancel-in-progress: true
jobs:
linux-arm64:
if: github.repository == 'openclaw/WebKit'
runs-on: ubuntu-24.04-arm
timeout-minutes: 150
steps:
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1
with:
ref: ${{ github.event.pull_request.head.sha || github.sha }}
persist-credentials: false
sparse-checkout-cone-mode: false
sparse-checkout: |
/*
!/LayoutTests/
!/JSTests/*
!/PerformanceTests/
!/Websites/
!/ManualTests/
!/WebDriverTests/
/JSTests/stress/
/JSTests/modules/
/JSTests/*.yaml
/JSTests/BUN-TEST-DIFFERENCES.md
- name: Install native build and regression tools
run: |
curl -fsSL --retry 5 https://apt.llvm.org/llvm-snapshot.gpg.key | sudo tee /etc/apt/trusted.gpg.d/apt.llvm.org.asc > /dev/null
echo 'deb https://apt.llvm.org/noble/ llvm-toolchain-noble-23 main' | sudo tee /etc/apt/sources.list.d/llvm.list
sudo apt-get update -qq
sudo apt-get install -y --no-install-recommends cmake ninja-build clang-23 lld-23 libicu-dev ruby ruby-highline libjson-pp-perl
- name: Build native ARM64 JSC and assembler tests
run: |
test "$(uname -m)" = aarch64
cmake -S . -B "$RUNNER_TEMP/jsc-build" -G Ninja \
-DPORT=JSCOnly -DCMAKE_BUILD_TYPE=Release -DDEVELOPER_MODE=ON -DCMAKE_COMPILE_WARNING_AS_ERROR=OFF \
-DENABLE_STATIC_JSC=ON -DUSE_BUN_JSC_ADDITIONS=ON -DUSE_BUN_EVENT_LOOP=ON \
-DUSE_MIMALLOC=ON -DUSE_EXTERNAL_MIMALLOC=OFF -DENABLE_FTL_JIT=ON \
-DENABLE_BUN_SKIP_FAILING_ASSERTIONS=ON -DALLOW_LINE_AND_COLUMN_NUMBER_IN_BUILTINS=ON \
-DENABLE_REMOTE_INSPECTOR=ON -DCMAKE_C_COMPILER=clang-23 -DCMAKE_CXX_COMPILER=clang++-23
cmake --build "$RUNNER_TEMP/jsc-build" --target jsc testmasm testFFI -j4
- name: Qualify assembler and all JIT tiers
env:
ENGINE_BUILD_DIR: ${{ runner.temp }}/jsc-build
QUALIFICATION_DIR: ${{ runner.temp }}/arm64-proof
run: bash .github/openclaw/qualify-arm64.sh
- uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a
if: always()
with:
name: linux-arm64-proof
path: |
${{ runner.temp }}/arm64-proof/*.json
${{ runner.temp }}/arm64-proof/*.log
${{ runner.temp }}/arm64-proof/jsc-*/results
if-no-files-found: warn
retention-days: 7
9 changes: 7 additions & 2 deletions .github/workflows/openclaw-artifacts.yml
Original file line number Diff line number Diff line change
Expand Up @@ -164,7 +164,9 @@ jobs:
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1
with:
persist-credentials: false
sparse-checkout: .github
sparse-checkout: |
.github
JSTests/stress
- uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c
with:
name: bun-webkit-windows-arm64
Expand All @@ -173,9 +175,12 @@ jobs:
shell: pwsh
run: ./.github/openclaw/qualify-windows-arm64.ps1 -ArtifactDirectory "$env:RUNNER_TEMP/windows-artifact" -OutputDirectory "$env:RUNNER_TEMP/windows-proof" -Source $env:GITHUB_SHA
- uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a
if: always()
with:
name: windows-arm64-qualification
path: ${{ runner.temp }}/windows-proof/gate.json
path: |
${{ runner.temp }}/windows-proof/gate.json
${{ runner.temp }}/windows-proof/accounting-*.log
if-no-files-found: error
retention-days: 7
assemble:
Expand Down
9 changes: 7 additions & 2 deletions .github/workflows/openclaw-checks.yml
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,9 @@ jobs:
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1
with:
persist-credentials: false
sparse-checkout: .github
sparse-checkout: |
.github
JSTests/stress
- uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c
with:
name: bun-webkit-windows-arm64
Expand All @@ -58,9 +60,12 @@ jobs:
shell: pwsh
run: ./.github/openclaw/qualify-windows-arm64.ps1 -ArtifactDirectory "$env:RUNNER_TEMP/windows-artifact" -OutputDirectory "$env:RUNNER_TEMP/windows-proof" -Source $env:GITHUB_SHA
- uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a
if: always()
with:
name: windows-arm64-qualification
path: ${{ runner.temp }}/windows-proof/gate.json
path: |
${{ runner.temp }}/windows-proof/gate.json
${{ runner.temp }}/windows-proof/accounting-*.log
if-no-files-found: error
retention-days: 7
linux-native:
Expand Down
6 changes: 5 additions & 1 deletion OPENCLAW.md
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,7 @@ results, and checks Segmenter, four Proxy regressions, both 14-variant ALS hook
modes and at least 14 worker resource-limit cases. Other platforms are built;
this gate does not claim native runtime qualification on every architecture. A
Windows 11 ARM64 job verifies archive provenance and native ARM64 PE identity,
then runs JSC startup, DFG JIT and testFFI probes. Assembly and publication require
then runs JSC startup, DFG JIT, testFFI and ArrayBuffer accounting in five execution modes. Assembly and publication require
its source- and archive-bound receipt.

## Publish and verify
Expand Down Expand Up @@ -150,6 +150,10 @@ baseline. Engine and Bun headers and libraries must be rebuilt together.

## Unreleased

## ARM64 arithmetic and Windows artifacts (2026-10-05)

- Fix ARM64 register-to-memory 64-bit addition so DFG typed-array allocations update their accounting counter without corrupting the source register; cover the operation in testmasm and qualify native ARM64 JSC across interpreter and JIT tiers.

- Add the upstream non-LTO Windows ARM64 archive and require native Windows 11 ARM64 startup, JIT and FFI qualification before publication.

## Repeated stack coordinates (2026-10-05)
Expand Down
2 changes: 1 addition & 1 deletion Source/JavaScriptCore/assembler/MacroAssemblerARM64.h
Original file line number Diff line number Diff line change
Expand Up @@ -453,7 +453,7 @@ class MacroAssemblerARM64 : public AbstractMacroAssembler<Assembler> {
void add64(RegisterID src, Address dest)
{
load64(dest, getCachedDataTempRegisterIDAndInvalidate());
m_assembler.add<64>(src, dataTempRegister, dataTempRegister);
m_assembler.add<64>(dataTempRegister, dataTempRegister, src);
store64(dataTempRegister, dest);
}

Expand Down
23 changes: 23 additions & 0 deletions Source/JavaScriptCore/assembler/testmasm.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1028,6 +1028,28 @@ void testAdd64Imm32()
}
}

void testAdd64RegisterAddress()
{
auto add = compile([] (CCallHelpers& jit) {
emitFunctionPrologue(jit);
jit.add64(GPRInfo::argumentGPR1, CCallHelpers::Address(GPRInfo::argumentGPR0, sizeof(uint64_t)));
jit.move(GPRInfo::argumentGPR1, GPRInfo::returnValueGPR);
emitFunctionEpilogue(jit);
jit.ret();
});

for (auto initial : int64Operands()) {
for (auto source : int64Operands()) {
uint64_t values[] = { 0x0123456789abcdefULL, static_cast<uint64_t>(initial), 0xfedcba9876543210ULL };
auto result = invoke<uint64_t>(add, values, static_cast<uint64_t>(source));
CHECK_EQ(values[1], static_cast<uint64_t>(initial) + static_cast<uint64_t>(source));
CHECK_EQ(result, static_cast<uint64_t>(source));
CHECK_EQ(values[0], 0x0123456789abcdefULL);
CHECK_EQ(values[2], 0xfedcba9876543210ULL);
}
}
}

void testAdd64ArgImm32()
{
for (auto immediate : int32Operands()) {
Expand Down Expand Up @@ -8534,6 +8556,7 @@ void run(const char* filter) WTF_IGNORES_THREAD_SAFETY_ANALYSIS
RUN(testAdd32Imm());
RUN(testAdd32ArgImm());
RUN(testAdd64Imm32());
RUN(testAdd64RegisterAddress());
RUN(testAdd64ArgImm32());
RUN(testAdd64Imm64());
RUN(testAdd64ArgImm64());
Expand Down
2 changes: 2 additions & 0 deletions jsc.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@

## Unreleased

- Fix ARM64 MacroAssembler register-to-memory `add64` operand order, restoring DFG typed-array allocation accounting and preserving the source register.

- Fix `Intl.Segmenter` containing lookups around surrogate pairs so segment boundaries and word status agree with iteration.
- Expose the require(esm) `__esModule` marker as an own enumerable namespace property while preserving unmarked imports and live bindings. Adapts [oven-sh/WebKit#279](https://github.com/oven-sh/WebKit/pull/279). Thanks @robobun!

Expand Down