From 9b4e44c56583c70c82189fd2e49b331054fe7110 Mon Sep 17 00:00:00 2001 From: Peter Steinberger Date: Mon, 5 Oct 2026 00:35:14 -0700 Subject: [PATCH 1/2] perf(jsc): carry stack coordinate cache into rebased release Port openclaw/WebKit#8 with identical engine and regression patch bytes. Compose only CI checkout-depth and release-documentation overlaps, retain all rebased qualification gates, and require the cache regression across execution and cache modes. --- .../openclaw/qualification/stack/check-jsc.sh | 4 +-- .github/openclaw/qualify-engine.sh | 2 +- .github/openclaw/release-notes.md | 6 ++++- .github/workflows/openclaw-checks.yml | 27 ++++++++++++++++++- .../stack-position-regressions-cache.js | 20 ++++++++++++++ OPENCLAW.md | 19 +++++++++++-- .../JavaScriptCore/parser/SourceProvider.cpp | 9 ++++--- Source/JavaScriptCore/parser/SourceProvider.h | 5 +++- 8 files changed, 81 insertions(+), 11 deletions(-) create mode 100644 JSTests/stress/stack-position-regressions-cache.js diff --git a/.github/openclaw/qualification/stack/check-jsc.sh b/.github/openclaw/qualification/stack/check-jsc.sh index 5a471af0bf5c7..38916e2c4b69e 100644 --- a/.github/openclaw/qualification/stack/check-jsc.sh +++ b/.github/openclaw/qualification/stack/check-jsc.sh @@ -10,7 +10,7 @@ for mode in default interpreter optimizer eager-ftl; do optimizer) args=(--useBytecodeOptimizer=true);; eager-ftl) args=(--useConcurrentJIT=false --thresholdForOptimizeAfterWarmUp=10 --thresholdForFTLOptimizeAfterWarmUp=20);; esac - for fixture in callsite-syntax-positions.js stack-position-regressions.js callsite-async-position.mjs; do + for fixture in callsite-syntax-positions.js stack-position-regressions.js stack-position-regressions-cache.js callsite-async-position.mjs; do module=(); case "$fixture" in *.mjs) module=(-m);; esac "$JSC" "${args[@]}" "${module[@]}" "JSTests/stress/$fixture" > "$RESULT_DIR/$mode-$fixture.log" 2>&1 done @@ -21,7 +21,7 @@ for kind in owned persistent; do args=(--diskCachePath="$cache") if test "$kind" = persistent; then args+=(--diskCachePayloadIsPersistentForTesting=true); fi for repeat in cold warm; do - for fixture in callsite-syntax-positions.js stack-position-regressions.js callsite-async-position.mjs; do + for fixture in callsite-syntax-positions.js stack-position-regressions.js stack-position-regressions-cache.js callsite-async-position.mjs; do module=(); case "$fixture" in *.mjs) module=(-m);; esac "$JSC" "${args[@]}" "${module[@]}" "JSTests/stress/$fixture" > "$RESULT_DIR/cache-$kind-$repeat-$fixture.log" 2>&1 done diff --git a/.github/openclaw/qualify-engine.sh b/.github/openclaw/qualify-engine.sh index 8f70de85ef506..f7caa584a068d 100644 --- a/.github/openclaw/qualify-engine.sh +++ b/.github/openclaw/qualify-engine.sh @@ -10,7 +10,7 @@ jsc="$QUALIFICATION_DIR/engine/bun-webkit/bin/jsc" Tools/Scripts/run-jsc-stress-tests JSTests/stress --jsc "$jsc" --child-processes 16 \ --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' \ --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 +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 --require stack-position-regressions-cache.js python3 .github/openclaw/check-stack-visitor.py "$jsc" > "$QUALIFICATION_DIR/stack-visitor.log" WEBKIT_CHECKOUT="$PWD" JSC="$jsc" RESULT_DIR="$QUALIFICATION_DIR/jsc-stack" bash .github/openclaw/qualification/stack/check-jsc.sh Tools/Scripts/run-jsc-stress-tests JSTests/modules.yaml --jsc "$jsc" --child-processes 16 \ diff --git a/.github/openclaw/release-notes.md b/.github/openclaw/release-notes.md index 5616a9cfa535e..1fb8b75ce88c9 100644 --- a/.github/openclaw/release-notes.md +++ b/.github/openclaw/release-notes.md @@ -1,4 +1,8 @@ -This release rebases the qualified OpenClaw engine patches onto upstream `5718a6ec579b98362ea7276a426deedcc6281ef5`. +This release adds engine PR #8's repeated stack-coordinate cache to the qualified OpenClaw engine on upstream `5718a6ec579b98362ea7276a426deedcc6281ef5`. + +- Cache requested provider-relative stack line and column conversions under the existing line-table lock, preserving document offsets, builtin overrides, and persistent bytecode format; thanks @steipete. Regressions cover repeated reads, independent providers, UTF-16 columns, and compressed line-table boundaries across interpreter/JIT tiers and cache modes. + +The previously released engine changes remain included: - Release free allocator memory from idle compiler threads and long Atomics waits; thanks @Jarred-Sumner. - Preserve Segmenter surrogate boundaries, opt-in Proxy global prototypes, module-loader AsyncLocalStorage context, and per-VM worker heap/stack budgets; thanks @steipete and @robobun. diff --git a/.github/workflows/openclaw-checks.yml b/.github/workflows/openclaw-checks.yml index 72d2a1b97eadf..fa01c1c169cc8 100644 --- a/.github/workflows/openclaw-checks.yml +++ b/.github/workflows/openclaw-checks.yml @@ -16,7 +16,8 @@ jobs: steps: - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 with: - fetch-depth: 64 + ref: ${{ github.event.pull_request.head.sha || github.sha }} + fetch-depth: 0 persist-credentials: false sparse-checkout-cone-mode: false sparse-checkout: | @@ -86,3 +87,27 @@ jobs: ${{ runner.temp }}/qualification/sync/runtime-cache/*.json if-no-files-found: error retention-days: 7 + macos-fork-artifact: + if: github.repository == 'openclaw/WebKit' + runs-on: blacksmith-32vcpu-ubuntu-2404 + timeout-minutes: 150 + steps: + - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 + with: + ref: ${{ github.event.pull_request.head.sha || github.sha }} + fetch-depth: 0 + persist-credentials: false + sparse-checkout-cone-mode: false + sparse-checkout: | + /* + !/LayoutTests/ + !/JSTests/* + !/PerformanceTests/ + !/Websites/ + !/ManualTests/ + !/WebDriverTests/ + - uses: actions/setup-node@820762786026740c76f36085b0efc47a31fe5020 + with: + node-version: '26.3.0' + - name: Build the macOS fork artifact on Linux without publishing + run: python3 .github/openclaw/artifacts.py build --lane bun-webkit-macos-arm64 --output "$RUNNER_TEMP/macos-fork-artifact" diff --git a/JSTests/stress/stack-position-regressions-cache.js b/JSTests/stress/stack-position-regressions-cache.js new file mode 100644 index 0000000000000..d9b9fd0375156 --- /dev/null +++ b/JSTests/stress/stack-position-regressions-cache.js @@ -0,0 +1,20 @@ +// Exercise cached offsets from independent providers, including equal offsets +// with different line tables, UTF-16 columns, and compressed-table boundaries. +const sources = []; +for (const terminator of ["\n", "\r\n", "\u2028", "\u2029"]) { + for (const lines of [0, 1, 63, 64, 65, 127]) { + const prefix = "/* 🦞 */ "; + const body = prefix + "(function positionCacheProbe() { return new Error('cache'); })"; + const source = terminator.repeat(lines) + body; + sources.push({ create: eval(source), line: lines + 1, column: body.indexOf("new Error") + 1 }); + } +} +for (let repeat = 0; repeat < Math.min(testLoopCount, 30); ++repeat) { + for (const { create, line, column } of sources) { + const error = create(); + // Anonymous eval frames omit their URL from stack strings, but expose + // the same computed coordinates through JSC's error properties. + if (error.line !== line || error.column !== column) + throw new Error(`Expected ${line}:${column}, got ${error.line}:${error.column}`); + } +} diff --git a/OPENCLAW.md b/OPENCLAW.md index f25f99405a4f1..cc67c916ef38a 100644 --- a/OPENCLAW.md +++ b/OPENCLAW.md @@ -54,8 +54,11 @@ Batch 1 is published from `openclaw/batch-1` so concurrently reviewed batch-2 engine changes can land on `openclaw/main` without entering this release. All publication branches require the same protected environment and full-SHA/green-job checks. -Pull requests run **OpenClaw engine checks**, a read-only Linux build using the same -lane recipe and Bun qualification gate. It also runs the ArrayBuffer accounting +Pull requests run **OpenClaw engine checks** at the exact PR head, with a native +Linux build using the same lane recipe and Bun qualification gate, plus the +macOS arm64 fork artifact cross-built on a separate Linux runner. Both jobs are +read-only; the macOS lane proves compilation, not native macOS execution. +The native job also runs the ArrayBuffer accounting and confirmed parked-waiter inspection regression with LLInt, DFG, FTL and concurrent GC. This workflow provides the pre-merge check; it cannot publish. @@ -145,6 +148,12 @@ baseline. Engine and Bun headers and libraries must be rebuilt together. No changes yet. +## Repeated stack coordinates (2026-10-05) + +- Cache source-relative stack line and column lookups on demand, including syntax-selected positions; carries engine PR #8 onto the rebased release line, thanks @steipete. +- Build both fork engine artifacts at the exact pull-request head before landing, with native Linux qualification and a Linux-hosted macOS arm64 compilation check. +- Require the repeated-coordinate regression in the full JSC stress matrix and direct execution/cache modes while retaining every rebased release gate. + ## Rebased engine batch - Keep syntax-selected stack positions separate from exception ranges for calls, constructors, property reads, and async continuations. Preserve the positions across bytecode rewriting, optimization, and cache replay. @@ -222,3 +231,9 @@ regressions. A combined regression inspects a worker during the new idle-memory purge and notification windows. VM inspection runs before the idle-purge branch, with the waiter-list lock dropped and VM heap access restored only for inspection; both paths recheck notification/termination before waiting again. + +The source provider caches only requested line/column conversions under its existing +line-table lock. Coordinates remain relative to the provider's text; document +offsets and builtin provider overrides retain their existing behavior. The cache +fills only when a reader requests a position, and the persistent bytecode-cache +format is unchanged. diff --git a/Source/JavaScriptCore/parser/SourceProvider.cpp b/Source/JavaScriptCore/parser/SourceProvider.cpp index bc5c9c6aa51d3..ddd3e0e3871c8 100644 --- a/Source/JavaScriptCore/parser/SourceProvider.cpp +++ b/Source/JavaScriptCore/parser/SourceProvider.cpp @@ -320,8 +320,12 @@ static unsigned lineEndFor(StringView text, const EncodedLineStarts& lineStarts, LineColumn LineStartTable::lineColumnForOffset(StringView text, unsigned offset) { Locker locker { m_lock }; - auto line = EncodedLineStarts { ensureBuilt(text).bytes }.lineContaining(offset); - return { line.line0Based, offset - line.start }; + // Syntax-selected stack positions bypass the per-instruction expression cache. + // Cache provider-relative coordinates so document offsets remain applied by the caller. + return m_cachedLineColumns.ensure(offset, [&] { + auto line = EncodedLineStarts { ensureBuilt(text).bytes }.lineContaining(offset); + return LineColumn { line.line0Based, offset - line.start }; + }).iterator->value; } LineStartTable::PositionInfo LineStartTable::positionInfoForOffset(StringView text, unsigned offset) @@ -365,4 +369,3 @@ LineColumn BuiltinsSourceProvider::lineColumnInTextForOffset(unsigned offset) } } // namespace JSC - diff --git a/Source/JavaScriptCore/parser/SourceProvider.h b/Source/JavaScriptCore/parser/SourceProvider.h index 423b7514e2f09..264633da658b3 100644 --- a/Source/JavaScriptCore/parser/SourceProvider.h +++ b/Source/JavaScriptCore/parser/SourceProvider.h @@ -40,6 +40,7 @@ WTF_ALLOW_UNSAFE_BUFFER_USAGE_BEGIN #include #include #include +#include #include #include #include @@ -125,6 +126,8 @@ class LineStartTable { mutable Lock m_lock; LineStarts m_lineStarts WTF_GUARDED_BY_LOCK(m_lock); + using LineColumnMap = UncheckedKeyHashMap, WTF::UnsignedWithZeroKeyHashTraits>; + LineColumnMap m_cachedLineColumns WTF_GUARDED_BY_LOCK(m_lock); }; class JS_EXPORT_PRIVATE SourceProvider : public ThreadSafeRefCounted { @@ -505,4 +508,4 @@ class SourceProviderBufferGuard { SourceProvider* m_sourceProvider { nullptr }; }; -} // namespace JSC \ No newline at end of file +} // namespace JSC From 9d96953151513e834a39477554b82158e470a161 Mon Sep 17 00:00:00 2001 From: Peter Steinberger Date: Mon, 5 Oct 2026 01:15:34 -0700 Subject: [PATCH 2/2] test(jsc): retain explicit stack execution tiers Include baseline-only, DFG-only and concurrent-GC stack runs from the original cache qualification, preserve the existing direct/cache modes, and cover the surrounding error-stack stress selection. Keep the engine port unchanged. --- .github/openclaw/qualification/stack/check-jsc.sh | 7 +++++-- .github/openclaw/qualify-engine.sh | 4 ++-- OPENCLAW.md | 2 +- 3 files changed, 8 insertions(+), 5 deletions(-) diff --git a/.github/openclaw/qualification/stack/check-jsc.sh b/.github/openclaw/qualification/stack/check-jsc.sh index 38916e2c4b69e..16dedfcb39dd1 100644 --- a/.github/openclaw/qualification/stack/check-jsc.sh +++ b/.github/openclaw/qualification/stack/check-jsc.sh @@ -3,12 +3,15 @@ set -euo pipefail : "${WEBKIT_CHECKOUT:?}" "${JSC:?}" "${RESULT_DIR:?}" mkdir -p "$RESULT_DIR" cd "$WEBKIT_CHECKOUT" -for mode in default interpreter optimizer eager-ftl; do +for mode in default interpreter baseline dfg optimizer eager-ftl concurrent; do args=() case "$mode" in interpreter) args=(--useJIT=false);; + baseline) args=(--useDFGJIT=false --useFTLJIT=false);; + dfg) args=(--useFTLJIT=false --useConcurrentJIT=false);; optimizer) args=(--useBytecodeOptimizer=true);; - eager-ftl) args=(--useConcurrentJIT=false --thresholdForOptimizeAfterWarmUp=10 --thresholdForFTLOptimizeAfterWarmUp=20);; + eager-ftl) args=(--useConcurrentJIT=false --thresholdForJITAfterWarmUp=10 --thresholdForOptimizeAfterWarmUp=10 --thresholdForFTLOptimizeAfterWarmUp=20);; + concurrent) args=(--gcAtEnd=true --useConcurrentGC=true);; esac for fixture in callsite-syntax-positions.js stack-position-regressions.js stack-position-regressions-cache.js callsite-async-position.mjs; do module=(); case "$fixture" in *.mjs) module=(-m);; esac diff --git a/.github/openclaw/qualify-engine.sh b/.github/openclaw/qualify-engine.sh index f7caa584a068d..5782a9d8245ef 100644 --- a/.github/openclaw/qualify-engine.sh +++ b/.github/openclaw/qualify-engine.sh @@ -8,7 +8,7 @@ tar -xzf "$ARTIFACT_DIR/bun-webkit-linux-amd64.tar.gz" -C "$QUALIFICATION_DIR/en jsc="$QUALIFICATION_DIR/engine/bun-webkit/bin/jsc" "$jsc" --useDollarVM=true -e 'if(typeof $vm.moduleNamespaceForRequire!=="function")throw Error("namespace hook absent")' Tools/Scripts/run-jsc-stress-tests JSTests/stress --jsc "$jsc" --child-processes 16 \ - --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' \ + --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 --require stack-position-regressions-cache.js python3 .github/openclaw/check-stack-visitor.py "$jsc" > "$QUALIFICATION_DIR/stack-visitor.log" @@ -38,5 +38,5 @@ for name,minimum in [('jsc-stress',1400),('jsc-modules',1600)]: assert len(lines)>=minimum,(name,len(lines)) assert all(line.endswith(': PASS') for line in lines),name+' failures' counts[name]=len(lines) -(root/'engine-gate.json').write_text(json.dumps({'passed':True,'source':sys.argv[2],'stress_counts':counts,'sampling_tiers':4,'arraybuffer_execution_modes':5,'stack_execution_modes':4,'stack_cache':json.loads((root/'jsc-stack/cache-files.json').read_text())},indent=2)+'\n') +(root/'engine-gate.json').write_text(json.dumps({'passed':True,'source':sys.argv[2],'stress_counts':counts,'sampling_tiers':4,'arraybuffer_execution_modes':5,'stack_execution_modes':7,'stack_cache':json.loads((root/'jsc-stack/cache-files.json').read_text())},indent=2)+'\n') PY diff --git a/OPENCLAW.md b/OPENCLAW.md index cc67c916ef38a..dec1e15a9e551 100644 --- a/OPENCLAW.md +++ b/OPENCLAW.md @@ -152,7 +152,7 @@ No changes yet. - Cache source-relative stack line and column lookups on demand, including syntax-selected positions; carries engine PR #8 onto the rebased release line, thanks @steipete. - Build both fork engine artifacts at the exact pull-request head before landing, with native Linux qualification and a Linux-hosted macOS arm64 compilation check. -- Require the repeated-coordinate regression in the full JSC stress matrix and direct execution/cache modes while retaining every rebased release gate. +- Require the repeated-coordinate regression and surrounding error-stack stress cases, with seven direct stack execution modes including interpreter, baseline-only, DFG-only, eager FTL and concurrent GC, while retaining every rebased release gate. ## Rebased engine batch