Skip to content

Let the grace passes mark objects the resolve guard was discarding (issue 5425) - #5477

Open
shai-almog wants to merge 3 commits into
masterfrom
fix-5425-late-grace-remark
Open

Let the grace passes mark objects the resolve guard was discarding (issue 5425)#5477
shai-almog wants to merge 3 commits into
masterfrom
fix-5425-late-grace-remark

Conversation

@shai-almog

@shai-almog shai-almog commented Jul 27, 2026

Copy link
Copy Markdown
Collaborator

Fixes #5425.

The failure

The reporter's dictionary load corrupts the heap on iOS: garbled words, an invalid block type from the inflater, an impossible NPE. Reproduced by running his Dtest.java classes verbatim over the dictionary.res attached to the issue, built through the ParparVM clean target, with a second thread allocating alongside the loader. Under -DCN1_GC_VERIFY the unfixed VM aborts every run with thousands of dangling references; without the verifier, SIGSEGV inside Hashtable.rehash / get.

Root cause — one guard, applied wider than its purpose

Per-object mark history named the holder of every dangling reference: a fresh legacy object stamped live by codenameOneGCSweep's grace branch with lastTraced == 0 — never traced in its life. The cause:

if(cn1ConservativeResolve((void*)obj) != obj && !cn1GcImmortalObjContains(obj))
    return;      // gcMarkObject refuses to mark

That guard exists to reject conservatively derived pointers — arbitrary machine words off a native stack that may not be objects at all, where dereferencing would crash. But it rejects every pointer it cannot resolve, and resolution is against a page/extent snapshot built at mark start — so an object allocated later can never resolve.

