Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
136 commits
Select commit Hold shift + click to select a range
17c5ce8
ParparVM perf tier1: box caches, BCE, conditional-volatile SSA, nurse…
shai-almog Jun 27, 2026
185085c
ParparVM: pre-size the StringBuilder in invokedynamic string-concat l…
shai-almog Jun 27, 2026
f123617
ParparVM tagged-int: fix 4 header-deref crashes found by edge-case re…
shai-almog Jun 27, 2026
7049ad0
ParparVM: native HashMap.get (closed-world hot-path wrapper collapse)
shai-almog Jun 27, 2026
519d5e1
ParparVM: native HashMap.put (closed-world hot-path wrapper collapse)
shai-almog Jun 27, 2026
bc9e713
ParparVM: native StringBuilder.append(int)/append(long) digit writers
shai-almog Jun 27, 2026
5da6233
ParparVM: HashMap Entry object pool (eliminate churn allocation)
shai-almog Jun 27, 2026
ae6af08
ParparVM: StringBuilder.toString shares the buffer (copy-on-write)
shai-almog Jun 27, 2026
be882e4
ParparVM: inline the full method-frame setup/teardown
shai-almog Jun 27, 2026
7fc70f2
ParparVM: inline pop() (operand-stack pop helper)
shai-almog Jun 27, 2026
d4185da
ParparVM: @StackAllocate annotation -> stack-allocate non-escaping ob…
shai-almog Jun 28, 2026
8ea3c18
ParparVM: scalar-replace @StackAllocate objects -> clang promotes to …
shai-almog Jun 28, 2026
7469b54
ParparVM: fuse LCMP+IFxx into a direct long comparison (clang can vec…
shai-almog Jun 28, 2026
ab36ca8
ParparVM: elide per-line stack-trace line stores on non-throwing lines
shai-almog Jun 28, 2026
aa2838e
ParparVM: parallel GC marking (work-pool drain, atomic claim)
shai-almog Jun 28, 2026
7df04ea
ParparVM: non-moving BiBOP segregated heap + page sweep for small obj…
shai-almog Jun 28, 2026
0260fe8
ParparVM: frameless codegen for primitive-only methods (recursion 4.6…
shai-almog Jun 29, 2026
b0e6c98
ParparVM: conservative native-stack GC + frameless for object-bearing…
shai-almog Jun 29, 2026
9933311
ParparVM: fix Thread.start/join visibility race (alive set on wrong t…
shai-almog Jun 29, 2026
d34c80c
ParparVM: default-on conservative native-stack GC + object/instance f…
shai-almog Jun 29, 2026
37e8f62
ParparVM: inline BiBOP bump alloc fast-path at the new-site (-DCN1_IN…
shai-almog Jun 29, 2026
85502d5
ParparVM: inline tiny leaf constructors + de-atomic byte accounting (…
shai-almog Jun 29, 2026
3edcbc8
ParparVM: fix use-after-free in conservative-GC root-snapshot build (…
shai-almog Jun 29, 2026
b3dcdf3
ParparVM: make the alloc fast-path default-on (un-gate; this is an AO…
shai-almog Jun 30, 2026
bd0d808
ParparVM: adaptive allocation pacing (bound churn RSS) + batched page…
shai-almog Jun 30, 2026
9c9e298
ParparVM: O(live-pages) BiBOP sweep -- O(1) reclaim/skip of homogeneo…
shai-almog Jun 30, 2026
4b82a95
ParparVM: trim the per-object BiBOP alloc fast path (dead store + cou…
shai-almog Jun 30, 2026
60b9910
ParparVM: shrink the per-object header 48 -> 16 bytes (relocate 3 GC …
shai-almog Jun 30, 2026
3008cfa
ParparVM: always-on per-object memset elimination + thread-stop GC ha…
shai-almog Jul 2, 2026
0f8afae
ParparVM: single-writer allObjectsInHeap (dead-thread drain) + once-p…
shai-almog Jul 2, 2026
a7de4b0
ParparVM: general fused objects (@Fused) + small arrays in BiBOP + Ja…
shai-almog Jul 2, 2026
6373a47
ParparVM: computed-size fused children (new int[w*h]) + ThinLTO for R…
shai-almog Jul 2, 2026
d136205
ParparVM: register-allocatable locals, frameless whitelist holes, fmo…
shai-almog Jul 2, 2026
7a507a4
ParparVM: per-page sticky monitor flag replaces the global sweep supp…
shai-almog Jul 2, 2026
04e9dad
ParparVM: closed-world devirtualization, String finalizer removal, st…
shai-almog Jul 2, 2026
4a2aa8b
Compact open-addressed HashMap + native String compare: hashMapChurn …
shai-almog Jul 2, 2026
fe7a37e
Fused StringBuilder + native single-block toString; setLength zero-fi…
shai-almog Jul 2, 2026
65a965f
Diverging array checks in frameless loops: quicksort below HotSpot
shai-almog Jul 2, 2026
088d89e
Implicit stack allocation of non-escaping StringBuilders + two GC-tri…
shai-almog Jul 2, 2026
287369b
Merge remote-tracking branch 'origin/master' into parparvm-perf-tier1
shai-almog Jul 2, 2026
d58d816
String/StringBuilder call-site intrinsics; native-bracket elision; GC…
shai-almog Jul 2, 2026
a2148bd
Fix Linux/Windows portability of the conservative-GC + allocator code
shai-almog Jul 2, 2026
ecaa19e
Fix remaining CI failures: Win32 shim gaps, JS delegate retention, st…
shai-almog Jul 2, 2026
64946a6
Fix missing include when trivial-accessor inlining crosses classes
shai-almog Jul 2, 2026
59ea779
Fix two more trivial-accessor inlining bugs: setter arity, fold order…
shai-almog Jul 2, 2026
f363d3f
Fix setjmp/longjmp UB in try/catch codegen: restoreTo must be volatile
shai-almog Jul 2, 2026
acd947f
Fix mid-line preprocessor directive from the ctor-inlining emission
shai-almog Jul 2, 2026
e094586
iOS port: migrate the ellipsis-string pin to the immortal-root registry
shai-almog Jul 3, 2026
3a2eb81
Preallocate StackOverflowError: throwing at exhaustion must not build…
shai-almog Jul 3, 2026
ba4ed19
Fix the two SpotBugs gate violations in BytecodeMethod
shai-almog Jul 3, 2026
4f911a8
Benchmark suite in-repo; charAt logical-length bound; tagged ints def…
shai-almog Jul 3, 2026
39e18bb
Developer guide: satisfy the prose quality gates on the new section
shai-almog Jul 3, 2026
1669554
Refresh Linux picker goldens: the old ones encoded the case-conversio…
shai-almog Jul 3, 2026
736f43b
watch tests: capture app console + instrument toast/annotation state
shai-almog Jul 3, 2026
86bbe64
VM: release the class-init monitor when a static initializer throws
shai-almog Jul 3, 2026
5f0af69
Merge remote-tracking branch 'origin/master' into parparvm-perf-tier1
shai-almog Jul 3, 2026
3cdc797
signed bytes on ARM-Linux; deterministic toast test; linux suite diag…
shai-almog Jul 3, 2026
c879be9
VM: key monitor reentrancy on the pthread, not the thread-state id
shai-almog Jul 3, 2026
6a0070c
CN1_MONITOR_SELF: use the compat shim's pthread_self().id on Windows
shai-almog Jul 3, 2026
81af321
linux harness: create CN1_APP_LOG_TEE parent dirs
shai-almog Jul 3, 2026
1bcf273
JS port: protect runtime-delegate twins from identifier minification
shai-almog Jul 3, 2026
d2b2608
linux CI: gdb hang watchdog for the suite wedge
shai-almog Jul 3, 2026
7072d13
VM: frameless SOE guard must not misfire on foreign stacks
shai-almog Jul 3, 2026
66378a1
linux CI: the suite wedge is a silent crash -- capture cores + exit s…
shai-almog Jul 3, 2026
64c1dce
linux CI: richer GC-crash post-mortem
shai-almog Jul 3, 2026
163a5aa
toast test: hold for the slide animation before capturing
shai-almog Jul 3, 2026
30257f5
VM: acquire-load the mark word in parallel GC marking (arm64 heap cor…
shai-almog Jul 3, 2026
3ad6797
VM: default parallel GC marking to serial (arm64 isolation experiment)
shai-almog Jul 3, 2026
2763ccf
VM: exempt conservative stack scan from ASan (enables meaningful ASan…
shai-almog Jul 3, 2026
52bd25b
ToastBar: add non-animated show; make the TOP-position screenshot det…
shai-almog Jul 3, 2026
ef6e7b6
Revert ToastBar test changes to the known-good baseline
shai-almog Jul 3, 2026
c9d3b55
TEMP: Linux x64 ASan diagnostic workflow
shai-almog Jul 4, 2026
3a5b59c
linux-asan-x64: push-trigger on the feature branch (workflow_dispatch…
shai-almog Jul 4, 2026
c400885
linux-asan-x64: build the maven plugin before suite classes (mirror p…
shai-almog Jul 4, 2026
094245a
Remove temp Linux x64 ASan diagnostic workflow
shai-almog Jul 4, 2026
27a3332
VM: CN1_BIBOP_VALIDATE invariant checks for the x64 allocator crash
shai-almog Jul 4, 2026
68db022
TEMP: Linux x64 BiBOP-validate diagnostic workflow (x3 suite runs, re…
shai-almog Jul 4, 2026
0d1f7a1
CN1_BIBOP_VALIDATE: validate the object at the mark-drain source
shai-almog Jul 4, 2026
9c5ab69
CN1_BIBOP_VALIDATE: catch stale-mark + out-of-text markFunction at drain
shai-almog Jul 4, 2026
78752a2
VM FIX: gcMarkObject must reject freed BiBOP slots (x64 GC crash)
shai-almog Jul 4, 2026
de65c2c
Remove temp x64 BiBOP-validate diagnostic workflow (bug fixed + confi…
shai-almog Jul 4, 2026
0abf15f
Windows port: offscreen WIC capture for the cn1ss suite + BiBOP no-fa…
shai-almog Jul 5, 2026
1ab06af
GC forensics: pin the intermittent Linux gcMarkObject mid-mark SIGSEGV
shai-almog Jul 5, 2026
ff7a1f1
GC: SATB write barrier to close the concurrent-mark cross-thread race
shai-almog Jul 5, 2026
9ccb524
GC forensics: name the corrupt-child culprit (parent class + mark cal…
shai-almog Jul 5, 2026
2faff80
ci: post-mortem prints the mark drain-parent + its class name
shai-almog Jul 5, 2026
60ef4c7
GC: belt pass -- guarantee mark-drain completeness before sweep
shai-almog Jul 5, 2026
58b71b4
ci: A/B the O(1) all-dead reclaim (-DCN1_BIBOP_NO_FASTSWEEP) as crash…
shai-almog Jul 5, 2026
952ae3d
GC forensics: name the drain-incompleteness (belt-recovered class -> …
shai-almog Jul 5, 2026
8b6a56f
GC: revert looped belt to single-pass (looping livelocks vs active mu…
shai-almog Jul 5, 2026
0171685
GC: drain grace-object subtrees before sweep (fixes swept-while-reach…
shai-almog Jul 5, 2026
6dd95de
GC: add SATB insertion barrier to close the grace-object residual
shai-almog Jul 5, 2026
ffde9e2
GC: keep SATB armed through grace pass + belt (drain the log last)
shai-almog Jul 5, 2026
4888b66
GC: stop-the-world final mark -- loop belt to a true fixpoint (deadlo…
shai-almog Jul 5, 2026
275b672
Revert "GC: stop-the-world final mark -- loop belt to a true fixpoint…
shai-almog Jul 5, 2026
8055d28
GC: poor-man's generational adoption -- mature surviving BiBOP subtre…
shai-almog Jul 5, 2026
2a8e47a
GC: teach the remaining -4 (adopted) sentinel sites + VALIDATE guards
shai-almog Jul 5, 2026
7340a05
ci(linux): drop the temporary CN1_BIBOP_VALIDATE diagnostic from the …
shai-almog Jul 5, 2026
9c9bccf
GC: defer adoption registration out of the mark (fix reentrant allObj…
shai-almog Jul 5, 2026
35978db
GC: don't resurrect a dead matured object as an immortal root in the …
shai-almog Jul 5, 2026
47d7e5a
ci(linux): TEMP disambiguation -- build with adoption disabled (CN1_A…
shai-almog Jul 5, 2026
1df30d3
GC: a matured object's DEATH belongs to one collector (fix double-fin…
shai-almog Jul 5, 2026
8a14511
GC/stack: compute the real native stack limit on Linux (fix frameless…
shai-almog Jul 6, 2026
e7c670b
Revert "GC/stack: compute the real native stack limit on Linux (fix f…
shai-almog Jul 6, 2026
e8b3c11
linux: raise CN1 thread stack 16MB -> 64MB (fix arm64 EDT stack overf…
shai-almog Jul 6, 2026
3f8c626
ci(diag): dump native backtrace at 40MB stack depth to pin the arm64 …
shai-almog Jul 6, 2026
8cbd52e
Revert "ci(diag): dump native backtrace at 40MB stack depth to pin th…
shai-almog Jul 6, 2026
7fa3f90
Revert "linux: raise CN1 thread stack 16MB -> 64MB (fix arm64 EDT sta…
shai-almog Jul 6, 2026
58d4c9d
linux: register a per-thread alternate signal stack (make stack-overf…
shai-almog Jul 6, 2026
31ec45e
linux: dump the crash backtrace to stderr too (not just the BuildClou…
shai-almog Jul 6, 2026
4064ad5
linux: make stack-overflow faults dump a backtrace (SA_ONSTACK + wild…
shai-almog Jul 6, 2026
6bd4d7f
ci(linux): addr2line the CN1 fault-backtrace addresses in the post-mo…
shai-almog Jul 6, 2026
21040cc
ci(diag): fault dump prints faultPC/SP + thread stack bounds (is it a…
shai-almog Jul 6, 2026
4e9c332
ci(experiment): force CN1_GC_SIGNAL_STOP=1 to test the conservative r…
shai-almog Jul 6, 2026
6526da7
Revert "ci(experiment): force CN1_GC_SIGNAL_STOP=1 to test the conser…
shai-almog Jul 6, 2026
c55bd60
TEMP experiment: default frameless.objects/instance OFF to test arm64…
shai-almog Jul 6, 2026
77718d1
ci(diag): count conservative-resolve rejections of plausible live obj…
shai-almog Jul 6, 2026
1aa8d05
GC: conservative resolve must not drop a live object past the STALE s…
shai-almog Jul 6, 2026
3cc11bf
Revert "GC: conservative resolve must not drop a live object past the…
shai-almog Jul 6, 2026
64b64bd
Revert resolve idx>=bump change + drop diag flag (fix was wrong: acce…
shai-almog Jul 6, 2026
4447e98
GC: anchor object-bearing frameless frames in memory so the conservat…
shai-almog Jul 6, 2026
c554c2a
GC: zero the frameless frame at entry (memset) alongside the escape a…
shai-almog Jul 6, 2026
c6144be
GC: default object-bearing frameless OFF (unsound under conservative …
shai-almog Jul 6, 2026
8ab9c72
Revert anchor/memset/frameless-off experiments to baseline (best-know…
shai-almog Jul 6, 2026
d19c6bf
GC forensic (TEMP): detect + identify the arm64 conservative-scan roo…
shai-almog Jul 6, 2026
9171a22
GC forensic (TEMP): guard+identify the garbage parentCls gcMarkObject…
shai-almog Jul 6, 2026
dc2d1d7
GC forensic: fix build - widen gcMarkCurrentDrainObj to CN1_ROOTMISS_…
shai-almog Jul 6, 2026
94eeafb
GC: validate parentCls before dereferencing it in gcMarkObject (conse…
shai-almog Jul 6, 2026
b0f8f7c
GC: never let a finalizer/collection exception kill the GC thread (fi…
shai-almog Jul 7, 2026
0c1f0f5
Merge remote-tracking branch 'origin/master' into parparvm-perf-tier1
shai-almog Jul 7, 2026
de59f33
docs: migrate the @Fused perf-guide snippet to a demo-backed include
shai-almog Jul 7, 2026
d467f6b
Merge remote-tracking branch 'origin/master' into parparvm-perf-tier1
shai-almog Jul 7, 2026
26a1b92
mac-native: refresh ToastBarTopPosition golden (deterministic render,…
shai-almog Jul 7, 2026
6294eaf
tvOS: refresh ToastBarTopPosition golden (deterministic, max_channel_…
shai-almog Jul 7, 2026
4a970d8
screenshots: make ToastBar deterministic on slow sims + retry unready…
shai-almog Jul 7, 2026
5cc5d1b
iOS: re-baseline ToastBarTopPosition golden from the settled (6000ms)…
shai-almog Jul 7, 2026
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
43 changes: 42 additions & 1 deletion .github/workflows/linux-build-run.yml
Original file line number Diff line number Diff line change
Expand Up @@ -207,6 +207,9 @@ jobs:
working-directory: vm
env:
CN1_SHOT_OUTPUT_DIR: ${{ github.workspace }}/artifacts/linux-port/raw
# Full app stdout/stderr -- the only evidence when the suite wedges
# mid-run (uploaded with the screenshot artifact below).
CN1_APP_LOG_TEE: ${{ github.workspace }}/artifacts/linux-port/raw/app-output.log
# Build the native ELF (and the demo) against an old glibc for portability.
CN1_CC: /usr/local/bin/cn1-zig-cc
# After the suite runs, the capture test relinks the same objects into a
Expand All @@ -215,13 +218,50 @@ jobs:
LIBGL_ALWAYS_SOFTWARE: '1'
run: |
set -e
sudo apt-get install -y --no-install-recommends gdb >/dev/null 2>&1 || true
Xvfb :99 -screen 0 1200x1600x24 >/tmp/xvfb-run.log 2>&1 &
export DISPLAY=:99
sleep 2
# The suite intermittently DIES mid-run with no output (gdb-attach
# watchdog found the pid already gone -- a silent crash, not a hang).
# Enable core dumps and post-mortem them into the artifact.
ulimit -c unlimited
echo '/tmp/cn1-cores/core.%e.%p' | sudo tee /proc/sys/kernel/core_pattern >/dev/null
mkdir -p /tmp/cn1-cores
rc=0
mvn -B clean package -pl JavaAPI -am -DskipTests
mvn -B test -pl tests -am \
'-Dtest=CleanTargetLinuxIntegrationTest#capturesHelloSuiteOverWebSocketLinux' \
'-Dsurefire.failIfNoSpecifiedTests=false'
'-Dsurefire.failIfNoSpecifiedTests=false' || rc=$?
for core in /tmp/cn1-cores/core.*; do
[ -f "$core" ] || continue
elf="$(/usr/bin/find /tmp -maxdepth 4 -name LinuxHelloMain -type f 2>/dev/null | head -1)"
mkdir -p "$(dirname "$CN1_APP_LOG_TEE")"
{
echo "=== post-mortem of $core (elf=$elf) ==="
gdb "$elf" "$core" -batch -ex 'set pagination off' \
-ex 'thread apply all bt' \
-ex 'thread 1' -ex 'bt full' \
-ex 'frame 0' -ex 'info args' \
-ex 'frame 1' -ex 'info locals' -ex 'info args' \
-ex 'p gcMarkWorklistTop' -ex 'p currentGcMarkValue' \
2>&1
} >> "$(dirname "$CN1_APP_LOG_TEE")/crash-stacks.txt" || true
done
# Resolve the CN1 FAULT/ABORT backtrace addresses (LinuxHelloMain[0x...]) that the
# SA_ONSTACK handler dumped to app-output.log into function names -- the faulting
# frames (e.g. a stack overflow's recursion / the caller that ran CN1 on a small
# native stack). The suite binary is not stripped, so addr2line resolves them.
elf="$(/usr/bin/find /tmp -maxdepth 4 -name LinuxHelloMain -type f 2>/dev/null | head -1)"
if [ -n "$elf" ] && [ -f "$CN1_APP_LOG_TEE" ]; then
{
echo "=== addr2line of CN1 backtrace addresses (from app log) ==="
grep -oE "LinuxHelloMain\[0x[0-9a-f]+\]" "$CN1_APP_LOG_TEE" | grep -oE "0x[0-9a-f]+" | while read a; do
echo "$a -> $(addr2line -f -C -e "$elf" "$a" 2>/dev/null | tr '\n' ' ')"
done
} >> "$(dirname "$CN1_APP_LOG_TEE")/crash-stacks.txt" 2>&1 || true
fi
exit $rc

- name: Upload screenshot artifact (${{ matrix.arch }})
if: always()
Expand Down Expand Up @@ -268,6 +308,7 @@ jobs:
docker run --rm \
-v "$GITHUB_WORKSPACE":/cn1 -w /cn1 \
-e CN1_SHOT_OUTPUT_DIR=/cn1/artifacts/linux-port/raw-musl \
-e CN1_APP_LOG_TEE=/cn1/artifacts/linux-port/raw-musl/app-output.log \
-e LIBGL_ALWAYS_SOFTWARE=1 \
docker.io/library/alpine:3.20 sh -ec '
sed -i "s|^#\(.*/community\)|\1|" /etc/apk/repositories
Expand Down
34 changes: 34 additions & 0 deletions .github/workflows/parparvm-tests-windows.yml
Original file line number Diff line number Diff line change
Expand Up @@ -366,6 +366,22 @@ jobs:
mvn -B clean package -pl JavaAPI -am -DskipTests
mvn -B test -pl tests -am '-Dtest=CleanTargetIntegrationTest#capturesHelloSuiteOverWebSocket' '-Dsurefire.failIfNoSpecifiedTests=false'

# Preserve the native cn1WindowsLog output so the offscreen-capture path is
# confirmable on the green x64 gate too (compare against the arm64 leg).
- name: Collect native windows log (x64)
if: always()
shell: pwsh
run: |
New-Item -ItemType Directory -Force -Path artifacts/windows-port/raw | Out-Null
$log = Join-Path $env:TEMP 'cn1windows.log'
if (Test-Path $log) {
Copy-Item $log artifacts/windows-port/raw/cn1windows-native.log -Force
Write-Host "collected native log ($((Get-Item $log).Length) bytes)"
} else {
"no native cn1windows.log found at $log" | Out-File artifacts/windows-port/raw/cn1windows-native.log
Write-Host "no native log at $log"
}

- name: Upload screenshot artifact (x64)
if: always()
uses: actions/upload-artifact@v4
Expand Down Expand Up @@ -474,6 +490,24 @@ jobs:
mvn -B clean package -pl JavaAPI -am -DskipTests
mvn -B test -pl tests -am '-Dtest=CleanTargetIntegrationTest#capturesHelloSuiteOverWebSocket' '-Dsurefire.failIfNoSpecifiedTests=false'

# Preserve the native cn1WindowsLog output (%TEMP%\cn1windows.log) so an
# offscreen-target / capture failure is diagnosable from the artifact, and
# so the raw dir is never empty (the arm64 artifact would otherwise not be
# produced when the suite stalls before writing any PNG).
- name: Collect native windows log (arm64)
if: always()
shell: pwsh
run: |
New-Item -ItemType Directory -Force -Path artifacts/windows-port/raw | Out-Null
$log = Join-Path $env:TEMP 'cn1windows.log'
if (Test-Path $log) {
Copy-Item $log artifacts/windows-port/raw/cn1windows-native.log -Force
Write-Host "collected native log ($((Get-Item $log).Length) bytes)"
} else {
"no native cn1windows.log found at $log" | Out-File artifacts/windows-port/raw/cn1windows-native.log
Write-Host "no native log at $log"
}

- name: Upload screenshot artifact (arm64)
if: always()
uses: actions/upload-artifact@v4
Expand Down
36 changes: 36 additions & 0 deletions CodenameOne/src/com/codename1/annotations/Fused.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
package com.codename1.annotations;

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

/// Marks a class whose constructor-created primitive-array fields are fully
/// ENCAPSULATED, letting ParparVM allocate each instance TOGETHER with those
/// arrays as a single heap block ("fused object"): one allocation instead of
/// several, one object for the garbage collector instead of several, and the
/// arrays share the owner's cache lines. A fused array keeps a completely
/// ordinary header -- all reading code, `System.arraycopy`, iteration and
/// bounds checks work unchanged -- but it has no independent GC identity: it
/// is never separately tracked, marked for reclamation, or swept; it simply
/// lives and dies with its owner.
///
/// The translator fuses a constructor-assigned field when the constructor
/// contains an unconditional `this.f = new T[n]` where `T` is a primitive type
/// and `n` is a constructor parameter or constant (bounds-check guards that
/// throw are fine before it). Constructors keep chaining normally, and every
/// other instantiation path -- reflection, deserialization, oversized arrays
/// that do not fit a fused block -- transparently falls back to ordinary
/// separate allocations with identical semantics.
///
/// THE CONTRACT: the marked class must never let a fused array reference
/// escape into another object's field, an array element, or a static that can
/// outlive the instance. Holding it in locals, passing it as a call argument,
/// or copying its contents out is always safe (the VM keeps the whole block
/// alive through any reference into it held on a thread stack). Typical
/// candidates: string/buffer classes owning their `char[]`, image types owning
/// their pixel `int[]`/`byte[]` data.
@Retention(RetentionPolicy.CLASS)
@Target(ElementType.TYPE)
public @interface Fused {
}
41 changes: 41 additions & 0 deletions Ports/LinuxPort/nativeSources/cn1_linux_crash_protection.c
Original file line number Diff line number Diff line change
Expand Up @@ -203,6 +203,34 @@ static void cn1_cp_write_dec(int fd, long v) {
}

static void cn1_cp_signal_handler(int sig, siginfo_t *info, void *ctx) {
/* Always dump the faulting thread's backtrace to stderr (fd 2 -> the app-output tee),
* not just to the BuildCloud pending file. This is how a STACK-OVERFLOW crash becomes
* legible: with the per-thread alt signal stack this handler now runs even on overflow,
* and the repeating ADDR/off= lines below are the recursion. Addresses only (async-
* signal-safe writes; no -rdynamic, so no codegen perturbation). off= is the address
* minus the load base = the binary's static address (matches addr2line / gdb symbols). */
#if CN1_CP_HAVE_BACKTRACE
{
void *bt[96];
int bn = backtrace(bt, 96);
CN1_CP_W_STR(2, "\n===CN1CRASH sig=");
cn1_cp_write_dec(2, (long)sig);
CN1_CP_W_STR(2, " frames=");
cn1_cp_write_dec(2, (long)bn);
CN1_CP_W_STR(2, " base=");
cn1_cp_write_hex(2, cn1_cp_load_base);
if (info != NULL) { CN1_CP_W_STR(2, " fault="); cn1_cp_write_hex(2, (uintptr_t)info->si_addr); }
CN1_CP_W_STR(2, "===\n");
for (int i = 0; i < bn; i++) {
CN1_CP_W_STR(2, "CN1BT off=");
cn1_cp_write_hex(2, (uintptr_t)bt[i] - cn1_cp_load_base);
CN1_CP_W_STR(2, " abs=");
cn1_cp_write_hex(2, (uintptr_t)bt[i]);
CN1_CP_W_STR(2, "\n");
}
CN1_CP_W_STR(2, "===CN1CRASH END===\n");
}
#endif
if (cn1_cp_pending_path_set) {
int fd = open(cn1_cp_pending_path,
O_WRONLY | O_CREAT | O_TRUNC, 0600);
Expand Down Expand Up @@ -285,6 +313,19 @@ void cn1_crash_protection_install(void) {
cn1_cp_resolve_pending_path();
cn1_cp_log_install();
cn1_cp_install_signal_handlers();
/* Alternate signal stack for THIS (main) thread, so the SA_ONSTACK handler runs even
* on a stack-overflow SIGSEGV when the main stack is exhausted. Per-thread; the CN1
* worker/EDT/GC threads register their own in threadRunner/gcMarkWorkerMain. Without
* it a stack overflow dies silently instead of dumping the recursion backtrace. */
{
stack_t ss;
ss.ss_sp = malloc(512 * 1024);
if (ss.ss_sp != NULL) {
ss.ss_size = 512 * 1024;
ss.ss_flags = 0;
sigaltstack(&ss, NULL);
}
}
}

/* ----------------------------------------------------------------------
Expand Down
108 changes: 86 additions & 22 deletions Ports/LinuxPort/nativeSources/cn1_linux_window.c
Original file line number Diff line number Diff line change
Expand Up @@ -36,11 +36,15 @@
* host -- see Ports/LinuxPort/status.md.
*/

#ifndef _GNU_SOURCE
#define _GNU_SOURCE /* pthread_getattr_np + ucontext REG_* gregs; before any libc include */
#endif
#include "cn1_linux_gfx.h"
#include <stdlib.h>
#include <string.h>
#include <pthread.h>
#include <signal.h>
#include <ucontext.h>
#ifdef __GLIBC__
#include <execinfo.h> /* backtrace() -- glibc only; musl has no execinfo.h */
#endif
Expand Down Expand Up @@ -428,15 +432,80 @@ static gboolean cn1OnDelete(GtkWidget* widget, GdkEvent* e, gpointer data) {
* the fault first-chance). It is a release/CI resilience mechanism, not a debug
* aid -- set CN1_LINUX_NO_FAULT_HANDLER=1 to disable it when debugging under gdb.
*/
/* Dump the faulting thread's native backtrace to stderr (the app-output tee). Async-
* signal-safe (write + backtrace). With the per-thread alternate signal stack this runs
* even on a STACK OVERFLOW; the repeating frames are the recursion. Called from both the
* SIGABRT path and the wild-address SIGSEGV path (stack-overflow guard-page faults land
* there), so an overflow is finally legible instead of a bare, corrupt-unwind core. */
/* async-signal-safe "KEY=0xHEX\n" */
static void cn1LinuxWriteHexKV(const char* key, unsigned long v) {
char buf[64]; int i = 0;
while (key[i]) { buf[i] = key[i]; i++; }
buf[i++] = '='; buf[i++] = '0'; buf[i++] = 'x';
char hx[16]; int h = 0;
if (v == 0) hx[h++] = '0';
while (v) { int d = v & 0xf; hx[h++] = (char)(d < 10 ? '0' + d : 'a' + d - 10); v >>= 4; }
while (h > 0) buf[i++] = hx[--h];
buf[i++] = '\n';
write(2, buf, i);
}

static void cn1LinuxDumpNativeBacktrace(const char* label, siginfo_t* si, void* ucv) {
signal(SIGSEGV, SIG_DFL);
signal(SIGBUS, SIG_DFL);
write(2, "\n=====CN1 ", 10);
write(2, label, strlen(label));
write(2, " BACKTRACE=====\n", 16);
if (si != NULL) cn1LinuxWriteHexKV("faultAddr", (unsigned long)(uintptr_t)si->si_addr);
/* faulting PC + SP from the ucontext, and this thread's real stack bounds -- if the
* used span (top - sp) is a fraction of a MB, this is a SMALL-STACK thread overflow
* (e.g. CN1 rendering on a WebKit/Gallium/GLib worker thread), not deep recursion. */
if (ucv != NULL) {
ucontext_t* uc = (ucontext_t*)ucv;
unsigned long sp = 0, pc = 0;
#if defined(__aarch64__)
sp = (unsigned long)uc->uc_mcontext.sp;
pc = (unsigned long)uc->uc_mcontext.pc;
#elif defined(__x86_64__)
sp = (unsigned long)uc->uc_mcontext.gregs[REG_RSP];
pc = (unsigned long)uc->uc_mcontext.gregs[REG_RIP];
#endif
if (pc) cn1LinuxWriteHexKV("faultPC", pc);
if (sp) cn1LinuxWriteHexKV("faultSP", sp);
pthread_attr_t at;
if (pthread_getattr_np(pthread_self(), &at) == 0) {
void* base = 0; size_t sz = 0;
if (pthread_attr_getstack(&at, &base, &sz) == 0) {
cn1LinuxWriteHexKV("stackLo", (unsigned long)(uintptr_t)base);
cn1LinuxWriteHexKV("stackSz", (unsigned long)sz);
if (sp) cn1LinuxWriteHexKV("stackUsed", (unsigned long)(((uintptr_t)base + sz) - sp));
}
pthread_attr_destroy(&at);
}
}
#ifdef __GLIBC__
void* bt[96];
int n = backtrace(bt, 96);
backtrace_symbols_fd(bt, n, 2);
#else
const char* na = "(native backtrace unavailable on this libc)\n";
write(2, na, strlen(na));
#endif
const char* ftr = "=====END CN1 BACKTRACE=====\n";
write(2, ftr, strlen(ftr));
}

static void cn1LinuxFaultToException(int sig, siginfo_t* si, void* ucv) {
(void) ucv;
/* Only a genuine null-ish deref (null + a small field/vtable/array offset)
* is converted to an NPE. A wild faulting address is real memory corruption:
* restore the default disposition and return so the re-executed instruction
* faults again into a core dump that stays diagnosable, instead of being
* masked as a recoverable NPE that silently corrupts further state. */
* is converted to an NPE. A wild faulting address is real memory corruption OR a
* stack overflow (the guard-page address is high): dump a backtrace so the failing
* frame / recursion is identifiable from the CI log, then restore the default
* disposition and return so the re-executed instruction faults again into a core
* dump, instead of being masked as a recoverable NPE that silently corrupts state. */
if ((sig == SIGSEGV || sig == SIGBUS) && si != NULL &&
(uintptr_t) si->si_addr >= 0x10000) {
cn1LinuxDumpNativeBacktrace("FAULT", si, ucv);
signal(sig, SIG_DFL);
return;
}
Expand All @@ -453,23 +522,7 @@ static void cn1LinuxFaultToException(int sig, siginfo_t* si, void* ucv) {
* even where an interactive debugger is unavailable (CI containers). Enabled only
* when CN1_LINUX_ABORT_BACKTRACE is set so it never interferes with normal runs. */
static void cn1LinuxAbortBacktrace(int sig) {
/* Restore default SIGSEGV/SIGBUS so a fault while walking a corrupted stack
* just dies here instead of longjmp-ing out via the NPE handler. */
signal(SIGSEGV, SIG_DFL);
signal(SIGBUS, SIG_DFL);
const char* hdr = "\n=====CN1 ABORT BACKTRACE=====\n";
write(2, hdr, strlen(hdr));
#ifdef __GLIBC__
void* bt[80];
int n = backtrace(bt, 80);
backtrace_symbols_fd(bt, n, 2);
#else
/* musl has no execinfo.h/backtrace(); fall through with just the markers. */
const char* na = "(native backtrace unavailable on this libc)\n";
write(2, na, strlen(na));
#endif
const char* ftr = "=====END CN1 ABORT BACKTRACE=====\n";
write(2, ftr, strlen(ftr));
cn1LinuxDumpNativeBacktrace("ABORT", NULL, NULL);
signal(sig, SIG_DFL);
raise(sig);
}
Expand All @@ -480,10 +533,21 @@ static void cn1LinuxInstallFaultHandlers() {
return;
}
installed = 1;
/* Alternate signal stack for THIS (main) thread so SA_ONSTACK works here too. The CN1
* worker/EDT/GC threads register their own in threadRunner/gcMarkWorkerMain. */
{
stack_t ss;
ss.ss_sp = malloc(512 * 1024);
if (ss.ss_sp != NULL) { ss.ss_size = 512 * 1024; ss.ss_flags = 0; sigaltstack(&ss, NULL); }
}
struct sigaction sa;
memset(&sa, 0, sizeof(sa));
sa.sa_sigaction = cn1LinuxFaultToException;
sa.sa_flags = SA_SIGINFO | SA_NODEFER;
/* SA_ONSTACK: run the handler on the per-thread alternate signal stack (registered in
* threadRunner / gcMarkWorkerMain / crash-protection install) so it can execute even
* on a STACK OVERFLOW, when the faulting thread's own stack is exhausted -- otherwise
* the handler re-faults and the process dies silently with no backtrace. */
sa.sa_flags = SA_SIGINFO | SA_NODEFER | SA_ONSTACK;
sigemptyset(&sa.sa_mask);
sigaction(SIGSEGV, &sa, NULL);
sigaction(SIGBUS, &sa, NULL);
Expand Down
10 changes: 10 additions & 0 deletions Ports/WindowsPort/nativeSources/cn1_windows.h
Original file line number Diff line number Diff line change
Expand Up @@ -217,6 +217,16 @@ typedef struct {
JAVA_INT shotW;
JAVA_INT shotH;

/* Offscreen-capture mode (the cn1ss WebSocket screenshot suite): a real
* (hidden) window is still created so the message pump, DPI and exact client
* size are identical to a normal run, but windowGraphics is pointed at an
* offscreen WIC bitmap of that same client size instead of the HWND target.
* That makes captureWindowToPngBytes read back real frames (the proven WIC
* path) instead of falling back to a fresh per-screenshot mutable-image
* repaint -- which is the expensive step that stalled the slow windows-11-arm
* runner mid-suite. Unlike `headless` there is no single-shot auto-exit. */
volatile LONG offscreenCapture;

/* Pending window resize. WM_SIZE (main thread) records the new size here and
* the EDT applies the Direct2D Resize between its own frames -- resizing the
* HWND render target from another thread while the EDT is mid-BeginDraw is
Expand Down
Loading
Loading