When the mid-mark grace passes (#5442 BiBOP, #5471 legacy) walk allObjectsInHeap and the page registry and hand such an object over, the mark is silently discarded. The sweep then grace-promotes it (mark == -1currentGcMarkValue) without tracing, in the same index-ordered walk that frees dead objects — so an older object reachable only through it is reclaimed at a lower index. Hashtable.rehash produces exactly this shape: a fresh Entry[] holding the only reference to older entries.

The fix

Those two walks hold authoritative references — they read the object out of a registry, not off a stack — so they need no proof. cn1GcTrustedRoots marks that window and gcMarkObject skips the guard for it. The existing passes then mark what they were already trying to mark.

Nothing new runs. A pass that was throwing its results away stops doing so.

It also corrects the comments asserting a fresh object is safe because grace keeps it. Grace keeps the object; it does not trace the subtree. That premise appears at the guard, at the snapshot-rebuild note and at the sweep's promotion site, and is what this defect was hiding behind.

⚠️ Do not set cn1GcTrustedRoots around anything marking from a stack scan or register file — that is the case the guard exists for.

Validation

Reproducer (his code + data) 3/3 clean, 166 collection cycles, 800M verified references — baseline fails 100% with ~7000 violations
run-gc-verify.sh GREEN 9/9 drivers, both fault self-tests firing
Geomean 1.22x vs 1.21x on master — checksums bit-identical to the host JVM
objectAllocation 3.85x vs 3.46x on master, within run variance

History of this branch

An earlier revision added a late-grace re-mark pass to compensate for the guard rather than fix it. It was correct but cost 2.1x on objectAllocation and +8% geomean, and review found two further defects in it: a suffix scan unsound against placeObjectInHeapCollection's tombstone reuse, and an O(heap) pass widening the unbarriered mark-to-sweep window. Both are moot now — there is no extra pass. Thanks to both reviewers; the slot-reuse catch is what forced the full-table scan that exposed the cost that sent me back to the guard.

Caveats

No regression test. Eight attempts at a synthetic gate driver, none reproduced (best: 4 verify passes / 138M refs / 0 violations with the fix off, against 40–56 passes on the real workload). Deleted rather than shipped, per this repo's own standard that a gate nobody has watched fail is not a gate. The working reproducer is the reporter's Dtest.java plus his dictionary.res, two mutator threads, no pacing. Pinning this down deserves a follow-up.

macOS arm64 desktop only. Nothing verified on device, which is where the reported symptom lives. Worth having the reporter build this branch.

Separate issues surfaced during the investigation

  • gcMarkResolveThreadCount hardcodes int n = 1, forcing serial marking, with a comment admitting a second parked ordering hole.
  • SATB termination breaks on "a batch marked nothing new", which is not "the log is empty". A real leak — 7445 → ~40 violations when closed — measured 59,691–526,560 entries still queued at snapshot close.
  • cn1BibopPacingCap sizes backpressure off machine RAM, so peak RSS tracks allocation rate rather than live set: 34 GB against a ~150 MB live set. On a device that is a jetsam, and a plausible part of the reporter's "IOS reports the app crashed".
  • cn1FusedInstallPrimArray creates permanently unregistered, never-marked objects — safe today only because they are leaves.

🤖 Generated with Claude Code

Copilot AI review requested due to automatic review settings July 27, 2026 11:52

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 7686f97bbf

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread vm/ByteCodeTranslator/src/cn1_globals.m Outdated
Comment thread vm/ByteCodeTranslator/src/cn1_globals.m Outdated

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR addresses ParparVM/iOS heap corruption in the concurrent GC by adding a “late-grace re-mark” at the end of codenameOneGCMark() to ensure fresh objects that would otherwise be sweep grace-promoted are traced (so their subtrees aren’t reclaimed). It also makes conservative-root snapshot invalidation available outside CN1_GC_VERIFY and updates comments to reflect the corrected safety model around grace vs subtree tracing.

Changes:

  • Add a late-grace re-mark pass that refreshes conservative snapshots and traces still-fresh legacy + BiBOP objects before sweep.
  • Make cn1ConsSnapEpochReset() available outside CN1_GC_VERIFY so snapshot rebuilds aren’t silently epoch-gated away.
  • Clarify/update comments around sweep grace-promotion and gcMarkObject()’s conservative resolve guard behavior.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread vm/ByteCodeTranslator/src/cn1_globals.m Outdated
Comment thread vm/ByteCodeTranslator/src/cn1_globals.m Outdated
@github-actions

github-actions Bot commented Jul 27, 2026

Copy link
Copy Markdown
Contributor

✅ ByteCodeTranslator Quality Report

Test & Coverage

  • Tests: 411 total, 0 failed, 14 skipped

Benchmark Results

  • Execution Time: 20094 ms

  • Hotspots (Top 20 sampled methods):

    • 14.24% java.util.ArrayList.indexOf (238 samples)
    • 10.17% com.codename1.tools.translator.Parser.addToConstantPool (170 samples)
    • 3.95% com.codename1.tools.translator.ByteCodeClass.hasDeclaredMethod (66 samples)
    • 3.89% com.codename1.tools.translator.Parser.cn1EnsureSubclassIndex (65 samples)
    • 3.65% java.lang.StringBuilder.append (61 samples)
    • 3.17% com.codename1.tools.translator.BytecodeMethod.equals (53 samples)
    • 2.33% org.objectweb.asm.tree.analysis.Analyzer.findSubroutine (39 samples)
    • 1.97% com.codename1.tools.translator.BytecodeMethod.optimize (33 samples)
    • 1.92% com.codename1.tools.translator.Parser.classIndex (32 samples)
    • 1.86% org.objectweb.asm.tree.analysis.Analyzer.analyze (31 samples)
    • 1.74% com.codename1.tools.translator.bytecodes.Invoke.resolveDirectTarget (29 samples)
    • 1.68% java.lang.String.equals (28 samples)
    • 1.44% java.util.HashMap.hash (24 samples)
    • 1.38% java.lang.Object.hashCode (23 samples)
    • 1.26% com.codename1.tools.translator.BytecodeMethod.appendCMethodPrefix (21 samples)
    • 1.20% java.lang.System.identityHashCode (20 samples)
    • 1.20% java.io.UnixFileSystem.getBooleanAttributes0 (20 samples)
    • 1.08% com.codename1.tools.translator.bytecodes.Invoke.addDependencies (18 samples)
    • 1.02% org.objectweb.asm.ClassReader.readCode (17 samples)
    • 0.96% com.codename1.tools.translator.bytecodes.Invoke.findMethodUp (16 samples)
  • ⚠️ Coverage report not generated.

Static Analysis

  • ✅ SpotBugs: no findings (report was not generated by the build).
  • ⚠️ PMD report not generated.
  • ⚠️ Checkstyle report not generated.

Generated automatically by the PR CI workflow.

@github-actions

Copy link
Copy Markdown
Contributor

Cloudflare Preview

@github-actions

github-actions Bot commented Jul 27, 2026

Copy link
Copy Markdown
Contributor

✅ Continuous Quality Report

Test & Coverage

Static Analysis

  • SpotBugs [Report archive]
    • ByteCodeTranslator: 0 findings (no issues)
    • android: 0 findings (no issues)
    • codenameone-maven-plugin: 0 findings (no issues)
    • core-unittests: 0 findings (no issues)
    • ios: 0 findings (no issues)
  • PMD: 0 findings (no issues) [Report archive]
  • Checkstyle: 0 findings (no issues) [Report archive]

Generated automatically by the PR CI workflow.

@shai-almog

shai-almog commented Jul 27, 2026

Copy link
Copy Markdown
Collaborator Author

Compared 146 screenshots: 146 matched.
Native Windows port, REAL shipping pipeline: the hellocodenameone screenshot suite rendered by a binary CROSS-COMPILED on Linux (clang-cl + xwin, WebView2 linked) and RUN on a Windows x64 runner. Compared against the in-repo baseline in scripts/windows/screenshots.

Benchmark Results

Detailed Performance Metrics

Metric Duration
SIMD kernel backend SSE2 (x64) / NEON (arm64) native kernels
SIMD int-add (64K x300) java 63ms / native 4ms = 15.7x speedup
SIMD float-mul (64K x300) java 84ms / native 4ms = 21.0x speedup
SIMD kernel correctness PASS (native result == scalar reference)
Base64 native bridge unavailable (CN1 + SIMD + image benchmarks only)
Base64 payload size 8192 bytes
Base64 benchmark iterations 6000
Base64 SIMD byte path gated to scalar (CPU autovectorizes scalar; explicit SIMD not beneficial here)
Base64 CN1 encode 192.000 ms
Base64 CN1 decode 125.000 ms
Base64 SIMD encode 101.000 ms
Base64 encode ratio (SIMD/CN1) 0.526x (47.4% faster)
Base64 SIMD decode 94.000 ms
Base64 decode ratio (SIMD/CN1) 0.752x (24.8% faster)
Image encode benchmark iterations 100
Image createMask (SIMD off) 21.000 ms
Image createMask (SIMD on) 18.000 ms
Image createMask ratio (SIMD on/off) 0.857x (14.3% faster)
Image applyMask (SIMD off) 53.000 ms
Image applyMask (SIMD on) 47.000 ms
Image applyMask ratio (SIMD on/off) 0.887x (11.3% faster)
Image modifyAlpha (SIMD off) 48.000 ms
Image modifyAlpha (SIMD on) 47.000 ms
Image modifyAlpha ratio (SIMD on/off) 0.979x (2.1% faster)
Image modifyAlpha removeColor (SIMD off) 132.000 ms
Image modifyAlpha removeColor (SIMD on) 28.000 ms
Image modifyAlpha removeColor ratio (SIMD on/off) 0.212x (78.8% faster)

@shai-almog

shai-almog commented Jul 27, 2026

Copy link
Copy Markdown
Collaborator Author

Compared 146 screenshots: 146 matched.
Native Windows port (x64 / Intel-AMD): full hellocodenameone screenshot suite rendered offscreen with Direct2D/DirectWrite, plus the real benchmarks (base64 native/CN1/SIMD, image createMask/applyMask/modifyAlpha/PNG/JPEG, SSE2 SIMD kernels). Compared against the in-repo baseline in scripts/windows/screenshots.

Benchmark Results

Detailed Performance Metrics

Metric Duration
SIMD kernel backend SSE2 (x64) / NEON (arm64) native kernels
SIMD int-add (64K x300) java 64ms / native 5ms = 12.8x speedup
SIMD float-mul (64K x300) java 64ms / native 5ms = 12.8x speedup
SIMD kernel correctness PASS (native result == scalar reference)
Base64 native bridge unavailable (CN1 + SIMD + image benchmarks only)
Base64 payload size 8192 bytes
Base64 benchmark iterations 6000
Base64 SIMD byte path gated to scalar (CPU autovectorizes scalar; explicit SIMD not beneficial here)
Base64 CN1 encode 197.000 ms
Base64 CN1 decode 124.000 ms
Base64 SIMD encode 99.000 ms
Base64 encode ratio (SIMD/CN1) 0.503x (49.7% faster)
Base64 SIMD decode 88.000 ms
Base64 decode ratio (SIMD/CN1) 0.710x (29.0% faster)
Image encode benchmark iterations 100
Image createMask (SIMD off) 27.000 ms
Image createMask (SIMD on) 25.000 ms
Image createMask ratio (SIMD on/off) 0.926x (7.4% faster)
Image applyMask (SIMD off) 169.000 ms
Image applyMask (SIMD on) 77.000 ms
Image applyMask ratio (SIMD on/off) 0.456x (54.4% faster)
Image modifyAlpha (SIMD off) 68.000 ms
Image modifyAlpha (SIMD on) 68.000 ms
Image modifyAlpha ratio (SIMD on/off) 1.000x (0.0% slower)
Image modifyAlpha removeColor (SIMD off) 75.000 ms
Image modifyAlpha removeColor (SIMD on) 69.000 ms
Image modifyAlpha removeColor ratio (SIMD on/off) 0.920x (8.0% faster)

@shai-almog

shai-almog commented Jul 27, 2026

Copy link
Copy Markdown
Collaborator Author

Compared 147 screenshots: 147 matched.
Native Linux port (x64), GTK3/Cairo/Pango, ParparVM bytecode-to-C (no JVM): the hellocodenameone screenshot suite rendered by a native ELF built + run on the GitHub x64 runner. Baseline: scripts/linux/screenshots.

@shai-almog

shai-almog commented Jul 27, 2026

Copy link
Copy Markdown
Collaborator Author

Compared 147 screenshots: 147 matched.
Native Linux port (arm64), GTK3/Cairo/Pango, ParparVM bytecode-to-C (no JVM): the hellocodenameone screenshot suite rendered by a native ELF built + run on the GitHub arm64 runner. Baseline: scripts/linux/screenshots-arm.

@shai-almog

shai-almog commented Jul 27, 2026

Copy link
Copy Markdown
Collaborator Author

Compared 146 screenshots: 146 matched.
Native Windows port (arm64 / Apple Silicon - Arm): full hellocodenameone screenshot suite rendered offscreen with Direct2D/DirectWrite, plus the real benchmarks (base64 native/CN1/SIMD, image createMask/applyMask/modifyAlpha/PNG/JPEG, NEON SIMD kernels). Compared against the in-repo baseline in scripts/windows/screenshots.

Benchmark Results

Detailed Performance Metrics

Metric Duration
SIMD kernel backend SSE2 (x64) / NEON (arm64) native kernels
SIMD int-add (64K x300) java 53ms / native 3ms = 17.6x speedup
SIMD float-mul (64K x300) java 55ms / native 3ms = 18.3x speedup
SIMD kernel correctness PASS (native result == scalar reference)
Base64 native bridge unavailable (CN1 + SIMD + image benchmarks only)
Base64 payload size 8192 bytes
Base64 benchmark iterations 6000
Base64 SIMD byte path gated to scalar (CPU autovectorizes scalar; explicit SIMD not beneficial here)
Base64 CN1 encode 244.000 ms
Base64 CN1 decode 127.000 ms
Base64 SIMD encode 63.000 ms
Base64 encode ratio (SIMD/CN1) 0.258x (74.2% faster)
Base64 SIMD decode 58.000 ms
Base64 decode ratio (SIMD/CN1) 0.457x (54.3% faster)
Image encode benchmark iterations 100
Image createMask (SIMD off) 12.000 ms
Image createMask (SIMD on) 8.000 ms
Image createMask ratio (SIMD on/off) 0.667x (33.3% faster)
Image applyMask (SIMD off) 24.000 ms
Image applyMask (SIMD on) 18.000 ms
Image applyMask ratio (SIMD on/off) 0.750x (25.0% faster)
Image modifyAlpha (SIMD off) 15.000 ms
Image modifyAlpha (SIMD on) 13.000 ms
Image modifyAlpha ratio (SIMD on/off) 0.867x (13.3% faster)
Image modifyAlpha removeColor (SIMD off) 18.000 ms
Image modifyAlpha removeColor (SIMD on) 12.000 ms
Image modifyAlpha removeColor ratio (SIMD on/off) 0.667x (33.3% faster)

@shai-almog

shai-almog commented Jul 27, 2026

Copy link
Copy Markdown
Collaborator Author

Compared 181 screenshots: 181 matched.
✅ JavaScript-port screenshot tests passed.

@shai-almog

shai-almog commented Jul 27, 2026

Copy link
Copy Markdown
Collaborator Author

Compared 217 screenshots: 217 matched.
✅ Native Apple Watch (watchOS, Core Graphics) screenshot tests passed.

@shai-almog

shai-almog commented Jul 27, 2026

Copy link
Copy Markdown
Collaborator Author

Compared 148 screenshots: 148 matched.
✅ Native Mac screenshot tests passed.

Benchmark Results

  • VM Translation Time: 0 seconds
  • Compilation Time: 266 seconds

Detailed Performance Metrics

Metric Duration
SIMD kernel backend SSE2 (x64) / NEON (arm64) native kernels
SIMD int-add (64K x300) java 93ms / native 6ms = 15.5x speedup
SIMD float-mul (64K x300) java 339ms / native 5ms = 67.8x speedup
SIMD kernel correctness PASS (native result == scalar reference)
Base64 payload size 8192 bytes
Base64 benchmark iterations 6000
Base64 SIMD byte path active (NEON-accelerated)
Base64 CN1 encode 153.000 ms
Base64 CN1 decode 125.000 ms
Base64 native encode 561.000 ms
Base64 encode ratio (CN1/native) 0.273x (72.7% faster)
Base64 native decode 292.000 ms
Base64 decode ratio (CN1/native) 0.428x (57.2% faster)
Base64 SIMD encode 50.000 ms
Base64 encode ratio (SIMD/CN1) 0.327x (67.3% faster)
Base64 SIMD decode 48.000 ms
Base64 decode ratio (SIMD/CN1) 0.384x (61.6% faster)
Base64 encode ratio (SIMD/native) 0.089x (91.1% faster)
Base64 decode ratio (SIMD/native) 0.164x (83.6% faster)
Image encode benchmark iterations 100
Image createMask (SIMD off) 12.000 ms
Image createMask (SIMD on) 6.000 ms
Image createMask ratio (SIMD on/off) 0.500x (50.0% faster)
Image applyMask (SIMD off) 72.000 ms
Image applyMask (SIMD on) 53.000 ms
Image applyMask ratio (SIMD on/off) 0.736x (26.4% faster)
Image modifyAlpha (SIMD off) 49.000 ms
Image modifyAlpha (SIMD on) 45.000 ms
Image modifyAlpha ratio (SIMD on/off) 0.918x (8.2% faster)
Image modifyAlpha removeColor (SIMD off) 57.000 ms
Image modifyAlpha removeColor (SIMD on) 43.000 ms
Image modifyAlpha removeColor ratio (SIMD on/off) 0.754x (24.6% faster)

Copilot AI review requested due to automatic review settings July 27, 2026 13:43

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 1 out of 1 changed files in this pull request and generated 2 comments.

Comment thread vm/ByteCodeTranslator/src/cn1_globals.m Outdated
Comment thread vm/ByteCodeTranslator/src/cn1_globals.m Outdated
@shai-almog

shai-almog commented Jul 27, 2026

Copy link
Copy Markdown
Collaborator Author

Compared 149 screenshots: 149 matched.
✅ Native iOS Metal screenshot tests passed.

Benchmark Results

  • VM Translation Time: 0 seconds
  • Compilation Time: 574 seconds

Build and Run Timing

Metric Duration
Simulator Boot 94000 ms
Simulator Boot (Run) 0 ms
App Install 13000 ms
App Launch 4000 ms
Test Execution 907000 ms

Detailed Performance Metrics

Metric Duration
SIMD kernel backend SSE2 (x64) / NEON (arm64) native kernels
SIMD int-add (64K x300) java 57ms / native 3ms = 19.0x speedup
SIMD float-mul (64K x300) java 72ms / native 3ms = 24.0x speedup
SIMD kernel correctness PASS (native result == scalar reference)
Base64 payload size 8192 bytes
Base64 benchmark iterations 6000
Base64 SIMD byte path active (NEON-accelerated)
Base64 CN1 encode 295.000 ms
Base64 CN1 decode 143.000 ms
Base64 native encode 329.000 ms
Base64 encode ratio (CN1/native) 0.897x (10.3% faster)
Base64 native decode 447.000 ms
Base64 decode ratio (CN1/native) 0.320x (68.0% faster)
Base64 SIMD encode 108.000 ms
Base64 encode ratio (SIMD/CN1) 0.366x (63.4% faster)
Base64 SIMD decode 61.000 ms
Base64 decode ratio (SIMD/CN1) 0.427x (57.3% faster)
Base64 encode ratio (SIMD/native) 0.328x (67.2% faster)
Base64 decode ratio (SIMD/native) 0.136x (86.4% faster)
Image encode benchmark iterations 100
Image createMask (SIMD off) 17.000 ms
Image createMask (SIMD on) 2.000 ms
Image createMask ratio (SIMD on/off) 0.118x (88.2% faster)
Image applyMask (SIMD off) 61.000 ms
Image applyMask (SIMD on) 47.000 ms
Image applyMask ratio (SIMD on/off) 0.770x (23.0% faster)
Image modifyAlpha (SIMD off) 40.000 ms
Image modifyAlpha (SIMD on) 34.000 ms
Image modifyAlpha ratio (SIMD on/off) 0.850x (15.0% faster)
Image modifyAlpha removeColor (SIMD off) 84.000 ms
Image modifyAlpha removeColor (SIMD on) 53.000 ms
Image modifyAlpha removeColor ratio (SIMD on/off) 0.631x (36.9% faster)

@shai-almog

shai-almog commented Jul 27, 2026

Copy link
Copy Markdown
Collaborator Author

Compared 144 screenshots: 144 matched.
✅ Native Apple TV (tvOS, Metal) screenshot tests passed.

…ssue 5425)

The reporter's dictionary load corrupts the heap on iOS: garbled words, an
"invalid block type" from the inflater, an impossible NPE. Reproduced by running
his Dtest.java classes verbatim over the dictionary.res attached to the issue,
with a second thread allocating alongside the loader. Under -DCN1_GC_VERIFY the
unfixed VM aborts every run with thousands of dangling references; without it,
SIGSEGV inside Hashtable.rehash / get.

Per-object mark history named the holder of every dangling reference: a fresh
legacy object stamped live by codenameOneGCSweep's grace branch with
lastTraced == 0 -- never traced in its life. The cause is one guard:

    if(cn1ConservativeResolve((void*)obj) != obj && !cn1GcImmortalObjContains(obj))
        return;

It exists to reject CONSERVATIVELY DERIVED pointers -- arbitrary machine words
off a native stack that may not be objects at all. But it rejects every pointer
it cannot resolve, and resolution is against a page/extent snapshot built at MARK
START, so an object allocated later can never resolve. When the mid-mark grace
passes walk allObjectsInHeap and the page registry and hand such an object over,
the mark is silently discarded. The sweep then grace-promotes it (mark == -1 ->
currentGcMarkValue) WITHOUT tracing, in the same index-ordered walk that frees
dead objects, so an older object reachable only through it is reclaimed at a
lower index.

Those two walks hold AUTHORITATIVE references: they read the object out of a
registry, not off a stack. They need no proof. cn1GcTrustedRoots marks that
window so gcMarkObject skips the guard for them, and the existing passes then
mark what they were already trying to mark. Nothing new runs; a pass that was
throwing its results away stops doing so.

Also corrects the comments asserting a fresh object is safe because grace keeps
it. Grace keeps the OBJECT; it does not trace the SUBTREE. That premise appears
at the guard, at the snapshot-rebuild note and at the sweep's promotion site, and
is what this defect was hiding behind.

Do NOT set cn1GcTrustedRoots around anything marking from a stack scan or
register file -- that is the case the guard exists for.

Validation: reproducer clean 3/3 over 166 collection cycles and 800M verified
references (baseline fails 100% with ~7000 violations); run-gc-verify.sh GREEN
over all nine drivers with both fault self-tests firing; geomean 1.22x vs 1.21x
on master, objectAllocation 3.85x vs 3.46x -- within run variance, all checksums
bit-identical to the host JVM.

An earlier revision of this branch instead added a late-grace re-mark pass to
compensate for the guard. It was correct but cost 2.1x on objectAllocation and
+8% geomean, and review found two further defects in it (a suffix scan unsound
against tombstone reuse, and an O(heap) pass widening the unbarriered
mark-to-sweep window). Fixing the guard makes all of that unnecessary.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Copilot AI review requested due to automatic review settings July 27, 2026 15:33
@shai-almog
shai-almog force-pushed the fix-5425-late-grace-remark branch from f68878d to 8e9f717 Compare July 27, 2026 15:33
@shai-almog shai-almog changed the title Trace fresh objects the sweep grace-promotes but nothing marked (issue 5425) Let the grace passes mark objects the resolve guard was discarding (issue 5425) Jul 27, 2026

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 8e9f717ca6

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread vm/ByteCodeTranslator/src/cn1_globals.m Outdated

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 2 out of 2 changed files in this pull request and generated 3 comments.

Comment thread vm/ByteCodeTranslator/src/cn1_globals.m Outdated
Comment thread vm/ByteCodeTranslator/src/cn1_globals.m Outdated
Comment thread vm/ByteCodeTranslator/src/cn1_globals.h Outdated
cn1GcTrustedRoots spanned gcMarkDrain() in the legacy grace pass. The drain
follows child words out of arbitrary generated mark functions -- including those
of DEAD objects kept alive by a conservative native-stack false positive, whose
reference fields can dangle into freed or unmapped memory. Bypassing
cn1ConservativeResolve for those is exactly the SIGSEGV the guard exists to
prevent, so the change reopened the crash it was meant to be safe around.

Trust covers only the registry walk, which is where the authoritative references
are. Every fresh entry is stamped and queued by then, so the drain needs none.
The BiBOP half already cleared per-page before its drain; the legacy half now
matches.

Also from review:
- Acquire-load the mark word in the legacy grace walk before dereferencing the
  header. The mark word is the publication point (see gcMarkObject); a plain read
  can let a weak-memory target observe the object without the preceding
  parentClsReference store. Pre-existing, but the resolve guard had been
  backstopping it for this population.
- Revert gcAuditSnapshot to CN1_GRACE_AUDIT-only. Promoting it was leftover from
  a narrowing that no longer exists: an O(#pages) walk plus atomics in every
  build for a field only read under that flag, with a stale comment referring to
  a pass that was deleted.

Re-validated: reproducer clean 3/3 over 149 collection cycles and 732M verified
references; run-gc-verify.sh GREEN over all nine drivers with both fault
self-tests firing; GcStress checksum unchanged.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Copilot AI review requested due to automatic review settings July 27, 2026 18:07

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 1 out of 1 changed files in this pull request and generated 10 comments.

Comment thread vm/ByteCodeTranslator/src/cn1_globals.m Outdated
Comment thread vm/ByteCodeTranslator/src/cn1_globals.m Outdated
Comment thread vm/ByteCodeTranslator/src/cn1_globals.m Outdated
Comment thread vm/ByteCodeTranslator/src/cn1_globals.m Outdated
Comment thread vm/ByteCodeTranslator/src/cn1_globals.m Outdated
Comment thread vm/ByteCodeTranslator/src/cn1_globals.m
Comment thread vm/ByteCodeTranslator/src/cn1_globals.m Outdated
Comment thread vm/ByteCodeTranslator/src/cn1_globals.m Outdated
Comment thread vm/ByteCodeTranslator/src/cn1_globals.m Outdated
Comment thread vm/ByteCodeTranslator/src/cn1_globals.m Outdated
@shai-almog

shai-almog commented Jul 27, 2026

Copy link
Copy Markdown
Collaborator Author

Compared 143 screenshots: 143 matched.
✅ Native iOS screenshot tests passed.

Benchmark Results

  • VM Translation Time: 0 seconds
  • Compilation Time: 378 seconds

Build and Run Timing

Metric Duration
Simulator Boot 76000 ms
Simulator Boot (Run) 2000 ms
App Install 12000 ms
App Launch 4000 ms
Test Execution 812000 ms

Detailed Performance Metrics

Metric Duration
SIMD kernel backend SSE2 (x64) / NEON (arm64) native kernels
SIMD int-add (64K x300) java 61ms / native 2ms = 30.5x speedup
SIMD float-mul (64K x300) java 92ms / native 3ms = 30.6x speedup
SIMD kernel correctness PASS (native result == scalar reference)
Base64 payload size 8192 bytes
Base64 benchmark iterations 6000
Base64 SIMD byte path active (NEON-accelerated)
Base64 CN1 encode 206.000 ms
Base64 CN1 decode 194.000 ms
Base64 native encode 1030.000 ms
Base64 encode ratio (CN1/native) 0.200x (80.0% faster)
Base64 native decode 678.000 ms
Base64 decode ratio (CN1/native) 0.286x (71.4% faster)
Base64 SIMD encode 83.000 ms
Base64 encode ratio (SIMD/CN1) 0.403x (59.7% faster)
Base64 SIMD decode 122.000 ms
Base64 decode ratio (SIMD/CN1) 0.629x (37.1% faster)
Base64 encode ratio (SIMD/native) 0.081x (91.9% faster)
Base64 decode ratio (SIMD/native) 0.180x (82.0% faster)
Image encode benchmark iterations 100
Image createMask (SIMD off) 7.000 ms
Image createMask (SIMD on) 2.000 ms
Image createMask ratio (SIMD on/off) 0.286x (71.4% faster)
Image applyMask (SIMD off) 59.000 ms
Image applyMask (SIMD on) 63.000 ms
Image applyMask ratio (SIMD on/off) 1.068x (6.8% slower)
Image modifyAlpha (SIMD off) 53.000 ms
Image modifyAlpha (SIMD on) 43.000 ms
Image modifyAlpha ratio (SIMD on/off) 0.811x (18.9% faster)
Image modifyAlpha removeColor (SIMD off) 86.000 ms
Image modifyAlpha removeColor (SIMD on) 77.000 ms
Image modifyAlpha removeColor ratio (SIMD on/off) 0.895x (10.5% faster)

cn1GcTrustedRoots was a process-wide global. Marking can run as a worker pool
(gcMarkDrainParallel), so a concurrent worker would also bypass the
conservative-resolve guard while a grace pass held the flag -- on pointers that
are NOT authoritative, which is the precise case the guard exists for. It is safe
today only because gcMarkResolveThreadCount() returns 1, and the comment there
says that serial default is a temporary isolation experiment: the hazard would
arm itself the moment parallel marking is restored, and would look unrelated when
it fired. Make it __thread.

Manual set/unset also is not nesting-safe -- the BiBOP half toggles it per page
inside a loop, so an early return or a future nested trusted scope would leak a
trusted window into unrelated marking. CN1_GC_TRUSTED_BEGIN/END save and restore
the previous value instead of clearing to zero.

Re-validated: reproducer clean 2/2 over 102 collection cycles and 492M verified
references; run-gc-verify.sh GREEN over all nine drivers with both fault
self-tests firing; GcStress checksum unchanged, including a
-DCN1_GC_MARK_THREADS=4 build -- the parallel configuration this is about.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Copilot AI review requested due to automatic review settings July 28, 2026 01:13

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 1 out of 1 changed files in this pull request and generated no new comments.

Comments suppressed due to low confidence (1)

vm/ByteCodeTranslator/src/cn1_globals.m:1079

  • CN1_GC_TRUSTED_BEGIN/END currently declares int __cn1TrustSaved in the surrounding scope. Identifiers beginning with __ are reserved, and the plain declaration also makes the macros easy to misuse (e.g., multiple BEGIN/END pairs in the same scope) despite the nearby comment mentioning nesting/early-return safety.

Wrap the BEGIN/END pair in a single do { ... } while(0) statement and use a non-reserved local name so the saved value is scoped to the pair and can’t collide with other uses.

// restore, so nesting and any future early return cannot leak a trusted window
// into unrelated marking.
static __thread int cn1GcTrustedRoots = 0;
#define CN1_GC_TRUSTED_BEGIN() int __cn1TrustSaved = cn1GcTrustedRoots; cn1GcTrustedRoots = 1
#define CN1_GC_TRUSTED_END()   cn1GcTrustedRoots = __cn1TrustSaved

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.

[Bug] probably a bug in the new parpavm optimizations

2 participants