diff --git a/CLAUDE.md b/CLAUDE.md index fd07c171ed..568047b6a7 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -8,7 +8,7 @@ This file provides guidance to Claude Code (claude.ai/code) when working with co Perry is a native TypeScript compiler written in Rust that compiles TypeScript source code directly to native executables. It uses SWC for TypeScript parsing and LLVM for code generation. -**Current Version:** 0.5.1565 +**Current Version:** 0.5.1566 ## TypeScript Parity Status diff --git a/Cargo.lock b/Cargo.lock index 36b9765959..90632d9236 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -5690,7 +5690,7 @@ checksum = "1542e48011813fbdf3c075da4a4ed53ee93c816eef62e36eb5064a6fd2be10a5" [[package]] name = "perry" -version = "0.5.1565" +version = "0.5.1566" dependencies = [ "anyhow", "base64 0.22.1", @@ -5754,7 +5754,7 @@ dependencies = [ [[package]] name = "perry-api-manifest" -version = "0.5.1565" +version = "0.5.1566" dependencies = [ "perry-dispatch", "serde", @@ -5762,7 +5762,7 @@ dependencies = [ [[package]] name = "perry-audio-miniaudio" -version = "0.5.1565" +version = "0.5.1566" dependencies = [ "cc", "libc", @@ -5771,7 +5771,7 @@ dependencies = [ [[package]] name = "perry-codegen" -version = "0.5.1565" +version = "0.5.1566" dependencies = [ "aho-corasick", "anyhow", @@ -5789,7 +5789,7 @@ dependencies = [ [[package]] name = "perry-codegen-arkts" -version = "0.5.1565" +version = "0.5.1566" dependencies = [ "anyhow", "perry-hir", @@ -5797,7 +5797,7 @@ dependencies = [ [[package]] name = "perry-codegen-glance" -version = "0.5.1565" +version = "0.5.1566" dependencies = [ "anyhow", "perry-hir", @@ -5805,7 +5805,7 @@ dependencies = [ [[package]] name = "perry-codegen-js" -version = "0.5.1565" +version = "0.5.1566" dependencies = [ "anyhow", "perry-dispatch", @@ -5814,7 +5814,7 @@ dependencies = [ [[package]] name = "perry-codegen-swiftui" -version = "0.5.1565" +version = "0.5.1566" dependencies = [ "anyhow", "perry-hir", @@ -5822,7 +5822,7 @@ dependencies = [ [[package]] name = "perry-codegen-wasm" -version = "0.5.1565" +version = "0.5.1566" dependencies = [ "anyhow", "base64 0.22.1", @@ -5834,7 +5834,7 @@ dependencies = [ [[package]] name = "perry-codegen-wear-tiles" -version = "0.5.1565" +version = "0.5.1566" dependencies = [ "anyhow", "perry-hir", @@ -5842,7 +5842,7 @@ dependencies = [ [[package]] name = "perry-container-compose" -version = "0.5.1565" +version = "0.5.1566" dependencies = [ "anyhow", "async-trait", @@ -5870,14 +5870,14 @@ dependencies = [ [[package]] name = "perry-container-e2e" -version = "0.5.1565" +version = "0.5.1566" dependencies = [ "anyhow", ] [[package]] name = "perry-diagnostics" -version = "0.5.1565" +version = "0.5.1566" dependencies = [ "serde", "serde_json", @@ -5885,7 +5885,7 @@ dependencies = [ [[package]] name = "perry-dispatch" -version = "0.5.1565" +version = "0.5.1566" [[package]] name = "perry-doc-fixture-my-bindings" @@ -5896,7 +5896,7 @@ dependencies = [ [[package]] name = "perry-doc-tests" -version = "0.5.1565" +version = "0.5.1566" dependencies = [ "anyhow", "clap", @@ -5911,7 +5911,7 @@ dependencies = [ [[package]] name = "perry-ext-ads" -version = "0.5.1565" +version = "0.5.1566" dependencies = [ "block2", "objc2", @@ -5921,7 +5921,7 @@ dependencies = [ [[package]] name = "perry-ext-argon2" -version = "0.5.1565" +version = "0.5.1566" dependencies = [ "argon2", "perry-ffi", @@ -5930,7 +5930,7 @@ dependencies = [ [[package]] name = "perry-ext-axios" -version = "0.5.1565" +version = "0.5.1566" dependencies = [ "perry-ffi", "reqwest", @@ -5939,7 +5939,7 @@ dependencies = [ [[package]] name = "perry-ext-bcrypt" -version = "0.5.1565" +version = "0.5.1566" dependencies = [ "bcrypt", "perry-ffi", @@ -5947,7 +5947,7 @@ dependencies = [ [[package]] name = "perry-ext-better-sqlite3" -version = "0.5.1565" +version = "0.5.1566" dependencies = [ "perry-ffi", "rusqlite", @@ -5955,7 +5955,7 @@ dependencies = [ [[package]] name = "perry-ext-cheerio" -version = "0.5.1565" +version = "0.5.1566" dependencies = [ "perry-ffi", "scraper", @@ -5963,7 +5963,7 @@ dependencies = [ [[package]] name = "perry-ext-commander" -version = "0.5.1565" +version = "0.5.1566" dependencies = [ "perry-ffi", "perry-runtime", @@ -5971,7 +5971,7 @@ dependencies = [ [[package]] name = "perry-ext-cron" -version = "0.5.1565" +version = "0.5.1566" dependencies = [ "chrono", "cron", @@ -5981,7 +5981,7 @@ dependencies = [ [[package]] name = "perry-ext-dayjs" -version = "0.5.1565" +version = "0.5.1566" dependencies = [ "chrono", "perry-ffi", @@ -5989,7 +5989,7 @@ dependencies = [ [[package]] name = "perry-ext-decimal" -version = "0.5.1565" +version = "0.5.1566" dependencies = [ "perry-ffi", "rust_decimal", @@ -5997,7 +5997,7 @@ dependencies = [ [[package]] name = "perry-ext-dotenv" -version = "0.5.1565" +version = "0.5.1566" dependencies = [ "perry-ffi", "serde_json", @@ -6005,7 +6005,7 @@ dependencies = [ [[package]] name = "perry-ext-ethers" -version = "0.5.1565" +version = "0.5.1566" dependencies = [ "perry-ffi", "rand 0.10.2", @@ -6013,7 +6013,7 @@ dependencies = [ [[package]] name = "perry-ext-events" -version = "0.5.1565" +version = "0.5.1566" dependencies = [ "perry-ffi", "perry-runtime", @@ -6021,14 +6021,14 @@ dependencies = [ [[package]] name = "perry-ext-exponential-backoff" -version = "0.5.1565" +version = "0.5.1566" dependencies = [ "perry-ffi", ] [[package]] name = "perry-ext-fastify" -version = "0.5.1565" +version = "0.5.1566" dependencies = [ "bytes", "http-body-util", @@ -6046,7 +6046,7 @@ dependencies = [ [[package]] name = "perry-ext-fetch" -version = "0.5.1565" +version = "0.5.1566" dependencies = [ "bytes", "lazy_static", @@ -6059,7 +6059,7 @@ dependencies = [ [[package]] name = "perry-ext-http" -version = "0.5.1565" +version = "0.5.1566" dependencies = [ "base64 0.22.1", "bytes", @@ -6091,7 +6091,7 @@ dependencies = [ [[package]] name = "perry-ext-ioredis" -version = "0.5.1565" +version = "0.5.1566" dependencies = [ "lazy_static", "perry-ffi", @@ -6101,7 +6101,7 @@ dependencies = [ [[package]] name = "perry-ext-jsonwebtoken" -version = "0.5.1565" +version = "0.5.1566" dependencies = [ "base64 0.22.1", "jsonwebtoken", @@ -6112,7 +6112,7 @@ dependencies = [ [[package]] name = "perry-ext-lru-cache" -version = "0.5.1565" +version = "0.5.1566" dependencies = [ "lru", "perry-ffi", @@ -6121,7 +6121,7 @@ dependencies = [ [[package]] name = "perry-ext-moment" -version = "0.5.1565" +version = "0.5.1566" dependencies = [ "chrono", "perry-ffi", @@ -6129,7 +6129,7 @@ dependencies = [ [[package]] name = "perry-ext-mongodb" -version = "0.5.1565" +version = "0.5.1566" dependencies = [ "bson", "futures-util", @@ -6141,7 +6141,7 @@ dependencies = [ [[package]] name = "perry-ext-mysql2" -version = "0.5.1565" +version = "0.5.1566" dependencies = [ "chrono", "perry-ffi", @@ -6153,7 +6153,7 @@ dependencies = [ [[package]] name = "perry-ext-nanoid" -version = "0.5.1565" +version = "0.5.1566" dependencies = [ "nanoid", "perry-ffi", @@ -6162,7 +6162,7 @@ dependencies = [ [[package]] name = "perry-ext-net" -version = "0.5.1565" +version = "0.5.1566" dependencies = [ "bytes", "perry-ffi", @@ -6177,7 +6177,7 @@ dependencies = [ [[package]] name = "perry-ext-node-forge" -version = "0.5.1565" +version = "0.5.1566" dependencies = [ "const-oid 0.10.2", "der 0.8.1", @@ -6196,7 +6196,7 @@ dependencies = [ [[package]] name = "perry-ext-nodemailer" -version = "0.5.1565" +version = "0.5.1566" dependencies = [ "lettre", "perry-ffi", @@ -6206,7 +6206,7 @@ dependencies = [ [[package]] name = "perry-ext-parcel-watcher" -version = "0.5.1565" +version = "0.5.1566" dependencies = [ "notify", "perry-ffi", @@ -6218,7 +6218,7 @@ dependencies = [ [[package]] name = "perry-ext-pdf" -version = "0.5.1565" +version = "0.5.1566" dependencies = [ "perry-ffi", "printpdf", @@ -6226,7 +6226,7 @@ dependencies = [ [[package]] name = "perry-ext-pg" -version = "0.5.1565" +version = "0.5.1566" dependencies = [ "perry-ffi", "sqlx", @@ -6235,7 +6235,7 @@ dependencies = [ [[package]] name = "perry-ext-qs" -version = "0.5.1565" +version = "0.5.1566" dependencies = [ "perry-ffi", "perry-runtime", @@ -6244,7 +6244,7 @@ dependencies = [ [[package]] name = "perry-ext-ratelimit" -version = "0.5.1565" +version = "0.5.1566" dependencies = [ "governor", "perry-ffi", @@ -6252,7 +6252,7 @@ dependencies = [ [[package]] name = "perry-ext-sharp" -version = "0.5.1565" +version = "0.5.1566" dependencies = [ "fast_image_resize", "image", @@ -6263,7 +6263,7 @@ dependencies = [ [[package]] name = "perry-ext-streams" -version = "0.5.1565" +version = "0.5.1566" dependencies = [ "lazy_static", "perry-ffi", @@ -6272,7 +6272,7 @@ dependencies = [ [[package]] name = "perry-ext-typescript" -version = "0.5.1565" +version = "0.5.1566" dependencies = [ "anyhow", "perry-ffi", @@ -6292,7 +6292,7 @@ dependencies = [ [[package]] name = "perry-ext-undici" -version = "0.5.1565" +version = "0.5.1566" dependencies = [ "perry-ffi", "perry-runtime", @@ -6301,7 +6301,7 @@ dependencies = [ [[package]] name = "perry-ext-uuid" -version = "0.5.1565" +version = "0.5.1566" dependencies = [ "perry-ffi", "uuid", @@ -6309,7 +6309,7 @@ dependencies = [ [[package]] name = "perry-ext-validator" -version = "0.5.1565" +version = "0.5.1566" dependencies = [ "perry-ffi", "perry-validation", @@ -6318,7 +6318,7 @@ dependencies = [ [[package]] name = "perry-ext-ws" -version = "0.5.1565" +version = "0.5.1566" dependencies = [ "futures-util", "lazy_static", @@ -6331,7 +6331,7 @@ dependencies = [ [[package]] name = "perry-ext-zlib" -version = "0.5.1565" +version = "0.5.1566" dependencies = [ "brotli", "flate2", @@ -6341,7 +6341,7 @@ dependencies = [ [[package]] name = "perry-ffi" -version = "0.5.1565" +version = "0.5.1566" dependencies = [ "dashmap 6.2.1", "once_cell", @@ -6351,7 +6351,7 @@ dependencies = [ [[package]] name = "perry-hir" -version = "0.5.1565" +version = "0.5.1566" dependencies = [ "anyhow", "perry-api-manifest", @@ -6372,11 +6372,11 @@ dependencies = [ [[package]] name = "perry-native-registration" -version = "0.5.1565" +version = "0.5.1566" [[package]] name = "perry-parser" -version = "0.5.1565" +version = "0.5.1566" dependencies = [ "anyhow", "perry-diagnostics", @@ -6390,7 +6390,7 @@ dependencies = [ [[package]] name = "perry-perex" -version = "0.5.1565" +version = "0.5.1566" dependencies = [ "perex", "regex", @@ -6398,7 +6398,7 @@ dependencies = [ [[package]] name = "perry-runtime" -version = "0.5.1565" +version = "0.5.1566" dependencies = [ "ahash", "anyhow", @@ -6458,14 +6458,14 @@ dependencies = [ [[package]] name = "perry-runtime-static" -version = "0.5.1565" +version = "0.5.1566" dependencies = [ "perry-runtime", ] [[package]] name = "perry-stdlib" -version = "0.5.1565" +version = "0.5.1566" dependencies = [ "aes 0.8.4", "aes 0.9.1", @@ -6560,14 +6560,14 @@ dependencies = [ [[package]] name = "perry-stdlib-static" -version = "0.5.1565" +version = "0.5.1566" dependencies = [ "perry-stdlib", ] [[package]] name = "perry-transform" -version = "0.5.1565" +version = "0.5.1566" dependencies = [ "anyhow", "perry-hir", @@ -6576,7 +6576,7 @@ dependencies = [ [[package]] name = "perry-ui" -version = "0.5.1565" +version = "0.5.1566" dependencies = [ "dirs", "perry-ffi", @@ -6586,7 +6586,7 @@ dependencies = [ [[package]] name = "perry-ui-android" -version = "0.5.1565" +version = "0.5.1566" dependencies = [ "base64 0.22.1", "itoa", @@ -6604,7 +6604,7 @@ dependencies = [ [[package]] name = "perry-ui-geisterhand" -version = "0.5.1565" +version = "0.5.1566" dependencies = [ "rand 0.10.2", "serde", @@ -6614,7 +6614,7 @@ dependencies = [ [[package]] name = "perry-ui-gtk4" -version = "0.5.1565" +version = "0.5.1566" dependencies = [ "base64 0.22.1", "cairo-rs 0.22.9", @@ -6637,7 +6637,7 @@ dependencies = [ [[package]] name = "perry-ui-ios" -version = "0.5.1565" +version = "0.5.1566" dependencies = [ "base64 0.22.1", "block2", @@ -6654,7 +6654,7 @@ dependencies = [ [[package]] name = "perry-ui-macos" -version = "0.5.1565" +version = "0.5.1566" dependencies = [ "base64 0.22.1", "block2", @@ -6671,7 +6671,7 @@ dependencies = [ [[package]] name = "perry-ui-model" -version = "0.5.1565" +version = "0.5.1566" [[package]] name = "perry-ui-test" @@ -6682,11 +6682,11 @@ dependencies = [ [[package]] name = "perry-ui-testkit" -version = "0.5.1565" +version = "0.5.1566" [[package]] name = "perry-ui-tvos" -version = "0.5.1565" +version = "0.5.1566" dependencies = [ "base64 0.22.1", "block2", @@ -6703,7 +6703,7 @@ dependencies = [ [[package]] name = "perry-ui-visionos" -version = "0.5.1565" +version = "0.5.1566" dependencies = [ "base64 0.22.1", "block2", @@ -6720,7 +6720,7 @@ dependencies = [ [[package]] name = "perry-ui-watchos" -version = "0.5.1565" +version = "0.5.1566" dependencies = [ "block2", "libc", @@ -6734,7 +6734,7 @@ dependencies = [ [[package]] name = "perry-ui-windows" -version = "0.5.1565" +version = "0.5.1566" dependencies = [ "base64 0.22.1", "libc", @@ -6753,7 +6753,7 @@ dependencies = [ [[package]] name = "perry-ui-windows-winui" -version = "0.5.1565" +version = "0.5.1566" dependencies = [ "base64 0.22.1", "libc", @@ -6766,7 +6766,7 @@ dependencies = [ [[package]] name = "perry-updater" -version = "0.5.1565" +version = "0.5.1566" dependencies = [ "anyhow", "base64 0.22.1", @@ -6782,7 +6782,7 @@ dependencies = [ [[package]] name = "perry-validation" -version = "0.5.1565" +version = "0.5.1566" dependencies = [ "idna", "regex", @@ -6792,7 +6792,7 @@ dependencies = [ [[package]] name = "perry-wasm-host" -version = "0.5.1565" +version = "0.5.1566" dependencies = [ "wasmi", ] diff --git a/Cargo.toml b/Cargo.toml index d15ea1e7f6..eb96d810ac 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -338,7 +338,7 @@ codegen-units = 1 codegen-units = 1 [workspace.package] -version = "0.5.1565" +version = "0.5.1566" edition = "2021" license = "MIT" repository = "https://github.com/PerryTS/perry" diff --git a/benchmarks/native_property_get/README.md b/benchmarks/native_property_get/README.md new file mode 100644 index 0000000000..3452726bde --- /dev/null +++ b/benchmarks/native_property_get/README.md @@ -0,0 +1,49 @@ +# Native property Get probes + +These probes accompany the 2026-09-14 native-side property Get brief. They +separate positive own/prototype reads from non-regex iterator, promise, and +JSON work. Each program checks its result; the runner also compares stdout +with the repository's pinned Node version and rejects an empty result. + +Build the compiler and both static wrappers from the same checkout and +package set. Use an isolated target directory/worktree for each concurrent +build. For the Linux instruction comparison: + +```sh +CARGO_PROFILE_RELEASE_CODEGEN_UNITS=16 \ +CARGO_PROFILE_RELEASE_DEBUG=1 CARGO_PROFILE_RELEASE_STRIP=none \ +cargo build --release --locked -j4 \ + -p perry -p perry-runtime-static -p perry-stdlib-static +python3 benchmarks/native_property_get/measure.py \ + --runtime-dir target/release --out /absolute/path/to/new-results \ + --node /path/to/node-v26.5.1/bin/node +``` + +The output directory must be new. The runner records the source commit, +compiler/archive/probe SHA-256 hashes, three `instructions:u` counts, +instruction-sampled DWARF call stacks, period-weighted folded stacks, and +inclusive Get attribution. Nested Get frames count once in the inclusive +share. Shares are sampling estimates; the stat counts are the performance +comparison. `--debug-symbols` is required when compiling the probes, and +`--no-auto-optimize` pins the runtime archives. LLVM's addr2line is selected +locally when available so host binutils does not choke on runtime DWARF. + +`--smoke` compiles every probe, runs 100 iterations, and compares with Node +without requiring Linux perf. A zero Get attribution is rejected: it can +indicate stripped symbols or a probe that did not exercise the intended path. + +The iterator reuses its result object. It avoids large retained arrays and +therefore does not depend on resolving the concurrent #10215 investigation. +The thenable and toJSON probes use positive lookups because main already +optimizes many negative probes for those names. + +Measured results, scope, and validation are in [REPORT.md](REPORT.md). To replay +the fault injections with no other builds reading this worktree: + +```sh +python3 benchmarks/native_property_get/faults.py --out /absolute/path/to/new-fault-results +``` + +The three mutants must fail their named test assertions, and restored sources +must pass the eight focused tests. The GC custody inventory classifies the +forced-slow boolean as test-only; production binaries contain no switch. diff --git a/benchmarks/native_property_get/REPORT.md b/benchmarks/native_property_get/REPORT.md new file mode 100644 index 0000000000..614dc6217f --- /dev/null +++ b/benchmarks/native_property_get/REPORT.md @@ -0,0 +1,137 @@ +# Native-side property Get: measured ordinary-data path + +Base: `9fda98df68d9fac3c08b2385fae007aa9f5278df` (main, 0.5.1563). +Date: 2026-09-14. Measurements: Linux x86_64, perrymaster, AMD Ryzen 7 7700X. + +## Change and safety boundary + +`Reflect.get`, the named-field ABI, and the native borrowed-name getter share +an inlinable positive lookup. It classifies the pointer once per chain link, +requires an arena-backed GC object with an ordinary shape and an admitted +ordinary class ID, checks the accessor-key Bloom summary, then reads the +current shape's own data slot. It follows explicit ordinary prototypes or +already-rooted synthetic class prototype pointers for at most 32 links. +It does not infer absence: every unproven case falls through to the original +Get implementation. Keys remain borrowed throughout this callback-free, +JS-allocation-free lookup; the consult-only keys index cannot build or collect. + +No new GC roots or pointer caches are introduced. The `toJSON` caller retains +its handle scope, reuses the rooted canonical key, and uses the new canonical +key Get API. Default builtin prototype resolution remains on the slow path: +it can initialize objects and observe replacement through globalThis. + +Own/prototype accessors, proxies, private-looking names, `constructor`, declared +classes, process.env, mapped arguments, element-bearing objects, and native +exotics decline. In particular, RegExp expandos and `lastIndex` still use the +existing exotic handler. Inherited null/undefined values conservatively decline +to preserve the current slow resolver's behavior. A distinct Reflect receiver +can use a data hit because no getter or receiver binding is observed. + +This deliberately limits the optimization to ordinary data reads. The measured +improvement does not require exotic bypasses, new per-realm builtin pointers, +or a per-call-site cache. Remaining hot costs include class-ID membership and +prototype registries; those have separate invalidation and collision rules. + +## Instruction evidence + +The base workloads were measured before runtime edits. All five programs check +their checksum and match pinned Node 26.5.1 stdout. Both builds use the same +checkout base, package set, release codegen-units=16, debug=1, strip=none, and +repository unwind/frame-pointer flags. Probe compilation uses +`--no-auto-optimize --debug-symbols` and an explicit `PERRY_RUNTIME_DIR`. +The table uses medians of three `perf stat -e instructions:u` runs, including +startup. It makes no shared-host wall-clock speed claim. + +| Workload | Iterations | Base instructions | Candidate instructions | Reduction | Inclusive Get share, base → candidate | +|---|---:|---:|---:|---:|---:| +| `reflect_own` | 200,000 | 498,582,567 | 143,385,957 | 71.24% | 98.80% → 97.20% | +| `reflect_proto` | 200,000 | 1,369,594,468 | 305,995,648 | 77.66% | 98.90% → 99.35% | +| `iterator` | 200,000 | 886,642,572 | 886,162,527 | 0.05% | 0.68% → 0.79% | +| `thenable` | 20,000 | 365,634,266 | 320,441,221 | 12.36% | 15.50% → 2.66% | +| `tojson` | 100,000 | 742,646,720 | 423,858,278 | 42.93% | 50.71% → 28.22% | + +`iterator` is a user `for…of` iterator that reuses its result object: generic +Get is under 1% of the baseline, so the neutral result is expected. Positive +thenable assimilation and inherited `toJSON` expose the runtime cost clearly. +The synthetic Reflect probes cover own data and two prototype links. + +Attribution uses `perf record -e instructions:u -c 500003 --call-graph +dwarf,16384 -m 64M --no-buildid --no-buildid-cache`, then `perf script --no-inline`. +All ten final profiles reported no lost samples. Folded stacks are weighted by +sample period, and nested Get frames count once in the inclusive share. Shares +are sampling estimates, especially the small iterator share. An earlier smaller +sampling buffer dropped samples; those attribution runs are excluded here. + +The JSON files in `evidence/` record all counts, shares, top inclusive-Get leaf +symbols, probe/binary hashes, and compiler/runtime archive hashes. The compressed +`measurement-receipts.tar.gz` contains raw stat rows, checked stdout, final +folded stacks, perf record logs, and the measured candidate source patch. +The patch records the pre-commit source snapshot; later evidence and harness +reporting edits do not change the measured runtime. Full perf data and linked +probe binaries remain under `/root/native-property-get/` on perrymaster. + +## Correctness and fault injection + +Eight Rust tests compare ordinary reads against a forced slow path and cover +all value kinds, prototype mutation, shadow/delete/freeze, own and inherited +getters, setter-only descriptors, proxies, class statics, private-looking keys, +arrays, Buffer, mapped arguments, RegExp expandos/accessors, long keys, and +spilled/deleted shape slots. Getter counts assert one invocation per Get. +`test-files/test_gap_native_property_get.ts` adds 42 lines of checked parity output, +including distinct receivers, proxy traps/revocation, key-conversion order, +symbols, class/private syntax, Date, typed arrays, and RegExp. + +The test-only forced-slow switch is thread-local; it cannot affect production. +`faults.py` runs healthy tests before and after each serial source-mutation +campaign, restores source files in `finally`, and requires a named assertion +failure (a compile failure does not count). All three mutations were killed by named assertion failures (exit 101): + +| Mutation | Failing test | +|---|---| +| Force every classified Get to decline | `own_data_is_served_and_matches_forced_slow_for_all_value_kinds` | +| Ignore the accessor Bloom guard | `accessor_bloom_guard_preserves_own_and_inherited_getter_calls` | +| Ignore RegExp own `value` expandos in the fallback | `regexp_expandos_and_accessors_remain_on_the_exotic_path` | + +The last mutation exercises the preserved exotic fallback; this change adds no +exotic fast path. The restored eight-test suite passed before and after the +campaign. See `evidence/faults.json` and `fault-logs.tar.gz`. + +## Validation + +- Full runtime unit suite: 3,803 passed, 4 ignored, 0 failed, single-threaded. + Base suite: 3,795 passed, 4 ignored; the known native-stack test passed here. +- `cargo check -p perry-runtime --no-default-features --features full`: passed. +- Clippy all-target diagnostics compared with the captured base: no additions + or removals after normalizing shifted line numbers. Both runs retain the same + 12 pre-existing `approx_constant` errors; see `evidence/clippy-diff.json`. +- `cargo fmt --all --check`, `git diff --check`, and Python syntax checks passed. +- The final measurement harness smoke test passed all five programs on Linux. + The 42-line parity program matches Node on both base and candidate. +- The final full lint replay ran both script and compile tiers: 80 of 83 + commands passed, with three existing failures and two CI-only skips. A new test-only + boolean initially required a custody verdict; the explicit `test_only` entry + fixes that failure, with no added pointer debt. The script-tier replay passes + every executable gate except the pre-existing public benchmark freshness red. + The compile tier also exposes pre-existing unused WebAssembly helpers, an existing test cache assignment, and API + docs drift (`bun-pty`). The unused-helper failures reproduce on a clean base + worktree. All ten API-manifest source files are byte-identical to base, and + the generated diff is saved. See `evidence/base-gate-comparison.json`. +- GC ratchet: all 14 probes match Node, with seven measured repetitions and two + trace passes on both base and candidate. All 126 shared-CI metric medians + (including the probe-specific exclusions) are exactly identical. The checker + passes against a fresh same-host base artifact. The repository's older pinned + artifact fails on both base and candidate; it was not changed or re-pinned. + See `evidence/gc-ratchet-ab.json` and the raw GC receipts. +- GC stress: all 86 corpus programs ran across seven PR arms: 428 PASS, 167 + UNVER (output matched but that arm did not collect), 7 FAIL. Every required + arm passes aggregate liveness. All seven failures are the HTTP/2 pending-event + callback fixture. A fresh-base replay reproduces all seven failures with + byte-identical evidence strings and collector counters. See + `evidence/gc-stress-base-http2.json` and `gc-stress-receipts.tar.gz`. The + single-fixture replay has inert arms as expected; the full candidate corpus + satisfies every arm's liveness requirement. + +Main advanced to `eb13fa188d` while this work was validated, including separate +GC and HTTP fixes. The measurements and A/B receipts deliberately remain on +`9fda98df68`; the merge train must replay the combined tree. This branch does +not absorb or rebaseline those independent changes. diff --git a/benchmarks/native_property_get/evidence/api-docs-drift.patch.gz b/benchmarks/native_property_get/evidence/api-docs-drift.patch.gz new file mode 100644 index 0000000000..2914094f81 Binary files /dev/null and b/benchmarks/native_property_get/evidence/api-docs-drift.patch.gz differ diff --git a/benchmarks/native_property_get/evidence/base-gate-comparison.json b/benchmarks/native_property_get/evidence/base-gate-comparison.json new file mode 100644 index 0000000000..8aa96cceb5 --- /dev/null +++ b/benchmarks/native_property_get/evidence/base-gate-comparison.json @@ -0,0 +1,40 @@ +{ + "base_commit": "9fda98df68d9fac3c08b2385fae007aa9f5278df", + "warnings": { + "base": [ + "error: function `registered_extern_handle` is never used", + "error: function `wasm_memory_descriptor_maximum` is never used" + ], + "candidate": [ + "error: function `registered_extern_handle` is never used", + "error: function `wasm_memory_descriptor_maximum` is never used" + ], + "base_test_warnings": [ + "warning: value assigned to `cache` is never read", + "warning: function `register_extern_wrapper` is never used", + "warning: function `registered_extern_handle` is never used", + "warning: function `wasm_memory_descriptor_maximum` is never used" + ], + "candidate_test_warnings": [ + "warning: value assigned to `cache` is never read", + "warning: function `register_extern_wrapper` is never used", + "warning: function `registered_extern_handle` is never used", + "warning: function `wasm_memory_descriptor_maximum` is never used" + ] + }, + "api_manifest_source_identical": true, + "api_manifest_files": [ + "crates/perry-api-manifest/Cargo.toml", + "crates/perry-api-manifest/src/emit.rs", + "crates/perry-api-manifest/src/entries.rs", + "crates/perry-api-manifest/src/entries/part_1.rs", + "crates/perry-api-manifest/src/entries/part_2.rs", + "crates/perry-api-manifest/src/entries/part_3.rs", + "crates/perry-api-manifest/src/entries/part_4.rs", + "crates/perry-api-manifest/src/lib.rs", + "crates/perry-api-manifest/src/native_abi.rs", + "crates/perry-api-manifest/tests/stub_inventory.rs" + ], + "api_docs_drift": "Generated docs add bun-pty from unchanged manifest; saved in api-docs-drift.patch.gz.", + "public_freshness_base": "public baseline error: public artifact benchmark inputs changed; regenerate it with ./benchmarks/run_public_baseline.sh" +} diff --git a/benchmarks/native_property_get/evidence/baseline-build.json b/benchmarks/native_property_get/evidence/baseline-build.json new file mode 100644 index 0000000000..d24f64d7e8 --- /dev/null +++ b/benchmarks/native_property_get/evidence/baseline-build.json @@ -0,0 +1,16 @@ +{ + "commit": "9fda98df68d9fac3c08b2385fae007aa9f5278df", + "node": "v26.5.1", + "artifacts": { + "perry": "5c3d79c951b6d10ebb394f01d8e4258445567433060e025a900a58eb79c15f71", + "libperry_runtime.a": "d4d6cfef4cc9c076fb00dedf99c56b5be9cab5f4a4813c1b097fd41f47ff18b6", + "libperry_stdlib.a": "871746c24fbedff2fc2076fec4ae598a38a79efabdff96ab1cec835457ff2203" + }, + "probes": { + "reflect_own": "be47a8c4951b74b7eb1303afd1b02ed929b954f485cf7f0d10194968c1f29cba", + "reflect_proto": "45e82bd599efdcff78032103ec31d87ad087afdf7bdc7438b4abf91142b8b2c3", + "iterator": "da8ac63a3918d5aede6185b823e94d896dab89327a2b2718278a8355df1240a4", + "thenable": "f418d2de753b6baef4d2cad9fba2dd7d41d44e460f9da74fad84778b0c6374ec", + "tojson": "139928fda9c0e8f2c2d1a38feb4ac8f18ef14d8dad13114b325b06650e2155e0" + } +} diff --git a/benchmarks/native_property_get/evidence/baseline.json b/benchmarks/native_property_get/evidence/baseline.json new file mode 100644 index 0000000000..e1cc3171d9 --- /dev/null +++ b/benchmarks/native_property_get/evidence/baseline.json @@ -0,0 +1,428 @@ +{ + "reflect_own": { + "iterations": 200000, + "instructions": [ + 498582567, + 498582572, + 498582459 + ], + "median": 498582567, + "get_inclusive_percent": 98.79518072289157, + "get_leaf_percent_of_program": [ + [ + "js_reflect_get", + 12.951807228915662 + ], + [ + "js_object_get_field_by_name", + 10.943775100401606 + ], + [ + "perry_runtime::object::field_get_set::get_field_by_name_tail::get_field_by_name_object_tail", + 9.337349397590362 + ], + [ + "perry_runtime::object::class_registry::parent_static::is_class_object_ptr", + 9.337349397590362 + ], + [ + "js_to_primitive", + 9.036144578313253 + ], + [ + "::copy_of_key", + 4.216867469879518 + ], + [ + "perry_runtime::async_hooks::is_async_resource_handle", + 4.116465863453815 + ], + [ + "core::str::converts::from_utf8", + 3.6144578313253013 + ], + [ + "perry_runtime::closure::dynamic_props::is_closure_ptr", + 2.8112449799196786 + ], + [ + "perry_runtime::object::arguments::is_arguments_object", + 2.710843373493976 + ], + [ + "perry_runtime::proxy::target_get_property_key", + 2.710843373493976 + ], + [ + "perry_runtime::object::shapes::shape_live_inline_slot_count_by_id", + 2.710843373493976 + ], + [ + "perry_runtime::object::class_meta_registry::get_parent_class_id", + 2.610441767068273 + ], + [ + "perry_runtime::buffer::header::is_registered_buffer", + 2.108433734939759 + ], + [ + "perry_runtime::object::field_get_set::has_property::closure_dynamic_prop_by_key", + 1.7068273092369477 + ], + [ + "perry_runtime::proxy::reflect_value_is_object", + 1.7068273092369477 + ], + [ + "perry_runtime::temporal::is_temporal_cell_addr", + 1.5060240963855422 + ], + [ + "perry_runtime::typedarray_props::typed_array_addr_from_value", + 1.5060240963855422 + ], + [ + "perry_runtime::date::is_date_cell_addr", + 1.5060240963855422 + ], + [ + "trunc", + 1.104417670682731 + ] + ], + "sampled_instructions": 498002988, + "binary_sha256": "e2c4b05f839d026a5c5f5bd3d49f5e647855b1f45a23a5c49d0f04fd179b9082" + }, + "reflect_proto": { + "iterations": 200000, + "instructions": [ + 1369594500, + 1369593934, + 1369594468 + ], + "median": 1369594468, + "get_inclusive_percent": 98.90470974808325, + "get_leaf_percent_of_program": [ + [ + "perry_runtime::object::class_meta_registry::get_parent_class_id", + 16.502373128879153 + ], + [ + "js_object_get_field_by_name", + 10.076670317634173 + ], + [ + "perry_runtime::object::class_registry::state::class_decl_prototype_object", + 7.6305220883534135 + ], + [ + "perry_runtime::object::field_get_set::get_field_by_name_tail::get_field_by_name_object_tail", + 7.484483388097846 + ], + [ + "js_reflect_get", + 4.490690032858708 + ], + [ + "perry_runtime::object::class_registry::prototype_objects::resolve_proto_chain_field_inner", + 4.417670682730924 + ], + [ + "core::str::converts::from_utf8", + 3.833515881708653 + ], + [ + "perry_runtime::async_hooks::is_async_resource_handle", + 3.687477181453085 + ], + [ + "perry_runtime::closure::dynamic_props::is_closure_ptr", + 3.4684191310697337 + ], + [ + "trunc", + 2.9937933552391383 + ], + [ + "perry_runtime::object::arguments::is_arguments_object", + 2.8112449799196786 + ], + [ + "perry_runtime::object::class_registry::parent_static::is_class_object_ptr", + 2.7747353048557866 + ], + [ + "perry_runtime::buffer::header::is_registered_buffer", + 2.701715954728003 + ], + [ + "::copy_of_key", + 2.701715954728003 + ], + [ + "perry_runtime::temporal::is_temporal_cell_addr", + 2.701715954728003 + ], + [ + "js_to_primitive", + 2.3366192040890836 + ], + [ + "perry_runtime::proxy::reflect_value_is_object", + 1.8985031033223805 + ], + [ + "perry_runtime::object::class_registry::prototype_objects::class_prototype_object", + 1.8619934282584885 + ], + [ + "perry_runtime::object::prototype_chain::meta_capable_object", + 1.3508579773640015 + ], + [ + "perry_runtime::closure::dynamic_props::closure_get_dynamic_prop", + 1.3143483023001095 + ] + ], + "sampled_instructions": 1369508217, + "binary_sha256": "6defa98c303cff2f955b8611a71b528144ff0697d7c6d4f041cbd10f7f63025b" + }, + "iterator": { + "iterations": 200000, + "instructions": [ + 886642572, + 886643407, + 886642216 + ], + "median": 886642572, + "get_inclusive_percent": 0.676818950930626, + "get_leaf_percent_of_program": [ + [ + "perry_runtime::object::field_get_set::get_field_by_name_tail::get_field_by_name_object_tail", + 0.11280315848843768 + ], + [ + "perry_runtime::object::prototype_chain::meta_capable_object", + 0.11280315848843768 + ], + [ + "perry_runtime::object::class_registry::parent_static::is_class_object_ptr", + 0.11280315848843768 + ], + [ + "mi_free", + 0.05640157924421884 + ], + [ + "js_array_get_f64", + 0.05640157924421884 + ], + [ + "js_object_get_field_by_name", + 0.05640157924421884 + ], + [ + "perry_runtime::object::field_get_set::accessors::array_subclass_prototype_field", + 0.05640157924421884 + ], + [ + "__memmove_avx512_unaligned_erms", + 0.05640157924421884 + ], + [ + "::from_utf8_lossy", + 0.05640157924421884 + ] + ], + "sampled_instructions": 886505319, + "binary_sha256": "46bc863ba75b118f1193d2a3fb4cfc22c1deb50ab23a56f58b573b217b62bb50" + }, + "thenable": { + "iterations": 20000, + "instructions": [ + 365635735, + 365634266, + 365626025 + ], + "median": 365634266, + "get_inclusive_percent": 15.500685871056241, + "get_leaf_percent_of_program": [ + [ + "perry_runtime::object::field_get_set::get_field_by_name_tail::get_field_by_name_object_tail", + 1.7832647462277091 + ], + [ + "js_object_get_field_by_name", + 1.7832647462277091 + ], + [ + "perry_runtime::object::object_ops::keys_array::own_key_present", + 1.0973936899862826 + ], + [ + "js_dynamic_object_get_property", + 0.9602194787379973 + ], + [ + "perry_runtime::object::class_meta_registry::get_parent_class_id", + 0.823045267489712 + ], + [ + "core::str::converts::from_utf8", + 0.6858710562414266 + ], + [ + "perry_runtime::buffer::header::is_registered_buffer", + 0.411522633744856 + ], + [ + "perry_runtime::date::is_date_cell_addr", + 0.411522633744856 + ], + [ + ">>>::with::<::drop::{closure#0}, ()>", + 0.411522633744856 + ], + [ + "perry_runtime::object::class_registry::parent_static::is_class_object_ptr", + 0.411522633744856 + ], + [ + "perry_runtime::object::field_get_set::has_property::closure_dynamic_prop_by_key", + 0.411522633744856 + ], + [ + "perry_runtime::object::keys_lookup::keys_array_dense_slots", + 0.411522633744856 + ], + [ + "perry_runtime::object::object_keys_array", + 0.411522633744856 + ], + [ + "perry_runtime::temporal::is_temporal_cell_addr", + 0.411522633744856 + ], + [ + "perry_runtime::map::is_registered_map", + 0.27434842249657065 + ], + [ + "perry_runtime::object::field_get_set::enumeration::canonical_array_index", + 0.27434842249657065 + ], + [ + "perry_runtime::object::field_get_set::get_field_by_name_async::async_resource_property", + 0.27434842249657065 + ], + [ + "perry_runtime::arena::allocators::arena_alloc", + 0.27434842249657065 + ], + [ + ">>>>::read", + 0.27434842249657065 + ], + [ + "perry_runtime::async_hooks::is_async_resource_handle", + 0.27434842249657065 + ] + ], + "sampled_instructions": 364502187, + "binary_sha256": "d4fba118c00d48a015b0cd5b29238b10cc85306cdf7202f95f456e58c7366791" + }, + "tojson": { + "iterations": 100000, + "instructions": [ + 742647158, + 742646366, + 742646720 + ], + "median": 742646720, + "get_inclusive_percent": 50.707070707070706, + "get_leaf_percent_of_program": [ + [ + "perry_runtime::object::class_meta_registry::get_parent_class_id", + 6.734006734006734 + ], + [ + "perry_runtime::object::field_get_set::get_field_by_name_tail::get_field_by_name_object_tail", + 6.127946127946128 + ], + [ + "js_object_get_field_by_name", + 5.3872053872053876 + ], + [ + "perry_runtime::object::class_registry::state::class_decl_prototype_object", + 3.771043771043771 + ], + [ + "perry_runtime::closure::dynamic_props::is_closure_ptr", + 3.2323232323232323 + ], + [ + "::copy_of_key", + 3.0976430976430978 + ], + [ + "perry_runtime::async_hooks::is_async_resource_handle", + 2.356902356902357 + ], + [ + "perry_runtime::object::class_registry::prototype_objects::resolve_proto_chain_field_inner", + 2.2222222222222223 + ], + [ + "perry_runtime::object::class_registry::parent_static::is_class_object_ptr", + 1.8181818181818181 + ], + [ + "core::str::converts::from_utf8", + 1.6161616161616161 + ], + [ + "perry_runtime::buffer::header::is_registered_buffer", + 1.5488215488215489 + ], + [ + "perry_runtime::temporal::is_temporal_cell_addr", + 1.2794612794612794 + ], + [ + "perry_runtime::date::is_date_cell_addr", + 1.2121212121212122 + ], + [ + "perry_runtime::object::arguments::is_arguments_object", + 0.9427609427609428 + ], + [ + "::hash_one::<&i32>", + 0.8754208754208754 + ], + [ + "perry_runtime::async_hooks::try_async_resource_property_dispatch", + 0.8754208754208754 + ], + [ + "trunc", + 0.8080808080808081 + ], + [ + "perry_runtime::object::object_keys_array", + 0.8080808080808081 + ], + [ + "perry_runtime::typedarray_props::typed_array_addr_from_value", + 0.8080808080808081 + ], + [ + "perry_runtime::object::field_get_set::has_property::closure_dynamic_prop_by_key", + 0.8080808080808081 + ] + ], + "sampled_instructions": 742504455, + "binary_sha256": "fbde7ea49d88679942de1296eadd260ba780e8b352e1794567d17d428f3de268" + } +} diff --git a/benchmarks/native_property_get/evidence/candidate-build.json b/benchmarks/native_property_get/evidence/candidate-build.json new file mode 100644 index 0000000000..c2ccb129d7 --- /dev/null +++ b/benchmarks/native_property_get/evidence/candidate-build.json @@ -0,0 +1,17 @@ +{ + "commit": "9fda98df68d9fac3c08b2385fae007aa9f5278df", + "node": "v26.5.1", + "source_patch_sha256": "3311791400a786a77d4c0a612dfdeffe16834ddaad24abdd15ec9783563bbc8d", + "artifacts": { + "perry": "0a1de70b4120ca8a578495031cd47c23f368dd1af87ba740bee1e95422353641", + "libperry_runtime.a": "ee64fc51d1cf58565c79edd88e0c449888920df0fddf69048ae7e0e2e612e0d1", + "libperry_stdlib.a": "66b042e43daa18f116074ce891ddd93ac8cff8f4055dfb11bbb3a69291b665ed" + }, + "probes": { + "reflect_own": "be47a8c4951b74b7eb1303afd1b02ed929b954f485cf7f0d10194968c1f29cba", + "reflect_proto": "45e82bd599efdcff78032103ec31d87ad087afdf7bdc7438b4abf91142b8b2c3", + "iterator": "da8ac63a3918d5aede6185b823e94d896dab89327a2b2718278a8355df1240a4", + "thenable": "f418d2de753b6baef4d2cad9fba2dd7d41d44e460f9da74fad84778b0c6374ec", + "tojson": "139928fda9c0e8f2c2d1a38feb4ac8f18ef14d8dad13114b325b06650e2155e0" + } +} diff --git a/benchmarks/native_property_get/evidence/candidate.json b/benchmarks/native_property_get/evidence/candidate.json new file mode 100644 index 0000000000..1a04a75ddb --- /dev/null +++ b/benchmarks/native_property_get/evidence/candidate.json @@ -0,0 +1,280 @@ +{ + "reflect_own": { + "iterations": 200000, + "instructions": [ + 143386174, + 143385957, + 143385319 + ], + "median": 143385957, + "get_inclusive_percent": 97.2027972027972, + "get_leaf_percent_of_program": [ + [ + "perry_runtime::object::class_registry::class_meta::is_anon_shape_class_id", + 44.40559440559441 + ], + [ + "perry_runtime::object::native_get::try_data_get_bytes", + 36.01398601398601 + ], + [ + "perry_runtime::object::keys_lookup::keys_array_dense_slots", + 6.643356643356643 + ], + [ + "core::str::converts::from_utf8", + 4.195804195804196 + ], + [ + "perry_runtime::object::keys_lookup::keys_find_slot_by_bytes", + 2.4475524475524475 + ], + [ + "perry_runtime::object::field_get_set::accessors::object_field_at_with_live", + 2.097902097902098 + ], + [ + "js_reflect_get", + 0.6993006993006993 + ], + [ + ">>,)>>::call_once", + 0.6993006993006993 + ] + ], + "sampled_instructions": 143000858, + "binary_sha256": "827154821308f72122097f731f94852b498ea02ca8579442536fef79a1d05e3b" + }, + "reflect_proto": { + "iterations": 200000, + "instructions": [ + 305995648, + 305995727, + 305995519 + ], + "median": 305995648, + "get_inclusive_percent": 99.34533551554829, + "get_leaf_percent_of_program": [ + [ + "perry_runtime::object::class_registry::state::class_decl_prototype_object", + 31.42389525368249 + ], + [ + "perry_runtime::object::native_get::try_data_get_bytes", + 24.713584288052374 + ], + [ + "perry_runtime::object::class_registry::class_meta::is_anon_shape_class_id", + 11.129296235679215 + ], + [ + "perry_runtime::object::class_registry::prototype_objects::class_prototype_object", + 8.51063829787234 + ], + [ + "::hash_one::<&i32>", + 6.219312602291326 + ], + [ + "perry_runtime::object::arguments::is_arguments_object", + 4.4189852700491 + ], + [ + "perry_runtime::object::keys_lookup::keys_array_dense_slots", + 4.4189852700491 + ], + [ + "perry_runtime::object::keys_lookup::keys_find_slot_by_bytes", + 2.618657937806874 + ], + [ + "core::str::converts::from_utf8", + 2.127659574468085 + ], + [ + " as core::hash::Hasher>::write", + 1.4729950900163666 + ], + [ + "js_reflect_get", + 1.309328968903437 + ], + [ + ">>,)>>::call_once", + 0.8183306055646481 + ], + [ + "perry_runtime::object::field_get_set::accessors::object_field_at_with_live", + 0.16366612111292964 + ] + ], + "sampled_instructions": 305501833, + "binary_sha256": "b00e40d520ad8dad3928a2211df4dfee7d12a7d63f49a5fe8036792a50cc3f5b" + }, + "iterator": { + "iterations": 200000, + "instructions": [ + 886162527, + 886162328, + 886163743 + ], + "median": 886162527, + "get_inclusive_percent": 0.7900677200902935, + "get_leaf_percent_of_program": [ + [ + "perry_runtime::url::search_params::shape_is_url_search_params", + 0.11286681715575621 + ], + [ + "::from_utf8_lossy", + 0.056433408577878104 + ], + [ + "perry_runtime::typedarray_props::typed_array_addr_from_value", + 0.056433408577878104 + ], + [ + "perry_runtime::object::keys_lookup::keys_find_slot_by_bytes", + 0.056433408577878104 + ], + [ + "perry_runtime::object::prototype_chain::meta_capable_object", + 0.056433408577878104 + ], + [ + "perry_runtime::object::field_get_set::prototype_override::inherited_field_if_overridden", + 0.056433408577878104 + ], + [ + "core::str::converts::from_utf8", + 0.056433408577878104 + ], + [ + "perry_runtime::object::class_meta_registry::get_parent_class_id", + 0.056433408577878104 + ], + [ + "perry_runtime::object::keys_lookup::keys_array_dense_slots", + 0.056433408577878104 + ], + [ + "perry_runtime::buffer::header::is_registered_buffer", + 0.056433408577878104 + ], + [ + "js_object_get_field_by_name", + 0.056433408577878104 + ], + [ + "perry_runtime::object::prototype_chain::object_static_prototype", + 0.056433408577878104 + ], + [ + "perry_runtime::object::field_get_set::accessors::prototype_property_value_with_guard", + 0.056433408577878104 + ] + ], + "sampled_instructions": 886005316, + "binary_sha256": "9a1a0d4c17406b127ddc54655ca55cb7d0af40faf98968c231da0bc927105931" + }, + "thenable": { + "iterations": 20000, + "instructions": [ + 320438913, + 320443434, + 320441221 + ], + "median": 320441221, + "get_inclusive_percent": 2.6604068857589986, + "get_leaf_percent_of_program": [ + [ + "perry_runtime::object::class_registry::class_meta::is_anon_shape_class_id", + 1.5649452269170578 + ], + [ + "perry_runtime::object::native_get::try_data_get_bytes", + 0.9389671361502347 + ], + [ + "perry_runtime::object::keys_lookup::keys_find_slot_by_bytes", + 0.1564945226917058 + ] + ], + "sampled_instructions": 319501917, + "binary_sha256": "abd61256d1ad0cf5d612c00a87e32e0d56ad833138e75afdcc728e2fa41fdfd9" + }, + "tojson": { + "iterations": 100000, + "instructions": [ + 423858278, + 423858072, + 423858284 + ], + "median": 423858278, + "get_inclusive_percent": 28.217237308146398, + "get_leaf_percent_of_program": [ + [ + "perry_runtime::object::class_registry::state::class_decl_prototype_object", + 7.319952774498229 + ], + [ + "perry_runtime::object::native_get::try_data_get_bytes", + 5.076741440377804 + ], + [ + "perry_runtime::object::class_registry::class_meta::is_anon_shape_class_id", + 5.076741440377804 + ], + [ + "perry_runtime::object::class_registry::prototype_objects::class_prototype_object", + 2.243211334120425 + ], + [ + "perry_runtime::perf_hooks::is_perf_entry_object", + 2.125147579693034 + ], + [ + "core::str::converts::from_utf8", + 2.0070838252656436 + ], + [ + "::hash_one::<&i32>", + 1.770956316410862 + ], + [ + "js_reflect_get", + 0.9445100354191264 + ], + [ + "perry_runtime::object::keys_lookup::keys_array_dense_slots", + 0.5903187721369539 + ], + [ + "perry_runtime::object::keys_lookup::keys_find_slot_by_bytes", + 0.2361275088547816 + ], + [ + "perry_runtime::object::field_get_set::accessors::object_field_at_with_live", + 0.2361275088547816 + ], + [ + ">>,)>>::call_once", + 0.2361275088547816 + ], + [ + "perry_runtime::object::arguments::is_arguments_object", + 0.1180637544273908 + ], + [ + "__memcmp_evex_movbe", + 0.1180637544273908 + ], + [ + " as core::hash::Hasher>::write", + 0.1180637544273908 + ] + ], + "sampled_instructions": 423502541, + "binary_sha256": "8772b57dfb1695585556246aca33a17550b3051f7e679a947ae43b4bb22fc8d7" + } +} diff --git a/benchmarks/native_property_get/evidence/clippy-diff.json b/benchmarks/native_property_get/evidence/clippy-diff.json new file mode 100644 index 0000000000..f68ac0546f --- /dev/null +++ b/benchmarks/native_property_get/evidence/clippy-diff.json @@ -0,0 +1,8 @@ +{ + "comparison": "9fda98df68 vs candidate; cargo clippy -j4 -p perry-runtime --all-targets --message-format=json", + "normalization": "level, code, message, primary filenames; ignores shifted line numbers", + "baseline_diagnostics": 1987, + "candidate_diagnostics": 1987, + "new_diagnostics": [], + "removed_diagnostics": [] +} diff --git a/benchmarks/native_property_get/evidence/clippy-logs.tar.gz b/benchmarks/native_property_get/evidence/clippy-logs.tar.gz new file mode 100644 index 0000000000..0d107143f9 Binary files /dev/null and b/benchmarks/native_property_get/evidence/clippy-logs.tar.gz differ diff --git a/benchmarks/native_property_get/evidence/fault-logs.tar.gz b/benchmarks/native_property_get/evidence/fault-logs.tar.gz new file mode 100644 index 0000000000..db00b7a9fe Binary files /dev/null and b/benchmarks/native_property_get/evidence/fault-logs.tar.gz differ diff --git a/benchmarks/native_property_get/evidence/faults.json b/benchmarks/native_property_get/evidence/faults.json new file mode 100644 index 0000000000..b76475a1b2 --- /dev/null +++ b/benchmarks/native_property_get/evidence/faults.json @@ -0,0 +1,20 @@ +[ + { + "mutation": "fast_path_removed", + "test": "object::native_get::tests::own_data_is_served_and_matches_forced_slow_for_all_value_kinds", + "exit_code": 101, + "assertion_failed": true + }, + { + "mutation": "accessor_guard_removed", + "test": "object::native_get::tests::accessor_bloom_guard_preserves_own_and_inherited_getter_calls", + "exit_code": 101, + "assertion_failed": true + }, + { + "mutation": "exotic_expando_ignored", + "test": "object::native_get::tests::regexp_expandos_and_accessors_remain_on_the_exotic_path", + "exit_code": 101, + "assertion_failed": true + } +] diff --git a/benchmarks/native_property_get/evidence/gc-base-runtime.sha256 b/benchmarks/native_property_get/evidence/gc-base-runtime.sha256 new file mode 100644 index 0000000000..51a2460b2c --- /dev/null +++ b/benchmarks/native_property_get/evidence/gc-base-runtime.sha256 @@ -0,0 +1,3 @@ +fa2df7f3d54bd25eab5e730c3e9ba27549ec980cb146f3ff35c946bd6f8dabcf /root/native-property-get/base-runtime/libperry_runtime.a +70e7bb414429841a2c5b6b99d8013eb7943b5f8f2ca92fea62d77288569d078b /root/native-property-get/base-runtime/libperry_stdlib.a +57778c1bc33a8367ef6e8d0ea0993e61c2d387020a13ed0bc802a90eceeee09e /root/native-property-get/base-runtime/perry diff --git a/benchmarks/native_property_get/evidence/gc-ratchet-ab.json b/benchmarks/native_property_get/evidence/gc-ratchet-ab.json new file mode 100644 index 0000000000..e7d214604b --- /dev/null +++ b/benchmarks/native_property_get/evidence/gc-ratchet-ab.json @@ -0,0 +1,1016 @@ +{ + "base_commit": "9fda98df68d9fac3c08b2385fae007aa9f5278df", + "profile": "shared_ci", + "correctness": "14/14 match Node on base and candidate", + "all_gated_medians_identical": true, + "rows": [ + { + "probe": "01_nursery_churn", + "metric": "heap_used_bytes", + "base_median": 438744, + "candidate_median": 438744, + "base_spread": 0, + "candidate_spread": 0 + }, + { + "probe": "01_nursery_churn", + "metric": "heap_total_bytes", + "base_median": 14680064, + "candidate_median": 14680064, + "base_spread": 0, + "candidate_spread": 0 + }, + { + "probe": "01_nursery_churn", + "metric": "minor_cycles", + "base_median": 1, + "candidate_median": 1, + "base_spread": 0, + "candidate_spread": 0 + }, + { + "probe": "01_nursery_churn", + "metric": "step_cycles", + "base_median": 1, + "candidate_median": 1, + "base_spread": 0, + "candidate_spread": 0 + }, + { + "probe": "01_nursery_churn", + "metric": "copied_objects", + "base_median": 6690, + "candidate_median": 6690, + "base_spread": 0, + "candidate_spread": 0 + }, + { + "probe": "01_nursery_churn", + "metric": "copied_bytes", + "base_median": 466392, + "candidate_median": 466392, + "base_spread": 0, + "candidate_spread": 0 + }, + { + "probe": "01_nursery_churn", + "metric": "promoted_objects", + "base_median": 0, + "candidate_median": 0, + "base_spread": 0, + "candidate_spread": 0 + }, + { + "probe": "01_nursery_churn", + "metric": "promoted_bytes", + "base_median": 0, + "candidate_median": 0, + "base_spread": 0, + "candidate_spread": 0 + }, + { + "probe": "01_nursery_churn", + "metric": "freed_bytes", + "base_median": 11067816, + "candidate_median": 11067816, + "base_spread": 0, + "candidate_spread": 0 + }, + { + "probe": "02_survivor_promotion", + "metric": "heap_used_bytes", + "base_median": 2562832, + "candidate_median": 2562832, + "base_spread": 0, + "candidate_spread": 0 + }, + { + "probe": "02_survivor_promotion", + "metric": "heap_total_bytes", + "base_median": 15728640, + "candidate_median": 15728640, + "base_spread": 0, + "candidate_spread": 0 + }, + { + "probe": "02_survivor_promotion", + "metric": "minor_cycles", + "base_median": 1, + "candidate_median": 1, + "base_spread": 0, + "candidate_spread": 0 + }, + { + "probe": "02_survivor_promotion", + "metric": "step_cycles", + "base_median": 1, + "candidate_median": 1, + "base_spread": 0, + "candidate_spread": 0 + }, + { + "probe": "02_survivor_promotion", + "metric": "copied_objects", + "base_median": 37366, + "candidate_median": 37366, + "base_spread": 0, + "candidate_spread": 0 + }, + { + "probe": "02_survivor_promotion", + "metric": "copied_bytes", + "base_median": 1690264, + "candidate_median": 1690264, + "base_spread": 0, + "candidate_spread": 0 + }, + { + "probe": "02_survivor_promotion", + "metric": "promoted_objects", + "base_median": 0, + "candidate_median": 0, + "base_spread": 0, + "candidate_spread": 0 + }, + { + "probe": "02_survivor_promotion", + "metric": "promoted_bytes", + "base_median": 0, + "candidate_median": 0, + "base_spread": 0, + "candidate_spread": 0 + }, + { + "probe": "02_survivor_promotion", + "metric": "freed_bytes", + "base_median": 8795408, + "candidate_median": 8795408, + "base_spread": 0, + "candidate_spread": 0 + }, + { + "probe": "03_cross_gen_writes", + "metric": "heap_used_bytes", + "base_median": 528, + "candidate_median": 528, + "base_spread": 0, + "candidate_spread": 0 + }, + { + "probe": "03_cross_gen_writes", + "metric": "heap_total_bytes", + "base_median": 12582912, + "candidate_median": 12582912, + "base_spread": 0, + "candidate_spread": 0 + }, + { + "probe": "03_cross_gen_writes", + "metric": "minor_cycles", + "base_median": 1, + "candidate_median": 1, + "base_spread": 0, + "candidate_spread": 0 + }, + { + "probe": "03_cross_gen_writes", + "metric": "step_cycles", + "base_median": 1, + "candidate_median": 1, + "base_spread": 0, + "candidate_spread": 0 + }, + { + "probe": "03_cross_gen_writes", + "metric": "copied_objects", + "base_median": 4107, + "candidate_median": 4107, + "base_spread": 0, + "candidate_spread": 0 + }, + { + "probe": "03_cross_gen_writes", + "metric": "copied_bytes", + "base_median": 197056, + "candidate_median": 197056, + "base_spread": 0, + "candidate_spread": 0 + }, + { + "probe": "03_cross_gen_writes", + "metric": "promoted_objects", + "base_median": 0, + "candidate_median": 0, + "base_spread": 0, + "candidate_spread": 0 + }, + { + "probe": "03_cross_gen_writes", + "metric": "promoted_bytes", + "base_median": 0, + "candidate_median": 0, + "base_spread": 0, + "candidate_spread": 0 + }, + { + "probe": "03_cross_gen_writes", + "metric": "freed_bytes", + "base_median": 9240008, + "candidate_median": 9240008, + "base_spread": 0, + "candidate_spread": 0 + }, + { + "probe": "04_dead_after_deep_stack", + "metric": "heap_used_bytes", + "base_median": 536, + "candidate_median": 536, + "base_spread": 0, + "candidate_spread": 0 + }, + { + "probe": "04_dead_after_deep_stack", + "metric": "heap_total_bytes", + "base_median": 18874368, + "candidate_median": 18874368, + "base_spread": 0, + "candidate_spread": 0 + }, + { + "probe": "04_dead_after_deep_stack", + "metric": "minor_cycles", + "base_median": 7, + "candidate_median": 7, + "base_spread": 0, + "candidate_spread": 0 + }, + { + "probe": "04_dead_after_deep_stack", + "metric": "step_cycles", + "base_median": 7, + "candidate_median": 7, + "base_spread": 0, + "candidate_spread": 0 + }, + { + "probe": "04_dead_after_deep_stack", + "metric": "copied_objects", + "base_median": 2666, + "candidate_median": 2666, + "base_spread": 0, + "candidate_spread": 0 + }, + { + "probe": "04_dead_after_deep_stack", + "metric": "copied_bytes", + "base_median": 111336, + "candidate_median": 111336, + "base_spread": 0, + "candidate_spread": 0 + }, + { + "probe": "04_dead_after_deep_stack", + "metric": "promoted_objects", + "base_median": 682, + "candidate_median": 682, + "base_spread": 0, + "candidate_spread": 0 + }, + { + "probe": "04_dead_after_deep_stack", + "metric": "promoted_bytes", + "base_median": 27320, + "candidate_median": 27320, + "base_spread": 0, + "candidate_spread": 0 + }, + { + "probe": "04_dead_after_deep_stack", + "metric": "freed_bytes", + "base_median": 76493952, + "candidate_median": 76493952, + "base_spread": 0, + "candidate_spread": 0 + }, + { + "probe": "05_closure_capture", + "metric": "heap_used_bytes", + "base_median": 544, + "candidate_median": 544, + "base_spread": 0, + "candidate_spread": 0 + }, + { + "probe": "05_closure_capture", + "metric": "heap_total_bytes", + "base_median": 14680064, + "candidate_median": 14680064, + "base_spread": 0, + "candidate_spread": 0 + }, + { + "probe": "05_closure_capture", + "metric": "minor_cycles", + "base_median": 3, + "candidate_median": 3, + "base_spread": 0, + "candidate_spread": 0 + }, + { + "probe": "05_closure_capture", + "metric": "step_cycles", + "base_median": 3, + "candidate_median": 3, + "base_spread": 0, + "candidate_spread": 0 + }, + { + "probe": "05_closure_capture", + "metric": "copied_objects", + "base_median": 14285, + "candidate_median": 14285, + "base_spread": 0, + "candidate_spread": 0 + }, + { + "probe": "05_closure_capture", + "metric": "copied_bytes", + "base_median": 534720, + "candidate_median": 534720, + "base_spread": 0, + "candidate_spread": 0 + }, + { + "probe": "05_closure_capture", + "metric": "promoted_objects", + "base_median": 6154, + "candidate_median": 6154, + "base_spread": 0, + "candidate_spread": 0 + }, + { + "probe": "05_closure_capture", + "metric": "promoted_bytes", + "base_median": 221624, + "candidate_median": 221624, + "base_spread": 0, + "candidate_spread": 0 + }, + { + "probe": "05_closure_capture", + "metric": "freed_bytes", + "base_median": 27897872, + "candidate_median": 27897872, + "base_spread": 0, + "candidate_spread": 0 + }, + { + "probe": "06_string_retention", + "metric": "heap_used_bytes", + "base_median": 1072, + "candidate_median": 1072, + "base_spread": 0, + "candidate_spread": 0 + }, + { + "probe": "06_string_retention", + "metric": "heap_total_bytes", + "base_median": 22020096, + "candidate_median": 22020096, + "base_spread": 0, + "candidate_spread": 0 + }, + { + "probe": "06_string_retention", + "metric": "minor_cycles", + "base_median": 4, + "candidate_median": 4, + "base_spread": 0, + "candidate_spread": 0 + }, + { + "probe": "06_string_retention", + "metric": "step_cycles", + "base_median": 4, + "candidate_median": 4, + "base_spread": 0, + "candidate_spread": 0 + }, + { + "probe": "06_string_retention", + "metric": "copied_objects", + "base_median": 270, + "candidate_median": 270, + "base_spread": 0, + "candidate_spread": 0 + }, + { + "probe": "06_string_retention", + "metric": "copied_bytes", + "base_median": 70144, + "candidate_median": 70144, + "base_spread": 0, + "candidate_spread": 0 + }, + { + "probe": "06_string_retention", + "metric": "promoted_objects", + "base_median": 14, + "candidate_median": 14, + "base_spread": 0, + "candidate_spread": 0 + }, + { + "probe": "06_string_retention", + "metric": "promoted_bytes", + "base_median": 1072, + "candidate_median": 1072, + "base_spread": 0, + "candidate_spread": 0 + }, + { + "probe": "06_string_retention", + "metric": "freed_bytes", + "base_median": 68136816, + "candidate_median": 68136816, + "base_spread": 0, + "candidate_spread": 0 + }, + { + "probe": "07_array_grow_evacuate", + "metric": "heap_used_bytes", + "base_median": 1211232, + "candidate_median": 1211232, + "base_spread": 0, + "candidate_spread": 0 + }, + { + "probe": "07_array_grow_evacuate", + "metric": "heap_total_bytes", + "base_median": 25165824, + "candidate_median": 25165824, + "base_spread": 0, + "candidate_spread": 0 + }, + { + "probe": "07_array_grow_evacuate", + "metric": "minor_cycles", + "base_median": 5, + "candidate_median": 5, + "base_spread": 0, + "candidate_spread": 0 + }, + { + "probe": "07_array_grow_evacuate", + "metric": "step_cycles", + "base_median": 5, + "candidate_median": 5, + "base_spread": 0, + "candidate_spread": 0 + }, + { + "probe": "07_array_grow_evacuate", + "metric": "copied_objects", + "base_median": 6702, + "candidate_median": 6702, + "base_spread": 0, + "candidate_spread": 0 + }, + { + "probe": "07_array_grow_evacuate", + "metric": "copied_bytes", + "base_median": 2636392, + "candidate_median": 2636392, + "base_spread": 0, + "candidate_spread": 0 + }, + { + "probe": "07_array_grow_evacuate", + "metric": "promoted_objects", + "base_median": 6154, + "candidate_median": 6154, + "base_spread": 0, + "candidate_spread": 0 + }, + { + "probe": "07_array_grow_evacuate", + "metric": "promoted_bytes", + "base_median": 841048, + "candidate_median": 841048, + "base_spread": 0, + "candidate_spread": 0 + }, + { + "probe": "07_array_grow_evacuate", + "metric": "freed_bytes", + "base_median": 83570704, + "candidate_median": 83570704, + "base_spread": 0, + "candidate_spread": 0 + }, + { + "probe": "08_map_set_sidetables", + "metric": "heap_used_bytes", + "base_median": 688, + "candidate_median": 688, + "base_spread": 0, + "candidate_spread": 0 + }, + { + "probe": "08_map_set_sidetables", + "metric": "heap_total_bytes", + "base_median": 14680064, + "candidate_median": 14680064, + "base_spread": 0, + "candidate_spread": 0 + }, + { + "probe": "08_map_set_sidetables", + "metric": "minor_cycles", + "base_median": 6, + "candidate_median": 6, + "base_spread": 0, + "candidate_spread": 0 + }, + { + "probe": "08_map_set_sidetables", + "metric": "step_cycles", + "base_median": 6, + "candidate_median": 6, + "base_spread": 0, + "candidate_spread": 0 + }, + { + "probe": "08_map_set_sidetables", + "metric": "copied_objects", + "base_median": 2461, + "candidate_median": 2461, + "base_spread": 0, + "candidate_spread": 0 + }, + { + "probe": "08_map_set_sidetables", + "metric": "copied_bytes", + "base_median": 98472, + "candidate_median": 98472, + "base_spread": 0, + "candidate_spread": 0 + }, + { + "probe": "08_map_set_sidetables", + "metric": "promoted_objects", + "base_median": 14, + "candidate_median": 14, + "base_spread": 0, + "candidate_spread": 0 + }, + { + "probe": "08_map_set_sidetables", + "metric": "promoted_bytes", + "base_median": 592, + "candidate_median": 592, + "base_spread": 0, + "candidate_spread": 0 + }, + { + "probe": "08_map_set_sidetables", + "metric": "freed_bytes", + "base_median": 56598904, + "candidate_median": 56598904, + "base_spread": 0, + "candidate_spread": 0 + }, + { + "probe": "09_try_catch_roots", + "metric": "heap_used_bytes", + "base_median": 443520, + "candidate_median": 443520, + "base_spread": 0, + "candidate_spread": 0 + }, + { + "probe": "09_try_catch_roots", + "metric": "heap_total_bytes", + "base_median": 14680064, + "candidate_median": 14680064, + "base_spread": 0, + "candidate_spread": 0 + }, + { + "probe": "09_try_catch_roots", + "metric": "minor_cycles", + "base_median": 1, + "candidate_median": 1, + "base_spread": 0, + "candidate_spread": 0 + }, + { + "probe": "09_try_catch_roots", + "metric": "step_cycles", + "base_median": 1, + "candidate_median": 1, + "base_spread": 0, + "candidate_spread": 0 + }, + { + "probe": "09_try_catch_roots", + "metric": "copied_objects", + "base_median": 6673, + "candidate_median": 6673, + "base_spread": 0, + "candidate_spread": 0 + }, + { + "probe": "09_try_catch_roots", + "metric": "copied_bytes", + "base_median": 462024, + "candidate_median": 462024, + "base_spread": 0, + "candidate_spread": 0 + }, + { + "probe": "09_try_catch_roots", + "metric": "promoted_objects", + "base_median": 0, + "candidate_median": 0, + "base_spread": 0, + "candidate_spread": 0 + }, + { + "probe": "09_try_catch_roots", + "metric": "promoted_bytes", + "base_median": 0, + "candidate_median": 0, + "base_spread": 0, + "candidate_spread": 0 + }, + { + "probe": "09_try_catch_roots", + "metric": "freed_bytes", + "base_median": 10023568, + "candidate_median": 10023568, + "base_spread": 0, + "candidate_spread": 0 + }, + { + "probe": "10_store_receiver_across_alloc", + "metric": "heap_used_bytes", + "base_median": 446776, + "candidate_median": 446776, + "base_spread": 0, + "candidate_spread": 0 + }, + { + "probe": "10_store_receiver_across_alloc", + "metric": "heap_total_bytes", + "base_median": 22020096, + "candidate_median": 22020096, + "base_spread": 0, + "candidate_spread": 0 + }, + { + "probe": "10_store_receiver_across_alloc", + "metric": "minor_cycles", + "base_median": 8, + "candidate_median": 8, + "base_spread": 0, + "candidate_spread": 0 + }, + { + "probe": "10_store_receiver_across_alloc", + "metric": "step_cycles", + "base_median": 8, + "candidate_median": 8, + "base_spread": 0, + "candidate_spread": 0 + }, + { + "probe": "10_store_receiver_across_alloc", + "metric": "copied_objects", + "base_median": 14625, + "candidate_median": 14625, + "base_spread": 0, + "candidate_spread": 0 + }, + { + "probe": "10_store_receiver_across_alloc", + "metric": "copied_bytes", + "base_median": 787792, + "candidate_median": 787792, + "base_spread": 0, + "candidate_spread": 0 + }, + { + "probe": "10_store_receiver_across_alloc", + "metric": "promoted_objects", + "base_median": 6107, + "candidate_median": 6107, + "base_spread": 0, + "candidate_spread": 0 + }, + { + "probe": "10_store_receiver_across_alloc", + "metric": "promoted_bytes", + "base_median": 446368, + "candidate_median": 446368, + "base_spread": 0, + "candidate_spread": 0 + }, + { + "probe": "10_store_receiver_across_alloc", + "metric": "freed_bytes", + "base_median": 88640584, + "candidate_median": 88640584, + "base_spread": 0, + "candidate_spread": 0 + }, + { + "probe": "11_collect_at_depth", + "metric": "heap_used_bytes", + "base_median": 444720, + "candidate_median": 444720, + "base_spread": 0, + "candidate_spread": 0 + }, + { + "probe": "11_collect_at_depth", + "metric": "heap_total_bytes", + "base_median": 12582912, + "candidate_median": 12582912, + "base_spread": 0, + "candidate_spread": 0 + }, + { + "probe": "11_collect_at_depth", + "metric": "minor_cycles", + "base_median": 1, + "candidate_median": 1, + "base_spread": 0, + "candidate_spread": 0 + }, + { + "probe": "11_collect_at_depth", + "metric": "step_cycles", + "base_median": 1, + "candidate_median": 1, + "base_spread": 0, + "candidate_spread": 0 + }, + { + "probe": "11_collect_at_depth", + "metric": "copied_objects", + "base_median": 6686, + "candidate_median": 6686, + "base_spread": 0, + "candidate_spread": 0 + }, + { + "probe": "11_collect_at_depth", + "metric": "copied_bytes", + "base_median": 462544, + "candidate_median": 462544, + "base_spread": 0, + "candidate_spread": 0 + }, + { + "probe": "11_collect_at_depth", + "metric": "promoted_objects", + "base_median": 0, + "candidate_median": 0, + "base_spread": 0, + "candidate_spread": 0 + }, + { + "probe": "11_collect_at_depth", + "metric": "promoted_bytes", + "base_median": 0, + "candidate_median": 0, + "base_spread": 0, + "candidate_spread": 0 + }, + { + "probe": "11_collect_at_depth", + "metric": "freed_bytes", + "base_median": 8974576, + "candidate_median": 8974576, + "base_spread": 0, + "candidate_spread": 0 + }, + { + "probe": "12_large_live_set", + "metric": "heap_used_bytes", + "base_median": 569448, + "candidate_median": 569448, + "base_spread": 0, + "candidate_spread": 0 + }, + { + "probe": "12_large_live_set", + "metric": "heap_total_bytes", + "base_median": 74448896, + "candidate_median": 74448896, + "base_spread": 0, + "candidate_spread": 0 + }, + { + "probe": "12_large_live_set", + "metric": "minor_cycles", + "base_median": 5, + "candidate_median": 5, + "base_spread": 0, + "candidate_spread": 0 + }, + { + "probe": "12_large_live_set", + "metric": "step_cycles", + "base_median": 5, + "candidate_median": 5, + "base_spread": 0, + "candidate_spread": 0 + }, + { + "probe": "12_large_live_set", + "metric": "copied_objects", + "base_median": 104050, + "candidate_median": 104050, + "base_spread": 0, + "candidate_spread": 0 + }, + { + "probe": "12_large_live_set", + "metric": "copied_bytes", + "base_median": 4162008, + "candidate_median": 4162008, + "base_spread": 0, + "candidate_spread": 0 + }, + { + "probe": "12_large_live_set", + "metric": "promoted_objects", + "base_median": 530030, + "candidate_median": 530030, + "base_spread": 0, + "candidate_spread": 0 + }, + { + "probe": "12_large_live_set", + "metric": "promoted_bytes", + "base_median": 21201208, + "candidate_median": 21201208, + "base_spread": 0, + "candidate_spread": 0 + }, + { + "probe": "12_large_live_set", + "metric": "freed_bytes", + "base_median": 63732784, + "candidate_median": 63732784, + "base_spread": 0, + "candidate_spread": 0 + }, + { + "probe": "13_large_eden_survivors", + "metric": "heap_used_bytes", + "base_median": 444976, + "candidate_median": 444976, + "base_spread": 0, + "candidate_spread": 0 + }, + { + "probe": "13_large_eden_survivors", + "metric": "heap_total_bytes", + "base_median": 61865984, + "candidate_median": 61865984, + "base_spread": 0, + "candidate_spread": 0 + }, + { + "probe": "13_large_eden_survivors", + "metric": "minor_cycles", + "base_median": 3, + "candidate_median": 3, + "base_spread": 0, + "candidate_spread": 0 + }, + { + "probe": "13_large_eden_survivors", + "metric": "step_cycles", + "base_median": 3, + "candidate_median": 3, + "base_spread": 0, + "candidate_spread": 0 + }, + { + "probe": "13_large_eden_survivors", + "metric": "copied_objects", + "base_median": 382868, + "candidate_median": 382868, + "base_spread": 0, + "candidate_spread": 0 + }, + { + "probe": "13_large_eden_survivors", + "metric": "copied_bytes", + "base_median": 21804904, + "candidate_median": 21804904, + "base_spread": 0, + "candidate_spread": 0 + }, + { + "probe": "13_large_eden_survivors", + "metric": "promoted_objects", + "base_median": 387152, + "candidate_median": 387152, + "base_spread": 0, + "candidate_spread": 0 + }, + { + "probe": "13_large_eden_survivors", + "metric": "promoted_bytes", + "base_median": 21975560, + "candidate_median": 21975560, + "base_spread": 0, + "candidate_spread": 0 + }, + { + "probe": "13_large_eden_survivors", + "metric": "freed_bytes", + "base_median": 108045936, + "candidate_median": 108045936, + "base_spread": 0, + "candidate_spread": 0 + }, + { + "probe": "14_grow_then_churn", + "metric": "heap_used_bytes", + "base_median": 429408, + "candidate_median": 429408, + "base_spread": 0, + "candidate_spread": 0 + }, + { + "probe": "14_grow_then_churn", + "metric": "heap_total_bytes", + "base_median": 23068672, + "candidate_median": 23068672, + "base_spread": 0, + "candidate_spread": 0 + }, + { + "probe": "14_grow_then_churn", + "metric": "minor_cycles", + "base_median": 22, + "candidate_median": 22, + "base_spread": 0, + "candidate_spread": 0 + }, + { + "probe": "14_grow_then_churn", + "metric": "step_cycles", + "base_median": 23, + "candidate_median": 23, + "base_spread": 0, + "candidate_spread": 0 + }, + { + "probe": "14_grow_then_churn", + "metric": "copied_objects", + "base_median": 330, + "candidate_median": 330, + "base_spread": 0, + "candidate_spread": 0 + }, + { + "probe": "14_grow_then_churn", + "metric": "copied_bytes", + "base_median": 1277072, + "candidate_median": 1277072, + "base_spread": 0, + "candidate_spread": 0 + }, + { + "probe": "14_grow_then_churn", + "metric": "promoted_objects", + "base_median": 405282, + "candidate_median": 405282, + "base_spread": 0, + "candidate_spread": 0 + }, + { + "probe": "14_grow_then_churn", + "metric": "promoted_bytes", + "base_median": 147967848, + "candidate_median": 147967848, + "base_spread": 0, + "candidate_spread": 0 + }, + { + "probe": "14_grow_then_churn", + "metric": "freed_bytes", + "base_median": 119549744, + "candidate_median": 119549744, + "base_spread": 0, + "candidate_spread": 0 + } + ] +} diff --git a/benchmarks/native_property_get/evidence/gc-ratchet-receipts.tar.gz b/benchmarks/native_property_get/evidence/gc-ratchet-receipts.tar.gz new file mode 100644 index 0000000000..4e0492c2cd Binary files /dev/null and b/benchmarks/native_property_get/evidence/gc-ratchet-receipts.tar.gz differ diff --git a/benchmarks/native_property_get/evidence/gc-stress-base-http2.json b/benchmarks/native_property_get/evidence/gc-stress-base-http2.json new file mode 100644 index 0000000000..8a8ce25067 --- /dev/null +++ b/benchmarks/native_property_get/evidence/gc-stress-base-http2.json @@ -0,0 +1 @@ +{"node":"26.5.1","pressure_mb":"8","complete":true,"shard":{"index":1,"count":1,"corpus_total":1,"corpus_selected":1},"arms":[{"id":"default","requires":"scavenge"},{"id":"safepoint_minor","requires":"scavenge"},{"id":"evac_minor","requires":"move"},{"id":"verify_evac","requires":"scavenge"},{"id":"force_verify","requires":"move"},{"id":"cons_scan_off","requires":"scavenge"},{"id":"shipped_default","requires":"none"}],"cells":[{"test":"test_gap_gc_http2_pending_event_callback_rooting","arm":"default","result":"FAIL","cycles":0,"evacuated":0,"scavenged":0,"reclaimed":0,"evidence":"output-mismatch cycles=0 evacuated=0 scavenged=0 reclaimed=0"},{"test":"test_gap_gc_http2_pending_event_callback_rooting","arm":"safepoint_minor","result":"FAIL","cycles":0,"evacuated":0,"scavenged":0,"reclaimed":0,"evidence":"output-mismatch cycles=0 evacuated=0 scavenged=0 reclaimed=0"},{"test":"test_gap_gc_http2_pending_event_callback_rooting","arm":"evac_minor","result":"FAIL","cycles":1,"evacuated":0,"scavenged":0,"reclaimed":23800,"evidence":"output-mismatch cycles=1 evacuated=0 scavenged=0 reclaimed=23800"},{"test":"test_gap_gc_http2_pending_event_callback_rooting","arm":"verify_evac","result":"FAIL","cycles":0,"evacuated":0,"scavenged":0,"reclaimed":0,"evidence":"output-mismatch cycles=0 evacuated=0 scavenged=0 reclaimed=0"},{"test":"test_gap_gc_http2_pending_event_callback_rooting","arm":"force_verify","result":"FAIL","cycles":1,"evacuated":0,"scavenged":4175,"reclaimed":71400,"evidence":"output-mismatch cycles=1 evacuated=0 scavenged=4175 reclaimed=71400"},{"test":"test_gap_gc_http2_pending_event_callback_rooting","arm":"cons_scan_off","result":"FAIL","cycles":0,"evacuated":0,"scavenged":0,"reclaimed":0,"evidence":"output-mismatch cycles=0 evacuated=0 scavenged=0 reclaimed=0"},{"test":"test_gap_gc_http2_pending_event_callback_rooting","arm":"shipped_default","result":"FAIL","cycles":0,"evacuated":0,"scavenged":0,"reclaimed":0,"evidence":"output-mismatch cycles=0 evacuated=0 scavenged=0 reclaimed=0"}],"summary":{"pass":0,"unverified":0,"xfail":0,"fail":7}} diff --git a/benchmarks/native_property_get/evidence/gc-stress-receipts.tar.gz b/benchmarks/native_property_get/evidence/gc-stress-receipts.tar.gz new file mode 100644 index 0000000000..de38f1af3c Binary files /dev/null and b/benchmarks/native_property_get/evidence/gc-stress-receipts.tar.gz differ diff --git a/benchmarks/native_property_get/evidence/gc-stress.json b/benchmarks/native_property_get/evidence/gc-stress.json new file mode 100644 index 0000000000..6c852e5bbb --- /dev/null +++ b/benchmarks/native_property_get/evidence/gc-stress.json @@ -0,0 +1 @@ +{"node":"26.5.1","pressure_mb":"8","complete":true,"shard":{"index":1,"count":1,"corpus_total":86,"corpus_selected":86},"arms":[{"id":"default","requires":"scavenge"},{"id":"safepoint_minor","requires":"scavenge"},{"id":"evac_minor","requires":"move"},{"id":"verify_evac","requires":"scavenge"},{"id":"force_verify","requires":"move"},{"id":"cons_scan_off","requires":"scavenge"},{"id":"shipped_default","requires":"none"}],"cells":[{"test":"test_gap_repsel_canonical_i32","arm":"default","result":"UNVER","cycles":0,"evacuated":0,"scavenged":0,"reclaimed":0,"evidence":"cycles=0 evacuated=0 scavenged=0 reclaimed=0"},{"test":"test_gap_repsel_canonical_i32","arm":"safepoint_minor","result":"UNVER","cycles":0,"evacuated":0,"scavenged":0,"reclaimed":0,"evidence":"cycles=0 evacuated=0 scavenged=0 reclaimed=0"},{"test":"test_gap_repsel_canonical_i32","arm":"evac_minor","result":"UNVER","cycles":1,"evacuated":0,"scavenged":0,"reclaimed":27520,"evidence":"cycles=1 evacuated=0 scavenged=0 reclaimed=27520"},{"test":"test_gap_repsel_canonical_i32","arm":"verify_evac","result":"UNVER","cycles":0,"evacuated":0,"scavenged":0,"reclaimed":0,"evidence":"cycles=0 evacuated=0 scavenged=0 reclaimed=0"},{"test":"test_gap_repsel_canonical_i32","arm":"force_verify","result":"PASS","cycles":1,"evacuated":0,"scavenged":4156,"reclaimed":82560,"evidence":"cycles=1 evacuated=0 scavenged=4156 reclaimed=82560"},{"test":"test_gap_repsel_canonical_i32","arm":"cons_scan_off","result":"UNVER","cycles":0,"evacuated":0,"scavenged":0,"reclaimed":0,"evidence":"cycles=0 evacuated=0 scavenged=0 reclaimed=0"},{"test":"test_gap_repsel_canonical_i32","arm":"shipped_default","result":"PASS","cycles":0,"evacuated":0,"scavenged":0,"reclaimed":0,"evidence":"cycles=0 evacuated=0 scavenged=0 reclaimed=0"},{"test":"test_gap_int_valued_ta_locals","arm":"default","result":"UNVER","cycles":0,"evacuated":0,"scavenged":0,"reclaimed":0,"evidence":"cycles=0 evacuated=0 scavenged=0 reclaimed=0"},{"test":"test_gap_int_valued_ta_locals","arm":"safepoint_minor","result":"UNVER","cycles":0,"evacuated":0,"scavenged":0,"reclaimed":0,"evidence":"cycles=0 evacuated=0 scavenged=0 reclaimed=0"},{"test":"test_gap_int_valued_ta_locals","arm":"evac_minor","result":"PASS","cycles":1,"evacuated":0,"scavenged":12,"reclaimed":7320,"evidence":"cycles=1 evacuated=0 scavenged=12 reclaimed=7320"},{"test":"test_gap_int_valued_ta_locals","arm":"verify_evac","result":"UNVER","cycles":0,"evacuated":0,"scavenged":0,"reclaimed":0,"evidence":"cycles=0 evacuated=0 scavenged=0 reclaimed=0"},{"test":"test_gap_int_valued_ta_locals","arm":"force_verify","result":"PASS","cycles":1,"evacuated":0,"scavenged":12,"reclaimed":7320,"evidence":"cycles=1 evacuated=0 scavenged=12 reclaimed=7320"},{"test":"test_gap_int_valued_ta_locals","arm":"cons_scan_off","result":"UNVER","cycles":0,"evacuated":0,"scavenged":0,"reclaimed":0,"evidence":"cycles=0 evacuated=0 scavenged=0 reclaimed=0"},{"test":"test_gap_int_valued_ta_locals","arm":"shipped_default","result":"PASS","cycles":0,"evacuated":0,"scavenged":0,"reclaimed":0,"evidence":"cycles=0 evacuated=0 scavenged=0 reclaimed=0"},{"test":"test_gap_repsel_loop_bounded_i32","arm":"default","result":"UNVER","cycles":0,"evacuated":0,"scavenged":0,"reclaimed":0,"evidence":"cycles=0 evacuated=0 scavenged=0 reclaimed=0"},{"test":"test_gap_repsel_loop_bounded_i32","arm":"safepoint_minor","result":"UNVER","cycles":0,"evacuated":0,"scavenged":0,"reclaimed":0,"evidence":"cycles=0 evacuated=0 scavenged=0 reclaimed=0"},{"test":"test_gap_repsel_loop_bounded_i32","arm":"evac_minor","result":"UNVER","cycles":1,"evacuated":0,"scavenged":0,"reclaimed":26544,"evidence":"cycles=1 evacuated=0 scavenged=0 reclaimed=26544"},{"test":"test_gap_repsel_loop_bounded_i32","arm":"verify_evac","result":"UNVER","cycles":0,"evacuated":0,"scavenged":0,"reclaimed":0,"evidence":"cycles=0 evacuated=0 scavenged=0 reclaimed=0"},{"test":"test_gap_repsel_loop_bounded_i32","arm":"force_verify","result":"PASS","cycles":1,"evacuated":0,"scavenged":4136,"reclaimed":79632,"evidence":"cycles=1 evacuated=0 scavenged=4136 reclaimed=79632"},{"test":"test_gap_repsel_loop_bounded_i32","arm":"cons_scan_off","result":"UNVER","cycles":0,"evacuated":0,"scavenged":0,"reclaimed":0,"evidence":"cycles=0 evacuated=0 scavenged=0 reclaimed=0"},{"test":"test_gap_repsel_loop_bounded_i32","arm":"shipped_default","result":"PASS","cycles":0,"evacuated":0,"scavenged":0,"reclaimed":0,"evidence":"cycles=0 evacuated=0 scavenged=0 reclaimed=0"},{"test":"test_gap_specabi_polymorphic_coexist","arm":"default","result":"UNVER","cycles":0,"evacuated":0,"scavenged":0,"reclaimed":0,"evidence":"cycles=0 evacuated=0 scavenged=0 reclaimed=0"},{"test":"test_gap_specabi_polymorphic_coexist","arm":"safepoint_minor","result":"UNVER","cycles":0,"evacuated":0,"scavenged":0,"reclaimed":0,"evidence":"cycles=0 evacuated=0 scavenged=0 reclaimed=0"},{"test":"test_gap_specabi_polymorphic_coexist","arm":"evac_minor","result":"UNVER","cycles":1,"evacuated":0,"scavenged":0,"reclaimed":25208,"evidence":"cycles=1 evacuated=0 scavenged=0 reclaimed=25208"},{"test":"test_gap_specabi_polymorphic_coexist","arm":"verify_evac","result":"UNVER","cycles":0,"evacuated":0,"scavenged":0,"reclaimed":0,"evidence":"cycles=0 evacuated=0 scavenged=0 reclaimed=0"},{"test":"test_gap_specabi_polymorphic_coexist","arm":"force_verify","result":"PASS","cycles":1,"evacuated":0,"scavenged":4157,"reclaimed":75624,"evidence":"cycles=1 evacuated=0 scavenged=4157 reclaimed=75624"},{"test":"test_gap_specabi_polymorphic_coexist","arm":"cons_scan_off","result":"UNVER","cycles":0,"evacuated":0,"scavenged":0,"reclaimed":0,"evidence":"cycles=0 evacuated=0 scavenged=0 reclaimed=0"},{"test":"test_gap_specabi_polymorphic_coexist","arm":"shipped_default","result":"PASS","cycles":0,"evacuated":0,"scavenged":0,"reclaimed":0,"evidence":"cycles=0 evacuated=0 scavenged=0 reclaimed=0"},{"test":"test_gap_specabi_reassign","arm":"default","result":"UNVER","cycles":0,"evacuated":0,"scavenged":0,"reclaimed":0,"evidence":"cycles=0 evacuated=0 scavenged=0 reclaimed=0"},{"test":"test_gap_specabi_reassign","arm":"safepoint_minor","result":"UNVER","cycles":0,"evacuated":0,"scavenged":0,"reclaimed":0,"evidence":"cycles=0 evacuated=0 scavenged=0 reclaimed=0"},{"test":"test_gap_specabi_reassign","arm":"evac_minor","result":"UNVER","cycles":1,"evacuated":0,"scavenged":0,"reclaimed":24992,"evidence":"cycles=1 evacuated=0 scavenged=0 reclaimed=24992"},{"test":"test_gap_specabi_reassign","arm":"verify_evac","result":"UNVER","cycles":0,"evacuated":0,"scavenged":0,"reclaimed":0,"evidence":"cycles=0 evacuated=0 scavenged=0 reclaimed=0"},{"test":"test_gap_specabi_reassign","arm":"force_verify","result":"PASS","cycles":1,"evacuated":0,"scavenged":4162,"reclaimed":74976,"evidence":"cycles=1 evacuated=0 scavenged=4162 reclaimed=74976"},{"test":"test_gap_specabi_reassign","arm":"cons_scan_off","result":"UNVER","cycles":0,"evacuated":0,"scavenged":0,"reclaimed":0,"evidence":"cycles=0 evacuated=0 scavenged=0 reclaimed=0"},{"test":"test_gap_specabi_reassign","arm":"shipped_default","result":"PASS","cycles":0,"evacuated":0,"scavenged":0,"reclaimed":0,"evidence":"cycles=0 evacuated=0 scavenged=0 reclaimed=0"},{"test":"test_gap_specabi_recursion_escape","arm":"default","result":"UNVER","cycles":0,"evacuated":0,"scavenged":0,"reclaimed":0,"evidence":"cycles=0 evacuated=0 scavenged=0 reclaimed=0"},{"test":"test_gap_specabi_recursion_escape","arm":"safepoint_minor","result":"UNVER","cycles":0,"evacuated":0,"scavenged":0,"reclaimed":0,"evidence":"cycles=0 evacuated=0 scavenged=0 reclaimed=0"},{"test":"test_gap_specabi_recursion_escape","arm":"evac_minor","result":"PASS","cycles":1,"evacuated":0,"scavenged":4,"reclaimed":1728,"evidence":"cycles=1 evacuated=0 scavenged=4 reclaimed=1728"},{"test":"test_gap_specabi_recursion_escape","arm":"verify_evac","result":"UNVER","cycles":0,"evacuated":0,"scavenged":0,"reclaimed":0,"evidence":"cycles=0 evacuated=0 scavenged=0 reclaimed=0"},{"test":"test_gap_specabi_recursion_escape","arm":"force_verify","result":"PASS","cycles":1,"evacuated":0,"scavenged":4,"reclaimed":1728,"evidence":"cycles=1 evacuated=0 scavenged=4 reclaimed=1728"},{"test":"test_gap_specabi_recursion_escape","arm":"cons_scan_off","result":"UNVER","cycles":0,"evacuated":0,"scavenged":0,"reclaimed":0,"evidence":"cycles=0 evacuated=0 scavenged=0 reclaimed=0"},{"test":"test_gap_specabi_recursion_escape","arm":"shipped_default","result":"PASS","cycles":0,"evacuated":0,"scavenged":0,"reclaimed":0,"evidence":"cycles=0 evacuated=0 scavenged=0 reclaimed=0"},{"test":"test_gap_specabi_view_detach","arm":"default","result":"UNVER","cycles":0,"evacuated":0,"scavenged":0,"reclaimed":0,"evidence":"cycles=0 evacuated=0 scavenged=0 reclaimed=0"},{"test":"test_gap_specabi_view_detach","arm":"safepoint_minor","result":"UNVER","cycles":0,"evacuated":0,"scavenged":0,"reclaimed":0,"evidence":"cycles=0 evacuated=0 scavenged=0 reclaimed=0"},{"test":"test_gap_specabi_view_detach","arm":"evac_minor","result":"PASS","cycles":1,"evacuated":0,"scavenged":6,"reclaimed":2256,"evidence":"cycles=1 evacuated=0 scavenged=6 reclaimed=2256"},{"test":"test_gap_specabi_view_detach","arm":"verify_evac","result":"UNVER","cycles":0,"evacuated":0,"scavenged":0,"reclaimed":0,"evidence":"cycles=0 evacuated=0 scavenged=0 reclaimed=0"},{"test":"test_gap_specabi_view_detach","arm":"force_verify","result":"PASS","cycles":1,"evacuated":0,"scavenged":6,"reclaimed":2256,"evidence":"cycles=1 evacuated=0 scavenged=6 reclaimed=2256"},{"test":"test_gap_specabi_view_detach","arm":"cons_scan_off","result":"UNVER","cycles":0,"evacuated":0,"scavenged":0,"reclaimed":0,"evidence":"cycles=0 evacuated=0 scavenged=0 reclaimed=0"},{"test":"test_gap_specabi_view_detach","arm":"shipped_default","result":"PASS","cycles":0,"evacuated":0,"scavenged":0,"reclaimed":0,"evidence":"cycles=0 evacuated=0 scavenged=0 reclaimed=0"},{"test":"test_gap_specabi_ordinary_param_guards","arm":"default","result":"PASS","cycles":22,"evacuated":0,"scavenged":302656,"reclaimed":778253744,"evidence":"cycles=22 evacuated=0 scavenged=302656 reclaimed=778253744"},{"test":"test_gap_specabi_ordinary_param_guards","arm":"safepoint_minor","result":"PASS","cycles":22,"evacuated":0,"scavenged":302656,"reclaimed":778253744,"evidence":"cycles=22 evacuated=0 scavenged=302656 reclaimed=778253744"},{"test":"test_gap_specabi_ordinary_param_guards","arm":"evac_minor","result":"PASS","cycles":22,"evacuated":0,"scavenged":290734,"reclaimed":781580080,"evidence":"cycles=22 evacuated=0 scavenged=290734 reclaimed=781580080"},{"test":"test_gap_specabi_ordinary_param_guards","arm":"verify_evac","result":"PASS","cycles":22,"evacuated":0,"scavenged":302656,"reclaimed":778253744,"evidence":"cycles=22 evacuated=0 scavenged=302656 reclaimed=778253744"},{"test":"test_gap_specabi_ordinary_param_guards","arm":"force_verify","result":"PASS","cycles":22,"evacuated":0,"scavenged":290734,"reclaimed":781580080,"evidence":"cycles=22 evacuated=0 scavenged=290734 reclaimed=781580080"},{"test":"test_gap_specabi_ordinary_param_guards","arm":"cons_scan_off","result":"PASS","cycles":22,"evacuated":0,"scavenged":290734,"reclaimed":781580080,"evidence":"cycles=22 evacuated=0 scavenged=290734 reclaimed=781580080"},{"test":"test_gap_specabi_ordinary_param_guards","arm":"shipped_default","result":"PASS","cycles":11,"evacuated":0,"scavenged":295072,"reclaimed":678707712,"evidence":"cycles=11 evacuated=0 scavenged=295072 reclaimed=678707712"},{"test":"test_gap_ta_param_numeric_read","arm":"default","result":"UNVER","cycles":1,"evacuated":0,"scavenged":0,"reclaimed":33576,"evidence":"cycles=1 evacuated=0 scavenged=0 reclaimed=33576"},{"test":"test_gap_ta_param_numeric_read","arm":"safepoint_minor","result":"UNVER","cycles":1,"evacuated":0,"scavenged":0,"reclaimed":33576,"evidence":"cycles=1 evacuated=0 scavenged=0 reclaimed=33576"},{"test":"test_gap_ta_param_numeric_read","arm":"evac_minor","result":"UNVER","cycles":1,"evacuated":0,"scavenged":0,"reclaimed":33576,"evidence":"cycles=1 evacuated=0 scavenged=0 reclaimed=33576"},{"test":"test_gap_ta_param_numeric_read","arm":"verify_evac","result":"PASS","cycles":1,"evacuated":0,"scavenged":4181,"reclaimed":100728,"evidence":"cycles=1 evacuated=0 scavenged=4181 reclaimed=100728"},{"test":"test_gap_ta_param_numeric_read","arm":"force_verify","result":"PASS","cycles":1,"evacuated":0,"scavenged":4181,"reclaimed":100728,"evidence":"cycles=1 evacuated=0 scavenged=4181 reclaimed=100728"},{"test":"test_gap_ta_param_numeric_read","arm":"cons_scan_off","result":"UNVER","cycles":1,"evacuated":0,"scavenged":0,"reclaimed":33576,"evidence":"cycles=1 evacuated=0 scavenged=0 reclaimed=33576"},{"test":"test_gap_ta_param_numeric_read","arm":"shipped_default","result":"PASS","cycles":0,"evacuated":0,"scavenged":0,"reclaimed":0,"evidence":"cycles=0 evacuated=0 scavenged=0 reclaimed=0"},{"test":"test_gap_typedarray_param_read","arm":"default","result":"UNVER","cycles":1,"evacuated":0,"scavenged":0,"reclaimed":31592,"evidence":"cycles=1 evacuated=0 scavenged=0 reclaimed=31592"},{"test":"test_gap_typedarray_param_read","arm":"safepoint_minor","result":"UNVER","cycles":1,"evacuated":0,"scavenged":0,"reclaimed":31592,"evidence":"cycles=1 evacuated=0 scavenged=0 reclaimed=31592"},{"test":"test_gap_typedarray_param_read","arm":"evac_minor","result":"UNVER","cycles":1,"evacuated":0,"scavenged":0,"reclaimed":31592,"evidence":"cycles=1 evacuated=0 scavenged=0 reclaimed=31592"},{"test":"test_gap_typedarray_param_read","arm":"verify_evac","result":"PASS","cycles":1,"evacuated":0,"scavenged":4188,"reclaimed":94776,"evidence":"cycles=1 evacuated=0 scavenged=4188 reclaimed=94776"},{"test":"test_gap_typedarray_param_read","arm":"force_verify","result":"PASS","cycles":1,"evacuated":0,"scavenged":4188,"reclaimed":94776,"evidence":"cycles=1 evacuated=0 scavenged=4188 reclaimed=94776"},{"test":"test_gap_typedarray_param_read","arm":"cons_scan_off","result":"UNVER","cycles":1,"evacuated":0,"scavenged":0,"reclaimed":31592,"evidence":"cycles=1 evacuated=0 scavenged=0 reclaimed=31592"},{"test":"test_gap_typedarray_param_read","arm":"shipped_default","result":"PASS","cycles":0,"evacuated":0,"scavenged":0,"reclaimed":0,"evidence":"cycles=0 evacuated=0 scavenged=0 reclaimed=0"},{"test":"test_gap_repsel_canonical_str_locals","arm":"default","result":"UNVER","cycles":0,"evacuated":0,"scavenged":0,"reclaimed":0,"evidence":"cycles=0 evacuated=0 scavenged=0 reclaimed=0"},{"test":"test_gap_repsel_canonical_str_locals","arm":"safepoint_minor","result":"UNVER","cycles":0,"evacuated":0,"scavenged":0,"reclaimed":0,"evidence":"cycles=0 evacuated=0 scavenged=0 reclaimed=0"},{"test":"test_gap_repsel_canonical_str_locals","arm":"evac_minor","result":"PASS","cycles":1,"evacuated":0,"scavenged":71,"reclaimed":17568,"evidence":"cycles=1 evacuated=0 scavenged=71 reclaimed=17568"},{"test":"test_gap_repsel_canonical_str_locals","arm":"verify_evac","result":"UNVER","cycles":0,"evacuated":0,"scavenged":0,"reclaimed":0,"evidence":"cycles=0 evacuated=0 scavenged=0 reclaimed=0"},{"test":"test_gap_repsel_canonical_str_locals","arm":"force_verify","result":"PASS","cycles":1,"evacuated":0,"scavenged":71,"reclaimed":17568,"evidence":"cycles=1 evacuated=0 scavenged=71 reclaimed=17568"},{"test":"test_gap_repsel_canonical_str_locals","arm":"cons_scan_off","result":"UNVER","cycles":0,"evacuated":0,"scavenged":0,"reclaimed":0,"evidence":"cycles=0 evacuated=0 scavenged=0 reclaimed=0"},{"test":"test_gap_repsel_canonical_str_locals","arm":"shipped_default","result":"PASS","cycles":0,"evacuated":0,"scavenged":0,"reclaimed":0,"evidence":"cycles=0 evacuated=0 scavenged=0 reclaimed=0"},{"test":"test_gap_repsel_ptr_shape_locals","arm":"default","result":"PASS","cycles":1,"evacuated":0,"scavenged":4194,"reclaimed":2222616,"evidence":"cycles=1 evacuated=0 scavenged=4194 reclaimed=2222616"},{"test":"test_gap_repsel_ptr_shape_locals","arm":"safepoint_minor","result":"PASS","cycles":1,"evacuated":0,"scavenged":4194,"reclaimed":2222616,"evidence":"cycles=1 evacuated=0 scavenged=4194 reclaimed=2222616"},{"test":"test_gap_repsel_ptr_shape_locals","arm":"evac_minor","result":"PASS","cycles":1,"evacuated":0,"scavenged":4194,"reclaimed":2222616,"evidence":"cycles=1 evacuated=0 scavenged=4194 reclaimed=2222616"},{"test":"test_gap_repsel_ptr_shape_locals","arm":"verify_evac","result":"PASS","cycles":1,"evacuated":0,"scavenged":4194,"reclaimed":2222616,"evidence":"cycles=1 evacuated=0 scavenged=4194 reclaimed=2222616"},{"test":"test_gap_repsel_ptr_shape_locals","arm":"force_verify","result":"PASS","cycles":1,"evacuated":0,"scavenged":4194,"reclaimed":2222616,"evidence":"cycles=1 evacuated=0 scavenged=4194 reclaimed=2222616"},{"test":"test_gap_repsel_ptr_shape_locals","arm":"cons_scan_off","result":"PASS","cycles":1,"evacuated":0,"scavenged":4194,"reclaimed":2222616,"evidence":"cycles=1 evacuated=0 scavenged=4194 reclaimed=2222616"},{"test":"test_gap_repsel_ptr_shape_locals","arm":"shipped_default","result":"PASS","cycles":0,"evacuated":0,"scavenged":0,"reclaimed":0,"evidence":"cycles=0 evacuated=0 scavenged=0 reclaimed=0"},{"test":"test_gap_repsel_ptr_shape_barriers","arm":"default","result":"UNVER","cycles":0,"evacuated":0,"scavenged":0,"reclaimed":0,"evidence":"cycles=0 evacuated=0 scavenged=0 reclaimed=0"},{"test":"test_gap_repsel_ptr_shape_barriers","arm":"safepoint_minor","result":"UNVER","cycles":0,"evacuated":0,"scavenged":0,"reclaimed":0,"evidence":"cycles=0 evacuated=0 scavenged=0 reclaimed=0"},{"test":"test_gap_repsel_ptr_shape_barriers","arm":"evac_minor","result":"UNVER","cycles":1,"evacuated":0,"scavenged":0,"reclaimed":24896,"evidence":"cycles=1 evacuated=0 scavenged=0 reclaimed=24896"},{"test":"test_gap_repsel_ptr_shape_barriers","arm":"verify_evac","result":"UNVER","cycles":0,"evacuated":0,"scavenged":0,"reclaimed":0,"evidence":"cycles=0 evacuated=0 scavenged=0 reclaimed=0"},{"test":"test_gap_repsel_ptr_shape_barriers","arm":"force_verify","result":"PASS","cycles":1,"evacuated":0,"scavenged":4142,"reclaimed":74688,"evidence":"cycles=1 evacuated=0 scavenged=4142 reclaimed=74688"},{"test":"test_gap_repsel_ptr_shape_barriers","arm":"cons_scan_off","result":"UNVER","cycles":0,"evacuated":0,"scavenged":0,"reclaimed":0,"evidence":"cycles=0 evacuated=0 scavenged=0 reclaimed=0"},{"test":"test_gap_repsel_ptr_shape_barriers","arm":"shipped_default","result":"PASS","cycles":0,"evacuated":0,"scavenged":0,"reclaimed":0,"evidence":"cycles=0 evacuated=0 scavenged=0 reclaimed=0"},{"test":"test_gap_repsel_return_shape","arm":"default","result":"PASS","cycles":5,"evacuated":0,"scavenged":97226,"reclaimed":58552360,"evidence":"cycles=5 evacuated=0 scavenged=97226 reclaimed=58552360"},{"test":"test_gap_repsel_return_shape","arm":"safepoint_minor","result":"PASS","cycles":5,"evacuated":0,"scavenged":97226,"reclaimed":58552360,"evidence":"cycles=5 evacuated=0 scavenged=97226 reclaimed=58552360"},{"test":"test_gap_repsel_return_shape","arm":"evac_minor","result":"PASS","cycles":5,"evacuated":0,"scavenged":97226,"reclaimed":64787920,"evidence":"cycles=5 evacuated=0 scavenged=97226 reclaimed=64787920"},{"test":"test_gap_repsel_return_shape","arm":"verify_evac","result":"PASS","cycles":5,"evacuated":0,"scavenged":126583,"reclaimed":69032112,"evidence":"cycles=5 evacuated=0 scavenged=126583 reclaimed=69032112"},{"test":"test_gap_repsel_return_shape","arm":"force_verify","result":"PASS","cycles":5,"evacuated":0,"scavenged":126583,"reclaimed":69032112,"evidence":"cycles=5 evacuated=0 scavenged=126583 reclaimed=69032112"},{"test":"test_gap_repsel_return_shape","arm":"cons_scan_off","result":"PASS","cycles":5,"evacuated":0,"scavenged":97226,"reclaimed":64787920,"evidence":"cycles=5 evacuated=0 scavenged=97226 reclaimed=64787920"},{"test":"test_gap_repsel_return_shape","arm":"shipped_default","result":"PASS","cycles":2,"evacuated":0,"scavenged":100294,"reclaimed":60576048,"evidence":"cycles=2 evacuated=0 scavenged=100294 reclaimed=60576048"},{"test":"test_gap_repsel_cjs_iife_return_shape","arm":"default","result":"PASS","cycles":3,"evacuated":0,"scavenged":58092,"reclaimed":27481872,"evidence":"cycles=3 evacuated=0 scavenged=58092 reclaimed=27481872"},{"test":"test_gap_repsel_cjs_iife_return_shape","arm":"safepoint_minor","result":"PASS","cycles":3,"evacuated":0,"scavenged":58092,"reclaimed":27481872,"evidence":"cycles=3 evacuated=0 scavenged=58092 reclaimed=27481872"},{"test":"test_gap_repsel_cjs_iife_return_shape","arm":"evac_minor","result":"PASS","cycles":3,"evacuated":0,"scavenged":58092,"reclaimed":27481872,"evidence":"cycles=3 evacuated=0 scavenged=58092 reclaimed=27481872"},{"test":"test_gap_repsel_cjs_iife_return_shape","arm":"verify_evac","result":"PASS","cycles":3,"evacuated":0,"scavenged":34601,"reclaimed":25277088,"evidence":"cycles=3 evacuated=0 scavenged=34601 reclaimed=25277088"},{"test":"test_gap_repsel_cjs_iife_return_shape","arm":"force_verify","result":"PASS","cycles":3,"evacuated":0,"scavenged":34601,"reclaimed":25277088,"evidence":"cycles=3 evacuated=0 scavenged=34601 reclaimed=25277088"},{"test":"test_gap_repsel_cjs_iife_return_shape","arm":"cons_scan_off","result":"PASS","cycles":3,"evacuated":0,"scavenged":58092,"reclaimed":27481872,"evidence":"cycles=3 evacuated=0 scavenged=58092 reclaimed=27481872"},{"test":"test_gap_repsel_cjs_iife_return_shape","arm":"shipped_default","result":"PASS","cycles":1,"evacuated":0,"scavenged":31117,"reclaimed":36513504,"evidence":"cycles=1 evacuated=0 scavenged=31117 reclaimed=36513504"},{"test":"test_gap_repsel_ptr_shape_elements","arm":"default","result":"UNVER","cycles":0,"evacuated":0,"scavenged":0,"reclaimed":0,"evidence":"cycles=0 evacuated=0 scavenged=0 reclaimed=0"},{"test":"test_gap_repsel_ptr_shape_elements","arm":"safepoint_minor","result":"UNVER","cycles":0,"evacuated":0,"scavenged":0,"reclaimed":0,"evidence":"cycles=0 evacuated=0 scavenged=0 reclaimed=0"},{"test":"test_gap_repsel_ptr_shape_elements","arm":"evac_minor","result":"PASS","cycles":1,"evacuated":0,"scavenged":4160,"reclaimed":2951976,"evidence":"cycles=1 evacuated=0 scavenged=4160 reclaimed=2951976"},{"test":"test_gap_repsel_ptr_shape_elements","arm":"verify_evac","result":"UNVER","cycles":0,"evacuated":0,"scavenged":0,"reclaimed":0,"evidence":"cycles=0 evacuated=0 scavenged=0 reclaimed=0"},{"test":"test_gap_repsel_ptr_shape_elements","arm":"force_verify","result":"PASS","cycles":1,"evacuated":0,"scavenged":4160,"reclaimed":2951976,"evidence":"cycles=1 evacuated=0 scavenged=4160 reclaimed=2951976"},{"test":"test_gap_repsel_ptr_shape_elements","arm":"cons_scan_off","result":"UNVER","cycles":0,"evacuated":0,"scavenged":0,"reclaimed":0,"evidence":"cycles=0 evacuated=0 scavenged=0 reclaimed=0"},{"test":"test_gap_repsel_ptr_shape_elements","arm":"shipped_default","result":"PASS","cycles":0,"evacuated":0,"scavenged":0,"reclaimed":0,"evidence":"cycles=0 evacuated=0 scavenged=0 reclaimed=0"},{"test":"test_gap_repsel_p4a_holes_axis","arm":"default","result":"UNVER","cycles":0,"evacuated":0,"scavenged":0,"reclaimed":0,"evidence":"cycles=0 evacuated=0 scavenged=0 reclaimed=0"},{"test":"test_gap_repsel_p4a_holes_axis","arm":"safepoint_minor","result":"UNVER","cycles":0,"evacuated":0,"scavenged":0,"reclaimed":0,"evidence":"cycles=0 evacuated=0 scavenged=0 reclaimed=0"},{"test":"test_gap_repsel_p4a_holes_axis","arm":"evac_minor","result":"UNVER","cycles":1,"evacuated":0,"scavenged":0,"reclaimed":29200,"evidence":"cycles=1 evacuated=0 scavenged=0 reclaimed=29200"},{"test":"test_gap_repsel_p4a_holes_axis","arm":"verify_evac","result":"UNVER","cycles":0,"evacuated":0,"scavenged":0,"reclaimed":0,"evidence":"cycles=0 evacuated=0 scavenged=0 reclaimed=0"},{"test":"test_gap_repsel_p4a_holes_axis","arm":"force_verify","result":"PASS","cycles":1,"evacuated":0,"scavenged":4116,"reclaimed":87600,"evidence":"cycles=1 evacuated=0 scavenged=4116 reclaimed=87600"},{"test":"test_gap_repsel_p4a_holes_axis","arm":"cons_scan_off","result":"UNVER","cycles":0,"evacuated":0,"scavenged":0,"reclaimed":0,"evidence":"cycles=0 evacuated=0 scavenged=0 reclaimed=0"},{"test":"test_gap_repsel_p4a_holes_axis","arm":"shipped_default","result":"PASS","cycles":0,"evacuated":0,"scavenged":0,"reclaimed":0,"evidence":"cycles=0 evacuated=0 scavenged=0 reclaimed=0"},{"test":"test_gap_repsel_p4a_inline_tiers","arm":"default","result":"UNVER","cycles":0,"evacuated":0,"scavenged":0,"reclaimed":0,"evidence":"cycles=0 evacuated=0 scavenged=0 reclaimed=0"},{"test":"test_gap_repsel_p4a_inline_tiers","arm":"safepoint_minor","result":"UNVER","cycles":0,"evacuated":0,"scavenged":0,"reclaimed":0,"evidence":"cycles=0 evacuated=0 scavenged=0 reclaimed=0"},{"test":"test_gap_repsel_p4a_inline_tiers","arm":"evac_minor","result":"UNVER","cycles":1,"evacuated":0,"scavenged":0,"reclaimed":65560,"evidence":"cycles=1 evacuated=0 scavenged=0 reclaimed=65560"},{"test":"test_gap_repsel_p4a_inline_tiers","arm":"verify_evac","result":"UNVER","cycles":0,"evacuated":0,"scavenged":0,"reclaimed":0,"evidence":"cycles=0 evacuated=0 scavenged=0 reclaimed=0"},{"test":"test_gap_repsel_p4a_inline_tiers","arm":"force_verify","result":"PASS","cycles":1,"evacuated":0,"scavenged":4117,"reclaimed":196680,"evidence":"cycles=1 evacuated=0 scavenged=4117 reclaimed=196680"},{"test":"test_gap_repsel_p4a_inline_tiers","arm":"cons_scan_off","result":"UNVER","cycles":0,"evacuated":0,"scavenged":0,"reclaimed":0,"evidence":"cycles=0 evacuated=0 scavenged=0 reclaimed=0"},{"test":"test_gap_repsel_p4a_inline_tiers","arm":"shipped_default","result":"PASS","cycles":0,"evacuated":0,"scavenged":0,"reclaimed":0,"evidence":"cycles=0 evacuated=0 scavenged=0 reclaimed=0"},{"test":"test_gap_repsel_p4a_logical_numeric","arm":"default","result":"UNVER","cycles":0,"evacuated":0,"scavenged":0,"reclaimed":0,"evidence":"cycles=0 evacuated=0 scavenged=0 reclaimed=0"},{"test":"test_gap_repsel_p4a_logical_numeric","arm":"safepoint_minor","result":"UNVER","cycles":0,"evacuated":0,"scavenged":0,"reclaimed":0,"evidence":"cycles=0 evacuated=0 scavenged=0 reclaimed=0"},{"test":"test_gap_repsel_p4a_logical_numeric","arm":"evac_minor","result":"UNVER","cycles":1,"evacuated":0,"scavenged":0,"reclaimed":48968,"evidence":"cycles=1 evacuated=0 scavenged=0 reclaimed=48968"},{"test":"test_gap_repsel_p4a_logical_numeric","arm":"verify_evac","result":"UNVER","cycles":0,"evacuated":0,"scavenged":0,"reclaimed":0,"evidence":"cycles=0 evacuated=0 scavenged=0 reclaimed=0"},{"test":"test_gap_repsel_p4a_logical_numeric","arm":"force_verify","result":"PASS","cycles":1,"evacuated":0,"scavenged":4123,"reclaimed":146904,"evidence":"cycles=1 evacuated=0 scavenged=4123 reclaimed=146904"},{"test":"test_gap_repsel_p4a_logical_numeric","arm":"cons_scan_off","result":"UNVER","cycles":0,"evacuated":0,"scavenged":0,"reclaimed":0,"evidence":"cycles=0 evacuated=0 scavenged=0 reclaimed=0"},{"test":"test_gap_repsel_p4a_logical_numeric","arm":"shipped_default","result":"PASS","cycles":0,"evacuated":0,"scavenged":0,"reclaimed":0,"evidence":"cycles=0 evacuated=0 scavenged=0 reclaimed=0"},{"test":"test_gap_repsel_p4a3_numarray_barriers","arm":"default","result":"UNVER","cycles":0,"evacuated":0,"scavenged":0,"reclaimed":0,"evidence":"cycles=0 evacuated=0 scavenged=0 reclaimed=0"},{"test":"test_gap_repsel_p4a3_numarray_barriers","arm":"safepoint_minor","result":"UNVER","cycles":0,"evacuated":0,"scavenged":0,"reclaimed":0,"evidence":"cycles=0 evacuated=0 scavenged=0 reclaimed=0"},{"test":"test_gap_repsel_p4a3_numarray_barriers","arm":"evac_minor","result":"UNVER","cycles":1,"evacuated":0,"scavenged":0,"reclaimed":57544,"evidence":"cycles=1 evacuated=0 scavenged=0 reclaimed=57544"},{"test":"test_gap_repsel_p4a3_numarray_barriers","arm":"verify_evac","result":"UNVER","cycles":0,"evacuated":0,"scavenged":0,"reclaimed":0,"evidence":"cycles=0 evacuated=0 scavenged=0 reclaimed=0"},{"test":"test_gap_repsel_p4a3_numarray_barriers","arm":"force_verify","result":"PASS","cycles":1,"evacuated":0,"scavenged":4123,"reclaimed":172632,"evidence":"cycles=1 evacuated=0 scavenged=4123 reclaimed=172632"},{"test":"test_gap_repsel_p4a3_numarray_barriers","arm":"cons_scan_off","result":"UNVER","cycles":0,"evacuated":0,"scavenged":0,"reclaimed":0,"evidence":"cycles=0 evacuated=0 scavenged=0 reclaimed=0"},{"test":"test_gap_repsel_p4a3_numarray_barriers","arm":"shipped_default","result":"PASS","cycles":0,"evacuated":0,"scavenged":0,"reclaimed":0,"evidence":"cycles=0 evacuated=0 scavenged=0 reclaimed=0"},{"test":"test_gap_repsel_p4a3_numarray_growth","arm":"default","result":"PASS","cycles":2,"evacuated":0,"scavenged":5271,"reclaimed":6074736,"evidence":"cycles=2 evacuated=0 scavenged=5271 reclaimed=6074736"},{"test":"test_gap_repsel_p4a3_numarray_growth","arm":"safepoint_minor","result":"PASS","cycles":2,"evacuated":0,"scavenged":5271,"reclaimed":6074736,"evidence":"cycles=2 evacuated=0 scavenged=5271 reclaimed=6074736"},{"test":"test_gap_repsel_p4a3_numarray_growth","arm":"evac_minor","result":"PASS","cycles":3,"evacuated":0,"scavenged":15606,"reclaimed":12794496,"evidence":"cycles=3 evacuated=0 scavenged=15606 reclaimed=12794496"},{"test":"test_gap_repsel_p4a3_numarray_growth","arm":"verify_evac","result":"PASS","cycles":2,"evacuated":0,"scavenged":5271,"reclaimed":6074736,"evidence":"cycles=2 evacuated=0 scavenged=5271 reclaimed=6074736"},{"test":"test_gap_repsel_p4a3_numarray_growth","arm":"force_verify","result":"PASS","cycles":3,"evacuated":0,"scavenged":15606,"reclaimed":12794496,"evidence":"cycles=3 evacuated=0 scavenged=15606 reclaimed=12794496"},{"test":"test_gap_repsel_p4a3_numarray_growth","arm":"cons_scan_off","result":"PASS","cycles":2,"evacuated":0,"scavenged":5271,"reclaimed":6074736,"evidence":"cycles=2 evacuated=0 scavenged=5271 reclaimed=6074736"},{"test":"test_gap_repsel_p4a3_numarray_growth","arm":"shipped_default","result":"PASS","cycles":0,"evacuated":0,"scavenged":0,"reclaimed":0,"evidence":"cycles=0 evacuated=0 scavenged=0 reclaimed=0"},{"test":"test_gap_repsel_p4a3_ptr_numarray","arm":"default","result":"UNVER","cycles":0,"evacuated":0,"scavenged":0,"reclaimed":0,"evidence":"cycles=0 evacuated=0 scavenged=0 reclaimed=0"},{"test":"test_gap_repsel_p4a3_ptr_numarray","arm":"safepoint_minor","result":"UNVER","cycles":0,"evacuated":0,"scavenged":0,"reclaimed":0,"evidence":"cycles=0 evacuated=0 scavenged=0 reclaimed=0"},{"test":"test_gap_repsel_p4a3_ptr_numarray","arm":"evac_minor","result":"UNVER","cycles":1,"evacuated":0,"scavenged":0,"reclaimed":182864,"evidence":"cycles=1 evacuated=0 scavenged=0 reclaimed=182864"},{"test":"test_gap_repsel_p4a3_ptr_numarray","arm":"verify_evac","result":"UNVER","cycles":0,"evacuated":0,"scavenged":0,"reclaimed":0,"evidence":"cycles=0 evacuated=0 scavenged=0 reclaimed=0"},{"test":"test_gap_repsel_p4a3_ptr_numarray","arm":"force_verify","result":"PASS","cycles":1,"evacuated":0,"scavenged":4119,"reclaimed":548592,"evidence":"cycles=1 evacuated=0 scavenged=4119 reclaimed=548592"},{"test":"test_gap_repsel_p4a3_ptr_numarray","arm":"cons_scan_off","result":"UNVER","cycles":0,"evacuated":0,"scavenged":0,"reclaimed":0,"evidence":"cycles=0 evacuated=0 scavenged=0 reclaimed=0"},{"test":"test_gap_repsel_p4a3_ptr_numarray","arm":"shipped_default","result":"PASS","cycles":0,"evacuated":0,"scavenged":0,"reclaimed":0,"evidence":"cycles=0 evacuated=0 scavenged=0 reclaimed=0"},{"test":"test_gap_repsel_p4b_field_store_elision","arm":"default","result":"PASS","cycles":1,"evacuated":0,"scavenged":4164,"reclaimed":2227176,"evidence":"cycles=1 evacuated=0 scavenged=4164 reclaimed=2227176"},{"test":"test_gap_repsel_p4b_field_store_elision","arm":"safepoint_minor","result":"PASS","cycles":1,"evacuated":0,"scavenged":4164,"reclaimed":2227176,"evidence":"cycles=1 evacuated=0 scavenged=4164 reclaimed=2227176"},{"test":"test_gap_repsel_p4b_field_store_elision","arm":"evac_minor","result":"PASS","cycles":1,"evacuated":0,"scavenged":4164,"reclaimed":2227176,"evidence":"cycles=1 evacuated=0 scavenged=4164 reclaimed=2227176"},{"test":"test_gap_repsel_p4b_field_store_elision","arm":"verify_evac","result":"PASS","cycles":1,"evacuated":0,"scavenged":4164,"reclaimed":2227176,"evidence":"cycles=1 evacuated=0 scavenged=4164 reclaimed=2227176"},{"test":"test_gap_repsel_p4b_field_store_elision","arm":"force_verify","result":"PASS","cycles":1,"evacuated":0,"scavenged":4164,"reclaimed":2227176,"evidence":"cycles=1 evacuated=0 scavenged=4164 reclaimed=2227176"},{"test":"test_gap_repsel_p4b_field_store_elision","arm":"cons_scan_off","result":"PASS","cycles":1,"evacuated":0,"scavenged":4164,"reclaimed":2227176,"evidence":"cycles=1 evacuated=0 scavenged=4164 reclaimed=2227176"},{"test":"test_gap_repsel_p4b_field_store_elision","arm":"shipped_default","result":"PASS","cycles":0,"evacuated":0,"scavenged":0,"reclaimed":0,"evidence":"cycles=0 evacuated=0 scavenged=0 reclaimed=0"},{"test":"test_gap_repsel_proven_this_frozen","arm":"default","result":"UNVER","cycles":0,"evacuated":0,"scavenged":0,"reclaimed":0,"evidence":"cycles=0 evacuated=0 scavenged=0 reclaimed=0"},{"test":"test_gap_repsel_proven_this_frozen","arm":"safepoint_minor","result":"UNVER","cycles":0,"evacuated":0,"scavenged":0,"reclaimed":0,"evidence":"cycles=0 evacuated=0 scavenged=0 reclaimed=0"},{"test":"test_gap_repsel_proven_this_frozen","arm":"evac_minor","result":"UNVER","cycles":1,"evacuated":0,"scavenged":0,"reclaimed":27520,"evidence":"cycles=1 evacuated=0 scavenged=0 reclaimed=27520"},{"test":"test_gap_repsel_proven_this_frozen","arm":"verify_evac","result":"UNVER","cycles":0,"evacuated":0,"scavenged":0,"reclaimed":0,"evidence":"cycles=0 evacuated=0 scavenged=0 reclaimed=0"},{"test":"test_gap_repsel_proven_this_frozen","arm":"force_verify","result":"PASS","cycles":1,"evacuated":0,"scavenged":4150,"reclaimed":82560,"evidence":"cycles=1 evacuated=0 scavenged=4150 reclaimed=82560"},{"test":"test_gap_repsel_proven_this_frozen","arm":"cons_scan_off","result":"UNVER","cycles":0,"evacuated":0,"scavenged":0,"reclaimed":0,"evidence":"cycles=0 evacuated=0 scavenged=0 reclaimed=0"},{"test":"test_gap_repsel_proven_this_frozen","arm":"shipped_default","result":"PASS","cycles":0,"evacuated":0,"scavenged":0,"reclaimed":0,"evidence":"cycles=0 evacuated=0 scavenged=0 reclaimed=0"},{"test":"test_gap_repsel_pshape_tower_delete","arm":"default","result":"PASS","cycles":6,"evacuated":0,"scavenged":78643,"reclaimed":1235416,"evidence":"cycles=6 evacuated=0 scavenged=78643 reclaimed=1235416"},{"test":"test_gap_repsel_pshape_tower_delete","arm":"safepoint_minor","result":"PASS","cycles":6,"evacuated":0,"scavenged":78643,"reclaimed":1235416,"evidence":"cycles=6 evacuated=0 scavenged=78643 reclaimed=1235416"},{"test":"test_gap_repsel_pshape_tower_delete","arm":"evac_minor","result":"PASS","cycles":6,"evacuated":0,"scavenged":78643,"reclaimed":1235416,"evidence":"cycles=6 evacuated=0 scavenged=78643 reclaimed=1235416"},{"test":"test_gap_repsel_pshape_tower_delete","arm":"verify_evac","result":"PASS","cycles":5,"evacuated":0,"scavenged":77025,"reclaimed":603176,"evidence":"cycles=5 evacuated=0 scavenged=77025 reclaimed=603176"},{"test":"test_gap_repsel_pshape_tower_delete","arm":"force_verify","result":"PASS","cycles":7,"evacuated":0,"scavenged":406462,"reclaimed":867864,"evidence":"cycles=7 evacuated=0 scavenged=406462 reclaimed=867864"},{"test":"test_gap_repsel_pshape_tower_delete","arm":"cons_scan_off","result":"PASS","cycles":6,"evacuated":0,"scavenged":78643,"reclaimed":1235416,"evidence":"cycles=6 evacuated=0 scavenged=78643 reclaimed=1235416"},{"test":"test_gap_repsel_pshape_tower_delete","arm":"shipped_default","result":"PASS","cycles":1,"evacuated":0,"scavenged":0,"reclaimed":286440,"evidence":"cycles=1 evacuated=0 scavenged=0 reclaimed=286440"},{"test":"test_gap_repsel_pshape_tombstone_oldgen_delete","arm":"default","result":"PASS","cycles":6,"evacuated":0,"scavenged":78643,"reclaimed":1179760,"evidence":"cycles=6 evacuated=0 scavenged=78643 reclaimed=1179760"},{"test":"test_gap_repsel_pshape_tombstone_oldgen_delete","arm":"safepoint_minor","result":"PASS","cycles":6,"evacuated":0,"scavenged":78643,"reclaimed":1179760,"evidence":"cycles=6 evacuated=0 scavenged=78643 reclaimed=1179760"},{"test":"test_gap_repsel_pshape_tombstone_oldgen_delete","arm":"evac_minor","result":"PASS","cycles":6,"evacuated":0,"scavenged":78643,"reclaimed":1179760,"evidence":"cycles=6 evacuated=0 scavenged=78643 reclaimed=1179760"},{"test":"test_gap_repsel_pshape_tombstone_oldgen_delete","arm":"verify_evac","result":"PASS","cycles":5,"evacuated":0,"scavenged":68851,"reclaimed":546840,"evidence":"cycles=5 evacuated=0 scavenged=68851 reclaimed=546840"},{"test":"test_gap_repsel_pshape_tombstone_oldgen_delete","arm":"force_verify","result":"PASS","cycles":7,"evacuated":0,"scavenged":402289,"reclaimed":853896,"evidence":"cycles=7 evacuated=0 scavenged=402289 reclaimed=853896"},{"test":"test_gap_repsel_pshape_tombstone_oldgen_delete","arm":"cons_scan_off","result":"PASS","cycles":6,"evacuated":0,"scavenged":78643,"reclaimed":1179760,"evidence":"cycles=6 evacuated=0 scavenged=78643 reclaimed=1179760"},{"test":"test_gap_repsel_pshape_tombstone_oldgen_delete","arm":"shipped_default","result":"PASS","cycles":1,"evacuated":0,"scavenged":0,"reclaimed":284776,"evidence":"cycles=1 evacuated=0 scavenged=0 reclaimed=284776"},{"test":"test_gap_gc_ta_ctor_source_rooting","arm":"default","result":"UNVER","cycles":1,"evacuated":0,"scavenged":0,"reclaimed":34208,"evidence":"cycles=1 evacuated=0 scavenged=0 reclaimed=34208"},{"test":"test_gap_gc_ta_ctor_source_rooting","arm":"safepoint_minor","result":"UNVER","cycles":1,"evacuated":0,"scavenged":0,"reclaimed":34208,"evidence":"cycles=1 evacuated=0 scavenged=0 reclaimed=34208"},{"test":"test_gap_gc_ta_ctor_source_rooting","arm":"evac_minor","result":"UNVER","cycles":1,"evacuated":0,"scavenged":0,"reclaimed":34208,"evidence":"cycles=1 evacuated=0 scavenged=0 reclaimed=34208"},{"test":"test_gap_gc_ta_ctor_source_rooting","arm":"verify_evac","result":"PASS","cycles":1,"evacuated":0,"scavenged":4182,"reclaimed":102624,"evidence":"cycles=1 evacuated=0 scavenged=4182 reclaimed=102624"},{"test":"test_gap_gc_ta_ctor_source_rooting","arm":"force_verify","result":"PASS","cycles":1,"evacuated":0,"scavenged":4182,"reclaimed":102624,"evidence":"cycles=1 evacuated=0 scavenged=4182 reclaimed=102624"},{"test":"test_gap_gc_ta_ctor_source_rooting","arm":"cons_scan_off","result":"UNVER","cycles":1,"evacuated":0,"scavenged":0,"reclaimed":34208,"evidence":"cycles=1 evacuated=0 scavenged=0 reclaimed=34208"},{"test":"test_gap_gc_ta_ctor_source_rooting","arm":"shipped_default","result":"PASS","cycles":0,"evacuated":0,"scavenged":0,"reclaimed":0,"evidence":"cycles=0 evacuated=0 scavenged=0 reclaimed=0"},{"test":"test_gap_gc_string_literal_operand_rooting","arm":"default","result":"PASS","cycles":10,"evacuated":0,"scavenged":253745,"reclaimed":74477864,"evidence":"cycles=10 evacuated=0 scavenged=253745 reclaimed=74477864"},{"test":"test_gap_gc_string_literal_operand_rooting","arm":"safepoint_minor","result":"PASS","cycles":10,"evacuated":0,"scavenged":253745,"reclaimed":74477864,"evidence":"cycles=10 evacuated=0 scavenged=253745 reclaimed=74477864"},{"test":"test_gap_gc_string_literal_operand_rooting","arm":"evac_minor","result":"PASS","cycles":10,"evacuated":0,"scavenged":253745,"reclaimed":104954424,"evidence":"cycles=10 evacuated=0 scavenged=253745 reclaimed=104954424"},{"test":"test_gap_gc_string_literal_operand_rooting","arm":"verify_evac","result":"PASS","cycles":10,"evacuated":0,"scavenged":245645,"reclaimed":83541176,"evidence":"cycles=10 evacuated=0 scavenged=245645 reclaimed=83541176"},{"test":"test_gap_gc_string_literal_operand_rooting","arm":"force_verify","result":"PASS","cycles":10,"evacuated":0,"scavenged":248097,"reclaimed":113731360,"evidence":"cycles=10 evacuated=0 scavenged=248097 reclaimed=113731360"},{"test":"test_gap_gc_string_literal_operand_rooting","arm":"cons_scan_off","result":"PASS","cycles":10,"evacuated":0,"scavenged":253745,"reclaimed":104954424,"evidence":"cycles=10 evacuated=0 scavenged=253745 reclaimed=104954424"},{"test":"test_gap_gc_string_literal_operand_rooting","arm":"shipped_default","result":"PASS","cycles":3,"evacuated":0,"scavenged":590263,"reclaimed":19895584,"evidence":"cycles=3 evacuated=0 scavenged=590263 reclaimed=19895584"},{"test":"test_gap_repsel_scalar_replaced_locals","arm":"default","result":"PASS","cycles":21,"evacuated":0,"scavenged":483784,"reclaimed":941744704,"evidence":"cycles=21 evacuated=0 scavenged=483784 reclaimed=941744704"},{"test":"test_gap_repsel_scalar_replaced_locals","arm":"safepoint_minor","result":"PASS","cycles":21,"evacuated":0,"scavenged":483784,"reclaimed":941744704,"evidence":"cycles=21 evacuated=0 scavenged=483784 reclaimed=941744704"},{"test":"test_gap_repsel_scalar_replaced_locals","arm":"evac_minor","result":"PASS","cycles":21,"evacuated":0,"scavenged":391460,"reclaimed":983839760,"evidence":"cycles=21 evacuated=0 scavenged=391460 reclaimed=983839760"},{"test":"test_gap_repsel_scalar_replaced_locals","arm":"verify_evac","result":"PASS","cycles":21,"evacuated":0,"scavenged":483784,"reclaimed":941744624,"evidence":"cycles=21 evacuated=0 scavenged=483784 reclaimed=941744624"},{"test":"test_gap_repsel_scalar_replaced_locals","arm":"force_verify","result":"PASS","cycles":21,"evacuated":0,"scavenged":391460,"reclaimed":983839760,"evidence":"cycles=21 evacuated=0 scavenged=391460 reclaimed=983839760"},{"test":"test_gap_repsel_scalar_replaced_locals","arm":"cons_scan_off","result":"PASS","cycles":21,"evacuated":0,"scavenged":391460,"reclaimed":983839760,"evidence":"cycles=21 evacuated=0 scavenged=391460 reclaimed=983839760"},{"test":"test_gap_repsel_scalar_replaced_locals","arm":"shipped_default","result":"PASS","cycles":12,"evacuated":0,"scavenged":439041,"reclaimed":982858416,"evidence":"cycles=12 evacuated=0 scavenged=439041 reclaimed=982858416"},{"test":"test_gap_repsel_module_init_canonical","arm":"default","result":"UNVER","cycles":1,"evacuated":0,"scavenged":0,"reclaimed":26800,"evidence":"cycles=1 evacuated=0 scavenged=0 reclaimed=26800"},{"test":"test_gap_repsel_module_init_canonical","arm":"safepoint_minor","result":"UNVER","cycles":1,"evacuated":0,"scavenged":0,"reclaimed":26800,"evidence":"cycles=1 evacuated=0 scavenged=0 reclaimed=26800"},{"test":"test_gap_repsel_module_init_canonical","arm":"evac_minor","result":"UNVER","cycles":1,"evacuated":0,"scavenged":0,"reclaimed":26800,"evidence":"cycles=1 evacuated=0 scavenged=0 reclaimed=26800"},{"test":"test_gap_repsel_module_init_canonical","arm":"verify_evac","result":"PASS","cycles":1,"evacuated":0,"scavenged":4146,"reclaimed":80400,"evidence":"cycles=1 evacuated=0 scavenged=4146 reclaimed=80400"},{"test":"test_gap_repsel_module_init_canonical","arm":"force_verify","result":"PASS","cycles":1,"evacuated":0,"scavenged":4146,"reclaimed":80400,"evidence":"cycles=1 evacuated=0 scavenged=4146 reclaimed=80400"},{"test":"test_gap_repsel_module_init_canonical","arm":"cons_scan_off","result":"UNVER","cycles":1,"evacuated":0,"scavenged":0,"reclaimed":26800,"evidence":"cycles=1 evacuated=0 scavenged=0 reclaimed=26800"},{"test":"test_gap_repsel_module_init_canonical","arm":"shipped_default","result":"PASS","cycles":0,"evacuated":0,"scavenged":0,"reclaimed":0,"evidence":"cycles=0 evacuated=0 scavenged=0 reclaimed=0"},{"test":"test_gap_gc_pointer_publish","arm":"default","result":"PASS","cycles":5,"evacuated":0,"scavenged":19144,"reclaimed":77393952,"evidence":"cycles=5 evacuated=0 scavenged=19144 reclaimed=77393952"},{"test":"test_gap_gc_pointer_publish","arm":"safepoint_minor","result":"PASS","cycles":5,"evacuated":0,"scavenged":19144,"reclaimed":77393952,"evidence":"cycles=5 evacuated=0 scavenged=19144 reclaimed=77393952"},{"test":"test_gap_gc_pointer_publish","arm":"evac_minor","result":"PASS","cycles":5,"evacuated":0,"scavenged":19144,"reclaimed":77393952,"evidence":"cycles=5 evacuated=0 scavenged=19144 reclaimed=77393952"},{"test":"test_gap_gc_pointer_publish","arm":"verify_evac","result":"PASS","cycles":5,"evacuated":0,"scavenged":19144,"reclaimed":77393952,"evidence":"cycles=5 evacuated=0 scavenged=19144 reclaimed=77393952"},{"test":"test_gap_gc_pointer_publish","arm":"force_verify","result":"PASS","cycles":5,"evacuated":0,"scavenged":19144,"reclaimed":77393952,"evidence":"cycles=5 evacuated=0 scavenged=19144 reclaimed=77393952"},{"test":"test_gap_gc_pointer_publish","arm":"cons_scan_off","result":"PASS","cycles":5,"evacuated":0,"scavenged":19144,"reclaimed":77393952,"evidence":"cycles=5 evacuated=0 scavenged=19144 reclaimed=77393952"},{"test":"test_gap_gc_pointer_publish","arm":"shipped_default","result":"PASS","cycles":2,"evacuated":0,"scavenged":12679,"reclaimed":71143152,"evidence":"cycles=2 evacuated=0 scavenged=12679 reclaimed=71143152"},{"test":"test_gap_repsel_gc_stress","arm":"default","result":"PASS","cycles":12,"evacuated":0,"scavenged":557915,"reclaimed":468745200,"evidence":"cycles=12 evacuated=0 scavenged=557915 reclaimed=468745200"},{"test":"test_gap_repsel_gc_stress","arm":"safepoint_minor","result":"PASS","cycles":12,"evacuated":0,"scavenged":557915,"reclaimed":468745200,"evidence":"cycles=12 evacuated=0 scavenged=557915 reclaimed=468745200"},{"test":"test_gap_repsel_gc_stress","arm":"evac_minor","result":"PASS","cycles":15,"evacuated":0,"scavenged":553338,"reclaimed":462782912,"evidence":"cycles=15 evacuated=0 scavenged=553338 reclaimed=462782912"},{"test":"test_gap_repsel_gc_stress","arm":"verify_evac","result":"PASS","cycles":16,"evacuated":0,"scavenged":574419,"reclaimed":493711024,"evidence":"cycles=16 evacuated=0 scavenged=574419 reclaimed=493711024"},{"test":"test_gap_repsel_gc_stress","arm":"force_verify","result":"PASS","cycles":16,"evacuated":0,"scavenged":618428,"reclaimed":441028080,"evidence":"cycles=16 evacuated=0 scavenged=618428 reclaimed=441028080"},{"test":"test_gap_repsel_gc_stress","arm":"cons_scan_off","result":"PASS","cycles":15,"evacuated":0,"scavenged":553338,"reclaimed":462782912,"evidence":"cycles=15 evacuated=0 scavenged=553338 reclaimed=462782912"},{"test":"test_gap_repsel_gc_stress","arm":"shipped_default","result":"PASS","cycles":7,"evacuated":0,"scavenged":480130,"reclaimed":463768560,"evidence":"cycles=7 evacuated=0 scavenged=480130 reclaimed=463768560"},{"test":"test_gap_gc_spread_accessor_rooting","arm":"default","result":"PASS","cycles":4,"evacuated":0,"scavenged":11901,"reclaimed":39628704,"evidence":"cycles=4 evacuated=0 scavenged=11901 reclaimed=39628704"},{"test":"test_gap_gc_spread_accessor_rooting","arm":"safepoint_minor","result":"PASS","cycles":4,"evacuated":0,"scavenged":11901,"reclaimed":39628704,"evidence":"cycles=4 evacuated=0 scavenged=11901 reclaimed=39628704"},{"test":"test_gap_gc_spread_accessor_rooting","arm":"evac_minor","result":"PASS","cycles":4,"evacuated":0,"scavenged":11901,"reclaimed":39628704,"evidence":"cycles=4 evacuated=0 scavenged=11901 reclaimed=39628704"},{"test":"test_gap_gc_spread_accessor_rooting","arm":"verify_evac","result":"PASS","cycles":4,"evacuated":0,"scavenged":11900,"reclaimed":39629136,"evidence":"cycles=4 evacuated=0 scavenged=11900 reclaimed=39629136"},{"test":"test_gap_gc_spread_accessor_rooting","arm":"force_verify","result":"PASS","cycles":4,"evacuated":0,"scavenged":11900,"reclaimed":39629136,"evidence":"cycles=4 evacuated=0 scavenged=11900 reclaimed=39629136"},{"test":"test_gap_gc_spread_accessor_rooting","arm":"cons_scan_off","result":"PASS","cycles":4,"evacuated":0,"scavenged":11901,"reclaimed":39628704,"evidence":"cycles=4 evacuated=0 scavenged=11901 reclaimed=39628704"},{"test":"test_gap_gc_spread_accessor_rooting","arm":"shipped_default","result":"PASS","cycles":1,"evacuated":0,"scavenged":9032,"reclaimed":45685464,"evidence":"cycles=1 evacuated=0 scavenged=9032 reclaimed=45685464"},{"test":"test_gap_gc_static_block_this_rooting","arm":"default","result":"PASS","cycles":4,"evacuated":0,"scavenged":15092,"reclaimed":39261768,"evidence":"cycles=4 evacuated=0 scavenged=15092 reclaimed=39261768"},{"test":"test_gap_gc_static_block_this_rooting","arm":"safepoint_minor","result":"PASS","cycles":4,"evacuated":0,"scavenged":15092,"reclaimed":39261768,"evidence":"cycles=4 evacuated=0 scavenged=15092 reclaimed=39261768"},{"test":"test_gap_gc_static_block_this_rooting","arm":"evac_minor","result":"PASS","cycles":4,"evacuated":0,"scavenged":15092,"reclaimed":39261768,"evidence":"cycles=4 evacuated=0 scavenged=15092 reclaimed=39261768"},{"test":"test_gap_gc_static_block_this_rooting","arm":"verify_evac","result":"PASS","cycles":4,"evacuated":0,"scavenged":15092,"reclaimed":39261768,"evidence":"cycles=4 evacuated=0 scavenged=15092 reclaimed=39261768"},{"test":"test_gap_gc_static_block_this_rooting","arm":"force_verify","result":"PASS","cycles":4,"evacuated":0,"scavenged":15092,"reclaimed":39261768,"evidence":"cycles=4 evacuated=0 scavenged=15092 reclaimed=39261768"},{"test":"test_gap_gc_static_block_this_rooting","arm":"cons_scan_off","result":"PASS","cycles":4,"evacuated":0,"scavenged":15092,"reclaimed":39261768,"evidence":"cycles=4 evacuated=0 scavenged=15092 reclaimed=39261768"},{"test":"test_gap_gc_static_block_this_rooting","arm":"shipped_default","result":"PASS","cycles":1,"evacuated":0,"scavenged":7455,"reclaimed":45857400,"evidence":"cycles=1 evacuated=0 scavenged=7455 reclaimed=45857400"},{"test":"test_gap_gc_inline_ctor_this_rooting","arm":"default","result":"PASS","cycles":4,"evacuated":0,"scavenged":10087,"reclaimed":40131648,"evidence":"cycles=4 evacuated=0 scavenged=10087 reclaimed=40131648"},{"test":"test_gap_gc_inline_ctor_this_rooting","arm":"safepoint_minor","result":"PASS","cycles":4,"evacuated":0,"scavenged":10087,"reclaimed":40131648,"evidence":"cycles=4 evacuated=0 scavenged=10087 reclaimed=40131648"},{"test":"test_gap_gc_inline_ctor_this_rooting","arm":"evac_minor","result":"PASS","cycles":4,"evacuated":0,"scavenged":10087,"reclaimed":40131648,"evidence":"cycles=4 evacuated=0 scavenged=10087 reclaimed=40131648"},{"test":"test_gap_gc_inline_ctor_this_rooting","arm":"verify_evac","result":"PASS","cycles":4,"evacuated":0,"scavenged":10087,"reclaimed":40131648,"evidence":"cycles=4 evacuated=0 scavenged=10087 reclaimed=40131648"},{"test":"test_gap_gc_inline_ctor_this_rooting","arm":"force_verify","result":"PASS","cycles":4,"evacuated":0,"scavenged":10087,"reclaimed":40131648,"evidence":"cycles=4 evacuated=0 scavenged=10087 reclaimed=40131648"},{"test":"test_gap_gc_inline_ctor_this_rooting","arm":"cons_scan_off","result":"PASS","cycles":4,"evacuated":0,"scavenged":10087,"reclaimed":40131648,"evidence":"cycles=4 evacuated=0 scavenged=10087 reclaimed=40131648"},{"test":"test_gap_gc_inline_ctor_this_rooting","arm":"shipped_default","result":"PASS","cycles":1,"evacuated":0,"scavenged":7887,"reclaimed":46238640,"evidence":"cycles=1 evacuated=0 scavenged=7887 reclaimed=46238640"},{"test":"test_gap_gc_catch_param_rooting","arm":"default","result":"PASS","cycles":4,"evacuated":0,"scavenged":14153,"reclaimed":39335616,"evidence":"cycles=4 evacuated=0 scavenged=14153 reclaimed=39335616"},{"test":"test_gap_gc_catch_param_rooting","arm":"safepoint_minor","result":"PASS","cycles":4,"evacuated":0,"scavenged":14153,"reclaimed":39335616,"evidence":"cycles=4 evacuated=0 scavenged=14153 reclaimed=39335616"},{"test":"test_gap_gc_catch_param_rooting","arm":"evac_minor","result":"PASS","cycles":4,"evacuated":0,"scavenged":14153,"reclaimed":39335616,"evidence":"cycles=4 evacuated=0 scavenged=14153 reclaimed=39335616"},{"test":"test_gap_gc_catch_param_rooting","arm":"verify_evac","result":"PASS","cycles":4,"evacuated":0,"scavenged":14153,"reclaimed":39335616,"evidence":"cycles=4 evacuated=0 scavenged=14153 reclaimed=39335616"},{"test":"test_gap_gc_catch_param_rooting","arm":"force_verify","result":"PASS","cycles":4,"evacuated":0,"scavenged":14153,"reclaimed":39335616,"evidence":"cycles=4 evacuated=0 scavenged=14153 reclaimed=39335616"},{"test":"test_gap_gc_catch_param_rooting","arm":"cons_scan_off","result":"PASS","cycles":4,"evacuated":0,"scavenged":14153,"reclaimed":39335616,"evidence":"cycles=4 evacuated=0 scavenged=14153 reclaimed=39335616"},{"test":"test_gap_gc_catch_param_rooting","arm":"shipped_default","result":"PASS","cycles":1,"evacuated":0,"scavenged":8030,"reclaimed":45800184,"evidence":"cycles=1 evacuated=0 scavenged=8030 reclaimed=45800184"},{"test":"test_gap_gc_closure_this_capture_rooting","arm":"default","result":"PASS","cycles":4,"evacuated":0,"scavenged":5368,"reclaimed":31121040,"evidence":"cycles=4 evacuated=0 scavenged=5368 reclaimed=31121040"},{"test":"test_gap_gc_closure_this_capture_rooting","arm":"safepoint_minor","result":"PASS","cycles":4,"evacuated":0,"scavenged":5368,"reclaimed":31121040,"evidence":"cycles=4 evacuated=0 scavenged=5368 reclaimed=31121040"},{"test":"test_gap_gc_closure_this_capture_rooting","arm":"evac_minor","result":"PASS","cycles":4,"evacuated":0,"scavenged":5368,"reclaimed":31121040,"evidence":"cycles=4 evacuated=0 scavenged=5368 reclaimed=31121040"},{"test":"test_gap_gc_closure_this_capture_rooting","arm":"verify_evac","result":"PASS","cycles":4,"evacuated":0,"scavenged":5368,"reclaimed":31121040,"evidence":"cycles=4 evacuated=0 scavenged=5368 reclaimed=31121040"},{"test":"test_gap_gc_closure_this_capture_rooting","arm":"force_verify","result":"PASS","cycles":4,"evacuated":0,"scavenged":5368,"reclaimed":31121040,"evidence":"cycles=4 evacuated=0 scavenged=5368 reclaimed=31121040"},{"test":"test_gap_gc_closure_this_capture_rooting","arm":"cons_scan_off","result":"PASS","cycles":4,"evacuated":0,"scavenged":5368,"reclaimed":31121040,"evidence":"cycles=4 evacuated=0 scavenged=5368 reclaimed=31121040"},{"test":"test_gap_gc_closure_this_capture_rooting","arm":"shipped_default","result":"PASS","cycles":1,"evacuated":0,"scavenged":3346,"reclaimed":46783680,"evidence":"cycles=1 evacuated=0 scavenged=3346 reclaimed=46783680"},{"test":"test_gap_gc_method_receiver_rooting","arm":"default","result":"PASS","cycles":4,"evacuated":0,"scavenged":8330,"reclaimed":40320048,"evidence":"cycles=4 evacuated=0 scavenged=8330 reclaimed=40320048"},{"test":"test_gap_gc_method_receiver_rooting","arm":"safepoint_minor","result":"PASS","cycles":4,"evacuated":0,"scavenged":8330,"reclaimed":40320048,"evidence":"cycles=4 evacuated=0 scavenged=8330 reclaimed=40320048"},{"test":"test_gap_gc_method_receiver_rooting","arm":"evac_minor","result":"PASS","cycles":4,"evacuated":0,"scavenged":8330,"reclaimed":40320048,"evidence":"cycles=4 evacuated=0 scavenged=8330 reclaimed=40320048"},{"test":"test_gap_gc_method_receiver_rooting","arm":"verify_evac","result":"PASS","cycles":4,"evacuated":0,"scavenged":8330,"reclaimed":40320048,"evidence":"cycles=4 evacuated=0 scavenged=8330 reclaimed=40320048"},{"test":"test_gap_gc_method_receiver_rooting","arm":"force_verify","result":"PASS","cycles":4,"evacuated":0,"scavenged":8330,"reclaimed":40320048,"evidence":"cycles=4 evacuated=0 scavenged=8330 reclaimed=40320048"},{"test":"test_gap_gc_method_receiver_rooting","arm":"cons_scan_off","result":"PASS","cycles":4,"evacuated":0,"scavenged":8330,"reclaimed":40320048,"evidence":"cycles=4 evacuated=0 scavenged=8330 reclaimed=40320048"},{"test":"test_gap_gc_method_receiver_rooting","arm":"shipped_default","result":"PASS","cycles":1,"evacuated":0,"scavenged":3903,"reclaimed":46704384,"evidence":"cycles=1 evacuated=0 scavenged=3903 reclaimed=46704384"},{"test":"test_gap_gc_index_get_receiver_rooting","arm":"default","result":"PASS","cycles":10,"evacuated":0,"scavenged":173994,"reclaimed":208886184,"evidence":"cycles=10 evacuated=0 scavenged=173994 reclaimed=208886184"},{"test":"test_gap_gc_index_get_receiver_rooting","arm":"safepoint_minor","result":"PASS","cycles":10,"evacuated":0,"scavenged":173994,"reclaimed":208886184,"evidence":"cycles=10 evacuated=0 scavenged=173994 reclaimed=208886184"},{"test":"test_gap_gc_index_get_receiver_rooting","arm":"evac_minor","result":"PASS","cycles":10,"evacuated":0,"scavenged":173994,"reclaimed":208886184,"evidence":"cycles=10 evacuated=0 scavenged=173994 reclaimed=208886184"},{"test":"test_gap_gc_index_get_receiver_rooting","arm":"verify_evac","result":"PASS","cycles":10,"evacuated":0,"scavenged":173994,"reclaimed":208886184,"evidence":"cycles=10 evacuated=0 scavenged=173994 reclaimed=208886184"},{"test":"test_gap_gc_index_get_receiver_rooting","arm":"force_verify","result":"PASS","cycles":10,"evacuated":0,"scavenged":173994,"reclaimed":208886184,"evidence":"cycles=10 evacuated=0 scavenged=173994 reclaimed=208886184"},{"test":"test_gap_gc_index_get_receiver_rooting","arm":"cons_scan_off","result":"PASS","cycles":10,"evacuated":0,"scavenged":173994,"reclaimed":208886184,"evidence":"cycles=10 evacuated=0 scavenged=173994 reclaimed=208886184"},{"test":"test_gap_gc_index_get_receiver_rooting","arm":"shipped_default","result":"PASS","cycles":4,"evacuated":0,"scavenged":79719,"reclaimed":187084536,"evidence":"cycles=4 evacuated=0 scavenged=79719 reclaimed=187084536"},{"test":"test_gap_gc_closure_call_callee_rooting","arm":"default","result":"PASS","cycles":4,"evacuated":0,"scavenged":4562,"reclaimed":31125504,"evidence":"cycles=4 evacuated=0 scavenged=4562 reclaimed=31125504"},{"test":"test_gap_gc_closure_call_callee_rooting","arm":"safepoint_minor","result":"PASS","cycles":4,"evacuated":0,"scavenged":4562,"reclaimed":31125504,"evidence":"cycles=4 evacuated=0 scavenged=4562 reclaimed=31125504"},{"test":"test_gap_gc_closure_call_callee_rooting","arm":"evac_minor","result":"PASS","cycles":4,"evacuated":0,"scavenged":4562,"reclaimed":31125504,"evidence":"cycles=4 evacuated=0 scavenged=4562 reclaimed=31125504"},{"test":"test_gap_gc_closure_call_callee_rooting","arm":"verify_evac","result":"PASS","cycles":4,"evacuated":0,"scavenged":4562,"reclaimed":31125504,"evidence":"cycles=4 evacuated=0 scavenged=4562 reclaimed=31125504"},{"test":"test_gap_gc_closure_call_callee_rooting","arm":"force_verify","result":"PASS","cycles":4,"evacuated":0,"scavenged":4562,"reclaimed":31125504,"evidence":"cycles=4 evacuated=0 scavenged=4562 reclaimed=31125504"},{"test":"test_gap_gc_closure_call_callee_rooting","arm":"cons_scan_off","result":"PASS","cycles":4,"evacuated":0,"scavenged":4562,"reclaimed":31125504,"evidence":"cycles=4 evacuated=0 scavenged=4562 reclaimed=31125504"},{"test":"test_gap_gc_closure_call_callee_rooting","arm":"shipped_default","result":"PASS","cycles":1,"evacuated":0,"scavenged":3407,"reclaimed":46784976,"evidence":"cycles=1 evacuated=0 scavenged=3407 reclaimed=46784976"},{"test":"test_gap_gc_closure_call_this_rooting","arm":"default","result":"PASS","cycles":4,"evacuated":0,"scavenged":5372,"reclaimed":31121040,"evidence":"cycles=4 evacuated=0 scavenged=5372 reclaimed=31121040"},{"test":"test_gap_gc_closure_call_this_rooting","arm":"safepoint_minor","result":"PASS","cycles":4,"evacuated":0,"scavenged":5372,"reclaimed":31121040,"evidence":"cycles=4 evacuated=0 scavenged=5372 reclaimed=31121040"},{"test":"test_gap_gc_closure_call_this_rooting","arm":"evac_minor","result":"PASS","cycles":4,"evacuated":0,"scavenged":5372,"reclaimed":31121040,"evidence":"cycles=4 evacuated=0 scavenged=5372 reclaimed=31121040"},{"test":"test_gap_gc_closure_call_this_rooting","arm":"verify_evac","result":"PASS","cycles":4,"evacuated":0,"scavenged":5372,"reclaimed":31121040,"evidence":"cycles=4 evacuated=0 scavenged=5372 reclaimed=31121040"},{"test":"test_gap_gc_closure_call_this_rooting","arm":"force_verify","result":"PASS","cycles":4,"evacuated":0,"scavenged":5372,"reclaimed":31121040,"evidence":"cycles=4 evacuated=0 scavenged=5372 reclaimed=31121040"},{"test":"test_gap_gc_closure_call_this_rooting","arm":"cons_scan_off","result":"PASS","cycles":4,"evacuated":0,"scavenged":5372,"reclaimed":31121040,"evidence":"cycles=4 evacuated=0 scavenged=5372 reclaimed=31121040"},{"test":"test_gap_gc_closure_call_this_rooting","arm":"shipped_default","result":"PASS","cycles":1,"evacuated":0,"scavenged":3346,"reclaimed":46783680,"evidence":"cycles=1 evacuated=0 scavenged=3346 reclaimed=46783680"},{"test":"test_gap_gc_closure_call_argument_rooting","arm":"default","result":"PASS","cycles":4,"evacuated":0,"scavenged":5373,"reclaimed":31121040,"evidence":"cycles=4 evacuated=0 scavenged=5373 reclaimed=31121040"},{"test":"test_gap_gc_closure_call_argument_rooting","arm":"safepoint_minor","result":"PASS","cycles":4,"evacuated":0,"scavenged":5373,"reclaimed":31121040,"evidence":"cycles=4 evacuated=0 scavenged=5373 reclaimed=31121040"},{"test":"test_gap_gc_closure_call_argument_rooting","arm":"evac_minor","result":"PASS","cycles":4,"evacuated":0,"scavenged":5373,"reclaimed":31121040,"evidence":"cycles=4 evacuated=0 scavenged=5373 reclaimed=31121040"},{"test":"test_gap_gc_closure_call_argument_rooting","arm":"verify_evac","result":"PASS","cycles":4,"evacuated":0,"scavenged":5373,"reclaimed":31121040,"evidence":"cycles=4 evacuated=0 scavenged=5373 reclaimed=31121040"},{"test":"test_gap_gc_closure_call_argument_rooting","arm":"force_verify","result":"PASS","cycles":4,"evacuated":0,"scavenged":5373,"reclaimed":31121040,"evidence":"cycles=4 evacuated=0 scavenged=5373 reclaimed=31121040"},{"test":"test_gap_gc_closure_call_argument_rooting","arm":"cons_scan_off","result":"PASS","cycles":4,"evacuated":0,"scavenged":5373,"reclaimed":31121040,"evidence":"cycles=4 evacuated=0 scavenged=5373 reclaimed=31121040"},{"test":"test_gap_gc_closure_call_argument_rooting","arm":"shipped_default","result":"PASS","cycles":1,"evacuated":0,"scavenged":3346,"reclaimed":46783680,"evidence":"cycles=1 evacuated=0 scavenged=3346 reclaimed=46783680"},{"test":"test_gap_gc_call_argument_rooting","arm":"default","result":"UNVER","cycles":0,"evacuated":0,"scavenged":0,"reclaimed":0,"evidence":"cycles=0 evacuated=0 scavenged=0 reclaimed=0"},{"test":"test_gap_gc_call_argument_rooting","arm":"safepoint_minor","result":"UNVER","cycles":0,"evacuated":0,"scavenged":0,"reclaimed":0,"evidence":"cycles=0 evacuated=0 scavenged=0 reclaimed=0"},{"test":"test_gap_gc_call_argument_rooting","arm":"evac_minor","result":"PASS","cycles":1,"evacuated":0,"scavenged":16,"reclaimed":3133992,"evidence":"cycles=1 evacuated=0 scavenged=16 reclaimed=3133992"},{"test":"test_gap_gc_call_argument_rooting","arm":"verify_evac","result":"UNVER","cycles":0,"evacuated":0,"scavenged":0,"reclaimed":0,"evidence":"cycles=0 evacuated=0 scavenged=0 reclaimed=0"},{"test":"test_gap_gc_call_argument_rooting","arm":"force_verify","result":"PASS","cycles":1,"evacuated":0,"scavenged":16,"reclaimed":3133992,"evidence":"cycles=1 evacuated=0 scavenged=16 reclaimed=3133992"},{"test":"test_gap_gc_call_argument_rooting","arm":"cons_scan_off","result":"UNVER","cycles":0,"evacuated":0,"scavenged":0,"reclaimed":0,"evidence":"cycles=0 evacuated=0 scavenged=0 reclaimed=0"},{"test":"test_gap_gc_call_argument_rooting","arm":"shipped_default","result":"PASS","cycles":0,"evacuated":0,"scavenged":0,"reclaimed":0,"evidence":"cycles=0 evacuated=0 scavenged=0 reclaimed=0"},{"test":"test_gap_gc_next_request_import","arm":"default","result":"PASS","cycles":2,"evacuated":0,"scavenged":5285,"reclaimed":2286656,"evidence":"cycles=2 evacuated=0 scavenged=5285 reclaimed=2286656"},{"test":"test_gap_gc_next_request_import","arm":"safepoint_minor","result":"PASS","cycles":2,"evacuated":0,"scavenged":5285,"reclaimed":2286656,"evidence":"cycles=2 evacuated=0 scavenged=5285 reclaimed=2286656"},{"test":"test_gap_gc_next_request_import","arm":"evac_minor","result":"PASS","cycles":2,"evacuated":0,"scavenged":5285,"reclaimed":2286656,"evidence":"cycles=2 evacuated=0 scavenged=5285 reclaimed=2286656"},{"test":"test_gap_gc_next_request_import","arm":"verify_evac","result":"PASS","cycles":2,"evacuated":0,"scavenged":5285,"reclaimed":2286656,"evidence":"cycles=2 evacuated=0 scavenged=5285 reclaimed=2286656"},{"test":"test_gap_gc_next_request_import","arm":"force_verify","result":"PASS","cycles":3,"evacuated":0,"scavenged":10033,"reclaimed":3627256,"evidence":"cycles=3 evacuated=0 scavenged=10033 reclaimed=3627256"},{"test":"test_gap_gc_next_request_import","arm":"cons_scan_off","result":"PASS","cycles":2,"evacuated":0,"scavenged":5285,"reclaimed":2286656,"evidence":"cycles=2 evacuated=0 scavenged=5285 reclaimed=2286656"},{"test":"test_gap_gc_next_request_import","arm":"shipped_default","result":"PASS","cycles":1,"evacuated":0,"scavenged":0,"reclaimed":281168,"evidence":"cycles=1 evacuated=0 scavenged=0 reclaimed=281168"},{"test":"test_gap_gc_rest_argument_rooting","arm":"default","result":"PASS","cycles":1,"evacuated":0,"scavenged":406,"reclaimed":3085224,"evidence":"cycles=1 evacuated=0 scavenged=406 reclaimed=3085224"},{"test":"test_gap_gc_rest_argument_rooting","arm":"safepoint_minor","result":"PASS","cycles":1,"evacuated":0,"scavenged":406,"reclaimed":3085224,"evidence":"cycles=1 evacuated=0 scavenged=406 reclaimed=3085224"},{"test":"test_gap_gc_rest_argument_rooting","arm":"evac_minor","result":"PASS","cycles":1,"evacuated":0,"scavenged":406,"reclaimed":3085224,"evidence":"cycles=1 evacuated=0 scavenged=406 reclaimed=3085224"},{"test":"test_gap_gc_rest_argument_rooting","arm":"verify_evac","result":"PASS","cycles":1,"evacuated":0,"scavenged":406,"reclaimed":3085224,"evidence":"cycles=1 evacuated=0 scavenged=406 reclaimed=3085224"},{"test":"test_gap_gc_rest_argument_rooting","arm":"force_verify","result":"PASS","cycles":1,"evacuated":0,"scavenged":406,"reclaimed":3085224,"evidence":"cycles=1 evacuated=0 scavenged=406 reclaimed=3085224"},{"test":"test_gap_gc_rest_argument_rooting","arm":"cons_scan_off","result":"PASS","cycles":1,"evacuated":0,"scavenged":406,"reclaimed":3085224,"evidence":"cycles=1 evacuated=0 scavenged=406 reclaimed=3085224"},{"test":"test_gap_gc_rest_argument_rooting","arm":"shipped_default","result":"PASS","cycles":0,"evacuated":0,"scavenged":0,"reclaimed":0,"evidence":"cycles=0 evacuated=0 scavenged=0 reclaimed=0"},{"test":"test_gap_gc_same_module_call_argument_rooting","arm":"default","result":"PASS","cycles":1,"evacuated":0,"scavenged":70,"reclaimed":3135408,"evidence":"cycles=1 evacuated=0 scavenged=70 reclaimed=3135408"},{"test":"test_gap_gc_same_module_call_argument_rooting","arm":"safepoint_minor","result":"PASS","cycles":1,"evacuated":0,"scavenged":70,"reclaimed":3135408,"evidence":"cycles=1 evacuated=0 scavenged=70 reclaimed=3135408"},{"test":"test_gap_gc_same_module_call_argument_rooting","arm":"evac_minor","result":"PASS","cycles":1,"evacuated":0,"scavenged":70,"reclaimed":3135408,"evidence":"cycles=1 evacuated=0 scavenged=70 reclaimed=3135408"},{"test":"test_gap_gc_same_module_call_argument_rooting","arm":"verify_evac","result":"PASS","cycles":1,"evacuated":0,"scavenged":70,"reclaimed":3135408,"evidence":"cycles=1 evacuated=0 scavenged=70 reclaimed=3135408"},{"test":"test_gap_gc_same_module_call_argument_rooting","arm":"force_verify","result":"PASS","cycles":1,"evacuated":0,"scavenged":70,"reclaimed":3135408,"evidence":"cycles=1 evacuated=0 scavenged=70 reclaimed=3135408"},{"test":"test_gap_gc_same_module_call_argument_rooting","arm":"cons_scan_off","result":"PASS","cycles":1,"evacuated":0,"scavenged":70,"reclaimed":3135408,"evidence":"cycles=1 evacuated=0 scavenged=70 reclaimed=3135408"},{"test":"test_gap_gc_same_module_call_argument_rooting","arm":"shipped_default","result":"PASS","cycles":0,"evacuated":0,"scavenged":0,"reclaimed":0,"evidence":"cycles=0 evacuated=0 scavenged=0 reclaimed=0"},{"test":"test_gap_gc_new_instance_rooting","arm":"default","result":"PASS","cycles":4,"evacuated":0,"scavenged":6101,"reclaimed":31032144,"evidence":"cycles=4 evacuated=0 scavenged=6101 reclaimed=31032144"},{"test":"test_gap_gc_new_instance_rooting","arm":"safepoint_minor","result":"PASS","cycles":4,"evacuated":0,"scavenged":6101,"reclaimed":31032144,"evidence":"cycles=4 evacuated=0 scavenged=6101 reclaimed=31032144"},{"test":"test_gap_gc_new_instance_rooting","arm":"evac_minor","result":"PASS","cycles":4,"evacuated":0,"scavenged":6101,"reclaimed":31032144,"evidence":"cycles=4 evacuated=0 scavenged=6101 reclaimed=31032144"},{"test":"test_gap_gc_new_instance_rooting","arm":"verify_evac","result":"PASS","cycles":4,"evacuated":0,"scavenged":6101,"reclaimed":31032144,"evidence":"cycles=4 evacuated=0 scavenged=6101 reclaimed=31032144"},{"test":"test_gap_gc_new_instance_rooting","arm":"force_verify","result":"PASS","cycles":4,"evacuated":0,"scavenged":6101,"reclaimed":31032144,"evidence":"cycles=4 evacuated=0 scavenged=6101 reclaimed=31032144"},{"test":"test_gap_gc_new_instance_rooting","arm":"cons_scan_off","result":"PASS","cycles":4,"evacuated":0,"scavenged":6101,"reclaimed":31032144,"evidence":"cycles=4 evacuated=0 scavenged=6101 reclaimed=31032144"},{"test":"test_gap_gc_new_instance_rooting","arm":"shipped_default","result":"PASS","cycles":1,"evacuated":0,"scavenged":3960,"reclaimed":46697904,"evidence":"cycles=1 evacuated=0 scavenged=3960 reclaimed=46697904"},{"test":"test_gap_gc_assign_string_source_rooting","arm":"default","result":"PASS","cycles":2,"evacuated":0,"scavenged":14777,"reclaimed":9051096,"evidence":"cycles=2 evacuated=0 scavenged=14777 reclaimed=9051096"},{"test":"test_gap_gc_assign_string_source_rooting","arm":"safepoint_minor","result":"PASS","cycles":2,"evacuated":0,"scavenged":14777,"reclaimed":9051096,"evidence":"cycles=2 evacuated=0 scavenged=14777 reclaimed=9051096"},{"test":"test_gap_gc_assign_string_source_rooting","arm":"evac_minor","result":"PASS","cycles":3,"evacuated":0,"scavenged":24490,"reclaimed":18974616,"evidence":"cycles=3 evacuated=0 scavenged=24490 reclaimed=18974616"},{"test":"test_gap_gc_assign_string_source_rooting","arm":"verify_evac","result":"PASS","cycles":2,"evacuated":0,"scavenged":14589,"reclaimed":9008688,"evidence":"cycles=2 evacuated=0 scavenged=14589 reclaimed=9008688"},{"test":"test_gap_gc_assign_string_source_rooting","arm":"force_verify","result":"PASS","cycles":3,"evacuated":0,"scavenged":23546,"reclaimed":19017816,"evidence":"cycles=3 evacuated=0 scavenged=23546 reclaimed=19017816"},{"test":"test_gap_gc_assign_string_source_rooting","arm":"cons_scan_off","result":"PASS","cycles":2,"evacuated":0,"scavenged":14777,"reclaimed":9051096,"evidence":"cycles=2 evacuated=0 scavenged=14777 reclaimed=9051096"},{"test":"test_gap_gc_assign_string_source_rooting","arm":"shipped_default","result":"PASS","cycles":0,"evacuated":0,"scavenged":0,"reclaimed":0,"evidence":"cycles=0 evacuated=0 scavenged=0 reclaimed=0"},{"test":"test_gap_gc_typeof_string_cache_rooting","arm":"default","result":"PASS","cycles":4,"evacuated":0,"scavenged":703,"reclaimed":31529432,"evidence":"cycles=4 evacuated=0 scavenged=703 reclaimed=31529432"},{"test":"test_gap_gc_typeof_string_cache_rooting","arm":"safepoint_minor","result":"PASS","cycles":4,"evacuated":0,"scavenged":703,"reclaimed":31529432,"evidence":"cycles=4 evacuated=0 scavenged=703 reclaimed=31529432"},{"test":"test_gap_gc_typeof_string_cache_rooting","arm":"evac_minor","result":"PASS","cycles":4,"evacuated":0,"scavenged":703,"reclaimed":36755328,"evidence":"cycles=4 evacuated=0 scavenged=703 reclaimed=36755328"},{"test":"test_gap_gc_typeof_string_cache_rooting","arm":"verify_evac","result":"PASS","cycles":4,"evacuated":0,"scavenged":703,"reclaimed":31529432,"evidence":"cycles=4 evacuated=0 scavenged=703 reclaimed=31529432"},{"test":"test_gap_gc_typeof_string_cache_rooting","arm":"force_verify","result":"PASS","cycles":4,"evacuated":0,"scavenged":3051,"reclaimed":35989464,"evidence":"cycles=4 evacuated=0 scavenged=3051 reclaimed=35989464"},{"test":"test_gap_gc_typeof_string_cache_rooting","arm":"cons_scan_off","result":"PASS","cycles":4,"evacuated":0,"scavenged":703,"reclaimed":36755328,"evidence":"cycles=4 evacuated=0 scavenged=703 reclaimed=36755328"},{"test":"test_gap_gc_typeof_string_cache_rooting","arm":"shipped_default","result":"PASS","cycles":0,"evacuated":0,"scavenged":0,"reclaimed":0,"evidence":"cycles=0 evacuated=0 scavenged=0 reclaimed=0"},{"test":"test_gap_gc_closure_call_prev_this_rooting","arm":"default","result":"PASS","cycles":4,"evacuated":0,"scavenged":5681,"reclaimed":31097568,"evidence":"cycles=4 evacuated=0 scavenged=5681 reclaimed=31097568"},{"test":"test_gap_gc_closure_call_prev_this_rooting","arm":"safepoint_minor","result":"PASS","cycles":4,"evacuated":0,"scavenged":5681,"reclaimed":31097568,"evidence":"cycles=4 evacuated=0 scavenged=5681 reclaimed=31097568"},{"test":"test_gap_gc_closure_call_prev_this_rooting","arm":"evac_minor","result":"PASS","cycles":4,"evacuated":0,"scavenged":5681,"reclaimed":31097568,"evidence":"cycles=4 evacuated=0 scavenged=5681 reclaimed=31097568"},{"test":"test_gap_gc_closure_call_prev_this_rooting","arm":"verify_evac","result":"PASS","cycles":4,"evacuated":0,"scavenged":5681,"reclaimed":31097568,"evidence":"cycles=4 evacuated=0 scavenged=5681 reclaimed=31097568"},{"test":"test_gap_gc_closure_call_prev_this_rooting","arm":"force_verify","result":"PASS","cycles":4,"evacuated":0,"scavenged":5681,"reclaimed":31097568,"evidence":"cycles=4 evacuated=0 scavenged=5681 reclaimed=31097568"},{"test":"test_gap_gc_closure_call_prev_this_rooting","arm":"cons_scan_off","result":"PASS","cycles":4,"evacuated":0,"scavenged":5681,"reclaimed":31097568,"evidence":"cycles=4 evacuated=0 scavenged=5681 reclaimed=31097568"},{"test":"test_gap_gc_closure_call_prev_this_rooting","arm":"shipped_default","result":"PASS","cycles":1,"evacuated":0,"scavenged":2740,"reclaimed":46856688,"evidence":"cycles=1 evacuated=0 scavenged=2740 reclaimed=46856688"},{"test":"test_gap_gc_regexp_receiver_rooting","arm":"default","result":"UNVER","cycles":2,"evacuated":0,"scavenged":0,"reclaimed":1373408,"evidence":"cycles=2 evacuated=0 scavenged=0 reclaimed=1373408"},{"test":"test_gap_gc_regexp_receiver_rooting","arm":"safepoint_minor","result":"UNVER","cycles":2,"evacuated":0,"scavenged":0,"reclaimed":1373408,"evidence":"cycles=2 evacuated=0 scavenged=0 reclaimed=1373408"},{"test":"test_gap_gc_regexp_receiver_rooting","arm":"evac_minor","result":"PASS","cycles":1,"evacuated":0,"scavenged":4417,"reclaimed":2186664,"evidence":"cycles=1 evacuated=0 scavenged=4417 reclaimed=2186664"},{"test":"test_gap_gc_regexp_receiver_rooting","arm":"verify_evac","result":"UNVER","cycles":2,"evacuated":0,"scavenged":0,"reclaimed":1373408,"evidence":"cycles=2 evacuated=0 scavenged=0 reclaimed=1373408"},{"test":"test_gap_gc_regexp_receiver_rooting","arm":"force_verify","result":"PASS","cycles":1,"evacuated":0,"scavenged":4417,"reclaimed":2186664,"evidence":"cycles=1 evacuated=0 scavenged=4417 reclaimed=2186664"},{"test":"test_gap_gc_regexp_receiver_rooting","arm":"cons_scan_off","result":"UNVER","cycles":2,"evacuated":0,"scavenged":0,"reclaimed":1373408,"evidence":"cycles=2 evacuated=0 scavenged=0 reclaimed=1373408"},{"test":"test_gap_gc_regexp_receiver_rooting","arm":"shipped_default","result":"PASS","cycles":0,"evacuated":0,"scavenged":0,"reclaimed":0,"evidence":"cycles=0 evacuated=0 scavenged=0 reclaimed=0"},{"test":"test_gap_gc_staging_args_rooting","arm":"default","result":"UNVER","cycles":0,"evacuated":0,"scavenged":0,"reclaimed":0,"evidence":"cycles=0 evacuated=0 scavenged=0 reclaimed=0"},{"test":"test_gap_gc_staging_args_rooting","arm":"safepoint_minor","result":"UNVER","cycles":1,"evacuated":0,"scavenged":0,"reclaimed":33648,"evidence":"cycles=1 evacuated=0 scavenged=0 reclaimed=33648"},{"test":"test_gap_gc_staging_args_rooting","arm":"evac_minor","result":"PASS","cycles":1,"evacuated":0,"scavenged":27,"reclaimed":50472,"evidence":"cycles=1 evacuated=0 scavenged=27 reclaimed=50472"},{"test":"test_gap_gc_staging_args_rooting","arm":"verify_evac","result":"UNVER","cycles":1,"evacuated":0,"scavenged":0,"reclaimed":33648,"evidence":"cycles=1 evacuated=0 scavenged=0 reclaimed=33648"},{"test":"test_gap_gc_staging_args_rooting","arm":"force_verify","result":"PASS","cycles":1,"evacuated":0,"scavenged":27,"reclaimed":50472,"evidence":"cycles=1 evacuated=0 scavenged=27 reclaimed=50472"},{"test":"test_gap_gc_staging_args_rooting","arm":"cons_scan_off","result":"UNVER","cycles":1,"evacuated":0,"scavenged":0,"reclaimed":33648,"evidence":"cycles=1 evacuated=0 scavenged=0 reclaimed=33648"},{"test":"test_gap_gc_staging_args_rooting","arm":"shipped_default","result":"PASS","cycles":0,"evacuated":0,"scavenged":0,"reclaimed":0,"evidence":"cycles=0 evacuated=0 scavenged=0 reclaimed=0"},{"test":"test_gap_gc_interval_args_rooting","arm":"default","result":"UNVER","cycles":1,"evacuated":0,"scavenged":0,"reclaimed":48080,"evidence":"cycles=1 evacuated=0 scavenged=0 reclaimed=48080"},{"test":"test_gap_gc_interval_args_rooting","arm":"safepoint_minor","result":"UNVER","cycles":1,"evacuated":0,"scavenged":0,"reclaimed":32080,"evidence":"cycles=1 evacuated=0 scavenged=0 reclaimed=32080"},{"test":"test_gap_gc_interval_args_rooting","arm":"evac_minor","result":"PASS","cycles":1,"evacuated":0,"scavenged":12,"reclaimed":48120,"evidence":"cycles=1 evacuated=0 scavenged=12 reclaimed=48120"},{"test":"test_gap_gc_interval_args_rooting","arm":"verify_evac","result":"UNVER","cycles":1,"evacuated":0,"scavenged":0,"reclaimed":32080,"evidence":"cycles=1 evacuated=0 scavenged=0 reclaimed=32080"},{"test":"test_gap_gc_interval_args_rooting","arm":"force_verify","result":"PASS","cycles":1,"evacuated":0,"scavenged":12,"reclaimed":48120,"evidence":"cycles=1 evacuated=0 scavenged=12 reclaimed=48120"},{"test":"test_gap_gc_interval_args_rooting","arm":"cons_scan_off","result":"UNVER","cycles":1,"evacuated":0,"scavenged":0,"reclaimed":32080,"evidence":"cycles=1 evacuated=0 scavenged=0 reclaimed=32080"},{"test":"test_gap_gc_interval_args_rooting","arm":"shipped_default","result":"PASS","cycles":0,"evacuated":0,"scavenged":0,"reclaimed":0,"evidence":"cycles=0 evacuated=0 scavenged=0 reclaimed=0"},{"test":"test_gap_gc_process_env_cache_rooting","arm":"default","result":"PASS","cycles":3,"evacuated":0,"scavenged":9615,"reclaimed":14699160,"evidence":"cycles=3 evacuated=0 scavenged=9615 reclaimed=14699160"},{"test":"test_gap_gc_process_env_cache_rooting","arm":"safepoint_minor","result":"PASS","cycles":3,"evacuated":0,"scavenged":9674,"reclaimed":14699400,"evidence":"cycles=3 evacuated=0 scavenged=9674 reclaimed=14699400"},{"test":"test_gap_gc_process_env_cache_rooting","arm":"evac_minor","result":"PASS","cycles":3,"evacuated":0,"scavenged":10543,"reclaimed":20892648,"evidence":"cycles=3 evacuated=0 scavenged=10543 reclaimed=20892648"},{"test":"test_gap_gc_process_env_cache_rooting","arm":"verify_evac","result":"PASS","cycles":3,"evacuated":0,"scavenged":9674,"reclaimed":14699400,"evidence":"cycles=3 evacuated=0 scavenged=9674 reclaimed=14699400"},{"test":"test_gap_gc_process_env_cache_rooting","arm":"force_verify","result":"PASS","cycles":4,"evacuated":0,"scavenged":10032,"reclaimed":29584704,"evidence":"cycles=4 evacuated=0 scavenged=10032 reclaimed=29584704"},{"test":"test_gap_gc_process_env_cache_rooting","arm":"cons_scan_off","result":"PASS","cycles":3,"evacuated":0,"scavenged":9674,"reclaimed":14699400,"evidence":"cycles=3 evacuated=0 scavenged=9674 reclaimed=14699400"},{"test":"test_gap_gc_process_env_cache_rooting","arm":"shipped_default","result":"PASS","cycles":0,"evacuated":0,"scavenged":0,"reclaimed":0,"evidence":"cycles=0 evacuated=0 scavenged=0 reclaimed=0"},{"test":"test_gap_gc_symbol_local_rooting","arm":"default","result":"PASS","cycles":86,"evacuated":0,"scavenged":5,"reclaimed":31949288,"evidence":"cycles=86 evacuated=0 scavenged=5 reclaimed=31949288"},{"test":"test_gap_gc_symbol_local_rooting","arm":"safepoint_minor","result":"PASS","cycles":86,"evacuated":0,"scavenged":5,"reclaimed":31949288,"evidence":"cycles=86 evacuated=0 scavenged=5 reclaimed=31949288"},{"test":"test_gap_gc_symbol_local_rooting","arm":"evac_minor","result":"PASS","cycles":86,"evacuated":0,"scavenged":5,"reclaimed":31949288,"evidence":"cycles=86 evacuated=0 scavenged=5 reclaimed=31949288"},{"test":"test_gap_gc_symbol_local_rooting","arm":"verify_evac","result":"PASS","cycles":86,"evacuated":0,"scavenged":5,"reclaimed":31949288,"evidence":"cycles=86 evacuated=0 scavenged=5 reclaimed=31949288"},{"test":"test_gap_gc_symbol_local_rooting","arm":"force_verify","result":"PASS","cycles":86,"evacuated":0,"scavenged":8170,"reclaimed":31952312,"evidence":"cycles=86 evacuated=0 scavenged=8170 reclaimed=31952312"},{"test":"test_gap_gc_symbol_local_rooting","arm":"cons_scan_off","result":"PASS","cycles":86,"evacuated":0,"scavenged":5,"reclaimed":31949288,"evidence":"cycles=86 evacuated=0 scavenged=5 reclaimed=31949288"},{"test":"test_gap_gc_symbol_local_rooting","arm":"shipped_default","result":"PASS","cycles":85,"evacuated":0,"scavenged":8170,"reclaimed":31952280,"evidence":"cycles=85 evacuated=0 scavenged=8170 reclaimed=31952280"},{"test":"test_gap_gc_dynamic_construct_receiver_rooting","arm":"default","result":"PASS","cycles":4,"evacuated":0,"scavenged":10086,"reclaimed":30365064,"evidence":"cycles=4 evacuated=0 scavenged=10086 reclaimed=30365064"},{"test":"test_gap_gc_dynamic_construct_receiver_rooting","arm":"safepoint_minor","result":"PASS","cycles":4,"evacuated":0,"scavenged":10086,"reclaimed":30365064,"evidence":"cycles=4 evacuated=0 scavenged=10086 reclaimed=30365064"},{"test":"test_gap_gc_dynamic_construct_receiver_rooting","arm":"evac_minor","result":"PASS","cycles":4,"evacuated":0,"scavenged":10086,"reclaimed":30365064,"evidence":"cycles=4 evacuated=0 scavenged=10086 reclaimed=30365064"},{"test":"test_gap_gc_dynamic_construct_receiver_rooting","arm":"verify_evac","result":"PASS","cycles":4,"evacuated":0,"scavenged":10086,"reclaimed":30365064,"evidence":"cycles=4 evacuated=0 scavenged=10086 reclaimed=30365064"},{"test":"test_gap_gc_dynamic_construct_receiver_rooting","arm":"force_verify","result":"PASS","cycles":4,"evacuated":0,"scavenged":10086,"reclaimed":30365064,"evidence":"cycles=4 evacuated=0 scavenged=10086 reclaimed=30365064"},{"test":"test_gap_gc_dynamic_construct_receiver_rooting","arm":"cons_scan_off","result":"PASS","cycles":4,"evacuated":0,"scavenged":10086,"reclaimed":30365064,"evidence":"cycles=4 evacuated=0 scavenged=10086 reclaimed=30365064"},{"test":"test_gap_gc_dynamic_construct_receiver_rooting","arm":"shipped_default","result":"PASS","cycles":1,"evacuated":0,"scavenged":6185,"reclaimed":45917400,"evidence":"cycles=1 evacuated=0 scavenged=6185 reclaimed=45917400"},{"test":"test_gap_gc_inlined_ctor_body_locals_rooting","arm":"default","result":"UNVER","cycles":0,"evacuated":0,"scavenged":0,"reclaimed":0,"evidence":"cycles=0 evacuated=0 scavenged=0 reclaimed=0"},{"test":"test_gap_gc_inlined_ctor_body_locals_rooting","arm":"safepoint_minor","result":"UNVER","cycles":0,"evacuated":0,"scavenged":0,"reclaimed":0,"evidence":"cycles=0 evacuated=0 scavenged=0 reclaimed=0"},{"test":"test_gap_gc_inlined_ctor_body_locals_rooting","arm":"evac_minor","result":"UNVER","cycles":1,"evacuated":0,"scavenged":0,"reclaimed":108544,"evidence":"cycles=1 evacuated=0 scavenged=0 reclaimed=108544"},{"test":"test_gap_gc_inlined_ctor_body_locals_rooting","arm":"verify_evac","result":"UNVER","cycles":0,"evacuated":0,"scavenged":0,"reclaimed":0,"evidence":"cycles=0 evacuated=0 scavenged=0 reclaimed=0"},{"test":"test_gap_gc_inlined_ctor_body_locals_rooting","arm":"force_verify","result":"PASS","cycles":1,"evacuated":0,"scavenged":4185,"reclaimed":325632,"evidence":"cycles=1 evacuated=0 scavenged=4185 reclaimed=325632"},{"test":"test_gap_gc_inlined_ctor_body_locals_rooting","arm":"cons_scan_off","result":"UNVER","cycles":0,"evacuated":0,"scavenged":0,"reclaimed":0,"evidence":"cycles=0 evacuated=0 scavenged=0 reclaimed=0"},{"test":"test_gap_gc_inlined_ctor_body_locals_rooting","arm":"shipped_default","result":"PASS","cycles":0,"evacuated":0,"scavenged":0,"reclaimed":0,"evidence":"cycles=0 evacuated=0 scavenged=0 reclaimed=0"},{"test":"test_gap_gc_optional_param_receiver_rooting","arm":"default","result":"PASS","cycles":2,"evacuated":0,"scavenged":8594,"reclaimed":11642328,"evidence":"cycles=2 evacuated=0 scavenged=8594 reclaimed=11642328"},{"test":"test_gap_gc_optional_param_receiver_rooting","arm":"safepoint_minor","result":"PASS","cycles":2,"evacuated":0,"scavenged":8594,"reclaimed":11642328,"evidence":"cycles=2 evacuated=0 scavenged=8594 reclaimed=11642328"},{"test":"test_gap_gc_optional_param_receiver_rooting","arm":"evac_minor","result":"PASS","cycles":2,"evacuated":0,"scavenged":8594,"reclaimed":11642328,"evidence":"cycles=2 evacuated=0 scavenged=8594 reclaimed=11642328"},{"test":"test_gap_gc_optional_param_receiver_rooting","arm":"verify_evac","result":"PASS","cycles":2,"evacuated":0,"scavenged":8594,"reclaimed":11642328,"evidence":"cycles=2 evacuated=0 scavenged=8594 reclaimed=11642328"},{"test":"test_gap_gc_optional_param_receiver_rooting","arm":"force_verify","result":"PASS","cycles":2,"evacuated":0,"scavenged":8594,"reclaimed":11642328,"evidence":"cycles=2 evacuated=0 scavenged=8594 reclaimed=11642328"},{"test":"test_gap_gc_optional_param_receiver_rooting","arm":"cons_scan_off","result":"PASS","cycles":2,"evacuated":0,"scavenged":8594,"reclaimed":11642328,"evidence":"cycles=2 evacuated=0 scavenged=8594 reclaimed=11642328"},{"test":"test_gap_gc_optional_param_receiver_rooting","arm":"shipped_default","result":"PASS","cycles":0,"evacuated":0,"scavenged":0,"reclaimed":0,"evidence":"cycles=0 evacuated=0 scavenged=0 reclaimed=0"},{"test":"test_gap_gc_iterator_drain_rooting","arm":"default","result":"PASS","cycles":4,"evacuated":0,"scavenged":60,"reclaimed":50330880,"evidence":"cycles=4 evacuated=0 scavenged=60 reclaimed=50330880"},{"test":"test_gap_gc_iterator_drain_rooting","arm":"safepoint_minor","result":"PASS","cycles":4,"evacuated":0,"scavenged":60,"reclaimed":50330880,"evidence":"cycles=4 evacuated=0 scavenged=60 reclaimed=50330880"},{"test":"test_gap_gc_iterator_drain_rooting","arm":"evac_minor","result":"PASS","cycles":4,"evacuated":0,"scavenged":60,"reclaimed":50330880,"evidence":"cycles=4 evacuated=0 scavenged=60 reclaimed=50330880"},{"test":"test_gap_gc_iterator_drain_rooting","arm":"verify_evac","result":"PASS","cycles":4,"evacuated":0,"scavenged":60,"reclaimed":50330880,"evidence":"cycles=4 evacuated=0 scavenged=60 reclaimed=50330880"},{"test":"test_gap_gc_iterator_drain_rooting","arm":"force_verify","result":"PASS","cycles":4,"evacuated":0,"scavenged":60,"reclaimed":50330880,"evidence":"cycles=4 evacuated=0 scavenged=60 reclaimed=50330880"},{"test":"test_gap_gc_iterator_drain_rooting","arm":"cons_scan_off","result":"PASS","cycles":4,"evacuated":0,"scavenged":60,"reclaimed":50330880,"evidence":"cycles=4 evacuated=0 scavenged=60 reclaimed=50330880"},{"test":"test_gap_gc_iterator_drain_rooting","arm":"shipped_default","result":"PASS","cycles":1,"evacuated":0,"scavenged":30,"reclaimed":50328000,"evidence":"cycles=1 evacuated=0 scavenged=30 reclaimed=50328000"},{"test":"test_gap_gc_global_builtin_lookup_rooting","arm":"default","result":"PASS","cycles":2,"evacuated":0,"scavenged":42634,"reclaimed":89512,"evidence":"cycles=2 evacuated=0 scavenged=42634 reclaimed=89512"},{"test":"test_gap_gc_global_builtin_lookup_rooting","arm":"safepoint_minor","result":"PASS","cycles":2,"evacuated":0,"scavenged":42634,"reclaimed":89512,"evidence":"cycles=2 evacuated=0 scavenged=42634 reclaimed=89512"},{"test":"test_gap_gc_global_builtin_lookup_rooting","arm":"evac_minor","result":"PASS","cycles":2,"evacuated":0,"scavenged":42634,"reclaimed":89512,"evidence":"cycles=2 evacuated=0 scavenged=42634 reclaimed=89512"},{"test":"test_gap_gc_global_builtin_lookup_rooting","arm":"verify_evac","result":"PASS","cycles":3,"evacuated":0,"scavenged":36033,"reclaimed":469944,"evidence":"cycles=3 evacuated=0 scavenged=36033 reclaimed=469944"},{"test":"test_gap_gc_global_builtin_lookup_rooting","arm":"force_verify","result":"PASS","cycles":3,"evacuated":0,"scavenged":78667,"reclaimed":471000,"evidence":"cycles=3 evacuated=0 scavenged=78667 reclaimed=471000"},{"test":"test_gap_gc_global_builtin_lookup_rooting","arm":"cons_scan_off","result":"PASS","cycles":2,"evacuated":0,"scavenged":42634,"reclaimed":89512,"evidence":"cycles=2 evacuated=0 scavenged=42634 reclaimed=89512"},{"test":"test_gap_gc_global_builtin_lookup_rooting","arm":"shipped_default","result":"PASS","cycles":0,"evacuated":0,"scavenged":0,"reclaimed":0,"evidence":"cycles=0 evacuated=0 scavenged=0 reclaimed=0"},{"test":"test_gap_gc_spread_symbol_iterator_rooting","arm":"default","result":"PASS","cycles":3,"evacuated":0,"scavenged":26,"reclaimed":25164576,"evidence":"cycles=3 evacuated=0 scavenged=26 reclaimed=25164576"},{"test":"test_gap_gc_spread_symbol_iterator_rooting","arm":"safepoint_minor","result":"PASS","cycles":3,"evacuated":0,"scavenged":26,"reclaimed":25164576,"evidence":"cycles=3 evacuated=0 scavenged=26 reclaimed=25164576"},{"test":"test_gap_gc_spread_symbol_iterator_rooting","arm":"evac_minor","result":"PASS","cycles":3,"evacuated":0,"scavenged":26,"reclaimed":25164576,"evidence":"cycles=3 evacuated=0 scavenged=26 reclaimed=25164576"},{"test":"test_gap_gc_spread_symbol_iterator_rooting","arm":"verify_evac","result":"PASS","cycles":3,"evacuated":0,"scavenged":26,"reclaimed":25164576,"evidence":"cycles=3 evacuated=0 scavenged=26 reclaimed=25164576"},{"test":"test_gap_gc_spread_symbol_iterator_rooting","arm":"force_verify","result":"PASS","cycles":3,"evacuated":0,"scavenged":26,"reclaimed":25164576,"evidence":"cycles=3 evacuated=0 scavenged=26 reclaimed=25164576"},{"test":"test_gap_gc_spread_symbol_iterator_rooting","arm":"cons_scan_off","result":"PASS","cycles":3,"evacuated":0,"scavenged":26,"reclaimed":25164576,"evidence":"cycles=3 evacuated=0 scavenged=26 reclaimed=25164576"},{"test":"test_gap_gc_spread_symbol_iterator_rooting","arm":"shipped_default","result":"PASS","cycles":0,"evacuated":0,"scavenged":0,"reclaimed":0,"evidence":"cycles=0 evacuated=0 scavenged=0 reclaimed=0"},{"test":"test_gap_repsel_element_shape_loop_clone","arm":"default","result":"UNVER","cycles":0,"evacuated":0,"scavenged":0,"reclaimed":0,"evidence":"cycles=0 evacuated=0 scavenged=0 reclaimed=0"},{"test":"test_gap_repsel_element_shape_loop_clone","arm":"safepoint_minor","result":"UNVER","cycles":0,"evacuated":0,"scavenged":0,"reclaimed":0,"evidence":"cycles=0 evacuated=0 scavenged=0 reclaimed=0"},{"test":"test_gap_repsel_element_shape_loop_clone","arm":"evac_minor","result":"UNVER","cycles":1,"evacuated":0,"scavenged":0,"reclaimed":66992,"evidence":"cycles=1 evacuated=0 scavenged=0 reclaimed=66992"},{"test":"test_gap_repsel_element_shape_loop_clone","arm":"verify_evac","result":"UNVER","cycles":0,"evacuated":0,"scavenged":0,"reclaimed":0,"evidence":"cycles=0 evacuated=0 scavenged=0 reclaimed=0"},{"test":"test_gap_repsel_element_shape_loop_clone","arm":"force_verify","result":"PASS","cycles":1,"evacuated":0,"scavenged":4270,"reclaimed":200976,"evidence":"cycles=1 evacuated=0 scavenged=4270 reclaimed=200976"},{"test":"test_gap_repsel_element_shape_loop_clone","arm":"cons_scan_off","result":"UNVER","cycles":0,"evacuated":0,"scavenged":0,"reclaimed":0,"evidence":"cycles=0 evacuated=0 scavenged=0 reclaimed=0"},{"test":"test_gap_repsel_element_shape_loop_clone","arm":"shipped_default","result":"PASS","cycles":0,"evacuated":0,"scavenged":0,"reclaimed":0,"evidence":"cycles=0 evacuated=0 scavenged=0 reclaimed=0"},{"test":"test_gap_gc_alloc_point_no_move","arm":"default","result":"PASS","cycles":43,"evacuated":0,"scavenged":11590,"reclaimed":1200966696,"evidence":"cycles=43 evacuated=0 scavenged=11590 reclaimed=1200966696"},{"test":"test_gap_gc_alloc_point_no_move","arm":"safepoint_minor","result":"PASS","cycles":43,"evacuated":0,"scavenged":11590,"reclaimed":1200966696,"evidence":"cycles=43 evacuated=0 scavenged=11590 reclaimed=1200966696"},{"test":"test_gap_gc_alloc_point_no_move","arm":"evac_minor","result":"PASS","cycles":43,"evacuated":0,"scavenged":11590,"reclaimed":1200966696,"evidence":"cycles=43 evacuated=0 scavenged=11590 reclaimed=1200966696"},{"test":"test_gap_gc_alloc_point_no_move","arm":"verify_evac","result":"PASS","cycles":43,"evacuated":0,"scavenged":11590,"reclaimed":1200966696,"evidence":"cycles=43 evacuated=0 scavenged=11590 reclaimed=1200966696"},{"test":"test_gap_gc_alloc_point_no_move","arm":"force_verify","result":"PASS","cycles":43,"evacuated":0,"scavenged":11590,"reclaimed":1200966696,"evidence":"cycles=43 evacuated=0 scavenged=11590 reclaimed=1200966696"},{"test":"test_gap_gc_alloc_point_no_move","arm":"cons_scan_off","result":"PASS","cycles":43,"evacuated":0,"scavenged":11590,"reclaimed":1200966696,"evidence":"cycles=43 evacuated=0 scavenged=11590 reclaimed=1200966696"},{"test":"test_gap_gc_alloc_point_no_move","arm":"shipped_default","result":"PASS","cycles":39,"evacuated":0,"scavenged":12221,"reclaimed":1200967392,"evidence":"cycles=39 evacuated=0 scavenged=12221 reclaimed=1200967392"},{"test":"test_gap_gc_class_field_receiver_rooting","arm":"default","result":"PASS","cycles":9,"evacuated":0,"scavenged":35919,"reclaimed":176707584,"evidence":"cycles=9 evacuated=0 scavenged=35919 reclaimed=176707584"},{"test":"test_gap_gc_class_field_receiver_rooting","arm":"safepoint_minor","result":"PASS","cycles":9,"evacuated":0,"scavenged":35919,"reclaimed":176707584,"evidence":"cycles=9 evacuated=0 scavenged=35919 reclaimed=176707584"},{"test":"test_gap_gc_class_field_receiver_rooting","arm":"evac_minor","result":"PASS","cycles":9,"evacuated":0,"scavenged":35919,"reclaimed":176707584,"evidence":"cycles=9 evacuated=0 scavenged=35919 reclaimed=176707584"},{"test":"test_gap_gc_class_field_receiver_rooting","arm":"verify_evac","result":"PASS","cycles":9,"evacuated":0,"scavenged":35919,"reclaimed":176707584,"evidence":"cycles=9 evacuated=0 scavenged=35919 reclaimed=176707584"},{"test":"test_gap_gc_class_field_receiver_rooting","arm":"force_verify","result":"PASS","cycles":9,"evacuated":0,"scavenged":35919,"reclaimed":176707584,"evidence":"cycles=9 evacuated=0 scavenged=35919 reclaimed=176707584"},{"test":"test_gap_gc_class_field_receiver_rooting","arm":"cons_scan_off","result":"PASS","cycles":9,"evacuated":0,"scavenged":35919,"reclaimed":176707584,"evidence":"cycles=9 evacuated=0 scavenged=35919 reclaimed=176707584"},{"test":"test_gap_gc_class_field_receiver_rooting","arm":"shipped_default","result":"PASS","cycles":5,"evacuated":0,"scavenged":23387,"reclaimed":180415392,"evidence":"cycles=5 evacuated=0 scavenged=23387 reclaimed=180415392"},{"test":"test_gap_gc_index_set_bounded_globalthis_ta_rooting","arm":"default","result":"PASS","cycles":42,"evacuated":0,"scavenged":156760,"reclaimed":1098436200,"evidence":"cycles=42 evacuated=0 scavenged=156760 reclaimed=1098436200"},{"test":"test_gap_gc_index_set_bounded_globalthis_ta_rooting","arm":"safepoint_minor","result":"PASS","cycles":42,"evacuated":0,"scavenged":156760,"reclaimed":1098436200,"evidence":"cycles=42 evacuated=0 scavenged=156760 reclaimed=1098436200"},{"test":"test_gap_gc_index_set_bounded_globalthis_ta_rooting","arm":"evac_minor","result":"PASS","cycles":42,"evacuated":0,"scavenged":156760,"reclaimed":1098436200,"evidence":"cycles=42 evacuated=0 scavenged=156760 reclaimed=1098436200"},{"test":"test_gap_gc_index_set_bounded_globalthis_ta_rooting","arm":"verify_evac","result":"PASS","cycles":42,"evacuated":0,"scavenged":156760,"reclaimed":1098436200,"evidence":"cycles=42 evacuated=0 scavenged=156760 reclaimed=1098436200"},{"test":"test_gap_gc_index_set_bounded_globalthis_ta_rooting","arm":"force_verify","result":"PASS","cycles":42,"evacuated":0,"scavenged":156760,"reclaimed":1098436200,"evidence":"cycles=42 evacuated=0 scavenged=156760 reclaimed=1098436200"},{"test":"test_gap_gc_index_set_bounded_globalthis_ta_rooting","arm":"cons_scan_off","result":"PASS","cycles":42,"evacuated":0,"scavenged":156760,"reclaimed":1098436200,"evidence":"cycles=42 evacuated=0 scavenged=156760 reclaimed=1098436200"},{"test":"test_gap_gc_index_set_bounded_globalthis_ta_rooting","arm":"shipped_default","result":"PASS","cycles":38,"evacuated":0,"scavenged":155182,"reclaimed":1110715920,"evidence":"cycles=38 evacuated=0 scavenged=155182 reclaimed=1110715920"},{"test":"test_gap_gc_namespace_and_computed_dispatch_rooting","arm":"default","result":"PASS","cycles":3,"evacuated":0,"scavenged":10394,"reclaimed":20947824,"evidence":"cycles=3 evacuated=0 scavenged=10394 reclaimed=20947824"},{"test":"test_gap_gc_namespace_and_computed_dispatch_rooting","arm":"safepoint_minor","result":"PASS","cycles":3,"evacuated":0,"scavenged":10394,"reclaimed":20947824,"evidence":"cycles=3 evacuated=0 scavenged=10394 reclaimed=20947824"},{"test":"test_gap_gc_namespace_and_computed_dispatch_rooting","arm":"evac_minor","result":"PASS","cycles":3,"evacuated":0,"scavenged":10394,"reclaimed":20947824,"evidence":"cycles=3 evacuated=0 scavenged=10394 reclaimed=20947824"},{"test":"test_gap_gc_namespace_and_computed_dispatch_rooting","arm":"verify_evac","result":"PASS","cycles":3,"evacuated":0,"scavenged":10394,"reclaimed":20947824,"evidence":"cycles=3 evacuated=0 scavenged=10394 reclaimed=20947824"},{"test":"test_gap_gc_namespace_and_computed_dispatch_rooting","arm":"force_verify","result":"PASS","cycles":3,"evacuated":0,"scavenged":10394,"reclaimed":20947824,"evidence":"cycles=3 evacuated=0 scavenged=10394 reclaimed=20947824"},{"test":"test_gap_gc_namespace_and_computed_dispatch_rooting","arm":"cons_scan_off","result":"PASS","cycles":3,"evacuated":0,"scavenged":10394,"reclaimed":20947824,"evidence":"cycles=3 evacuated=0 scavenged=10394 reclaimed=20947824"},{"test":"test_gap_gc_namespace_and_computed_dispatch_rooting","arm":"shipped_default","result":"PASS","cycles":0,"evacuated":0,"scavenged":0,"reclaimed":0,"evidence":"cycles=0 evacuated=0 scavenged=0 reclaimed=0"},{"test":"test_gap_gc_container_value_rooting","arm":"default","result":"UNVER","cycles":0,"evacuated":0,"scavenged":0,"reclaimed":0,"evidence":"cycles=0 evacuated=0 scavenged=0 reclaimed=0"},{"test":"test_gap_gc_container_value_rooting","arm":"safepoint_minor","result":"UNVER","cycles":0,"evacuated":0,"scavenged":0,"reclaimed":0,"evidence":"cycles=0 evacuated=0 scavenged=0 reclaimed=0"},{"test":"test_gap_gc_container_value_rooting","arm":"evac_minor","result":"PASS","cycles":1,"evacuated":0,"scavenged":4206,"reclaimed":1448160,"evidence":"cycles=1 evacuated=0 scavenged=4206 reclaimed=1448160"},{"test":"test_gap_gc_container_value_rooting","arm":"verify_evac","result":"UNVER","cycles":0,"evacuated":0,"scavenged":0,"reclaimed":0,"evidence":"cycles=0 evacuated=0 scavenged=0 reclaimed=0"},{"test":"test_gap_gc_container_value_rooting","arm":"force_verify","result":"PASS","cycles":1,"evacuated":0,"scavenged":4206,"reclaimed":1448160,"evidence":"cycles=1 evacuated=0 scavenged=4206 reclaimed=1448160"},{"test":"test_gap_gc_container_value_rooting","arm":"cons_scan_off","result":"UNVER","cycles":0,"evacuated":0,"scavenged":0,"reclaimed":0,"evidence":"cycles=0 evacuated=0 scavenged=0 reclaimed=0"},{"test":"test_gap_gc_container_value_rooting","arm":"shipped_default","result":"PASS","cycles":0,"evacuated":0,"scavenged":0,"reclaimed":0,"evidence":"cycles=0 evacuated=0 scavenged=0 reclaimed=0"},{"test":"test_gap_gc_define_properties_key_rooting","arm":"default","result":"UNVER","cycles":0,"evacuated":0,"scavenged":0,"reclaimed":0,"evidence":"cycles=0 evacuated=0 scavenged=0 reclaimed=0"},{"test":"test_gap_gc_define_properties_key_rooting","arm":"safepoint_minor","result":"UNVER","cycles":0,"evacuated":0,"scavenged":0,"reclaimed":0,"evidence":"cycles=0 evacuated=0 scavenged=0 reclaimed=0"},{"test":"test_gap_gc_define_properties_key_rooting","arm":"evac_minor","result":"UNVER","cycles":1,"evacuated":0,"scavenged":0,"reclaimed":179640,"evidence":"cycles=1 evacuated=0 scavenged=0 reclaimed=179640"},{"test":"test_gap_gc_define_properties_key_rooting","arm":"verify_evac","result":"UNVER","cycles":0,"evacuated":0,"scavenged":0,"reclaimed":0,"evidence":"cycles=0 evacuated=0 scavenged=0 reclaimed=0"},{"test":"test_gap_gc_define_properties_key_rooting","arm":"force_verify","result":"PASS","cycles":1,"evacuated":0,"scavenged":4194,"reclaimed":538920,"evidence":"cycles=1 evacuated=0 scavenged=4194 reclaimed=538920"},{"test":"test_gap_gc_define_properties_key_rooting","arm":"cons_scan_off","result":"UNVER","cycles":0,"evacuated":0,"scavenged":0,"reclaimed":0,"evidence":"cycles=0 evacuated=0 scavenged=0 reclaimed=0"},{"test":"test_gap_gc_define_properties_key_rooting","arm":"shipped_default","result":"PASS","cycles":0,"evacuated":0,"scavenged":0,"reclaimed":0,"evidence":"cycles=0 evacuated=0 scavenged=0 reclaimed=0"},{"test":"test_gap_gc_define_property_descriptor_rooting","arm":"default","result":"PASS","cycles":1,"evacuated":0,"scavenged":4387,"reclaimed":2193240,"evidence":"cycles=1 evacuated=0 scavenged=4387 reclaimed=2193240"},{"test":"test_gap_gc_define_property_descriptor_rooting","arm":"safepoint_minor","result":"PASS","cycles":1,"evacuated":0,"scavenged":4387,"reclaimed":2193240,"evidence":"cycles=1 evacuated=0 scavenged=4387 reclaimed=2193240"},{"test":"test_gap_gc_define_property_descriptor_rooting","arm":"evac_minor","result":"PASS","cycles":1,"evacuated":0,"scavenged":4387,"reclaimed":2193240,"evidence":"cycles=1 evacuated=0 scavenged=4387 reclaimed=2193240"},{"test":"test_gap_gc_define_property_descriptor_rooting","arm":"verify_evac","result":"PASS","cycles":1,"evacuated":0,"scavenged":4387,"reclaimed":2193240,"evidence":"cycles=1 evacuated=0 scavenged=4387 reclaimed=2193240"},{"test":"test_gap_gc_define_property_descriptor_rooting","arm":"force_verify","result":"PASS","cycles":1,"evacuated":0,"scavenged":4387,"reclaimed":2193240,"evidence":"cycles=1 evacuated=0 scavenged=4387 reclaimed=2193240"},{"test":"test_gap_gc_define_property_descriptor_rooting","arm":"cons_scan_off","result":"PASS","cycles":1,"evacuated":0,"scavenged":4387,"reclaimed":2193240,"evidence":"cycles=1 evacuated=0 scavenged=4387 reclaimed=2193240"},{"test":"test_gap_gc_define_property_descriptor_rooting","arm":"shipped_default","result":"PASS","cycles":0,"evacuated":0,"scavenged":0,"reclaimed":0,"evidence":"cycles=0 evacuated=0 scavenged=0 reclaimed=0"},{"test":"test_gap_repsel_element_shape_param_binding","arm":"default","result":"UNVER","cycles":0,"evacuated":0,"scavenged":0,"reclaimed":0,"evidence":"cycles=0 evacuated=0 scavenged=0 reclaimed=0"},{"test":"test_gap_repsel_element_shape_param_binding","arm":"safepoint_minor","result":"UNVER","cycles":0,"evacuated":0,"scavenged":0,"reclaimed":0,"evidence":"cycles=0 evacuated=0 scavenged=0 reclaimed=0"},{"test":"test_gap_repsel_element_shape_param_binding","arm":"evac_minor","result":"PASS","cycles":1,"evacuated":0,"scavenged":31,"reclaimed":35352,"evidence":"cycles=1 evacuated=0 scavenged=31 reclaimed=35352"},{"test":"test_gap_repsel_element_shape_param_binding","arm":"verify_evac","result":"UNVER","cycles":0,"evacuated":0,"scavenged":0,"reclaimed":0,"evidence":"cycles=0 evacuated=0 scavenged=0 reclaimed=0"},{"test":"test_gap_repsel_element_shape_param_binding","arm":"force_verify","result":"PASS","cycles":1,"evacuated":0,"scavenged":31,"reclaimed":35352,"evidence":"cycles=1 evacuated=0 scavenged=31 reclaimed=35352"},{"test":"test_gap_repsel_element_shape_param_binding","arm":"cons_scan_off","result":"UNVER","cycles":0,"evacuated":0,"scavenged":0,"reclaimed":0,"evidence":"cycles=0 evacuated=0 scavenged=0 reclaimed=0"},{"test":"test_gap_repsel_element_shape_param_binding","arm":"shipped_default","result":"PASS","cycles":0,"evacuated":0,"scavenged":0,"reclaimed":0,"evidence":"cycles=0 evacuated=0 scavenged=0 reclaimed=0"},{"test":"test_gap_repsel_element_group_numeric","arm":"default","result":"UNVER","cycles":0,"evacuated":0,"scavenged":0,"reclaimed":0,"evidence":"cycles=0 evacuated=0 scavenged=0 reclaimed=0"},{"test":"test_gap_repsel_element_group_numeric","arm":"safepoint_minor","result":"UNVER","cycles":0,"evacuated":0,"scavenged":0,"reclaimed":0,"evidence":"cycles=0 evacuated=0 scavenged=0 reclaimed=0"},{"test":"test_gap_repsel_element_group_numeric","arm":"evac_minor","result":"UNVER","cycles":1,"evacuated":0,"scavenged":0,"reclaimed":92264,"evidence":"cycles=1 evacuated=0 scavenged=0 reclaimed=92264"},{"test":"test_gap_repsel_element_group_numeric","arm":"verify_evac","result":"UNVER","cycles":0,"evacuated":0,"scavenged":0,"reclaimed":0,"evidence":"cycles=0 evacuated=0 scavenged=0 reclaimed=0"},{"test":"test_gap_repsel_element_group_numeric","arm":"force_verify","result":"PASS","cycles":1,"evacuated":0,"scavenged":4149,"reclaimed":276792,"evidence":"cycles=1 evacuated=0 scavenged=4149 reclaimed=276792"},{"test":"test_gap_repsel_element_group_numeric","arm":"cons_scan_off","result":"UNVER","cycles":0,"evacuated":0,"scavenged":0,"reclaimed":0,"evidence":"cycles=0 evacuated=0 scavenged=0 reclaimed=0"},{"test":"test_gap_repsel_element_group_numeric","arm":"shipped_default","result":"PASS","cycles":0,"evacuated":0,"scavenged":0,"reclaimed":0,"evidence":"cycles=0 evacuated=0 scavenged=0 reclaimed=0"},{"test":"test_gap_gc_container_value_rooting","arm":"default","result":"UNVER","cycles":0,"evacuated":0,"scavenged":0,"reclaimed":0,"evidence":"cycles=0 evacuated=0 scavenged=0 reclaimed=0"},{"test":"test_gap_gc_container_value_rooting","arm":"safepoint_minor","result":"UNVER","cycles":0,"evacuated":0,"scavenged":0,"reclaimed":0,"evidence":"cycles=0 evacuated=0 scavenged=0 reclaimed=0"},{"test":"test_gap_gc_container_value_rooting","arm":"evac_minor","result":"PASS","cycles":1,"evacuated":0,"scavenged":4206,"reclaimed":1448160,"evidence":"cycles=1 evacuated=0 scavenged=4206 reclaimed=1448160"},{"test":"test_gap_gc_container_value_rooting","arm":"verify_evac","result":"UNVER","cycles":0,"evacuated":0,"scavenged":0,"reclaimed":0,"evidence":"cycles=0 evacuated=0 scavenged=0 reclaimed=0"},{"test":"test_gap_gc_container_value_rooting","arm":"force_verify","result":"PASS","cycles":1,"evacuated":0,"scavenged":4206,"reclaimed":1448160,"evidence":"cycles=1 evacuated=0 scavenged=4206 reclaimed=1448160"},{"test":"test_gap_gc_container_value_rooting","arm":"cons_scan_off","result":"UNVER","cycles":0,"evacuated":0,"scavenged":0,"reclaimed":0,"evidence":"cycles=0 evacuated=0 scavenged=0 reclaimed=0"},{"test":"test_gap_gc_container_value_rooting","arm":"shipped_default","result":"PASS","cycles":0,"evacuated":0,"scavenged":0,"reclaimed":0,"evidence":"cycles=0 evacuated=0 scavenged=0 reclaimed=0"},{"test":"test_gap_gc_define_properties_key_rooting","arm":"default","result":"UNVER","cycles":0,"evacuated":0,"scavenged":0,"reclaimed":0,"evidence":"cycles=0 evacuated=0 scavenged=0 reclaimed=0"},{"test":"test_gap_gc_define_properties_key_rooting","arm":"safepoint_minor","result":"UNVER","cycles":0,"evacuated":0,"scavenged":0,"reclaimed":0,"evidence":"cycles=0 evacuated=0 scavenged=0 reclaimed=0"},{"test":"test_gap_gc_define_properties_key_rooting","arm":"evac_minor","result":"UNVER","cycles":1,"evacuated":0,"scavenged":0,"reclaimed":179640,"evidence":"cycles=1 evacuated=0 scavenged=0 reclaimed=179640"},{"test":"test_gap_gc_define_properties_key_rooting","arm":"verify_evac","result":"UNVER","cycles":0,"evacuated":0,"scavenged":0,"reclaimed":0,"evidence":"cycles=0 evacuated=0 scavenged=0 reclaimed=0"},{"test":"test_gap_gc_define_properties_key_rooting","arm":"force_verify","result":"PASS","cycles":1,"evacuated":0,"scavenged":4194,"reclaimed":538920,"evidence":"cycles=1 evacuated=0 scavenged=4194 reclaimed=538920"},{"test":"test_gap_gc_define_properties_key_rooting","arm":"cons_scan_off","result":"UNVER","cycles":0,"evacuated":0,"scavenged":0,"reclaimed":0,"evidence":"cycles=0 evacuated=0 scavenged=0 reclaimed=0"},{"test":"test_gap_gc_define_properties_key_rooting","arm":"shipped_default","result":"PASS","cycles":0,"evacuated":0,"scavenged":0,"reclaimed":0,"evidence":"cycles=0 evacuated=0 scavenged=0 reclaimed=0"},{"test":"test_gap_gc_define_property_descriptor_rooting","arm":"default","result":"PASS","cycles":1,"evacuated":0,"scavenged":4387,"reclaimed":2193240,"evidence":"cycles=1 evacuated=0 scavenged=4387 reclaimed=2193240"},{"test":"test_gap_gc_define_property_descriptor_rooting","arm":"safepoint_minor","result":"PASS","cycles":1,"evacuated":0,"scavenged":4387,"reclaimed":2193240,"evidence":"cycles=1 evacuated=0 scavenged=4387 reclaimed=2193240"},{"test":"test_gap_gc_define_property_descriptor_rooting","arm":"evac_minor","result":"PASS","cycles":1,"evacuated":0,"scavenged":4387,"reclaimed":2193240,"evidence":"cycles=1 evacuated=0 scavenged=4387 reclaimed=2193240"},{"test":"test_gap_gc_define_property_descriptor_rooting","arm":"verify_evac","result":"PASS","cycles":1,"evacuated":0,"scavenged":4387,"reclaimed":2193240,"evidence":"cycles=1 evacuated=0 scavenged=4387 reclaimed=2193240"},{"test":"test_gap_gc_define_property_descriptor_rooting","arm":"force_verify","result":"PASS","cycles":1,"evacuated":0,"scavenged":4387,"reclaimed":2193240,"evidence":"cycles=1 evacuated=0 scavenged=4387 reclaimed=2193240"},{"test":"test_gap_gc_define_property_descriptor_rooting","arm":"cons_scan_off","result":"PASS","cycles":1,"evacuated":0,"scavenged":4387,"reclaimed":2193240,"evidence":"cycles=1 evacuated=0 scavenged=4387 reclaimed=2193240"},{"test":"test_gap_gc_define_property_descriptor_rooting","arm":"shipped_default","result":"PASS","cycles":0,"evacuated":0,"scavenged":0,"reclaimed":0,"evidence":"cycles=0 evacuated=0 scavenged=0 reclaimed=0"},{"test":"test_gap_gc_uint8_buffer_callback_rooting","arm":"default","result":"PASS","cycles":3,"evacuated":0,"scavenged":8492,"reclaimed":17962896,"evidence":"cycles=3 evacuated=0 scavenged=8492 reclaimed=17962896"},{"test":"test_gap_gc_uint8_buffer_callback_rooting","arm":"safepoint_minor","result":"PASS","cycles":3,"evacuated":0,"scavenged":8492,"reclaimed":17962896,"evidence":"cycles=3 evacuated=0 scavenged=8492 reclaimed=17962896"},{"test":"test_gap_gc_uint8_buffer_callback_rooting","arm":"evac_minor","result":"PASS","cycles":3,"evacuated":0,"scavenged":8492,"reclaimed":17962896,"evidence":"cycles=3 evacuated=0 scavenged=8492 reclaimed=17962896"},{"test":"test_gap_gc_uint8_buffer_callback_rooting","arm":"verify_evac","result":"PASS","cycles":3,"evacuated":0,"scavenged":8492,"reclaimed":17962896,"evidence":"cycles=3 evacuated=0 scavenged=8492 reclaimed=17962896"},{"test":"test_gap_gc_uint8_buffer_callback_rooting","arm":"force_verify","result":"PASS","cycles":3,"evacuated":0,"scavenged":8492,"reclaimed":17962896,"evidence":"cycles=3 evacuated=0 scavenged=8492 reclaimed=17962896"},{"test":"test_gap_gc_uint8_buffer_callback_rooting","arm":"cons_scan_off","result":"PASS","cycles":3,"evacuated":0,"scavenged":8492,"reclaimed":17962896,"evidence":"cycles=3 evacuated=0 scavenged=8492 reclaimed=17962896"},{"test":"test_gap_gc_uint8_buffer_callback_rooting","arm":"shipped_default","result":"PASS","cycles":1,"evacuated":0,"scavenged":4427,"reclaimed":27340728,"evidence":"cycles=1 evacuated=0 scavenged=4427 reclaimed=27340728"},{"test":"test_gap_gc_fetch_method_value_cache_rooting","arm":"default","result":"PASS","cycles":2,"evacuated":0,"scavenged":9415,"reclaimed":11556768,"evidence":"cycles=2 evacuated=0 scavenged=9415 reclaimed=11556768"},{"test":"test_gap_gc_fetch_method_value_cache_rooting","arm":"safepoint_minor","result":"PASS","cycles":2,"evacuated":0,"scavenged":9415,"reclaimed":11556768,"evidence":"cycles=2 evacuated=0 scavenged=9415 reclaimed=11556768"},{"test":"test_gap_gc_fetch_method_value_cache_rooting","arm":"evac_minor","result":"PASS","cycles":2,"evacuated":0,"scavenged":9415,"reclaimed":11556768,"evidence":"cycles=2 evacuated=0 scavenged=9415 reclaimed=11556768"},{"test":"test_gap_gc_fetch_method_value_cache_rooting","arm":"verify_evac","result":"PASS","cycles":2,"evacuated":0,"scavenged":9415,"reclaimed":11556768,"evidence":"cycles=2 evacuated=0 scavenged=9415 reclaimed=11556768"},{"test":"test_gap_gc_fetch_method_value_cache_rooting","arm":"force_verify","result":"PASS","cycles":2,"evacuated":0,"scavenged":9415,"reclaimed":11556768,"evidence":"cycles=2 evacuated=0 scavenged=9415 reclaimed=11556768"},{"test":"test_gap_gc_fetch_method_value_cache_rooting","arm":"cons_scan_off","result":"PASS","cycles":2,"evacuated":0,"scavenged":9415,"reclaimed":11556768,"evidence":"cycles=2 evacuated=0 scavenged=9415 reclaimed=11556768"},{"test":"test_gap_gc_fetch_method_value_cache_rooting","arm":"shipped_default","result":"PASS","cycles":0,"evacuated":0,"scavenged":0,"reclaimed":0,"evidence":"cycles=0 evacuated=0 scavenged=0 reclaimed=0"},{"test":"test_gap_gc_request_signal_rooting","arm":"default","result":"PASS","cycles":3,"evacuated":0,"scavenged":9682,"reclaimed":27271800,"evidence":"cycles=3 evacuated=0 scavenged=9682 reclaimed=27271800"},{"test":"test_gap_gc_request_signal_rooting","arm":"safepoint_minor","result":"PASS","cycles":3,"evacuated":0,"scavenged":9682,"reclaimed":27271800,"evidence":"cycles=3 evacuated=0 scavenged=9682 reclaimed=27271800"},{"test":"test_gap_gc_request_signal_rooting","arm":"evac_minor","result":"PASS","cycles":3,"evacuated":0,"scavenged":9682,"reclaimed":27271800,"evidence":"cycles=3 evacuated=0 scavenged=9682 reclaimed=27271800"},{"test":"test_gap_gc_request_signal_rooting","arm":"verify_evac","result":"PASS","cycles":3,"evacuated":0,"scavenged":9682,"reclaimed":27271800,"evidence":"cycles=3 evacuated=0 scavenged=9682 reclaimed=27271800"},{"test":"test_gap_gc_request_signal_rooting","arm":"force_verify","result":"PASS","cycles":3,"evacuated":0,"scavenged":9682,"reclaimed":27271800,"evidence":"cycles=3 evacuated=0 scavenged=9682 reclaimed=27271800"},{"test":"test_gap_gc_request_signal_rooting","arm":"cons_scan_off","result":"PASS","cycles":3,"evacuated":0,"scavenged":9682,"reclaimed":27271800,"evidence":"cycles=3 evacuated=0 scavenged=9682 reclaimed=27271800"},{"test":"test_gap_gc_request_signal_rooting","arm":"shipped_default","result":"PASS","cycles":0,"evacuated":0,"scavenged":0,"reclaimed":0,"evidence":"cycles=0 evacuated=0 scavenged=0 reclaimed=0"},{"test":"test_gap_gc_net_once_flags_rekey","arm":"default","result":"PASS","cycles":3,"evacuated":0,"scavenged":8465,"reclaimed":11702312,"evidence":"cycles=3 evacuated=0 scavenged=8465 reclaimed=11702312"},{"test":"test_gap_gc_net_once_flags_rekey","arm":"safepoint_minor","result":"PASS","cycles":3,"evacuated":0,"scavenged":8465,"reclaimed":11702312,"evidence":"cycles=3 evacuated=0 scavenged=8465 reclaimed=11702312"},{"test":"test_gap_gc_net_once_flags_rekey","arm":"evac_minor","result":"PASS","cycles":2,"evacuated":0,"scavenged":343,"reclaimed":9419960,"evidence":"cycles=2 evacuated=0 scavenged=343 reclaimed=9419960"},{"test":"test_gap_gc_net_once_flags_rekey","arm":"verify_evac","result":"PASS","cycles":3,"evacuated":0,"scavenged":8465,"reclaimed":11702312,"evidence":"cycles=3 evacuated=0 scavenged=8465 reclaimed=11702312"},{"test":"test_gap_gc_net_once_flags_rekey","arm":"force_verify","result":"PASS","cycles":4,"evacuated":0,"scavenged":8805,"reclaimed":19925400,"evidence":"cycles=4 evacuated=0 scavenged=8805 reclaimed=19925400"},{"test":"test_gap_gc_net_once_flags_rekey","arm":"cons_scan_off","result":"PASS","cycles":3,"evacuated":0,"scavenged":8465,"reclaimed":11702312,"evidence":"cycles=3 evacuated=0 scavenged=8465 reclaimed=11702312"},{"test":"test_gap_gc_net_once_flags_rekey","arm":"shipped_default","result":"PASS","cycles":0,"evacuated":0,"scavenged":0,"reclaimed":0,"evidence":"cycles=0 evacuated=0 scavenged=0 reclaimed=0"},{"test":"test_gap_gc_http2_pending_event_callback_rooting","arm":"default","result":"FAIL","cycles":0,"evacuated":0,"scavenged":0,"reclaimed":0,"evidence":"output-mismatch cycles=0 evacuated=0 scavenged=0 reclaimed=0"},{"test":"test_gap_gc_http2_pending_event_callback_rooting","arm":"safepoint_minor","result":"FAIL","cycles":0,"evacuated":0,"scavenged":0,"reclaimed":0,"evidence":"output-mismatch cycles=0 evacuated=0 scavenged=0 reclaimed=0"},{"test":"test_gap_gc_http2_pending_event_callback_rooting","arm":"evac_minor","result":"FAIL","cycles":1,"evacuated":0,"scavenged":0,"reclaimed":23800,"evidence":"output-mismatch cycles=1 evacuated=0 scavenged=0 reclaimed=23800"},{"test":"test_gap_gc_http2_pending_event_callback_rooting","arm":"verify_evac","result":"FAIL","cycles":0,"evacuated":0,"scavenged":0,"reclaimed":0,"evidence":"output-mismatch cycles=0 evacuated=0 scavenged=0 reclaimed=0"},{"test":"test_gap_gc_http2_pending_event_callback_rooting","arm":"force_verify","result":"FAIL","cycles":1,"evacuated":0,"scavenged":4175,"reclaimed":71400,"evidence":"output-mismatch cycles=1 evacuated=0 scavenged=4175 reclaimed=71400"},{"test":"test_gap_gc_http2_pending_event_callback_rooting","arm":"cons_scan_off","result":"FAIL","cycles":0,"evacuated":0,"scavenged":0,"reclaimed":0,"evidence":"output-mismatch cycles=0 evacuated=0 scavenged=0 reclaimed=0"},{"test":"test_gap_gc_http2_pending_event_callback_rooting","arm":"shipped_default","result":"FAIL","cycles":0,"evacuated":0,"scavenged":0,"reclaimed":0,"evidence":"output-mismatch cycles=0 evacuated=0 scavenged=0 reclaimed=0"},{"test":"test_gap_gc_interp_global_this_rooting","arm":"default","result":"UNVER","cycles":0,"evacuated":0,"scavenged":0,"reclaimed":0,"evidence":"cycles=0 evacuated=0 scavenged=0 reclaimed=0"},{"test":"test_gap_gc_interp_global_this_rooting","arm":"safepoint_minor","result":"UNVER","cycles":0,"evacuated":0,"scavenged":0,"reclaimed":0,"evidence":"cycles=0 evacuated=0 scavenged=0 reclaimed=0"},{"test":"test_gap_gc_interp_global_this_rooting","arm":"evac_minor","result":"UNVER","cycles":1,"evacuated":0,"scavenged":0,"reclaimed":22560,"evidence":"cycles=1 evacuated=0 scavenged=0 reclaimed=22560"},{"test":"test_gap_gc_interp_global_this_rooting","arm":"verify_evac","result":"UNVER","cycles":0,"evacuated":0,"scavenged":0,"reclaimed":0,"evidence":"cycles=0 evacuated=0 scavenged=0 reclaimed=0"},{"test":"test_gap_gc_interp_global_this_rooting","arm":"force_verify","result":"PASS","cycles":1,"evacuated":0,"scavenged":4115,"reclaimed":67680,"evidence":"cycles=1 evacuated=0 scavenged=4115 reclaimed=67680"},{"test":"test_gap_gc_interp_global_this_rooting","arm":"cons_scan_off","result":"UNVER","cycles":0,"evacuated":0,"scavenged":0,"reclaimed":0,"evidence":"cycles=0 evacuated=0 scavenged=0 reclaimed=0"},{"test":"test_gap_gc_interp_global_this_rooting","arm":"shipped_default","result":"PASS","cycles":0,"evacuated":0,"scavenged":0,"reclaimed":0,"evidence":"cycles=0 evacuated=0 scavenged=0 reclaimed=0"},{"test":"test_gap_gc_string_copy_source_rooting","arm":"default","result":"PASS","cycles":1,"evacuated":0,"scavenged":7,"reclaimed":3148344,"evidence":"cycles=1 evacuated=0 scavenged=7 reclaimed=3148344"},{"test":"test_gap_gc_string_copy_source_rooting","arm":"safepoint_minor","result":"PASS","cycles":1,"evacuated":0,"scavenged":7,"reclaimed":3148344,"evidence":"cycles=1 evacuated=0 scavenged=7 reclaimed=3148344"},{"test":"test_gap_gc_string_copy_source_rooting","arm":"evac_minor","result":"PASS","cycles":1,"evacuated":0,"scavenged":7,"reclaimed":3148344,"evidence":"cycles=1 evacuated=0 scavenged=7 reclaimed=3148344"},{"test":"test_gap_gc_string_copy_source_rooting","arm":"verify_evac","result":"PASS","cycles":1,"evacuated":0,"scavenged":7,"reclaimed":3148344,"evidence":"cycles=1 evacuated=0 scavenged=7 reclaimed=3148344"},{"test":"test_gap_gc_string_copy_source_rooting","arm":"force_verify","result":"PASS","cycles":1,"evacuated":0,"scavenged":7,"reclaimed":3148344,"evidence":"cycles=1 evacuated=0 scavenged=7 reclaimed=3148344"},{"test":"test_gap_gc_string_copy_source_rooting","arm":"cons_scan_off","result":"PASS","cycles":1,"evacuated":0,"scavenged":7,"reclaimed":3148344,"evidence":"cycles=1 evacuated=0 scavenged=7 reclaimed=3148344"},{"test":"test_gap_gc_string_copy_source_rooting","arm":"shipped_default","result":"PASS","cycles":0,"evacuated":0,"scavenged":0,"reclaimed":0,"evidence":"cycles=0 evacuated=0 scavenged=0 reclaimed=0"},{"test":"test_gap_gc_string_repeat_reentrant_count","arm":"default","result":"UNVER","cycles":0,"evacuated":0,"scavenged":0,"reclaimed":0,"evidence":"cycles=0 evacuated=0 scavenged=0 reclaimed=0"},{"test":"test_gap_gc_string_repeat_reentrant_count","arm":"safepoint_minor","result":"UNVER","cycles":0,"evacuated":0,"scavenged":0,"reclaimed":0,"evidence":"cycles=0 evacuated=0 scavenged=0 reclaimed=0"},{"test":"test_gap_gc_string_repeat_reentrant_count","arm":"evac_minor","result":"UNVER","cycles":1,"evacuated":0,"scavenged":0,"reclaimed":29752,"evidence":"cycles=1 evacuated=0 scavenged=0 reclaimed=29752"},{"test":"test_gap_gc_string_repeat_reentrant_count","arm":"verify_evac","result":"UNVER","cycles":0,"evacuated":0,"scavenged":0,"reclaimed":0,"evidence":"cycles=0 evacuated=0 scavenged=0 reclaimed=0"},{"test":"test_gap_gc_string_repeat_reentrant_count","arm":"force_verify","result":"PASS","cycles":1,"evacuated":0,"scavenged":4154,"reclaimed":89256,"evidence":"cycles=1 evacuated=0 scavenged=4154 reclaimed=89256"},{"test":"test_gap_gc_string_repeat_reentrant_count","arm":"cons_scan_off","result":"UNVER","cycles":0,"evacuated":0,"scavenged":0,"reclaimed":0,"evidence":"cycles=0 evacuated=0 scavenged=0 reclaimed=0"},{"test":"test_gap_gc_string_repeat_reentrant_count","arm":"shipped_default","result":"PASS","cycles":0,"evacuated":0,"scavenged":0,"reclaimed":0,"evidence":"cycles=0 evacuated=0 scavenged=0 reclaimed=0"},{"test":"test_gap_gc_coalesce_local_root","arm":"default","result":"PASS","cycles":13,"evacuated":0,"scavenged":11792,"reclaimed":78358488,"evidence":"cycles=13 evacuated=0 scavenged=11792 reclaimed=78358488"},{"test":"test_gap_gc_coalesce_local_root","arm":"safepoint_minor","result":"PASS","cycles":13,"evacuated":0,"scavenged":11792,"reclaimed":78358488,"evidence":"cycles=13 evacuated=0 scavenged=11792 reclaimed=78358488"},{"test":"test_gap_gc_coalesce_local_root","arm":"evac_minor","result":"PASS","cycles":14,"evacuated":0,"scavenged":12223,"reclaimed":94898968,"evidence":"cycles=14 evacuated=0 scavenged=12223 reclaimed=94898968"},{"test":"test_gap_gc_coalesce_local_root","arm":"verify_evac","result":"PASS","cycles":13,"evacuated":0,"scavenged":34001,"reclaimed":89143280,"evidence":"cycles=13 evacuated=0 scavenged=34001 reclaimed=89143280"},{"test":"test_gap_gc_coalesce_local_root","arm":"force_verify","result":"PASS","cycles":14,"evacuated":0,"scavenged":26425,"reclaimed":97490336,"evidence":"cycles=14 evacuated=0 scavenged=26425 reclaimed=97490336"},{"test":"test_gap_gc_coalesce_local_root","arm":"cons_scan_off","result":"PASS","cycles":14,"evacuated":0,"scavenged":12223,"reclaimed":94898968,"evidence":"cycles=14 evacuated=0 scavenged=12223 reclaimed=94898968"},{"test":"test_gap_gc_coalesce_local_root","arm":"shipped_default","result":"PASS","cycles":1,"evacuated":0,"scavenged":8208,"reclaimed":36294288,"evidence":"cycles=1 evacuated=0 scavenged=8208 reclaimed=36294288"},{"test":"test_gap_gc_for_in_proxy_callback_roots","arm":"default","result":"UNVER","cycles":0,"evacuated":0,"scavenged":0,"reclaimed":0,"evidence":"cycles=0 evacuated=0 scavenged=0 reclaimed=0"},{"test":"test_gap_gc_for_in_proxy_callback_roots","arm":"safepoint_minor","result":"UNVER","cycles":0,"evacuated":0,"scavenged":0,"reclaimed":0,"evidence":"cycles=0 evacuated=0 scavenged=0 reclaimed=0"},{"test":"test_gap_gc_for_in_proxy_callback_roots","arm":"evac_minor","result":"UNVER","cycles":1,"evacuated":0,"scavenged":0,"reclaimed":85440,"evidence":"cycles=1 evacuated=0 scavenged=0 reclaimed=85440"},{"test":"test_gap_gc_for_in_proxy_callback_roots","arm":"verify_evac","result":"UNVER","cycles":0,"evacuated":0,"scavenged":0,"reclaimed":0,"evidence":"cycles=0 evacuated=0 scavenged=0 reclaimed=0"},{"test":"test_gap_gc_for_in_proxy_callback_roots","arm":"force_verify","result":"PASS","cycles":1,"evacuated":0,"scavenged":4220,"reclaimed":256320,"evidence":"cycles=1 evacuated=0 scavenged=4220 reclaimed=256320"},{"test":"test_gap_gc_for_in_proxy_callback_roots","arm":"cons_scan_off","result":"UNVER","cycles":0,"evacuated":0,"scavenged":0,"reclaimed":0,"evidence":"cycles=0 evacuated=0 scavenged=0 reclaimed=0"},{"test":"test_gap_gc_for_in_proxy_callback_roots","arm":"shipped_default","result":"PASS","cycles":0,"evacuated":0,"scavenged":0,"reclaimed":0,"evidence":"cycles=0 evacuated=0 scavenged=0 reclaimed=0"},{"test":"test_gap_gc_string_suffix_cursor","arm":"default","result":"UNVER","cycles":0,"evacuated":0,"scavenged":0,"reclaimed":0,"evidence":"cycles=0 evacuated=0 scavenged=0 reclaimed=0"},{"test":"test_gap_gc_string_suffix_cursor","arm":"safepoint_minor","result":"UNVER","cycles":0,"evacuated":0,"scavenged":0,"reclaimed":0,"evidence":"cycles=0 evacuated=0 scavenged=0 reclaimed=0"},{"test":"test_gap_gc_string_suffix_cursor","arm":"evac_minor","result":"PASS","cycles":1,"evacuated":0,"scavenged":41,"reclaimed":116928,"evidence":"cycles=1 evacuated=0 scavenged=41 reclaimed=116928"},{"test":"test_gap_gc_string_suffix_cursor","arm":"verify_evac","result":"UNVER","cycles":0,"evacuated":0,"scavenged":0,"reclaimed":0,"evidence":"cycles=0 evacuated=0 scavenged=0 reclaimed=0"},{"test":"test_gap_gc_string_suffix_cursor","arm":"force_verify","result":"PASS","cycles":1,"evacuated":0,"scavenged":41,"reclaimed":116928,"evidence":"cycles=1 evacuated=0 scavenged=41 reclaimed=116928"},{"test":"test_gap_gc_string_suffix_cursor","arm":"cons_scan_off","result":"UNVER","cycles":0,"evacuated":0,"scavenged":0,"reclaimed":0,"evidence":"cycles=0 evacuated=0 scavenged=0 reclaimed=0"},{"test":"test_gap_gc_string_suffix_cursor","arm":"shipped_default","result":"PASS","cycles":0,"evacuated":0,"scavenged":0,"reclaimed":0,"evidence":"cycles=0 evacuated=0 scavenged=0 reclaimed=0"},{"test":"test_gap_gc_array_shift_queue","arm":"default","result":"UNVER","cycles":13,"evacuated":0,"scavenged":0,"reclaimed":6292440,"evidence":"cycles=13 evacuated=0 scavenged=0 reclaimed=6292440"},{"test":"test_gap_gc_array_shift_queue","arm":"safepoint_minor","result":"UNVER","cycles":13,"evacuated":0,"scavenged":0,"reclaimed":6292440,"evidence":"cycles=13 evacuated=0 scavenged=0 reclaimed=6292440"},{"test":"test_gap_gc_array_shift_queue","arm":"evac_minor","result":"UNVER","cycles":13,"evacuated":0,"scavenged":0,"reclaimed":6292440,"evidence":"cycles=13 evacuated=0 scavenged=0 reclaimed=6292440"},{"test":"test_gap_gc_array_shift_queue","arm":"verify_evac","result":"PASS","cycles":13,"evacuated":0,"scavenged":12456,"reclaimed":7871088,"evidence":"cycles=13 evacuated=0 scavenged=12456 reclaimed=7871088"},{"test":"test_gap_gc_array_shift_queue","arm":"force_verify","result":"PASS","cycles":25,"evacuated":0,"scavenged":44772,"reclaimed":2326064,"evidence":"cycles=25 evacuated=0 scavenged=44772 reclaimed=2326064"},{"test":"test_gap_gc_array_shift_queue","arm":"cons_scan_off","result":"UNVER","cycles":13,"evacuated":0,"scavenged":0,"reclaimed":6292440,"evidence":"cycles=13 evacuated=0 scavenged=0 reclaimed=6292440"},{"test":"test_gap_gc_array_shift_queue","arm":"shipped_default","result":"PASS","cycles":12,"evacuated":0,"scavenged":0,"reclaimed":13270688,"evidence":"cycles=12 evacuated=0 scavenged=0 reclaimed=13270688"}],"summary":{"pass":428,"unverified":167,"xfail":0,"fail":7}} diff --git a/benchmarks/native_property_get/evidence/measurement-receipts.tar.gz b/benchmarks/native_property_get/evidence/measurement-receipts.tar.gz new file mode 100644 index 0000000000..b2e1b15828 Binary files /dev/null and b/benchmarks/native_property_get/evidence/measurement-receipts.tar.gz differ diff --git a/benchmarks/native_property_get/evidence/parity.stdout b/benchmarks/native_property_get/evidence/parity.stdout new file mode 100644 index 0000000000..afc75a2cbf --- /dev/null +++ b/benchmarks/native_property_get/evidence/parity.stdout @@ -0,0 +1,42 @@ +inherited data true +shadowed data true +deleted shadow true +own undefined true +frozen prototype true +frozen own data true +own getter result true +own getter once true +distinct receiver true +distinct getter once true +inherited getter receiver true +inherited getter once true +data shadows getter true +shadow suppresses getter true +setter only true +proxy result true +proxy once true +proxy prototype true +prototype proxy once true +revoked proxy throws true +coerced key result true +coercion then getter once true +symbol key true +private-looking string true +long key true +inherited class static true +private field true +class expression static true +closure expando true +array expando true +array length true +typed array index true +date constructor true +regexp expando true +regexp lastIndex true +regexp accessor true +regexp getter once true +inherited toJSON getter receiver true +toJSON getter once true +toJSON method once true +noncallable toJSON true +native property Get parity passed diff --git a/benchmarks/native_property_get/evidence/runtime-source-sha256.json b/benchmarks/native_property_get/evidence/runtime-source-sha256.json new file mode 100644 index 0000000000..b694018dc0 --- /dev/null +++ b/benchmarks/native_property_get/evidence/runtime-source-sha256.json @@ -0,0 +1,9 @@ +{ + "crates/perry-runtime/src/json/stringify.rs": "9ca55d9bb6ce1903a6fb53958601c6e42fc952ab093d5cbd598768212f4041bc", + "crates/perry-runtime/src/object/field_get_set/get_field_by_name.rs": "7fdde1279dcc2ab18ef17dd59b55d483a47e40d14d64cd94352cbf1ddc174067", + "crates/perry-runtime/src/object/mod.rs": "7c5950f1320cd07d3dfbceb4e27a81c3cfe994fce92b8e0eaa60e0a7c25d79c8", + "crates/perry-runtime/src/object/native_get.rs": "cf4e83f0e00ba10a51f759ccf487c1a5d3aec2d00dce836e80a00f7b11f19727", + "crates/perry-runtime/src/object/native_get/tests.rs": "d423834bdf4a2a054efc1003fca777bdf2f8fb89c4d93ec4fe3f809c81507e4d", + "crates/perry-runtime/src/proxy/reflect.rs": "32c75ac50727e3bab55c9433a603c7debce999732d1316f4288493457b2f6f45", + "crates/perry-runtime/src/value/dynamic_object.rs": "c27f4e02c6b498ce70299ba782a9dd31b8dc38ba4fa4dca3d2e18ee558c59f19" +} diff --git a/benchmarks/native_property_get/evidence/validation-logs.tar.gz b/benchmarks/native_property_get/evidence/validation-logs.tar.gz new file mode 100644 index 0000000000..3550f98a82 Binary files /dev/null and b/benchmarks/native_property_get/evidence/validation-logs.tar.gz differ diff --git a/benchmarks/native_property_get/evidence/validation.json b/benchmarks/native_property_get/evidence/validation.json new file mode 100644 index 0000000000..0189269932 --- /dev/null +++ b/benchmarks/native_property_get/evidence/validation.json @@ -0,0 +1,31 @@ +{ + "runtime_tests": { + "candidate": { + "passed": 3803, + "ignored": 4, + "failed": 0 + }, + "base": { + "passed": 3795, + "ignored": 4, + "failed": 0 + } + }, + "regex_off_check": "pass", + "fmt": "pass", + "clippy_new_diagnostics": 0, + "fault_mutations_killed": 3, + "node_parity_output_lines": 42, + "lint": { + "commands": 83, + "failed": 3, + "ci_only_skipped": 2, + "failures": [ + "public benchmark freshness (reproduced on base)", + "workspace warnings (existing WebAssembly helpers and test cache assignment)", + "API docs drift (unchanged API manifest adds bun-pty)" + ] + }, + "gc_ratchet": "same-host base comparison passes; 126 medians identical; old pinned baseline fails on both", + "gc_stress": "428 PASS, 167 UNVER, seven HTTP/2 failures reproduced exactly on base; all required arms live" +} diff --git a/benchmarks/native_property_get/faults.py b/benchmarks/native_property_get/faults.py new file mode 100644 index 0000000000..8aaf6594c3 --- /dev/null +++ b/benchmarks/native_property_get/faults.py @@ -0,0 +1,74 @@ +#!/usr/bin/env python3 +"""Run three source mutations serially, restoring each file even on failure. + +Run only while no other build or validation reads this worktree. +""" +import argparse +import json +from pathlib import Path +import subprocess + + +def main(): + parser = argparse.ArgumentParser(description=__doc__) + parser.add_argument("--out", required=True, type=Path) + args = parser.parse_args() + repo = Path(__file__).resolve().parents[2] + out = args.out.resolve() + out.mkdir(parents=True, exist_ok=False) + native_get = repo / "crates/perry-runtime/src/object/native_get.rs" + expando = repo / "crates/perry-runtime/src/object/exotic_expando.rs" + prefix = "object::native_get::tests::" + mutations = [ + ("fast_path_removed", native_get, + "if !receiver.is_pointer() || key.first() == Some(&b'#')", + "if true || !receiver.is_pointer() || key.first() == Some(&b'#')", + prefix + "own_data_is_served_and_matches_forced_slow_for_all_value_kinds"), + ("accessor_guard_removed", native_get, + "if meta.is_null() || (*meta).accessor_key_bits & accessor_bit == 0 {", + "if true {", + prefix + "accessor_bloom_guard_preserves_own_and_inherited_getter_calls"), + ("exotic_expando_ignored", expando, + 'if kind != ExoticKind::Error && !expando_in_use() && !super::descriptors_in_use() {', + 'if kind == ExoticKind::RegExp && name == "value" { return None; }\n' + ' if kind != ExoticKind::Error && !expando_in_use() && !super::descriptors_in_use() {', + prefix + "regexp_expandos_and_accessors_remain_on_the_exotic_path"), + ] + + def test(name, label, exact=False): + argv = ["cargo", "test", "-j4", "-p", "perry-runtime", "--lib", name, + "--", "--test-threads=1"] + if exact: + argv.append("--exact") + with (out / (label + ".log")).open("w") as log: + result = subprocess.run(argv, cwd=repo, stdout=log, stderr=subprocess.STDOUT) + return result.returncode, (out / (label + ".log")).read_text() + + code, _ = test("object::native_get::tests", "healthy_before") + if code: + raise RuntimeError("healthy native Get tests failed before fault injection") + results = [] + for name, path, old, new, test_name in mutations: + original = path.read_bytes() + text = original.decode() + if text.count(old) != 1: + raise ValueError(f"{name}: mutation anchor is not unique") + try: + path.write_text(text.replace(old, new)) + code, log = test(test_name, name, exact=True) + killed = code != 0 and f"test {test_name} ... FAILED" in log and "test result: FAILED" in log + results.append({"mutation": name, "test": test_name, + "exit_code": code, "assertion_failed": killed}) + (out / "results.json").write_text(json.dumps(results, indent=2) + "\n") + if not killed: + raise RuntimeError(f"{name}: expected an assertion failure, see log") + print(name, "killed by", test_name, flush=True) + finally: + path.write_bytes(original) + code, _ = test("object::native_get::tests", "healthy_after") + if code: + raise RuntimeError("healthy native Get tests failed after restoring sources") + + +if __name__ == "__main__": + main() diff --git a/benchmarks/native_property_get/iterator.ts b/benchmarks/native_property_get/iterator.ts new file mode 100644 index 0000000000..e5099b3164 --- /dev/null +++ b/benchmarks/native_property_get/iterator.ts @@ -0,0 +1,17 @@ +// Reuse the result object to expose next/done/value reads without allocating +// a result or retaining a large array on every iteration. +const count = Number(process.argv[2] || "200000"); +let index = 0; +const result = { done: false, value: 0 }; +const iterator: any = { + next() { + result.done = index >= count; + result.value = index++; + return result; + }, +}; +const iterable: any = { [Symbol.iterator]() { return iterator; } }; +let sum = 0; +for (const value of iterable) sum += value; +if (sum !== count * (count - 1) / 2) throw new Error("iterator mismatch"); +console.log(sum); diff --git a/benchmarks/native_property_get/measure.py b/benchmarks/native_property_get/measure.py new file mode 100644 index 0000000000..e9b33defe9 --- /dev/null +++ b/benchmarks/native_property_get/measure.py @@ -0,0 +1,166 @@ +#!/usr/bin/env python3 +"""Compile checked probes, count Linux user instructions, and fold attribution.""" +import argparse +import collections +import hashlib +import json +import os +from pathlib import Path +import re +import shutil +import statistics +import subprocess + + +PROBES = { + "reflect_own": 200000, + "reflect_proto": 200000, + "iterator": 200000, + "thenable": 20000, + "tojson": 100000, +} +GET = re.compile( + r"js_reflect_get|js_object_get_field_by_name|js_dynamic_object_get_property" + r"|get_field_by_name_object_tail|native_get::" +) + + +def digest(path): + with path.open("rb") as stream: + return hashlib.file_digest(stream, "sha256").hexdigest() + + +def run(argv, **kwargs): + return subprocess.run(argv, check=True, **kwargs) + + +def fold(stream): + # Same root-to-leaf frame order as repro/tonly/fold.py. Preserve perf's + # sample periods as weights, including a shortened final sample period. + stacks = collections.Counter() + frames = [] + period = 0 + for line in stream: + if not line.strip(): + if frames: + stacks[";".join(reversed(frames))] += period + frames = [] + period = 0 + elif not line[0].isspace(): + match = re.search(r"\s(\d+)\s+instructions:u:", line) + if not match: + raise ValueError("unexpected perf sample header: " + line) + period = int(match[1]) + else: + parts = line.strip().split(None, 1) + if len(parts) == 2: + symbol = parts[1].rsplit(" (", 1)[0] + frames.append(re.sub(r"\+0x[0-9a-f]+$", "", symbol)) + if frames: + stacks[";".join(reversed(frames))] += period + if not stacks or not sum(stacks.values()): + raise ValueError("perf produced no instruction stacks") + return stacks + + +def main(): + parser = argparse.ArgumentParser(description=__doc__) + parser.add_argument("--runtime-dir", required=True, type=Path) + parser.add_argument("--out", required=True, type=Path) + parser.add_argument("--node", default="node") + parser.add_argument("--smoke", action="store_true", help="compile/parity only") + args = parser.parse_args() + source = Path(__file__).resolve().parent + repo = source.parents[1] + runtime = args.runtime_dir.resolve() + perry = runtime / "perry" + out = args.out.resolve() + out.mkdir(parents=True, exist_ok=False) + env = dict(os.environ, PERRY_RUNTIME_DIR=str(runtime), + PERRY_CACHE_DIR=str(out / "cache"), PERRY_NO_TELEMETRY="1") + # LLVM-built runtime DWARF can exceed the host binutils reader's support. + # Keep this override local to the run, including perf record's postlude. + addr2line = shutil.which("llvm-addr2line-22") + if addr2line: + config = out / "perfconfig" + config.write_text(f"[annotate]\naddr2line = {addr2line}\n") + env["PERF_CONFIG"] = str(config) + node_version = run([args.node, "--version"], capture_output=True, text=True).stdout.strip() + if node_version.lstrip("v") != (repo / ".node-version").read_text().strip().lstrip("v"): + raise ValueError("Node version does not match .node-version: " + node_version) + patch = run(["git", "-C", str(repo), "diff", "--binary", "HEAD"], capture_output=True).stdout + (out / "source.patch").write_bytes(patch) + metadata = { + "commit": run(["git", "-C", str(repo), "rev-parse", "HEAD"], + capture_output=True, text=True).stdout.strip(), + "node": node_version, + "source_patch_sha256": hashlib.sha256(patch).hexdigest(), + "artifacts": {name: digest(runtime / name) for name in + ("perry", "libperry_runtime.a", "libperry_stdlib.a")}, + "probes": {name: digest(source / (name + ".ts")) for name in PROBES}, + } + (out / "metadata.json").write_text(json.dumps(metadata, indent=2) + "\n") + results = {} + for name, default_count in PROBES.items(): + count = 100 if args.smoke else default_count + ts = source / (name + ".ts") + binary = out / name + with (out / (name + ".compile.log")).open("w") as log: + run([str(perry), "compile", str(ts), "--no-auto-optimize", "--debug-symbols", + "-o", str(binary)], + env=env, stdout=log, stderr=subprocess.STDOUT) + expected = run([args.node, "--experimental-strip-types", str(ts), str(count)], + capture_output=True).stdout + actual = run([str(binary), str(count)], env=env, capture_output=True).stdout + if not expected or actual != expected: + raise ValueError(f"{name}: Node {expected!r}, Perry {actual!r}") + (out / (name + ".stdout")).write_bytes(actual) + if args.smoke: + print(name, "parity passed", flush=True) + continue + counts = [] + for repeat in range(3): + stat = out / f"{name}.{repeat}.stat" + run(["perf", "stat", "-x,", "-e", "instructions:u", "-o", str(stat), + "--", str(binary), str(count)], env=env, stdout=subprocess.DEVNULL) + rows = [line.split(",") for line in stat.read_text().splitlines()] + counts.append(next(int(row[0]) for row in rows if len(row) > 2 + and row[2] == "instructions:u")) + data = out / (name + ".perf.data") + with (out / (name + ".record.log")).open("w") as log: + run(["perf", "record", "--no-buildid", "--no-buildid-cache", "-m", "64M", + "-e", "instructions:u", "-c", "500003", + "--call-graph", "dwarf,16384", "-o", str(data), "--", str(binary), str(count)], + env=env, stdout=subprocess.DEVNULL, stderr=log) + if "lost" in (out / (name + ".record.log")).read_text().lower(): + raise ValueError(f"{name}: perf lost samples; attribution is incomplete") + with subprocess.Popen(["perf", "script", "--no-inline", "-i", str(data)], + stdout=subprocess.PIPE, text=True, env=env) as process: + stacks = fold(process.stdout) + if process.wait() != 0: + raise RuntimeError("perf script failed") + with (out / (name + ".folded")).open("w") as stream: + for stack, weight in stacks.most_common(): + stream.write(f"{weight} {stack}\n") + total = sum(stacks.values()) + get_weight = sum(weight for stack, weight in stacks.items() if GET.search(stack)) + if not get_weight: + raise ValueError(f"{name}: no Get frames; verify symbols and probe coverage") + leaves = collections.Counter() + for stack, weight in stacks.items(): + if GET.search(stack): + leaves[stack.rsplit(";", 1)[-1]] += weight + results[name] = { + "iterations": count, "instructions": counts, + "median": statistics.median(counts), + "binary_sha256": digest(binary), "sampled_instructions": total, + "get_inclusive_percent": 100 * get_weight / total, + "get_leaf_percent_of_program": [(leaf, 100 * weight / total) + for leaf, weight in leaves.most_common(20)], + } + (out / "results.json").write_text(json.dumps(results, indent=2) + "\n") + print(name, results[name]["median"], results[name]["get_inclusive_percent"], flush=True) + + +if __name__ == "__main__": + main() diff --git a/benchmarks/native_property_get/reflect_own.ts b/benchmarks/native_property_get/reflect_own.ts new file mode 100644 index 0000000000..e3f788f2c3 --- /dev/null +++ b/benchmarks/native_property_get/reflect_own.ts @@ -0,0 +1,7 @@ +// Runtime Reflect.get entry, with an ordinary own data property. +const count = Number(process.argv[2] || "200000"); +const object: any = { value: 7 }; +let sum = 0; +for (let i = 0; i < count; i++) sum += Reflect.get(object, "value"); +if (sum !== count * 7) throw new Error("own data read mismatch"); +console.log(sum); diff --git a/benchmarks/native_property_get/reflect_proto.ts b/benchmarks/native_property_get/reflect_proto.ts new file mode 100644 index 0000000000..fd45556cfd --- /dev/null +++ b/benchmarks/native_property_get/reflect_proto.ts @@ -0,0 +1,7 @@ +// Same read through two explicitly recorded ordinary prototype links. +const count = Number(process.argv[2] || "200000"); +const object: any = Object.create(Object.create({ value: 7 })); +let sum = 0; +for (let i = 0; i < count; i++) sum += Reflect.get(object, "value"); +if (sum !== count * 7) throw new Error("prototype data read mismatch"); +console.log(sum); diff --git a/benchmarks/native_property_get/thenable.ts b/benchmarks/native_property_get/thenable.ts new file mode 100644 index 0000000000..6ad27063ee --- /dev/null +++ b/benchmarks/native_property_get/thenable.ts @@ -0,0 +1,14 @@ +// A positive then lookup; the existing negative-then probe cannot serve it. +const count = Number(process.argv[2] || "20000"); +const thenable: any = { then(resolve: any) { resolve(7); } }; +let sum = 0; +let completed = 0; +for (let i = 0; i < count; i++) { + Promise.resolve(thenable).then((value: number) => { + sum += value; + if (++completed === count) { + if (sum !== count * 7) throw new Error("thenable mismatch"); + console.log(sum); + } + }); +} diff --git a/benchmarks/native_property_get/tojson.ts b/benchmarks/native_property_get/tojson.ts new file mode 100644 index 0000000000..532ffe260c --- /dev/null +++ b/benchmarks/native_property_get/tojson.ts @@ -0,0 +1,11 @@ +// A positive inherited toJSON lookup on every stringify call. +const count = Number(process.argv[2] || "100000"); +const object: any = Object.create({ toJSON() { return 7; } }); +let sum = 0; +for (let i = 0; i < count; i++) { + const text = JSON.stringify(object); + if (text !== "7") throw new Error("toJSON mismatch"); + sum += text.length; +} +if (sum !== count) throw new Error("stringify count mismatch"); +console.log(sum); diff --git a/benchmarks/runtime_handle_scopes/README.md b/benchmarks/runtime_handle_scopes/README.md new file mode 100644 index 0000000000..211c2c94d1 --- /dev/null +++ b/benchmarks/runtime_handle_scopes/README.md @@ -0,0 +1,134 @@ +# Runtime handle scopes — 2026-09-14 + +Baseline: `9fda98df68d9fac3c08b2385fae007aa9f5278df` (Perry 0.5.1563). +Linux host: perrymaster.skelpo.net, x86_64. The baseline was built from a clean +worktree; the promise profile was recorded before runtime edits began. + +## Instruction counts + +Whole-program user instructions, `perf stat -e instructions:u -r 3`: + +| workload | baseline | changed | reduction | +|---|---:|---:|---:| +| 100,000 promise/await iterations | 1,100,168,994 | 1,050,989,735 | 4.47% | +| 100,000 JSON round-trips | 3,190,619,152 | 3,132,631,719 | 1.82% | +| Original hoisted regex `test` probe | 13,773,038,016 | 13,003,278,589 | 5.59% | +| Original regex `exec1` probe | 31,188,918,430 | 29,854,497,805 | 4.28% | + +Every binary's stdout matched Node 26.5.1. These are complete workload counts, +including startup and input construction, without subtracting a loop control. +No wall-clock speedup is claimed on the shared host. The measured build uses +the repository's release profile with 16 codegen units, debug level 1, and no +symbol stripping; both sides use identical settings and package selections. + +`perf record -e instructions:u -c 1000003 --call-graph dwarf` produced the +folded stacks in `evidence/`. The pre-edit promise profile had 1,097 samples; +4.10% of leaves named runtime handles or operations on their slots. JSON had +3,189 samples and 3.98% such leaves. This classifier includes inlined primitive +slot loads/stores, not just function-call overhead. The corresponding changed +shares are 4.29% and 0.48%; residual handle work does not vanish in every probe. +Inclusive shares also contain callbacks invoked through handle helpers and +must not be interpreted as the cost of rooting itself. Instruction totals, +rather than these small sampled shares, are the performance verdict. + +## Reproduction + +Build each source tree independently with the same environment: + +```sh +export CARGO_PROFILE_RELEASE_CODEGEN_UNITS=16 +export CARGO_PROFILE_RELEASE_DEBUG=1 +export CARGO_PROFILE_RELEASE_STRIP=none +export CARGO_INCREMENTAL=0 +cargo build --release -p perry -p perry-runtime-static -p perry-stdlib-static +PERRY_RUNTIME_DIR="$PWD/target/release" PERRY_NO_AUTO_OPTIMIZE=1 \ + python3 benchmarks/runtime_handle_scopes/measure.py \ + --perry "$PWD/target/release/perry" --output /tmp/handle-profile +``` + +For the recorded A/B, `PERRY_BUILD_COMMIT` was pinned to the baseline SHA for +both complete builds and their compiler invocations. This intentionally permits +relinking the changed runtime while keeping coherence stamps equal; it does not +replace rebuilding both static wrappers. Compiler/archive hashes are retained +in `evidence/` and under `/root/runtime-handle-evidence/` on the measurement host. +The later GC stress build also selects `perry-ext-net` and `perry-ext-http` +to unify their Tokio configuration with the stdlib. Its runtime archive hash +is unchanged; its compiler and stdlib hashes differ from the measured build. +Raw perf data +and validation logs are also retained locally in +`/tmp/perry-runtime-handle-evidence/`. + +## Contracts and tradeoffs + +The live prefix is still `[0, top)`. Handles remain indices so buffer growth +does not invalidate them, and the scope lifetime and bounds/kind checks remain. +On native TLS targets, scopes and handles cache a reference to non-dropping +metadata; its `Cell` fields prevent sending them between threads. These scopes +and handles grow from one machine word to two. Android and HarmonyOS retain scoped Vec +access through a thread-bound token because its OS-backed TLS frees even +non-dropping metadata at teardown. Each root slot shrinks from 24 to 16 bytes +by encoding the raw tag in the variant discriminant. The merge train retains +the original four-slot initial capacity (64 bytes with the smaller slots), then +doubles it. The instruction measurements above used the PR's earlier 64-slot +initial capacity; they do not measure the changed initial growth schedule. + +The existing named hot-TLS pointer and its offsets stay in place. The fallback +metadata can be accessed without filling the hot cache, preserving #9183's +initialization contract. On native TLS targets, a separate guard owns the buffer, unpublishes its +cache pointer, and clears the live prefix before freeing it. Late scope +destructors see empty metadata; a push after buffer destruction fails through +the destroyed owner guard instead of resurrecting storage. On OS-backed TLS, the Vec owns +its allocation and late scopes resolve the TLS key again instead of retaining +an address after teardown. Cache unpublication tolerates an Android pool that +has already been destroyed. + +Scanners visit local slot copies and commit relocations by index. This avoids +references into reallocated storage, including a reentrant legacy Copy visitor; +a non-rewriting callback cannot have its own update overwritten. Throw restore +and scope drop both retain `Vec::truncate`'s non-growing semantics. The root +write barrier tests the existing global idle flag before decoding slot kind; +an active cycle still uses the existing per-thread shading machinery. + +## Failure evidence + +`fault-results.json` records all four injected faults: + +| fault | observed failure | +|---|---| +| Scan `[0, top - 1)` | Moving-root test: final root 256 was not relocated. | +| Skip throw-savepoint truncation | Real caught-throw test: live depth does not return to its saved value. | +| Skip root shading during marking | Marking test: newly published handle target is still white. | +| Skip cache unpublication | Late TLS destructor asserts that the cache pointer is null; process aborts. | + +All mutations were removed. The restored full runtime suite passed 3,802 tests, +with four ignored and zero failures. Regex-off checking passed. Runtime clippy +diagnostics match the clean baseline exactly, including its 12 existing +`approx_constant` errors in tests. The root-holder custody and raw-handle debt +ratchets pass without modifying their inventories. + +The GC ratchet was measured with seven runs per probe on macOS arm64. Both +builds match Node on all 14 probes, and all 126 heap/cycle/copy/promotion/free +metric medians are identical. Both fail the same 30 cells against the older +pinned artifact; this is a reproduced baseline failure, not a green pinned +ratchet. The two compressed measurements, exact comparison, and shared list +of pinned failures are in `evidence/`. RSS and wall time are excluded from +this comparison, as required by the shared-host profile. + +GC instrument smoke passes, including all 14 real probes and the forced +verification/evacuation checks. The full GC matrix matches Node in 595/602 +cells (428 PASS, 167 UNVER); every required collection mode is live. The seven +failures are the HTTP/2 pending-event fixture in all arms: its server tries +port 443, already occupied on the host, and exits without callback output. +An independently rebuilt clean Linux baseline produces the identical empty +output and bind error; `http2-baseline-comparison.json` records both results. +In a private network namespace, both binaries time out after 20 seconds without +callbacks (`http2-netns-results.json`); this fixture remains unverified. +The matrix JSON is retained without suppressing those failures. + +The full lint replay executes 83 gates: 80 pass, three fail, and two additional +CI-only checks are skipped. All three failures reproduce on the clean baseline: +public benchmark freshness, two dead-code errors in unchanged WebAssembly +helpers under the workspace warning check, and missing generated `bun-pty` +API documentation. The clean baseline compiler generates byte-identical API +documents to the changed compiler. The generated drift is retained as evidence +and is not included as an unrelated source change. diff --git a/benchmarks/runtime_handle_scopes/evidence/api-docs-drift.patch.gz b/benchmarks/runtime_handle_scopes/evidence/api-docs-drift.patch.gz new file mode 100644 index 0000000000..2914094f81 Binary files /dev/null and b/benchmarks/runtime_handle_scopes/evidence/api-docs-drift.patch.gz differ diff --git a/benchmarks/runtime_handle_scopes/evidence/baseline-artifact-sha256.txt b/benchmarks/runtime_handle_scopes/evidence/baseline-artifact-sha256.txt new file mode 100644 index 0000000000..2a6ffc7e3a --- /dev/null +++ b/benchmarks/runtime_handle_scopes/evidence/baseline-artifact-sha256.txt @@ -0,0 +1,3 @@ +640ea229e6db05f6fd866ab51fd9e639d7de0d8abd3386bf413b2f3e55ea578b /root/runtime-handle-scopes-20260914/target/release/perry +6ac20705316e387871b5b7e4188adeb9d76e3dcb30fc265e081b235b4c5766fe /root/runtime-handle-scopes-20260914/target/release/libperry_runtime.a +697f36b9c7821649e089877a43561ba22ce147aef7020d4e870ee7a090d06bd9 /root/runtime-handle-scopes-20260914/target/release/libperry_stdlib.a diff --git a/benchmarks/runtime_handle_scopes/evidence/baseline-exec1.folded.gz b/benchmarks/runtime_handle_scopes/evidence/baseline-exec1.folded.gz new file mode 100644 index 0000000000..b1c5813133 Binary files /dev/null and b/benchmarks/runtime_handle_scopes/evidence/baseline-exec1.folded.gz differ diff --git a/benchmarks/runtime_handle_scopes/evidence/baseline-exec1.stat b/benchmarks/runtime_handle_scopes/evidence/baseline-exec1.stat new file mode 100644 index 0000000000..189dd6603c --- /dev/null +++ b/benchmarks/runtime_handle_scopes/evidence/baseline-exec1.stat @@ -0,0 +1,3 @@ +# started on Mon Sep 14 03:46:30 2026 + +31188918430;;instructions:u;0.00%;2404090881;100.00;; diff --git a/benchmarks/runtime_handle_scopes/evidence/baseline-gc-ratchet.json.gz b/benchmarks/runtime_handle_scopes/evidence/baseline-gc-ratchet.json.gz new file mode 100644 index 0000000000..0926319b88 Binary files /dev/null and b/benchmarks/runtime_handle_scopes/evidence/baseline-gc-ratchet.json.gz differ diff --git a/benchmarks/runtime_handle_scopes/evidence/baseline-hoist.folded.gz b/benchmarks/runtime_handle_scopes/evidence/baseline-hoist.folded.gz new file mode 100644 index 0000000000..e5bfffccb1 Binary files /dev/null and b/benchmarks/runtime_handle_scopes/evidence/baseline-hoist.folded.gz differ diff --git a/benchmarks/runtime_handle_scopes/evidence/baseline-hoist.stat b/benchmarks/runtime_handle_scopes/evidence/baseline-hoist.stat new file mode 100644 index 0000000000..f926925167 --- /dev/null +++ b/benchmarks/runtime_handle_scopes/evidence/baseline-hoist.stat @@ -0,0 +1,3 @@ +# started on Mon Sep 14 03:45:11 2026 + +13773038016;;instructions:u;0.00%;845736469;100.00;; diff --git a/benchmarks/runtime_handle_scopes/evidence/baseline-json.folded.gz b/benchmarks/runtime_handle_scopes/evidence/baseline-json.folded.gz new file mode 100644 index 0000000000..0aa2a75978 Binary files /dev/null and b/benchmarks/runtime_handle_scopes/evidence/baseline-json.folded.gz differ diff --git a/benchmarks/runtime_handle_scopes/evidence/baseline-json.stat b/benchmarks/runtime_handle_scopes/evidence/baseline-json.stat new file mode 100644 index 0000000000..0f0e259f1c --- /dev/null +++ b/benchmarks/runtime_handle_scopes/evidence/baseline-json.stat @@ -0,0 +1,3 @@ +# started on Mon Sep 14 03:44:03 2026 + +3190619152;;instructions:u;0.00%;182814018;100.00;; diff --git a/benchmarks/runtime_handle_scopes/evidence/baseline-promises.folded.gz b/benchmarks/runtime_handle_scopes/evidence/baseline-promises.folded.gz new file mode 100644 index 0000000000..9ea94c56a5 Binary files /dev/null and b/benchmarks/runtime_handle_scopes/evidence/baseline-promises.folded.gz differ diff --git a/benchmarks/runtime_handle_scopes/evidence/baseline-promises.stat b/benchmarks/runtime_handle_scopes/evidence/baseline-promises.stat new file mode 100644 index 0000000000..856901a822 --- /dev/null +++ b/benchmarks/runtime_handle_scopes/evidence/baseline-promises.stat @@ -0,0 +1,3 @@ +# started on Mon Sep 14 03:43:37 2026 + +1100168994;;instructions:u;0.00%;67322145;100.00;; diff --git a/benchmarks/runtime_handle_scopes/evidence/baseline-workspace-warnings.log b/benchmarks/runtime_handle_scopes/evidence/baseline-workspace-warnings.log new file mode 100644 index 0000000000..ec02b37022 --- /dev/null +++ b/benchmarks/runtime_handle_scopes/evidence/baseline-workspace-warnings.log @@ -0,0 +1,163 @@ + Compiling perry-runtime v0.5.1563 (/Users/amlug/projects/perry/wt-runtime-handle-baseline/crates/perry-runtime) + Compiling aws-lc-rs v1.17.0 + Checking aws-lc-sys v0.41.0 + Checking rand_xorshift v0.4.0 + Compiling phf_shared v0.13.1 + Compiling rustls v0.23.43 + Checking nom v8.0.0 + Checking unarray v0.1.4 + Compiling fastrand v2.4.1 + Compiling phf_generator v0.13.1 + Checking proptest v1.11.0 + Checking rustls-webpki v0.103.13 + Compiling ident_case v1.0.1 + Compiling strsim v0.11.1 + Checking crypto-bigint v0.7.5 + Checking pkcs8 v0.11.0 + Checking hashbrown v0.16.1 + Checking concurrent-queue v2.5.0 + Checking parking v2.2.1 + Checking unicode-bidi v0.3.18 + Checking unicode-properties v0.1.4 + Checking tokio-rustls v0.26.4 + Checking tungstenite v0.29.0 + Checking hyper-rustls v0.27.9 + Checking reqwest v0.12.28 + Checking tokio-tungstenite v0.29.0 + Checking stringprep v0.1.5 + Checking event-listener v5.4.1 + Compiling equator-macro v0.4.2 + Compiling crunchy v0.2.4 + Compiling tiny-keccak v2.0.2 + Checking equator v0.4.2 + Checking weezl v0.1.12 + Checking hashlink v0.11.1 + Checking aligned-vec v0.6.4 + Compiling phf_codegen v0.13.1 + Checking matchers v0.2.0 + Checking sharded-slab v0.1.7 + Checking console v0.15.11 + Checking tracing-log v0.2.0 + Checking thread_local v1.1.9 + Compiling glob v0.3.3 + Checking byteorder v1.5.0 + Checking nu-ansi-term v0.50.3 + Checking dialoguer v0.11.0 + Checking tracing-subscriber v0.3.23 + Compiling clang-sys v1.8.1 + Checking v_frame v0.3.9 + Checking crypto-primes v0.7.2 + Checking pkcs1 v0.8.0-rc.4 + Compiling getrandom v0.2.17 + Checking num-rational v0.4.2 + Checking as-slice v0.2.1 + Checking atty v0.2.14 + Checking which v8.0.6 + Compiling paste v1.0.15 + Compiling unicode-segmentation v1.13.3 + Compiling once_cell v1.21.4 + Compiling av-scenechange v0.14.1 + Checking base64 v0.23.1 + Compiling built v0.8.1 + Checking rsa v0.10.0-rc.18 + Compiling convert_case v0.10.0 + Compiling rav1e v0.8.1 + Compiling const-random-macro v0.1.16 + Checking aligned v0.4.3 + Compiling phf_macros v0.13.1 + Checking no_std_io2 v0.9.4 + Compiling profiling-procmacros v1.0.18 + Compiling num-derive v0.4.2 + Compiling arg_enum_proc_macro v0.3.4 + Compiling regex-syntax v0.8.11 + Checking quick-error v2.0.1 + Compiling pastey v0.1.1 + Checking imgref v1.12.3 + Checking crc-catalog v2.5.0 + Checking y4m v0.8.0 + Checking alloc-no-stdlib v2.0.4 + Compiling unicode-xid v0.2.6 + Compiling minimal-lexical v0.2.1 + Compiling derive_more-impl v2.1.1 + Compiling nom v7.1.3 + Checking alloc-stdlib v0.2.2 + Compiling regex-automata v0.4.18 + Checking crc v3.4.0 + Checking phf v0.13.1 + Checking profiling v1.0.18 + Checking bitstream-io v4.10.0 + Compiling const-random v0.1.18 + Checking perry-container-compose v0.5.1563 (/Users/amlug/projects/perry/wt-runtime-handle-baseline/crates/perry-container-compose) + Checking av1-grain v0.2.5 + Compiling string_cache_codegen v0.6.1 + Checking rgb v0.8.53 + Checking maybe-rayon v0.1.1 + Checking miniz_oxide v0.8.9 + Compiling libloading v0.8.9 + Checking futures-intrusive v0.5.0 + Checking tokio-stream v0.1.18 + Checking crossbeam-queue v0.3.12 + Checking itertools v0.14.0 + Checking half v2.7.1 + Compiling macro_magic_core_macros v0.5.1 + Compiling derive-syn-parse v0.2.0 + Compiling simd_helpers v0.1.0 + Compiling fnv v1.0.7 + Compiling either v1.16.0 + Compiling noop_proc_macro v0.3.0 + Checking zune-core v0.5.1 + Compiling bindgen v0.72.1 + Checking precomputed-hash v0.1.1 + Checking zune-jpeg v0.5.15 + Compiling itertools v0.12.1 + Compiling darling_core v0.20.11 + Compiling macro_magic_core v0.5.1 + Checking sqlx-core v0.9.0 + Compiling web_atoms v0.2.4 + Compiling regex v1.13.1 + Checking derive_more v2.1.1 + Compiling cexpr v0.6.0 + Checking brotli-decompressor v5.0.1 + Checking loop9 v0.1.5 + Compiling darling_core v0.23.0 + Checking zune-inflate v0.2.54 + Checking fdeflate v0.3.7 + Checking core-foundation v0.10.1 + Checking security-framework-sys v2.17.0 + Checking avif-serialize v0.8.9 + Compiling system-configuration-sys v0.6.0 + Checking lebe v0.5.3 + Compiling rustc-hash v2.1.2 + Checking fax v0.2.7 + Checking byteorder-lite v0.1.0 + Compiling shlex v1.3.0 + Checking color_quant v1.1.0 + Checking pxfm v0.1.29 + Compiling bitflags v2.12.1 + Checking bit_field v0.10.3 + Checking exr v1.74.0 + Compiling darling_macro v0.23.0 + Checking moxcms v0.8.1 + Checking gif v0.14.2 + Checking image-webp v0.2.4 + Checking tiff v0.11.3 +error: function `registered_extern_handle` is never used + --> crates/perry-runtime/src/object/global_this_webassembly.rs:201:15 + | +201 | pub(crate) fn registered_extern_handle(wrapper: usize, expected_kind: &[u8]) -> Option { + | ^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: `-D dead-code` implied by `-D warnings` + = help: to override `-D warnings` add `#[expect(dead_code)]` or `#[allow(dead_code)]` + +error: function `wasm_memory_descriptor_maximum` is never used + --> crates/perry-runtime/src/object/global_this_webassembly.rs:662:4 + | +662 | fn wasm_memory_descriptor_maximum(descriptor: f64) -> u32 { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + + Checking ravif v0.13.0 + Checking security-framework v3.7.0 + Checking png v0.18.1 +error: could not compile `perry-runtime` (lib) due to 2 previous errors +warning: build failed, waiting for other jobs to finish... diff --git a/benchmarks/runtime_handle_scopes/evidence/fault-omit-last-root.txt b/benchmarks/runtime_handle_scopes/evidence/fault-omit-last-root.txt new file mode 100644 index 0000000000..18f4307517 --- /dev/null +++ b/benchmarks/runtime_handle_scopes/evidence/fault-omit-last-root.txt @@ -0,0 +1,7 @@ + +thread 'gc::tests::runtime_roots::handle_stack::handle_stack_growth_preserves_every_moving_root_including_last' (140172551) panicked at crates/perry-runtime/src/gc/tests/runtime_roots/handle_stack.rs:36:9: +assertion `left != right` failed: root 256 was not relocated + left: 5193047556104 + right: 5193047556104 +note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace +failures: diff --git a/benchmarks/runtime_handle_scopes/evidence/fault-skip-cache-unpublish-direct.txt b/benchmarks/runtime_handle_scopes/evidence/fault-skip-cache-unpublish-direct.txt new file mode 100644 index 0000000000..c52d35f341 --- /dev/null +++ b/benchmarks/runtime_handle_scopes/evidence/fault-skip-cache-unpublish-direct.txt @@ -0,0 +1,5 @@ +test gc::roots::runtime_handles::tests::handle_storage_teardown_clears_cache_before_late_scope_drop ... +thread '' (140212420) panicked at crates/perry-runtime/src/gc/roots/runtime_handles/tests.rs:69:13: +assertion failed: crate::tls_hot::hot().runtime_handle_stack.get().is_null() +note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace +fatal runtime error: thread local panicked on drop, aborting diff --git a/benchmarks/runtime_handle_scopes/evidence/fault-skip-marking-barrier.txt b/benchmarks/runtime_handle_scopes/evidence/fault-skip-marking-barrier.txt new file mode 100644 index 0000000000..db6628c14a --- /dev/null +++ b/benchmarks/runtime_handle_scopes/evidence/fault-skip-marking-barrier.txt @@ -0,0 +1,7 @@ + +thread 'gc::tests::runtime_roots::handle_stack::handle_stack_pushes_and_updates_shade_all_kinds_during_marking' (140202315) panicked at crates/perry-runtime/src/gc/tests/runtime_roots.rs:109:9: +assertion `left != right` failed: new handle during marking should be marked + left: 0 + right: 0 +note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace +failures: diff --git a/benchmarks/runtime_handle_scopes/evidence/fault-skip-throw-truncate.txt b/benchmarks/runtime_handle_scopes/evidence/fault-skip-throw-truncate.txt new file mode 100644 index 0000000000..c136279d53 --- /dev/null +++ b/benchmarks/runtime_handle_scopes/evidence/fault-skip-throw-truncate.txt @@ -0,0 +1,6 @@ + +thread 'gc::tests::runtime_roots::handle_stack::handle_stack_real_throw_restores_live_outer_scope_and_ffi_roots' (140196641) panicked at crates/perry-runtime/src/gc/tests/runtime_roots/handle_stack.rs:88:5: +assertion `left == right` failed + left: 259 + right: 1 +note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace diff --git a/benchmarks/runtime_handle_scopes/evidence/fix-artifact-sha256-after-ext.txt b/benchmarks/runtime_handle_scopes/evidence/fix-artifact-sha256-after-ext.txt new file mode 100644 index 0000000000..41088dee6d --- /dev/null +++ b/benchmarks/runtime_handle_scopes/evidence/fix-artifact-sha256-after-ext.txt @@ -0,0 +1,3 @@ +0fe4c749815c59704150d6d3cc27307e99c1cbf76b38d546ac7224dbc7b3d9ee target/release/perry +4c4ca74d1a1c2c44a4031484becbdaed81c9ad74f40714b0fb117ce90c3c894f target/release/libperry_runtime.a +59d55f6218322b4af78ac9fd91768990490c70c408cd7a63f80b1fae783c4317 target/release/libperry_stdlib.a diff --git a/benchmarks/runtime_handle_scopes/evidence/fix-artifact-sha256-before-ext.txt b/benchmarks/runtime_handle_scopes/evidence/fix-artifact-sha256-before-ext.txt new file mode 100644 index 0000000000..264cf6a1c0 --- /dev/null +++ b/benchmarks/runtime_handle_scopes/evidence/fix-artifact-sha256-before-ext.txt @@ -0,0 +1,3 @@ +0a51732419d93c3d0840a3b579029ea5f4757053ec43d234234cb00d00f6baaf target/release/perry +4c4ca74d1a1c2c44a4031484becbdaed81c9ad74f40714b0fb117ce90c3c894f target/release/libperry_runtime.a +1cd675153e968f039b680651813e5606f55bab986757e40c7a98ad288941da29 target/release/libperry_stdlib.a diff --git a/benchmarks/runtime_handle_scopes/evidence/fix-exec1.folded.gz b/benchmarks/runtime_handle_scopes/evidence/fix-exec1.folded.gz new file mode 100644 index 0000000000..783b24ce28 Binary files /dev/null and b/benchmarks/runtime_handle_scopes/evidence/fix-exec1.folded.gz differ diff --git a/benchmarks/runtime_handle_scopes/evidence/fix-exec1.stat b/benchmarks/runtime_handle_scopes/evidence/fix-exec1.stat new file mode 100644 index 0000000000..012a181ae1 --- /dev/null +++ b/benchmarks/runtime_handle_scopes/evidence/fix-exec1.stat @@ -0,0 +1,3 @@ +# started on Mon Sep 14 04:01:58 2026 + +29854497805;;instructions:u;0.00%;2506884207;100.00;; diff --git a/benchmarks/runtime_handle_scopes/evidence/fix-gc-instrument-smoke.log b/benchmarks/runtime_handle_scopes/evidence/fix-gc-instrument-smoke.log new file mode 100644 index 0000000000..9b8c6a2a5e --- /dev/null +++ b/benchmarks/runtime_handle_scopes/evidence/fix-gc-instrument-smoke.log @@ -0,0 +1,70 @@ +== compiling fixture with PERRY_GC_MOVING_LOOP_POLLS=1 (the schedule needs the polls) == +perry: in-process LLVM backend active (LLVM 22.1.8) +[perry] codegen: modules finished 1/1 (100%; 0.0 min elapsed; ETA ~0.0 min) +== arm 1: instruments OFF (baseline correctness) == + [off] correct output, exit 0, quarantine retirements=0 +== arm 2: PROTECT_FROMSPACE=1, no schedule (pressure-only) == + [protect] correct output, exit 0, quarantine retirements=0 +== arm 3: PROTECT_FROMSPACE=1 + SCHEDULE_RATE=1 (the investigation pairing) == + [protect+rate1] correct output, exit 0, quarantine retirements=1236 +== arm 4: PROTECT_FROMSPACE=1 + SCHEDULE_SEED (the tunable middle) == + [protect+schedule] correct output, exit 0, quarantine retirements=287 +== arm 5: the same seed again (the reproducer property) == + [protect+schedule-repeat] correct output, exit 0, quarantine retirements=287 +== arm 6: a different seed (the sweep must explore something) == + [protect+schedule-other] correct output, exit 0, quarantine retirements=302 + + [seeded schedule] pressure-only=0 < seeded(0.25)=287 < rate-1=1236 + [seeded schedule] same seed twice: 287 == 287 (reproducible) + +== arm 7: quarantine over the gc_ratchet probes (allocation-point route) == +perry: in-process LLVM backend active (LLVM 22.1.8) +[perry] codegen: modules finished 1/1 (100%; 0.0 min elapsed; ETA ~0.0 min) +perry: in-process LLVM backend active (LLVM 22.1.8) +[perry] codegen: modules finished 1/1 (100%; 0.0 min elapsed; ETA ~0.0 min) +perry: in-process LLVM backend active (LLVM 22.1.8) +[perry] codegen: modules finished 1/1 (100%; 0.0 min elapsed; ETA ~0.0 min) +perry: in-process LLVM backend active (LLVM 22.1.8) +[perry] codegen: modules finished 1/1 (100%; 0.0 min elapsed; ETA ~0.0 min) +perry: in-process LLVM backend active (LLVM 22.1.8) +[perry] codegen: modules finished 1/1 (100%; 0.0 min elapsed; ETA ~0.0 min) +perry: in-process LLVM backend active (LLVM 22.1.8) +[perry] codegen: modules finished 1/1 (100%; 0.0 min elapsed; ETA ~0.0 min) +perry: in-process LLVM backend active (LLVM 22.1.8) +[perry] codegen: modules finished 1/1 (100%; 0.0 min elapsed; ETA ~0.0 min) +perry: in-process LLVM backend active (LLVM 22.1.8) +[perry] codegen: modules finished 1/1 (100%; 0.0 min elapsed; ETA ~0.0 min) +perry: in-process LLVM backend active (LLVM 22.1.8) +[perry] codegen: modules finished 1/1 (100%; 0.0 min elapsed; ETA ~0.0 min) +perry: in-process LLVM backend active (LLVM 22.1.8) +[perry] codegen: modules finished 1/1 (100%; 0.0 min elapsed; ETA ~0.0 min) +perry: in-process LLVM backend active (LLVM 22.1.8) +[perry] codegen: modules finished 1/1 (100%; 0.0 min elapsed; ETA ~0.0 min) +perry: in-process LLVM backend active (LLVM 22.1.8) +[perry] codegen: modules finished 1/1 (100%; 0.0 min elapsed; ETA ~0.0 min) +perry: in-process LLVM backend active (LLVM 22.1.8) +[perry] codegen: modules finished 1/1 (100%; 0.0 min elapsed; ETA ~0.0 min) +perry: in-process LLVM backend active (LLVM 22.1.8) +[perry] codegen: modules finished 1/1 (100%; 0.0 min elapsed; ETA ~0.0 min) + 14/14 probes clean over from-space quarantine + +== arm 5: PERRY_GC_SCHEDULE_RATE=1 + PERRY_GC_VERIFY_EVACUATION (#7254's pairing) == +-- 5a: the pairing on known-good code must stay clean, and must be LIVE -- + correct output, exit 0, 3869 objects copied under the verifier (live, no false positive) +-- 5b: #7254's retained-growth workload must be correct under the pairing, and LIVE -- +[perry] codegen: modules finished 1/1 (100%; 0.0 min elapsed; ETA ~0.0 min) + correct output vs node, exit 0, 12532 objects copied under the verifier (live retained-growth coverage) + +== arm 6: the schedule terminates at the DEFAULT stride, on a realistic poll count == +perry: in-process LLVM backend active (LLVM 22.1.8) +[perry] codegen: modules finished 1/1 (100%; 0.0 min elapsed; ETA ~0.0 min) + correct output in 0s (budget 90s), [gc-schedule] forced_collections=2332 copying_minors=2332 moved_objects=149070 loop_polls=400065 + +PASS: instruments inert when off (0 retirements), live when on + (pressure-only=0, rate-1=1236 retirements), program correct in all arms. + Quarantine clean over 14 real probes (allocation-point route). + RATE=1+VERIFY_EVACUATION pairing live and correct on known-good code, + and validates #7254's retained-growth workload against Node with relocation live. + The schedule terminates at the shipped default on a realistic poll count + (0s of a 90s budget) while still forcing + 2332 collections that moved 149070 objects. diff --git a/benchmarks/runtime_handle_scopes/evidence/fix-gc-ratchet.json.gz b/benchmarks/runtime_handle_scopes/evidence/fix-gc-ratchet.json.gz new file mode 100644 index 0000000000..7f060eb3f4 Binary files /dev/null and b/benchmarks/runtime_handle_scopes/evidence/fix-gc-ratchet.json.gz differ diff --git a/benchmarks/runtime_handle_scopes/evidence/fix-gc-stress-coherent.json b/benchmarks/runtime_handle_scopes/evidence/fix-gc-stress-coherent.json new file mode 100644 index 0000000000..c69d90b9dc --- /dev/null +++ b/benchmarks/runtime_handle_scopes/evidence/fix-gc-stress-coherent.json @@ -0,0 +1 @@ +{"node":"26.5.1","pressure_mb":"8","complete":true,"shard":{"index":1,"count":1,"corpus_total":86,"corpus_selected":86},"arms":[{"id":"default","requires":"scavenge"},{"id":"safepoint_minor","requires":"scavenge"},{"id":"evac_minor","requires":"move"},{"id":"verify_evac","requires":"scavenge"},{"id":"force_verify","requires":"move"},{"id":"cons_scan_off","requires":"scavenge"},{"id":"shipped_default","requires":"none"}],"cells":[{"test":"test_gap_repsel_canonical_i32","arm":"default","result":"UNVER","cycles":0,"evacuated":0,"scavenged":0,"reclaimed":0,"evidence":"cycles=0 evacuated=0 scavenged=0 reclaimed=0"},{"test":"test_gap_repsel_canonical_i32","arm":"safepoint_minor","result":"UNVER","cycles":0,"evacuated":0,"scavenged":0,"reclaimed":0,"evidence":"cycles=0 evacuated=0 scavenged=0 reclaimed=0"},{"test":"test_gap_repsel_canonical_i32","arm":"evac_minor","result":"UNVER","cycles":1,"evacuated":0,"scavenged":0,"reclaimed":86128,"evidence":"cycles=1 evacuated=0 scavenged=0 reclaimed=86128"},{"test":"test_gap_repsel_canonical_i32","arm":"verify_evac","result":"UNVER","cycles":0,"evacuated":0,"scavenged":0,"reclaimed":0,"evidence":"cycles=0 evacuated=0 scavenged=0 reclaimed=0"},{"test":"test_gap_repsel_canonical_i32","arm":"force_verify","result":"PASS","cycles":1,"evacuated":0,"scavenged":6462,"reclaimed":215320,"evidence":"cycles=1 evacuated=0 scavenged=6462 reclaimed=215320"},{"test":"test_gap_repsel_canonical_i32","arm":"cons_scan_off","result":"UNVER","cycles":0,"evacuated":0,"scavenged":0,"reclaimed":0,"evidence":"cycles=0 evacuated=0 scavenged=0 reclaimed=0"},{"test":"test_gap_repsel_canonical_i32","arm":"shipped_default","result":"PASS","cycles":0,"evacuated":0,"scavenged":0,"reclaimed":0,"evidence":"cycles=0 evacuated=0 scavenged=0 reclaimed=0"},{"test":"test_gap_int_valued_ta_locals","arm":"default","result":"UNVER","cycles":0,"evacuated":0,"scavenged":0,"reclaimed":0,"evidence":"cycles=0 evacuated=0 scavenged=0 reclaimed=0"},{"test":"test_gap_int_valued_ta_locals","arm":"safepoint_minor","result":"UNVER","cycles":0,"evacuated":0,"scavenged":0,"reclaimed":0,"evidence":"cycles=0 evacuated=0 scavenged=0 reclaimed=0"},{"test":"test_gap_int_valued_ta_locals","arm":"evac_minor","result":"PASS","cycles":1,"evacuated":0,"scavenged":12,"reclaimed":12200,"evidence":"cycles=1 evacuated=0 scavenged=12 reclaimed=12200"},{"test":"test_gap_int_valued_ta_locals","arm":"verify_evac","result":"UNVER","cycles":0,"evacuated":0,"scavenged":0,"reclaimed":0,"evidence":"cycles=0 evacuated=0 scavenged=0 reclaimed=0"},{"test":"test_gap_int_valued_ta_locals","arm":"force_verify","result":"PASS","cycles":1,"evacuated":0,"scavenged":12,"reclaimed":12200,"evidence":"cycles=1 evacuated=0 scavenged=12 reclaimed=12200"},{"test":"test_gap_int_valued_ta_locals","arm":"cons_scan_off","result":"UNVER","cycles":0,"evacuated":0,"scavenged":0,"reclaimed":0,"evidence":"cycles=0 evacuated=0 scavenged=0 reclaimed=0"},{"test":"test_gap_int_valued_ta_locals","arm":"shipped_default","result":"PASS","cycles":0,"evacuated":0,"scavenged":0,"reclaimed":0,"evidence":"cycles=0 evacuated=0 scavenged=0 reclaimed=0"},{"test":"test_gap_repsel_loop_bounded_i32","arm":"default","result":"UNVER","cycles":0,"evacuated":0,"scavenged":0,"reclaimed":0,"evidence":"cycles=0 evacuated=0 scavenged=0 reclaimed=0"},{"test":"test_gap_repsel_loop_bounded_i32","arm":"safepoint_minor","result":"UNVER","cycles":0,"evacuated":0,"scavenged":0,"reclaimed":0,"evidence":"cycles=0 evacuated=0 scavenged=0 reclaimed=0"},{"test":"test_gap_repsel_loop_bounded_i32","arm":"evac_minor","result":"UNVER","cycles":1,"evacuated":0,"scavenged":0,"reclaimed":84176,"evidence":"cycles=1 evacuated=0 scavenged=0 reclaimed=84176"},{"test":"test_gap_repsel_loop_bounded_i32","arm":"verify_evac","result":"UNVER","cycles":0,"evacuated":0,"scavenged":0,"reclaimed":0,"evidence":"cycles=0 evacuated=0 scavenged=0 reclaimed=0"},{"test":"test_gap_repsel_loop_bounded_i32","arm":"force_verify","result":"PASS","cycles":1,"evacuated":0,"scavenged":6442,"reclaimed":210440,"evidence":"cycles=1 evacuated=0 scavenged=6442 reclaimed=210440"},{"test":"test_gap_repsel_loop_bounded_i32","arm":"cons_scan_off","result":"UNVER","cycles":0,"evacuated":0,"scavenged":0,"reclaimed":0,"evidence":"cycles=0 evacuated=0 scavenged=0 reclaimed=0"},{"test":"test_gap_repsel_loop_bounded_i32","arm":"shipped_default","result":"PASS","cycles":0,"evacuated":0,"scavenged":0,"reclaimed":0,"evidence":"cycles=0 evacuated=0 scavenged=0 reclaimed=0"},{"test":"test_gap_specabi_polymorphic_coexist","arm":"default","result":"UNVER","cycles":0,"evacuated":0,"scavenged":0,"reclaimed":0,"evidence":"cycles=0 evacuated=0 scavenged=0 reclaimed=0"},{"test":"test_gap_specabi_polymorphic_coexist","arm":"safepoint_minor","result":"UNVER","cycles":0,"evacuated":0,"scavenged":0,"reclaimed":0,"evidence":"cycles=0 evacuated=0 scavenged=0 reclaimed=0"},{"test":"test_gap_specabi_polymorphic_coexist","arm":"evac_minor","result":"UNVER","cycles":1,"evacuated":0,"scavenged":0,"reclaimed":81504,"evidence":"cycles=1 evacuated=0 scavenged=0 reclaimed=81504"},{"test":"test_gap_specabi_polymorphic_coexist","arm":"verify_evac","result":"UNVER","cycles":0,"evacuated":0,"scavenged":0,"reclaimed":0,"evidence":"cycles=0 evacuated=0 scavenged=0 reclaimed=0"},{"test":"test_gap_specabi_polymorphic_coexist","arm":"force_verify","result":"PASS","cycles":1,"evacuated":0,"scavenged":6463,"reclaimed":203760,"evidence":"cycles=1 evacuated=0 scavenged=6463 reclaimed=203760"},{"test":"test_gap_specabi_polymorphic_coexist","arm":"cons_scan_off","result":"UNVER","cycles":0,"evacuated":0,"scavenged":0,"reclaimed":0,"evidence":"cycles=0 evacuated=0 scavenged=0 reclaimed=0"},{"test":"test_gap_specabi_polymorphic_coexist","arm":"shipped_default","result":"PASS","cycles":0,"evacuated":0,"scavenged":0,"reclaimed":0,"evidence":"cycles=0 evacuated=0 scavenged=0 reclaimed=0"},{"test":"test_gap_specabi_reassign","arm":"default","result":"UNVER","cycles":0,"evacuated":0,"scavenged":0,"reclaimed":0,"evidence":"cycles=0 evacuated=0 scavenged=0 reclaimed=0"},{"test":"test_gap_specabi_reassign","arm":"safepoint_minor","result":"UNVER","cycles":0,"evacuated":0,"scavenged":0,"reclaimed":0,"evidence":"cycles=0 evacuated=0 scavenged=0 reclaimed=0"},{"test":"test_gap_specabi_reassign","arm":"evac_minor","result":"UNVER","cycles":1,"evacuated":0,"scavenged":0,"reclaimed":81072,"evidence":"cycles=1 evacuated=0 scavenged=0 reclaimed=81072"},{"test":"test_gap_specabi_reassign","arm":"verify_evac","result":"UNVER","cycles":0,"evacuated":0,"scavenged":0,"reclaimed":0,"evidence":"cycles=0 evacuated=0 scavenged=0 reclaimed=0"},{"test":"test_gap_specabi_reassign","arm":"force_verify","result":"PASS","cycles":1,"evacuated":0,"scavenged":6468,"reclaimed":202680,"evidence":"cycles=1 evacuated=0 scavenged=6468 reclaimed=202680"},{"test":"test_gap_specabi_reassign","arm":"cons_scan_off","result":"UNVER","cycles":0,"evacuated":0,"scavenged":0,"reclaimed":0,"evidence":"cycles=0 evacuated=0 scavenged=0 reclaimed=0"},{"test":"test_gap_specabi_reassign","arm":"shipped_default","result":"PASS","cycles":0,"evacuated":0,"scavenged":0,"reclaimed":0,"evidence":"cycles=0 evacuated=0 scavenged=0 reclaimed=0"},{"test":"test_gap_specabi_recursion_escape","arm":"default","result":"UNVER","cycles":0,"evacuated":0,"scavenged":0,"reclaimed":0,"evidence":"cycles=0 evacuated=0 scavenged=0 reclaimed=0"},{"test":"test_gap_specabi_recursion_escape","arm":"safepoint_minor","result":"UNVER","cycles":0,"evacuated":0,"scavenged":0,"reclaimed":0,"evidence":"cycles=0 evacuated=0 scavenged=0 reclaimed=0"},{"test":"test_gap_specabi_recursion_escape","arm":"evac_minor","result":"PASS","cycles":1,"evacuated":0,"scavenged":4,"reclaimed":2880,"evidence":"cycles=1 evacuated=0 scavenged=4 reclaimed=2880"},{"test":"test_gap_specabi_recursion_escape","arm":"verify_evac","result":"UNVER","cycles":0,"evacuated":0,"scavenged":0,"reclaimed":0,"evidence":"cycles=0 evacuated=0 scavenged=0 reclaimed=0"},{"test":"test_gap_specabi_recursion_escape","arm":"force_verify","result":"PASS","cycles":1,"evacuated":0,"scavenged":4,"reclaimed":2880,"evidence":"cycles=1 evacuated=0 scavenged=4 reclaimed=2880"},{"test":"test_gap_specabi_recursion_escape","arm":"cons_scan_off","result":"UNVER","cycles":0,"evacuated":0,"scavenged":0,"reclaimed":0,"evidence":"cycles=0 evacuated=0 scavenged=0 reclaimed=0"},{"test":"test_gap_specabi_recursion_escape","arm":"shipped_default","result":"PASS","cycles":0,"evacuated":0,"scavenged":0,"reclaimed":0,"evidence":"cycles=0 evacuated=0 scavenged=0 reclaimed=0"},{"test":"test_gap_specabi_view_detach","arm":"default","result":"UNVER","cycles":0,"evacuated":0,"scavenged":0,"reclaimed":0,"evidence":"cycles=0 evacuated=0 scavenged=0 reclaimed=0"},{"test":"test_gap_specabi_view_detach","arm":"safepoint_minor","result":"UNVER","cycles":0,"evacuated":0,"scavenged":0,"reclaimed":0,"evidence":"cycles=0 evacuated=0 scavenged=0 reclaimed=0"},{"test":"test_gap_specabi_view_detach","arm":"evac_minor","result":"PASS","cycles":1,"evacuated":0,"scavenged":6,"reclaimed":3760,"evidence":"cycles=1 evacuated=0 scavenged=6 reclaimed=3760"},{"test":"test_gap_specabi_view_detach","arm":"verify_evac","result":"UNVER","cycles":0,"evacuated":0,"scavenged":0,"reclaimed":0,"evidence":"cycles=0 evacuated=0 scavenged=0 reclaimed=0"},{"test":"test_gap_specabi_view_detach","arm":"force_verify","result":"PASS","cycles":1,"evacuated":0,"scavenged":6,"reclaimed":3760,"evidence":"cycles=1 evacuated=0 scavenged=6 reclaimed=3760"},{"test":"test_gap_specabi_view_detach","arm":"cons_scan_off","result":"UNVER","cycles":0,"evacuated":0,"scavenged":0,"reclaimed":0,"evidence":"cycles=0 evacuated=0 scavenged=0 reclaimed=0"},{"test":"test_gap_specabi_view_detach","arm":"shipped_default","result":"PASS","cycles":0,"evacuated":0,"scavenged":0,"reclaimed":0,"evidence":"cycles=0 evacuated=0 scavenged=0 reclaimed=0"},{"test":"test_gap_specabi_ordinary_param_guards","arm":"default","result":"PASS","cycles":19,"evacuated":0,"scavenged":297225,"reclaimed":1283776464,"evidence":"cycles=19 evacuated=0 scavenged=297225 reclaimed=1283776464"},{"test":"test_gap_specabi_ordinary_param_guards","arm":"safepoint_minor","result":"PASS","cycles":19,"evacuated":0,"scavenged":297225,"reclaimed":1283776464,"evidence":"cycles=19 evacuated=0 scavenged=297225 reclaimed=1283776464"},{"test":"test_gap_specabi_ordinary_param_guards","arm":"evac_minor","result":"PASS","cycles":19,"evacuated":0,"scavenged":297225,"reclaimed":1285371328,"evidence":"cycles=19 evacuated=0 scavenged=297225 reclaimed=1285371328"},{"test":"test_gap_specabi_ordinary_param_guards","arm":"verify_evac","result":"PASS","cycles":21,"evacuated":0,"scavenged":387309,"reclaimed":1323827528,"evidence":"cycles=21 evacuated=0 scavenged=387309 reclaimed=1323827528"},{"test":"test_gap_specabi_ordinary_param_guards","arm":"force_verify","result":"PASS","cycles":21,"evacuated":0,"scavenged":387309,"reclaimed":1324653696,"evidence":"cycles=21 evacuated=0 scavenged=387309 reclaimed=1324653696"},{"test":"test_gap_specabi_ordinary_param_guards","arm":"cons_scan_off","result":"PASS","cycles":19,"evacuated":0,"scavenged":297225,"reclaimed":1285371328,"evidence":"cycles=19 evacuated=0 scavenged=297225 reclaimed=1285371328"},{"test":"test_gap_specabi_ordinary_param_guards","arm":"shipped_default","result":"PASS","cycles":14,"evacuated":0,"scavenged":311091,"reclaimed":1217255040,"evidence":"cycles=14 evacuated=0 scavenged=311091 reclaimed=1217255040"},{"test":"test_gap_ta_param_numeric_read","arm":"default","result":"UNVER","cycles":1,"evacuated":0,"scavenged":0,"reclaimed":98240,"evidence":"cycles=1 evacuated=0 scavenged=0 reclaimed=98240"},{"test":"test_gap_ta_param_numeric_read","arm":"safepoint_minor","result":"UNVER","cycles":1,"evacuated":0,"scavenged":0,"reclaimed":98240,"evidence":"cycles=1 evacuated=0 scavenged=0 reclaimed=98240"},{"test":"test_gap_ta_param_numeric_read","arm":"evac_minor","result":"UNVER","cycles":1,"evacuated":0,"scavenged":0,"reclaimed":98240,"evidence":"cycles=1 evacuated=0 scavenged=0 reclaimed=98240"},{"test":"test_gap_ta_param_numeric_read","arm":"verify_evac","result":"PASS","cycles":1,"evacuated":0,"scavenged":6487,"reclaimed":245600,"evidence":"cycles=1 evacuated=0 scavenged=6487 reclaimed=245600"},{"test":"test_gap_ta_param_numeric_read","arm":"force_verify","result":"PASS","cycles":1,"evacuated":0,"scavenged":6487,"reclaimed":245600,"evidence":"cycles=1 evacuated=0 scavenged=6487 reclaimed=245600"},{"test":"test_gap_ta_param_numeric_read","arm":"cons_scan_off","result":"UNVER","cycles":1,"evacuated":0,"scavenged":0,"reclaimed":98240,"evidence":"cycles=1 evacuated=0 scavenged=0 reclaimed=98240"},{"test":"test_gap_ta_param_numeric_read","arm":"shipped_default","result":"PASS","cycles":0,"evacuated":0,"scavenged":0,"reclaimed":0,"evidence":"cycles=0 evacuated=0 scavenged=0 reclaimed=0"},{"test":"test_gap_typedarray_param_read","arm":"default","result":"UNVER","cycles":1,"evacuated":0,"scavenged":0,"reclaimed":94272,"evidence":"cycles=1 evacuated=0 scavenged=0 reclaimed=94272"},{"test":"test_gap_typedarray_param_read","arm":"safepoint_minor","result":"UNVER","cycles":1,"evacuated":0,"scavenged":0,"reclaimed":94272,"evidence":"cycles=1 evacuated=0 scavenged=0 reclaimed=94272"},{"test":"test_gap_typedarray_param_read","arm":"evac_minor","result":"UNVER","cycles":1,"evacuated":0,"scavenged":0,"reclaimed":94272,"evidence":"cycles=1 evacuated=0 scavenged=0 reclaimed=94272"},{"test":"test_gap_typedarray_param_read","arm":"verify_evac","result":"PASS","cycles":1,"evacuated":0,"scavenged":6494,"reclaimed":235680,"evidence":"cycles=1 evacuated=0 scavenged=6494 reclaimed=235680"},{"test":"test_gap_typedarray_param_read","arm":"force_verify","result":"PASS","cycles":1,"evacuated":0,"scavenged":6494,"reclaimed":235680,"evidence":"cycles=1 evacuated=0 scavenged=6494 reclaimed=235680"},{"test":"test_gap_typedarray_param_read","arm":"cons_scan_off","result":"UNVER","cycles":1,"evacuated":0,"scavenged":0,"reclaimed":94272,"evidence":"cycles=1 evacuated=0 scavenged=0 reclaimed=94272"},{"test":"test_gap_typedarray_param_read","arm":"shipped_default","result":"PASS","cycles":0,"evacuated":0,"scavenged":0,"reclaimed":0,"evidence":"cycles=0 evacuated=0 scavenged=0 reclaimed=0"},{"test":"test_gap_repsel_canonical_str_locals","arm":"default","result":"UNVER","cycles":0,"evacuated":0,"scavenged":0,"reclaimed":0,"evidence":"cycles=0 evacuated=0 scavenged=0 reclaimed=0"},{"test":"test_gap_repsel_canonical_str_locals","arm":"safepoint_minor","result":"UNVER","cycles":0,"evacuated":0,"scavenged":0,"reclaimed":0,"evidence":"cycles=0 evacuated=0 scavenged=0 reclaimed=0"},{"test":"test_gap_repsel_canonical_str_locals","arm":"evac_minor","result":"PASS","cycles":1,"evacuated":0,"scavenged":71,"reclaimed":29280,"evidence":"cycles=1 evacuated=0 scavenged=71 reclaimed=29280"},{"test":"test_gap_repsel_canonical_str_locals","arm":"verify_evac","result":"UNVER","cycles":0,"evacuated":0,"scavenged":0,"reclaimed":0,"evidence":"cycles=0 evacuated=0 scavenged=0 reclaimed=0"},{"test":"test_gap_repsel_canonical_str_locals","arm":"force_verify","result":"PASS","cycles":1,"evacuated":0,"scavenged":71,"reclaimed":29280,"evidence":"cycles=1 evacuated=0 scavenged=71 reclaimed=29280"},{"test":"test_gap_repsel_canonical_str_locals","arm":"cons_scan_off","result":"UNVER","cycles":0,"evacuated":0,"scavenged":0,"reclaimed":0,"evidence":"cycles=0 evacuated=0 scavenged=0 reclaimed=0"},{"test":"test_gap_repsel_canonical_str_locals","arm":"shipped_default","result":"PASS","cycles":0,"evacuated":0,"scavenged":0,"reclaimed":0,"evidence":"cycles=0 evacuated=0 scavenged=0 reclaimed=0"},{"test":"test_gap_repsel_ptr_shape_locals","arm":"default","result":"PASS","cycles":1,"evacuated":0,"scavenged":6499,"reclaimed":2971760,"evidence":"cycles=1 evacuated=0 scavenged=6499 reclaimed=2971760"},{"test":"test_gap_repsel_ptr_shape_locals","arm":"safepoint_minor","result":"PASS","cycles":1,"evacuated":0,"scavenged":6499,"reclaimed":2971760,"evidence":"cycles=1 evacuated=0 scavenged=6499 reclaimed=2971760"},{"test":"test_gap_repsel_ptr_shape_locals","arm":"evac_minor","result":"PASS","cycles":1,"evacuated":0,"scavenged":6499,"reclaimed":2971760,"evidence":"cycles=1 evacuated=0 scavenged=6499 reclaimed=2971760"},{"test":"test_gap_repsel_ptr_shape_locals","arm":"verify_evac","result":"PASS","cycles":1,"evacuated":0,"scavenged":6499,"reclaimed":2971760,"evidence":"cycles=1 evacuated=0 scavenged=6499 reclaimed=2971760"},{"test":"test_gap_repsel_ptr_shape_locals","arm":"force_verify","result":"PASS","cycles":1,"evacuated":0,"scavenged":6499,"reclaimed":2971760,"evidence":"cycles=1 evacuated=0 scavenged=6499 reclaimed=2971760"},{"test":"test_gap_repsel_ptr_shape_locals","arm":"cons_scan_off","result":"PASS","cycles":1,"evacuated":0,"scavenged":6499,"reclaimed":2971760,"evidence":"cycles=1 evacuated=0 scavenged=6499 reclaimed=2971760"},{"test":"test_gap_repsel_ptr_shape_locals","arm":"shipped_default","result":"PASS","cycles":0,"evacuated":0,"scavenged":0,"reclaimed":0,"evidence":"cycles=0 evacuated=0 scavenged=0 reclaimed=0"},{"test":"test_gap_repsel_ptr_shape_barriers","arm":"default","result":"UNVER","cycles":0,"evacuated":0,"scavenged":0,"reclaimed":0,"evidence":"cycles=0 evacuated=0 scavenged=0 reclaimed=0"},{"test":"test_gap_repsel_ptr_shape_barriers","arm":"safepoint_minor","result":"UNVER","cycles":0,"evacuated":0,"scavenged":0,"reclaimed":0,"evidence":"cycles=0 evacuated=0 scavenged=0 reclaimed=0"},{"test":"test_gap_repsel_ptr_shape_barriers","arm":"evac_minor","result":"UNVER","cycles":1,"evacuated":0,"scavenged":0,"reclaimed":80880,"evidence":"cycles=1 evacuated=0 scavenged=0 reclaimed=80880"},{"test":"test_gap_repsel_ptr_shape_barriers","arm":"verify_evac","result":"UNVER","cycles":0,"evacuated":0,"scavenged":0,"reclaimed":0,"evidence":"cycles=0 evacuated=0 scavenged=0 reclaimed=0"},{"test":"test_gap_repsel_ptr_shape_barriers","arm":"force_verify","result":"PASS","cycles":1,"evacuated":0,"scavenged":6448,"reclaimed":202200,"evidence":"cycles=1 evacuated=0 scavenged=6448 reclaimed=202200"},{"test":"test_gap_repsel_ptr_shape_barriers","arm":"cons_scan_off","result":"UNVER","cycles":0,"evacuated":0,"scavenged":0,"reclaimed":0,"evidence":"cycles=0 evacuated=0 scavenged=0 reclaimed=0"},{"test":"test_gap_repsel_ptr_shape_barriers","arm":"shipped_default","result":"PASS","cycles":0,"evacuated":0,"scavenged":0,"reclaimed":0,"evidence":"cycles=0 evacuated=0 scavenged=0 reclaimed=0"},{"test":"test_gap_repsel_return_shape","arm":"default","result":"PASS","cycles":4,"evacuated":0,"scavenged":104906,"reclaimed":83126464,"evidence":"cycles=4 evacuated=0 scavenged=104906 reclaimed=83126464"},{"test":"test_gap_repsel_return_shape","arm":"safepoint_minor","result":"PASS","cycles":4,"evacuated":0,"scavenged":104906,"reclaimed":83126464,"evidence":"cycles=4 evacuated=0 scavenged=104906 reclaimed=83126464"},{"test":"test_gap_repsel_return_shape","arm":"evac_minor","result":"PASS","cycles":4,"evacuated":0,"scavenged":104906,"reclaimed":83126464,"evidence":"cycles=4 evacuated=0 scavenged=104906 reclaimed=83126464"},{"test":"test_gap_repsel_return_shape","arm":"verify_evac","result":"PASS","cycles":6,"evacuated":0,"scavenged":114327,"reclaimed":83811552,"evidence":"cycles=6 evacuated=0 scavenged=114327 reclaimed=83811552"},{"test":"test_gap_repsel_return_shape","arm":"force_verify","result":"PASS","cycles":6,"evacuated":0,"scavenged":114327,"reclaimed":85580320,"evidence":"cycles=6 evacuated=0 scavenged=114327 reclaimed=85580320"},{"test":"test_gap_repsel_return_shape","arm":"cons_scan_off","result":"PASS","cycles":4,"evacuated":0,"scavenged":104906,"reclaimed":83126464,"evidence":"cycles=4 evacuated=0 scavenged=104906 reclaimed=83126464"},{"test":"test_gap_repsel_return_shape","arm":"shipped_default","result":"PASS","cycles":2,"evacuated":0,"scavenged":124204,"reclaimed":94536760,"evidence":"cycles=2 evacuated=0 scavenged=124204 reclaimed=94536760"},{"test":"test_gap_repsel_cjs_iife_return_shape","arm":"default","result":"PASS","cycles":3,"evacuated":0,"scavenged":61992,"reclaimed":45602912,"evidence":"cycles=3 evacuated=0 scavenged=61992 reclaimed=45602912"},{"test":"test_gap_repsel_cjs_iife_return_shape","arm":"safepoint_minor","result":"PASS","cycles":3,"evacuated":0,"scavenged":61992,"reclaimed":45602912,"evidence":"cycles=3 evacuated=0 scavenged=61992 reclaimed=45602912"},{"test":"test_gap_repsel_cjs_iife_return_shape","arm":"evac_minor","result":"PASS","cycles":3,"evacuated":0,"scavenged":61992,"reclaimed":45602912,"evidence":"cycles=3 evacuated=0 scavenged=61992 reclaimed=45602912"},{"test":"test_gap_repsel_cjs_iife_return_shape","arm":"verify_evac","result":"PASS","cycles":4,"evacuated":0,"scavenged":74301,"reclaimed":42792600,"evidence":"cycles=4 evacuated=0 scavenged=74301 reclaimed=42792600"},{"test":"test_gap_repsel_cjs_iife_return_shape","arm":"force_verify","result":"PASS","cycles":4,"evacuated":0,"scavenged":74301,"reclaimed":42792600,"evidence":"cycles=4 evacuated=0 scavenged=74301 reclaimed=42792600"},{"test":"test_gap_repsel_cjs_iife_return_shape","arm":"cons_scan_off","result":"PASS","cycles":3,"evacuated":0,"scavenged":61992,"reclaimed":45602912,"evidence":"cycles=3 evacuated=0 scavenged=61992 reclaimed=45602912"},{"test":"test_gap_repsel_cjs_iife_return_shape","arm":"shipped_default","result":"PASS","cycles":1,"evacuated":0,"scavenged":49811,"reclaimed":56836520,"evidence":"cycles=1 evacuated=0 scavenged=49811 reclaimed=56836520"},{"test":"test_gap_repsel_ptr_shape_elements","arm":"default","result":"UNVER","cycles":0,"evacuated":0,"scavenged":0,"reclaimed":0,"evidence":"cycles=0 evacuated=0 scavenged=0 reclaimed=0"},{"test":"test_gap_repsel_ptr_shape_elements","arm":"safepoint_minor","result":"UNVER","cycles":0,"evacuated":0,"scavenged":0,"reclaimed":0,"evidence":"cycles=0 evacuated=0 scavenged=0 reclaimed=0"},{"test":"test_gap_repsel_ptr_shape_elements","arm":"evac_minor","result":"PASS","cycles":1,"evacuated":0,"scavenged":6466,"reclaimed":4998880,"evidence":"cycles=1 evacuated=0 scavenged=6466 reclaimed=4998880"},{"test":"test_gap_repsel_ptr_shape_elements","arm":"verify_evac","result":"UNVER","cycles":0,"evacuated":0,"scavenged":0,"reclaimed":0,"evidence":"cycles=0 evacuated=0 scavenged=0 reclaimed=0"},{"test":"test_gap_repsel_ptr_shape_elements","arm":"force_verify","result":"PASS","cycles":1,"evacuated":0,"scavenged":6466,"reclaimed":4998880,"evidence":"cycles=1 evacuated=0 scavenged=6466 reclaimed=4998880"},{"test":"test_gap_repsel_ptr_shape_elements","arm":"cons_scan_off","result":"UNVER","cycles":0,"evacuated":0,"scavenged":0,"reclaimed":0,"evidence":"cycles=0 evacuated=0 scavenged=0 reclaimed=0"},{"test":"test_gap_repsel_ptr_shape_elements","arm":"shipped_default","result":"PASS","cycles":0,"evacuated":0,"scavenged":0,"reclaimed":0,"evidence":"cycles=0 evacuated=0 scavenged=0 reclaimed=0"},{"test":"test_gap_repsel_p4a_holes_axis","arm":"default","result":"UNVER","cycles":0,"evacuated":0,"scavenged":0,"reclaimed":0,"evidence":"cycles=0 evacuated=0 scavenged=0 reclaimed=0"},{"test":"test_gap_repsel_p4a_holes_axis","arm":"safepoint_minor","result":"UNVER","cycles":0,"evacuated":0,"scavenged":0,"reclaimed":0,"evidence":"cycles=0 evacuated=0 scavenged=0 reclaimed=0"},{"test":"test_gap_repsel_p4a_holes_axis","arm":"evac_minor","result":"UNVER","cycles":1,"evacuated":0,"scavenged":0,"reclaimed":89488,"evidence":"cycles=1 evacuated=0 scavenged=0 reclaimed=89488"},{"test":"test_gap_repsel_p4a_holes_axis","arm":"verify_evac","result":"UNVER","cycles":0,"evacuated":0,"scavenged":0,"reclaimed":0,"evidence":"cycles=0 evacuated=0 scavenged=0 reclaimed=0"},{"test":"test_gap_repsel_p4a_holes_axis","arm":"force_verify","result":"PASS","cycles":1,"evacuated":0,"scavenged":6422,"reclaimed":223720,"evidence":"cycles=1 evacuated=0 scavenged=6422 reclaimed=223720"},{"test":"test_gap_repsel_p4a_holes_axis","arm":"cons_scan_off","result":"UNVER","cycles":0,"evacuated":0,"scavenged":0,"reclaimed":0,"evidence":"cycles=0 evacuated=0 scavenged=0 reclaimed=0"},{"test":"test_gap_repsel_p4a_holes_axis","arm":"shipped_default","result":"PASS","cycles":0,"evacuated":0,"scavenged":0,"reclaimed":0,"evidence":"cycles=0 evacuated=0 scavenged=0 reclaimed=0"},{"test":"test_gap_repsel_p4a_inline_tiers","arm":"default","result":"UNVER","cycles":0,"evacuated":0,"scavenged":0,"reclaimed":0,"evidence":"cycles=0 evacuated=0 scavenged=0 reclaimed=0"},{"test":"test_gap_repsel_p4a_inline_tiers","arm":"safepoint_minor","result":"UNVER","cycles":0,"evacuated":0,"scavenged":0,"reclaimed":0,"evidence":"cycles=0 evacuated=0 scavenged=0 reclaimed=0"},{"test":"test_gap_repsel_p4a_inline_tiers","arm":"evac_minor","result":"UNVER","cycles":1,"evacuated":0,"scavenged":0,"reclaimed":162688,"evidence":"cycles=1 evacuated=0 scavenged=0 reclaimed=162688"},{"test":"test_gap_repsel_p4a_inline_tiers","arm":"verify_evac","result":"UNVER","cycles":0,"evacuated":0,"scavenged":0,"reclaimed":0,"evidence":"cycles=0 evacuated=0 scavenged=0 reclaimed=0"},{"test":"test_gap_repsel_p4a_inline_tiers","arm":"force_verify","result":"PASS","cycles":1,"evacuated":0,"scavenged":6423,"reclaimed":406720,"evidence":"cycles=1 evacuated=0 scavenged=6423 reclaimed=406720"},{"test":"test_gap_repsel_p4a_inline_tiers","arm":"cons_scan_off","result":"UNVER","cycles":0,"evacuated":0,"scavenged":0,"reclaimed":0,"evidence":"cycles=0 evacuated=0 scavenged=0 reclaimed=0"},{"test":"test_gap_repsel_p4a_inline_tiers","arm":"shipped_default","result":"PASS","cycles":0,"evacuated":0,"scavenged":0,"reclaimed":0,"evidence":"cycles=0 evacuated=0 scavenged=0 reclaimed=0"},{"test":"test_gap_repsel_p4a_logical_numeric","arm":"default","result":"UNVER","cycles":0,"evacuated":0,"scavenged":0,"reclaimed":0,"evidence":"cycles=0 evacuated=0 scavenged=0 reclaimed=0"},{"test":"test_gap_repsel_p4a_logical_numeric","arm":"safepoint_minor","result":"UNVER","cycles":0,"evacuated":0,"scavenged":0,"reclaimed":0,"evidence":"cycles=0 evacuated=0 scavenged=0 reclaimed=0"},{"test":"test_gap_repsel_p4a_logical_numeric","arm":"evac_minor","result":"UNVER","cycles":1,"evacuated":0,"scavenged":0,"reclaimed":129104,"evidence":"cycles=1 evacuated=0 scavenged=0 reclaimed=129104"},{"test":"test_gap_repsel_p4a_logical_numeric","arm":"verify_evac","result":"UNVER","cycles":0,"evacuated":0,"scavenged":0,"reclaimed":0,"evidence":"cycles=0 evacuated=0 scavenged=0 reclaimed=0"},{"test":"test_gap_repsel_p4a_logical_numeric","arm":"force_verify","result":"PASS","cycles":1,"evacuated":0,"scavenged":6427,"reclaimed":322760,"evidence":"cycles=1 evacuated=0 scavenged=6427 reclaimed=322760"},{"test":"test_gap_repsel_p4a_logical_numeric","arm":"cons_scan_off","result":"UNVER","cycles":0,"evacuated":0,"scavenged":0,"reclaimed":0,"evidence":"cycles=0 evacuated=0 scavenged=0 reclaimed=0"},{"test":"test_gap_repsel_p4a_logical_numeric","arm":"shipped_default","result":"PASS","cycles":0,"evacuated":0,"scavenged":0,"reclaimed":0,"evidence":"cycles=0 evacuated=0 scavenged=0 reclaimed=0"},{"test":"test_gap_repsel_p4a3_numarray_barriers","arm":"default","result":"UNVER","cycles":0,"evacuated":0,"scavenged":0,"reclaimed":0,"evidence":"cycles=0 evacuated=0 scavenged=0 reclaimed=0"},{"test":"test_gap_repsel_p4a3_numarray_barriers","arm":"safepoint_minor","result":"UNVER","cycles":0,"evacuated":0,"scavenged":0,"reclaimed":0,"evidence":"cycles=0 evacuated=0 scavenged=0 reclaimed=0"},{"test":"test_gap_repsel_p4a3_numarray_barriers","arm":"evac_minor","result":"UNVER","cycles":1,"evacuated":0,"scavenged":0,"reclaimed":146176,"evidence":"cycles=1 evacuated=0 scavenged=0 reclaimed=146176"},{"test":"test_gap_repsel_p4a3_numarray_barriers","arm":"verify_evac","result":"UNVER","cycles":0,"evacuated":0,"scavenged":0,"reclaimed":0,"evidence":"cycles=0 evacuated=0 scavenged=0 reclaimed=0"},{"test":"test_gap_repsel_p4a3_numarray_barriers","arm":"force_verify","result":"PASS","cycles":1,"evacuated":0,"scavenged":6429,"reclaimed":365440,"evidence":"cycles=1 evacuated=0 scavenged=6429 reclaimed=365440"},{"test":"test_gap_repsel_p4a3_numarray_barriers","arm":"cons_scan_off","result":"UNVER","cycles":0,"evacuated":0,"scavenged":0,"reclaimed":0,"evidence":"cycles=0 evacuated=0 scavenged=0 reclaimed=0"},{"test":"test_gap_repsel_p4a3_numarray_barriers","arm":"shipped_default","result":"PASS","cycles":0,"evacuated":0,"scavenged":0,"reclaimed":0,"evidence":"cycles=0 evacuated=0 scavenged=0 reclaimed=0"},{"test":"test_gap_repsel_p4a3_numarray_growth","arm":"default","result":"PASS","cycles":2,"evacuated":0,"scavenged":5271,"reclaimed":10124560,"evidence":"cycles=2 evacuated=0 scavenged=5271 reclaimed=10124560"},{"test":"test_gap_repsel_p4a3_numarray_growth","arm":"safepoint_minor","result":"PASS","cycles":2,"evacuated":0,"scavenged":5271,"reclaimed":10124560,"evidence":"cycles=2 evacuated=0 scavenged=5271 reclaimed=10124560"},{"test":"test_gap_repsel_p4a3_numarray_growth","arm":"evac_minor","result":"PASS","cycles":3,"evacuated":0,"scavenged":15606,"reclaimed":21324160,"evidence":"cycles=3 evacuated=0 scavenged=15606 reclaimed=21324160"},{"test":"test_gap_repsel_p4a3_numarray_growth","arm":"verify_evac","result":"PASS","cycles":2,"evacuated":0,"scavenged":5271,"reclaimed":10124560,"evidence":"cycles=2 evacuated=0 scavenged=5271 reclaimed=10124560"},{"test":"test_gap_repsel_p4a3_numarray_growth","arm":"force_verify","result":"PASS","cycles":3,"evacuated":0,"scavenged":15606,"reclaimed":21324160,"evidence":"cycles=3 evacuated=0 scavenged=15606 reclaimed=21324160"},{"test":"test_gap_repsel_p4a3_numarray_growth","arm":"cons_scan_off","result":"PASS","cycles":2,"evacuated":0,"scavenged":5271,"reclaimed":10124560,"evidence":"cycles=2 evacuated=0 scavenged=5271 reclaimed=10124560"},{"test":"test_gap_repsel_p4a3_numarray_growth","arm":"shipped_default","result":"PASS","cycles":0,"evacuated":0,"scavenged":0,"reclaimed":0,"evidence":"cycles=0 evacuated=0 scavenged=0 reclaimed=0"},{"test":"test_gap_repsel_p4a3_ptr_numarray","arm":"default","result":"UNVER","cycles":0,"evacuated":0,"scavenged":0,"reclaimed":0,"evidence":"cycles=0 evacuated=0 scavenged=0 reclaimed=0"},{"test":"test_gap_repsel_p4a3_ptr_numarray","arm":"safepoint_minor","result":"UNVER","cycles":0,"evacuated":0,"scavenged":0,"reclaimed":0,"evidence":"cycles=0 evacuated=0 scavenged=0 reclaimed=0"},{"test":"test_gap_repsel_p4a3_ptr_numarray","arm":"evac_minor","result":"UNVER","cycles":1,"evacuated":0,"scavenged":0,"reclaimed":396736,"evidence":"cycles=1 evacuated=0 scavenged=0 reclaimed=396736"},{"test":"test_gap_repsel_p4a3_ptr_numarray","arm":"verify_evac","result":"UNVER","cycles":0,"evacuated":0,"scavenged":0,"reclaimed":0,"evidence":"cycles=0 evacuated=0 scavenged=0 reclaimed=0"},{"test":"test_gap_repsel_p4a3_ptr_numarray","arm":"force_verify","result":"PASS","cycles":1,"evacuated":0,"scavenged":6427,"reclaimed":991840,"evidence":"cycles=1 evacuated=0 scavenged=6427 reclaimed=991840"},{"test":"test_gap_repsel_p4a3_ptr_numarray","arm":"cons_scan_off","result":"UNVER","cycles":0,"evacuated":0,"scavenged":0,"reclaimed":0,"evidence":"cycles=0 evacuated=0 scavenged=0 reclaimed=0"},{"test":"test_gap_repsel_p4a3_ptr_numarray","arm":"shipped_default","result":"PASS","cycles":0,"evacuated":0,"scavenged":0,"reclaimed":0,"evidence":"cycles=0 evacuated=0 scavenged=0 reclaimed=0"},{"test":"test_gap_repsel_p4b_field_store_elision","arm":"default","result":"PASS","cycles":1,"evacuated":0,"scavenged":6470,"reclaimed":2978240,"evidence":"cycles=1 evacuated=0 scavenged=6470 reclaimed=2978240"},{"test":"test_gap_repsel_p4b_field_store_elision","arm":"safepoint_minor","result":"PASS","cycles":1,"evacuated":0,"scavenged":6470,"reclaimed":2978240,"evidence":"cycles=1 evacuated=0 scavenged=6470 reclaimed=2978240"},{"test":"test_gap_repsel_p4b_field_store_elision","arm":"evac_minor","result":"PASS","cycles":1,"evacuated":0,"scavenged":6470,"reclaimed":2978240,"evidence":"cycles=1 evacuated=0 scavenged=6470 reclaimed=2978240"},{"test":"test_gap_repsel_p4b_field_store_elision","arm":"verify_evac","result":"PASS","cycles":1,"evacuated":0,"scavenged":6470,"reclaimed":2978240,"evidence":"cycles=1 evacuated=0 scavenged=6470 reclaimed=2978240"},{"test":"test_gap_repsel_p4b_field_store_elision","arm":"force_verify","result":"PASS","cycles":1,"evacuated":0,"scavenged":6470,"reclaimed":2978240,"evidence":"cycles=1 evacuated=0 scavenged=6470 reclaimed=2978240"},{"test":"test_gap_repsel_p4b_field_store_elision","arm":"cons_scan_off","result":"PASS","cycles":1,"evacuated":0,"scavenged":6470,"reclaimed":2978240,"evidence":"cycles=1 evacuated=0 scavenged=6470 reclaimed=2978240"},{"test":"test_gap_repsel_p4b_field_store_elision","arm":"shipped_default","result":"PASS","cycles":0,"evacuated":0,"scavenged":0,"reclaimed":0,"evidence":"cycles=0 evacuated=0 scavenged=0 reclaimed=0"},{"test":"test_gap_repsel_proven_this_frozen","arm":"default","result":"UNVER","cycles":0,"evacuated":0,"scavenged":0,"reclaimed":0,"evidence":"cycles=0 evacuated=0 scavenged=0 reclaimed=0"},{"test":"test_gap_repsel_proven_this_frozen","arm":"safepoint_minor","result":"UNVER","cycles":0,"evacuated":0,"scavenged":0,"reclaimed":0,"evidence":"cycles=0 evacuated=0 scavenged=0 reclaimed=0"},{"test":"test_gap_repsel_proven_this_frozen","arm":"evac_minor","result":"UNVER","cycles":1,"evacuated":0,"scavenged":0,"reclaimed":87040,"evidence":"cycles=1 evacuated=0 scavenged=0 reclaimed=87040"},{"test":"test_gap_repsel_proven_this_frozen","arm":"verify_evac","result":"UNVER","cycles":0,"evacuated":0,"scavenged":0,"reclaimed":0,"evidence":"cycles=0 evacuated=0 scavenged=0 reclaimed=0"},{"test":"test_gap_repsel_proven_this_frozen","arm":"force_verify","result":"PASS","cycles":1,"evacuated":0,"scavenged":6456,"reclaimed":217600,"evidence":"cycles=1 evacuated=0 scavenged=6456 reclaimed=217600"},{"test":"test_gap_repsel_proven_this_frozen","arm":"cons_scan_off","result":"UNVER","cycles":0,"evacuated":0,"scavenged":0,"reclaimed":0,"evidence":"cycles=0 evacuated=0 scavenged=0 reclaimed=0"},{"test":"test_gap_repsel_proven_this_frozen","arm":"shipped_default","result":"PASS","cycles":0,"evacuated":0,"scavenged":0,"reclaimed":0,"evidence":"cycles=0 evacuated=0 scavenged=0 reclaimed=0"},{"test":"test_gap_repsel_pshape_tower_delete","arm":"default","result":"PASS","cycles":6,"evacuated":0,"scavenged":78643,"reclaimed":4463496,"evidence":"cycles=6 evacuated=0 scavenged=78643 reclaimed=4463496"},{"test":"test_gap_repsel_pshape_tower_delete","arm":"safepoint_minor","result":"PASS","cycles":6,"evacuated":0,"scavenged":78643,"reclaimed":4463496,"evidence":"cycles=6 evacuated=0 scavenged=78643 reclaimed=4463496"},{"test":"test_gap_repsel_pshape_tower_delete","arm":"evac_minor","result":"PASS","cycles":6,"evacuated":0,"scavenged":78643,"reclaimed":4463496,"evidence":"cycles=6 evacuated=0 scavenged=78643 reclaimed=4463496"},{"test":"test_gap_repsel_pshape_tower_delete","arm":"verify_evac","result":"PASS","cycles":5,"evacuated":0,"scavenged":81383,"reclaimed":1180688,"evidence":"cycles=5 evacuated=0 scavenged=81383 reclaimed=1180688"},{"test":"test_gap_repsel_pshape_tower_delete","arm":"force_verify","result":"PASS","cycles":7,"evacuated":0,"scavenged":410820,"reclaimed":1577720,"evidence":"cycles=7 evacuated=0 scavenged=410820 reclaimed=1577720"},{"test":"test_gap_repsel_pshape_tower_delete","arm":"cons_scan_off","result":"PASS","cycles":6,"evacuated":0,"scavenged":78643,"reclaimed":4463496,"evidence":"cycles=6 evacuated=0 scavenged=78643 reclaimed=4463496"},{"test":"test_gap_repsel_pshape_tower_delete","arm":"shipped_default","result":"PASS","cycles":1,"evacuated":0,"scavenged":0,"reclaimed":603888,"evidence":"cycles=1 evacuated=0 scavenged=0 reclaimed=603888"},{"test":"test_gap_repsel_pshape_tombstone_oldgen_delete","arm":"default","result":"PASS","cycles":6,"evacuated":0,"scavenged":78643,"reclaimed":4063696,"evidence":"cycles=6 evacuated=0 scavenged=78643 reclaimed=4063696"},{"test":"test_gap_repsel_pshape_tombstone_oldgen_delete","arm":"safepoint_minor","result":"PASS","cycles":6,"evacuated":0,"scavenged":78643,"reclaimed":4063696,"evidence":"cycles=6 evacuated=0 scavenged=78643 reclaimed=4063696"},{"test":"test_gap_repsel_pshape_tombstone_oldgen_delete","arm":"evac_minor","result":"PASS","cycles":6,"evacuated":0,"scavenged":78643,"reclaimed":4063696,"evidence":"cycles=6 evacuated=0 scavenged=78643 reclaimed=4063696"},{"test":"test_gap_repsel_pshape_tombstone_oldgen_delete","arm":"verify_evac","result":"PASS","cycles":5,"evacuated":0,"scavenged":68851,"reclaimed":993664,"evidence":"cycles=5 evacuated=0 scavenged=68851 reclaimed=993664"},{"test":"test_gap_repsel_pshape_tombstone_oldgen_delete","arm":"force_verify","result":"PASS","cycles":7,"evacuated":0,"scavenged":400539,"reclaimed":1500880,"evidence":"cycles=7 evacuated=0 scavenged=400539 reclaimed=1500880"},{"test":"test_gap_repsel_pshape_tombstone_oldgen_delete","arm":"cons_scan_off","result":"PASS","cycles":6,"evacuated":0,"scavenged":78643,"reclaimed":4063696,"evidence":"cycles=6 evacuated=0 scavenged=78643 reclaimed=4063696"},{"test":"test_gap_repsel_pshape_tombstone_oldgen_delete","arm":"shipped_default","result":"PASS","cycles":1,"evacuated":0,"scavenged":0,"reclaimed":600640,"evidence":"cycles=1 evacuated=0 scavenged=0 reclaimed=600640"},{"test":"test_gap_gc_ta_ctor_source_rooting","arm":"default","result":"UNVER","cycles":1,"evacuated":0,"scavenged":0,"reclaimed":99504,"evidence":"cycles=1 evacuated=0 scavenged=0 reclaimed=99504"},{"test":"test_gap_gc_ta_ctor_source_rooting","arm":"safepoint_minor","result":"UNVER","cycles":1,"evacuated":0,"scavenged":0,"reclaimed":99504,"evidence":"cycles=1 evacuated=0 scavenged=0 reclaimed=99504"},{"test":"test_gap_gc_ta_ctor_source_rooting","arm":"evac_minor","result":"UNVER","cycles":1,"evacuated":0,"scavenged":0,"reclaimed":99504,"evidence":"cycles=1 evacuated=0 scavenged=0 reclaimed=99504"},{"test":"test_gap_gc_ta_ctor_source_rooting","arm":"verify_evac","result":"PASS","cycles":1,"evacuated":0,"scavenged":6488,"reclaimed":248760,"evidence":"cycles=1 evacuated=0 scavenged=6488 reclaimed=248760"},{"test":"test_gap_gc_ta_ctor_source_rooting","arm":"force_verify","result":"PASS","cycles":1,"evacuated":0,"scavenged":6488,"reclaimed":248760,"evidence":"cycles=1 evacuated=0 scavenged=6488 reclaimed=248760"},{"test":"test_gap_gc_ta_ctor_source_rooting","arm":"cons_scan_off","result":"UNVER","cycles":1,"evacuated":0,"scavenged":0,"reclaimed":99504,"evidence":"cycles=1 evacuated=0 scavenged=0 reclaimed=99504"},{"test":"test_gap_gc_ta_ctor_source_rooting","arm":"shipped_default","result":"PASS","cycles":0,"evacuated":0,"scavenged":0,"reclaimed":0,"evidence":"cycles=0 evacuated=0 scavenged=0 reclaimed=0"},{"test":"test_gap_gc_string_literal_operand_rooting","arm":"default","result":"PASS","cycles":11,"evacuated":0,"scavenged":347174,"reclaimed":212394664,"evidence":"cycles=11 evacuated=0 scavenged=347174 reclaimed=212394664"},{"test":"test_gap_gc_string_literal_operand_rooting","arm":"safepoint_minor","result":"PASS","cycles":11,"evacuated":0,"scavenged":347174,"reclaimed":212394664,"evidence":"cycles=11 evacuated=0 scavenged=347174 reclaimed=212394664"},{"test":"test_gap_gc_string_literal_operand_rooting","arm":"evac_minor","result":"PASS","cycles":10,"evacuated":0,"scavenged":253745,"reclaimed":235898832,"evidence":"cycles=10 evacuated=0 scavenged=253745 reclaimed=235898832"},{"test":"test_gap_gc_string_literal_operand_rooting","arm":"verify_evac","result":"PASS","cycles":13,"evacuated":0,"scavenged":425171,"reclaimed":230498992,"evidence":"cycles=13 evacuated=0 scavenged=425171 reclaimed=230498992"},{"test":"test_gap_gc_string_literal_operand_rooting","arm":"force_verify","result":"PASS","cycles":10,"evacuated":0,"scavenged":248097,"reclaimed":246885488,"evidence":"cycles=10 evacuated=0 scavenged=248097 reclaimed=246885488"},{"test":"test_gap_gc_string_literal_operand_rooting","arm":"cons_scan_off","result":"PASS","cycles":10,"evacuated":0,"scavenged":253745,"reclaimed":235898832,"evidence":"cycles=10 evacuated=0 scavenged=253745 reclaimed=235898832"},{"test":"test_gap_gc_string_literal_operand_rooting","arm":"shipped_default","result":"PASS","cycles":3,"evacuated":0,"scavenged":590263,"reclaimed":34243632,"evidence":"cycles=3 evacuated=0 scavenged=590263 reclaimed=34243632"},{"test":"test_gap_repsel_scalar_replaced_locals","arm":"default","result":"PASS","cycles":21,"evacuated":0,"scavenged":472898,"reclaimed":1684288912,"evidence":"cycles=21 evacuated=0 scavenged=472898 reclaimed=1684288912"},{"test":"test_gap_repsel_scalar_replaced_locals","arm":"safepoint_minor","result":"PASS","cycles":21,"evacuated":0,"scavenged":472898,"reclaimed":1684288912,"evidence":"cycles=21 evacuated=0 scavenged=472898 reclaimed=1684288912"},{"test":"test_gap_repsel_scalar_replaced_locals","arm":"evac_minor","result":"PASS","cycles":21,"evacuated":0,"scavenged":391460,"reclaimed":1700696248,"evidence":"cycles=21 evacuated=0 scavenged=391460 reclaimed=1700696248"},{"test":"test_gap_repsel_scalar_replaced_locals","arm":"verify_evac","result":"PASS","cycles":21,"evacuated":0,"scavenged":483784,"reclaimed":1592515720,"evidence":"cycles=21 evacuated=0 scavenged=483784 reclaimed=1592515720"},{"test":"test_gap_repsel_scalar_replaced_locals","arm":"force_verify","result":"PASS","cycles":21,"evacuated":0,"scavenged":391460,"reclaimed":1700696248,"evidence":"cycles=21 evacuated=0 scavenged=391460 reclaimed=1700696248"},{"test":"test_gap_repsel_scalar_replaced_locals","arm":"cons_scan_off","result":"PASS","cycles":21,"evacuated":0,"scavenged":391460,"reclaimed":1700696248,"evidence":"cycles=21 evacuated=0 scavenged=391460 reclaimed=1700696248"},{"test":"test_gap_repsel_scalar_replaced_locals","arm":"shipped_default","result":"PASS","cycles":12,"evacuated":0,"scavenged":439041,"reclaimed":1638097360,"evidence":"cycles=12 evacuated=0 scavenged=439041 reclaimed=1638097360"},{"test":"test_gap_repsel_module_init_canonical","arm":"default","result":"UNVER","cycles":1,"evacuated":0,"scavenged":0,"reclaimed":84896,"evidence":"cycles=1 evacuated=0 scavenged=0 reclaimed=84896"},{"test":"test_gap_repsel_module_init_canonical","arm":"safepoint_minor","result":"UNVER","cycles":1,"evacuated":0,"scavenged":0,"reclaimed":84896,"evidence":"cycles=1 evacuated=0 scavenged=0 reclaimed=84896"},{"test":"test_gap_repsel_module_init_canonical","arm":"evac_minor","result":"UNVER","cycles":1,"evacuated":0,"scavenged":0,"reclaimed":84896,"evidence":"cycles=1 evacuated=0 scavenged=0 reclaimed=84896"},{"test":"test_gap_repsel_module_init_canonical","arm":"verify_evac","result":"PASS","cycles":1,"evacuated":0,"scavenged":6452,"reclaimed":212240,"evidence":"cycles=1 evacuated=0 scavenged=6452 reclaimed=212240"},{"test":"test_gap_repsel_module_init_canonical","arm":"force_verify","result":"PASS","cycles":1,"evacuated":0,"scavenged":6452,"reclaimed":212240,"evidence":"cycles=1 evacuated=0 scavenged=6452 reclaimed=212240"},{"test":"test_gap_repsel_module_init_canonical","arm":"cons_scan_off","result":"UNVER","cycles":1,"evacuated":0,"scavenged":0,"reclaimed":84896,"evidence":"cycles=1 evacuated=0 scavenged=0 reclaimed=84896"},{"test":"test_gap_repsel_module_init_canonical","arm":"shipped_default","result":"PASS","cycles":0,"evacuated":0,"scavenged":0,"reclaimed":0,"evidence":"cycles=0 evacuated=0 scavenged=0 reclaimed=0"},{"test":"test_gap_gc_pointer_publish","arm":"default","result":"PASS","cycles":5,"evacuated":0,"scavenged":8752,"reclaimed":127090784,"evidence":"cycles=5 evacuated=0 scavenged=8752 reclaimed=127090784"},{"test":"test_gap_gc_pointer_publish","arm":"safepoint_minor","result":"PASS","cycles":5,"evacuated":0,"scavenged":8752,"reclaimed":127090784,"evidence":"cycles=5 evacuated=0 scavenged=8752 reclaimed=127090784"},{"test":"test_gap_gc_pointer_publish","arm":"evac_minor","result":"PASS","cycles":5,"evacuated":0,"scavenged":8752,"reclaimed":127090784,"evidence":"cycles=5 evacuated=0 scavenged=8752 reclaimed=127090784"},{"test":"test_gap_gc_pointer_publish","arm":"verify_evac","result":"PASS","cycles":5,"evacuated":0,"scavenged":23500,"reclaimed":128311480,"evidence":"cycles=5 evacuated=0 scavenged=23500 reclaimed=128311480"},{"test":"test_gap_gc_pointer_publish","arm":"force_verify","result":"PASS","cycles":5,"evacuated":0,"scavenged":23500,"reclaimed":128311480,"evidence":"cycles=5 evacuated=0 scavenged=23500 reclaimed=128311480"},{"test":"test_gap_gc_pointer_publish","arm":"cons_scan_off","result":"PASS","cycles":5,"evacuated":0,"scavenged":8752,"reclaimed":127090784,"evidence":"cycles=5 evacuated=0 scavenged=8752 reclaimed=127090784"},{"test":"test_gap_gc_pointer_publish","arm":"shipped_default","result":"PASS","cycles":2,"evacuated":0,"scavenged":17033,"reclaimed":117892200,"evidence":"cycles=2 evacuated=0 scavenged=17033 reclaimed=117892200"},{"test":"test_gap_repsel_gc_stress","arm":"default","result":"PASS","cycles":12,"evacuated":0,"scavenged":557915,"reclaimed":815686688,"evidence":"cycles=12 evacuated=0 scavenged=557915 reclaimed=815686688"},{"test":"test_gap_repsel_gc_stress","arm":"safepoint_minor","result":"PASS","cycles":12,"evacuated":0,"scavenged":557915,"reclaimed":815686688,"evidence":"cycles=12 evacuated=0 scavenged=557915 reclaimed=815686688"},{"test":"test_gap_repsel_gc_stress","arm":"evac_minor","result":"PASS","cycles":15,"evacuated":0,"scavenged":553338,"reclaimed":800286664,"evidence":"cycles=15 evacuated=0 scavenged=553338 reclaimed=800286664"},{"test":"test_gap_repsel_gc_stress","arm":"verify_evac","result":"PASS","cycles":16,"evacuated":0,"scavenged":574419,"reclaimed":843516696,"evidence":"cycles=16 evacuated=0 scavenged=574419 reclaimed=843516696"},{"test":"test_gap_repsel_gc_stress","arm":"force_verify","result":"PASS","cycles":16,"evacuated":0,"scavenged":618428,"reclaimed":742384688,"evidence":"cycles=16 evacuated=0 scavenged=618428 reclaimed=742384688"},{"test":"test_gap_repsel_gc_stress","arm":"cons_scan_off","result":"PASS","cycles":15,"evacuated":0,"scavenged":553338,"reclaimed":800286664,"evidence":"cycles=15 evacuated=0 scavenged=553338 reclaimed=800286664"},{"test":"test_gap_repsel_gc_stress","arm":"shipped_default","result":"PASS","cycles":7,"evacuated":0,"scavenged":480130,"reclaimed":772947600,"evidence":"cycles=7 evacuated=0 scavenged=480130 reclaimed=772947600"},{"test":"test_gap_gc_spread_accessor_rooting","arm":"default","result":"PASS","cycles":4,"evacuated":0,"scavenged":15686,"reclaimed":49705120,"evidence":"cycles=4 evacuated=0 scavenged=15686 reclaimed=49705120"},{"test":"test_gap_gc_spread_accessor_rooting","arm":"safepoint_minor","result":"PASS","cycles":4,"evacuated":0,"scavenged":15686,"reclaimed":49705120,"evidence":"cycles=4 evacuated=0 scavenged=15686 reclaimed=49705120"},{"test":"test_gap_gc_spread_accessor_rooting","arm":"evac_minor","result":"PASS","cycles":5,"evacuated":0,"scavenged":19364,"reclaimed":80653960,"evidence":"cycles=5 evacuated=0 scavenged=19364 reclaimed=80653960"},{"test":"test_gap_gc_spread_accessor_rooting","arm":"verify_evac","result":"PASS","cycles":4,"evacuated":0,"scavenged":15685,"reclaimed":49705840,"evidence":"cycles=4 evacuated=0 scavenged=15685 reclaimed=49705840"},{"test":"test_gap_gc_spread_accessor_rooting","arm":"force_verify","result":"PASS","cycles":5,"evacuated":0,"scavenged":19363,"reclaimed":80654680,"evidence":"cycles=5 evacuated=0 scavenged=19363 reclaimed=80654680"},{"test":"test_gap_gc_spread_accessor_rooting","arm":"cons_scan_off","result":"PASS","cycles":4,"evacuated":0,"scavenged":15686,"reclaimed":49705120,"evidence":"cycles=4 evacuated=0 scavenged=15686 reclaimed=49705120"},{"test":"test_gap_gc_spread_accessor_rooting","arm":"shipped_default","result":"PASS","cycles":1,"evacuated":0,"scavenged":9530,"reclaimed":75770240,"evidence":"cycles=1 evacuated=0 scavenged=9530 reclaimed=75770240"},{"test":"test_gap_gc_static_block_this_rooting","arm":"default","result":"PASS","cycles":4,"evacuated":0,"scavenged":19446,"reclaimed":64759600,"evidence":"cycles=4 evacuated=0 scavenged=19446 reclaimed=64759600"},{"test":"test_gap_gc_static_block_this_rooting","arm":"safepoint_minor","result":"PASS","cycles":4,"evacuated":0,"scavenged":19446,"reclaimed":64759600,"evidence":"cycles=4 evacuated=0 scavenged=19446 reclaimed=64759600"},{"test":"test_gap_gc_static_block_this_rooting","arm":"evac_minor","result":"PASS","cycles":4,"evacuated":0,"scavenged":19446,"reclaimed":64759600,"evidence":"cycles=4 evacuated=0 scavenged=19446 reclaimed=64759600"},{"test":"test_gap_gc_static_block_this_rooting","arm":"verify_evac","result":"PASS","cycles":4,"evacuated":0,"scavenged":19446,"reclaimed":64759600,"evidence":"cycles=4 evacuated=0 scavenged=19446 reclaimed=64759600"},{"test":"test_gap_gc_static_block_this_rooting","arm":"force_verify","result":"PASS","cycles":4,"evacuated":0,"scavenged":19446,"reclaimed":64759600,"evidence":"cycles=4 evacuated=0 scavenged=19446 reclaimed=64759600"},{"test":"test_gap_gc_static_block_this_rooting","arm":"cons_scan_off","result":"PASS","cycles":4,"evacuated":0,"scavenged":19446,"reclaimed":64759600,"evidence":"cycles=4 evacuated=0 scavenged=19446 reclaimed=64759600"},{"test":"test_gap_gc_static_block_this_rooting","arm":"shipped_default","result":"PASS","cycles":1,"evacuated":0,"scavenged":9870,"reclaimed":75674000,"evidence":"cycles=1 evacuated=0 scavenged=9870 reclaimed=75674000"},{"test":"test_gap_gc_inline_ctor_this_rooting","arm":"default","result":"PASS","cycles":4,"evacuated":0,"scavenged":10087,"reclaimed":66886080,"evidence":"cycles=4 evacuated=0 scavenged=10087 reclaimed=66886080"},{"test":"test_gap_gc_inline_ctor_this_rooting","arm":"safepoint_minor","result":"PASS","cycles":4,"evacuated":0,"scavenged":10087,"reclaimed":66886080,"evidence":"cycles=4 evacuated=0 scavenged=10087 reclaimed=66886080"},{"test":"test_gap_gc_inline_ctor_this_rooting","arm":"evac_minor","result":"PASS","cycles":4,"evacuated":0,"scavenged":10087,"reclaimed":66886080,"evidence":"cycles=4 evacuated=0 scavenged=10087 reclaimed=66886080"},{"test":"test_gap_gc_inline_ctor_this_rooting","arm":"verify_evac","result":"PASS","cycles":4,"evacuated":0,"scavenged":10087,"reclaimed":66886080,"evidence":"cycles=4 evacuated=0 scavenged=10087 reclaimed=66886080"},{"test":"test_gap_gc_inline_ctor_this_rooting","arm":"force_verify","result":"PASS","cycles":4,"evacuated":0,"scavenged":10087,"reclaimed":66886080,"evidence":"cycles=4 evacuated=0 scavenged=10087 reclaimed=66886080"},{"test":"test_gap_gc_inline_ctor_this_rooting","arm":"cons_scan_off","result":"PASS","cycles":4,"evacuated":0,"scavenged":10087,"reclaimed":66886080,"evidence":"cycles=4 evacuated=0 scavenged=10087 reclaimed=66886080"},{"test":"test_gap_gc_inline_ctor_this_rooting","arm":"shipped_default","result":"PASS","cycles":1,"evacuated":0,"scavenged":7887,"reclaimed":77064400,"evidence":"cycles=1 evacuated=0 scavenged=7887 reclaimed=77064400"},{"test":"test_gap_gc_catch_param_rooting","arm":"default","result":"PASS","cycles":3,"evacuated":0,"scavenged":14915,"reclaimed":44700480,"evidence":"cycles=3 evacuated=0 scavenged=14915 reclaimed=44700480"},{"test":"test_gap_gc_catch_param_rooting","arm":"safepoint_minor","result":"PASS","cycles":3,"evacuated":0,"scavenged":14915,"reclaimed":44700480,"evidence":"cycles=3 evacuated=0 scavenged=14915 reclaimed=44700480"},{"test":"test_gap_gc_catch_param_rooting","arm":"evac_minor","result":"PASS","cycles":4,"evacuated":0,"scavenged":18006,"reclaimed":80739040,"evidence":"cycles=4 evacuated=0 scavenged=18006 reclaimed=80739040"},{"test":"test_gap_gc_catch_param_rooting","arm":"verify_evac","result":"PASS","cycles":3,"evacuated":0,"scavenged":14915,"reclaimed":44700480,"evidence":"cycles=3 evacuated=0 scavenged=14915 reclaimed=44700480"},{"test":"test_gap_gc_catch_param_rooting","arm":"force_verify","result":"PASS","cycles":4,"evacuated":0,"scavenged":18006,"reclaimed":80739040,"evidence":"cycles=4 evacuated=0 scavenged=18006 reclaimed=80739040"},{"test":"test_gap_gc_catch_param_rooting","arm":"cons_scan_off","result":"PASS","cycles":3,"evacuated":0,"scavenged":14915,"reclaimed":44700480,"evidence":"cycles=3 evacuated=0 scavenged=14915 reclaimed=44700480"},{"test":"test_gap_gc_catch_param_rooting","arm":"shipped_default","result":"PASS","cycles":1,"evacuated":0,"scavenged":10475,"reclaimed":75572880,"evidence":"cycles=1 evacuated=0 scavenged=10475 reclaimed=75572880"},{"test":"test_gap_gc_closure_this_capture_rooting","arm":"default","result":"PASS","cycles":4,"evacuated":0,"scavenged":5368,"reclaimed":51868400,"evidence":"cycles=4 evacuated=0 scavenged=5368 reclaimed=51868400"},{"test":"test_gap_gc_closure_this_capture_rooting","arm":"safepoint_minor","result":"PASS","cycles":4,"evacuated":0,"scavenged":5368,"reclaimed":51868400,"evidence":"cycles=4 evacuated=0 scavenged=5368 reclaimed=51868400"},{"test":"test_gap_gc_closure_this_capture_rooting","arm":"evac_minor","result":"PASS","cycles":4,"evacuated":0,"scavenged":5368,"reclaimed":51868400,"evidence":"cycles=4 evacuated=0 scavenged=5368 reclaimed=51868400"},{"test":"test_gap_gc_closure_this_capture_rooting","arm":"verify_evac","result":"PASS","cycles":4,"evacuated":0,"scavenged":5368,"reclaimed":51868400,"evidence":"cycles=4 evacuated=0 scavenged=5368 reclaimed=51868400"},{"test":"test_gap_gc_closure_this_capture_rooting","arm":"force_verify","result":"PASS","cycles":4,"evacuated":0,"scavenged":5368,"reclaimed":51868400,"evidence":"cycles=4 evacuated=0 scavenged=5368 reclaimed=51868400"},{"test":"test_gap_gc_closure_this_capture_rooting","arm":"cons_scan_off","result":"PASS","cycles":4,"evacuated":0,"scavenged":5368,"reclaimed":51868400,"evidence":"cycles=4 evacuated=0 scavenged=5368 reclaimed=51868400"},{"test":"test_gap_gc_closure_this_capture_rooting","arm":"shipped_default","result":"PASS","cycles":1,"evacuated":0,"scavenged":3346,"reclaimed":77972800,"evidence":"cycles=1 evacuated=0 scavenged=3346 reclaimed=77972800"},{"test":"test_gap_gc_method_receiver_rooting","arm":"default","result":"PASS","cycles":4,"evacuated":0,"scavenged":8330,"reclaimed":67200080,"evidence":"cycles=4 evacuated=0 scavenged=8330 reclaimed=67200080"},{"test":"test_gap_gc_method_receiver_rooting","arm":"safepoint_minor","result":"PASS","cycles":4,"evacuated":0,"scavenged":8330,"reclaimed":67200080,"evidence":"cycles=4 evacuated=0 scavenged=8330 reclaimed=67200080"},{"test":"test_gap_gc_method_receiver_rooting","arm":"evac_minor","result":"PASS","cycles":4,"evacuated":0,"scavenged":8330,"reclaimed":67200080,"evidence":"cycles=4 evacuated=0 scavenged=8330 reclaimed=67200080"},{"test":"test_gap_gc_method_receiver_rooting","arm":"verify_evac","result":"PASS","cycles":4,"evacuated":0,"scavenged":8330,"reclaimed":67200080,"evidence":"cycles=4 evacuated=0 scavenged=8330 reclaimed=67200080"},{"test":"test_gap_gc_method_receiver_rooting","arm":"force_verify","result":"PASS","cycles":4,"evacuated":0,"scavenged":8330,"reclaimed":67200080,"evidence":"cycles=4 evacuated=0 scavenged=8330 reclaimed=67200080"},{"test":"test_gap_gc_method_receiver_rooting","arm":"cons_scan_off","result":"PASS","cycles":4,"evacuated":0,"scavenged":8330,"reclaimed":67200080,"evidence":"cycles=4 evacuated=0 scavenged=8330 reclaimed=67200080"},{"test":"test_gap_gc_method_receiver_rooting","arm":"shipped_default","result":"PASS","cycles":1,"evacuated":0,"scavenged":3903,"reclaimed":77840640,"evidence":"cycles=1 evacuated=0 scavenged=3903 reclaimed=77840640"},{"test":"test_gap_gc_index_get_receiver_rooting","arm":"default","result":"PASS","cycles":10,"evacuated":0,"scavenged":173994,"reclaimed":348143640,"evidence":"cycles=10 evacuated=0 scavenged=173994 reclaimed=348143640"},{"test":"test_gap_gc_index_get_receiver_rooting","arm":"safepoint_minor","result":"PASS","cycles":10,"evacuated":0,"scavenged":173994,"reclaimed":348143640,"evidence":"cycles=10 evacuated=0 scavenged=173994 reclaimed=348143640"},{"test":"test_gap_gc_index_get_receiver_rooting","arm":"evac_minor","result":"PASS","cycles":10,"evacuated":0,"scavenged":173994,"reclaimed":348143640,"evidence":"cycles=10 evacuated=0 scavenged=173994 reclaimed=348143640"},{"test":"test_gap_gc_index_get_receiver_rooting","arm":"verify_evac","result":"PASS","cycles":10,"evacuated":0,"scavenged":173994,"reclaimed":348143640,"evidence":"cycles=10 evacuated=0 scavenged=173994 reclaimed=348143640"},{"test":"test_gap_gc_index_get_receiver_rooting","arm":"force_verify","result":"PASS","cycles":10,"evacuated":0,"scavenged":173994,"reclaimed":348143640,"evidence":"cycles=10 evacuated=0 scavenged=173994 reclaimed=348143640"},{"test":"test_gap_gc_index_get_receiver_rooting","arm":"cons_scan_off","result":"PASS","cycles":10,"evacuated":0,"scavenged":173994,"reclaimed":348143640,"evidence":"cycles=10 evacuated=0 scavenged=173994 reclaimed=348143640"},{"test":"test_gap_gc_index_get_receiver_rooting","arm":"shipped_default","result":"PASS","cycles":4,"evacuated":0,"scavenged":79719,"reclaimed":311807560,"evidence":"cycles=4 evacuated=0 scavenged=79719 reclaimed=311807560"},{"test":"test_gap_gc_closure_call_callee_rooting","arm":"default","result":"PASS","cycles":4,"evacuated":0,"scavenged":4562,"reclaimed":51875840,"evidence":"cycles=4 evacuated=0 scavenged=4562 reclaimed=51875840"},{"test":"test_gap_gc_closure_call_callee_rooting","arm":"safepoint_minor","result":"PASS","cycles":4,"evacuated":0,"scavenged":4562,"reclaimed":51875840,"evidence":"cycles=4 evacuated=0 scavenged=4562 reclaimed=51875840"},{"test":"test_gap_gc_closure_call_callee_rooting","arm":"evac_minor","result":"PASS","cycles":4,"evacuated":0,"scavenged":4562,"reclaimed":51875840,"evidence":"cycles=4 evacuated=0 scavenged=4562 reclaimed=51875840"},{"test":"test_gap_gc_closure_call_callee_rooting","arm":"verify_evac","result":"PASS","cycles":4,"evacuated":0,"scavenged":4562,"reclaimed":51875840,"evidence":"cycles=4 evacuated=0 scavenged=4562 reclaimed=51875840"},{"test":"test_gap_gc_closure_call_callee_rooting","arm":"force_verify","result":"PASS","cycles":4,"evacuated":0,"scavenged":4562,"reclaimed":51875840,"evidence":"cycles=4 evacuated=0 scavenged=4562 reclaimed=51875840"},{"test":"test_gap_gc_closure_call_callee_rooting","arm":"cons_scan_off","result":"PASS","cycles":4,"evacuated":0,"scavenged":4562,"reclaimed":51875840,"evidence":"cycles=4 evacuated=0 scavenged=4562 reclaimed=51875840"},{"test":"test_gap_gc_closure_call_callee_rooting","arm":"shipped_default","result":"PASS","cycles":1,"evacuated":0,"scavenged":3407,"reclaimed":77974960,"evidence":"cycles=1 evacuated=0 scavenged=3407 reclaimed=77974960"},{"test":"test_gap_gc_closure_call_this_rooting","arm":"default","result":"PASS","cycles":4,"evacuated":0,"scavenged":5372,"reclaimed":51868400,"evidence":"cycles=4 evacuated=0 scavenged=5372 reclaimed=51868400"},{"test":"test_gap_gc_closure_call_this_rooting","arm":"safepoint_minor","result":"PASS","cycles":4,"evacuated":0,"scavenged":5372,"reclaimed":51868400,"evidence":"cycles=4 evacuated=0 scavenged=5372 reclaimed=51868400"},{"test":"test_gap_gc_closure_call_this_rooting","arm":"evac_minor","result":"PASS","cycles":4,"evacuated":0,"scavenged":5372,"reclaimed":51868400,"evidence":"cycles=4 evacuated=0 scavenged=5372 reclaimed=51868400"},{"test":"test_gap_gc_closure_call_this_rooting","arm":"verify_evac","result":"PASS","cycles":4,"evacuated":0,"scavenged":5372,"reclaimed":51868400,"evidence":"cycles=4 evacuated=0 scavenged=5372 reclaimed=51868400"},{"test":"test_gap_gc_closure_call_this_rooting","arm":"force_verify","result":"PASS","cycles":4,"evacuated":0,"scavenged":5372,"reclaimed":51868400,"evidence":"cycles=4 evacuated=0 scavenged=5372 reclaimed=51868400"},{"test":"test_gap_gc_closure_call_this_rooting","arm":"cons_scan_off","result":"PASS","cycles":4,"evacuated":0,"scavenged":5372,"reclaimed":51868400,"evidence":"cycles=4 evacuated=0 scavenged=5372 reclaimed=51868400"},{"test":"test_gap_gc_closure_call_this_rooting","arm":"shipped_default","result":"PASS","cycles":1,"evacuated":0,"scavenged":3346,"reclaimed":77972800,"evidence":"cycles=1 evacuated=0 scavenged=3346 reclaimed=77972800"},{"test":"test_gap_gc_closure_call_argument_rooting","arm":"default","result":"PASS","cycles":4,"evacuated":0,"scavenged":5373,"reclaimed":51868400,"evidence":"cycles=4 evacuated=0 scavenged=5373 reclaimed=51868400"},{"test":"test_gap_gc_closure_call_argument_rooting","arm":"safepoint_minor","result":"PASS","cycles":4,"evacuated":0,"scavenged":5373,"reclaimed":51868400,"evidence":"cycles=4 evacuated=0 scavenged=5373 reclaimed=51868400"},{"test":"test_gap_gc_closure_call_argument_rooting","arm":"evac_minor","result":"PASS","cycles":4,"evacuated":0,"scavenged":5373,"reclaimed":51868400,"evidence":"cycles=4 evacuated=0 scavenged=5373 reclaimed=51868400"},{"test":"test_gap_gc_closure_call_argument_rooting","arm":"verify_evac","result":"PASS","cycles":4,"evacuated":0,"scavenged":5373,"reclaimed":51868400,"evidence":"cycles=4 evacuated=0 scavenged=5373 reclaimed=51868400"},{"test":"test_gap_gc_closure_call_argument_rooting","arm":"force_verify","result":"PASS","cycles":4,"evacuated":0,"scavenged":5373,"reclaimed":51868400,"evidence":"cycles=4 evacuated=0 scavenged=5373 reclaimed=51868400"},{"test":"test_gap_gc_closure_call_argument_rooting","arm":"cons_scan_off","result":"PASS","cycles":4,"evacuated":0,"scavenged":5373,"reclaimed":51868400,"evidence":"cycles=4 evacuated=0 scavenged=5373 reclaimed=51868400"},{"test":"test_gap_gc_closure_call_argument_rooting","arm":"shipped_default","result":"PASS","cycles":1,"evacuated":0,"scavenged":3346,"reclaimed":77972800,"evidence":"cycles=1 evacuated=0 scavenged=3346 reclaimed=77972800"},{"test":"test_gap_gc_call_argument_rooting","arm":"default","result":"UNVER","cycles":0,"evacuated":0,"scavenged":0,"reclaimed":0,"evidence":"cycles=0 evacuated=0 scavenged=0 reclaimed=0"},{"test":"test_gap_gc_call_argument_rooting","arm":"safepoint_minor","result":"UNVER","cycles":0,"evacuated":0,"scavenged":0,"reclaimed":0,"evidence":"cycles=0 evacuated=0 scavenged=0 reclaimed=0"},{"test":"test_gap_gc_call_argument_rooting","arm":"evac_minor","result":"PASS","cycles":1,"evacuated":0,"scavenged":16,"reclaimed":5223320,"evidence":"cycles=1 evacuated=0 scavenged=16 reclaimed=5223320"},{"test":"test_gap_gc_call_argument_rooting","arm":"verify_evac","result":"UNVER","cycles":0,"evacuated":0,"scavenged":0,"reclaimed":0,"evidence":"cycles=0 evacuated=0 scavenged=0 reclaimed=0"},{"test":"test_gap_gc_call_argument_rooting","arm":"force_verify","result":"PASS","cycles":1,"evacuated":0,"scavenged":16,"reclaimed":5223320,"evidence":"cycles=1 evacuated=0 scavenged=16 reclaimed=5223320"},{"test":"test_gap_gc_call_argument_rooting","arm":"cons_scan_off","result":"UNVER","cycles":0,"evacuated":0,"scavenged":0,"reclaimed":0,"evidence":"cycles=0 evacuated=0 scavenged=0 reclaimed=0"},{"test":"test_gap_gc_call_argument_rooting","arm":"shipped_default","result":"PASS","cycles":0,"evacuated":0,"scavenged":0,"reclaimed":0,"evidence":"cycles=0 evacuated=0 scavenged=0 reclaimed=0"},{"test":"test_gap_gc_next_request_import","arm":"default","result":"PASS","cycles":2,"evacuated":0,"scavenged":6777,"reclaimed":3480440,"evidence":"cycles=2 evacuated=0 scavenged=6777 reclaimed=3480440"},{"test":"test_gap_gc_next_request_import","arm":"safepoint_minor","result":"PASS","cycles":2,"evacuated":0,"scavenged":6777,"reclaimed":3480440,"evidence":"cycles=2 evacuated=0 scavenged=6777 reclaimed=3480440"},{"test":"test_gap_gc_next_request_import","arm":"evac_minor","result":"PASS","cycles":2,"evacuated":0,"scavenged":6777,"reclaimed":3480440,"evidence":"cycles=2 evacuated=0 scavenged=6777 reclaimed=3480440"},{"test":"test_gap_gc_next_request_import","arm":"verify_evac","result":"PASS","cycles":2,"evacuated":0,"scavenged":6777,"reclaimed":3480440,"evidence":"cycles=2 evacuated=0 scavenged=6777 reclaimed=3480440"},{"test":"test_gap_gc_next_request_import","arm":"force_verify","result":"PASS","cycles":3,"evacuated":0,"scavenged":13206,"reclaimed":6547520,"evidence":"cycles=3 evacuated=0 scavenged=13206 reclaimed=6547520"},{"test":"test_gap_gc_next_request_import","arm":"cons_scan_off","result":"PASS","cycles":2,"evacuated":0,"scavenged":6777,"reclaimed":3480440,"evidence":"cycles=2 evacuated=0 scavenged=6777 reclaimed=3480440"},{"test":"test_gap_gc_next_request_import","arm":"shipped_default","result":"PASS","cycles":1,"evacuated":0,"scavenged":0,"reclaimed":629440,"evidence":"cycles=1 evacuated=0 scavenged=0 reclaimed=629440"},{"test":"test_gap_gc_rest_argument_rooting","arm":"default","result":"PASS","cycles":1,"evacuated":0,"scavenged":406,"reclaimed":5142040,"evidence":"cycles=1 evacuated=0 scavenged=406 reclaimed=5142040"},{"test":"test_gap_gc_rest_argument_rooting","arm":"safepoint_minor","result":"PASS","cycles":1,"evacuated":0,"scavenged":406,"reclaimed":5142040,"evidence":"cycles=1 evacuated=0 scavenged=406 reclaimed=5142040"},{"test":"test_gap_gc_rest_argument_rooting","arm":"evac_minor","result":"PASS","cycles":1,"evacuated":0,"scavenged":406,"reclaimed":5142040,"evidence":"cycles=1 evacuated=0 scavenged=406 reclaimed=5142040"},{"test":"test_gap_gc_rest_argument_rooting","arm":"verify_evac","result":"PASS","cycles":1,"evacuated":0,"scavenged":406,"reclaimed":5142040,"evidence":"cycles=1 evacuated=0 scavenged=406 reclaimed=5142040"},{"test":"test_gap_gc_rest_argument_rooting","arm":"force_verify","result":"PASS","cycles":1,"evacuated":0,"scavenged":406,"reclaimed":5142040,"evidence":"cycles=1 evacuated=0 scavenged=406 reclaimed=5142040"},{"test":"test_gap_gc_rest_argument_rooting","arm":"cons_scan_off","result":"PASS","cycles":1,"evacuated":0,"scavenged":406,"reclaimed":5142040,"evidence":"cycles=1 evacuated=0 scavenged=406 reclaimed=5142040"},{"test":"test_gap_gc_rest_argument_rooting","arm":"shipped_default","result":"PASS","cycles":0,"evacuated":0,"scavenged":0,"reclaimed":0,"evidence":"cycles=0 evacuated=0 scavenged=0 reclaimed=0"},{"test":"test_gap_gc_same_module_call_argument_rooting","arm":"default","result":"PASS","cycles":1,"evacuated":0,"scavenged":70,"reclaimed":5225680,"evidence":"cycles=1 evacuated=0 scavenged=70 reclaimed=5225680"},{"test":"test_gap_gc_same_module_call_argument_rooting","arm":"safepoint_minor","result":"PASS","cycles":1,"evacuated":0,"scavenged":70,"reclaimed":5225680,"evidence":"cycles=1 evacuated=0 scavenged=70 reclaimed=5225680"},{"test":"test_gap_gc_same_module_call_argument_rooting","arm":"evac_minor","result":"PASS","cycles":1,"evacuated":0,"scavenged":70,"reclaimed":5225680,"evidence":"cycles=1 evacuated=0 scavenged=70 reclaimed=5225680"},{"test":"test_gap_gc_same_module_call_argument_rooting","arm":"verify_evac","result":"PASS","cycles":1,"evacuated":0,"scavenged":70,"reclaimed":5225680,"evidence":"cycles=1 evacuated=0 scavenged=70 reclaimed=5225680"},{"test":"test_gap_gc_same_module_call_argument_rooting","arm":"force_verify","result":"PASS","cycles":1,"evacuated":0,"scavenged":70,"reclaimed":5225680,"evidence":"cycles=1 evacuated=0 scavenged=70 reclaimed=5225680"},{"test":"test_gap_gc_same_module_call_argument_rooting","arm":"cons_scan_off","result":"PASS","cycles":1,"evacuated":0,"scavenged":70,"reclaimed":5225680,"evidence":"cycles=1 evacuated=0 scavenged=70 reclaimed=5225680"},{"test":"test_gap_gc_same_module_call_argument_rooting","arm":"shipped_default","result":"PASS","cycles":0,"evacuated":0,"scavenged":0,"reclaimed":0,"evidence":"cycles=0 evacuated=0 scavenged=0 reclaimed=0"},{"test":"test_gap_gc_new_instance_rooting","arm":"default","result":"PASS","cycles":4,"evacuated":0,"scavenged":6101,"reclaimed":51720240,"evidence":"cycles=4 evacuated=0 scavenged=6101 reclaimed=51720240"},{"test":"test_gap_gc_new_instance_rooting","arm":"safepoint_minor","result":"PASS","cycles":4,"evacuated":0,"scavenged":6101,"reclaimed":51720240,"evidence":"cycles=4 evacuated=0 scavenged=6101 reclaimed=51720240"},{"test":"test_gap_gc_new_instance_rooting","arm":"evac_minor","result":"PASS","cycles":4,"evacuated":0,"scavenged":6101,"reclaimed":51720240,"evidence":"cycles=4 evacuated=0 scavenged=6101 reclaimed=51720240"},{"test":"test_gap_gc_new_instance_rooting","arm":"verify_evac","result":"PASS","cycles":4,"evacuated":0,"scavenged":6101,"reclaimed":51720240,"evidence":"cycles=4 evacuated=0 scavenged=6101 reclaimed=51720240"},{"test":"test_gap_gc_new_instance_rooting","arm":"force_verify","result":"PASS","cycles":4,"evacuated":0,"scavenged":6101,"reclaimed":51720240,"evidence":"cycles=4 evacuated=0 scavenged=6101 reclaimed=51720240"},{"test":"test_gap_gc_new_instance_rooting","arm":"cons_scan_off","result":"PASS","cycles":4,"evacuated":0,"scavenged":6101,"reclaimed":51720240,"evidence":"cycles=4 evacuated=0 scavenged=6101 reclaimed=51720240"},{"test":"test_gap_gc_new_instance_rooting","arm":"shipped_default","result":"PASS","cycles":1,"evacuated":0,"scavenged":3960,"reclaimed":77829840,"evidence":"cycles=1 evacuated=0 scavenged=3960 reclaimed=77829840"},{"test":"test_gap_gc_assign_string_source_rooting","arm":"default","result":"PASS","cycles":2,"evacuated":0,"scavenged":5880,"reclaimed":16530696,"evidence":"cycles=2 evacuated=0 scavenged=5880 reclaimed=16530696"},{"test":"test_gap_gc_assign_string_source_rooting","arm":"safepoint_minor","result":"PASS","cycles":2,"evacuated":0,"scavenged":5880,"reclaimed":16530696,"evidence":"cycles=2 evacuated=0 scavenged=5880 reclaimed=16530696"},{"test":"test_gap_gc_assign_string_source_rooting","arm":"evac_minor","result":"PASS","cycles":2,"evacuated":0,"scavenged":5880,"reclaimed":16530696,"evidence":"cycles=2 evacuated=0 scavenged=5880 reclaimed=16530696"},{"test":"test_gap_gc_assign_string_source_rooting","arm":"verify_evac","result":"PASS","cycles":3,"evacuated":0,"scavenged":25699,"reclaimed":26594160,"evidence":"cycles=3 evacuated=0 scavenged=25699 reclaimed=26594160"},{"test":"test_gap_gc_assign_string_source_rooting","arm":"force_verify","result":"PASS","cycles":3,"evacuated":0,"scavenged":25699,"reclaimed":26594160,"evidence":"cycles=3 evacuated=0 scavenged=25699 reclaimed=26594160"},{"test":"test_gap_gc_assign_string_source_rooting","arm":"cons_scan_off","result":"PASS","cycles":2,"evacuated":0,"scavenged":5880,"reclaimed":16530696,"evidence":"cycles=2 evacuated=0 scavenged=5880 reclaimed=16530696"},{"test":"test_gap_gc_assign_string_source_rooting","arm":"shipped_default","result":"PASS","cycles":0,"evacuated":0,"scavenged":0,"reclaimed":0,"evidence":"cycles=0 evacuated=0 scavenged=0 reclaimed=0"},{"test":"test_gap_gc_typeof_string_cache_rooting","arm":"default","result":"PASS","cycles":4,"evacuated":0,"scavenged":703,"reclaimed":65359480,"evidence":"cycles=4 evacuated=0 scavenged=703 reclaimed=65359480"},{"test":"test_gap_gc_typeof_string_cache_rooting","arm":"safepoint_minor","result":"PASS","cycles":4,"evacuated":0,"scavenged":703,"reclaimed":65359480,"evidence":"cycles=4 evacuated=0 scavenged=703 reclaimed=65359480"},{"test":"test_gap_gc_typeof_string_cache_rooting","arm":"evac_minor","result":"PASS","cycles":4,"evacuated":0,"scavenged":703,"reclaimed":70585696,"evidence":"cycles=4 evacuated=0 scavenged=703 reclaimed=70585696"},{"test":"test_gap_gc_typeof_string_cache_rooting","arm":"verify_evac","result":"PASS","cycles":4,"evacuated":0,"scavenged":703,"reclaimed":65359480,"evidence":"cycles=4 evacuated=0 scavenged=703 reclaimed=65359480"},{"test":"test_gap_gc_typeof_string_cache_rooting","arm":"force_verify","result":"PASS","cycles":4,"evacuated":0,"scavenged":3051,"reclaimed":59982440,"evidence":"cycles=4 evacuated=0 scavenged=3051 reclaimed=59982440"},{"test":"test_gap_gc_typeof_string_cache_rooting","arm":"cons_scan_off","result":"PASS","cycles":4,"evacuated":0,"scavenged":703,"reclaimed":70585696,"evidence":"cycles=4 evacuated=0 scavenged=703 reclaimed=70585696"},{"test":"test_gap_gc_typeof_string_cache_rooting","arm":"shipped_default","result":"PASS","cycles":0,"evacuated":0,"scavenged":0,"reclaimed":0,"evidence":"cycles=0 evacuated=0 scavenged=0 reclaimed=0"},{"test":"test_gap_gc_closure_call_prev_this_rooting","arm":"default","result":"PASS","cycles":4,"evacuated":0,"scavenged":5681,"reclaimed":51829280,"evidence":"cycles=4 evacuated=0 scavenged=5681 reclaimed=51829280"},{"test":"test_gap_gc_closure_call_prev_this_rooting","arm":"safepoint_minor","result":"PASS","cycles":4,"evacuated":0,"scavenged":5681,"reclaimed":51829280,"evidence":"cycles=4 evacuated=0 scavenged=5681 reclaimed=51829280"},{"test":"test_gap_gc_closure_call_prev_this_rooting","arm":"evac_minor","result":"PASS","cycles":4,"evacuated":0,"scavenged":5681,"reclaimed":51829280,"evidence":"cycles=4 evacuated=0 scavenged=5681 reclaimed=51829280"},{"test":"test_gap_gc_closure_call_prev_this_rooting","arm":"verify_evac","result":"PASS","cycles":4,"evacuated":0,"scavenged":5681,"reclaimed":51829280,"evidence":"cycles=4 evacuated=0 scavenged=5681 reclaimed=51829280"},{"test":"test_gap_gc_closure_call_prev_this_rooting","arm":"force_verify","result":"PASS","cycles":4,"evacuated":0,"scavenged":5681,"reclaimed":51829280,"evidence":"cycles=4 evacuated=0 scavenged=5681 reclaimed=51829280"},{"test":"test_gap_gc_closure_call_prev_this_rooting","arm":"cons_scan_off","result":"PASS","cycles":4,"evacuated":0,"scavenged":5681,"reclaimed":51829280,"evidence":"cycles=4 evacuated=0 scavenged=5681 reclaimed=51829280"},{"test":"test_gap_gc_closure_call_prev_this_rooting","arm":"shipped_default","result":"PASS","cycles":1,"evacuated":0,"scavenged":2740,"reclaimed":78094480,"evidence":"cycles=1 evacuated=0 scavenged=2740 reclaimed=78094480"},{"test":"test_gap_gc_regexp_receiver_rooting","arm":"default","result":"UNVER","cycles":2,"evacuated":0,"scavenged":0,"reclaimed":2235136,"evidence":"cycles=2 evacuated=0 scavenged=0 reclaimed=2235136"},{"test":"test_gap_gc_regexp_receiver_rooting","arm":"safepoint_minor","result":"UNVER","cycles":2,"evacuated":0,"scavenged":0,"reclaimed":2235136,"evidence":"cycles=2 evacuated=0 scavenged=0 reclaimed=2235136"},{"test":"test_gap_gc_regexp_receiver_rooting","arm":"evac_minor","result":"PASS","cycles":1,"evacuated":0,"scavenged":6559,"reclaimed":2950080,"evidence":"cycles=1 evacuated=0 scavenged=6559 reclaimed=2950080"},{"test":"test_gap_gc_regexp_receiver_rooting","arm":"verify_evac","result":"UNVER","cycles":2,"evacuated":0,"scavenged":0,"reclaimed":2235136,"evidence":"cycles=2 evacuated=0 scavenged=0 reclaimed=2235136"},{"test":"test_gap_gc_regexp_receiver_rooting","arm":"force_verify","result":"PASS","cycles":1,"evacuated":0,"scavenged":6559,"reclaimed":2950080,"evidence":"cycles=1 evacuated=0 scavenged=6559 reclaimed=2950080"},{"test":"test_gap_gc_regexp_receiver_rooting","arm":"cons_scan_off","result":"UNVER","cycles":2,"evacuated":0,"scavenged":0,"reclaimed":2235136,"evidence":"cycles=2 evacuated=0 scavenged=0 reclaimed=2235136"},{"test":"test_gap_gc_regexp_receiver_rooting","arm":"shipped_default","result":"PASS","cycles":0,"evacuated":0,"scavenged":0,"reclaimed":0,"evidence":"cycles=0 evacuated=0 scavenged=0 reclaimed=0"},{"test":"test_gap_gc_staging_args_rooting","arm":"default","result":"UNVER","cycles":0,"evacuated":0,"scavenged":0,"reclaimed":0,"evidence":"cycles=0 evacuated=0 scavenged=0 reclaimed=0"},{"test":"test_gap_gc_staging_args_rooting","arm":"safepoint_minor","result":"UNVER","cycles":0,"evacuated":0,"scavenged":0,"reclaimed":0,"evidence":"cycles=0 evacuated=0 scavenged=0 reclaimed=0"},{"test":"test_gap_gc_staging_args_rooting","arm":"evac_minor","result":"PASS","cycles":1,"evacuated":0,"scavenged":27,"reclaimed":84120,"evidence":"cycles=1 evacuated=0 scavenged=27 reclaimed=84120"},{"test":"test_gap_gc_staging_args_rooting","arm":"verify_evac","result":"UNVER","cycles":0,"evacuated":0,"scavenged":0,"reclaimed":16824,"evidence":"cycles=0 evacuated=0 scavenged=0 reclaimed=16824"},{"test":"test_gap_gc_staging_args_rooting","arm":"force_verify","result":"PASS","cycles":1,"evacuated":0,"scavenged":27,"reclaimed":84120,"evidence":"cycles=1 evacuated=0 scavenged=27 reclaimed=84120"},{"test":"test_gap_gc_staging_args_rooting","arm":"cons_scan_off","result":"UNVER","cycles":1,"evacuated":0,"scavenged":0,"reclaimed":67296,"evidence":"cycles=1 evacuated=0 scavenged=0 reclaimed=67296"},{"test":"test_gap_gc_staging_args_rooting","arm":"shipped_default","result":"PASS","cycles":0,"evacuated":0,"scavenged":0,"reclaimed":0,"evidence":"cycles=0 evacuated=0 scavenged=0 reclaimed=0"},{"test":"test_gap_gc_interval_args_rooting","arm":"default","result":"UNVER","cycles":1,"evacuated":0,"scavenged":0,"reclaimed":96160,"evidence":"cycles=1 evacuated=0 scavenged=0 reclaimed=96160"},{"test":"test_gap_gc_interval_args_rooting","arm":"safepoint_minor","result":"UNVER","cycles":1,"evacuated":0,"scavenged":0,"reclaimed":64160,"evidence":"cycles=1 evacuated=0 scavenged=0 reclaimed=64160"},{"test":"test_gap_gc_interval_args_rooting","arm":"evac_minor","result":"PASS","cycles":1,"evacuated":0,"scavenged":12,"reclaimed":80200,"evidence":"cycles=1 evacuated=0 scavenged=12 reclaimed=80200"},{"test":"test_gap_gc_interval_args_rooting","arm":"verify_evac","result":"UNVER","cycles":1,"evacuated":0,"scavenged":0,"reclaimed":64160,"evidence":"cycles=1 evacuated=0 scavenged=0 reclaimed=64160"},{"test":"test_gap_gc_interval_args_rooting","arm":"force_verify","result":"PASS","cycles":1,"evacuated":0,"scavenged":12,"reclaimed":80200,"evidence":"cycles=1 evacuated=0 scavenged=12 reclaimed=80200"},{"test":"test_gap_gc_interval_args_rooting","arm":"cons_scan_off","result":"UNVER","cycles":1,"evacuated":0,"scavenged":0,"reclaimed":64160,"evidence":"cycles=1 evacuated=0 scavenged=0 reclaimed=64160"},{"test":"test_gap_gc_interval_args_rooting","arm":"shipped_default","result":"PASS","cycles":0,"evacuated":0,"scavenged":0,"reclaimed":0,"evidence":"cycles=0 evacuated=0 scavenged=0 reclaimed=0"},{"test":"test_gap_gc_process_env_cache_rooting","arm":"default","result":"PASS","cycles":3,"evacuated":0,"scavenged":13664,"reclaimed":34362560,"evidence":"cycles=3 evacuated=0 scavenged=13664 reclaimed=34362560"},{"test":"test_gap_gc_process_env_cache_rooting","arm":"safepoint_minor","result":"PASS","cycles":3,"evacuated":0,"scavenged":15145,"reclaimed":34059040,"evidence":"cycles=3 evacuated=0 scavenged=15145 reclaimed=34059040"},{"test":"test_gap_gc_process_env_cache_rooting","arm":"evac_minor","result":"PASS","cycles":3,"evacuated":0,"scavenged":15018,"reclaimed":34174560,"evidence":"cycles=3 evacuated=0 scavenged=15018 reclaimed=34174560"},{"test":"test_gap_gc_process_env_cache_rooting","arm":"verify_evac","result":"PASS","cycles":3,"evacuated":0,"scavenged":15145,"reclaimed":34059040,"evidence":"cycles=3 evacuated=0 scavenged=15145 reclaimed=34059040"},{"test":"test_gap_gc_process_env_cache_rooting","arm":"force_verify","result":"PASS","cycles":3,"evacuated":0,"scavenged":14909,"reclaimed":34188720,"evidence":"cycles=3 evacuated=0 scavenged=14909 reclaimed=34188720"},{"test":"test_gap_gc_process_env_cache_rooting","arm":"cons_scan_off","result":"PASS","cycles":3,"evacuated":0,"scavenged":15145,"reclaimed":34059040,"evidence":"cycles=3 evacuated=0 scavenged=15145 reclaimed=34059040"},{"test":"test_gap_gc_process_env_cache_rooting","arm":"shipped_default","result":"PASS","cycles":0,"evacuated":0,"scavenged":0,"reclaimed":0,"evidence":"cycles=0 evacuated=0 scavenged=0 reclaimed=0"},{"test":"test_gap_gc_symbol_local_rooting","arm":"default","result":"PASS","cycles":86,"evacuated":0,"scavenged":5,"reclaimed":127779008,"evidence":"cycles=86 evacuated=0 scavenged=5 reclaimed=127779008"},{"test":"test_gap_gc_symbol_local_rooting","arm":"safepoint_minor","result":"PASS","cycles":86,"evacuated":0,"scavenged":5,"reclaimed":127779008,"evidence":"cycles=86 evacuated=0 scavenged=5 reclaimed=127779008"},{"test":"test_gap_gc_symbol_local_rooting","arm":"evac_minor","result":"PASS","cycles":86,"evacuated":0,"scavenged":5,"reclaimed":127779008,"evidence":"cycles=86 evacuated=0 scavenged=5 reclaimed=127779008"},{"test":"test_gap_gc_symbol_local_rooting","arm":"verify_evac","result":"PASS","cycles":86,"evacuated":0,"scavenged":5,"reclaimed":127779008,"evidence":"cycles=86 evacuated=0 scavenged=5 reclaimed=127779008"},{"test":"test_gap_gc_symbol_local_rooting","arm":"force_verify","result":"PASS","cycles":86,"evacuated":0,"scavenged":12524,"reclaimed":127858512,"evidence":"cycles=86 evacuated=0 scavenged=12524 reclaimed=127858512"},{"test":"test_gap_gc_symbol_local_rooting","arm":"cons_scan_off","result":"PASS","cycles":86,"evacuated":0,"scavenged":5,"reclaimed":127779008,"evidence":"cycles=86 evacuated=0 scavenged=5 reclaimed=127779008"},{"test":"test_gap_gc_symbol_local_rooting","arm":"shipped_default","result":"PASS","cycles":85,"evacuated":0,"scavenged":12524,"reclaimed":127858160,"evidence":"cycles=85 evacuated=0 scavenged=12524 reclaimed=127858160"},{"test":"test_gap_gc_dynamic_construct_receiver_rooting","arm":"default","result":"PASS","cycles":4,"evacuated":0,"scavenged":14546,"reclaimed":49906560,"evidence":"cycles=4 evacuated=0 scavenged=14546 reclaimed=49906560"},{"test":"test_gap_gc_dynamic_construct_receiver_rooting","arm":"safepoint_minor","result":"PASS","cycles":4,"evacuated":0,"scavenged":14546,"reclaimed":49906560,"evidence":"cycles=4 evacuated=0 scavenged=14546 reclaimed=49906560"},{"test":"test_gap_gc_dynamic_construct_receiver_rooting","arm":"evac_minor","result":"PASS","cycles":4,"evacuated":0,"scavenged":14546,"reclaimed":49906560,"evidence":"cycles=4 evacuated=0 scavenged=14546 reclaimed=49906560"},{"test":"test_gap_gc_dynamic_construct_receiver_rooting","arm":"verify_evac","result":"PASS","cycles":4,"evacuated":0,"scavenged":14546,"reclaimed":49906560,"evidence":"cycles=4 evacuated=0 scavenged=14546 reclaimed=49906560"},{"test":"test_gap_gc_dynamic_construct_receiver_rooting","arm":"force_verify","result":"PASS","cycles":4,"evacuated":0,"scavenged":14546,"reclaimed":49906560,"evidence":"cycles=4 evacuated=0 scavenged=14546 reclaimed=49906560"},{"test":"test_gap_gc_dynamic_construct_receiver_rooting","arm":"cons_scan_off","result":"PASS","cycles":4,"evacuated":0,"scavenged":14546,"reclaimed":49906560,"evidence":"cycles=4 evacuated=0 scavenged=14546 reclaimed=49906560"},{"test":"test_gap_gc_dynamic_construct_receiver_rooting","arm":"shipped_default","result":"PASS","cycles":1,"evacuated":0,"scavenged":9109,"reclaimed":75689560,"evidence":"cycles=1 evacuated=0 scavenged=9109 reclaimed=75689560"},{"test":"test_gap_gc_inlined_ctor_body_locals_rooting","arm":"default","result":"UNVER","cycles":0,"evacuated":0,"scavenged":0,"reclaimed":0,"evidence":"cycles=0 evacuated=0 scavenged=0 reclaimed=0"},{"test":"test_gap_gc_inlined_ctor_body_locals_rooting","arm":"safepoint_minor","result":"UNVER","cycles":0,"evacuated":0,"scavenged":0,"reclaimed":0,"evidence":"cycles=0 evacuated=0 scavenged=0 reclaimed=0"},{"test":"test_gap_gc_inlined_ctor_body_locals_rooting","arm":"evac_minor","result":"UNVER","cycles":1,"evacuated":0,"scavenged":0,"reclaimed":248176,"evidence":"cycles=1 evacuated=0 scavenged=0 reclaimed=248176"},{"test":"test_gap_gc_inlined_ctor_body_locals_rooting","arm":"verify_evac","result":"UNVER","cycles":0,"evacuated":0,"scavenged":0,"reclaimed":0,"evidence":"cycles=0 evacuated=0 scavenged=0 reclaimed=0"},{"test":"test_gap_gc_inlined_ctor_body_locals_rooting","arm":"force_verify","result":"PASS","cycles":1,"evacuated":0,"scavenged":6491,"reclaimed":620440,"evidence":"cycles=1 evacuated=0 scavenged=6491 reclaimed=620440"},{"test":"test_gap_gc_inlined_ctor_body_locals_rooting","arm":"cons_scan_off","result":"UNVER","cycles":0,"evacuated":0,"scavenged":0,"reclaimed":0,"evidence":"cycles=0 evacuated=0 scavenged=0 reclaimed=0"},{"test":"test_gap_gc_inlined_ctor_body_locals_rooting","arm":"shipped_default","result":"PASS","cycles":0,"evacuated":0,"scavenged":0,"reclaimed":0,"evidence":"cycles=0 evacuated=0 scavenged=0 reclaimed=0"},{"test":"test_gap_gc_optional_param_receiver_rooting","arm":"default","result":"PASS","cycles":2,"evacuated":0,"scavenged":12948,"reclaimed":18724160,"evidence":"cycles=2 evacuated=0 scavenged=12948 reclaimed=18724160"},{"test":"test_gap_gc_optional_param_receiver_rooting","arm":"safepoint_minor","result":"PASS","cycles":2,"evacuated":0,"scavenged":12948,"reclaimed":18724160,"evidence":"cycles=2 evacuated=0 scavenged=12948 reclaimed=18724160"},{"test":"test_gap_gc_optional_param_receiver_rooting","arm":"evac_minor","result":"PASS","cycles":2,"evacuated":0,"scavenged":12948,"reclaimed":18724160,"evidence":"cycles=2 evacuated=0 scavenged=12948 reclaimed=18724160"},{"test":"test_gap_gc_optional_param_receiver_rooting","arm":"verify_evac","result":"PASS","cycles":2,"evacuated":0,"scavenged":12948,"reclaimed":18724160,"evidence":"cycles=2 evacuated=0 scavenged=12948 reclaimed=18724160"},{"test":"test_gap_gc_optional_param_receiver_rooting","arm":"force_verify","result":"PASS","cycles":2,"evacuated":0,"scavenged":12948,"reclaimed":18724160,"evidence":"cycles=2 evacuated=0 scavenged=12948 reclaimed=18724160"},{"test":"test_gap_gc_optional_param_receiver_rooting","arm":"cons_scan_off","result":"PASS","cycles":2,"evacuated":0,"scavenged":12948,"reclaimed":18724160,"evidence":"cycles=2 evacuated=0 scavenged=12948 reclaimed=18724160"},{"test":"test_gap_gc_optional_param_receiver_rooting","arm":"shipped_default","result":"PASS","cycles":0,"evacuated":0,"scavenged":0,"reclaimed":0,"evidence":"cycles=0 evacuated=0 scavenged=0 reclaimed=0"},{"test":"test_gap_gc_iterator_drain_rooting","arm":"default","result":"PASS","cycles":4,"evacuated":0,"scavenged":60,"reclaimed":83884800,"evidence":"cycles=4 evacuated=0 scavenged=60 reclaimed=83884800"},{"test":"test_gap_gc_iterator_drain_rooting","arm":"safepoint_minor","result":"PASS","cycles":4,"evacuated":0,"scavenged":60,"reclaimed":83884800,"evidence":"cycles=4 evacuated=0 scavenged=60 reclaimed=83884800"},{"test":"test_gap_gc_iterator_drain_rooting","arm":"evac_minor","result":"PASS","cycles":4,"evacuated":0,"scavenged":60,"reclaimed":83884800,"evidence":"cycles=4 evacuated=0 scavenged=60 reclaimed=83884800"},{"test":"test_gap_gc_iterator_drain_rooting","arm":"verify_evac","result":"PASS","cycles":4,"evacuated":0,"scavenged":60,"reclaimed":83884800,"evidence":"cycles=4 evacuated=0 scavenged=60 reclaimed=83884800"},{"test":"test_gap_gc_iterator_drain_rooting","arm":"force_verify","result":"PASS","cycles":4,"evacuated":0,"scavenged":60,"reclaimed":83884800,"evidence":"cycles=4 evacuated=0 scavenged=60 reclaimed=83884800"},{"test":"test_gap_gc_iterator_drain_rooting","arm":"cons_scan_off","result":"PASS","cycles":4,"evacuated":0,"scavenged":60,"reclaimed":83884800,"evidence":"cycles=4 evacuated=0 scavenged=60 reclaimed=83884800"},{"test":"test_gap_gc_iterator_drain_rooting","arm":"shipped_default","result":"PASS","cycles":1,"evacuated":0,"scavenged":30,"reclaimed":83880000,"evidence":"cycles=1 evacuated=0 scavenged=30 reclaimed=83880000"},{"test":"test_gap_gc_global_builtin_lookup_rooting","arm":"default","result":"PASS","cycles":2,"evacuated":0,"scavenged":44661,"reclaimed":209584,"evidence":"cycles=2 evacuated=0 scavenged=44661 reclaimed=209584"},{"test":"test_gap_gc_global_builtin_lookup_rooting","arm":"safepoint_minor","result":"PASS","cycles":2,"evacuated":0,"scavenged":44661,"reclaimed":209584,"evidence":"cycles=2 evacuated=0 scavenged=44661 reclaimed=209584"},{"test":"test_gap_gc_global_builtin_lookup_rooting","arm":"evac_minor","result":"PASS","cycles":2,"evacuated":0,"scavenged":44661,"reclaimed":209584,"evidence":"cycles=2 evacuated=0 scavenged=44661 reclaimed=209584"},{"test":"test_gap_gc_global_builtin_lookup_rooting","arm":"verify_evac","result":"PASS","cycles":3,"evacuated":0,"scavenged":36333,"reclaimed":914896,"evidence":"cycles=3 evacuated=0 scavenged=36333 reclaimed=914896"},{"test":"test_gap_gc_global_builtin_lookup_rooting","arm":"force_verify","result":"PASS","cycles":3,"evacuated":0,"scavenged":80994,"reclaimed":916480,"evidence":"cycles=3 evacuated=0 scavenged=80994 reclaimed=916480"},{"test":"test_gap_gc_global_builtin_lookup_rooting","arm":"cons_scan_off","result":"PASS","cycles":2,"evacuated":0,"scavenged":44661,"reclaimed":209584,"evidence":"cycles=2 evacuated=0 scavenged=44661 reclaimed=209584"},{"test":"test_gap_gc_global_builtin_lookup_rooting","arm":"shipped_default","result":"PASS","cycles":0,"evacuated":0,"scavenged":0,"reclaimed":0,"evidence":"cycles=0 evacuated=0 scavenged=0 reclaimed=0"},{"test":"test_gap_gc_spread_symbol_iterator_rooting","arm":"default","result":"PASS","cycles":3,"evacuated":0,"scavenged":26,"reclaimed":41940960,"evidence":"cycles=3 evacuated=0 scavenged=26 reclaimed=41940960"},{"test":"test_gap_gc_spread_symbol_iterator_rooting","arm":"safepoint_minor","result":"PASS","cycles":3,"evacuated":0,"scavenged":26,"reclaimed":41940960,"evidence":"cycles=3 evacuated=0 scavenged=26 reclaimed=41940960"},{"test":"test_gap_gc_spread_symbol_iterator_rooting","arm":"evac_minor","result":"PASS","cycles":3,"evacuated":0,"scavenged":26,"reclaimed":41940960,"evidence":"cycles=3 evacuated=0 scavenged=26 reclaimed=41940960"},{"test":"test_gap_gc_spread_symbol_iterator_rooting","arm":"verify_evac","result":"PASS","cycles":3,"evacuated":0,"scavenged":26,"reclaimed":41940960,"evidence":"cycles=3 evacuated=0 scavenged=26 reclaimed=41940960"},{"test":"test_gap_gc_spread_symbol_iterator_rooting","arm":"force_verify","result":"PASS","cycles":3,"evacuated":0,"scavenged":26,"reclaimed":41940960,"evidence":"cycles=3 evacuated=0 scavenged=26 reclaimed=41940960"},{"test":"test_gap_gc_spread_symbol_iterator_rooting","arm":"cons_scan_off","result":"PASS","cycles":3,"evacuated":0,"scavenged":26,"reclaimed":41940960,"evidence":"cycles=3 evacuated=0 scavenged=26 reclaimed=41940960"},{"test":"test_gap_gc_spread_symbol_iterator_rooting","arm":"shipped_default","result":"PASS","cycles":0,"evacuated":0,"scavenged":0,"reclaimed":0,"evidence":"cycles=0 evacuated=0 scavenged=0 reclaimed=0"},{"test":"test_gap_repsel_element_shape_loop_clone","arm":"default","result":"UNVER","cycles":0,"evacuated":0,"scavenged":0,"reclaimed":0,"evidence":"cycles=0 evacuated=0 scavenged=0 reclaimed=0"},{"test":"test_gap_repsel_element_shape_loop_clone","arm":"safepoint_minor","result":"UNVER","cycles":0,"evacuated":0,"scavenged":0,"reclaimed":0,"evidence":"cycles=0 evacuated=0 scavenged=0 reclaimed=0"},{"test":"test_gap_repsel_element_shape_loop_clone","arm":"evac_minor","result":"UNVER","cycles":1,"evacuated":0,"scavenged":0,"reclaimed":165072,"evidence":"cycles=1 evacuated=0 scavenged=0 reclaimed=165072"},{"test":"test_gap_repsel_element_shape_loop_clone","arm":"verify_evac","result":"UNVER","cycles":0,"evacuated":0,"scavenged":0,"reclaimed":0,"evidence":"cycles=0 evacuated=0 scavenged=0 reclaimed=0"},{"test":"test_gap_repsel_element_shape_loop_clone","arm":"force_verify","result":"PASS","cycles":1,"evacuated":0,"scavenged":6576,"reclaimed":412680,"evidence":"cycles=1 evacuated=0 scavenged=6576 reclaimed=412680"},{"test":"test_gap_repsel_element_shape_loop_clone","arm":"cons_scan_off","result":"UNVER","cycles":0,"evacuated":0,"scavenged":0,"reclaimed":0,"evidence":"cycles=0 evacuated=0 scavenged=0 reclaimed=0"},{"test":"test_gap_repsel_element_shape_loop_clone","arm":"shipped_default","result":"PASS","cycles":0,"evacuated":0,"scavenged":0,"reclaimed":0,"evidence":"cycles=0 evacuated=0 scavenged=0 reclaimed=0"},{"test":"test_gap_gc_alloc_point_no_move","arm":"default","result":"PASS","cycles":43,"evacuated":0,"scavenged":15928,"reclaimed":1995287720,"evidence":"cycles=43 evacuated=0 scavenged=15928 reclaimed=1995287720"},{"test":"test_gap_gc_alloc_point_no_move","arm":"safepoint_minor","result":"PASS","cycles":43,"evacuated":0,"scavenged":15928,"reclaimed":1995287720,"evidence":"cycles=43 evacuated=0 scavenged=15928 reclaimed=1995287720"},{"test":"test_gap_gc_alloc_point_no_move","arm":"evac_minor","result":"PASS","cycles":43,"evacuated":0,"scavenged":15928,"reclaimed":1995287720,"evidence":"cycles=43 evacuated=0 scavenged=15928 reclaimed=1995287720"},{"test":"test_gap_gc_alloc_point_no_move","arm":"verify_evac","result":"PASS","cycles":43,"evacuated":0,"scavenged":15928,"reclaimed":1995287720,"evidence":"cycles=43 evacuated=0 scavenged=15928 reclaimed=1995287720"},{"test":"test_gap_gc_alloc_point_no_move","arm":"force_verify","result":"PASS","cycles":43,"evacuated":0,"scavenged":15928,"reclaimed":1995287720,"evidence":"cycles=43 evacuated=0 scavenged=15928 reclaimed=1995287720"},{"test":"test_gap_gc_alloc_point_no_move","arm":"cons_scan_off","result":"PASS","cycles":43,"evacuated":0,"scavenged":15928,"reclaimed":1995287720,"evidence":"cycles=43 evacuated=0 scavenged=15928 reclaimed=1995287720"},{"test":"test_gap_gc_alloc_point_no_move","arm":"shipped_default","result":"PASS","cycles":39,"evacuated":0,"scavenged":15668,"reclaimed":1974307040,"evidence":"cycles=39 evacuated=0 scavenged=15668 reclaimed=1974307040"},{"test":"test_gap_gc_class_field_receiver_rooting","arm":"default","result":"PASS","cycles":9,"evacuated":0,"scavenged":40278,"reclaimed":293835560,"evidence":"cycles=9 evacuated=0 scavenged=40278 reclaimed=293835560"},{"test":"test_gap_gc_class_field_receiver_rooting","arm":"safepoint_minor","result":"PASS","cycles":9,"evacuated":0,"scavenged":40278,"reclaimed":293835560,"evidence":"cycles=9 evacuated=0 scavenged=40278 reclaimed=293835560"},{"test":"test_gap_gc_class_field_receiver_rooting","arm":"evac_minor","result":"PASS","cycles":9,"evacuated":0,"scavenged":40278,"reclaimed":293835560,"evidence":"cycles=9 evacuated=0 scavenged=40278 reclaimed=293835560"},{"test":"test_gap_gc_class_field_receiver_rooting","arm":"verify_evac","result":"PASS","cycles":9,"evacuated":0,"scavenged":40278,"reclaimed":293835560,"evidence":"cycles=9 evacuated=0 scavenged=40278 reclaimed=293835560"},{"test":"test_gap_gc_class_field_receiver_rooting","arm":"force_verify","result":"PASS","cycles":9,"evacuated":0,"scavenged":40278,"reclaimed":293835560,"evidence":"cycles=9 evacuated=0 scavenged=40278 reclaimed=293835560"},{"test":"test_gap_gc_class_field_receiver_rooting","arm":"cons_scan_off","result":"PASS","cycles":9,"evacuated":0,"scavenged":40278,"reclaimed":293835560,"evidence":"cycles=9 evacuated=0 scavenged=40278 reclaimed=293835560"},{"test":"test_gap_gc_class_field_receiver_rooting","arm":"shipped_default","result":"PASS","cycles":4,"evacuated":0,"scavenged":26445,"reclaimed":263385240,"evidence":"cycles=4 evacuated=0 scavenged=26445 reclaimed=263385240"},{"test":"test_gap_gc_index_set_bounded_globalthis_ta_rooting","arm":"default","result":"PASS","cycles":42,"evacuated":0,"scavenged":167083,"reclaimed":1845634120,"evidence":"cycles=42 evacuated=0 scavenged=167083 reclaimed=1845634120"},{"test":"test_gap_gc_index_set_bounded_globalthis_ta_rooting","arm":"safepoint_minor","result":"PASS","cycles":42,"evacuated":0,"scavenged":167083,"reclaimed":1845634120,"evidence":"cycles=42 evacuated=0 scavenged=167083 reclaimed=1845634120"},{"test":"test_gap_gc_index_set_bounded_globalthis_ta_rooting","arm":"evac_minor","result":"PASS","cycles":42,"evacuated":0,"scavenged":167083,"reclaimed":1845634120,"evidence":"cycles=42 evacuated=0 scavenged=167083 reclaimed=1845634120"},{"test":"test_gap_gc_index_set_bounded_globalthis_ta_rooting","arm":"verify_evac","result":"PASS","cycles":42,"evacuated":0,"scavenged":167083,"reclaimed":1845634120,"evidence":"cycles=42 evacuated=0 scavenged=167083 reclaimed=1845634120"},{"test":"test_gap_gc_index_set_bounded_globalthis_ta_rooting","arm":"force_verify","result":"PASS","cycles":42,"evacuated":0,"scavenged":167083,"reclaimed":1845634120,"evidence":"cycles=42 evacuated=0 scavenged=167083 reclaimed=1845634120"},{"test":"test_gap_gc_index_set_bounded_globalthis_ta_rooting","arm":"cons_scan_off","result":"PASS","cycles":42,"evacuated":0,"scavenged":167083,"reclaimed":1845634120,"evidence":"cycles=42 evacuated=0 scavenged=167083 reclaimed=1845634120"},{"test":"test_gap_gc_index_set_bounded_globalthis_ta_rooting","arm":"shipped_default","result":"PASS","cycles":37,"evacuated":0,"scavenged":144604,"reclaimed":1819892320,"evidence":"cycles=37 evacuated=0 scavenged=144604 reclaimed=1819892320"},{"test":"test_gap_gc_namespace_and_computed_dispatch_rooting","arm":"default","result":"PASS","cycles":2,"evacuated":0,"scavenged":12976,"reclaimed":18760040,"evidence":"cycles=2 evacuated=0 scavenged=12976 reclaimed=18760040"},{"test":"test_gap_gc_namespace_and_computed_dispatch_rooting","arm":"safepoint_minor","result":"PASS","cycles":2,"evacuated":0,"scavenged":12976,"reclaimed":18760040,"evidence":"cycles=2 evacuated=0 scavenged=12976 reclaimed=18760040"},{"test":"test_gap_gc_namespace_and_computed_dispatch_rooting","arm":"evac_minor","result":"PASS","cycles":3,"evacuated":0,"scavenged":13476,"reclaimed":42629320,"evidence":"cycles=3 evacuated=0 scavenged=13476 reclaimed=42629320"},{"test":"test_gap_gc_namespace_and_computed_dispatch_rooting","arm":"verify_evac","result":"PASS","cycles":2,"evacuated":0,"scavenged":12976,"reclaimed":18760040,"evidence":"cycles=2 evacuated=0 scavenged=12976 reclaimed=18760040"},{"test":"test_gap_gc_namespace_and_computed_dispatch_rooting","arm":"force_verify","result":"PASS","cycles":3,"evacuated":0,"scavenged":13476,"reclaimed":42629320,"evidence":"cycles=3 evacuated=0 scavenged=13476 reclaimed=42629320"},{"test":"test_gap_gc_namespace_and_computed_dispatch_rooting","arm":"cons_scan_off","result":"PASS","cycles":2,"evacuated":0,"scavenged":12976,"reclaimed":18760040,"evidence":"cycles=2 evacuated=0 scavenged=12976 reclaimed=18760040"},{"test":"test_gap_gc_namespace_and_computed_dispatch_rooting","arm":"shipped_default","result":"PASS","cycles":0,"evacuated":0,"scavenged":0,"reclaimed":0,"evidence":"cycles=0 evacuated=0 scavenged=0 reclaimed=0"},{"test":"test_gap_gc_container_value_rooting","arm":"default","result":"UNVER","cycles":0,"evacuated":0,"scavenged":0,"reclaimed":0,"evidence":"cycles=0 evacuated=0 scavenged=0 reclaimed=0"},{"test":"test_gap_gc_container_value_rooting","arm":"safepoint_minor","result":"UNVER","cycles":0,"evacuated":0,"scavenged":0,"reclaimed":0,"evidence":"cycles=0 evacuated=0 scavenged=0 reclaimed=0"},{"test":"test_gap_gc_container_value_rooting","arm":"evac_minor","result":"PASS","cycles":1,"evacuated":0,"scavenged":6512,"reclaimed":2491320,"evidence":"cycles=1 evacuated=0 scavenged=6512 reclaimed=2491320"},{"test":"test_gap_gc_container_value_rooting","arm":"verify_evac","result":"UNVER","cycles":0,"evacuated":0,"scavenged":0,"reclaimed":0,"evidence":"cycles=0 evacuated=0 scavenged=0 reclaimed=0"},{"test":"test_gap_gc_container_value_rooting","arm":"force_verify","result":"PASS","cycles":1,"evacuated":0,"scavenged":6512,"reclaimed":2491320,"evidence":"cycles=1 evacuated=0 scavenged=6512 reclaimed=2491320"},{"test":"test_gap_gc_container_value_rooting","arm":"cons_scan_off","result":"UNVER","cycles":0,"evacuated":0,"scavenged":0,"reclaimed":0,"evidence":"cycles=0 evacuated=0 scavenged=0 reclaimed=0"},{"test":"test_gap_gc_container_value_rooting","arm":"shipped_default","result":"PASS","cycles":0,"evacuated":0,"scavenged":0,"reclaimed":0,"evidence":"cycles=0 evacuated=0 scavenged=0 reclaimed=0"},{"test":"test_gap_gc_define_properties_key_rooting","arm":"default","result":"UNVER","cycles":0,"evacuated":0,"scavenged":0,"reclaimed":0,"evidence":"cycles=0 evacuated=0 scavenged=0 reclaimed=0"},{"test":"test_gap_gc_define_properties_key_rooting","arm":"safepoint_minor","result":"UNVER","cycles":0,"evacuated":0,"scavenged":0,"reclaimed":0,"evidence":"cycles=0 evacuated=0 scavenged=0 reclaimed=0"},{"test":"test_gap_gc_define_properties_key_rooting","arm":"evac_minor","result":"UNVER","cycles":1,"evacuated":0,"scavenged":0,"reclaimed":390368,"evidence":"cycles=1 evacuated=0 scavenged=0 reclaimed=390368"},{"test":"test_gap_gc_define_properties_key_rooting","arm":"verify_evac","result":"UNVER","cycles":0,"evacuated":0,"scavenged":0,"reclaimed":0,"evidence":"cycles=0 evacuated=0 scavenged=0 reclaimed=0"},{"test":"test_gap_gc_define_properties_key_rooting","arm":"force_verify","result":"PASS","cycles":1,"evacuated":0,"scavenged":6500,"reclaimed":975920,"evidence":"cycles=1 evacuated=0 scavenged=6500 reclaimed=975920"},{"test":"test_gap_gc_define_properties_key_rooting","arm":"cons_scan_off","result":"UNVER","cycles":0,"evacuated":0,"scavenged":0,"reclaimed":0,"evidence":"cycles=0 evacuated=0 scavenged=0 reclaimed=0"},{"test":"test_gap_gc_define_properties_key_rooting","arm":"shipped_default","result":"PASS","cycles":0,"evacuated":0,"scavenged":0,"reclaimed":0,"evidence":"cycles=0 evacuated=0 scavenged=0 reclaimed=0"},{"test":"test_gap_gc_define_property_descriptor_rooting","arm":"default","result":"PASS","cycles":1,"evacuated":0,"scavenged":6759,"reclaimed":2903840,"evidence":"cycles=1 evacuated=0 scavenged=6759 reclaimed=2903840"},{"test":"test_gap_gc_define_property_descriptor_rooting","arm":"safepoint_minor","result":"PASS","cycles":1,"evacuated":0,"scavenged":6759,"reclaimed":2903840,"evidence":"cycles=1 evacuated=0 scavenged=6759 reclaimed=2903840"},{"test":"test_gap_gc_define_property_descriptor_rooting","arm":"evac_minor","result":"PASS","cycles":1,"evacuated":0,"scavenged":6759,"reclaimed":2903840,"evidence":"cycles=1 evacuated=0 scavenged=6759 reclaimed=2903840"},{"test":"test_gap_gc_define_property_descriptor_rooting","arm":"verify_evac","result":"PASS","cycles":1,"evacuated":0,"scavenged":6759,"reclaimed":2903840,"evidence":"cycles=1 evacuated=0 scavenged=6759 reclaimed=2903840"},{"test":"test_gap_gc_define_property_descriptor_rooting","arm":"force_verify","result":"PASS","cycles":1,"evacuated":0,"scavenged":6759,"reclaimed":2903840,"evidence":"cycles=1 evacuated=0 scavenged=6759 reclaimed=2903840"},{"test":"test_gap_gc_define_property_descriptor_rooting","arm":"cons_scan_off","result":"PASS","cycles":1,"evacuated":0,"scavenged":6759,"reclaimed":2903840,"evidence":"cycles=1 evacuated=0 scavenged=6759 reclaimed=2903840"},{"test":"test_gap_gc_define_property_descriptor_rooting","arm":"shipped_default","result":"PASS","cycles":0,"evacuated":0,"scavenged":0,"reclaimed":0,"evidence":"cycles=0 evacuated=0 scavenged=0 reclaimed=0"},{"test":"test_gap_repsel_element_shape_param_binding","arm":"default","result":"UNVER","cycles":0,"evacuated":0,"scavenged":0,"reclaimed":0,"evidence":"cycles=0 evacuated=0 scavenged=0 reclaimed=0"},{"test":"test_gap_repsel_element_shape_param_binding","arm":"safepoint_minor","result":"UNVER","cycles":0,"evacuated":0,"scavenged":0,"reclaimed":0,"evidence":"cycles=0 evacuated=0 scavenged=0 reclaimed=0"},{"test":"test_gap_repsel_element_shape_param_binding","arm":"evac_minor","result":"PASS","cycles":1,"evacuated":0,"scavenged":31,"reclaimed":60160,"evidence":"cycles=1 evacuated=0 scavenged=31 reclaimed=60160"},{"test":"test_gap_repsel_element_shape_param_binding","arm":"verify_evac","result":"UNVER","cycles":0,"evacuated":0,"scavenged":0,"reclaimed":0,"evidence":"cycles=0 evacuated=0 scavenged=0 reclaimed=0"},{"test":"test_gap_repsel_element_shape_param_binding","arm":"force_verify","result":"PASS","cycles":1,"evacuated":0,"scavenged":31,"reclaimed":60160,"evidence":"cycles=1 evacuated=0 scavenged=31 reclaimed=60160"},{"test":"test_gap_repsel_element_shape_param_binding","arm":"cons_scan_off","result":"UNVER","cycles":0,"evacuated":0,"scavenged":0,"reclaimed":0,"evidence":"cycles=0 evacuated=0 scavenged=0 reclaimed=0"},{"test":"test_gap_repsel_element_shape_param_binding","arm":"shipped_default","result":"PASS","cycles":0,"evacuated":0,"scavenged":0,"reclaimed":0,"evidence":"cycles=0 evacuated=0 scavenged=0 reclaimed=0"},{"test":"test_gap_repsel_element_group_numeric","arm":"default","result":"UNVER","cycles":0,"evacuated":0,"scavenged":0,"reclaimed":0,"evidence":"cycles=0 evacuated=0 scavenged=0 reclaimed=0"},{"test":"test_gap_repsel_element_group_numeric","arm":"safepoint_minor","result":"UNVER","cycles":0,"evacuated":0,"scavenged":0,"reclaimed":0,"evidence":"cycles=0 evacuated=0 scavenged=0 reclaimed=0"},{"test":"test_gap_repsel_element_group_numeric","arm":"evac_minor","result":"UNVER","cycles":1,"evacuated":0,"scavenged":0,"reclaimed":215616,"evidence":"cycles=1 evacuated=0 scavenged=0 reclaimed=215616"},{"test":"test_gap_repsel_element_group_numeric","arm":"verify_evac","result":"UNVER","cycles":0,"evacuated":0,"scavenged":0,"reclaimed":0,"evidence":"cycles=0 evacuated=0 scavenged=0 reclaimed=0"},{"test":"test_gap_repsel_element_group_numeric","arm":"force_verify","result":"PASS","cycles":1,"evacuated":0,"scavenged":6455,"reclaimed":539040,"evidence":"cycles=1 evacuated=0 scavenged=6455 reclaimed=539040"},{"test":"test_gap_repsel_element_group_numeric","arm":"cons_scan_off","result":"UNVER","cycles":0,"evacuated":0,"scavenged":0,"reclaimed":0,"evidence":"cycles=0 evacuated=0 scavenged=0 reclaimed=0"},{"test":"test_gap_repsel_element_group_numeric","arm":"shipped_default","result":"PASS","cycles":0,"evacuated":0,"scavenged":0,"reclaimed":0,"evidence":"cycles=0 evacuated=0 scavenged=0 reclaimed=0"},{"test":"test_gap_gc_container_value_rooting","arm":"default","result":"UNVER","cycles":0,"evacuated":0,"scavenged":0,"reclaimed":0,"evidence":"cycles=0 evacuated=0 scavenged=0 reclaimed=0"},{"test":"test_gap_gc_container_value_rooting","arm":"safepoint_minor","result":"UNVER","cycles":0,"evacuated":0,"scavenged":0,"reclaimed":0,"evidence":"cycles=0 evacuated=0 scavenged=0 reclaimed=0"},{"test":"test_gap_gc_container_value_rooting","arm":"evac_minor","result":"PASS","cycles":1,"evacuated":0,"scavenged":6512,"reclaimed":2491320,"evidence":"cycles=1 evacuated=0 scavenged=6512 reclaimed=2491320"},{"test":"test_gap_gc_container_value_rooting","arm":"verify_evac","result":"UNVER","cycles":0,"evacuated":0,"scavenged":0,"reclaimed":0,"evidence":"cycles=0 evacuated=0 scavenged=0 reclaimed=0"},{"test":"test_gap_gc_container_value_rooting","arm":"force_verify","result":"PASS","cycles":1,"evacuated":0,"scavenged":6512,"reclaimed":2491320,"evidence":"cycles=1 evacuated=0 scavenged=6512 reclaimed=2491320"},{"test":"test_gap_gc_container_value_rooting","arm":"cons_scan_off","result":"UNVER","cycles":0,"evacuated":0,"scavenged":0,"reclaimed":0,"evidence":"cycles=0 evacuated=0 scavenged=0 reclaimed=0"},{"test":"test_gap_gc_container_value_rooting","arm":"shipped_default","result":"PASS","cycles":0,"evacuated":0,"scavenged":0,"reclaimed":0,"evidence":"cycles=0 evacuated=0 scavenged=0 reclaimed=0"},{"test":"test_gap_gc_define_properties_key_rooting","arm":"default","result":"UNVER","cycles":0,"evacuated":0,"scavenged":0,"reclaimed":0,"evidence":"cycles=0 evacuated=0 scavenged=0 reclaimed=0"},{"test":"test_gap_gc_define_properties_key_rooting","arm":"safepoint_minor","result":"UNVER","cycles":0,"evacuated":0,"scavenged":0,"reclaimed":0,"evidence":"cycles=0 evacuated=0 scavenged=0 reclaimed=0"},{"test":"test_gap_gc_define_properties_key_rooting","arm":"evac_minor","result":"UNVER","cycles":1,"evacuated":0,"scavenged":0,"reclaimed":390368,"evidence":"cycles=1 evacuated=0 scavenged=0 reclaimed=390368"},{"test":"test_gap_gc_define_properties_key_rooting","arm":"verify_evac","result":"UNVER","cycles":0,"evacuated":0,"scavenged":0,"reclaimed":0,"evidence":"cycles=0 evacuated=0 scavenged=0 reclaimed=0"},{"test":"test_gap_gc_define_properties_key_rooting","arm":"force_verify","result":"PASS","cycles":1,"evacuated":0,"scavenged":6500,"reclaimed":975920,"evidence":"cycles=1 evacuated=0 scavenged=6500 reclaimed=975920"},{"test":"test_gap_gc_define_properties_key_rooting","arm":"cons_scan_off","result":"UNVER","cycles":0,"evacuated":0,"scavenged":0,"reclaimed":0,"evidence":"cycles=0 evacuated=0 scavenged=0 reclaimed=0"},{"test":"test_gap_gc_define_properties_key_rooting","arm":"shipped_default","result":"PASS","cycles":0,"evacuated":0,"scavenged":0,"reclaimed":0,"evidence":"cycles=0 evacuated=0 scavenged=0 reclaimed=0"},{"test":"test_gap_gc_define_property_descriptor_rooting","arm":"default","result":"PASS","cycles":1,"evacuated":0,"scavenged":6759,"reclaimed":2903840,"evidence":"cycles=1 evacuated=0 scavenged=6759 reclaimed=2903840"},{"test":"test_gap_gc_define_property_descriptor_rooting","arm":"safepoint_minor","result":"PASS","cycles":1,"evacuated":0,"scavenged":6759,"reclaimed":2903840,"evidence":"cycles=1 evacuated=0 scavenged=6759 reclaimed=2903840"},{"test":"test_gap_gc_define_property_descriptor_rooting","arm":"evac_minor","result":"PASS","cycles":1,"evacuated":0,"scavenged":6759,"reclaimed":2903840,"evidence":"cycles=1 evacuated=0 scavenged=6759 reclaimed=2903840"},{"test":"test_gap_gc_define_property_descriptor_rooting","arm":"verify_evac","result":"PASS","cycles":1,"evacuated":0,"scavenged":6759,"reclaimed":2903840,"evidence":"cycles=1 evacuated=0 scavenged=6759 reclaimed=2903840"},{"test":"test_gap_gc_define_property_descriptor_rooting","arm":"force_verify","result":"PASS","cycles":1,"evacuated":0,"scavenged":6759,"reclaimed":2903840,"evidence":"cycles=1 evacuated=0 scavenged=6759 reclaimed=2903840"},{"test":"test_gap_gc_define_property_descriptor_rooting","arm":"cons_scan_off","result":"PASS","cycles":1,"evacuated":0,"scavenged":6759,"reclaimed":2903840,"evidence":"cycles=1 evacuated=0 scavenged=6759 reclaimed=2903840"},{"test":"test_gap_gc_define_property_descriptor_rooting","arm":"shipped_default","result":"PASS","cycles":0,"evacuated":0,"scavenged":0,"reclaimed":0,"evidence":"cycles=0 evacuated=0 scavenged=0 reclaimed=0"},{"test":"test_gap_gc_uint8_buffer_callback_rooting","arm":"default","result":"PASS","cycles":3,"evacuated":0,"scavenged":12836,"reclaimed":29260120,"evidence":"cycles=3 evacuated=0 scavenged=12836 reclaimed=29260120"},{"test":"test_gap_gc_uint8_buffer_callback_rooting","arm":"safepoint_minor","result":"PASS","cycles":3,"evacuated":0,"scavenged":12836,"reclaimed":29260120,"evidence":"cycles=3 evacuated=0 scavenged=12836 reclaimed=29260120"},{"test":"test_gap_gc_uint8_buffer_callback_rooting","arm":"evac_minor","result":"PASS","cycles":3,"evacuated":0,"scavenged":12836,"reclaimed":29260120,"evidence":"cycles=3 evacuated=0 scavenged=12836 reclaimed=29260120"},{"test":"test_gap_gc_uint8_buffer_callback_rooting","arm":"verify_evac","result":"PASS","cycles":3,"evacuated":0,"scavenged":12836,"reclaimed":29260120,"evidence":"cycles=3 evacuated=0 scavenged=12836 reclaimed=29260120"},{"test":"test_gap_gc_uint8_buffer_callback_rooting","arm":"force_verify","result":"PASS","cycles":3,"evacuated":0,"scavenged":12836,"reclaimed":29260120,"evidence":"cycles=3 evacuated=0 scavenged=12836 reclaimed=29260120"},{"test":"test_gap_gc_uint8_buffer_callback_rooting","arm":"cons_scan_off","result":"PASS","cycles":3,"evacuated":0,"scavenged":12836,"reclaimed":29260120,"evidence":"cycles=3 evacuated=0 scavenged=12836 reclaimed=29260120"},{"test":"test_gap_gc_uint8_buffer_callback_rooting","arm":"shipped_default","result":"PASS","cycles":1,"evacuated":0,"scavenged":6727,"reclaimed":44836520,"evidence":"cycles=1 evacuated=0 scavenged=6727 reclaimed=44836520"},{"test":"test_gap_gc_fetch_method_value_cache_rooting","arm":"default","result":"PASS","cycles":2,"evacuated":0,"scavenged":12810,"reclaimed":18741800,"evidence":"cycles=2 evacuated=0 scavenged=12810 reclaimed=18741800"},{"test":"test_gap_gc_fetch_method_value_cache_rooting","arm":"safepoint_minor","result":"PASS","cycles":2,"evacuated":0,"scavenged":12810,"reclaimed":18741800,"evidence":"cycles=2 evacuated=0 scavenged=12810 reclaimed=18741800"},{"test":"test_gap_gc_fetch_method_value_cache_rooting","arm":"evac_minor","result":"PASS","cycles":2,"evacuated":0,"scavenged":12810,"reclaimed":18741800,"evidence":"cycles=2 evacuated=0 scavenged=12810 reclaimed=18741800"},{"test":"test_gap_gc_fetch_method_value_cache_rooting","arm":"verify_evac","result":"PASS","cycles":2,"evacuated":0,"scavenged":12810,"reclaimed":18741800,"evidence":"cycles=2 evacuated=0 scavenged=12810 reclaimed=18741800"},{"test":"test_gap_gc_fetch_method_value_cache_rooting","arm":"force_verify","result":"PASS","cycles":2,"evacuated":0,"scavenged":12810,"reclaimed":18741800,"evidence":"cycles=2 evacuated=0 scavenged=12810 reclaimed=18741800"},{"test":"test_gap_gc_fetch_method_value_cache_rooting","arm":"cons_scan_off","result":"PASS","cycles":2,"evacuated":0,"scavenged":12810,"reclaimed":18741800,"evidence":"cycles=2 evacuated=0 scavenged=12810 reclaimed=18741800"},{"test":"test_gap_gc_fetch_method_value_cache_rooting","arm":"shipped_default","result":"PASS","cycles":0,"evacuated":0,"scavenged":0,"reclaimed":0,"evidence":"cycles=0 evacuated=0 scavenged=0 reclaimed=0"},{"test":"test_gap_gc_request_signal_rooting","arm":"default","result":"PASS","cycles":3,"evacuated":0,"scavenged":13036,"reclaimed":44929600,"evidence":"cycles=3 evacuated=0 scavenged=13036 reclaimed=44929600"},{"test":"test_gap_gc_request_signal_rooting","arm":"safepoint_minor","result":"PASS","cycles":3,"evacuated":0,"scavenged":13036,"reclaimed":44929600,"evidence":"cycles=3 evacuated=0 scavenged=13036 reclaimed=44929600"},{"test":"test_gap_gc_request_signal_rooting","arm":"evac_minor","result":"PASS","cycles":3,"evacuated":0,"scavenged":13036,"reclaimed":44929600,"evidence":"cycles=3 evacuated=0 scavenged=13036 reclaimed=44929600"},{"test":"test_gap_gc_request_signal_rooting","arm":"verify_evac","result":"PASS","cycles":3,"evacuated":0,"scavenged":13036,"reclaimed":44929600,"evidence":"cycles=3 evacuated=0 scavenged=13036 reclaimed=44929600"},{"test":"test_gap_gc_request_signal_rooting","arm":"force_verify","result":"PASS","cycles":3,"evacuated":0,"scavenged":13036,"reclaimed":44929600,"evidence":"cycles=3 evacuated=0 scavenged=13036 reclaimed=44929600"},{"test":"test_gap_gc_request_signal_rooting","arm":"cons_scan_off","result":"PASS","cycles":3,"evacuated":0,"scavenged":13036,"reclaimed":44929600,"evidence":"cycles=3 evacuated=0 scavenged=13036 reclaimed=44929600"},{"test":"test_gap_gc_request_signal_rooting","arm":"shipped_default","result":"PASS","cycles":0,"evacuated":0,"scavenged":0,"reclaimed":0,"evidence":"cycles=0 evacuated=0 scavenged=0 reclaimed=0"},{"test":"test_gap_gc_net_once_flags_rekey","arm":"default","result":"PASS","cycles":3,"evacuated":0,"scavenged":12512,"reclaimed":18887544,"evidence":"cycles=3 evacuated=0 scavenged=12512 reclaimed=18887544"},{"test":"test_gap_gc_net_once_flags_rekey","arm":"safepoint_minor","result":"PASS","cycles":3,"evacuated":0,"scavenged":12512,"reclaimed":18887544,"evidence":"cycles=3 evacuated=0 scavenged=12512 reclaimed=18887544"},{"test":"test_gap_gc_net_once_flags_rekey","arm":"evac_minor","result":"PASS","cycles":2,"evacuated":0,"scavenged":343,"reclaimed":15739080,"evidence":"cycles=2 evacuated=0 scavenged=343 reclaimed=15739080"},{"test":"test_gap_gc_net_once_flags_rekey","arm":"verify_evac","result":"PASS","cycles":3,"evacuated":0,"scavenged":12512,"reclaimed":18887544,"evidence":"cycles=3 evacuated=0 scavenged=12512 reclaimed=18887544"},{"test":"test_gap_gc_net_once_flags_rekey","arm":"force_verify","result":"PASS","cycles":4,"evacuated":0,"scavenged":13159,"reclaimed":33340480,"evidence":"cycles=4 evacuated=0 scavenged=13159 reclaimed=33340480"},{"test":"test_gap_gc_net_once_flags_rekey","arm":"cons_scan_off","result":"PASS","cycles":3,"evacuated":0,"scavenged":12512,"reclaimed":18887544,"evidence":"cycles=3 evacuated=0 scavenged=12512 reclaimed=18887544"},{"test":"test_gap_gc_net_once_flags_rekey","arm":"shipped_default","result":"PASS","cycles":0,"evacuated":0,"scavenged":0,"reclaimed":0,"evidence":"cycles=0 evacuated=0 scavenged=0 reclaimed=0"},{"test":"test_gap_gc_http2_pending_event_callback_rooting","arm":"default","result":"FAIL","cycles":0,"evacuated":0,"scavenged":0,"reclaimed":0,"evidence":"output-mismatch cycles=0 evacuated=0 scavenged=0 reclaimed=0"},{"test":"test_gap_gc_http2_pending_event_callback_rooting","arm":"safepoint_minor","result":"FAIL","cycles":0,"evacuated":0,"scavenged":0,"reclaimed":0,"evidence":"output-mismatch cycles=0 evacuated=0 scavenged=0 reclaimed=0"},{"test":"test_gap_gc_http2_pending_event_callback_rooting","arm":"evac_minor","result":"FAIL","cycles":1,"evacuated":0,"scavenged":0,"reclaimed":78688,"evidence":"output-mismatch cycles=1 evacuated=0 scavenged=0 reclaimed=78688"},{"test":"test_gap_gc_http2_pending_event_callback_rooting","arm":"verify_evac","result":"FAIL","cycles":0,"evacuated":0,"scavenged":0,"reclaimed":0,"evidence":"output-mismatch cycles=0 evacuated=0 scavenged=0 reclaimed=0"},{"test":"test_gap_gc_http2_pending_event_callback_rooting","arm":"force_verify","result":"FAIL","cycles":1,"evacuated":0,"scavenged":6481,"reclaimed":196720,"evidence":"output-mismatch cycles=1 evacuated=0 scavenged=6481 reclaimed=196720"},{"test":"test_gap_gc_http2_pending_event_callback_rooting","arm":"cons_scan_off","result":"FAIL","cycles":0,"evacuated":0,"scavenged":0,"reclaimed":0,"evidence":"output-mismatch cycles=0 evacuated=0 scavenged=0 reclaimed=0"},{"test":"test_gap_gc_http2_pending_event_callback_rooting","arm":"shipped_default","result":"FAIL","cycles":0,"evacuated":0,"scavenged":0,"reclaimed":0,"evidence":"output-mismatch cycles=0 evacuated=0 scavenged=0 reclaimed=0"},{"test":"test_gap_gc_interp_global_this_rooting","arm":"default","result":"UNVER","cycles":0,"evacuated":0,"scavenged":0,"reclaimed":0,"evidence":"cycles=0 evacuated=0 scavenged=0 reclaimed=0"},{"test":"test_gap_gc_interp_global_this_rooting","arm":"safepoint_minor","result":"UNVER","cycles":0,"evacuated":0,"scavenged":0,"reclaimed":0,"evidence":"cycles=0 evacuated=0 scavenged=0 reclaimed=0"},{"test":"test_gap_gc_interp_global_this_rooting","arm":"evac_minor","result":"UNVER","cycles":1,"evacuated":0,"scavenged":0,"reclaimed":76208,"evidence":"cycles=1 evacuated=0 scavenged=0 reclaimed=76208"},{"test":"test_gap_gc_interp_global_this_rooting","arm":"verify_evac","result":"UNVER","cycles":0,"evacuated":0,"scavenged":0,"reclaimed":0,"evidence":"cycles=0 evacuated=0 scavenged=0 reclaimed=0"},{"test":"test_gap_gc_interp_global_this_rooting","arm":"force_verify","result":"PASS","cycles":1,"evacuated":0,"scavenged":6421,"reclaimed":190520,"evidence":"cycles=1 evacuated=0 scavenged=6421 reclaimed=190520"},{"test":"test_gap_gc_interp_global_this_rooting","arm":"cons_scan_off","result":"UNVER","cycles":0,"evacuated":0,"scavenged":0,"reclaimed":0,"evidence":"cycles=0 evacuated=0 scavenged=0 reclaimed=0"},{"test":"test_gap_gc_interp_global_this_rooting","arm":"shipped_default","result":"PASS","cycles":0,"evacuated":0,"scavenged":0,"reclaimed":0,"evidence":"cycles=0 evacuated=0 scavenged=0 reclaimed=0"},{"test":"test_gap_gc_string_copy_source_rooting","arm":"default","result":"PASS","cycles":1,"evacuated":0,"scavenged":7,"reclaimed":5247240,"evidence":"cycles=1 evacuated=0 scavenged=7 reclaimed=5247240"},{"test":"test_gap_gc_string_copy_source_rooting","arm":"safepoint_minor","result":"PASS","cycles":1,"evacuated":0,"scavenged":7,"reclaimed":5247240,"evidence":"cycles=1 evacuated=0 scavenged=7 reclaimed=5247240"},{"test":"test_gap_gc_string_copy_source_rooting","arm":"evac_minor","result":"PASS","cycles":1,"evacuated":0,"scavenged":7,"reclaimed":5247240,"evidence":"cycles=1 evacuated=0 scavenged=7 reclaimed=5247240"},{"test":"test_gap_gc_string_copy_source_rooting","arm":"verify_evac","result":"PASS","cycles":1,"evacuated":0,"scavenged":7,"reclaimed":5247240,"evidence":"cycles=1 evacuated=0 scavenged=7 reclaimed=5247240"},{"test":"test_gap_gc_string_copy_source_rooting","arm":"force_verify","result":"PASS","cycles":1,"evacuated":0,"scavenged":7,"reclaimed":5247240,"evidence":"cycles=1 evacuated=0 scavenged=7 reclaimed=5247240"},{"test":"test_gap_gc_string_copy_source_rooting","arm":"cons_scan_off","result":"PASS","cycles":1,"evacuated":0,"scavenged":7,"reclaimed":5247240,"evidence":"cycles=1 evacuated=0 scavenged=7 reclaimed=5247240"},{"test":"test_gap_gc_string_copy_source_rooting","arm":"shipped_default","result":"PASS","cycles":0,"evacuated":0,"scavenged":0,"reclaimed":0,"evidence":"cycles=0 evacuated=0 scavenged=0 reclaimed=0"},{"test":"test_gap_gc_string_repeat_reentrant_count","arm":"default","result":"UNVER","cycles":0,"evacuated":0,"scavenged":0,"reclaimed":0,"evidence":"cycles=0 evacuated=0 scavenged=0 reclaimed=0"},{"test":"test_gap_gc_string_repeat_reentrant_count","arm":"safepoint_minor","result":"UNVER","cycles":0,"evacuated":0,"scavenged":0,"reclaimed":0,"evidence":"cycles=0 evacuated=0 scavenged=0 reclaimed=0"},{"test":"test_gap_gc_string_repeat_reentrant_count","arm":"evac_minor","result":"UNVER","cycles":1,"evacuated":0,"scavenged":0,"reclaimed":91056,"evidence":"cycles=1 evacuated=0 scavenged=0 reclaimed=91056"},{"test":"test_gap_gc_string_repeat_reentrant_count","arm":"verify_evac","result":"UNVER","cycles":0,"evacuated":0,"scavenged":0,"reclaimed":0,"evidence":"cycles=0 evacuated=0 scavenged=0 reclaimed=0"},{"test":"test_gap_gc_string_repeat_reentrant_count","arm":"force_verify","result":"PASS","cycles":1,"evacuated":0,"scavenged":6460,"reclaimed":227640,"evidence":"cycles=1 evacuated=0 scavenged=6460 reclaimed=227640"},{"test":"test_gap_gc_string_repeat_reentrant_count","arm":"cons_scan_off","result":"UNVER","cycles":0,"evacuated":0,"scavenged":0,"reclaimed":0,"evidence":"cycles=0 evacuated=0 scavenged=0 reclaimed=0"},{"test":"test_gap_gc_string_repeat_reentrant_count","arm":"shipped_default","result":"PASS","cycles":0,"evacuated":0,"scavenged":0,"reclaimed":0,"evidence":"cycles=0 evacuated=0 scavenged=0 reclaimed=0"},{"test":"test_gap_gc_coalesce_local_root","arm":"default","result":"PASS","cycles":12,"evacuated":0,"scavenged":7317,"reclaimed":143808576,"evidence":"cycles=12 evacuated=0 scavenged=7317 reclaimed=143808576"},{"test":"test_gap_gc_coalesce_local_root","arm":"safepoint_minor","result":"PASS","cycles":12,"evacuated":0,"scavenged":7317,"reclaimed":143808576,"evidence":"cycles=12 evacuated=0 scavenged=7317 reclaimed=143808576"},{"test":"test_gap_gc_coalesce_local_root","arm":"evac_minor","result":"PASS","cycles":14,"evacuated":0,"scavenged":12223,"reclaimed":190579576,"evidence":"cycles=14 evacuated=0 scavenged=12223 reclaimed=190579576"},{"test":"test_gap_gc_coalesce_local_root","arm":"verify_evac","result":"PASS","cycles":12,"evacuated":0,"scavenged":40915,"reclaimed":139223776,"evidence":"cycles=12 evacuated=0 scavenged=40915 reclaimed=139223776"},{"test":"test_gap_gc_coalesce_local_root","arm":"force_verify","result":"PASS","cycles":14,"evacuated":0,"scavenged":30779,"reclaimed":194101648,"evidence":"cycles=14 evacuated=0 scavenged=30779 reclaimed=194101648"},{"test":"test_gap_gc_coalesce_local_root","arm":"cons_scan_off","result":"PASS","cycles":14,"evacuated":0,"scavenged":12223,"reclaimed":190579576,"evidence":"cycles=14 evacuated=0 scavenged=12223 reclaimed=190579576"},{"test":"test_gap_gc_coalesce_local_root","arm":"shipped_default","result":"PASS","cycles":1,"evacuated":0,"scavenged":7072,"reclaimed":60506600,"evidence":"cycles=1 evacuated=0 scavenged=7072 reclaimed=60506600"},{"test":"test_gap_gc_for_in_proxy_callback_roots","arm":"default","result":"UNVER","cycles":0,"evacuated":0,"scavenged":0,"reclaimed":0,"evidence":"cycles=0 evacuated=0 scavenged=0 reclaimed=0"},{"test":"test_gap_gc_for_in_proxy_callback_roots","arm":"safepoint_minor","result":"UNVER","cycles":0,"evacuated":0,"scavenged":0,"reclaimed":0,"evidence":"cycles=0 evacuated=0 scavenged=0 reclaimed=0"},{"test":"test_gap_gc_for_in_proxy_callback_roots","arm":"evac_minor","result":"UNVER","cycles":1,"evacuated":0,"scavenged":0,"reclaimed":201968,"evidence":"cycles=1 evacuated=0 scavenged=0 reclaimed=201968"},{"test":"test_gap_gc_for_in_proxy_callback_roots","arm":"verify_evac","result":"UNVER","cycles":0,"evacuated":0,"scavenged":0,"reclaimed":0,"evidence":"cycles=0 evacuated=0 scavenged=0 reclaimed=0"},{"test":"test_gap_gc_for_in_proxy_callback_roots","arm":"force_verify","result":"PASS","cycles":1,"evacuated":0,"scavenged":6526,"reclaimed":504920,"evidence":"cycles=1 evacuated=0 scavenged=6526 reclaimed=504920"},{"test":"test_gap_gc_for_in_proxy_callback_roots","arm":"cons_scan_off","result":"UNVER","cycles":0,"evacuated":0,"scavenged":0,"reclaimed":0,"evidence":"cycles=0 evacuated=0 scavenged=0 reclaimed=0"},{"test":"test_gap_gc_for_in_proxy_callback_roots","arm":"shipped_default","result":"PASS","cycles":0,"evacuated":0,"scavenged":0,"reclaimed":0,"evidence":"cycles=0 evacuated=0 scavenged=0 reclaimed=0"},{"test":"test_gap_gc_string_suffix_cursor","arm":"default","result":"UNVER","cycles":0,"evacuated":0,"scavenged":0,"reclaimed":0,"evidence":"cycles=0 evacuated=0 scavenged=0 reclaimed=0"},{"test":"test_gap_gc_string_suffix_cursor","arm":"safepoint_minor","result":"UNVER","cycles":0,"evacuated":0,"scavenged":0,"reclaimed":0,"evidence":"cycles=0 evacuated=0 scavenged=0 reclaimed=0"},{"test":"test_gap_gc_string_suffix_cursor","arm":"evac_minor","result":"PASS","cycles":1,"evacuated":0,"scavenged":41,"reclaimed":194880,"evidence":"cycles=1 evacuated=0 scavenged=41 reclaimed=194880"},{"test":"test_gap_gc_string_suffix_cursor","arm":"verify_evac","result":"UNVER","cycles":0,"evacuated":0,"scavenged":0,"reclaimed":0,"evidence":"cycles=0 evacuated=0 scavenged=0 reclaimed=0"},{"test":"test_gap_gc_string_suffix_cursor","arm":"force_verify","result":"PASS","cycles":1,"evacuated":0,"scavenged":41,"reclaimed":194880,"evidence":"cycles=1 evacuated=0 scavenged=41 reclaimed=194880"},{"test":"test_gap_gc_string_suffix_cursor","arm":"cons_scan_off","result":"UNVER","cycles":0,"evacuated":0,"scavenged":0,"reclaimed":0,"evidence":"cycles=0 evacuated=0 scavenged=0 reclaimed=0"},{"test":"test_gap_gc_string_suffix_cursor","arm":"shipped_default","result":"PASS","cycles":0,"evacuated":0,"scavenged":0,"reclaimed":0,"evidence":"cycles=0 evacuated=0 scavenged=0 reclaimed=0"},{"test":"test_gap_gc_array_shift_queue","arm":"default","result":"UNVER","cycles":13,"evacuated":0,"scavenged":0,"reclaimed":16346424,"evidence":"cycles=13 evacuated=0 scavenged=0 reclaimed=16346424"},{"test":"test_gap_gc_array_shift_queue","arm":"safepoint_minor","result":"UNVER","cycles":13,"evacuated":0,"scavenged":0,"reclaimed":16346424,"evidence":"cycles=13 evacuated=0 scavenged=0 reclaimed=16346424"},{"test":"test_gap_gc_array_shift_queue","arm":"evac_minor","result":"UNVER","cycles":13,"evacuated":0,"scavenged":0,"reclaimed":16346424,"evidence":"cycles=13 evacuated=0 scavenged=0 reclaimed=16346424"},{"test":"test_gap_gc_array_shift_queue","arm":"verify_evac","result":"PASS","cycles":13,"evacuated":0,"scavenged":12498,"reclaimed":18709392,"evidence":"cycles=13 evacuated=0 scavenged=12498 reclaimed=18709392"},{"test":"test_gap_gc_array_shift_queue","arm":"force_verify","result":"PASS","cycles":25,"evacuated":0,"scavenged":48861,"reclaimed":6047056,"evidence":"cycles=25 evacuated=0 scavenged=48861 reclaimed=6047056"},{"test":"test_gap_gc_array_shift_queue","arm":"cons_scan_off","result":"UNVER","cycles":13,"evacuated":0,"scavenged":0,"reclaimed":16346424,"evidence":"cycles=13 evacuated=0 scavenged=0 reclaimed=16346424"},{"test":"test_gap_gc_array_shift_queue","arm":"shipped_default","result":"PASS","cycles":12,"evacuated":0,"scavenged":0,"reclaimed":30117376,"evidence":"cycles=12 evacuated=0 scavenged=0 reclaimed=30117376"}],"summary":{"pass":428,"unverified":167,"xfail":0,"fail":7}} diff --git a/benchmarks/runtime_handle_scopes/evidence/fix-hoist.folded.gz b/benchmarks/runtime_handle_scopes/evidence/fix-hoist.folded.gz new file mode 100644 index 0000000000..b22815de60 Binary files /dev/null and b/benchmarks/runtime_handle_scopes/evidence/fix-hoist.folded.gz differ diff --git a/benchmarks/runtime_handle_scopes/evidence/fix-hoist.stat b/benchmarks/runtime_handle_scopes/evidence/fix-hoist.stat new file mode 100644 index 0000000000..669cc270fe --- /dev/null +++ b/benchmarks/runtime_handle_scopes/evidence/fix-hoist.stat @@ -0,0 +1,3 @@ +# started on Mon Sep 14 04:00:54 2026 + +13003278589;;instructions:u;0.00%;812668797;100.00;; diff --git a/benchmarks/runtime_handle_scopes/evidence/fix-json.folded.gz b/benchmarks/runtime_handle_scopes/evidence/fix-json.folded.gz new file mode 100644 index 0000000000..7354d82137 Binary files /dev/null and b/benchmarks/runtime_handle_scopes/evidence/fix-json.folded.gz differ diff --git a/benchmarks/runtime_handle_scopes/evidence/fix-json.stat b/benchmarks/runtime_handle_scopes/evidence/fix-json.stat new file mode 100644 index 0000000000..bc417da622 --- /dev/null +++ b/benchmarks/runtime_handle_scopes/evidence/fix-json.stat @@ -0,0 +1,3 @@ +# started on Mon Sep 14 04:00:03 2026 + +3132631719;;instructions:u;0.00%;187775736;100.00;; diff --git a/benchmarks/runtime_handle_scopes/evidence/fix-lint-full.log b/benchmarks/runtime_handle_scopes/evidence/fix-lint-full.log new file mode 100644 index 0000000000..ea53e935a3 --- /dev/null +++ b/benchmarks/runtime_handle_scopes/evidence/fix-lint-full.log @@ -0,0 +1,104 @@ +run_lint_gates: 79 gate commands derived from 45 lint run steps + + ok [Changeset gate self-test] ./scripts/check_changeset_fragment.sh --self-test + skip [Require a changelog.d/ fragment for crates/ changes] ./scripts/check_changeset_fragment.sh "${{ github.repository }}" "${{ github.event.pull_request.number }}" + (needs GitHub API repository/PR context) + ok [Validate benchmark artifact and fallback gates] python3 -m unittest discover -s tests -p 'test_benchmark_*.py' -v + ok [Validate benchmark artifact and fallback gates] ./tests/test_benchmark_peer_fallback.sh + ok [Validate benchmark artifact and fallback gates] ./tests/test_benchmark_output_verifier.sh + ok [Validate benchmark artifact and fallback gates] bash -n benchmarks/compare.sh benchmarks/quick.sh benchmarks/suite/run_benchmarks.sh benchmarks/honest_bench/run.sh benchmarks/honest_bench/harness/run_http_bench.sh tests/test_benchmark_peer_fallback.sh + ok [Validate Windows LLVM runtime npm staging] ./tests/test_stage_npm_windows_llvm.sh + ok [Release pipeline failure-path regressions] python3 tests/test_release_pipeline.py + ok [Release pipeline failure-path regressions] node --test scripts/publish/platform-visibility.test.mjs + ok [Install Rust toolchain] rustup toolchain install nightly-2026-08-20 --profile minimal --component rustfmt --component clippy + ok [Check formatting] cargo fmt --all -- --check + ok [CI plan policy self-test + docs table freshness] python3 scripts/ci_plan.py --self-test + ok [CI plan policy self-test + docs table freshness] python3 scripts/ci_pr_files.py --self-test + ok [CI plan policy self-test + docs table freshness] python3 scripts/ci_cargo_test_shard.py --self-test + skip [CI plan policy self-test + docs table freshness] python3 scripts/ci_cargo_test_shard.py --package perry --total-shards "${{ fromJSON(needs.plan.outputs.plan).cargo_test_perry.total }}" --validate + (needs the CI plan's shard count) + ok [Compiler-output gate liveness] python3 scripts/compiler_output_step_liveness.py --self-test + ok [Compiler-output gate liveness] python3 scripts/compiler_output_step_liveness.py + ok [Audit workspace architecture] python3 scripts/workspace_architecture.py --self-test + ok [Audit workspace architecture] python3 scripts/workspace_architecture.py --check --print-summary + ok [Linux incident collector fixtures] python3 scripts/test_capture_linux_incident.py + ok [Public benchmark evidence freshness] PYTHONPATH=. python3 tests/test_public_baseline.py + FAIL [Public benchmark evidence freshness] python3 benchmarks/ci_public_baseline_check.py + public baseline error: public artifact benchmark inputs changed; regenerate it with ./benchmarks/run_public_baseline.sh + ok [File size limit] ./scripts/check_file_size.sh + ok [Local binding type-proof audit] python3 scripts/local_binding_type_audit.py --self-test + ok [Local binding type-proof audit] python3 scripts/local_binding_type_audit.py + ok [Binding upstream pins (lock-step)] node scripts/binding_pins.mjs --check + ok [Bundled binding governance] python3 scripts/binding_governance.py --check + ok [GC store-site inventory] python3 scripts/gc_store_site_inventory.py --self-test + ok [GC store-site inventory] python3 scripts/gc_store_site_inventory.py + ok [Address-classification audit] python3 scripts/addr_class_inventory.py --self-test + ok [Address-classification audit] python3 scripts/addr_class_inventory.py + ok [String payload-access inventory] python3 scripts/string_payload_access_inventory.py --self-test + ok [String payload-access inventory] python3 scripts/string_payload_access_inventory.py + ok [Class-id collision audit] python3 scripts/class_id_collisions.py + ok [Object-header shape-descriptor census] python3 scripts/shape_descriptor_census.py + ok [GC pin-site custody audit] python3 scripts/gc_pin_sites.py --self-test + ok [GC pin-site custody audit] python3 scripts/gc_pin_sites.py + ok [Cross-thread promise provenance (#9552)] python3 scripts/check_cross_thread_promise_provenance.py --self-test + ok [Cross-thread promise provenance (#9552)] python3 scripts/check_cross_thread_promise_provenance.py + ok [Runtime GC-pointer holder custody audit] python3 scripts/gc_runtime_root_holders.py --self-test + ok [Runtime GC-pointer holder custody audit] python3 scripts/gc_runtime_root_holders.py + ok [Poll-capable reach audit (GC root dominance)] python3 scripts/gc_root_dominance_check.py --self-test + ok [Poll-capable reach audit (GC root dominance)] python3 scripts/gc_root_dominance_check.py --audit-poll-reach + ok [Thread-local GC scanner registration audit] python3 scripts/check_gc_scanner_latches.py --self-test + ok [Thread-local GC scanner registration audit] python3 scripts/check_gc_scanner_latches.py + ok [Rekeyed side-table custody audit] python3 scripts/gc_rekeyed_key_tables.py --self-test + ok [Rekeyed side-table custody audit] python3 scripts/gc_rekeyed_key_tables.py + ok [GC environment-knob drift audit] python3 scripts/check_gc_env_knobs.py --self-test + ok [GC environment-knob drift audit] python3 scripts/check_gc_env_knobs.py + ok [llvm-inprocess corpus currency] python3 scripts/check_llvm_corpus_currency.py --self-test + ok [llvm-inprocess corpus currency] python3 scripts/check_llvm_corpus_currency.py + ok [Statepoint pass source reader] python3 scripts/read_statepoint_rewrite_passes.py --self-test + ok [Statepoint pass source reader] python3 scripts/read_statepoint_rewrite_passes.py --check + ok [GC documentation claim audit] python3 scripts/check_gc_doc_claims.py --self-test + ok [GC documentation claim audit] python3 scripts/check_gc_doc_claims.py + ok [Windows-portable text I/O in the Windows-CI audits] python3 scripts/check_locale_independent_io.py --self-test + ok [Windows-portable text I/O in the Windows-CI audits] python3 scripts/check_locale_independent_io.py + ok [GC evacuation-liveness assert can say no] python3 scripts/gc_evacuation_liveness_assert.py --self-test + ok [Node version consistency] python3 scripts/check_node_version_consistency.py --self-test + ok [Node version consistency] python3 scripts/check_node_version_consistency.py + ok [Raw-handle debt ratchet] python3 scripts/raw_handle_debt.py --self-test + ok [Raw-handle debt ratchet] python3 scripts/raw_handle_debt.py + ok [Unrooted-local shape ratchet] python3 scripts/unrooted_local_shape.py --self-test + ok [Unrooted-local shape ratchet] python3 scripts/unrooted_local_shape.py --check + ok [Unrooted-local shape ratchet vs. merge base] python3 scripts/unrooted_local_shape.py --no-raise-vs "$BASE_SHA" + ok [Raw-handle debt ratchet vs. merge base] python3 scripts/raw_handle_debt.py --no-raise-vs "$BASE_SHA" + ok [Gap snapshot checker self-test] python3 scripts/gap_snapshot.py --self-test + ok [Platform-aware parity allowlist self-test] python3 scripts/parity_known_failures.py --self-test + ok [Parity allowlist ratchet (provenance + stale entries)] python3 scripts/parity_known_failures.py --audit + ok [Moving-GC gate wiring] python3 scripts/gc_gate_wiring_check.py --self-test + ok [Moving-GC gate wiring] python3 scripts/gc_gate_wiring_check.py + ok [GC matrix liveness gate] ./scripts/gc_repsel_matrix.sh --self-test-liveness-parser + ok [GC matrix liveness gate] python3 scripts/gc_repsel_matrix_merge.py --self-test + ok [GC matrix liveness gate] python3 scripts/gc_matrix_liveness_check.py --self-test + ok [GC matrix liveness gate] python3 scripts/gc_matrix_liveness_check.py --check-registry + ok [Test registration (dark tests)] python3 scripts/check_test_registration.py --self-test + ok [Test registration (dark tests)] python3 scripts/check_test_registration.py + ok [Per-test global sinks] python3 scripts/global_sink_isolation.py --self-test + ok [Per-test global sinks] python3 scripts/global_sink_isolation.py + +run_lint_gates: 6 compile commands derived from warnings + check (18 exclude args) + ok [warnings: rustc warnings (product)] RUSTFLAGS="-D warnings" cargo check -p perry --bins + FAIL [warnings: rustc warnings (host-compatible, all targets)] RUSTFLAGS="-D warnings" cargo check --workspace --all-targets --exclude perry-ui-android --exclude perry-ui-gtk4 --exclude perry-ui-ios --exclude perry-ui-macos --exclude perry-ui-tvos --exclude perry-ui-visionos --exclude perry-ui-watchos --exclude perry-ui-windows --exclude perry-ui-windows-winui + Checking bit-vec v0.8.0 + Checking quick-error v1.2.3 + Checking rusty-fork v0.3.1 + Checking bit-set v0.8.0 + error: could not compile `perry-runtime` (lib) due to 2 previous errors + warning: build failed, waiting for other jobs to finish... + ok [check: Clippy (product)] cargo clippy -p perry --bins + ok [check: Clippy (host-compatible)] cargo clippy --workspace --exclude perry-ui-android --exclude perry-ui-gtk4 --exclude perry-ui-ios --exclude perry-ui-macos --exclude perry-ui-tvos --exclude perry-ui-visionos --exclude perry-ui-watchos --exclude perry-ui-windows --exclude perry-ui-windows-winui + ok [check: Regenerate API docs] ./scripts/regen_api_docs.sh + FAIL [check: Check for API docs drift] git diff --quiet -- docs/src/api/reference.md docs/api/perry.d.ts + + +run_lint_gates: 3 of 83 FAILED; 2 CI-only skipped + [Public benchmark evidence freshness] python3 benchmarks/ci_public_baseline_check.py + [warnings: rustc warnings (host-compatible, all targets)] RUSTFLAGS="-D warnings" cargo check --workspace --all-targets --exclude perry-ui-android --exclude perry-ui-gtk4 --exclude perry-ui-ios --exclude perry-ui-macos --exclude perry-ui-tvos --exclude perry-ui-visionos --exclude perry-ui-watchos --exclude perry-ui-windows --exclude perry-ui-windows-winui + [check: Check for API docs drift] git diff --quiet -- docs/src/api/reference.md docs/api/perry.d.ts diff --git a/benchmarks/runtime_handle_scopes/evidence/fix-promises.folded.gz b/benchmarks/runtime_handle_scopes/evidence/fix-promises.folded.gz new file mode 100644 index 0000000000..f44daf93e5 Binary files /dev/null and b/benchmarks/runtime_handle_scopes/evidence/fix-promises.folded.gz differ diff --git a/benchmarks/runtime_handle_scopes/evidence/fix-promises.stat b/benchmarks/runtime_handle_scopes/evidence/fix-promises.stat new file mode 100644 index 0000000000..b7689f9cff --- /dev/null +++ b/benchmarks/runtime_handle_scopes/evidence/fix-promises.stat @@ -0,0 +1,3 @@ +# started on Mon Sep 14 03:59:08 2026 + +1050989735;;instructions:u;0.00%;103935311;100.00;; diff --git a/benchmarks/runtime_handle_scopes/evidence/gc-ratchet-comparison.json b/benchmarks/runtime_handle_scopes/evidence/gc-ratchet-comparison.json new file mode 100644 index 0000000000..1db55ef750 --- /dev/null +++ b/benchmarks/runtime_handle_scopes/evidence/gc-ratchet-comparison.json @@ -0,0 +1,12 @@ +{ + "baseline_sha": "9fda98df68d9fac3c08b2385fae007aa9f5278df", + "platform": "darwin-arm64", + "all_stdout_match_node": true, + "identical_cells": 126, + "excluded_metrics": [ + "peak_rss_bytes", + "rss_bytes", + "wall_ms" + ], + "differing_cells": [] +} diff --git a/benchmarks/runtime_handle_scopes/evidence/gc-ratchet-pinned-failures.txt b/benchmarks/runtime_handle_scopes/evidence/gc-ratchet-pinned-failures.txt new file mode 100644 index 0000000000..3ddb72bceb --- /dev/null +++ b/benchmarks/runtime_handle_scopes/evidence/gc-ratchet-pinned-failures.txt @@ -0,0 +1,31 @@ +Identical failures on clean baseline and changed runtime: + - 01_nursery_churn: heap_used_bytes 212,168 -> 438,744 (+106.79%), allowance 65,536 [increase] + - 01_nursery_churn: copied_objects 4,977 -> 6,690 (+34.42%), allowance 249 [either] + - 01_nursery_churn: copied_bytes 234,536 -> 466,392 (+98.86%), allowance 65,536 [either] + - 02_survivor_promotion: copied_objects 34,550 -> 37,366 (+8.15%), allowance 1,728 [either] + - 02_survivor_promotion: copied_bytes 1,450,992 -> 1,690,264 (+16.49%), allowance 72,550 [either] + - 04_dead_after_deep_stack: copied_objects 3,622 -> 2,666 (-26.39%), allowance 181 [either] + - 05_closure_capture: copied_objects 20,079 -> 14,285 (-28.86%), allowance 1,004 [either] + - 05_closure_capture: copied_bytes 743,384 -> 534,720 (-28.07%), allowance 65,536 [either] + - 05_closure_capture: promoted_objects 3,390 -> 6,154 (+81.53%), allowance 170 [either] + - 05_closure_capture: promoted_bytes 122,120 -> 221,624 (+81.48%), allowance 65,536 [either] + - 06_string_retention: copied_objects 298 -> 270 (-9.40%), allowance 16 [either] + - 07_array_grow_evacuate: heap_used_bytes 1,072,456 -> 1,211,232 (+12.94%), allowance 65,536 [increase] + - 07_array_grow_evacuate: copied_objects 4,991 -> 6,702 (+34.28%), allowance 250 [either] + - 07_array_grow_evacuate: promoted_objects 4,879 -> 6,154 (+26.13%), allowance 244 [either] + - 07_array_grow_evacuate: promoted_bytes 615,696 -> 841,048 (+36.60%), allowance 65,536 [either] + - 09_try_catch_roots: heap_used_bytes 212,168 -> 443,520 (+109.04%), allowance 65,536 [increase] + - 09_try_catch_roots: copied_objects 4,962 -> 6,673 (+34.48%), allowance 248 [either] + - 09_try_catch_roots: copied_bytes 230,352 -> 462,024 (+100.57%), allowance 65,536 [either] + - 09_try_catch_roots: freed_bytes 11,112,472 -> 10,023,568 (-9.80%), allowance 1,048,576 [either] + - 10_store_receiver_across_alloc: heap_used_bytes 220,384 -> 446,776 (+102.73%), allowance 65,536 [increase] + - 10_store_receiver_across_alloc: heap_total_bytes 16,777,216 -> 22,020,096 (+31.25%), allowance 1,048,576 [increase] + - 10_store_receiver_across_alloc: copied_objects 18,660 -> 14,625 (-21.62%), allowance 933 [either] + - 10_store_receiver_across_alloc: promoted_objects 4,722 -> 6,107 (+29.33%), allowance 236 [either] + - 10_store_receiver_across_alloc: promoted_bytes 228,440 -> 446,368 (+95.40%), allowance 65,536 [either] + - 11_collect_at_depth: heap_used_bytes 212,168 -> 444,720 (+109.61%), allowance 65,536 [increase] + - 11_collect_at_depth: copied_objects 4,975 -> 6,686 (+34.39%), allowance 249 [either] + - 11_collect_at_depth: copied_bytes 230,872 -> 462,544 (+100.35%), allowance 65,536 [either] + - 11_collect_at_depth: freed_bytes 10,062,936 -> 8,974,576 (-10.82%), allowance 1,048,576 [either] + - 12_large_live_set: copied_objects 59,405 -> 104,050 (+75.15%), allowance 2,970 [either] + - 12_large_live_set: copied_bytes 2,327,008 -> 4,162,008 (+78.86%), allowance 116,350 [either] diff --git a/benchmarks/runtime_handle_scopes/evidence/http2-baseline-comparison.json b/benchmarks/runtime_handle_scopes/evidence/http2-baseline-comparison.json new file mode 100644 index 0000000000..c31c9365b4 --- /dev/null +++ b/benchmarks/runtime_handle_scopes/evidence/http2-baseline-comparison.json @@ -0,0 +1,20 @@ +{ + "baseline_sha": "9fda98df68d9fac3c08b2385fae007aa9f5278df", + "host": "perrymaster.skelpo.net", + "test": "test_gap_gc_http2_pending_event_callback_rooting", + "baseline_worktree": "/root/runtime-handle-baseline-20260914", + "node_stdout": "settings cb fired\nping cb fired\nclose cb fired\n", + "baseline_stdout": "", + "changed_stdout": "", + "baseline_stderr": "[node:http2] bind 0.0.0.0:443 failed: Address already in use (os error 98)\n", + "changed_stderr": "[node:http2] bind 0.0.0.0:443 failed: Address already in use (os error 98)\n", + "baseline_exit_code": 0, + "changed_exit_code": 0, + "baseline_build_packages": [ + "perry", + "perry-runtime-static", + "perry-stdlib-static", + "perry-ext-net", + "perry-ext-http" + ] +} diff --git a/benchmarks/runtime_handle_scopes/evidence/http2-netns-results.json b/benchmarks/runtime_handle_scopes/evidence/http2-netns-results.json new file mode 100644 index 0000000000..89a6cb0b35 --- /dev/null +++ b/benchmarks/runtime_handle_scopes/evidence/http2-netns-results.json @@ -0,0 +1,14 @@ +[ + { + "kind": "baseline", + "exit_code": 124, + "stdout": "", + "stderr": "" + }, + { + "kind": "fix", + "exit_code": 124, + "stdout": "", + "stderr": "" + } +] diff --git a/benchmarks/runtime_handle_scopes/exec1.ts b/benchmarks/runtime_handle_scopes/exec1.ts new file mode 100644 index 0000000000..c2f866a24e --- /dev/null +++ b/benchmarks/runtime_handle_scopes/exec1.ts @@ -0,0 +1 @@ +const vals: string[] = []; for (let i = 0; i < 1000000; i++) vals.push((i % 2 ? "record_" : "!bad_") + i); let c = 0; const re = /([a-z]+)_([0-9]+)/; for (let i = 0; i < vals.length; i++) { const m = re.exec(vals[i]); if (m !== null) c += m[2].length; } console.log(c); diff --git a/benchmarks/runtime_handle_scopes/fault-results.json b/benchmarks/runtime_handle_scopes/fault-results.json new file mode 100644 index 0000000000..e9b770412d --- /dev/null +++ b/benchmarks/runtime_handle_scopes/fault-results.json @@ -0,0 +1,28 @@ +[ + { + "mutation": "omit-last-root", + "test": "gc::tests::runtime_roots::handle_stack::handle_stack_growth_preserves_every_moving_root_including_last", + "exit_code": 101, + "detected": true + }, + { + "mutation": "skip-throw-truncate", + "test": "gc::tests::runtime_roots::handle_stack::handle_stack_real_throw_restores_live_outer_scope_and_ffi_roots", + "exit_code": 101, + "detected": true + }, + { + "mutation": "skip-marking-barrier", + "test": "gc::tests::runtime_roots::handle_stack::handle_stack_pushes_and_updates_shade_all_kinds_during_marking", + "exit_code": 101, + "detected": true + }, + { + "mutation": "skip-cache-unpublish", + "test": "gc::roots::runtime_handles::tests::handle_storage_teardown_clears_cache_before_late_scope_drop", + "exit_code": 101, + "detected": true, + "evidence": "fault-skip-cache-unpublish-direct.log (same mutated test binary, --nocapture)", + "direct_exit_code": 133 + } +] diff --git a/benchmarks/runtime_handle_scopes/hoist.ts b/benchmarks/runtime_handle_scopes/hoist.ts new file mode 100644 index 0000000000..07c34ce7c5 --- /dev/null +++ b/benchmarks/runtime_handle_scopes/hoist.ts @@ -0,0 +1 @@ +const vals: string[] = []; for (let i = 0; i < 1000000; i++) vals.push((i % 2 ? "record_" : "!bad_") + i); let c = 0; const re = /^[a-z]+_[0-9]+$/; for (let i = 0; i < vals.length; i++) if (re.test(vals[i])) c++; console.log(c); diff --git a/benchmarks/runtime_handle_scopes/instructions.json b/benchmarks/runtime_handle_scopes/instructions.json new file mode 100644 index 0000000000..40031dd171 --- /dev/null +++ b/benchmarks/runtime_handle_scopes/instructions.json @@ -0,0 +1,31 @@ +{ + "base": "9fda98df68d9fac3c08b2385fae007aa9f5278df", + "event": "instructions:u", + "repeats": 3, + "results": [ + { + "workload": "promises", + "baseline_instructions": 1100168994.0, + "fix_instructions": 1050989735.0, + "reduction_percent": 4.470154973300399 + }, + { + "workload": "json", + "baseline_instructions": 3190619152.0, + "fix_instructions": 3132631719.0, + "reduction_percent": 1.8174351195645255 + }, + { + "workload": "hoist", + "baseline_instructions": 13773038016.0, + "fix_instructions": 13003278589.0, + "reduction_percent": 5.5888862435853115 + }, + { + "workload": "exec1", + "baseline_instructions": 31188918430.0, + "fix_instructions": 29854497805.0, + "reduction_percent": 4.278508817145921 + } + ] +} diff --git a/benchmarks/runtime_handle_scopes/json.ts b/benchmarks/runtime_handle_scopes/json.ts new file mode 100644 index 0000000000..cd21e41159 --- /dev/null +++ b/benchmarks/runtime_handle_scopes/json.ts @@ -0,0 +1,10 @@ +const source = '{"name":"a-long-heap-string-payload","count":42,"items":[1,2,3],"child":{"ok":true}}'; +let checksum = 0; +for (let i = 0; i < 100000; i++) { + const value = JSON.parse(source); + value.count = i; + const encoded = JSON.stringify(value); + const decoded = JSON.parse(encoded); + checksum += encoded.length + decoded.count + decoded.items[2]; +} +console.log(checksum); diff --git a/benchmarks/runtime_handle_scopes/measure.py b/benchmarks/runtime_handle_scopes/measure.py new file mode 100644 index 0000000000..a87da17c7c --- /dev/null +++ b/benchmarks/runtime_handle_scopes/measure.py @@ -0,0 +1,74 @@ +"""Instruction counts and folded stacks; run with the archives used by --perry. + +PERRY_RUNTIME_DIR and PERRY_NO_AUTO_OPTIMIZE=1 must identify the matching build. +The four sources include the original #10166 hoist/exec1 probes, unchanged. +""" +import argparse +import collections +import os +from pathlib import Path +import re +import subprocess + + +def main(): + parser = argparse.ArgumentParser(description=__doc__) + parser.add_argument('--perry', required=True) + parser.add_argument('--output', required=True) + parser.add_argument('--node', default='node') + parser.add_argument('--repeats', default=3, type=int) + args = parser.parse_args() + expected_node = 'v' + (Path(__file__).resolve().parents[2] / '.node-version').read_text().strip() + actual_node = subprocess.check_output([args.node, '--version'], text=True).strip() + assert actual_node == expected_node, f'expected Node {expected_node}, found {actual_node}' + output = Path(args.output).resolve() + output.mkdir(parents=True, exist_ok=True) + env = os.environ | {'PERRY_KEEP_SYMBOLS': '1', 'PERRY_NO_AUTO_OPTIMIZE': '1'} + for name in ('promises', 'json', 'hoist', 'exec1'): + source = Path(__file__).with_name(name + '.ts') + binary = output / name + with (output / (name + '.compile.log')).open('w') as log: + subprocess.run([args.perry, 'compile', str(source), '--no-auto-optimize', + '-o', str(binary)], env=env, stdout=log, stderr=log, check=True) + expected = subprocess.check_output([args.node, str(source)]) + actual = subprocess.check_output([str(binary)]) + (output / (name + '.out')).write_bytes(actual) + assert actual == expected, f'{name}: Node output mismatch' + with (output / (name + '.runs.out')).open('w') as log: + subprocess.run(['perf', 'stat', '-x', ';', '-r', str(args.repeats), + '-e', 'instructions:u', '-o', str(output / (name + '.stat')), + str(binary)], stdout=log, check=True) + data = output / (name + '.perf.data') + subprocess.run(['perf', 'record', '-q', '-e', 'instructions:u', '-c', '1000003', + '--call-graph', 'dwarf', '-o', str(data), '--', str(binary)], + stdout=log, check=True) + process = subprocess.Popen(['perf', 'script', '-i', str(data)], + stdout=subprocess.PIPE, text=True) + stacks = collections.Counter() + frames = [] + for line in process.stdout: + if not line.strip(): + if frames: + stacks[';'.join(reversed(frames))] += 1 + frames = [] + elif line[0] in ' \t': + parts = line.strip().split(None, 1) + if len(parts) == 2: + symbol = parts[1].rsplit(' (', 1)[0] + frames.append(re.sub(r'\+0x[0-9a-f]+$', '', symbol)) + if frames: + stacks[';'.join(reversed(frames))] += 1 + assert process.wait() == 0, 'perf script failed' + with (output / (name + '.folded')).open('w') as folded: + for stack, count in stacks.most_common(): + folded.write(f'{count} {stack}\n') + total = sum(stacks.values()) + assert total > 0, 'no instruction samples' + leaf = sum(count for stack, count in stacks.items() + if re.search(r'RuntimeHandle|runtime_handle|RUNTIME_HANDLE', + stack.rsplit(';', 1)[-1])) + print(name, 'samples', total, 'handle-related leaf %', 100 * leaf / total, flush=True) + + +if __name__ == '__main__': + main() diff --git a/benchmarks/runtime_handle_scopes/promises.ts b/benchmarks/runtime_handle_scopes/promises.ts new file mode 100644 index 0000000000..bafee1b950 --- /dev/null +++ b/benchmarks/runtime_handle_scopes/promises.ts @@ -0,0 +1,9 @@ +async function run(): Promise { + let checksum = 0; + for (let i = 0; i < 100000; i++) { + const value = await Promise.resolve(i).then((n: number) => n + 1); + checksum += value; + } + console.log(checksum); +} +run(); diff --git a/changelog.d/10245-prune-unused-reexports.md b/changelog.d/10245-prune-unused-reexports.md index 78b14254e6..b204364c47 100644 --- a/changelog.d/10245-prune-unused-reexports.md +++ b/changelog.d/10245-prune-unused-reexports.md @@ -1 +1 @@ -Perry now postpones unused named and wildcard re-exports during module collection when package sideEffects declarations prove their static dependency trees safe to omit. Barrels consisting of forwarded named imports are normalized to equivalent re-exports with dependency order preserved. Cyclic dependency trees stay intact so pruning cannot change their initialization entry point. Export demand grows to a fixed point across later importers and barrel chains; imports used by module code, dynamic-import namespaces, live bindings, and effectful or uncertain dependencies remain available. The compile summary reports omitted modules. Set PERRY_NO_REEXPORT_PRUNE=1 for an unpruned comparison, or PERRY_COLLECT_ONLY=1 to write the collection audit without generating code. +Perry now postpones unused named and wildcard re-exports during module collection when package sideEffects declarations or conservative analysis of inert initialization prove their static dependency trees safe to omit. Analysis also covers first-party modules and packages without metadata; explicit effectful contracts remain authoritative. Barrels consisting of forwarded named imports are normalized to equivalent re-exports with dependency order preserved. Cyclic dependency trees stay intact so pruning cannot change their initialization entry point. Export demand grows to a fixed point across later importers and barrel chains; imports used by module code, dynamic-import namespaces, live bindings, and effectful or uncertain dependencies remain available. The compile summary reports omitted modules. Set PERRY_NO_REEXPORT_PRUNE=1 for an unpruned comparison, or PERRY_COLLECT_ONLY=1 to write the collection audit without generating code. diff --git a/changelog.d/10248-native-property-get.md b/changelog.d/10248-native-property-get.md new file mode 100644 index 0000000000..93a0099906 --- /dev/null +++ b/changelog.d/10248-native-property-get.md @@ -0,0 +1,10 @@ +Speed up native property Get for ordinary own and prototype data properties. +The shared lookup borrows keys and checks shape and accessor metadata before +returning, while getters, proxies, class statics, and exotics retain their +existing dispatch. JSON.stringify reuses a canonical toJSON key. + +Linux user-instruction counts decreased 71–78% in Reflect.get probes, 12% in +positive thenable assimilation, and 43% in inherited toJSON serialization; the +user-iterator control was unchanged. Differential tests, Node parity, fault +injections, and measurement/validation receipts are recorded in +benchmarks/native_property_get/REPORT.md. diff --git a/changelog.d/10249-json-clone-traversal-evidence.md b/changelog.d/10249-json-clone-traversal-evidence.md new file mode 100644 index 0000000000..b1dd622120 --- /dev/null +++ b/changelog.d/10249-json-clone-traversal-evidence.md @@ -0,0 +1,3 @@ +### perf(json): count a compiled loop's whole-array materialization as traversal evidence + +Traversal feedback (#10150) switches a program's `JSON.parse` calls to eager parsing once its lazy arrays keep being fully traversed, but it only counted element reads in `lazy_get_rooted`. The element-shape loop clone (#10171) materializes a lazy array whole in its preheader through `js_array_refresh_local_head`, so scan loops served by the clone never produced evidence and paid for both the tape and the eager materialization on every parse. `js_array_refresh_local_head` now counts the first materialization of each lazy array as evidence. Measured on the bench mini: `records_array_16k:scan` 149.6 → 109.7 ms, `records_array_1m:scan` 180.4 → 132.6 ms, `records_array_8m:scan` 171.5 → 122.6 ms, other JSON rows unchanged. diff --git a/changelog.d/10250-class-prototype-symbol-props.md b/changelog.d/10250-class-prototype-symbol-props.md new file mode 100644 index 0000000000..e7346b158c --- /dev/null +++ b/changelog.d/10250-class-prototype-symbol-props.md @@ -0,0 +1 @@ +Resolve symbol-keyed properties that were added to a declared class's `.prototype` after the declaration (`C.prototype[S] = f`, `Object.defineProperty(C.prototype, S, ...)`, `Object.assign(C.prototype, { [Symbol.iterator]() {} })`) from instances and subclass instances. The symbol read fallback only walked the dynamic prototype-object table (`Object.create` / class-expression parents) and never the declared prototype object those writes land on, so `typeof instance[Symbol.iterator]` was `undefined` and `Symbol.iterator in instance` was false while the same string-keyed writes worked. drizzle-orm's `applyEffectWrapper` installs effect's `Effectable.Prototype` on its query classes this way, so every `yield* db.run(...)` in OpenCode's database layer failed with "next is not a function" (tracker #10107). diff --git a/changelog.d/10251-registry-address-index.md b/changelog.d/10251-registry-address-index.md new file mode 100644 index 0000000000..d1c6816fb8 --- /dev/null +++ b/changelog.d/10251-registry-address-index.md @@ -0,0 +1,17 @@ +Prepare the address-index fix for #10110: allow each registry to size its Bloom +filter and add `RegistryAddrIndex`, which rebuilds its bits from live addresses +as registrations retire. Readers retain three atomic bit tests with no lock or +allocation. Existing symbol filters retain their 16-word width and hash positions. + +Adapted from [proggeramlug/perry#1](https://github.com/proggeramlug/perry/pull/1). +Coverage includes the reported 644-address population, retirement, 20,000-address +churn, address reuse, and concurrent admissions/refreshes. Hash slices scale with +the chosen width so all three probes can use the entire array. + +The canonical-wrapper subsystem is still absent from `main`, so this is its +prerequisite, not a claim of the campaign's measured CPU improvement on `main`. +When that subsystem lands, use `RegistryAddrIndex<256>` in +`native_handle/canonical.rs`, admit before publishing the identity, and retire +after all three removal paths: stale identity replacement, finalized identity +removal, and explicit retirement (after `finalize_once`). Keep the authoritative +wrapper validation after `may_contain`. Issue #10110 remains open for that wiring. diff --git a/changelog.d/10252-runtime-handle-scopes.md b/changelog.d/10252-runtime-handle-scopes.md new file mode 100644 index 0000000000..5797288600 --- /dev/null +++ b/changelog.d/10252-runtime-handle-scopes.md @@ -0,0 +1,11 @@ +Reduce the per-call cost of Rust runtime GC handles. Scopes cache non-dropping +TLS metadata, root slots use 16 bytes, and idle pushes skip barrier dispatch. +Index handles retain bounds and kind checks across growth, moving collections, +and caught throws. Add fault-tested coverage for relocation, savepoint restore, +incremental marking, FFI indices, and TLS teardown. Measured user instruction +counts fall by 1.8–5.6% across JSON, promises, and the hoisted regex probes. + +The merge train retains scoped Vec access on Android and HarmonyOS, where OS-backed TLS frees +metadata at thread teardown even for values without Drop. Native targets cache +the metadata as above; both paths retain the original four-slot initial allocation +floor. Teardown tolerates an already-destroyed Android hot-cache pool. diff --git a/changelog.d/10253-gc-due-check-fast-path.md b/changelog.d/10253-gc-due-check-fast-path.md new file mode 100644 index 0000000000..8efb471ea1 --- /dev/null +++ b/changelog.d/10253-gc-due-check-fast-path.md @@ -0,0 +1,13 @@ +Made the GC's "is a collection due?" check cheap when nothing is due, without +changing any collection decision. Runtime safepoint polls (regex search quanta, +the microtask pump, the event loop) no longer build a step result whose debt +snapshot nobody read. The young-generation occupancy the nursery cap compares +against is now O(1): the bytes outside Eden's current block are cached per heap +generation, and every move of an arena's current block goes through +`Arena::set_current`, which invalidates the cache. Debug builds check every cached +answer against the block walk. `gc_check_trigger`, reached from every `gc_malloc` +and JSON parse, evaluates the due trigger once instead of up to three times. +Measured at the shipping release profile: 1M hoisted `re.exec` −12.5 % +instructions, 400k small `JSON.parse` −12.3 %, 1M hoisted `re.test` −6.6 %, +with async, allocation, string, BigInt and Map workloads unchanged and peak RSS +unchanged. diff --git a/changelog.d/10254-tiny-parse-nursery-cap.md b/changelog.d/10254-tiny-parse-nursery-cap.md new file mode 100644 index 0000000000..cc543e285e --- /dev/null +++ b/changelog.d/10254-tiny-parse-nursery-cap.md @@ -0,0 +1 @@ +- **perf(gc): a loop of small `JSON.parse` calls no longer grows the young generation to 48 MB before collecting (#10254).** The tiny-parse guard (#9831) forces a collection at a parse boundary only when total arena in-use reaches its 48 MB floor, and nothing else arms the nursery safepoint for a loop that allocates only inside the parser. The parse boundary now also collects when the young generation reaches its scavenge cap. A minor lowers that quantity to its survivors, so the new arm cannot re-fire until the cap has been refilled. On the quiet bench mini, `small_record:parse` peak RSS fell from 80 to 32 MiB (Node 59) and `object_1k:parse` from 65 to 32 MiB (Node 61), with CPU unchanged. No other row of the 50-row JSON matrix moved beyond noise, and every gated gc-ratchet counter is identical to main. diff --git a/changelog.d/10255-element-shape-native-domains.md b/changelog.d/10255-element-shape-native-domains.md new file mode 100644 index 0000000000..303930660e --- /dev/null +++ b/changelog.d/10255-element-shape-native-domains.md @@ -0,0 +1,111 @@ +Keep the element-shape loop clone's accumulator in the double domain and its +counter in the i32 domain (#7480 → #10171 → #10185). The JSON access screen's +four missing cells (`repeat` ×3, 16k `fields`) now beat Node and Bun at the +official settings, and every clone form got cheaper. + +**The defect.** The fast clone was already what executed, but the `repeat` loop +(`sum += rows[7].id`) took 16 instructions at ~13 CPU cycles per iteration: two +loop-carried chains crossed the integer/float register boundary every +iteration. + +* An `any`-typed `sum` lives in a precise GC root slot, and every root reload + passes through the RS4GC launder (`function/precise_roots.rs`, + `ROOT_RELOAD_LAUNDER`) that LLVM cannot see through, so mem2reg promoted the + slot as NaN-box `i64` bits: `x25 → fmov → fadd → fmov → x25`. +* A counter the clone never indexes with (the constant-index and carried forms) + had no i32 slot, so it stayed a double (`fadd d8, #1.0`) compared against + `count` — itself a root slot, reloaded through the launder and moved to an FP + register every iteration although the preheader had already materialized it + as an i32. + +**The change** (`stmt/element_shape_native.rs`). For the fast clone's lowering +the accumulator is redirected into a promotable `alloca double` (the +`numeric_accumulator_f64_slots` redirect the packed clones already use), seeded +with the value the deref block tag-tested as a Number; the counter gets an i32 +slot (its Let-site parallel slot, or a clone-private one seeded with the literal +start) that the `Update` lowering advances alone +(`deferred_integer_update_accumulators`), so the precomputed i32 trip count +turns the condition into `icmp slt i32`. Residual checks branch to a +`element_shape.loop.side_exit` trampoline that publishes both scalars to their +real slots and then enters the slow clone; the fall-through exit publishes them +in `element_shape.loop.fast.write_back`. A canonical-i32 counter (the indexing +forms) has nothing to defer and is left alone. + +Soundness is unchanged by construction: the redirected store sits exactly where +the root-slot store did, which #10185's fold / carried-commit ordering already +put after every side exit of the iteration, so the trampoline publishes the +iteration's entry accumulator and its own index — the state the slow clone +re-runs it from. The carried binding keeps its own end-of-iteration commit and +is never published by the trampoline. The seed, trampoline and write-back are +plain loads/stores plus one `sitofp`, created inside the call-free scan's block +range. The accumulator was already consumed as a raw double inside this clone +(`numeric_accumulator`), so the IEEE `fadd`s are the same operations on the same +operands in the same order (`-0`, NaN, overflow to Infinity bit-identical). The +i32 counter only ever runs against a trip count that is a literal, `arr.length` +or `materialize_loop_i32`'s integral `0..=i32::MAX`; fractional, NaN, negative +and out-of-range bounds still route to the slow clone. + +**The new `repeat` loop** (arm64, `run$spec_b_b`, whole body): + +``` +a90 cbz w9, side_exit ; residual check (header loads hoisted) +a94 ldr d2, [x10, w0, sxtw #3] ; field load +a98 fmov x12, d2 +a9c cmp x12, x11 ; Number tag test +aa0 b.gt side_exit +aa4 fadd d1, d1, d2 ; sum stays in d1 +aa8 add w8, w8, #1 ; i is an i32 +aac cmp w19, w8 ; against the materialized i32 count +ab0 b.ne a90 +``` + +**Per iteration** (`/usr/bin/time -l`, 50M iterations, 16k fixture): + +| mode | instructions main → PR | cycles main → PR | +|---|---:|---:| +| repeat | 16.0 → 9.0 | 13.0 → 3.0 | +| sequential | 25.0 → 22.0 | 13.0 → 3.4 | +| random | 29.0 → 27.0 | 15.6 → 15.6 | +| fields | 51.0 → 47.0 | 19.0 → 9.0 | + +**Access screen, official settings** (1M iterations, warmup 0; bench mini, best +of 9 interleaved rounds, ns/iteration): `repeat` 4.06 → 0.94 (Node 2.89–3.07, +Bun 4.47–4.60); `sequential` 4.06–4.16 → 1.07/1.63/3.31; `random` 4.86–5.45 → +4.80–5.39; `fields` 5.94–6.03 → 2.81/2.82/3.39 (16k Node 5.57). All 12 cells at +or better than the better of Node and Bun (worst ratio 0.70, 16k `random`). + +**Warmed** (50M iterations, 1M warmup, best of 5): 3 of 12 cells win (16k +`sequential`, 1m and 20m `fields`); 9 still miss, each for a structural reason +this change does not address: + +* `repeat` 0.94 vs 0.38/0.52/0.31 — one IEEE add per iteration is 3 cycles of + latency on this core; the JITs' 1.0–1.6 cycles mean an int32 speculated + accumulator, which JS double semantics for `sum` do not license here. +* `fields` 16k 2.81 vs Node 2.77 — three serial IEEE adds = 9 cycles (measured + 9.02), the double-domain floor for the source-order fold. +* `random` 4.86/5.09/5.39 vs 4.63/4.47/4.64 — the loop-carried chain is the + recurrence's division (15.6 cycles, unchanged by this change); the i64 `srem` + #10185 needs for exactness is ~5 % slower than an int32 division (same-host C + microbenchmark: 4.69 vs 4.46 ns), which is the whole 16k gap. The larger 1m/20m + gap is not in the loop code: Perry's 27 instructions are identical across + sizes while its cycles rise 15.6 → 16.3 → 17.3, and Bun gets FASTER than its + own 16k number — a record-memory locality difference, not investigated here. +* `sequential` 1m/20m 1.65/3.38 vs 1.32/2.02 — identical 22 instructions but + 3.4 → 5.2 → 10.9 cycles from 16k to 20m: memory-bound record access, the same + locality difference. + +**JSON matrix** (50 rows, 5 interleaved rounds, best-of, PR vs main): 48 rows +within ±2 % CPU (the one element-shape consumer, `scan`, 0.5–1.1 % faster), no +row's peak RSS higher. Two runtime-only rows moved further, one each way: +`long_string_1m:stringify` −4.4 % and `escaped_1m:stringify` +10.3 % (160.8 → +177.3 ms). Neither runs a clone; the latter's hot function +`json::stringify_flat::emit_piece` is identical runtime code shifted 128 bytes +by the smaller worker module, and appending unused functions to main's own +worker moves the same row to 149.0–149.4 ms — layout, not this change. + +Tests: five IR-census tests in `stmt/element_shape_native_tests.rs` (each fails +with the redirects disabled); the #10185 carried-commit and side-exit helpers +now count the trampoline spelling of an exit. `test_gap_json_record_loop_clone.ts` +gains `-0`/Infinity/NaN accumulation, first-iteration and mid-loop side exits +with a non-zero accumulator, fractional/NaN/negative/string trip counts on the +`repeat` form and a late `random` side exit; byte-identical to Node 26.5.1. diff --git a/crates/perry-codegen/src/stmt/element_shape_fields_random_tests.rs b/crates/perry-codegen/src/stmt/element_shape_fields_random_tests.rs index 5976551ff8..3359c4c4b3 100644 --- a/crates/perry-codegen/src/stmt/element_shape_fields_random_tests.rs +++ b/crates/perry-codegen/src/stmt/element_shape_fields_random_tests.rs @@ -208,10 +208,26 @@ fn fields_body() -> Vec { } /// How many side exits the clone can take per iteration — one branch per -/// residual / tag test that leaves for the slow preheader. +/// residual / tag test that leaves the clone. +/// +/// A side exit targets the slow preheader directly, or — once the clone keeps +/// its accumulator or counter in native storage (`stmt/element_shape_native.rs`) +/// — the write-back trampoline that publishes them and then enters the slow +/// preheader. Both spellings are one exit; counting only the first would make +/// every assertion below read zero for a clone that exits through the second. fn side_exit_count(fast: &str) -> usize { fast.matches("label %element_shape.loop.slow.preheader") .count() + + fast.matches("label %element_shape.loop.side_exit").count() +} + +/// The registers `block` defines with a `sitofp i32 … to double`. +fn sitofp_results(block: &str) -> Vec<&str> { + block + .lines() + .filter(|l| l.contains(" = sitofp i32 ")) + .filter_map(|l| l.trim_start().split(" = ").next()) + .collect() } /// The LAST block in `fast` whose label starts with `prefix`, body only. @@ -311,8 +327,21 @@ fn the_carried_write_back_follows_every_side_exit() { iteration, and every later index is silently wrong. \ final block:\n{committed}\nfull clone:\n{fast}" ); + // The accumulator's store is ALSO a `store double` in this block now that + // it lands in the clone's f64 alloca (`stmt/element_shape_native.rs`), so + // the publication is counted by what it stores: the carried i32 converted + // for its real slot. + let published = sitofp_results(committed); assert_eq!( - committed.matches("store double").count(), + published.len(), + 1, + "the carried value must be converted for publication exactly once per \ + iteration; final block:\n{committed}" + ); + assert_eq!( + committed + .matches(&format!("store double {}, ", published[0])) + .count(), 1, "the carried value must be published exactly once per iteration; \ final block:\n{committed}" @@ -652,3 +681,9 @@ fn a_class_typed_array_declines_the_string_length_read() { "the class-keyed arm reads raw doubles; there is no tag to test" ); } + +/// The accumulator and counter in their native domains +/// (`stmt/element_shape_native.rs`) — a child module so it reuses this file's +/// `random`/`fields` fixtures and exit-counting helpers. +#[path = "element_shape_native_tests.rs"] +mod native_domains; diff --git a/crates/perry-codegen/src/stmt/element_shape_loop.rs b/crates/perry-codegen/src/stmt/element_shape_loop.rs index 47e54c7c46..6f657bb8a9 100644 --- a/crates/perry-codegen/src/stmt/element_shape_loop.rs +++ b/crates/perry-codegen/src/stmt/element_shape_loop.rs @@ -238,6 +238,8 @@ impl MatchedIndex { #[derive(Debug)] struct ElementShapeVersionedLoop { counter_id: u32, + /// The counter's integer-literal start, in `0..=i32::MAX`. + counter_start: i64, bound: ElementShapeLoopBound, array_id: u32, identity: ElementShapeIdentity, @@ -1314,6 +1316,7 @@ fn match_element_shape_versioned_loop( Some(ElementShapeVersionedLoop { counter_id, + counter_start: start, bound, array_id, identity, @@ -1711,6 +1714,18 @@ pub(super) fn lower_element_shape_versioned_for( let scope_id = ctx.next_loop_proof_scope_id(); let fast_scan_start = ctx.func.num_blocks(); ctx.current_block = fast_pre_idx; + // The accumulator as an f64 and the counter as an i32 for the clone's + // duration (`stmt/element_shape_native.rs`). Entered after + // `fast_scan_start`, so its side-exit trampoline is inside the block range + // the call-free scan below covers. + let native = super::element_shape_native::NativeLoopDomains::enter( + ctx, + matched.counter_id, + matched.counter_start, + matched.accumulator_id, + &accumulator, + &slow_pre_label, + ); ctx.element_shape_loop_facts .push(crate::expr::ElementShapeLoopFact { array_local_id: matched.array_id, @@ -1722,7 +1737,7 @@ pub(super) fn lower_element_shape_versioned_for( class_name: report_class, elements_base: guard.elements_base, expected_shape_id: guard.expected_shape_id, - side_exit_label: slow_pre_label.clone(), + side_exit_label: native.side_exit_label().to_string(), statically_layout_proven, fields, synthesized_body: matched.fast_body.is_some(), @@ -1740,6 +1755,7 @@ pub(super) fn lower_element_shape_versioned_for( ); ctx.element_shape_loop_facts .retain(|fact| fact.scope_id != scope_id); + native.finish(ctx, &merge_label); lowered?; if !ctx.block().is_terminated() { ctx.block().br(&merge_label); diff --git a/crates/perry-codegen/src/stmt/element_shape_native.rs b/crates/perry-codegen/src/stmt/element_shape_native.rs new file mode 100644 index 0000000000..a406800e64 --- /dev/null +++ b/crates/perry-codegen/src/stmt/element_shape_native.rs @@ -0,0 +1,246 @@ +//! The element-shape fast clone's loop-carried scalars in their native +//! machine domains: the accumulator as an `f64`, the counter as an `i32`. +//! +//! ## What this removes +//! +//! Before this module the `repeat` clone (`sum += rows[7].id`) spent ~13 CPU +//! cycles per iteration on 16 instructions. Not instruction count: two +//! loop-carried chains crossed the integer/float register boundary every +//! iteration. +//! +//! * **The accumulator.** An `any`-typed `sum` lives in a precise GC root slot, +//! and every reload of a root slot passes through the RS4GC launder +//! (`function/precise_roots.rs`'s `ROOT_RELOAD_LAUNDER`, an `asm` identity +//! LLVM cannot see through). mem2reg therefore promoted the slot as NaN-box +//! `i64` bits, and the chain was `x25 → fmov → fadd → fmov → x25`. +//! * **The counter.** A counter the clone never uses as an index (the +//! constant-index and carried forms) has no i32 slot, so it stayed a double +//! (`fadd d8, d8, #1.0`) compared against `count` — itself a root slot, +//! reloaded through the same launder and re-transferred to an FP register on +//! every iteration, although the preheader had already materialized it as an +//! i32. +//! +//! ## What replaces it +//! +//! Both scalars move into plain promotable allocas for the fast clone's +//! lowering, exactly the redirects the packed clones already use +//! (`FnCtx::numeric_accumulator_f64_slots`, +//! `FnCtx::deferred_integer_update_accumulators`): +//! +//! * the accumulator's live value is an `alloca double` seeded in the fast +//! preheader with the value the deref block just tag-tested as a Number, so +//! every in-clone read and write is a `double` and mem2reg makes it an FP phi; +//! * the counter gets an i32 slot (reused if it already owns a parallel one, +//! clone-private otherwise) that the `Update` lowering advances ALONE, so the +//! precomputed i32 trip count turns the condition into `icmp slt i32`. +//! +//! The real slots are written back at the two places the clone can leave: +//! a side-exit trampoline every residual check branches to instead of the slow +//! preheader, and a block on the fall-through exit. +//! +//! ## Why this changes no observable behaviour +//! +//! * **Commit ordering (#10185) is untouched.** The redirected store happens at +//! exactly the point the root-slot store used to, and the matcher already +//! put that point after every side exit of the iteration (the K-statement +//! fold, the carried commit last). At any side exit the f64 alloca therefore +//! holds the value the iteration was entered with, the i32 counter holds the +//! iteration's own index (the `Update` runs after the body), and the +//! trampoline publishes exactly the state the slow clone must re-run the +//! iteration from. +//! * **Call-freeness is untouched.** The seed, the trampoline and the +//! write-back are plain loads, stores and one `sitofp`; the trampoline is +//! created inside the scanned block range, so the post-emission +//! `contains_gc_unsafe_call` scan still sees it. No poll is emitted in these +//! clones (`emit_gc_loop_safepoint`), so no collection observes the stale +//! root slot, and a stale slot holds a Number, which a scan treats as data. +//! * **JS `+`.** The redirect admits no write the clone did not already lower +//! as a bare `fadd`: the accumulator is the fact's `numeric_accumulator`, +//! which `is_numeric_expr` already trusted as a raw double inside this clone. +//! Keeping that same double in a register instead of a stack slot is the +//! same IEEE arithmetic on the same operands in the same order, so `-0`, NaN +//! and overflow to Infinity are bit-identical. +//! * **The i32 counter.** The trip count is always an i32 in the fast clone — +//! a literal in `0..=i32::MAX`, `arr.length`, or `materialize_loop_i32`'s +//! integral `0..=i32::MAX` — and the start is an integer literal in the same +//! range, so `i < bound <= i32::MAX` before every `add i32 1`: no wrap. A +//! fractional, NaN, negative or out-of-range `count` never reaches the clone. +//! +//! Every exclusion below declines ONE redirect and leaves that scalar exactly +//! as it was, never the clone. + +use crate::expr::FnCtx; +use crate::types::{DOUBLE, I32}; + +/// The scalars one fast clone moved into native storage, and where the clone +/// must publish them back. +pub(super) struct NativeLoopDomains { + /// `(local, f64 alloca, real slot)`. + accumulator: Option<(u32, String, String)>, + /// The counter's deferred double storage. + counter: Option, + /// The label residual checks branch to: the write-back trampoline, or the + /// slow preheader itself when nothing was redirected. + side_exit: String, +} + +struct DeferredCounter { + id: u32, + i32_slot: String, + double_slot: String, + /// The slot was minted for this clone and must be unregistered after it, + /// so the slow clone and the code after the loop see the counter exactly + /// as they did before. + private: bool, +} + +/// Can `id` carry the unboxed accumulator redirect? Mirrors the packed clones' +/// admission (`stable_packed_accumulator::collect_numeric_accumulators`) minus +/// the numeric fixpoint, which the element-shape matcher already discharged. +fn accumulator_is_redirectable(ctx: &FnCtx<'_>, id: u32) -> bool { + ctx.locals.contains_key(&id) + && !ctx.boxed_vars.contains(&id) + && !ctx.closure_captures.contains_key(&id) + && !ctx.module_globals.contains_key(&id) + // An i32 (parallel or canonical) or Str representation has storage the + // redirect would leave stale; a POD or scalar-replaced local is not a + // single slot at all. + && !ctx.i32_counter_slots.contains_key(&id) + && !ctx.local_slot_reps.contains_key(&id) + && !ctx.pod_records.contains_key(&id) + && !ctx.scalar_replaced.contains_key(&id) + // An enclosing clone already redirected it; its scope owns the alloca + // and the write-back, and overwriting the entry would strand both. + && !ctx.numeric_accumulator_f64_slots.contains_key(&id) +} + +impl NativeLoopDomains { + /// Emit the seeds into the current block (the fast preheader) and register + /// both redirects for the fast clone's lowering. + /// + /// `accumulator_value` is the value the deref block tag-tested as a Number; + /// the deref block dominates the fast preheader and nothing is stored in + /// between, so it is the slot's current value. + pub(super) fn enter( + ctx: &mut FnCtx<'_>, + counter_id: u32, + counter_start: i64, + accumulator_id: u32, + accumulator_value: &str, + slow_pre_label: &str, + ) -> Self { + let accumulator = accumulator_is_redirectable(ctx, accumulator_id).then(|| { + let real_slot = ctx.locals[&accumulator_id].clone(); + let alloca = ctx.func.alloca_entry(DOUBLE); + ctx.block().store(DOUBLE, accumulator_value, &alloca); + ctx.numeric_accumulator_f64_slots + .insert(accumulator_id, alloca.clone()); + (accumulator_id, alloca, real_slot) + }); + + let counter = Self::defer_counter(ctx, counter_id, counter_start); + + let side_exit = if accumulator.is_none() && counter.is_none() { + slow_pre_label.to_string() + } else { + let tramp_idx = ctx.new_block("element_shape.loop.side_exit"); + let saved = ctx.current_block; + ctx.current_block = tramp_idx; + emit_write_back(ctx, accumulator.as_ref(), counter.as_ref()); + ctx.block().br(slow_pre_label); + ctx.current_block = saved; + ctx.block_label(tramp_idx) + }; + + Self { + accumulator, + counter, + side_exit, + } + } + + fn defer_counter(ctx: &mut FnCtx<'_>, id: u32, start: i64) -> Option { + // A canonical-i32 counter has no double storage to defer — its `Update` + // is already one `add i32`. + if crate::expr::canonical_local_i32_slot(ctx, id).is_some() + || ctx.boxed_vars.contains(&id) + || ctx.closure_captures.contains_key(&id) + || ctx.module_globals.contains_key(&id) + || ctx.unsigned_i32_locals.contains(&id) + || ctx.deferred_integer_update_accumulators.contains(&id) + || !(0..=i64::from(i32::MAX)).contains(&start) + { + return None; + } + let double_slot = ctx.locals.get(&id)?.clone(); + let (i32_slot, private) = match ctx.i32_counter_slots.get(&id) { + // A Let-site parallel slot: every write mirrors it, so it already + // holds the counter's value. + Some(slot) => (slot.clone(), false), + None => { + let slot = ctx.func.alloca_entry(I32); + // The init was lowered before the clone was chosen and nothing + // wrote the counter since, so its value is the literal start. + ctx.block().store(I32, &start.to_string(), &slot); + ctx.i32_counter_slots.insert(id, slot.clone()); + (slot, true) + } + }; + ctx.deferred_integer_update_accumulators.insert(id); + Some(DeferredCounter { + id, + i32_slot, + double_slot, + private, + }) + } + + /// The label every residual check of the fast clone must branch to. + pub(super) fn side_exit_label(&self) -> &str { + &self.side_exit + } + + /// Publish the live values on the fall-through exit and end both + /// redirects. Must run right after the fast clone is lowered and BEFORE + /// the slow clone is, which reads and writes the real slots. + pub(super) fn finish(self, ctx: &mut FnCtx<'_>, merge_label: &str) { + let redirected = self.accumulator.is_some() || self.counter.is_some(); + if redirected && !ctx.block().is_terminated() { + let commit_idx = ctx.new_block("element_shape.loop.fast.write_back"); + let commit_label = ctx.block_label(commit_idx); + ctx.block().br(&commit_label); + ctx.current_block = commit_idx; + emit_write_back(ctx, self.accumulator.as_ref(), self.counter.as_ref()); + ctx.block().br(merge_label); + } + if let Some((id, _, _)) = &self.accumulator { + ctx.numeric_accumulator_f64_slots.remove(id); + } + if let Some(counter) = &self.counter { + ctx.deferred_integer_update_accumulators.remove(&counter.id); + if counter.private { + ctx.i32_counter_slots.remove(&counter.id); + } + } + } +} + +/// The write-back both exits share. A genuine double's bits are its NaN-box, so +/// the accumulator needs no conversion and — carrying no heap edge — no +/// barrier; the counter is an exact integer in `0..=i32::MAX`. +fn emit_write_back( + ctx: &mut FnCtx<'_>, + accumulator: Option<&(u32, String, String)>, + counter: Option<&DeferredCounter>, +) { + let blk = ctx.block(); + if let Some((_, alloca, real_slot)) = accumulator { + let value = blk.load(DOUBLE, alloca); + blk.store(DOUBLE, &value, real_slot); + } + if let Some(counter) = counter { + let value = blk.load(I32, &counter.i32_slot); + let as_double = blk.sitofp(I32, &value, DOUBLE); + blk.store(DOUBLE, &as_double, &counter.double_slot); + } +} diff --git a/crates/perry-codegen/src/stmt/element_shape_native_tests.rs b/crates/perry-codegen/src/stmt/element_shape_native_tests.rs new file mode 100644 index 0000000000..57217bdc5a --- /dev/null +++ b/crates/perry-codegen/src/stmt/element_shape_native_tests.rs @@ -0,0 +1,310 @@ +//! The element-shape fast clone's accumulator as an `f64` and its counter as an +//! `i32` (`stmt/element_shape_native.rs`). +//! +//! A child module of `element_shape_fields_random_tests`, so `use super::*` +//! brings that file's `random`/`fields` fixtures, the shape-keyed file's +//! untyped-receiver fixtures and `element_shape_loop_tests`'s slicing helpers. +//! +//! Every assertion here names storage by what the emitted IR itself says it is +//! — the fast preheader's seeds, the deref block's tag-tested reload — rather +//! than by register number, so a renumbering cannot make one vacuous. Each one +//! fails on the pre-change clone, which kept `sum` in its GC root slot (a +//! `ptr addrspace(1)` alloca reloaded through the RS4GC launder every +//! iteration) and the `repeat` counter as a double compared with `fcmp`. + +use super::*; + +/// `for (let i = 0; i < count; i++) sum += rows[7].id` with an untyped `sum` — +/// the benchmark's `repeat` mode, where both scalars were off-domain. +fn repeat_ir() -> String { + emit(&untyped_param_module( + Type::Any, + Type::Any, + vec![untyped_accumulate(untyped_elem_field( + Expr::Integer(7), + "v", + ))], + )) +} + +/// The first `store , ptr ` in `block` whose type is `ty`, +/// as `(value, slot)`. +fn first_store<'a>(block: &'a str, ty: &str) -> (&'a str, &'a str) { + block + .lines() + .find_map(|l| { + let rest = l.trim_start().strip_prefix(&format!("store {ty} "))?; + rest.split_once(", ptr ") + }) + .unwrap_or_else(|| panic!("no `store {ty}` in:\n{block}")) +} + +/// The root alloca a laundered reload `reg` was read from +/// (`.rs4p = load ptr addrspace(1), ptr `). +fn root_slot_of_reload<'a>(ir: &'a str, reg: &str) -> &'a str { + let needle = format!("{reg}.rs4p = load ptr addrspace(1), ptr "); + let at = ir + .find(&needle) + .unwrap_or_else(|| panic!("`{reg}` is not a root-slot reload")); + ir[at + needle.len()..].lines().next().unwrap().trim() +} + +/// Is `block`'s terminator an unconditional branch to the block `target` +/// names (`label %`, without its numeric suffix)? +fn terminator_targets(block: &str, target: &str) -> bool { + block + .lines() + .last() + .and_then(|l| l.trim().strip_prefix(&format!("br {target}."))) + .is_some_and(|suffix| suffix.chars().all(|c| c.is_ascii_digit())) +} + +/// `(accumulator alloca, accumulator root slot, counter i32 slot)` for the +/// repeat clone, read off the fast preheader's two seeds. +fn repeat_storage(ir: &str) -> (String, String, String) { + let pre = block_slice(ir, "element_shape.loop.fast.preheader"); + let (acc_value, acc_alloca) = first_store(pre, "double"); + let (start, counter_slot) = first_store(pre, "i32"); + assert_eq!( + start, "0", + "the counter's i32 slot must be seeded with the loop's literal start; \ + fast preheader:\n{pre}" + ); + let root = root_slot_of_reload(ir, acc_value); + ( + acc_alloca.to_string(), + root.to_string(), + counter_slot.to_string(), + ) +} + +#[test] +fn the_accumulator_never_touches_its_root_slot_inside_the_clone() { + let ir = repeat_ir(); + assert_shape_keyed_clone(&ir, "repeat with an untyped accumulator"); + let (acc_alloca, acc_root, _) = repeat_storage(&ir); + let fast = fast_clone_slice(&ir); + // The seed is the value the deref block tag-tested as a Number — the + // soundness of every raw `fadd` below rests on that being the same value. + let deref = block_slice(&ir, "element_shape.loop.preheader.deref"); + let seeded = first_store( + block_slice(&ir, "element_shape.loop.fast.preheader"), + "double", + ) + .0; + assert!( + deref.contains(&format!("{seeded} = bitcast i64 {seeded}.rs4o to double")) + && deref.contains(&format!("bitcast double {seeded} to i64")), + "the f64 alloca must be seeded with the reload the deref block \ + Number-tested; deref:\n{deref}" + ); + assert!( + !fast.contains("addrspace(1)") && !fast.contains("asm \"\""), + "no root slot may be loaded or stored inside the clone: every reload \ + passes through the RS4GC launder, which pins the loop-carried value to \ + a GPR and costs two GPR<->FPR transfers per iteration; emitted:\n{fast}" + ); + assert_eq!( + fast.matches(&format!("load double, ptr {acc_alloca}")) + .count(), + 1, + "the accumulator must be read from its f64 alloca once per iteration; \ + emitted:\n{fast}" + ); + assert_eq!( + fast.lines() + .filter(|l| { + l.trim_start().starts_with("store double ") + && l.ends_with(&format!(", ptr {acc_alloca}")) + }) + .count(), + 1, + "the accumulator must be committed to its f64 alloca exactly once per \ + iteration; emitted:\n{fast}" + ); + assert!( + !fast.contains(&format!("ptr {acc_root}")), + "the root slot `{acc_root}` must not appear inside the clone; \ + emitted:\n{fast}" + ); + assert_eq!( + fast.matches("fadd double").count(), + 1, + "exactly one IEEE add per iteration — the accumulator's; a second one \ + is the double counter this change retires; emitted:\n{fast}" + ); +} + +#[test] +fn the_repeat_counter_is_an_i32_against_the_materialized_bound() { + let ir = repeat_ir(); + let (_, _, counter_slot) = repeat_storage(&ir); + let cond = block_slice(&ir, "for.element_shape_fast.cond"); + assert!( + cond.contains(&format!("load i32, ptr {counter_slot}")) + && cond.contains("icmp slt i32") + && !cond.contains("fcmp") + && !cond.contains("addrspace(1)"), + "the condition must compare the i32 counter with the preheader's \ + materialized i32 bound — not reload `count` through its root slot and \ + `fcmp` a double counter against it; cond:\n{cond}" + ); + let update = block_slice(&ir, "for.element_shape_fast.update"); + assert!( + update.contains(&format!("load i32, ptr {counter_slot}")) + && update.contains("add i32") + && update.contains("store i32") + && !update.contains("store double") + && !update.contains("fadd"), + "`i++` must advance ONLY the i32 slot inside the clone; update:\n{update}" + ); + // The bound the i32 compare trusts is the validated materialization: a + // fractional, NaN, negative or out-of-range `count` branches to the slow + // clone before any of this is reachable. + for label in [ + "element_shape.loop.bound.range", + "element_shape.loop.bound.convert", + ] { + let block = block_slice(&ir, label); + assert!( + block.contains("label %element_shape.loop.slow.preheader"), + "`{label}` must still route an unrepresentable bound to the slow \ + clone; emitted:\n{block}" + ); + } +} + +#[test] +fn every_side_exit_publishes_both_scalars_before_the_slow_clone() { + let ir = repeat_ir(); + let (acc_alloca, acc_root, counter_slot) = repeat_storage(&ir); + let fast = fast_clone_slice(&ir); + assert_eq!( + fast.matches("label %element_shape.loop.slow.preheader") + .count(), + 0, + "no side exit may reach the slow clone without publishing the \ + redirected scalars first; emitted:\n{fast}" + ); + assert_eq!( + fast.matches("label %element_shape.loop.side_exit").count(), + 2, + "the residual check and the Number tag test must both leave through \ + the write-back trampoline; emitted:\n{fast}" + ); + for (label, successor) in [ + ( + "element_shape.loop.side_exit", + "label %element_shape.loop.slow.preheader", + ), + ( + "element_shape.loop.fast.write_back", + "label %element_shape.loop.merge", + ), + ] { + let block = block_slice(&ir, label); + let acc_load = block + .lines() + .position(|l| l.contains(&format!("load double, ptr {acc_alloca}"))); + let acc_publish = block.lines().position(|l| { + l.contains("store ptr addrspace(1)") && l.ends_with(&format!("ptr {acc_root}")) + }); + let counter_publish = block + .lines() + .position(|l| l.contains("store double %") && !l.contains("addrspace")); + assert!( + acc_load.is_some() + && acc_publish.is_some() + && block.contains(&format!("load i32, ptr {counter_slot}")) + && block.contains("sitofp i32") + && counter_publish.is_some() + && terminator_targets(block, successor) + && !block.contains("call "), + "`{label}` must publish the accumulator to its root slot `{acc_root}` \ + and the counter to its double slot, call-free, then branch to \ + `{successor}`; emitted:\n{block}" + ); + } + let exit = block_slice(&ir, "for.element_shape_fast.exit"); + assert!( + exit.contains("label %element_shape.loop.fast.write_back"), + "the fall-through exit must publish too; emitted:\n{exit}" + ); +} + +/// The carried index already had its own commit protocol (#10185). The +/// trampoline must publish the accumulator and the counter but NEVER the +/// carried binding: its real slot has to keep the previous iteration's commit, +/// which is the value the slow clone re-runs the recurrence from. +#[test] +fn the_trampoline_leaves_the_carried_binding_to_its_own_commit() { + let ir = emit(&access_module(random_body(true))); + assert_shape_keyed_clone(&ir, "random"); + let fast = fast_clone_slice(&ir); + let committed = last_block_with_prefix(&fast, "element_shape.number"); + let published = sitofp_results(committed); + assert_eq!( + published.len(), + 1, + "one carried commit; block:\n{committed}" + ); + let carried_slot = committed + .lines() + .find_map(|l| { + l.trim_start() + .strip_prefix(&format!("store double {}, ptr ", published[0])) + }) + .expect("the carried commit stores the converted value"); + let tramp = block_slice(&ir, "element_shape.loop.side_exit"); + assert!( + !tramp.contains(&format!("ptr {carried_slot}\n")) + && !tramp.trim_end().ends_with(&format!("ptr {carried_slot}")), + "the trampoline must not write the carried binding `{carried_slot}`: a \ + side exit happens AFTER this iteration's recurrence, so publishing it \ + would make the slow clone apply the recurrence twice; emitted:\n{tramp}" + ); + assert_eq!( + sitofp_results(tramp).len(), + 1, + "the trampoline converts exactly one i32 — the loop counter; \ + emitted:\n{tramp}" + ); + let cond = block_slice(&ir, "for.element_shape_fast.cond"); + assert!( + cond.contains("icmp slt i32") && !cond.contains("fcmp"), + "the carried form's counter must be an i32 too; cond:\n{cond}" + ); + assert!( + !fast.contains("addrspace(1)"), + "no root slot inside the carried clone; emitted:\n{fast}" + ); +} + +/// `fields`: the counter is already a canonical i32 (it indexes), so only the +/// accumulator is redirected — and all three folded adds stay in the double +/// domain. +#[test] +fn the_fields_clone_redirects_only_the_accumulator() { + let ir = emit(&access_module(fields_body())); + assert_shape_keyed_clone(&ir, "fields"); + let fast = fast_clone_slice(&ir); + assert!( + !fast.contains("addrspace(1)"), + "no root slot inside the fields clone; emitted:\n{fast}" + ); + assert_eq!(fast.matches("fadd double").count(), 3); + let tramp = block_slice(&ir, "element_shape.loop.side_exit"); + assert!( + tramp.contains("store ptr addrspace(1)") + && sitofp_results(tramp).is_empty() + && terminator_targets(tramp, "label %element_shape.loop.slow.preheader"), + "a canonical-i32 counter has no double storage to publish, so the \ + trampoline carries only the accumulator; emitted:\n{tramp}" + ); + assert_eq!( + side_exit_count(&fast), + fast.matches("label %element_shape.loop.side_exit").count(), + "every side exit of the fields clone must go through the trampoline; \ + emitted:\n{fast}" + ); +} diff --git a/crates/perry-codegen/src/stmt/mod.rs b/crates/perry-codegen/src/stmt/mod.rs index 73e8ecaf05..a6ca584e6a 100644 --- a/crates/perry-codegen/src/stmt/mod.rs +++ b/crates/perry-codegen/src/stmt/mod.rs @@ -24,6 +24,7 @@ mod element_shape_carried; mod element_shape_loop; #[cfg(test)] mod element_shape_loop_tests; +mod element_shape_native; mod if_stmt; mod let_buffer_views; mod let_object_facts; diff --git a/crates/perry-runtime/src/arena/block.rs b/crates/perry-runtime/src/arena/block.rs index 96e64195d1..4f84e098b6 100644 --- a/crates/perry-runtime/src/arena/block.rs +++ b/crates/perry-runtime/src/arena/block.rs @@ -565,6 +565,22 @@ impl Arena { } } + /// Point allocation at `blocks[idx]`. + /// + /// Every move of `current` goes through here, because the cached young + /// occupancy (`arena/from_space.rs`) assumes that between two reads only + /// the current Eden block's offset changed unless the heap generation + /// moved. The allocator's block switches happen outside any `HeapChange` + /// scope, so they must retire that cache themselves; the cache key does not + /// include the block index. Invalidating for every arena, not just Eden, + /// keeps the rule free of a space test; a switch costs one thread-local + /// store and happens once per block. + #[inline] + pub(crate) fn set_current(&mut self, idx: usize) { + self.current = idx; + super::from_space::invalidate_sealed_young_bytes(); + } + /// Lazy variant of `new`: starts with a single tombstone block /// (`data = null, size = 0`) instead of an eagerly-mapped 1 MB /// block, so JS-touching threads that never allocate in this @@ -685,7 +701,7 @@ impl Arena { self.blocks.len() - 1 } }; - self.current = new_idx; + self.set_current(new_idx); ARENA_TOTAL_BYTES.with(|t| t.set(t.get() + fresh_size)); } @@ -757,7 +773,7 @@ impl Arena { continue; } if let Some(ptr) = self.try_block_alloc(i, size, align) { - self.current = i; + self.set_current(i); // Resync inline state to the new current block. self.resync_inline_to_current(); return Some(ptr); @@ -797,7 +813,7 @@ impl Arena { } if let Some(ptr) = self.try_block_alloc_excluding_pages(i, size, align, excluded_pages) { - self.current = i; + self.set_current(i); self.resync_inline_to_current(); return ptr; } diff --git a/crates/perry-runtime/src/arena/from_space.rs b/crates/perry-runtime/src/arena/from_space.rs new file mode 100644 index 0000000000..994a0833e5 --- /dev/null +++ b/crates/perry-runtime/src/arena/from_space.rs @@ -0,0 +1,294 @@ +//! Young-generation occupancy without a block walk. +//! +//! `copying_from_space_in_use_bytes()` is the basis of the scavenge nursery +//! cap, and `gc_budgeted_due_trigger()` reads it on every runtime safepoint +//! poll and every `gc_malloc`. Summing `block.offset` over every Eden block and +//! every active-survivor block made each of those reads O(blocks), and the +//! young generation can hold hundreds of blocks once the cap scales with the +//! tenured set. +//! +//! # What changes between two reads +//! +//! Split the sum into the Eden block allocation is currently bumping +//! (`blocks[current]`) and everything else, the *sealed* bytes. Between two +//! reads the sealed bytes can change in only three ways: +//! +//! 1. **A free or move.** Every reset, detach, release, evacuation and survivor +//! flip runs inside a [`HeapChange`](crate::gc::heap_generation::HeapChange) +//! scope, which advances the heap generation when it opens and again when it +//! closes. The funnel is enforced: the reset and evacuation primitives call +//! `debug_assert_heap_change_open`. +//! 2. **The allocator moves `current`.** Filling a block and continuing in +//! another one (a fresh block, a reused tombstone slot, or a partly-used +//! block with room for a smaller request) happens outside any scope. Every +//! such move goes through [`Arena::set_current`], which invalidates the +//! cache. Comparing the index alone would not do: `current` can leave +//! block 3 for block 5 and come back to block 3 once block 5 is full. +//! 3. **The active survivor space changes.** That is a flip, so it is case 1. +//! +//! Survivor allocation fills the *inactive* space, which is not from-space +//! until the flip, and the flip happens inside the copying minor's scope. +//! +//! So the cache holds the sealed bytes keyed on the heap generation, and a read +//! is `sealed + blocks[current].offset`. Eden's `current` and the active +//! survivor index are deliberately not part of the key: every move of either is +//! already covered by rule 2 or rule 1, and each extra key field is one more +//! checked thread-local lookup on a path the due check takes on every poll. +//! Nothing is stored while a scope is open, because the generation does not +//! move inside one; a value stored before the scope opened stops matching when +//! it opens. +//! +//! Debug builds compare every cached answer with the walk, so a mutation site +//! that bypasses both rules fails the test that reaches it rather than skewing +//! the nursery cap. + +use super::*; + +#[derive(Clone, Copy)] +struct SealedYoungBytes { + valid: bool, + heap_generation: u64, + /// Eden bytes outside the current block, plus every byte of the active + /// survivor space. + sealed: usize, +} + +impl SealedYoungBytes { + const INVALID: Self = Self { + valid: false, + heap_generation: 0, + sealed: 0, + }; +} + +crate::perry_thread_local! { + static SEALED_YOUNG_BYTES: Cell = const { Cell::new(SealedYoungBytes::INVALID) }; +} + +#[cfg(test)] +crate::perry_thread_local! { + /// Fault injection for the invalidation test: while set, `Arena::set_current` + /// leaves the cache alone. + static SKIP_SET_CURRENT_INVALIDATION: Cell = const { Cell::new(false) }; +} + +/// Forget the cached sealed bytes. Called when an arena moves `current`. +#[inline] +pub(crate) fn invalidate_sealed_young_bytes() { + #[cfg(test)] + if SKIP_SET_CURRENT_INVALIDATION.with(Cell::get) { + return; + } + SEALED_YOUNG_BYTES.with(|cache| cache.set(SealedYoungBytes::INVALID)); +} + +/// Bytes currently allocated in Eden plus the active survivor from-space. +#[inline] +pub(crate) fn copying_from_space_in_use_bytes() -> usize { + let current_bytes = synced_current_eden_block_bytes(); + let generation = crate::gc::heap_generation::heap_generation(); + let cached = SEALED_YOUNG_BYTES.with(Cell::get); + if cached.valid && cached.heap_generation == generation { + let bytes = cached.sealed + current_bytes; + debug_assert_eq!( + bytes, + copying_from_space_in_use_bytes_walked(), + "cached from-space occupancy drifted from the block walk: a young block offset \ + or Eden's `current` changed outside a HeapChange scope without \ + `Arena::set_current` (see arena/from_space.rs)" + ); + return bytes; + } + walk_and_store(generation, current_bytes) +} + +/// `sync_inline_arena_state`, then the current Eden block's offset. The same +/// write and the same sampling note, through the hot-TLS addresses rather than +/// two `thread_local!` resolutions. +#[inline(always)] +fn synced_current_eden_block_bytes() -> usize { + // SAFETY: this thread's inline state and nursery arena; no `&mut` borrow of + // either is live across this call. + unsafe { + let inline = &*hot_inline_state(); + let eden = &mut *hot_arena(); + let current = eden.current; + if !inline.data.is_null() { + super::alloc_sample::note_inline_sync(eden.blocks[current].offset, inline.offset); + eden.blocks[current].offset = inline.offset; + } + eden.blocks[current].offset + } +} + +#[cold] +#[inline(never)] +fn walk_and_store(generation: u64, current_bytes: usize) -> usize { + let bytes = copying_from_space_in_use_bytes_walked(); + if !crate::gc::heap_generation::heap_change_open() { + SEALED_YOUNG_BYTES.with(|cache| { + cache.set(SealedYoungBytes { + valid: true, + heap_generation: generation, + sealed: bytes - current_bytes, + }) + }); + } + bytes +} + +/// The O(blocks) sum the cache stands in for. The source of truth. +pub(crate) fn copying_from_space_in_use_bytes_walked() -> usize { + sync_inline_arena_state(); + let eden = ARENA.with(|arena| { + let arena = unsafe { &*arena.get() }; + arena.blocks.iter().map(|b| b.offset).sum::() + }); + let active = ACTIVE_SURVIVOR.with(|active| active.get()); + let survivor = with_survivor_arena(active, |arena| { + arena.blocks.iter().map(|b| b.offset).sum::() + }); + eden + survivor +} + +#[cfg(test)] +mod tests { + use super::*; + + struct SkipInvalidation; + + impl SkipInvalidation { + fn new() -> Self { + SKIP_SET_CURRENT_INVALIDATION.with(|skip| skip.set(true)); + Self + } + } + + impl Drop for SkipInvalidation { + fn drop(&mut self) { + SKIP_SET_CURRENT_INVALIDATION.with(|skip| skip.set(false)); + invalidate_sealed_young_bytes(); + } + } + + /// The cached answer if the cache matches, without the debug cross-check, + /// so the fault-injection test can observe a stale value instead of + /// panicking inside the read. + fn cache_hit() -> Option { + let current_bytes = synced_current_eden_block_bytes(); + let cached = SEALED_YOUNG_BYTES.with(Cell::get); + (cached.valid && cached.heap_generation == crate::gc::heap_generation::heap_generation()) + .then(|| cached.sealed + current_bytes) + } + + fn eden_current() -> usize { + ARENA.with(|a| unsafe { (*a.get()).current }) + } + + fn eden_block(idx: usize) -> (usize, usize) { + ARENA.with(|a| { + let arena = unsafe { &*a.get() }; + let block = &arena.blocks[idx]; + (block.offset, block.size) + }) + } + + /// Prime the cache on Eden's only block with 64 bytes of headroom, continue + /// in a fresh block, fill it, and return to the first block through the + /// allocator's forward scan: `current` ends at the index it was primed at, + /// while a whole block of bytes landed elsewhere. Every step asserts where + /// the allocator went, so a change in allocation policy fails the test + /// instead of making it vacuous. + fn return_to_the_primed_block() { + let _no_gc = crate::gc::GcSuppressScope::new(); + // libtest runs each test on a fresh thread, so this is a fresh arena. + assert_eq!( + ARENA.with(|a| unsafe { (*a.get()).blocks.len() }), + 1, + "fixture needs a fresh thread-local Eden" + ); + let start = eden_current(); + let (used, size) = eden_block(start); + let _ = crate::arena::arena_alloc(size - used - 64, 8); + assert_eq!(eden_block(start).0, size - 64); + + let primed = copying_from_space_in_use_bytes(); + assert_eq!( + cache_hit(), + Some(primed), + "the first read must prime the cache" + ); + + // 128 bytes do not fit the 64-byte headroom: a fresh block is installed. + let _ = crate::arena::arena_alloc(128, 8); + let other = eden_current(); + assert_ne!(other, start); + let (other_used, other_size) = eden_block(other); + let _ = crate::arena::arena_alloc(other_size - other_used, 8); + assert_eq!( + eden_block(other).0, + other_size, + "the fresh block must be full" + ); + + // Only the primed block can serve 32 bytes now. + let _ = crate::arena::arena_alloc(32, 8); + assert_eq!( + eden_current(), + start, + "the forward scan must return to the primed block" + ); + assert!(copying_from_space_in_use_bytes_walked() >= primed + other_size); + } + + #[test] + fn cached_occupancy_matches_the_walk_after_returning_to_the_primed_block() { + return_to_the_primed_block(); + assert_eq!( + cache_hit(), + None, + "set_current must have invalidated the cache" + ); + assert_eq!( + copying_from_space_in_use_bytes(), + copying_from_space_in_use_bytes_walked() + ); + } + + /// Fault injection for the invalidation in `Arena::set_current`: without it + /// the same sequence leaves a cache that matches every key and is short by + /// the fresh block's bytes. + #[test] + fn without_set_current_invalidation_the_cache_goes_stale() { + let _skip = SkipInvalidation::new(); + return_to_the_primed_block(); + let walked = copying_from_space_in_use_bytes_walked(); + let stale = cache_hit().expect("with invalidation skipped the primed entry still matches"); + assert!( + stale < walked, + "the stale cache must miss the fresh block's bytes (stale {stale}, walked {walked})" + ); + } + + /// The other half of the key: a collection opens a `HeapChange`, and the + /// generation it advances retires the cached entry. + #[test] + fn a_heap_change_scope_retires_the_cached_entry() { + let _no_gc = crate::gc::GcSuppressScope::new(); + let _ = crate::arena::arena_alloc(256, 8); + let primed = copying_from_space_in_use_bytes(); + assert_eq!(cache_hit(), Some(primed)); + { + let _scope = crate::gc::heap_generation::HeapChange::begin( + crate::gc::heap_generation::HeapChangeKind::Sweep, + ); + assert_eq!(cache_hit(), None); + // Reads inside the scope walk and store nothing. + assert_eq!(copying_from_space_in_use_bytes(), primed); + assert_eq!(cache_hit(), None); + } + assert_eq!(cache_hit(), None); + assert_eq!(copying_from_space_in_use_bytes(), primed); + assert_eq!(cache_hit(), Some(primed)); + } +} diff --git a/crates/perry-runtime/src/arena/mod.rs b/crates/perry-runtime/src/arena/mod.rs index 1fc2224482..785f7dc4d1 100644 --- a/crates/perry-runtime/src/arena/mod.rs +++ b/crates/perry-runtime/src/arena/mod.rs @@ -12,6 +12,7 @@ pub(crate) mod alloc_sample; mod allocators; mod block; mod construction; +mod from_space; pub(crate) use construction::ConstructionBatch; mod inline; mod page_meta; diff --git a/crates/perry-runtime/src/arena/promote.rs b/crates/perry-runtime/src/arena/promote.rs index fcfacf6442..7d5bdbd3a3 100644 --- a/crates/perry-runtime/src/arena/promote.rs +++ b/crates/perry-runtime/src/arena/promote.rs @@ -587,11 +587,12 @@ fn reset_young_after_promotion() { if arena.blocks.iter().all(|block| block.data.is_null()) { arena.install_fresh_block(BLOCK_SIZE); } - arena.current = arena + let first_live = arena .blocks .iter() .position(|block| !block.data.is_null()) .unwrap_or(0); + arena.set_current(first_live); INLINE_STATE.with(|s| { let inline = &mut *s.get(); if !inline.data.is_null() { @@ -610,7 +611,7 @@ fn reset_young_after_promotion() { block.offset = 0; block.dead_cycles = 0; } - arena.current = 0; + arena.set_current(0); }); } let active = ACTIVE_SURVIVOR.with(|active| active.get()); diff --git a/crates/perry-runtime/src/arena/quarantine.rs b/crates/perry-runtime/src/arena/quarantine.rs index 37986e7e06..30d0571532 100644 --- a/crates/perry-runtime/src/arena/quarantine.rs +++ b/crates/perry-runtime/src/arena/quarantine.rs @@ -580,11 +580,12 @@ pub(crate) fn copying_quarantine_from_spaces_and_flip() -> ArenaResetStats { }); with_survivor_arena_mut(active, |arena| { - arena.current = arena + let first_live = arena .blocks .iter() .position(|block| !block.data.is_null()) .unwrap_or(0); + arena.set_current(first_live); }); ACTIVE_SURVIVOR.with(|active_cell| active_cell.set(1 - active)); @@ -619,7 +620,7 @@ unsafe fn detach_used_blocks(arena: &mut Arena) -> Vec<(*mut u8, usize, usize)> block.offset = 0; block.dead_cycles = 0; } - arena.current = 0; + arena.set_current(0); detached } @@ -654,7 +655,7 @@ unsafe fn ensure_usable_current_block(arena: &mut Arena) { .iter() .position(|block| !block.data.is_null() && block.offset == 0) { - arena.current = idx; + arena.set_current(idx); return; } arena.install_fresh_block(BLOCK_SIZE); diff --git a/crates/perry-runtime/src/arena/reset.rs b/crates/perry-runtime/src/arena/reset.rs index faf9525426..e643f869a0 100644 --- a/crates/perry-runtime/src/arena/reset.rs +++ b/crates/perry-runtime/src/arena/reset.rs @@ -22,7 +22,7 @@ pub fn arena_reset_all_blocks_to_zero() { block.clear_object_starts(); block.offset = 0; } - arena.current = 0; + arena.set_current(0); // Free list is now invalid (all entries point into reset blocks). crate::gc::ARENA_FREE_LIST.with(|fl| fl.borrow_mut().clear()); crate::gc::ARENA_FREE_LIST_NONEMPTY.with(|c| c.set(false)); @@ -110,7 +110,7 @@ fn reset_region_to_zero(arena: &mut Arena) -> (usize, usize) { if arena.generation == HeapGeneration::Old { old_gen_in_use_bytes_sub(reusable_bytes); } - arena.current = 0; + arena.set_current(0); (reset_blocks, reusable_bytes) } @@ -129,18 +129,8 @@ pub(crate) fn copying_active_survivor_in_use_bytes() -> usize { } /// Bytes currently allocated in Eden plus the active survivor from-space. -pub(crate) fn copying_from_space_in_use_bytes() -> usize { - sync_inline_arena_state(); - let eden = ARENA.with(|arena| { - let arena = unsafe { &*arena.get() }; - arena.blocks.iter().map(|b| b.offset).sum::() - }); - let active = ACTIVE_SURVIVOR.with(|active| active.get()); - let survivor = with_survivor_arena(active, |arena| { - arena.blocks.iter().map(|b| b.offset).sum::() - }); - eden + survivor -} +/// O(1) between layout changes; see `arena/from_space.rs`. +pub(crate) use super::from_space::copying_from_space_in_use_bytes; /// #7901: is `block_idx` inside the copying collector's from-space (Eden plus /// the ACTIVE survivor semispace)? The inactive semispace is to-space and is @@ -501,7 +491,7 @@ pub fn arena_reset_empty_blocks(block_has_live: &[bool]) -> ArenaResetStats { // `arena.current` where it was — the next `Arena::alloc` slow // path will tombstone-reuse a slot and update `current` then. if !arena.blocks[new_current].data.is_null() { - arena.current = new_current; + arena.set_current(new_current); } let _ = (n_live, n_total); INLINE_STATE.with(|s| { @@ -817,7 +807,7 @@ impl ArenaResetEmptyBlocksState { .map(|block| !block.data.is_null()) .unwrap_or(false) { - arena.current = new_current; + arena.set_current(new_current); } INLINE_STATE.with(|s| { let inline = &mut *s.get(); @@ -980,7 +970,7 @@ impl SurvivorArenaReclaimState { .enumerate() .find(|(_, block)| !block.data.is_null() && block.offset == 0) { - arena.current = idx; + arena.set_current(idx); } else if arena .blocks .get(arena.current) @@ -993,7 +983,7 @@ impl SurvivorArenaReclaimState { .enumerate() .find(|(_, block)| !block.data.is_null()) { - arena.current = idx; + arena.set_current(idx); } } }); @@ -1297,7 +1287,7 @@ impl OldArenaReclaimDeadBlocksState { .enumerate() .find(|(_, block)| !block.data.is_null() && block.offset == 0) { - arena.current = idx; + arena.set_current(idx); } else if arena .blocks .get(arena.current) @@ -1310,7 +1300,7 @@ impl OldArenaReclaimDeadBlocksState { .enumerate() .find(|(_, block)| !block.data.is_null()) { - arena.current = idx; + arena.set_current(idx); } } }); @@ -1382,7 +1372,7 @@ pub(crate) fn old_arena_reclaim_dead_blocks(block_has_live: &[bool]) -> ArenaRes .enumerate() .find(|(_, block)| !block.data.is_null() && block.offset == 0) { - arena.current = idx; + arena.set_current(idx); } else if arena .blocks .get(arena.current) @@ -1395,7 +1385,7 @@ pub(crate) fn old_arena_reclaim_dead_blocks(block_has_live: &[bool]) -> ArenaRes .enumerate() .find(|(_, block)| !block.data.is_null()) { - arena.current = idx; + arena.set_current(idx); } } } @@ -1482,7 +1472,7 @@ pub(crate) fn old_arena_reclaim_selected_dead_blocks( .enumerate() .find(|(_, block)| !block.data.is_null() && block.offset == 0) { - arena.current = idx; + arena.set_current(idx); } else if arena .blocks .get(arena.current) @@ -1495,7 +1485,7 @@ pub(crate) fn old_arena_reclaim_selected_dead_blocks( .enumerate() .find(|(_, block)| !block.data.is_null()) { - arena.current = idx; + arena.set_current(idx); } } } @@ -1579,7 +1569,7 @@ fn reclaim_dead_survivor_arena_blocks( .enumerate() .find(|(_, block)| !block.data.is_null() && block.offset == 0) { - arena.current = idx; + arena.set_current(idx); } else if arena .blocks .get(arena.current) @@ -1592,7 +1582,7 @@ fn reclaim_dead_survivor_arena_blocks( .enumerate() .find(|(_, block)| !block.data.is_null()) { - arena.current = idx; + arena.set_current(idx); } } } diff --git a/crates/perry-runtime/src/array/header.rs b/crates/perry-runtime/src/array/header.rs index 08c965df62..b832d073a5 100644 --- a/crates/perry-runtime/src/array/header.rs +++ b/crates/perry-runtime/src/array/header.rs @@ -1363,10 +1363,17 @@ pub extern "C" fn js_array_refresh_local_head(value: f64) -> f64 { if !crate::value::addr_class::is_plausible_heap_addr(raw) { return value; } + // Cold arms only (loop-clone preheader, guarded read repair): see + // `traversal_feedback::note_compiled_materialization`. + let materializes_lazy = + unsafe { crate::json::traversal_feedback::lazy_array_unmaterialized(raw) }; let cleaned = clean_arr_ptr(raw as *const ArrayHeader); if cleaned.is_null() || cleaned as usize == raw { return value; } + if materializes_lazy { + crate::json::traversal_feedback::note_compiled_materialization(); + } f64::from_bits(crate::value::POINTER_TAG | (cleaned as u64 & crate::value::POINTER_MASK)) } diff --git a/crates/perry-runtime/src/array/mod.rs b/crates/perry-runtime/src/array/mod.rs index dd08760e23..d3816eca98 100644 --- a/crates/perry-runtime/src/array/mod.rs +++ b/crates/perry-runtime/src/array/mod.rs @@ -64,6 +64,8 @@ mod index_get_exit_tests; #[cfg(test)] mod push_pop_tests; #[cfg(test)] +mod refresh_traversal_evidence_tests; +#[cfg(test)] mod spread_dense_tests; #[cfg(test)] mod strict_store_tests; diff --git a/crates/perry-runtime/src/array/refresh_traversal_evidence_tests.rs b/crates/perry-runtime/src/array/refresh_traversal_evidence_tests.rs new file mode 100644 index 0000000000..a930eda60a --- /dev/null +++ b/crates/perry-runtime/src/array/refresh_traversal_evidence_tests.rs @@ -0,0 +1,55 @@ +//! A lazy JSON array materialized whole by compiled code refreshing its local +//! head is traversal evidence, exactly once per array, and a plain array is +//! never evidence. Without this the element-shape loop clone (#10171) served +//! every scan loop through its preheader and traversal feedback never learned +//! that the tape was being wasted. + +fn lazy_array_box(bytes: &[u8], len: u32) -> f64 { + let tape = crate::json_tape::build_tape(bytes).unwrap(); + let text = crate::string::js_string_from_bytes(bytes.as_ptr(), bytes.len() as u32); + let lazy = unsafe { crate::json_tape::alloc_lazy_array(&tape.entries, 0, len, text) }; + let header = unsafe { crate::value::addr_class::try_read_gc_header(lazy as usize) }.unwrap(); + assert_eq!( + header.obj_type, + crate::gc::GC_TYPE_LAZY_ARRAY, + "fixture must be a real lazy array or the assertions below are vacuous" + ); + crate::value::js_nanbox_pointer(lazy as i64) +} + +#[test] +fn compiled_materialization_of_a_lazy_array_counts_once_and_a_plain_array_never() { + crate::json::traversal_feedback::reset_for_tests(); + let scope = crate::gc::RuntimeHandleScope::new(); + + let mut plain = crate::array::js_array_alloc(2); + plain = crate::array::js_array_push_f64(plain, 1.0); + let plain_box = scope.root_nanbox_f64(crate::value::js_nanbox_pointer(plain as i64)); + let _ = crate::array::header::js_array_refresh_local_head(plain_box.get_nanbox_f64()); + assert_eq!( + crate::json::traversal_feedback::score_for_tests(), + 0, + "a plain array is not traversal evidence" + ); + + let lazy = scope.root_nanbox_f64(lazy_array_box(br#"[{"id":1},{"id":2},{"id":3}]"#, 3)); + let first = crate::array::header::js_array_refresh_local_head(lazy.get_nanbox_f64()); + assert_ne!( + first.to_bits(), + lazy.get_nanbox_f64().to_bits(), + "the head was refreshed" + ); + assert_eq!( + crate::json::traversal_feedback::score_for_tests(), + 2, + "materializing a lazy array whole is one flip's worth of evidence" + ); + + let _ = crate::array::header::js_array_refresh_local_head(lazy.get_nanbox_f64()); + assert_eq!( + crate::json::traversal_feedback::score_for_tests(), + 2, + "an already materialized lazy array is not evidence again" + ); + crate::json::traversal_feedback::reset_for_tests(); +} diff --git a/crates/perry-runtime/src/gc/heap_generation.rs b/crates/perry-runtime/src/gc/heap_generation.rs index 4f91460663..18841477b3 100644 --- a/crates/perry-runtime/src/gc/heap_generation.rs +++ b/crates/perry-runtime/src/gc/heap_generation.rs @@ -69,7 +69,6 @@ crate::perry_thread_local! { /// This thread's current heap generation. #[inline] -#[cfg_attr(not(feature = "regex-engine"), allow(dead_code))] pub(crate) fn heap_generation() -> u64 { HEAP_GENERATION.with(Cell::get) } @@ -113,6 +112,14 @@ impl Drop for HeapChange { } } +/// Is a [`HeapChange`] scope open on this thread? A cache keyed on the heap +/// generation must not store while one is, because the generation does not +/// advance inside a scope. +#[inline] +pub(crate) fn heap_change_open() -> bool { + OPEN_HEAP_CHANGES.try_with(Cell::get).unwrap_or(1) > 0 +} + /// Called by every primitive that frees or moves heap memory. #[inline] #[track_caller] diff --git a/crates/perry-runtime/src/gc/mod.rs b/crates/perry-runtime/src/gc/mod.rs index 757909bcc0..09f92ead01 100644 --- a/crates/perry-runtime/src/gc/mod.rs +++ b/crates/perry-runtime/src/gc/mod.rs @@ -38,7 +38,9 @@ pub use types::*; mod json_defer; mod policy; pub(crate) use json_defer::JsonParseAllocation; +#[cfg(test)] pub(crate) use policy::gc_runtime_safepoint; +pub(crate) use policy::gc_runtime_safepoint_poll; pub(crate) use policy::note_young_leaf_born_old; /// The one writer of `GC_SAFEPOINT_PENDING` — it also keeps the poll's global /// arming shadow in step. See `gc/poll_arm.rs`. diff --git a/crates/perry-runtime/src/gc/policy.rs b/crates/perry-runtime/src/gc/policy.rs index 6a772c0b14..1c33d1e9d4 100644 --- a/crates/perry-runtime/src/gc/policy.rs +++ b/crates/perry-runtime/src/gc/policy.rs @@ -105,6 +105,16 @@ pub(super) fn next_arena_trigger_base() -> usize { /// (near-zero infant mortality), a saturated survivor space, and 1427 /// collections for a run that allocates ~1.4 GB. pub(super) fn young_scavenge_cap_due() -> bool { + young_scavenge_cap_due_with_old_reclaimable(old_gen_reclaimable_pressure_bytes) +} + +/// [`young_scavenge_cap_due`] with the old-gen reclaimable-pressure read +/// supplied by the caller. `gc_budgeted_due_trigger` has already read that +/// value for its old-reclaim arm, and nothing between that read and this one +/// touches old-gen, so it passes a closure returning the value it holds rather +/// than paying two more thread-local reads. The read happens where it always +/// did: after the census seed, which changes only the young-side factor. +fn young_scavenge_cap_due_with_old_reclaimable(old_reclaimable: impl FnOnce() -> usize) -> bool { if !nursery_cap_active() { return false; } @@ -114,10 +124,16 @@ pub(super) fn young_scavenge_cap_due() -> bool { // process, halfway to the base cap). Not while a collection is in // progress or a budgeted cycle is active — the young generation is being // rewritten then and the walk would read forwarding stubs. - if GC_FLAGS.with(|f| f.get()) & GC_FLAG_IN_ALLOC == 0 && !gc_budgeted_cycle_active() { + // + // The seeded test comes first because the seed can happen only once: in + // steady state this is one thread-local read instead of three. + if !super::tenuring::object_census_seeded() + && GC_FLAGS.with(|f| f.get()) & GC_FLAG_IN_ALLOC == 0 + && !gc_budgeted_cycle_active() + { super::tenuring::maybe_seed_object_census_from_allocation(from_space_in_use); } - from_space_in_use >= scavenge_nursery_cap_dueness_bytes() + from_space_in_use >= scavenge_nursery_cap_dueness_bytes_with(old_reclaimable) } /// #10169: does the young generation hold at least one BASE nursery cap of @@ -142,11 +158,22 @@ pub(crate) fn young_generation_holds_a_nursery() -> bool { /// NOT feed `effective_next_arena_trigger`: this is about *dueness*, and a test /// that also moved the trigger clamp would be changing two things at once. fn scavenge_nursery_cap_dueness_bytes() -> usize { + scavenge_nursery_cap_dueness_bytes_with(old_gen_reclaimable_pressure_bytes) +} + +fn scavenge_nursery_cap_dueness_bytes_with(old_reclaimable: impl FnOnce() -> usize) -> usize { #[cfg(test)] if let Some(bytes) = GC_NURSERY_CAP_TEST_DUE_BYTES.with(Cell::get) { return bytes; } - super::tenuring::scavenge_nursery_cap_effective_bytes() + let influx_driven = super::tenuring::influx_driven_nursery_cap_bytes(); + let old_reclaimable = old_reclaimable(); + debug_assert_eq!( + old_reclaimable, + old_gen_reclaimable_pressure_bytes(), + "the old-gen pressure a due check reuses must still be the current value" + ); + super::tenuring::scavenge_nursery_cap_from(influx_driven, old_reclaimable) } #[cfg(test)] @@ -437,6 +464,24 @@ pub(super) fn tiny_parse_pressure_due_with( && in_use >= base.saturating_add(tiny_parse_pressure_headroom_bytes(step)) } +/// Should a tiny-parse boundary collect under the generational collector? +/// +/// The priced in-use guard ([`tiny_parse_pressure_due`]) answers "has the +/// arena grown enough to be worth a collection", on total in-use, which a +/// collection cannot lower below the live set (#9831). It never asked the +/// generational question: is the young generation at its scavenge cap? A loop +/// of tiny `JSON.parse` calls allocates only under the parser's suppression +/// window and at bounded inline-object births, so nothing else arms the +/// nursery safepoint for it, and the young generation grew to the guard's +/// 48 MB floor before its first minor: `small_record:parse` peaked at 80 MiB +/// against Node's 59 with 0–9 ‰ of each collection surviving. The nursery cap +/// is safe to consult where the absolute in-use guard was not: a minor lowers +/// the quantity it tests to the survivors, so it cannot fire again until the +/// cap has been refilled. +pub(super) fn tiny_parse_generational_collection_due(in_use: usize, in_use_trigger: usize) -> bool { + tiny_parse_pressure_due(in_use, in_use_trigger) || young_scavenge_cap_due() +} + /// The live [`tiny_parse_pressure_due_with`]: current base and step. pub(super) fn tiny_parse_pressure_due(in_use: usize, in_use_trigger: usize) -> bool { #[cfg(test)] @@ -1525,7 +1570,12 @@ fn gc_bump_malloc_trigger_inner(collect_now: bool) { // The guard now also requires the arena to have grown past the // productivity-priced headroom since the last collection ended. let in_use = crate::arena::arena_in_use_bytes(); - if !tiny_parse_pressure_due(in_use, tiny_parse_in_use_trigger_for_mode()) { + let due = if use_gen_gc { + tiny_parse_generational_collection_due(in_use, tiny_parse_in_use_trigger_for_mode()) + } else { + tiny_parse_pressure_due(in_use, tiny_parse_in_use_trigger_for_mode()) + }; + if !due { return; } if use_gen_gc { @@ -1581,7 +1631,12 @@ fn gc_collect_pending_suppressed_parse_slow() { // boundary collection from stacking a second minor on top of it. A request // nothing has satisfied is still due and still collects. let in_use = crate::arena::arena_in_use_bytes(); - if !tiny_parse_pressure_due(in_use, tiny_parse_in_use_trigger_for_mode()) { + let due = if gen_gc_enabled() { + tiny_parse_generational_collection_due(in_use, tiny_parse_in_use_trigger_for_mode()) + } else { + tiny_parse_pressure_due(in_use, tiny_parse_in_use_trigger_for_mode()) + }; + if !due { return; } diag_tiny_parse_forced_collection("parse_boundary", in_use); @@ -1609,8 +1664,8 @@ pub fn gc_schedule_parse_boundary_collection_if_pressure() { return; } // #9831: priced the same way as the post-parse guard above — see - // `tiny_parse_pressure_due_with`. - if !tiny_parse_pressure_due( + // `tiny_parse_pressure_due_with` — plus the young generation's own cap. + if !tiny_parse_generational_collection_due( crate::arena::arena_in_use_bytes(), gc_tiny_parse_in_use_trigger_dyn_bytes(), ) { @@ -2770,6 +2825,14 @@ pub fn gc_check_trigger() { return; } + // This function asks "what is due?" up to three times on the path where + // nothing is (the old-reclaim arm, the nursery arm, the assist gate). Every + // arm that acts returns, so the state each later question sees is the + // state the first one saw, and a repeatable answer can be reused. An + // answer from the #10169 flag branch is re-evaluated, exactly as before. + let mut due_memo = DueTriggerMemo::new(); + let mut due = || due_memo.get(gc_budgeted_due_trigger_eval); + // #5476: a workload that churns *large* temporaries (>16 KB, born directly // in the old arena) grows the old generation without ever exercising the // nursery. Old-gen reclaim pressure schedules a budgeted full cycle that @@ -2824,10 +2887,7 @@ pub fn gc_check_trigger() { // on. Polls off ⇒ the precise path is reached less often ⇒ this arm fires // more often ⇒ the census counter rises. Inert, not unsound. if !gc_budgeted_cycle_active() - && matches!( - gc_budgeted_due_trigger(), - Some(BudgetedGcTrigger::OldReclaim) - ) + && matches!(due(), Some(BudgetedGcTrigger::OldReclaim)) && !GC_OLD_RECLAIM_IN_PROGRESS.with(Cell::get) { let _reentry = OldReclaimReentryGuard::enter(); @@ -2909,7 +2969,7 @@ pub fn gc_check_trigger() { || gc_moving_loop_polls_enabled() || super::roots::registered_root_scanners_block_budgeted_gc()) { - let direct_kind = match gc_budgeted_due_trigger() { + let direct_kind = match due() { // #7909: `YoungScavengeCap` is a nursery-churn trigger exactly like // `ArenaBytes` here — this arm's whole job is to route nursery // pressure to a collection that can actually reclaim it, so the two @@ -3054,7 +3114,7 @@ pub fn gc_check_trigger() { } } - if !gc_budgeted_cycle_active() && gc_budgeted_due_trigger().is_none() { + if !gc_budgeted_cycle_active() && due().is_none() { return; } @@ -3255,6 +3315,45 @@ pub(crate) fn note_young_leaf_born_old() { } pub(super) fn gc_budgeted_due_trigger() -> Option { + gc_budgeted_due_trigger_eval().0 +} + +/// Reuse of a repeatable [`gc_budgeted_due_trigger_eval`] answer across the +/// questions one `gc_check_trigger` call asks. An unrepeatable answer is +/// returned once and the next question evaluates again. +pub(super) struct DueTriggerMemo(Option>); + +impl DueTriggerMemo { + pub(super) const fn new() -> Self { + Self(None) + } + + pub(super) fn get( + &mut self, + eval: impl FnOnce() -> (Option, bool), + ) -> Option { + if let Some(due) = self.0 { + return due; + } + let (due, repeatable) = eval(); + if repeatable { + self.0 = Some(due); + } + due + } +} + +/// [`gc_budgeted_due_trigger`], plus whether evaluating it again with no state +/// changed in between is guaranteed to give the same answer. +/// +/// Only the #10169 flag branch below breaks that: it consumes the flag and +/// returns early, so a second evaluation takes the ordinary path and may +/// answer differently (for example `OldReclaim`). The #8122 census seed does +/// not break it. The seed runs inside the young-cap arm before that arm's +/// comparison, so the first evaluation already compared against the seeded +/// cap; no earlier arm reads the census and the malloc arm after it does not +/// either, and a second evaluation cannot seed again. +pub(super) fn gc_budgeted_due_trigger_eval() -> (Option, bool) { // #10169: a leaf born old under young pressure gives the nursery minor // ONE-TIME priority over old-reclaim, and only while the young generation // is still unmeasured. A young generation that a minor has already @@ -3268,16 +3367,16 @@ pub(super) fn gc_budgeted_due_trigger() -> Option { if GC_YOUNG_LEAF_BORN_OLD.with(Cell::get) { GC_YOUNG_LEAF_BORN_OLD.with(|flag| flag.set(false)); if !super::young_generation_measured_retained() && young_scavenge_cap_due() { - return Some(BudgetedGcTrigger::YoungScavengeCap); + return (Some(BudgetedGcTrigger::YoungScavengeCap), false); } } let old_pending = GC_OLD_RECLAIM_PENDING.with(Cell::get); // #6010: external Map/Set side-buffer bytes escalate to OldReclaim too. - let old_in_use = - old_gen_reclaimable_pressure_bytes().saturating_add(external_side_live_bytes()); + let old_reclaimable = old_gen_reclaimable_pressure_bytes(); + let old_in_use = old_reclaimable.saturating_add(external_side_live_bytes()); let old_baseline = GC_LAST_OLD_RECLAIM_IN_USE_BYTES.with(|bytes| bytes.get()); if old_pending || old_reclaim_pressure_due(old_in_use, old_baseline) { - return Some(BudgetedGcTrigger::OldReclaim); + return (Some(BudgetedGcTrigger::OldReclaim), true); } // Two separately-scoped arena arms (see `young_scavenge_cap_due` for why @@ -3286,19 +3385,21 @@ pub(super) fn gc_budgeted_due_trigger() -> Option { // only. let total = crate::arena::arena_total_bytes(); if total >= next_arena_trigger_base() { - return Some(BudgetedGcTrigger::ArenaBytes); + return (Some(BudgetedGcTrigger::ArenaBytes), true); } - if young_scavenge_cap_due() { - return Some(BudgetedGcTrigger::YoungScavengeCap); + // Old-gen is untouched since the read above: the arms in between only + // read, and the census seed walks the young generation. + if young_scavenge_cap_due_with_old_reclaimable(|| old_reclaimable) { + return (Some(BudgetedGcTrigger::YoungScavengeCap), true); } let malloc_count = malloc_object_count(); let next_malloc_trigger = GC_NEXT_MALLOC_TRIGGER.with(|c| c.get()); if malloc_count >= next_malloc_trigger { - return Some(BudgetedGcTrigger::MallocCount); + return (Some(BudgetedGcTrigger::MallocCount), true); } - None + (None, true) } /// Phase 1 of the moving-GC project: run a copying (moving) minor at a @@ -3876,6 +3977,47 @@ fn gc_start_budgeted_cycle_for_pressure(progress_kind: GcProgressKind) -> Option }) } +/// What one budgeted step decided, without the debt figures. +/// +/// The step machinery returns this; only callers that hand a +/// [`JsGcStepResult`] to someone attach the debt, through +/// [`GcStepReport::with_debt`]. The runtime's own polls (regex quanta, the +/// microtask pump, the event loop) discard the result, and a +/// `GcDebtSnapshot` costs a second evaluation of the nursery cap, the arena +/// trigger and the old-reclaim band on every poll that finds nothing due. +/// +/// Attaching the debt after the step returns reads the same values it used to +/// read inside the step: the snapshot was always the last thing a step +/// computed, and the only work between that point and the caller is dropping +/// `BudgetedGcStepGuard`, which clears a flag the snapshot does not read. +#[derive(Clone, Copy, Debug, PartialEq, Eq)] +pub(crate) struct GcStepReport { + pub(crate) status: u32, + phase: u32, + collection_kind: u32, + trigger_kind: u32, + active: bool, + completed: bool, +} + +impl GcStepReport { + /// The FFI result, with the debt measured now. + pub(crate) fn with_debt(self) -> JsGcStepResult { + let debt = GcDebtSnapshot::current(); + JsGcStepResult { + status: self.status, + phase: self.phase, + collection_kind: self.collection_kind, + trigger_kind: self.trigger_kind, + active: u32::from(self.active), + completed: u32::from(self.completed), + arena_debt_bytes: debt.arena_debt_bytes, + malloc_debt_objects: debt.malloc_debt_objects, + old_reclaim_debt_bytes: debt.old_reclaim_debt_bytes, + } + } +} + fn gc_step_result( status: u32, phase: u32, @@ -3883,26 +4025,22 @@ fn gc_step_result( trigger_kind: u32, active: bool, completed: bool, -) -> JsGcStepResult { - let debt = GcDebtSnapshot::current(); - JsGcStepResult { +) -> GcStepReport { + GcStepReport { status, phase, collection_kind, trigger_kind, - active: u32::from(active), - completed: u32::from(completed), - arena_debt_bytes: debt.arena_debt_bytes, - malloc_debt_objects: debt.malloc_debt_objects, - old_reclaim_debt_bytes: debt.old_reclaim_debt_bytes, + active, + completed, } } -fn gc_idle_step_result() -> JsGcStepResult { +fn gc_idle_step_result() -> GcStepReport { gc_step_result(JS_GC_STEP_STATUS_IDLE, 0, 0, 0, false, false) } -fn gc_cycle_step_result(status: u32, cycle: &BudgetedGcCycle, completed: bool) -> JsGcStepResult { +fn gc_cycle_step_result(status: u32, cycle: &BudgetedGcCycle, completed: bool) -> GcStepReport { gc_step_result( status, cycle.state.phase().ffi_code(), @@ -3913,7 +4051,7 @@ fn gc_cycle_step_result(status: u32, cycle: &BudgetedGcCycle, completed: bool) - ) } -fn gc_budgeted_status_result() -> JsGcStepResult { +fn gc_budgeted_status_result() -> GcStepReport { if !gc_budgeted_cycle_active() { return gc_idle_step_result(); } @@ -3932,7 +4070,7 @@ fn gc_budgeted_status_result() -> JsGcStepResult { } } -fn gc_budgeted_skipped_result() -> JsGcStepResult { +fn gc_budgeted_skipped_result() -> GcStepReport { if !gc_budgeted_cycle_active() { return gc_step_result(JS_GC_STEP_STATUS_SKIPPED, 0, 0, 0, false, false); } @@ -3945,7 +4083,7 @@ fn gc_budgeted_skipped_result() -> JsGcStepResult { }) } -fn gc_finish_budgeted_cycle(mut cycle: BudgetedGcCycle) -> JsGcStepResult { +fn gc_finish_budgeted_cycle(mut cycle: BudgetedGcCycle) -> GcStepReport { let outcome = cycle .state .take_outcome() @@ -3981,7 +4119,7 @@ fn gc_finish_budgeted_cycle(mut cycle: BudgetedGcCycle) -> JsGcStepResult { } enum BudgetedStepOutcome { - Result(JsGcStepResult), + Result(GcStepReport), Completed(BudgetedGcCycle), } @@ -4018,7 +4156,7 @@ pub(super) fn gc_drain_active_budgeted_cycle() { } } -fn gc_budgeted_step_work_units_inner(work_units: usize) -> JsGcStepResult { +fn gc_budgeted_step_work_units_inner(work_units: usize) -> GcStepReport { gc_budgeted_step_work_units_inner_with_progress(work_units, GcProgressKind::NormalIncremental) } @@ -4056,7 +4194,7 @@ pub(super) fn gc_idle_reclaim_try_start() -> bool { /// cycle stops reporting `ACTIVE`. Never starts a cycle of its own — when none /// is active the stepper's pressure check runs exactly as it would at any host /// safepoint. -pub(super) fn gc_idle_reclaim_step(budget_us: u64) -> JsGcStepResult { +pub(super) fn gc_idle_reclaim_step(budget_us: u64) -> GcStepReport { let start = Instant::now(); let mut result = gc_budgeted_step_work_units_inner(GC_NORMAL_INCREMENTAL_WORK_UNITS); while result.status == JS_GC_STEP_STATUS_ACTIVE @@ -4083,7 +4221,7 @@ fn defer_nursery_cap_to_precise_safepoint() { fn gc_budgeted_step_work_units_inner_with_progress( work_units: usize, start_progress_kind: GcProgressKind, -) -> JsGcStepResult { +) -> GcStepReport { if work_units == 0 { return gc_budgeted_status_result(); } @@ -4095,13 +4233,34 @@ fn gc_budgeted_step_work_units_inner_with_progress( return gc_budgeted_skipped_result(); }; - if !gc_budgeted_cycle_active() { + // The common host poll: no cycle, nothing due. Everything past this point + // (starting and stepping a cycle) lives out of line, so that poll does not + // pay the multi-kilobyte frame the cycle machinery needs. `_guard` stays + // held across the call, exactly as when the code was inline. + let due = if gc_budgeted_cycle_active() { + None + } else { let Some(due) = gc_budgeted_due_trigger() else { super::instruments::note_budgeted_step_skip( super::instruments::BudgetedStepSkip::NoTrigger, ); return gc_idle_step_result(); }; + Some(due) + }; + gc_budgeted_start_or_step(due, work_units, start_progress_kind) +} + +/// The part of [`gc_budgeted_step_work_units_inner_with_progress`] that starts +/// a cycle for `due` (when `Some`, i.e. no cycle was active and a trigger was +/// due) and steps the active cycle. The caller holds `BudgetedGcStepGuard`. +#[inline(never)] +fn gc_budgeted_start_or_step( + due: Option, + work_units: usize, + start_progress_kind: GcProgressKind, +) -> GcStepReport { + if let Some(due) = due { if due == BudgetedGcTrigger::YoungScavengeCap && start_progress_kind.is_budgeted() { // ★ #7909. Starting a budgeted cycle here is strictly worse than // starting nothing, and it is self-sustaining. @@ -4222,11 +4381,25 @@ fn gc_budgeted_step_work_units_inner_with_progress( fn gc_mutator_assist_step_work_units_inner_with_progress( work_units: usize, start_progress_kind: GcProgressKind, -) -> JsGcStepResult { +) -> GcStepReport { gc_budgeted_step_work_units_inner_with_progress(work_units, start_progress_kind) } +/// A host safepoint that reports what it did, debt included. For callers +/// that read the result: `js_gc_safepoint` and tests. pub(crate) fn gc_runtime_safepoint() -> JsGcStepResult { + gc_runtime_safepoint_report().with_debt() +} + +/// The runtime's own safepoint poll (regex quanta, the microtask pump, the +/// event loop). Makes exactly the decisions [`gc_runtime_safepoint`] makes and +/// skips only the debt snapshot, which none of these callers reads. +#[inline] +pub(crate) fn gc_runtime_safepoint_poll() { + let _ = gc_runtime_safepoint_report(); +} + +fn gc_runtime_safepoint_report() -> GcStepReport { let budget = gc_progress_contract().budget_for(GcProgressKind::NormalIncremental); let Some(work_units) = budget.work_units else { return gc_budgeted_status_result(); @@ -4246,14 +4419,14 @@ fn write_gc_step_result(out: *mut JsGcStepResult, result: JsGcStepResult) -> u32 #[no_mangle] pub extern "C" fn js_gc_step_work_units(work_units: u64, out: *mut JsGcStepResult) -> u32 { let work_units = usize::try_from(work_units).unwrap_or(usize::MAX); - let result = gc_budgeted_step_work_units_inner(work_units); + let result = gc_budgeted_step_work_units_inner(work_units).with_debt(); write_gc_step_result(out, result) } #[no_mangle] pub extern "C" fn js_gc_step_us(budget_us: u64, out: *mut JsGcStepResult) -> u32 { if budget_us == 0 { - let result = gc_budgeted_status_result(); + let result = gc_budgeted_status_result().with_debt(); return write_gc_step_result(out, result); } @@ -4264,12 +4437,12 @@ pub extern "C" fn js_gc_step_us(budget_us: u64, out: *mut JsGcStepResult) -> u32 { result = gc_budgeted_step_work_units_inner(1); } - write_gc_step_result(out, result) + write_gc_step_result(out, result.with_debt()) } #[no_mangle] pub extern "C" fn js_gc_step_status(out: *mut JsGcStepResult) -> u32 { - let result = gc_budgeted_status_result(); + let result = gc_budgeted_status_result().with_debt(); write_gc_step_result(out, result) } diff --git a/crates/perry-runtime/src/gc/roots.rs b/crates/perry-runtime/src/gc/roots.rs index 661431c69b..dd7bc06de8 100644 --- a/crates/perry-runtime/src/gc/roots.rs +++ b/crates/perry-runtime/src/gc/roots.rs @@ -103,8 +103,10 @@ pub(super) struct MutableRootScannerEntry { #[derive(Clone, Copy, Debug, Eq, PartialEq)] pub(super) enum RuntimeHandleSlot { Nanbox(u64), - RawTagged { addr: usize, tag: u64 }, HeapWord(u64), + RawPointer(usize), + RawString(usize), + RawBigInt(usize), } crate::perry_thread_local! { diff --git a/crates/perry-runtime/src/gc/roots/runtime_handles.rs b/crates/perry-runtime/src/gc/roots/runtime_handles.rs index 503d47c1fb..b3ea2b8d6b 100644 --- a/crates/perry-runtime/src/gc/roots/runtime_handles.rs +++ b/crates/perry-runtime/src/gc/roots/runtime_handles.rs @@ -1,10 +1,21 @@ use super::*; +#[cfg(not(any(target_os = "android", target_env = "ohos")))] +mod stack; +#[cfg(any(target_os = "android", target_env = "ohos"))] +#[path = "runtime_handles/stack_os_tls.rs"] +mod stack; +#[cfg(test)] +mod tests; +use stack::{RuntimeHandleStack, StackRef}; + struct RuntimeHandleStackHotGuard; impl Drop for RuntimeHandleStackHotGuard { fn drop(&mut self) { crate::tls_hot::unpublish_runtime_handle_stack(); + #[cfg(not(any(target_os = "android", target_env = "ohos")))] + RUNTIME_HANDLE_STACK.with(RuntimeHandleStack::release); } } @@ -12,9 +23,10 @@ thread_local! { /// This stays a raw `thread_local!` as the initialization fallback for its /// named `HotTls` slot. Going through `perry_thread_local!` here would call /// `hot()` while `HotTls::fill` is still resolving this address (#9183). - static RUNTIME_HANDLE_STACK: RefCell> = const { RefCell::new(Vec::new()) }; - /// Registered after the stack itself, so reverse-order TLS destruction - /// clears the named cache pointer before the stack storage is destroyed. + static RUNTIME_HANDLE_STACK: RuntimeHandleStack = const { RuntimeHandleStack::new() }; + /// Unpublishes the cache at teardown. Native TLS registers this owner + /// before allocation and releases the manual buffer here; OS-backed TLS keeps + /// the original Vec owner in RUNTIME_HANDLE_STACK instead. static RUNTIME_HANDLE_STACK_HOT_GUARD: RuntimeHandleStackHotGuard = const { RuntimeHandleStackHotGuard }; } @@ -28,25 +40,38 @@ pub(crate) fn runtime_handle_stack_hot_addr() -> *mut u8 { addr } -/// Borrow this thread's transient-handle stack. +/// Resolve this thread's transient-handle metadata, without initializing HOT. /// /// On Apple aarch64 the steady-state path reads the address from an already /// published `HotTls`. During `HotTls::fill` (and on every other target) it /// uses the ordinary thread-local directly, so opening a handle scope cannot /// recursively initialize the cache (#9183). #[inline(always)] -fn with_runtime_handle_stack(f: impl FnOnce(&RefCell>) -> R) -> R { +#[cfg(not(any(target_os = "android", target_env = "ohos")))] +fn runtime_handle_stack() -> StackRef { if let Some(hot) = crate::tls_hot::hot_if_published() { let stack = hot.runtime_handle_stack.get(); - if stack.is_null() { - return RUNTIME_HANDLE_STACK.with(f); + if !stack.is_null() { + // SAFETY: fill publishes this thread's non-dropping metadata. + // The buffer owner clears it before freeing the allocation. + return unsafe { &*(stack as *const RuntimeHandleStack) }; } - // SAFETY: `fill` stores this thread's `RUNTIME_HANDLE_STACK` address - // before publishing the cache, and the address is stable for the - // lifetime of the thread. - return f(unsafe { &*(stack as *const RefCell>) }); } - RUNTIME_HANDLE_STACK.with(f) + RUNTIME_HANDLE_STACK.with(|stack| { + // SAFETY: the metadata is const-initialized and has no Drop. Its + // cells remain valid throughout thread teardown. Cell is !Sync, so + // scopes and handles carrying this reference cannot leave the thread. + unsafe { &*(stack as *const RuntimeHandleStack) } + }) +} + +// Rust's Android/HarmonyOS OS-TLS backend frees the metadata even when T has no Drop. +// Keep the original scoped lookup there; a token never borrows TLS storage +// across a callback or a later destructor. +#[cfg(any(target_os = "android", target_env = "ohos"))] +#[inline(always)] +fn runtime_handle_stack() -> StackRef { + StackRef::new() } /// Scoped owner for transient runtime handles. @@ -56,26 +81,26 @@ fn with_runtime_handle_stack(f: impl FnOnce(&RefCell>) /// scope removes every handle created from it. pub struct RuntimeHandleScope { pub(super) base: usize, + stack: StackRef, } impl RuntimeHandleScope { #[inline] pub fn new() -> Self { - let base = with_runtime_handle_stack(|stack| stack.borrow().len()); - Self { base } + let stack = runtime_handle_stack(); + Self { + base: stack.len(), + stack, + } } #[inline] pub(super) fn push<'scope>(&'scope self, slot: RuntimeHandleSlot) -> RuntimeHandle<'scope> { runtime_handle_slot_write_barrier(slot); - let index = with_runtime_handle_stack(|stack| { - let mut stack = stack.borrow_mut(); - let index = stack.len(); - stack.push(slot); - index - }); + let index = self.stack.push(slot); RuntimeHandle { index, + stack: self.stack, _scope: PhantomData, } } @@ -127,40 +152,33 @@ impl RuntimeHandleScope { #[inline] pub fn root_raw_mut_ptr<'scope, T>(&'scope self, ptr: *mut T) -> RuntimeHandle<'scope> { - self.push(RuntimeHandleSlot::RawTagged { - addr: ptr as usize, - tag: POINTER_TAG, - }) + self.push(RuntimeHandleSlot::RawPointer(ptr as usize)) } #[inline] pub fn root_raw_const_ptr<'scope, T>(&'scope self, ptr: *const T) -> RuntimeHandle<'scope> { - self.push(RuntimeHandleSlot::RawTagged { - addr: ptr as usize, - tag: POINTER_TAG, - }) + self.push(RuntimeHandleSlot::RawPointer(ptr as usize)) } pub fn root_string_ptr<'scope>( &'scope self, ptr: *const crate::StringHeader, ) -> RuntimeHandle<'scope> { - self.push(RuntimeHandleSlot::RawTagged { - addr: ptr as usize, - tag: STRING_TAG, - }) + self.push(RuntimeHandleSlot::RawString(ptr as usize)) } pub fn root_bigint_ptr<'scope, T>(&'scope self, ptr: *const T) -> RuntimeHandle<'scope> { - self.push(RuntimeHandleSlot::RawTagged { - addr: ptr as usize, - tag: BIGINT_TAG, - }) + self.push(RuntimeHandleSlot::RawBigInt(ptr as usize)) } #[cfg(test)] pub(crate) fn active_len_for_tests() -> usize { - with_runtime_handle_stack(|stack| stack.borrow().len()) + runtime_handle_stack().len() + } + + #[cfg(test)] + pub(crate) fn capacity_for_tests() -> usize { + runtime_handle_stack().capacity() } } @@ -168,22 +186,37 @@ impl RuntimeHandleScope { /// Rust frames. `longjmp` skips `RuntimeHandleScope::drop`, so exception /// unwinding restores this depth explicitly. pub(crate) fn runtime_handle_stack_savepoint() -> usize { - with_runtime_handle_stack(|stack| stack.borrow().len()) + runtime_handle_stack().len() } /// Discard transient roots owned by Rust frames skipped by a JS exception. pub(crate) fn runtime_handle_stack_restore(savepoint: usize) { - with_runtime_handle_stack(|stack| stack.borrow_mut().truncate(savepoint)); + runtime_handle_stack().truncate(savepoint); } -#[inline] +#[inline(always)] fn runtime_handle_slot_write_barrier(slot: RuntimeHandleSlot) { + // Root stores shade only during incremental marking. Keep the idle test + // ahead of kind decoding and the out-of-line active barrier machinery. + if crate::gc::barrier::incremental_mark_barrier_globally_idle() { + return; + } + runtime_handle_slot_write_barrier_active(slot); +} + +#[cold] +#[inline(never)] +fn runtime_handle_slot_write_barrier_active(slot: RuntimeHandleSlot) { match slot { RuntimeHandleSlot::Nanbox(bits) => runtime_write_barrier_root_nanbox(bits), RuntimeHandleSlot::HeapWord(bits) => runtime_write_barrier_root_heap_word(bits), - RuntimeHandleSlot::RawTagged { addr, tag } => { + RuntimeHandleSlot::RawPointer(addr) + | RuntimeHandleSlot::RawString(addr) + | RuntimeHandleSlot::RawBigInt(addr) => { if addr != 0 { - runtime_write_barrier_root_nanbox(tag | (addr as u64 & POINTER_MASK)); + runtime_write_barrier_root_nanbox( + raw_slot_tag(slot) | (addr as u64 & POINTER_MASK), + ); } } } @@ -198,15 +231,14 @@ impl Default for RuntimeHandleScope { impl Drop for RuntimeHandleScope { #[inline] fn drop(&mut self) { - with_runtime_handle_stack(|stack| { - stack.borrow_mut().truncate(self.base); - }); + self.stack.truncate(self.base); } } #[derive(Clone, Copy)] pub struct RuntimeHandle<'scope> { pub(super) index: usize, + stack: StackRef, pub(super) _scope: PhantomData<&'scope RuntimeHandleScope>, } @@ -228,29 +260,36 @@ fn handle_kind_mismatch(expected: &str) -> ! { panic!("runtime handle kind mismatch: expected {expected}"); } +#[inline] +fn raw_slot_tag(slot: RuntimeHandleSlot) -> u64 { + match slot { + RuntimeHandleSlot::RawPointer(_) => POINTER_TAG, + RuntimeHandleSlot::RawString(_) => STRING_TAG, + RuntimeHandleSlot::RawBigInt(_) => BIGINT_TAG, + _ => handle_kind_mismatch("raw pointer"), + } +} + +// Encode the three raw tags in the discriminant, rather than storing a +// separate u64 tag alongside every payload (24 -> 16 bytes on 64-bit hosts). +const _: () = assert!(std::mem::size_of::() == 16); + impl<'scope> RuntimeHandle<'scope> { #[inline] pub(super) fn with_slot(&self, f: impl FnOnce(RuntimeHandleSlot) -> R) -> R { - with_runtime_handle_stack(|stack| { - let stack = stack.borrow(); - let slot = match stack.get(self.index) { - Some(slot) => *slot, - None => handle_used_after_scope(), - }; - f(slot) - }) + let slot = match self.stack.get(self.index) { + Some(slot) => slot, + None => handle_used_after_scope(), + }; + f(slot) } #[inline] pub(super) fn with_slot_mut(&self, f: impl FnOnce(&mut RuntimeHandleSlot) -> R) -> R { - with_runtime_handle_stack(|stack| { - let mut stack = stack.borrow_mut(); - let slot = match stack.get_mut(self.index) { - Some(slot) => slot, - None => handle_used_after_scope(), - }; - f(slot) - }) + let mut slot = self.with_slot(|slot| slot); + let result = f(&mut slot); + self.stack.set(self.index, slot); + result } /// Pass the handle's current mutable pointer to `f` without exposing a @@ -296,9 +335,7 @@ impl<'scope> RuntimeHandle<'scope> { #[inline] pub unsafe fn with_string_bytes(&self, f: impl FnOnce(&[u8]) -> R) -> R { let ptr = self.with_slot(|slot| match slot { - RuntimeHandleSlot::RawTagged { addr, tag } if tag == STRING_TAG => { - addr as *const crate::StringHeader - } + RuntimeHandleSlot::RawString(addr) => addr as *const crate::StringHeader, _ => handle_kind_mismatch("rooted string pointer"), }); let bytes = unsafe { @@ -414,61 +451,66 @@ impl<'scope> RuntimeHandle<'scope> { #[inline] pub fn get_raw_mut_ptr(&self) -> *mut T { self.with_slot(|slot| match slot { - RuntimeHandleSlot::RawTagged { addr, .. } => addr as *mut T, + RuntimeHandleSlot::RawPointer(addr) + | RuntimeHandleSlot::RawString(addr) + | RuntimeHandleSlot::RawBigInt(addr) => addr as *mut T, _ => handle_kind_mismatch("raw pointer"), }) } pub fn set_raw_mut_ptr(&self, ptr: *mut T) { - self.with_slot_mut(|slot| match slot { - RuntimeHandleSlot::RawTagged { addr, tag } => { - *addr = ptr as usize; - if !ptr.is_null() { - runtime_write_barrier_root_nanbox(*tag | (ptr as u64 & POINTER_MASK)); - } - } - _ => panic!("runtime handle kind mismatch: expected raw pointer"), - }); + self.set_raw_const_ptr(ptr.cast_const()); } #[inline] pub fn get_raw_const_ptr(&self) -> *const T { self.with_slot(|slot| match slot { - RuntimeHandleSlot::RawTagged { addr, .. } => addr as *const T, + RuntimeHandleSlot::RawPointer(addr) + | RuntimeHandleSlot::RawString(addr) + | RuntimeHandleSlot::RawBigInt(addr) => addr as *const T, _ => handle_kind_mismatch("raw pointer"), }) } pub fn set_raw_const_ptr(&self, ptr: *const T) { - self.with_slot_mut(|slot| match slot { - RuntimeHandleSlot::RawTagged { addr, tag } => { - *addr = ptr as usize; - if !ptr.is_null() { - runtime_write_barrier_root_nanbox(*tag | (ptr as u64 & POINTER_MASK)); - } + let slot = self.with_slot_mut(|slot| { + match slot { + RuntimeHandleSlot::RawPointer(addr) + | RuntimeHandleSlot::RawString(addr) + | RuntimeHandleSlot::RawBigInt(addr) => *addr = ptr as usize, + _ => handle_kind_mismatch("raw pointer"), } - _ => panic!("runtime handle kind mismatch: expected raw pointer"), + *slot }); + runtime_handle_slot_write_barrier(slot); + } +} + +/// Visit a copy, then commit a relocation to its indexed slot. No reference +/// into the growable buffer crosses a visitor call; the legacy Copy visitor +/// may invoke arbitrary callbacks. Non-rewriting visits must not overwrite a +/// value changed by such a callback. +fn visit_runtime_handle_slot(stack: StackRef, index: usize, visitor: &mut RuntimeRootVisitor<'_>) { + let Some(mut slot) = stack.get(index) else { + return; + }; + let rewritten = match &mut slot { + RuntimeHandleSlot::Nanbox(bits) => visitor.visit_nanbox_u64_slot(bits), + RuntimeHandleSlot::RawPointer(addr) => visitor.visit_tagged_usize_slot(addr, POINTER_TAG), + RuntimeHandleSlot::RawString(addr) => visitor.visit_tagged_usize_slot(addr, STRING_TAG), + RuntimeHandleSlot::RawBigInt(addr) => visitor.visit_tagged_usize_slot(addr, BIGINT_TAG), + RuntimeHandleSlot::HeapWord(bits) => visitor.visit_heap_word_u64_slot(bits), + }; + if rewritten { + stack.set(index, slot); } } pub(crate) fn scan_runtime_handle_roots_mut(visitor: &mut RuntimeRootVisitor<'_>) { - with_runtime_handle_stack(|stack| { - let mut stack = stack.borrow_mut(); - for slot in stack.iter_mut() { - match slot { - RuntimeHandleSlot::Nanbox(bits) => { - visitor.visit_nanbox_u64_slot(bits); - } - RuntimeHandleSlot::RawTagged { addr, tag } => { - visitor.visit_tagged_usize_slot(addr, *tag); - } - RuntimeHandleSlot::HeapWord(bits) => { - visitor.visit_heap_word_u64_slot(bits); - } - } - } - }); + let stack = runtime_handle_stack(); + for index in 0..stack.len() { + visit_runtime_handle_slot(stack, index, visitor); + } } #[derive(Default)] @@ -488,25 +530,13 @@ pub(crate) fn scan_runtime_handle_roots_mut_step( let state = state .downcast_mut::() .expect("runtime handle root scanner state type"); - with_runtime_handle_stack(|stack| { - let mut stack = stack.borrow_mut(); - while *remaining > 0 && state.cursor < stack.len() { - match &mut stack[state.cursor] { - RuntimeHandleSlot::Nanbox(bits) => { - visitor.visit_nanbox_u64_slot(bits); - } - RuntimeHandleSlot::RawTagged { addr, tag } => { - visitor.visit_tagged_usize_slot(addr, *tag); - } - RuntimeHandleSlot::HeapWord(bits) => { - visitor.visit_heap_word_u64_slot(bits); - } - } - state.cursor += 1; - *remaining -= 1; - } - state.cursor >= stack.len() - }) + let stack = runtime_handle_stack(); + while *remaining > 0 && state.cursor < stack.len() { + visit_runtime_handle_slot(stack, state.cursor, visitor); + state.cursor += 1; + *remaining -= 1; + } + state.cursor >= stack.len() } // --------------------------------------------------------------------------- @@ -526,7 +556,7 @@ pub(crate) fn scan_runtime_handle_roots_mut_step( #[no_mangle] pub extern "C" fn js_ffi_root_scope_enter() -> usize { - with_runtime_handle_stack(|stack| stack.borrow().len()) + runtime_handle_stack().len() } /// Root a raw heap ADDRESS (e.g. an `i64` closure pointer from an ext @@ -535,20 +565,15 @@ pub extern "C" fn js_ffi_root_scope_enter() -> usize { pub extern "C" fn js_ffi_root_push_heap_addr(addr: u64) -> usize { let slot = RuntimeHandleSlot::HeapWord(addr); runtime_handle_slot_write_barrier(slot); - with_runtime_handle_stack(|stack| { - let mut stack = stack.borrow_mut(); - let index = stack.len(); - stack.push(slot); - index - }) + runtime_handle_stack().push(slot) } #[no_mangle] pub extern "C" fn js_ffi_root_get_heap_addr(index: usize) -> u64 { - with_runtime_handle_stack(|stack| match stack.borrow().get(index) { - Some(RuntimeHandleSlot::HeapWord(bits)) => *bits, + match runtime_handle_stack().get(index) { + Some(RuntimeHandleSlot::HeapWord(bits)) => bits, _ => 0, - }) + } } /// Root a NaN-boxed VALUE (string/buffer/object handed to callbacks). @@ -556,23 +581,18 @@ pub extern "C" fn js_ffi_root_get_heap_addr(index: usize) -> u64 { pub extern "C" fn js_ffi_root_push_nanbox(bits: u64) -> usize { let slot = RuntimeHandleSlot::Nanbox(bits); runtime_handle_slot_write_barrier(slot); - with_runtime_handle_stack(|stack| { - let mut stack = stack.borrow_mut(); - let index = stack.len(); - stack.push(slot); - index - }) + runtime_handle_stack().push(slot) } #[no_mangle] pub extern "C" fn js_ffi_root_get_nanbox(index: usize) -> u64 { - with_runtime_handle_stack(|stack| match stack.borrow().get(index) { - Some(RuntimeHandleSlot::Nanbox(bits)) => *bits, + match runtime_handle_stack().get(index) { + Some(RuntimeHandleSlot::Nanbox(bits)) => bits, _ => 0, - }) + } } #[no_mangle] pub extern "C" fn js_ffi_root_scope_exit(base: usize) { - with_runtime_handle_stack(|stack| stack.borrow_mut().truncate(base)); + runtime_handle_stack().truncate(base); } diff --git a/crates/perry-runtime/src/gc/roots/runtime_handles/stack.rs b/crates/perry-runtime/src/gc/roots/runtime_handles/stack.rs new file mode 100644 index 0000000000..b3a09d6845 --- /dev/null +++ b/crates/perry-runtime/src/gc/roots/runtime_handles/stack.rs @@ -0,0 +1,127 @@ +use super::{RuntimeHandleSlot, RUNTIME_HANDLE_STACK_HOT_GUARD}; +use std::alloc::{alloc, dealloc, handle_alloc_error, realloc, Layout}; +use std::cell::Cell; + +/// Non-dropping metadata: ELF can resolve this TLS without a destructor-init +/// check. The separate hot guard owns the allocation and clears these cells +/// before freeing it. Scopes can therefore cache a reference to the metadata, +/// including in a TLS destructor that runs after the buffer has been freed. +/// Cell makes the scope and its borrowed handles thread-bound. +pub(super) struct RuntimeHandleStack { + data: Cell<*mut RuntimeHandleSlot>, + top: Cell, + capacity: Cell, +} + +pub(super) type StackRef = &'static RuntimeHandleStack; + +impl RuntimeHandleStack { + pub(super) const fn new() -> Self { + Self { + data: Cell::new(std::ptr::null_mut()), + top: Cell::new(0), + capacity: Cell::new(0), + } + } + + #[inline(always)] + pub(super) fn len(&self) -> usize { + self.top.get() + } + + #[inline(always)] + pub(super) fn truncate(&self, base: usize) { + // Restore can precede a surviving scope's Drop. Never grow the live + // prefix back to that scope's now-obsolete base (Vec::truncate semantics). + self.top.set(self.top.get().min(base)); + } + + #[inline(always)] + pub(super) fn push(&self, slot: RuntimeHandleSlot) -> usize { + let index = self.top.get(); + if index == self.capacity.get() { + self.grow(); + } + // SAFETY: grow guarantees capacity > index. Slots are Copy, so a + // truncated slot needs no drop before reuse. Publish only after write. + unsafe { self.data.get().add(index).write(slot) }; + self.top.set(index + 1); + index + } + + #[inline(always)] + pub(super) fn get(&self, index: usize) -> Option { + if index >= self.top.get() { + return None; + } + // SAFETY: precisely [0, top) is initialized. No reference into the + // buffer escapes; a callback may grow or scan the stack after this copy. + Some(unsafe { self.data.get().add(index).read() }) + } + + #[inline(always)] + pub(super) fn set(&self, index: usize, slot: RuntimeHandleSlot) { + if index >= self.top.get() { + super::handle_used_after_scope(); + } + // SAFETY: the checked live index is initialized. Reload the base: a + // visitor called between get and set may have reallocated the buffer. + unsafe { self.data.get().add(index).write(slot) }; + } + + #[cold] + #[inline(never)] + fn grow(&self) { + // Register the owner before allocating. During late TLS teardown this + // also rejects resurrection after its destructor has already run. + RUNTIME_HANDLE_STACK_HOT_GUARD.with(|_| {}); + let old_capacity = self.capacity.get(); + let capacity = old_capacity + .checked_mul(2) + .expect("runtime handle stack capacity overflow") + .max(4); + let layout = Layout::array::(capacity) + .expect("runtime handle stack allocation overflow"); + // SAFETY: data belongs exclusively to this thread and was allocated + // with the matching old layout. Rust allocation does not run Perry GC. + let data = unsafe { + if old_capacity == 0 { + alloc(layout) + } else { + realloc( + self.data.get().cast(), + Layout::array::(old_capacity).unwrap(), + layout.size(), + ) + } + }; + if data.is_null() { + handle_alloc_error(layout); + } + self.data.set(data.cast()); + self.capacity.set(capacity); + } + + pub(super) fn release(&self) { + let capacity = self.capacity.replace(0); + let data = self.data.replace(std::ptr::null_mut()); + self.top.set(0); + if capacity != 0 { + // SAFETY: the guard releases its one allocation once, after + // unpublishing the cache. No references into the buffer exist. + unsafe { + dealloc( + data.cast(), + Layout::array::(capacity).unwrap(), + ); + } + } + } + + #[cfg(test)] + pub(super) fn capacity(&self) -> usize { + self.capacity.get() + } +} + +const _: () = assert!(!std::mem::needs_drop::()); diff --git a/crates/perry-runtime/src/gc/roots/runtime_handles/stack_os_tls.rs b/crates/perry-runtime/src/gc/roots/runtime_handles/stack_os_tls.rs new file mode 100644 index 0000000000..b70f97a14a --- /dev/null +++ b/crates/perry-runtime/src/gc/roots/runtime_handles/stack_os_tls.rs @@ -0,0 +1,65 @@ +//! Android and HarmonyOS use OS-backed TLS: even a non-Drop value's allocation is freed +//! during teardown. Preserve the original scoped Vec access on these targets. +//! Scopes and handles carry a thread-bound token, never an escaped TLS address. + +use super::{handle_used_after_scope, RuntimeHandleSlot, RUNTIME_HANDLE_STACK}; +use std::cell::RefCell; +use std::marker::PhantomData; + +pub(super) struct RuntimeHandleStack(RefCell>); + +impl RuntimeHandleStack { + pub(super) const fn new() -> Self { + Self(RefCell::new(Vec::new())) + } +} + +#[derive(Clone, Copy)] +pub(super) struct StackRef(PhantomData<*mut ()>); + +impl StackRef { + pub(super) const fn new() -> Self { + Self(PhantomData) + } + + #[inline] + pub(super) fn len(self) -> usize { + RUNTIME_HANDLE_STACK.with(|stack| stack.0.borrow().len()) + } + + #[inline] + pub(super) fn truncate(self, base: usize) { + RUNTIME_HANDLE_STACK.with(|stack| stack.0.borrow_mut().truncate(base)); + } + + #[inline] + pub(super) fn push(self, slot: RuntimeHandleSlot) -> usize { + RUNTIME_HANDLE_STACK.with(|stack| { + let mut slots = stack.0.borrow_mut(); + let index = slots.len(); + slots.push(slot); + index + }) + } + + #[inline] + pub(super) fn get(self, index: usize) -> Option { + RUNTIME_HANDLE_STACK.with(|stack| stack.0.borrow().get(index).copied()) + } + + #[inline] + pub(super) fn set(self, index: usize, slot: RuntimeHandleSlot) { + RUNTIME_HANDLE_STACK.with(|stack| { + let mut slots = stack.0.borrow_mut(); + let Some(target) = slots.get_mut(index) else { + handle_used_after_scope(); + }; + *target = slot; + }); + } + + #[cfg(test)] + pub(super) fn capacity(self) -> usize { + RUNTIME_HANDLE_STACK.with(|stack| stack.0.borrow().capacity()) + } +} diff --git a/crates/perry-runtime/src/gc/roots/runtime_handles/tests.rs b/crates/perry-runtime/src/gc/roots/runtime_handles/tests.rs new file mode 100644 index 0000000000..96e32046cd --- /dev/null +++ b/crates/perry-runtime/src/gc/roots/runtime_handles/tests.rs @@ -0,0 +1,123 @@ +use super::*; + +#[test] +fn copy_visitor_can_read_update_and_grow_the_handle_stack() { + let scope = RuntimeHandleScope::new(); + let first = scope.root_nanbox_f64(11.0); + let second = scope.root_nanbox_f64(12.0); + let count = runtime_handle_stack().capacity() + 1; + let mut visits = 0; + let mut callback = |value| { + visits += 1; + if value == 11.0 { + first.set_nanbox_f64(21.0); + let nested = RuntimeHandleScope::new(); + for i in 0..count { + let _ = nested.root_nanbox_f64(i as f64); + } + assert_eq!(second.get_nanbox_f64(), 12.0); + } + }; + scan_runtime_handle_roots_mut(&mut RuntimeRootVisitor::for_copy(&mut callback)); + assert_eq!(visits, 2); + assert_eq!( + first.get_nanbox_f64(), + 21.0, + "a non-rewriting visit must not undo callback updates" + ); + assert_eq!(RuntimeHandleScope::active_len_for_tests(), scope.base + 2); +} + +#[test] +fn ffi_indices_and_kind_checks_survive_growth_and_restore() { + let base = js_ffi_root_scope_enter(); + let nanbox = js_ffi_root_push_nanbox(13.0f64.to_bits()); + let heap_word = js_ffi_root_push_heap_addr(0); + for i in 0..257 { + js_ffi_root_push_nanbox((i as f64).to_bits()); + } + assert_eq!(js_ffi_root_get_nanbox(nanbox), 13.0f64.to_bits()); + assert_eq!(js_ffi_root_get_heap_addr(heap_word), 0); + assert_eq!(js_ffi_root_get_heap_addr(nanbox), 0); + assert_eq!(js_ffi_root_get_nanbox(heap_word), 0); + js_ffi_root_scope_exit(usize::MAX); + assert_eq!(js_ffi_root_scope_enter(), base + 259); + js_ffi_root_scope_exit(base); + assert_eq!(js_ffi_root_scope_enter(), base); + assert_eq!(js_ffi_root_get_nanbox(nanbox), 0); + + let scope = RuntimeHandleScope::new(); + let root = scope.root_nanbox_f64(31.0); + assert!(std::panic::catch_unwind(std::panic::AssertUnwindSafe(|| { + root.get_heap_word_u64() + })) + .is_err()); + runtime_handle_stack_restore(base); + assert!( + std::panic::catch_unwind(std::panic::AssertUnwindSafe(|| { root.get_nanbox_f64() })) + .is_err() + ); +} + +#[test] +#[cfg(not(any(target_os = "android", target_env = "ohos")))] +fn handle_storage_teardown_clears_cache_before_late_scope_drop() { + struct LateScope(RefCell>); + impl Drop for LateScope { + fn drop(&mut self) { + assert_eq!(runtime_handle_stack().len(), 0); + assert_eq!(runtime_handle_stack().capacity(), 0); + assert!(crate::tls_hot::hot().runtime_handle_stack.get().is_null()); + drop(self.0.get_mut().take()); + assert_eq!(runtime_handle_stack().len(), 0); + assert!( + std::panic::catch_unwind(|| { + let scope = RuntimeHandleScope::new(); + let _ = scope.root_nanbox_f64(1.0); + }) + .is_err(), + "released TLS storage must not be resurrected" + ); + } + } + thread_local! { + static BEFORE_BUFFER: LateScope = const { LateScope(RefCell::new(None)) }; + } + std::thread::spawn(|| { + BEFORE_BUFFER.with(|_| {}); + let scope = RuntimeHandleScope::new(); + let root = scope.root_nanbox_f64(7.0); + assert_eq!(root.get_nanbox_f64(), 7.0); + assert!(!crate::tls_hot::hot().runtime_handle_stack.get().is_null()); + BEFORE_BUFFER.with(|late| *late.0.borrow_mut() = Some(scope)); + }) + .join() + .expect("handle teardown thread"); +} + +#[cfg(any(target_os = "android", target_env = "ohos"))] +#[test] +fn late_scope_drop_resolves_os_tls_without_retaining_its_address() { + use std::sync::atomic::{AtomicBool, Ordering}; + static DROPPED: AtomicBool = AtomicBool::new(false); + struct LateScope(RefCell>); + impl Drop for LateScope { + fn drop(&mut self) { + drop(self.0.get_mut().take()); + DROPPED.store(true, Ordering::SeqCst); + } + } + thread_local! { + static BEFORE_BUFFER: LateScope = const { LateScope(RefCell::new(None)) }; + } + std::thread::spawn(|| { + BEFORE_BUFFER.with(|_| {}); + let scope = RuntimeHandleScope::new(); + let root = scope.root_nanbox_f64(7.0); + assert_eq!(root.get_nanbox_f64(), 7.0); + BEFORE_BUFFER.with(|late| *late.0.borrow_mut() = Some(scope)); + }) + .join() + .expect("late OS-TLS scope thread"); + assert!(DROPPED.load(Ordering::SeqCst)); +} diff --git a/crates/perry-runtime/src/gc/tenuring.rs b/crates/perry-runtime/src/gc/tenuring.rs index 72255c0b78..33ba9fdfca 100644 --- a/crates/perry-runtime/src/gc/tenuring.rs +++ b/crates/perry-runtime/src/gc/tenuring.rs @@ -488,6 +488,14 @@ pub(super) fn note_surviving_object_census(moved_bytes: usize, moved_objects: us /// /// Returns without walking when the base cap is not yet half full, when a /// census (either kind) already exists, or when the nursery is empty. +/// Has the object denomination been seeded, by an allocation census or by a +/// copying minor's survivor census? Once true it stays true for the process +/// (tests reset it). +#[inline] +pub(super) fn object_census_seeded() -> bool { + OBJECT_CENSUS_SEEDED.with(Cell::get) +} + pub(super) fn maybe_seed_object_census_from_allocation(from_space_in_use_bytes: usize) { if OBJECT_CENSUS_SEEDED.with(Cell::get) { return; diff --git a/crates/perry-runtime/src/gc/tests/runtime_roots.rs b/crates/perry-runtime/src/gc/tests/runtime_roots.rs index cfa84c979c..0a4f3e5294 100644 --- a/crates/perry-runtime/src/gc/tests/runtime_roots.rs +++ b/crates/perry-runtime/src/gc/tests/runtime_roots.rs @@ -6,6 +6,7 @@ mod bound_method_builder; mod callback_scanners; mod fs_options_object; mod generator_attach_prototype; +mod handle_stack; mod hook_dispatch_handles; mod interned_string_caches; mod iter_result_keys; diff --git a/crates/perry-runtime/src/gc/tests/runtime_roots/handle_stack.rs b/crates/perry-runtime/src/gc/tests/runtime_roots/handle_stack.rs new file mode 100644 index 0000000000..2fc6bb85c0 --- /dev/null +++ b/crates/perry-runtime/src/gc/tests/runtime_roots/handle_stack.rs @@ -0,0 +1,183 @@ +use super::*; + +#[test] +fn handle_stack_growth_preserves_every_moving_root_including_last() { + let _guard = CopyingNurseryTestGuard::new(0); + let _triggers = GcTriggerThresholdTestGuard::suppress_automatic_triggers(); + let _evacuation = ForcedEvacuationTestGuard::on(); + register_runtime_handle_root_scanner_for_tests(); + let scope = RuntimeHandleScope::new(); + let capacity_before = RuntimeHandleScope::capacity_for_tests(); + let count = capacity_before + 257; + let mut roots = Vec::new(); + for i in 0..count { + let text = format!("moving handle payload number {i}"); + let ptr = crate::js_string_from_bytes(text.as_ptr(), text.len() as u32); + roots.push(match i % 3 { + 0 => scope.root_string_ptr(ptr), + 1 => scope.root_nanbox_u64(string_bits(ptr as usize)), + _ => scope.root_heap_word_u64(ptr as u64), + }); + } + assert!(RuntimeHandleScope::capacity_for_tests() > capacity_before); + let address = |i: usize, root: &RuntimeHandle<'_>| match i % 3 { + 0 => root.with_const_ptr(|ptr: *const crate::StringHeader| ptr as usize), + 1 => (root.get_nanbox_u64() & POINTER_MASK) as usize, + _ => root.get_heap_word_u64() as usize, + }; + let before: Vec<_> = roots + .iter() + .enumerate() + .map(|(i, h)| address(i, h)) + .collect(); + gc_collect_minor(); + for (i, root) in roots.iter().enumerate() { + let after = address(i, root); + assert_ne!(before[i], after, "root {i} was not relocated"); + unsafe { + assert_string_bytes( + after as *const crate::StringHeader, + format!("moving handle payload number {i}").as_bytes(), + ); + } + } + // Grow again while these relocated handles remain live, then collect again. + let capacity = RuntimeHandleScope::capacity_for_tests(); + for i in 0..=capacity { + let _ = scope.root_nanbox_f64(i as f64); + } + assert!(RuntimeHandleScope::capacity_for_tests() > capacity); + gc_collect_minor(); + for (i, root) in roots.iter().enumerate() { + unsafe { + assert_string_bytes( + address(i, root) as *const crate::StringHeader, + format!("moving handle payload number {i}").as_bytes(), + ); + } + } +} + +#[test] +fn handle_stack_real_throw_restores_live_outer_scope_and_ffi_roots() { + unsafe extern "C" { + fn js_ffi_root_scope_enter() -> usize; + fn js_ffi_root_push_nanbox(bits: u64) -> usize; + } + let _guard = CopyingNurseryTestGuard::new(0); + let _triggers = GcTriggerThresholdTestGuard::suppress_automatic_triggers(); + let _evacuation = ForcedEvacuationTestGuard::on(); + register_runtime_handle_root_scanner_for_tests(); + let scope = RuntimeHandleScope::new(); + let ptr = crate::js_string_from_bytes(b"outer root across caught throw".as_ptr(), 30); + let root = scope.root_string_ptr(ptr); + let base = RuntimeHandleScope::active_len_for_tests(); + let result: Result<(), f64> = crate::exception::catch_js_throw(|| { + let inner = RuntimeHandleScope::new(); + for i in 0..257 { + let _ = inner.root_nanbox_f64(i as f64); + } + unsafe { + let _ffi_base = js_ffi_root_scope_enter(); + js_ffi_root_push_nanbox(42.0f64.to_bits()); + } + assert!(RuntimeHandleScope::active_len_for_tests() > base); + crate::exception::js_throw(17.0); + }); + assert_eq!(result, Err(17.0)); + assert_eq!(RuntimeHandleScope::active_len_for_tests(), base); + gc_collect_minor(); + root.with_const_ptr(|after: *const crate::StringHeader| { + assert_ne!(ptr, after.cast_mut(), "the surviving outer root must move"); + unsafe { assert_string_bytes(after, b"outer root across caught throw") }; + }); + let tail = scope.root_nanbox_f64(23.0); + assert_eq!(tail.get_nanbox_f64(), 23.0); + assert_eq!(RuntimeHandleScope::active_len_for_tests(), base + 1); +} + +#[test] +fn handle_stack_pushes_and_updates_shade_all_kinds_during_marking() { + let _guard = GcTestIsolationGuard::new(); + clear_marks(); + clear_mark_seeds(); + let pointers: Vec<_> = (0..10) + .map(|_| crate::arena::arena_alloc_gc(64, 8, GC_TYPE_OBJECT)) + .collect(); + let valid_ptrs = build_valid_pointer_set(); + let scope = RuntimeHandleScope::new(); + let mut state = new_runtime_handle_root_scan_state(); + let mut marker = RuntimeRootVisitor::for_mark(&valid_ptrs); + let mut budget = 1; + assert!(scan_runtime_handle_roots_mut_step( + &mut marker, + state.as_mut(), + &mut budget + )); + // The scanner has already passed the current prefix. These pre-existing + // white objects must be shaded by publishing roots, not allocate-black. + let active = IncrementalMarkBarrierTestGuard::new(&valid_ptrs); + let roots = [ + scope.root_nanbox_u64(ptr_bits(pointers[0] as usize)), + scope.root_heap_word_u64(pointers[1] as u64), + scope.root_raw_mut_ptr(pointers[2]), + scope.root_string_ptr(pointers[3].cast()), + scope.root_bigint_ptr(pointers[4]), + ]; + for ptr in &pointers[..5] { + assert_marked_user_ptr(*ptr as usize, "new handle during marking"); + } + roots[0].set_nanbox_u64(ptr_bits(pointers[5] as usize)); + roots[1].set_heap_word_u64(pointers[6] as u64); + roots[2].set_raw_mut_ptr(pointers[7]); + roots[3].set_raw_const_ptr(pointers[8]); + roots[4].set_raw_const_ptr(pointers[9]); + for ptr in &pointers[5..] { + assert_marked_user_ptr(*ptr as usize, "updated handle during marking"); + } + drop(active); + clear_marks(); + let _idle = scope.root_nanbox_u64(ptr_bits(pointers[0] as usize)); + unsafe { + assert_eq!( + (*header_from_user_ptr(pointers[0])).gc_flags & GC_FLAG_MARKED, + 0 + ); + } +} + +#[test] +fn handle_stack_budgeted_scan_resumes_after_growth_and_truncation() { + let _guard = GcTestIsolationGuard::new(); + let scope = RuntimeHandleScope::new(); + let _first = scope.root_nanbox_f64(1.0); + let inner = RuntimeHandleScope::new(); + let _second = inner.root_nanbox_f64(2.0); + let mut seen = Vec::new(); + let mut record = |value| seen.push(value); + let mut visitor = RuntimeRootVisitor::for_copy(&mut record); + let mut state = new_runtime_handle_root_scan_state(); + let mut budget = 1; + assert!(!scan_runtime_handle_roots_mut_step( + &mut visitor, + state.as_mut(), + &mut budget + )); + assert_eq!(budget, 0); + drop(inner); + let count = RuntimeHandleScope::capacity_for_tests() + 1; + for i in 0..count { + let _ = scope.root_nanbox_f64((i + 3) as f64); + } + let mut budget = count + 1; + assert!(scan_runtime_handle_roots_mut_step( + &mut visitor, + state.as_mut(), + &mut budget + )); + assert_eq!(budget, 1); + assert_eq!(seen.len(), count + 1); + assert_eq!(seen[0], 1.0); + assert_eq!(*seen.last().unwrap(), (count + 2) as f64); + assert!(!seen.contains(&2.0)); +} diff --git a/crates/perry-runtime/src/gc/tests/tiny_parse_pressure.rs b/crates/perry-runtime/src/gc/tests/tiny_parse_pressure.rs index 12ea368ffb..e6eedd9dbf 100644 --- a/crates/perry-runtime/src/gc/tests/tiny_parse_pressure.rs +++ b/crates/perry-runtime/src/gc/tests/tiny_parse_pressure.rs @@ -232,3 +232,43 @@ fn a_finished_collection_moves_the_base_to_the_post_collection_reading() { "the base must be the post-collection `arena_in_use_bytes()` reading" ); } + +/// The nursery cap schedules a tiny-parse boundary collection on its own, well +/// below the priced in-use guard, and only while the cap is actually due. +#[test] +fn a_due_nursery_cap_schedules_the_boundary_collection_below_the_in_use_guard() { + use super::super::policy::{ + gc_schedule_parse_boundary_collection_if_pressure, ScavengeNurseryCapTestGuard, + GC_SUPPRESSED_TINY_PARSE_COLLECTION_PENDING, + }; + use super::support::*; + let _isolation = GcTestIsolationGuard::new(); + let _pacing = crate::gc::policy::force_moving_gc_pacing(); + let pending = || GC_SUPPRESSED_TINY_PARSE_COLLECTION_PENDING.with(std::cell::Cell::get); + GC_SUPPRESSED_TINY_PARSE_COLLECTION_PENDING.with(|p| p.set(false)); + let filler = [b'j'; 64]; + crate::string::js_string_from_bytes(filler.as_ptr(), filler.len() as u32); + let in_use = crate::arena::arena_in_use_bytes(); + assert!( + !tiny_parse_pressure_due(in_use, 48 * MB), + "fixture: the priced in-use guard must not be due, or this proves nothing" + ); + + { + let _cap = ScavengeNurseryCapTestGuard::due_at_bytes(usize::MAX); + gc_schedule_parse_boundary_collection_if_pressure(); + assert!( + !pending(), + "neither the guard nor the cap is due: nothing scheduled" + ); + } + { + let _cap = ScavengeNurseryCapTestGuard::due_at_bytes(1); + gc_schedule_parse_boundary_collection_if_pressure(); + assert!( + pending(), + "a due nursery cap schedules the boundary collection" + ); + } + GC_SUPPRESSED_TINY_PARSE_COLLECTION_PENDING.with(|p| p.set(false)); +} diff --git a/crates/perry-runtime/src/gc/tests/young_leaf_route.rs b/crates/perry-runtime/src/gc/tests/young_leaf_route.rs index 2865f520b5..e55674ae49 100644 --- a/crates/perry-runtime/src/gc/tests/young_leaf_route.rs +++ b/crates/perry-runtime/src/gc/tests/young_leaf_route.rs @@ -4,8 +4,8 @@ //! exactly once per leaf, and a measured young generation buys none. use super::super::policy::{ - gc_budgeted_due_trigger, note_young_leaf_born_old, BudgetedGcTrigger, - ScavengeNurseryCapTestGuard, GC_OLD_RECLAIM_PENDING, + gc_budgeted_due_trigger, gc_budgeted_due_trigger_eval, note_young_leaf_born_old, + BudgetedGcTrigger, DueTriggerMemo, ScavengeNurseryCapTestGuard, GC_OLD_RECLAIM_PENDING, }; use super::super::*; use super::support::*; @@ -65,3 +65,74 @@ fn young_leaf_born_old_prioritises_the_nursery_minor_until_measured() { None => clear_young_survival_for_tests(), } } + +/// `gc_check_trigger` reuses a due answer only when evaluating again would give +/// the same one. The leaf priority is the answer that would not: it consumes +/// its flag, so the next evaluation takes the ordinary path. Every other +/// answer, including a plain `OldReclaim` after the flag is gone, repeats. +#[test] +fn only_the_leaf_priority_answer_is_unrepeatable() { + let _isolation = GcTestIsolationGuard::new(); + let _pacing = crate::gc::policy::force_moving_gc_pacing(); + let _triggers = GcTriggerThresholdTestGuard::suppress_automatic_triggers(); + let _cap_due = ScavengeNurseryCapTestGuard::due_at_bytes(1); + let filler = [b'y'; 64]; + crate::string::js_string_from_bytes(filler.as_ptr(), filler.len() as u32); + let previous_survival = last_young_survival_permille(); + GC_OLD_RECLAIM_PENDING.with(|pending| pending.set(true)); + clear_young_survival_for_tests(); + + note_young_leaf_born_old(); + assert_eq!( + gc_budgeted_due_trigger_eval(), + (Some(BudgetedGcTrigger::YoungScavengeCap), false) + ); + assert_eq!( + gc_budgeted_due_trigger_eval(), + (Some(BudgetedGcTrigger::OldReclaim), true) + ); + assert_eq!( + gc_budgeted_due_trigger_eval(), + (Some(BudgetedGcTrigger::OldReclaim), true) + ); + + // The same sequence through the memo `gc_check_trigger` uses: the leaf + // answer is not reused, the answer after it is. + note_young_leaf_born_old(); + let mut memo = DueTriggerMemo::new(); + assert_eq!( + memo.get(gc_budgeted_due_trigger_eval), + Some(BudgetedGcTrigger::YoungScavengeCap) + ); + assert_eq!( + memo.get(gc_budgeted_due_trigger_eval), + Some(BudgetedGcTrigger::OldReclaim) + ); + GC_OLD_RECLAIM_PENDING.with(|pending| pending.set(false)); + assert_eq!( + memo.get(gc_budgeted_due_trigger_eval), + Some(BudgetedGcTrigger::OldReclaim), + "a repeatable answer is reused for the rest of one gc_check_trigger call" + ); + + match previous_survival { + Some(permille) => seed_young_survival_for_tests(permille), + None => clear_young_survival_for_tests(), + } +} + +/// The memo on its own: a repeatable answer is evaluated once, an +/// unrepeatable one is never reused. +#[test] +fn due_trigger_memo_reuses_only_repeatable_answers() { + let mut memo = DueTriggerMemo::new(); + assert_eq!( + memo.get(|| (Some(BudgetedGcTrigger::YoungScavengeCap), false)), + Some(BudgetedGcTrigger::YoungScavengeCap) + ); + assert_eq!(memo.get(|| (None, true)), None); + assert_eq!( + memo.get(|| panic!("a repeatable answer must not be evaluated again")), + None + ); +} diff --git a/crates/perry-runtime/src/json/stringify.rs b/crates/perry-runtime/src/json/stringify.rs index e68a0f40be..f768849c94 100644 --- a/crates/perry-runtime/src/json/stringify.rs +++ b/crates/perry-runtime/src/json/stringify.rs @@ -273,19 +273,17 @@ pub(crate) unsafe fn object_get_to_json(ptr: *const u8) -> Option { let scope = crate::gc::RuntimeHandleScope::new(); let recv_handle = scope.root_nanbox_f64(recv); - let key = js_string_from_bytes(b"toJSON".as_ptr(), 6); + let key = crate::string::canonical_key(b"toJSON"); let key_handle = scope.root_string_ptr(key); - let obj_ptr = recv_handle.get_nanbox_f64(); - let obj_ptr = (obj_ptr.to_bits() & POINTER_MASK) as *const crate::ObjectHeader; let method = key_handle.with_const_ptr(|key: *const crate::string::StringHeader| { - crate::object::js_object_get_field_by_name(obj_ptr, key) + crate::object::native_get::get_by_canonical_key(recv_handle.get_nanbox_f64(), key) }); // Only treat it as toJSON if it actually resolved to a callable closure // (POINTER_TAG + closure). A plain object with no `toJSON`, or a `toJSON` // data field that isn't a function, returns `None` → serialize normally. - let method_bits = method.bits(); + let method_bits = method.to_bits(); if (method_bits & 0xFFFF_0000_0000_0000) != POINTER_TAG { return None; } diff --git a/crates/perry-runtime/src/json/traversal_feedback.rs b/crates/perry-runtime/src/json/traversal_feedback.rs index 55f3ea2d2f..bca5cd65a2 100644 --- a/crates/perry-runtime/src/json/traversal_feedback.rs +++ b/crates/perry-runtime/src/json/traversal_feedback.rs @@ -24,10 +24,16 @@ //! 1.11x -> 0.98x, records_array_8m:scan CPU 0.93x -> 0.77x and RSS 190 -> //! 163 MiB; every other cell unchanged. //! -//! Only element-by-element reads in `lazy_get_rooted` count (the flip, or an -//! in-order read of the last element). Stringify, -//! revivers, array methods and mutation also force materialization, but none of -//! them is evidence that a scan would have been cheaper eagerly. +//! Two kinds of event count as traversal evidence: element-by-element reads in +//! `lazy_get_rooted` (the flip, or an in-order read of the last element), and a +//! compiled loop or indexed read materializing the whole array through +//! `js_array_refresh_local_head` — the element-shape loop clone's preheader +//! (#10171) and the guarded indexed-read repair both do that before a single +//! element is read lazily, so without it a program whose scan loop is served by +//! the clone never produced evidence and paid for the tape AND the eager +//! materialization on every parse. Stringify, revivers, array methods and +//! mutation also force materialization, but none of them is evidence that a +//! scan would have been cheaper eagerly. use std::cell::Cell; @@ -78,6 +84,35 @@ pub(crate) unsafe fn after_cold_read( } } +/// A lazy array was materialized whole by compiled code refreshing its local +/// head (`js_array_refresh_local_head`) — the element-shape loop clone's +/// preheader or a guarded indexed read's repair. Every element is now eagerly +/// built, so the tape this array was parsed onto was wasted: the same evidence +/// as a flip. +pub(crate) fn note_compiled_materialization() { + SCORE.with(|s| s.set(s.get().saturating_add(2).min(SCORE_MAX))); +} + +/// Is `addr` a lazy JSON array whose elements have not been built yet? Read by +/// `js_array_refresh_local_head` before it materializes, so evidence is noted +/// once per array. Its emitters are cold arms that run about once per +/// receiver, so the tracked-header probe costs nothing measurable. +/// +/// # Safety +/// +/// `addr` must be a plausible heap address; the tracked-header read validates +/// ownership before anything is dereferenced. +pub(crate) unsafe fn lazy_array_unmaterialized(addr: usize) -> bool { + let Some(header) = crate::value::addr_class::try_read_tracked_gc_header(addr) else { + return false; + }; + if (*header.as_ptr()).obj_type != crate::gc::GC_TYPE_LAZY_ARRAY { + return false; + } + let lazy = addr as *const crate::json_tape::LazyArrayHeader; + (*lazy).magic == crate::json_tape::LAZY_ARRAY_MAGIC && (*lazy).materialized.is_null() +} + /// Should an otherwise tape-eligible parse go eagerly instead? pub(crate) fn prefer_eager() -> bool { if SCORE.with(Cell::get) < PREFER_EAGER_AT { @@ -96,6 +131,11 @@ pub(crate) fn prefer_eager() -> bool { }) } +#[cfg(test)] +pub(crate) fn score_for_tests() -> u8 { + SCORE.with(Cell::get) +} + #[cfg(test)] pub(crate) fn reset_for_tests() { SCORE.with(|s| s.set(0)); diff --git a/crates/perry-runtime/src/lib.rs b/crates/perry-runtime/src/lib.rs index ce248fe2b2..c540da6335 100644 --- a/crates/perry-runtime/src/lib.rs +++ b/crates/perry-runtime/src/lib.rs @@ -743,7 +743,7 @@ pub(crate) mod stdlib_pump { // #2532/#9696 — drain every initialized extension through the // registry; stdlib deliberately has no per-extension pump arms. run_aux_pumps(); - let _ = crate::gc::gc_runtime_safepoint(); + crate::gc::gc_runtime_safepoint_poll(); } static STDLIB_HAS_ACTIVE_FN: AtomicPtr<()> = AtomicPtr::new(null_mut()); diff --git a/crates/perry-runtime/src/object/field_get_set/get_field_by_name.rs b/crates/perry-runtime/src/object/field_get_set/get_field_by_name.rs index 721af36d12..5032f7d384 100644 --- a/crates/perry-runtime/src/object/field_get_set/get_field_by_name.rs +++ b/crates/perry-runtime/src/object/field_get_set/get_field_by_name.rs @@ -27,6 +27,9 @@ pub extern "C" fn js_object_get_field_by_name( obj: *const ObjectHeader, key: *const crate::StringHeader, ) -> JSValue { + if let Some(value) = unsafe { super::super::native_get::try_data_get_by_name(obj, key) } { + return value; + } // Guard hoisted to the call site: an ordinary key is rejected on a length // compare and one byte here, so the overwhelmingly common property read // makes no call into the private-member path at all. diff --git a/crates/perry-runtime/src/object/field_get_set/has_property.rs b/crates/perry-runtime/src/object/field_get_set/has_property.rs index e107abafc7..0a0be3f051 100644 --- a/crates/perry-runtime/src/object/field_get_set/has_property.rs +++ b/crates/perry-runtime/src/object/field_get_set/has_property.rs @@ -390,6 +390,14 @@ pub extern "C" fn js_object_has_property(obj: f64, key: f64) -> f64 { // semantics. Fixes effect's `Predicate.hasProperty(classObj, TypeId)` // (`isSchema` → `dual` → `transformOrFail`) and `Sym in obj` generally. if unsafe { crate::symbol::js_is_symbol(key) } != 0 { + // Presence must not call a symbol getter or confuse an existing + // undefined-valued property with an absent one. + if unsafe { + crate::symbol::has_own_symbol_property(obj, key) + || crate::symbol::has_declared_prototype_symbol_property(obj, key) + } { + return nanbox_true; + } let v = unsafe { crate::symbol::js_object_get_symbol_property(obj, key) }; return if v.to_bits() != crate::value::TAG_UNDEFINED { nanbox_true diff --git a/crates/perry-runtime/src/object/mod.rs b/crates/perry-runtime/src/object/mod.rs index 2460f50b2f..3870a0d455 100644 --- a/crates/perry-runtime/src/object/mod.rs +++ b/crates/perry-runtime/src/object/mod.rs @@ -135,6 +135,7 @@ pub(crate) mod iterator_prototypes; pub(crate) mod map_set_subclass; mod namespace_create; mod native_call_method; +pub(crate) mod native_get; mod native_module; mod nm_namespace_hooks; pub(crate) use native_module::class_instance_has_member; diff --git a/crates/perry-runtime/src/object/native_get.rs b/crates/perry-runtime/src/object/native_get.rs new file mode 100644 index 0000000000..c674092e5b --- /dev/null +++ b/crates/perry-runtime/src/object/native_get.rs @@ -0,0 +1,171 @@ +//! Callback-free positive Get for ordinary data properties. +//! +//! Every pointer and key borrow below is confined to a lookup that cannot +//! allocate in the JS heap, collect, or invoke user code. A miss is NOT an +//! absent-property verdict: the caller must run the existing generic Get. + +use super::{shapes, ObjectHeader}; +use crate::value::JSValue; + +/// Read using an already materialized property key. String keys, including +/// inline strings, require neither ToPropertyKey nor another key allocation. +#[inline] +pub(crate) unsafe fn try_data_get(receiver: JSValue, key: JSValue) -> Option { + if !receiver.is_pointer() || !key.is_any_string() { + return None; + } + let mut scratch = [0; crate::value::SHORT_STRING_MAX_LEN]; + let bytes = crate::string::js_string_key_bytes(key, &mut scratch)?; + try_data_get_bytes(receiver, bytes) +} + +/// Raw-pointer sibling for the named-field ABI, which accepts both raw and +/// NaN-boxed object pointers. Reject other encodings before touching memory. +#[inline] +pub(crate) unsafe fn try_data_get_by_name( + object: *const ObjectHeader, + key: *const crate::StringHeader, +) -> Option { + if key.is_null() || !crate::value::addr_class::is_plausible_heap_addr(key as usize) { + return None; + } + if (*key).byte_len > (*key).capacity || (*key).byte_len >= 1 << 28 { + return None; + } + let bits = object as u64; + let receiver = match bits >> 48 { + 0 => JSValue::pointer(object as *mut u8), + 0x7FFD => JSValue::from_bits(bits), + _ => return None, + }; + let bytes = + std::slice::from_raw_parts(crate::string::string_data(key), (*key).byte_len as usize); + try_data_get_bytes(receiver, bytes) +} + +/// Borrowed-name API for native callers. The keys lookup is consult-only: +/// it never builds a shape index, materializes a key, or resolves a builtin. +#[inline] +pub(crate) unsafe fn try_data_get_bytes(receiver: JSValue, key: &[u8]) -> Option { + #[cfg(test)] + if FORCE_SLOW.with(|value| value.get()) { + return None; + } + if !receiver.is_pointer() || key.first() == Some(&b'#') || key == b"constructor" { + return None; + } + // Descriptor summaries use the same byte hash. Invalid UTF-8 stays on + // the WTF-8-aware slow path; no String is allocated for ordinary keys. + std::str::from_utf8(key).ok()?; + let accessor_bit = 1u64 << (super::key_bytes_hash(key.as_ptr(), key.len()) & 63); + let mut object = receiver.as_pointer::(); + let mut inherited = false; + for _ in 0..32 { + let addr = object as usize; + // Headerless Buffer/TypedArray/foreign handles must never be read as + // GcHeader-bearing objects. As in the existing own-field fast lane, + // require positive arena membership before inspecting the header. + if !crate::value::addr_class::is_plausible_heap_addr(addr) + || crate::arena::classify_heap_generation(addr) == crate::arena::HeapGeneration::Unknown + { + return None; + } + let header = crate::value::addr_class::try_read_gc_header(addr)?; + if header.obj_type != crate::gc::GC_TYPE_OBJECT + || header.gc_flags & crate::gc::GC_FLAG_FORWARDED != 0 + || header._reserved & crate::gc::OBJ_FLAG_TYPED_ARRAY_PROTO != 0 + { + return None; + } + let descriptor = shapes::object_shape_descriptor(object)?; + if descriptor.object_kind != shapes::ShapeObjectKind::Ordinary { + return None; + } + let class_id = (*object).class_id; + // Positive membership, rather than a blacklist of native class ids. + // Synthetic function/Object.create ids occupy the allocated prefix + // of this counter's range; reserved native ids are outside it. + let synthetic = class_id >= 0x8000_0000 + && class_id < super::NEXT_SYNTHETIC_CLASS_ID.load(std::sync::atomic::Ordering::Relaxed); + if class_id != 0 && !synthetic && !super::is_anon_shape_class_id(class_id) { + return None; + } + if crate::process::is_process_env_ptr(addr) + || super::is_arguments_object(object) + || (key == b"toJSON" && crate::perf_hooks::is_perf_entry_object(object)) + { + return None; + } + let meta = (*object).meta; + if !meta.is_null() && (*meta).elements != 0 { + return None; + } + // A data slot can remain underneath an accessor. A clear Bloom bit + // proves no accessor for THIS key; collisions conservatively decline. + // Same summary as descriptor_state::may_have_descriptor_entry; the + // receiver has already been classified, so do not classify it again. + if meta.is_null() || (*meta).accessor_key_bits & accessor_bit == 0 { + let keys = descriptor.keys as usize as *const crate::array::ArrayHeader; + if !keys.is_null() { + if let Some(slot) = + super::keys_find_slot_by_bytes(keys, descriptor.logical_key_count, key) + { + let value = super::field_get_set::object_field_at_with_live( + object, + slot, + descriptor.live_inline_slot_count, + ); + // Legacy inherited resolution treats undefined/null as + // misses at some class edges. Preserve that fallback, and + // the f64 entry's native-handle alias on undefined reads. + if (inherited && (value.is_undefined() || value.is_null())) + || (value.is_undefined() && super::native_this_alias::alias_active()) + { + return None; + } + return Some(value); + } + } + } else { + return None; + } + if !meta.is_null() && (*meta).prototype != 0 { + let prototype = JSValue::from_bits((*meta).prototype); + if !prototype.is_pointer() { + return None; + } + object = prototype.as_pointer(); + } else if synthetic { + // These are already-rooted per-class pointers, with no builtin + // name lookup or lazy construction. Declared prototype metadata + // has separate precedence and stays on the generic path. + if !super::class_decl_prototype_object(class_id).is_null() { + return None; + } + object = super::class_prototype_object(class_id); + } else { + // Default builtin prototypes may need initialization and can be + // replaced through globalThis. Do not resolve them under a borrow. + return None; + } + inherited = true; + } + None +} + +/// Spec Get for a canonical (pre-interned) string key and the same receiver. +/// The caller must keep both inputs live until entry. A fast hit is a leaf; +/// the existing Reflect implementation owns all handles on a slow read. +#[inline] +pub(crate) unsafe fn get_by_canonical_key(receiver: f64, key: *const crate::StringHeader) -> f64 { + let property_key = JSValue::from_bits(crate::value::nanbox_string_key(key).to_bits()); + crate::proxy::js_reflect_get(receiver, f64::from_bits(property_key.bits()), receiver) +} + +#[cfg(test)] +thread_local! { + static FORCE_SLOW: std::cell::Cell = const { std::cell::Cell::new(false) }; +} + +#[cfg(test)] +mod tests; diff --git a/crates/perry-runtime/src/object/native_get/tests.rs b/crates/perry-runtime/src/object/native_get/tests.rs new file mode 100644 index 0000000000..ddbbbc1bce --- /dev/null +++ b/crates/perry-runtime/src/object/native_get/tests.rs @@ -0,0 +1,272 @@ +use super::*; +use crate::object::{ + js_object_alloc, js_object_create, js_object_delete_field, js_object_freeze, + js_object_set_field_by_name, +}; +use std::sync::atomic::{AtomicUsize, Ordering}; + +fn key(name: &str) -> *mut crate::StringHeader { + crate::string::canonical_key(name.as_bytes()) +} + +fn boxed(object: *const ObjectHeader) -> f64 { + crate::value::js_nanbox_pointer(object as i64) +} + +fn object(name: &str, value: f64) -> *mut ObjectHeader { + let object = js_object_alloc(0, 0); + js_object_set_field_by_name(object, key(name), value); + object +} + +struct Slow(bool); +impl Slow { + fn enter() -> Self { + Self(FORCE_SLOW.with(|value| value.replace(true))) + } +} +impl Drop for Slow { + fn drop(&mut self) { + FORCE_SLOW.with(|value| value.set(self.0)); + } +} + +fn get(object: f64, name: &str) -> f64 { + unsafe { get_by_canonical_key(object, key(name)) } +} + +fn differential(object: f64, name: &str, expected: f64) { + assert_eq!(get(object, name).to_bits(), expected.to_bits()); + let _slow = Slow::enter(); + assert_eq!(get(object, name).to_bits(), expected.to_bits()); +} + +#[test] +fn own_data_is_served_and_matches_forced_slow_for_all_value_kinds() { + let _no_gc = crate::gc::GcSuppressScope::new(); + let child = object("child", 1.0); + let values = [ + 7.0, + -0.0, + f64::from_bits(crate::value::TAG_UNDEFINED), + f64::from_bits(crate::value::TAG_NULL), + f64::from_bits(crate::value::TAG_TRUE), + crate::value::js_nanbox_string(key("a heap string value") as i64), + boxed(child), + ]; + for value in values { + let object = object("value", value); + let fast = unsafe { try_data_get_by_name(object, key("value")) }; + assert_eq!(fast.map(|value| value.bits()), Some(value.to_bits())); + differential(boxed(object), "value", value); + } +} + +#[test] +fn prototype_data_mutation_shadow_delete_and_freeze_match_forced_slow() { + let _no_gc = crate::gc::GcSuppressScope::new(); + let prototype = object("value", 7.0); + let middle = js_object_create(boxed(prototype)); + let child = js_object_create(middle); + let child_ptr = crate::value::js_nanbox_get_pointer(child) as *mut ObjectHeader; + assert_eq!( + unsafe { try_data_get_by_name(child_ptr, key("value")) } + .unwrap() + .bits(), + 7.0f64.to_bits() + ); + differential(child, "value", 7.0); + js_object_set_field_by_name(prototype, key("value"), 11.0); + differential(child, "value", 11.0); + js_object_set_field_by_name(child_ptr, key("value"), 13.0); + differential(child, "value", 13.0); + assert_eq!(js_object_delete_field(child_ptr, key("value")), 1); + differential(child, "value", 11.0); + js_object_freeze(boxed(prototype)); + assert!(unsafe { try_data_get_by_name(child_ptr, key("value")) }.is_some()); + differential(child, "value", 11.0); + let replacement = object("value", 17.0); + crate::object::prototype_chain::object_set_user_prototype( + child_ptr as usize, + boxed(replacement).to_bits(), + ); + differential(child, "value", 17.0); +} + +static GETTER_CALLS: AtomicUsize = AtomicUsize::new(0); + +extern "C" fn getter(_closure: *const crate::closure::ClosureHeader) -> f64 { + GETTER_CALLS.fetch_add(1, Ordering::Relaxed); + 47.0 +} + +fn install_getter(object: *mut ObjectHeader, name: &str, has_getter: bool) { + let closure = crate::closure::js_closure_alloc(getter as *const u8, 0); + crate::object::descriptor_state::set_accessor_descriptor( + object as usize, + name.to_string(), + crate::object::descriptor_state::AccessorDescriptor { + get: if has_getter { + crate::value::js_nanbox_pointer(closure as i64).to_bits() + } else { + 0 + }, + set: 0, + }, + ); +} + +#[test] +fn accessor_bloom_guard_preserves_own_and_inherited_getter_calls() { + let _no_gc = crate::gc::GcSuppressScope::new(); + GETTER_CALLS.store(0, Ordering::Relaxed); + let prototype = object("value", 1.0); + install_getter(prototype, "value", true); + let child = js_object_create(boxed(prototype)); + for receiver in [boxed(prototype), child] { + assert!( + unsafe { try_data_get_bytes(JSValue::from_bits(receiver.to_bits()), b"value") } + .is_none() + ); + differential(receiver, "value", 47.0); + } + assert_eq!( + GETTER_CALLS.load(Ordering::Relaxed), + 4, + "one getter invocation per Get" + ); + let child_ptr = crate::value::js_nanbox_get_pointer(child) as *mut ObjectHeader; + js_object_set_field_by_name(child_ptr, key("own"), 53.0); + differential(child, "own", 53.0); + assert_eq!(GETTER_CALLS.load(Ordering::Relaxed), 4); + install_getter(prototype, "value", false); + differential( + boxed(prototype), + "value", + f64::from_bits(crate::value::TAG_UNDEFINED), + ); +} + +#[test] +fn unsupported_receivers_and_private_names_decline_and_preserve_results() { + let _no_gc = crate::gc::GcSuppressScope::new(); + let target = object("value", 59.0); + let handler = js_object_alloc(0, 0); + let proxy = crate::proxy::js_proxy_new(boxed(target), boxed(handler)); + let array = crate::array::js_array_alloc(4); + let array_value = crate::value::js_nanbox_pointer(array as i64); + js_object_set_field_by_name(array as *mut ObjectHeader, key("value"), 59.0); + let closure = crate::closure::js_closure_alloc(getter as *const u8, 0); + js_object_set_field_by_name(closure as *mut ObjectHeader, key("value"), 59.0); + for receiver in [ + proxy, + array_value, + crate::value::js_nanbox_pointer(closure as i64), + ] { + assert!( + unsafe { try_data_get_bytes(JSValue::from_bits(receiver.to_bits()), b"value") } + .is_none() + ); + differential(receiver, "value", 59.0); + } + let private = object("#value", 61.0); + assert!(unsafe { try_data_get_by_name(private, key("#value")) }.is_none()); + differential(boxed(private), "#value", 61.0); + assert!(unsafe { try_data_get_bytes(JSValue::number(3.5), b"value") }.is_none()); +} + +#[cfg(feature = "regex-engine")] +#[test] +fn regexp_expandos_and_accessors_remain_on_the_exotic_path() { + let _no_gc = crate::gc::GcSuppressScope::new(); + let regexp = crate::regex::js_regexp_new(key("x"), key("g")); + let regexp_obj = regexp as *mut ObjectHeader; + js_object_set_field_by_name(regexp_obj, key("value"), 67.0); + assert!(unsafe { try_data_get_by_name(regexp_obj, key("value")) }.is_none()); + differential(boxed(regexp_obj), "value", 67.0); + GETTER_CALLS.store(0, Ordering::Relaxed); + install_getter(regexp_obj, "value", true); + differential(boxed(regexp_obj), "value", 47.0); + assert_eq!(GETTER_CALLS.load(Ordering::Relaxed), 2); +} + +#[test] +fn borrowed_long_and_inline_keys_match_without_materializing_for_the_fast_read() { + let _no_gc = crate::gc::GcSuppressScope::new(); + for name in [ + "x", + "a property name longer than the inline copy buffer; keep borrowing through this lookup", + ] { + let object = object(name, 71.0); + assert_eq!( + unsafe { + try_data_get_bytes(JSValue::from_bits(boxed(object).to_bits()), name.as_bytes()) + } + .unwrap() + .bits(), + 71.0f64.to_bits() + ); + differential(boxed(object), name, 71.0); + } +} + +#[test] +fn wide_spilled_and_deleted_keys_match_forced_slow() { + let _no_gc = crate::gc::GcSuppressScope::new(); + let object = js_object_alloc(0, 0); + for i in 0..100 { + js_object_set_field_by_name(object, key(&format!("field{i}")), i as f64); + } + for i in [0, 4, 50, 99] { + let name = format!("field{i}"); + assert!(unsafe { try_data_get_by_name(object, key(&name)) }.is_some()); + differential(boxed(object), &name, i as f64); + assert_eq!(js_object_delete_field(object, key(&name)), 1); + assert!(unsafe { try_data_get_by_name(object, key(&name)) }.is_none()); + differential( + boxed(object), + &name, + f64::from_bits(crate::value::TAG_UNDEFINED), + ); + js_object_set_field_by_name(object, key(&name), 101.0); + differential(boxed(object), &name, 101.0); + } +} + +#[test] +fn class_statics_buffers_and_mapped_arguments_use_the_slow_path() { + let _no_gc = crate::gc::GcSuppressScope::new(); + unsafe { + let class_id = 0x0065_5234; + crate::object::js_register_class_id(class_id); + crate::object::js_class_register_static_field( + class_id, + b"value".as_ptr(), + 5, + 73.0, + std::ptr::null_mut(), + ); + let class_value = f64::from_bits(crate::value::INT32_TAG | u64::from(class_id)); + assert!(try_data_get_bytes(JSValue::from_bits(class_value.to_bits()), b"value").is_none()); + differential(class_value, "value", 73.0); + + let buffer = crate::buffer::js_buffer_alloc(8, 11); + let buffer_value = crate::value::js_nanbox_pointer(buffer as i64); + assert!( + try_data_get_bytes(JSValue::from_bits(buffer_value.to_bits()), b"length").is_none() + ); + differential(buffer_value, "length", 8.0); + + let args = crate::array::js_array_alloc(1); + crate::array::js_array_push(args, JSValue::number(1.0)); + let arguments = crate::object::js_arguments_object_alloc( + crate::value::js_nanbox_pointer(args as i64), + f64::from_bits(crate::value::TAG_UNDEFINED), + 0, + ); + let cell = crate::r#box::js_box_alloc(79.0); + crate::object::js_arguments_object_map_index(arguments, 0, cell); + assert!(try_data_get_by_name(arguments, key("0")).is_none()); + differential(boxed(arguments), "0", 79.0); + } +} diff --git a/crates/perry-runtime/src/promise/microtasks.rs b/crates/perry-runtime/src/promise/microtasks.rs index fc5e035631..6f42949582 100644 --- a/crates/perry-runtime/src/promise/microtasks.rs +++ b/crates/perry-runtime/src/promise/microtasks.rs @@ -384,7 +384,7 @@ pub(crate) fn empty_checkpoint_eligible_for_test() -> bool { } fn finish_gc_and_box_boundary() { - let _ = crate::gc::gc_runtime_safepoint(); + crate::gc::gc_runtime_safepoint_poll(); // Phase 1 of the moving-GC project (see project_gc_one_great_moving_gc): at // the OUTERMOST microtask-pump boundary the JS stack has fully unwound, so diff --git a/crates/perry-runtime/src/proxy/reflect.rs b/crates/perry-runtime/src/proxy/reflect.rs index f90ce9b89d..646e934456 100644 --- a/crates/perry-runtime/src/proxy/reflect.rs +++ b/crates/perry-runtime/src/proxy/reflect.rs @@ -17,6 +17,17 @@ use super::{ /// as "use target". #[no_mangle] pub extern "C" fn js_reflect_get(target: f64, key: f64, receiver: f64) -> f64 { + // A data read has no observable receiver binding or key conversion. The + // classifier accepts only already-string keys and proves every traversed + // object ordinary before returning, so no handles are needed on a hit. + if let Some(value) = unsafe { + crate::object::native_get::try_data_get( + crate::value::JSValue::from_bits(target.to_bits()), + crate::value::JSValue::from_bits(key.to_bits()), + ) + } { + return f64::from_bits(value.bits()); + } if !reflect_value_is_object(target) { return reflect_non_object_typeerror("get"); } diff --git a/crates/perry-runtime/src/regex/perex_runtime.rs b/crates/perry-runtime/src/regex/perex_runtime.rs index 41d51b4617..0efc08964e 100644 --- a/crates/perry-runtime/src/regex/perex_runtime.rs +++ b/crates/perry-runtime/src/regex/perex_runtime.rs @@ -56,7 +56,7 @@ impl From for EngineError { /// that requests cancellation or forces actual collection. No input/program /// view or scratch slice is live when any poll is invoked. pub(crate) fn poll() -> Result<(), EngineError> { - crate::gc::gc_runtime_safepoint(); + crate::gc::gc_runtime_safepoint_poll(); Ok(()) } diff --git a/crates/perry-runtime/src/registry_latch.rs b/crates/perry-runtime/src/registry_latch.rs index 8e64271625..0dc12ae5e1 100644 --- a/crates/perry-runtime/src/registry_latch.rs +++ b/crates/perry-runtime/src/registry_latch.rs @@ -62,6 +62,7 @@ //! is what ships. use std::sync::atomic::{AtomicBool, AtomicU64, AtomicUsize, Ordering}; +use std::sync::{Mutex, MutexGuard}; /// A one-way "this feature has been registered at least once" flag. /// @@ -297,6 +298,12 @@ impl RegistryAddrWindow { /// here is the one the measurements above were taken at, and is deliberately /// not raised on speculation. /// +/// `WORDS` is a per-owner parameter, defaulting to 16. Issue #10110 measured +/// 643–648 live canonical wrappers saturating this default on a separate +/// campaign branch. That owner needs 256 words and [`RegistryAddrIndex`] to +/// release retired addresses; increasing a monotone filter only postpones +/// saturation. The canonical-wrapper subsystem has not landed on `main` yet. +/// /// **Bits accrue per ADMISSION, not per live entry.** Both tables this guards /// are re-keyed by the collector — a symbol or a prototype that is evacuated is /// admitted again at its new address, and the bits its old address set are @@ -327,32 +334,36 @@ impl RegistryAddrWindow { /// pre-check would let a thread publish an address without ever acquiring /// another thread's bit-setting, so a reader synchronising only with this /// thread could miss it. -pub struct RegistryAddrFilter { - words: [AtomicU64; Self::WORDS], +pub struct RegistryAddrFilter { + words: [AtomicU64; WORDS], } -impl Default for RegistryAddrFilter { +impl Default for RegistryAddrFilter { fn default() -> Self { Self::new() } } -impl std::fmt::Debug for RegistryAddrFilter { +impl std::fmt::Debug for RegistryAddrFilter { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { f.debug_struct("RegistryAddrFilter") + .field("words", &WORDS) .field("bits_set", &self.bits_set()) .finish() } } -impl RegistryAddrFilter { - const WORDS: usize = 16; - const BITS: u64 = (Self::WORDS as u64) * 64; +impl RegistryAddrFilter { + const BITS: u64 = (WORDS as u64) * 64; + const HASH_BITS: u32 = Self::BITS.ilog2(); /// An empty filter: contains no address at all. pub const fn new() -> Self { + // Three disjoint slices must fit in the 64-bit hash. Power-of-two + // widths keep the hot probes division-free after constant folding. + assert!(WORDS.is_power_of_two() && WORDS <= (1 << 15)); Self { - words: [const { AtomicU64::new(0) }; Self::WORDS], + words: [const { AtomicU64::new(0) }; WORDS], } } @@ -368,9 +379,9 @@ impl RegistryAddrFilter { const fn bit_positions(addr: usize) -> (u64, u64, u64) { let h = ((addr as u64) >> 3).wrapping_mul(0x9E37_79B9_7F4A_7C15); ( - (h >> 54) % Self::BITS, - (h >> 44) % Self::BITS, - (h >> 34) % Self::BITS, + (h >> (64 - Self::HASH_BITS)) % Self::BITS, + (h >> (64 - 2 * Self::HASH_BITS)) % Self::BITS, + (h >> (64 - 3 * Self::HASH_BITS)) % Self::BITS, ) } @@ -404,6 +415,34 @@ impl RegistryAddrFilter { } } + /// Replace every word with the bits of `live`. + /// + /// **Only [`RegistryAddrIndex`] may call this**, and only while holding the + /// guard that serializes this filter's admissions: the soundness argument + /// is written out on that type, and it depends on `live` being the complete + /// current population and on no `admit` running concurrently. + fn overwrite_from_live(&self, live: impl IntoIterator) { + let mut fresh = [0u64; WORDS]; + for addr in live { + let (a, b, c) = Self::bit_positions(addr); + for bit in [a, b, c] { + fresh[(bit / 64) as usize] |= 1u64 << (bit % 64); + } + } + // Release, so a reader that observes a word observes the set membership + // that produced it. Word-at-a-time publication is safe because every + // live address's bits are present in both the old and the new value. + for (word, value) in self.words.iter().zip(fresh.iter()) { + word.store(*value, Ordering::Release); + } + } + + /// How many bits this filter has in total. Diagnostics and tests only: + /// an occupancy count means nothing without the capacity it is a share of. + pub fn capacity_bits(&self) -> u32 { + Self::BITS as u32 + } + /// How many bits are set. Diagnostics and tests only: a filter whose bits /// are nearly all set has stopped discriminating, and a test that wants to /// prove the fast path RAN needs to know the filter is not saturated. @@ -422,8 +461,8 @@ impl RegistryAddrFilter { /// as unregistered. A test that calls this must restore what it cleared — /// see [`Self::restore_for_tests`]. #[cfg(test)] - pub(crate) fn take_for_tests(&self) -> [u64; Self::WORDS] { - let mut previous = [0u64; Self::WORDS]; + pub(crate) fn take_for_tests(&self) -> [u64; WORDS] { + let mut previous = [0u64; WORDS]; for (slot, word) in previous.iter_mut().zip(self.words.iter()) { *slot = word.swap(0, Ordering::AcqRel); } @@ -432,7 +471,7 @@ impl RegistryAddrFilter { /// Test hook: OR the saved bits back in. #[cfg(test)] - pub(crate) fn restore_for_tests(&self, saved: [u64; Self::WORDS]) { + pub(crate) fn restore_for_tests(&self, saved: [u64; WORDS]) { for (word, bits) in self.words.iter().zip(saved.iter()) { word.fetch_or(*bits, Ordering::AcqRel); } @@ -440,8 +479,8 @@ impl RegistryAddrFilter { /// Test hook: the current bit words, as a plain value. #[cfg(test)] - pub(crate) fn snapshot_for_tests(&self) -> [u64; Self::WORDS] { - let mut words = [0u64; Self::WORDS]; + pub(crate) fn snapshot_for_tests(&self) -> [u64; WORDS] { + let mut words = [0u64; WORDS]; for (slot, word) in words.iter_mut().zip(self.words.iter()) { *slot = word.load(Ordering::Acquire); } @@ -456,7 +495,7 @@ impl RegistryAddrFilter { /// filter already happened to accept" is only checkable against the filter /// as it stood BEFORE the move. #[cfg(test)] - pub(crate) fn snapshot_may_contain(words: &[u64; Self::WORDS], addr: usize) -> bool { + pub(crate) fn snapshot_may_contain(words: &[u64; WORDS], addr: usize) -> bool { let (a, b, c) = Self::bit_positions(addr); [a, b, c] .into_iter() @@ -466,7 +505,176 @@ impl RegistryAddrFilter { /// The word count, so a caller can name the snapshot type. #[cfg(test)] pub(crate) const fn words() -> usize { - Self::WORDS + WORDS + } +} + +/// The addresses an owner currently holds, sized from that population and +/// narrowed when the population shrinks. +/// +/// [`RegistryAddrFilter`] answers "was this address EVER admitted?", by default +/// in 1,024 bits. Both halves of that sentence are what the #10110 census +/// measured going wrong for the canonical-handle owner: 643–648 live addresses +/// do not fit in 1,024 bits, and the 712–721 admissions a single command makes +/// are never released, so even a filter sized for the live set would walk +/// towards saturation over a session. This type fixes both without changing the +/// hot side: `may_contain` is still the same three bit tests on a plain array of +/// atomics, with no lock, no allocation and no pointer chase. +/// +/// # What it adds +/// +/// The owner's live addresses are kept in an exact set, mutated only when a +/// wrapper is created or retired — 712–721 and 69–76 times per command +/// respectively, against tens of millions of probes, so the mutex that guards it +/// is never contended by the hot path. When enough has changed, the bit array is +/// recomputed from that exact set, which is what releases retired addresses. +/// Refreshes are paced by the live population, with no timer or workload knob. +/// +/// # Why overwriting the bits is sound +/// +/// [`RegistryAddrFilter`] deliberately has no `clear`, because clearing bits +/// could make a live registered address read as unregistered — the one +/// dangerous answer. The refresh here is not a clear: +/// +/// * Every `admit` and every refresh for one index is serialized by the same +/// mutex, so no admission's bit-set can be lost to a refresh's store. This is +/// why `admit` takes the guard *before* setting the bits. +/// * The new words are computed from the complete live set. For an address that +/// is live, its three bits are therefore set in the new value; they are also +/// set in the old value, because it was admitted under this same guard and +/// every refresh published while it was live also included it. A reader may +/// observe any mix of old and new words, and in every such mix all three of a +/// live address's bits are set. **No reader can observe a false negative at +/// any instant**, which is the same guarantee the monotone filter gives. +/// * An address that is *not* live may start reading `false`. That is the point, +/// and it is safe because the owner removes an address from the live set only +/// after its authoritative registry no longer recognises it, and because the +/// index is only ever a gate in front of that authoritative lookup: a stale +/// `true` costs the lookup that was always there, exactly as a Bloom false +/// positive does. +/// +/// # The ordering rule (binding, inherited unchanged) +/// +/// [`admit`](Self::admit) must run BEFORE the registry mutation it advertises, +/// and [`retire`](Self::retire) AFTER the registry mutation that ends the +/// address's registration. Retiring first would open a window in which the +/// registry still recognises an address the index already denies. +/// +/// This is a set, not a reference count: repeated admissions are idempotent. +/// The owner must serialize an address's complete registration/retirement +/// lifecycle, including reuse, so a delayed retirement cannot erase a newer +/// admission. As with the monotone filter, publishing an address to another +/// thread must establish a happens-before edge from its admission. +pub struct RegistryAddrIndex { + filter: RegistryAddrFilter, + live: Mutex>, +} + +struct LiveAddrs { + addrs: crate::fast_hash::PtrHashSet, + /// Admissions plus retirements since the last refresh. Only ever touched + /// under the `live` guard. + changes_since_refresh: usize, +} + +/// A refresh costs O(live), so it may not run more often than once per O(live) +/// changes. This floor keeps a tiny population from refreshing on every single +/// change; it is a lower bound on work, not a tuning knob for a workload. +const REFRESH_CHANGE_FLOOR: usize = 64; + +impl Default for RegistryAddrIndex { + fn default() -> Self { + Self::new() + } +} + +impl std::fmt::Debug for RegistryAddrIndex { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + f.debug_struct("RegistryAddrIndex") + .field("words", &WORDS) + .field("bits_set", &self.bits_set()) + .field("live", &self.live_count()) + .finish() + } +} + +impl RegistryAddrIndex { + /// An empty index: contains no address at all. + pub const fn new() -> Self { + Self { + filter: RegistryAddrFilter::new(), + live: Mutex::new(None), + } + } + + /// `false` ⟹ `addr` is definitively not registered with this owner. + /// + /// The hot side, unchanged from [`RegistryAddrFilter::may_contain`]: three + /// bit tests, short-circuiting, no lock and no allocation. + #[inline(always)] + pub fn may_contain(&self, addr: usize) -> bool { + self.filter.may_contain(addr) + } + + fn guard(&self) -> MutexGuard<'_, Option> { + // A poisoned guard must not turn a classification into a panic: the + // live set is only an accelerator, and its worst state is stale. + self.live + .lock() + .unwrap_or_else(|poisoned| poisoned.into_inner()) + } + + /// Add `addr`. MUST run before the registry mutation it advertises. + pub fn admit(&self, addr: usize) { + let mut guard = self.guard(); + let state = guard.get_or_insert_with(|| LiveAddrs { + addrs: crate::fast_hash::new_ptr_hash_set(), + changes_since_refresh: 0, + }); + // Under the guard, so a concurrent refresh cannot drop these bits. + self.filter.admit(addr); + if state.addrs.insert(addr) { + state.changes_since_refresh += 1; + } + self.refresh_if_warranted(state); + } + + /// Drop `addr`. MUST run after the registry mutation that ends its + /// registration, so no window exists in which the registry recognises an + /// address this index already denies. + pub fn retire(&self, addr: usize) { + let mut guard = self.guard(); + let Some(state) = guard.as_mut() else { + return; + }; + if !state.addrs.remove(&addr) { + return; + } + state.changes_since_refresh += 1; + self.refresh_if_warranted(state); + } + + fn refresh_if_warranted(&self, state: &mut LiveAddrs) { + if state.changes_since_refresh < REFRESH_CHANGE_FLOOR.max(state.addrs.len()) { + return; + } + state.changes_since_refresh = 0; + self.filter.overwrite_from_live(state.addrs.iter().copied()); + } + + /// Bits set in the underlying array, and its capacity. Diagnostics: an + /// occupancy count means nothing without the capacity it is a share of. + pub fn occupancy(&self) -> (u32, u32) { + (self.filter.bits_set(), self.filter.capacity_bits()) + } + + fn bits_set(&self) -> u32 { + self.filter.bits_set() + } + + /// How many addresses the owner currently holds. + pub fn live_count(&self) -> usize { + self.guard().as_ref().map_or(0, |state| state.addrs.len()) } } @@ -474,6 +682,211 @@ impl RegistryAddrFilter { mod tests { use super::*; + /// Synthetic wrapper addresses: 8-byte aligned, above the handle band, + /// spread the way `gc_malloc` results are rather than densely packed. + fn wrapper_addrs(count: usize) -> Vec { + (0..count).map(|i| 0x7F00_0000_1000usize + i * 96).collect() + } + + fn probe_addrs(count: usize) -> Vec { + (0..count).map(|i| 0x6100_0000_0008usize + i * 64).collect() + } + + fn pass_rate(index: &RegistryAddrIndex, probes: &[usize]) -> f64 { + let passes = probes.iter().filter(|&&a| index.may_contain(a)).count(); + passes as f64 / probes.len() as f64 + } + + #[test] + fn wider_filter_uses_the_full_width_for_each_probe() { + let mut high_half_seen = [false; 3]; + for addr in wrapper_addrs(644) { + let (a, b, c) = RegistryAddrFilter::<256>::bit_positions(addr); + for (seen, bit) in high_half_seen.iter_mut().zip([a, b, c]) { + assert!(bit < 16_384); + *seen |= bit >= 8_192; + } + } + assert!(high_half_seen.into_iter().all(|seen| seen)); + } + + #[test] + fn index_supports_duplicate_admission_and_address_reuse() { + let index: RegistryAddrIndex<256> = RegistryAddrIndex::new(); + let addr = wrapper_addrs(1)[0]; + index.retire(addr); + assert_eq!(index.live_count(), 0); + assert!(!index.may_contain(addr)); + + for _ in 0..3 { + index.admit(addr); + index.admit(addr); + assert_eq!(index.live_count(), 1); + assert!(index.may_contain(addr)); + index.retire(addr); + index.retire(addr); + assert_eq!(index.live_count(), 0); + } + index.admit(addr); + assert_eq!(index.live_count(), 1); + assert!(index.may_contain(addr)); + } + + #[test] + fn concurrent_refreshes_preserve_published_live_addresses() { + use std::sync::Barrier; + + const WRITERS: usize = 4; + const RESIDENT: usize = 64; + let index: RegistryAddrIndex<256> = RegistryAddrIndex::new(); + let resident = wrapper_addrs(RESIDENT); + for &addr in &resident { + index.admit(addr); + } + let start = Barrier::new(WRITERS + 1); + std::thread::scope(|scope| { + for writer in 0..WRITERS { + let (index, start, resident) = (&index, &start, &resident); + scope.spawn(move || { + start.wait(); + for i in 0..2_000 { + let addr = 0x6100_0000_0008 + (i * WRITERS + writer) * 64; + index.admit(addr); + assert!(index.may_contain(addr)); + for &live in resident { + assert!(index.may_contain(live)); + } + index.retire(addr); + } + }); + } + start.wait(); + for _ in 0..2_000 { + for &addr in &resident { + assert!(index.may_contain(addr)); + } + } + }); + assert_eq!(index.live_count(), RESIDENT); + for addr in resident { + assert!(index.may_contain(addr)); + } + assert!(pass_rate(&index, &probe_addrs(20_000)) < 0.02); + } + + /// The 644-address population reported in #10110 must fit in the proposed + /// canonical-owner width. The default width is a saturation witness. + #[test] + fn sized_index_holds_the_measured_live_population() { + const LIVE: usize = 644; + let live = wrapper_addrs(LIVE); + let probes = probe_addrs(20_000); + + let sized: RegistryAddrIndex<256> = RegistryAddrIndex::new(); + for &addr in &live { + sized.admit(addr); + } + assert_eq!(sized.live_count(), LIVE); + let sized_rate = pass_rate(&sized, &probes); + assert!( + sized_rate < 0.02, + "sized index admits {sized_rate} of non-members" + ); + for &addr in &live { + assert!(sized.may_contain(addr), "sized index lost a live address"); + } + + let small: RegistryAddrIndex<16> = RegistryAddrIndex::new(); + for &addr in &live { + small.admit(addr); + } + let small_rate = pass_rate(&small, &probes); + assert!( + small_rate > 0.30, + "the 1,024-bit witness should be saturated by this population, got {small_rate}" + ); + assert!( + sized_rate * 10.0 < small_rate, + "{sized_rate} vs {small_rate}" + ); + } + + /// Retirement must actually narrow the index, and must never drop a live + /// address while doing it. A refresh that kept retired addresses would make + /// this type no better than the monotone filter. + #[test] + fn refresh_releases_retired_addresses_and_keeps_live_ones() { + let addrs = wrapper_addrs(600); + let index: RegistryAddrIndex<256> = RegistryAddrIndex::new(); + for &addr in &addrs { + index.admit(addr); + } + let (occupied, capacity) = index.occupancy(); + assert!(occupied > 0 && occupied < capacity); + + let (live, retired) = addrs.split_at(50); + for &addr in retired { + index.retire(addr); + } + assert_eq!(index.live_count(), live.len()); + let (after, _) = index.occupancy(); + assert!( + after * 4 < occupied, + "occupancy {after} should collapse towards the live set from {occupied}" + ); + for &addr in live { + assert!( + index.may_contain(addr), + "a live address was dropped by a refresh" + ); + } + let still_admitted = retired.iter().filter(|&&a| index.may_contain(a)).count(); + assert!( + still_admitted * 10 < retired.len(), + "{still_admitted} of {} retired addresses still pass", + retired.len() + ); + } + + /// The defect the census measured is a WALK: bits accrue per admission, so a + /// long session saturates even a filter sized for its live set. Churn a + /// population two orders of magnitude larger than what is ever live and + /// require that selectivity survives it. + #[test] + fn churn_does_not_walk_towards_saturation() { + const CHURN: usize = 20_000; + const RESIDENT: usize = 64; + let addrs = wrapper_addrs(CHURN); + let index: RegistryAddrIndex<256> = RegistryAddrIndex::new(); + let monotone: RegistryAddrFilter<256> = RegistryAddrFilter::new(); + for (i, &addr) in addrs.iter().enumerate() { + index.admit(addr); + monotone.admit(addr); + if i >= RESIDENT { + index.retire(addrs[i - RESIDENT]); + } + } + assert!(index.live_count() <= RESIDENT + 1, "{}", index.live_count()); + let probes = probe_addrs(20_000); + let rate = pass_rate(&index, &probes); + assert!( + rate < 0.02, + "index admits {rate} of non-members after churn" + ); + let monotone_passes = probes.iter().filter(|&&a| monotone.may_contain(a)).count(); + let monotone_rate = monotone_passes as f64 / probes.len() as f64; + assert!( + monotone_rate > 0.50, + "the monotone witness of the same size should be saturated, got {monotone_rate}" + ); + for i in (CHURN - RESIDENT)..CHURN { + assert!( + index.may_contain(addrs[i]), + "churn dropped a resident address" + ); + } + } + #[test] fn starts_idle_and_arms_once() { let latch = RegistryLatch::new(); @@ -570,7 +983,7 @@ mod tests { #[test] fn empty_filter_contains_nothing() { - let f = RegistryAddrFilter::new(); + let f: RegistryAddrFilter = RegistryAddrFilter::new(); assert_eq!(f.bits_set(), 0); for addr in [0usize, 8, 0x1000, 0x7fff_ffff_f000, usize::MAX] { assert!( @@ -585,7 +998,7 @@ mod tests { /// was never given; it is never allowed to reject one it was. #[test] fn filter_never_rejects_an_admitted_address() { - let f = RegistryAddrFilter::new(); + let f: RegistryAddrFilter = RegistryAddrFilter::new(); let mut admitted = Vec::new(); // A realistic spread: 8-byte-aligned addresses across a wide heap. for i in 0..256usize { @@ -609,7 +1022,7 @@ mod tests { /// majority of addresses it was never given. #[test] fn filter_rejects_almost_everything_it_was_never_given() { - let f = RegistryAddrFilter::new(); + let f: RegistryAddrFilter = RegistryAddrFilter::new(); // 160 entries — the number of class prototypes `claude-code --help` // registers, i.e. the population this size was chosen for. for i in 0..160usize { diff --git a/crates/perry-runtime/src/symbol.rs b/crates/perry-runtime/src/symbol.rs index 6725d3a010..5bab20ae26 100644 --- a/crates/perry-runtime/src/symbol.rs +++ b/crates/perry-runtime/src/symbol.rs @@ -55,6 +55,7 @@ pub use properties::{ }; // Symbol-keyed property reads. +pub(crate) use get::has_declared_prototype_symbol_property; pub(crate) use get::{has_own_symbol_property, inherited_symbol_property, own_symbol_property}; pub use get::{ js_object_get_symbol_property, js_object_get_symbol_property_ic_miss, @@ -530,7 +531,8 @@ pub(crate) fn insert_symbol_pointer_in_set(set: &mut PtrHashSet, ptr: usi /// Width of the symbol filter's bit array, for tests that snapshot it. #[cfg(test)] -pub(crate) const SYMBOL_FILTER_WORDS: usize = crate::registry_latch::RegistryAddrFilter::words(); +pub(crate) const SYMBOL_FILTER_WORDS: usize = + crate::registry_latch::RegistryAddrFilter::<16>::words(); /// Save/restore this test's address filter around a fixture that needs to /// observe a NEARLY-EMPTY filter. A reset must put back at least what it found, diff --git a/crates/perry-runtime/src/symbol/get.rs b/crates/perry-runtime/src/symbol/get.rs index 1fa3794a4d..326f73dee6 100644 --- a/crates/perry-runtime/src/symbol/get.rs +++ b/crates/perry-runtime/src/symbol/get.rs @@ -1202,6 +1202,20 @@ pub unsafe extern "C" fn js_object_get_symbol_property(obj_f64: f64, sym_f64: f6 if let Some(v) = crate::object::resolve_proto_chain_symbol(cid, sym_f64) { return v; } + // A symbol-keyed property added to a DECLARED class's + // `.prototype` after the declaration — `C.prototype[S] = f`, + // `Object.defineProperty(C.prototype, S, ...)`, or + // `Object.assign(C.prototype, { [Symbol.iterator]() {} })` — + // is stored on the declared prototype object, which the + // CLASS_PROTOTYPE_OBJECTS walk above never visits (that table + // holds `Object.create` / class-expression parents). String + // keys already resolve there; symbol keys returned undefined, + // so drizzle-orm's `applyEffectWrapper` (effect's + // `Effectable.Prototype` assigned onto query classes) left + // `yield* query` with no iterator ("next is not a function"). + if let Some(v) = declared_prototype_chain_symbol(obj_f64, sym_f64, cid) { + return v; + } // #1838: a class can define a computed well-known-symbol METHOD // (`[Symbol.iterator]() {}`) — class lowering names it // `@@iterator` in the vtable (class_members.rs), NOT as a symbol @@ -1226,6 +1240,73 @@ pub unsafe extern "C" fn js_object_get_symbol_property(obj_f64: f64, sym_f64: f6 f64::from_bits(TAG_UNDEFINED) } +/// Walk the declared class prototype objects (`C.prototype` for `C` and each +/// ancestor) for a symbol-keyed property written after the class declaration. +/// Accessors run with the original receiver; data properties are returned as +/// stored. Nearest class first, so a subclass's prototype write shadows a +/// base class's. +unsafe fn declared_prototype_chain_symbol(receiver: f64, sym: f64, class_id: u32) -> Option { + let holder = declared_prototype_symbol_holder(receiver, sym, class_id)?; + if let Some(acc) = accessors::symbol_accessor_property(holder, sym) { + return Some(accessors::invoke_symbol_accessor_getter(acc.get, receiver)); + } + own_symbol_property(holder, sym) +} + +/// Locate a declared prototype property without invoking its getter. An +/// explicit prototype replaces the class default, including when it is null. +unsafe fn declared_prototype_symbol_holder( + receiver: f64, + sym: f64, + mut class_id: u32, +) -> Option { + let receiver_addr = (receiver.to_bits() & crate::value::POINTER_MASK) as usize; + if crate::object::prototype_chain::object_has_user_prototype_override(receiver_addr) { + return None; + } + for _ in 0..32 { + let declared = crate::object::class_decl_prototype_object(class_id); + if !declared.is_null() { + let proto_value = crate::value::js_nanbox_pointer(declared as i64); + if has_own_symbol_property(proto_value, sym) { + return Some(proto_value); + } + if crate::object::prototype_chain::object_has_user_prototype_override(declared as usize) + { + return None; + } + } + match crate::object::get_parent_class_id(class_id) { + Some(parent) if parent != 0 && parent != class_id => class_id = parent, + _ => break, + } + } + None +} + +/// Presence of the declared-prototype properties handled above, including +/// accessors and data properties whose value is undefined. +pub(crate) unsafe fn has_declared_prototype_symbol_property(receiver: f64, sym: f64) -> bool { + let value = crate::value::JSValue::from_bits(receiver.to_bits()); + if !value.is_pointer() { + return false; + } + let ptr = value.as_pointer::(); + // Pointer tags also carry handles and headerless storage. Require allocator + // ownership before reading a header, then admit only live object layouts. + let Some(header) = crate::value::addr_class::try_read_tracked_gc_header(ptr as usize) else { + return false; + }; + let header = header.as_ref(); + if header.obj_type != crate::gc::GC_TYPE_OBJECT + || header.gc_flags & crate::gc::GC_FLAG_FORWARDED != 0 + { + return false; + } + let class_id = (*ptr).class_id; + class_id != 0 && declared_prototype_symbol_holder(receiver, sym, class_id).is_some() +} + /// #1838: map a well-known symbol value to the synthetic `@@` vtable key /// that class lowering assigns to a computed `[Symbol.X]() {}` method (see /// `lower_decl/class_members.rs`). Returns `None` for symbols that don't name a diff --git a/crates/perry-runtime/src/tls_hot.rs b/crates/perry-runtime/src/tls_hot.rs index 49148ef3f5..860eb92159 100644 --- a/crates/perry-runtime/src/tls_hot.rs +++ b/crates/perry-runtime/src/tls_hot.rs @@ -535,9 +535,11 @@ pub(crate) fn hot_if_published() -> Option<&'static HotTls> { /// `SlotGuard`; the named fast path must preserve it (#9183). #[inline(always)] pub(crate) fn unpublish_runtime_handle_stack() { - if let Some(hot) = hot_if_published() { - hot.runtime_handle_stack.set(std::ptr::null_mut()); - } + // Teardown is cold. Clear the cache without filling it. Android's pooled + // cache may already be destroyed, which also makes it unpublished. + let _ = HOT.try_with(|cell| unsafe { + (*cell.get()).runtime_handle_stack.set(std::ptr::null_mut()); + }); } /// The per-thread address cache. On Apple aarch64 this is an `mrs` plus two diff --git a/crates/perry-runtime/src/value/dynamic_object.rs b/crates/perry-runtime/src/value/dynamic_object.rs index 9b3b916ec0..ece7502fcb 100644 --- a/crates/perry-runtime/src/value/dynamic_object.rs +++ b/crates/perry-runtime/src/value/dynamic_object.rs @@ -311,6 +311,15 @@ pub unsafe extern "C" fn js_dynamic_object_get_property( property_name_ptr: *const i8, property_name_len: usize, ) -> f64 { + if !property_name_ptr.is_null() && property_name_len != 0 { + let name = std::slice::from_raw_parts(property_name_ptr as *const u8, property_name_len); + if let Some(value) = crate::object::native_get::try_data_get_bytes( + JSValue::from_bits(obj_value.to_bits()), + name, + ) { + return f64::from_bits(value.bits()); + } + } // A revocable Proxy value reaching this generic dynamic getter must go // through the Proxy's `get` trap. Proxy ids live at the TOP of the handle // band ([PROXY_ID_BAND_START, HANDLE_BAND_MAX)), so without this branch the diff --git a/crates/perry/src/commands/compile/collect_modules.rs b/crates/perry/src/commands/compile/collect_modules.rs index 30ff194fc2..cb0b2f13cb 100644 --- a/crates/perry/src/commands/compile/collect_modules.rs +++ b/crates/perry/src/commands/compile/collect_modules.rs @@ -1935,6 +1935,7 @@ fn collect_module_one( if reexport_prune::record( ctx, &canonical, + entry_path, export_index, export, &resolved_path, diff --git a/crates/perry/src/commands/compile/collect_modules/reexport_prune.rs b/crates/perry/src/commands/compile/collect_modules/reexport_prune.rs index ec729cb5cd..74460865b2 100644 --- a/crates/perry/src/commands/compile/collect_modules/reexport_prune.rs +++ b/crates/perry/src/commands/compile/collect_modules/reexport_prune.rs @@ -1,7 +1,8 @@ //! Collection-time, monotone export demand. Ordinary imports are retained. An //! unused re-export is postponed only when its entire static dependency tree -//! is covered by package sideEffects contracts. Later importers can reactivate -//! it; HIR edges are removed only after every collection root has settled. +//! is covered by package sideEffects contracts or inert-initialization proofs. +//! Later importers can reactivate it; HIR edges are removed only after every +//! collection root has settled. use std::collections::{HashMap, HashSet}; use std::path::{Path, PathBuf}; @@ -11,6 +12,7 @@ use perry_hir::{Export, Import, ImportSpecifier}; use super::super::CompilationContext; mod forwarding; +mod purity; mod scan; mod side_effects; pub(super) use forwarding::normalize as normalize_forwarding_barrel; @@ -161,6 +163,7 @@ impl ReexportPruner { pub(super) fn record( ctx: &mut CompilationContext, from: &Path, + from_source: &Path, index: usize, export: &Export, target: &Path, @@ -170,7 +173,8 @@ pub(super) fn record( return true; } let mut state = std::mem::take(&mut ctx.reexport_pruner); - let safe = state.scan.declared_pure(from) && state.scan.can_drop_tree(source_path, ctx); + let safe = + state.scan.module_is_pure(from_source, ctx) && state.scan.can_drop_tree(source_path, ctx); let mut edge = Edge { from: from.to_owned(), index, diff --git a/crates/perry/src/commands/compile/collect_modules/reexport_prune/forwarding.rs b/crates/perry/src/commands/compile/collect_modules/reexport_prune/forwarding.rs index ccad9a82f0..a875eaa82c 100644 --- a/crates/perry/src/commands/compile/collect_modules/reexport_prune/forwarding.rs +++ b/crates/perry/src/commands/compile/collect_modules/reexport_prune/forwarding.rs @@ -1,8 +1,9 @@ //! Published barrels (notably Remeda) spell re-exports as imports followed by //! `export { local as public }`. Normalize only modules whose runtime imports -//! are named bindings forwarded through local export lists, with an entirely -//! side-effect-free static dependency tree. Converting the complete import -//! group preserves dependency order even when pure initializers form a cycle. +//! are bare imports or named bindings forwarded through local export lists, +//! with an entirely side-effect-free, acyclic static dependency tree. Bare +//! imports remain imports; the cycle proof permits moving forwarded bindings +//! after them without changing the initialization of exported values. use std::collections::{HashMap, HashSet}; use std::path::Path; @@ -96,22 +97,20 @@ pub(crate) fn normalize( return None; } - // Collection visits imports before re-exports. Moving only part of that - // group (or mixing it with existing re-exports) can reverse the entry into - // a cycle: even sideEffects:false modules can read each other's exported - // `var` initializers. Require the entire runtime group to move together. + // Keep namespaces and bindings with uses other than forwarding. Genuine + // bare imports can stay in place: can_drop_tree below proves the complete + // graph free of effects AND cycles before any dependency order changes. + // This covers generated barrels that repeat their shared helper chunks + // as bare imports, as well as mixed direct/indirect re-exports. if module.body.iter().any(|item| match item { ast::ModuleItem::ModuleDecl(ast::ModuleDecl::Import(import)) if !import.type_only => { - import.specifiers.is_empty() - || import.specifiers.iter().any(|spec| match spec { - ast::ImportSpecifier::Named(named) => { - !named.is_type_only && !candidates.contains(named.local.sym.as_ref()) - } - _ => true, - }) + import.specifiers.iter().any(|spec| match spec { + ast::ImportSpecifier::Named(named) => { + !named.is_type_only && !candidates.contains(named.local.sym.as_ref()) + } + _ => true, + }) } - ast::ModuleItem::ModuleDecl(ast::ModuleDecl::ExportNamed(export)) => export.src.is_some(), - ast::ModuleItem::ModuleDecl(ast::ModuleDecl::ExportAll(_)) => true, _ => false, }) { return None; diff --git a/crates/perry/src/commands/compile/collect_modules/reexport_prune/purity.rs b/crates/perry/src/commands/compile/collect_modules/reexport_prune/purity.rs new file mode 100644 index 0000000000..31bba094fe --- /dev/null +++ b/crates/perry/src/commands/compile/collect_modules/reexport_prune/purity.rs @@ -0,0 +1,139 @@ +//! Deliberately small proof for modules without a package sideEffects contract. +//! Only expressions whose evaluation cannot call user code, read a binding, or +//! throw are accepted. Dependencies are checked separately by the scanner. + +use swc_ecma_ast as ast; + +pub(super) fn module_is_pure(module: &ast::Module) -> bool { + module.body.iter().all(|item| match item { + ast::ModuleItem::Stmt(ast::Stmt::Empty(_)) => true, + ast::ModuleItem::Stmt(ast::Stmt::Expr(stmt)) => expr_is_pure(&stmt.expr), + ast::ModuleItem::Stmt(ast::Stmt::Decl(decl)) => decl_is_pure(decl), + ast::ModuleItem::ModuleDecl(decl) => match decl { + ast::ModuleDecl::Import(import) => { + import.type_only + || (import.phase == ast::ImportPhase::Evaluation && import.with.is_none()) + } + ast::ModuleDecl::ExportNamed(export) => export.with.is_none(), + ast::ModuleDecl::ExportAll(export) => export.with.is_none(), + ast::ModuleDecl::ExportDecl(export) => decl_is_pure(&export.decl), + ast::ModuleDecl::ExportDefaultDecl(export) => match &export.decl { + ast::DefaultDecl::Fn(function) => function_is_pure(&function.function), + ast::DefaultDecl::TsInterfaceDecl(_) => true, + _ => false, + }, + ast::ModuleDecl::ExportDefaultExpr(export) => expr_is_pure(&export.expr), + _ => false, + }, + _ => false, + }) +} + +fn decl_is_pure(decl: &ast::Decl) -> bool { + match decl { + ast::Decl::Fn(function) => function_is_pure(&function.function), + ast::Decl::Var(var) => { + var.declare + || var.decls.iter().all(|decl| { + // Destructuring can invoke iterators/getters even when the + // initializer is a freshly created object or array. + matches!(decl.name, ast::Pat::Ident(_)) + && decl.init.as_deref().is_none_or(expr_is_pure) + }) + } + ast::Decl::TsInterface(_) | ast::Decl::TsTypeAlias(_) => true, + _ => false, + } +} + +fn function_is_pure(function: &ast::Function) -> bool { + // Parameters and the body execute on invocation; decorators execute when + // the enclosing module initializes. Do not inspect deferred function code. + function.decorators.is_empty() + && function + .params + .iter() + .all(|param| param.decorators.is_empty()) +} + +fn expr_is_pure(expr: &ast::Expr) -> bool { + match expr { + ast::Expr::Lit(lit) => !matches!(lit, ast::Lit::JSXText(_)), + ast::Expr::Fn(function) => function_is_pure(&function.function), + ast::Expr::Arrow(_) => true, + ast::Expr::Paren(paren) => expr_is_pure(&paren.expr), + ast::Expr::TsAs(expr) => expr_is_pure(&expr.expr), + ast::Expr::TsSatisfies(expr) => expr_is_pure(&expr.expr), + ast::Expr::TsTypeAssertion(expr) => expr_is_pure(&expr.expr), + ast::Expr::TsConstAssertion(expr) => expr_is_pure(&expr.expr), + ast::Expr::TsNonNull(expr) => expr_is_pure(&expr.expr), + ast::Expr::Array(array) => array + .elems + .iter() + .flatten() + .all(|element| element.spread.is_none() && expr_is_pure(&element.expr)), + ast::Expr::Object(object) => object.props.iter().all(|property| { + let ast::PropOrSpread::Prop(property) = property else { + return false; + }; + match property.as_ref() { + ast::Prop::KeyValue(property) => { + !matches!(property.key, ast::PropName::Computed(_)) + && expr_is_pure(&property.value) + } + _ => false, + } + }), + // Even an identifier read can throw (TDZ/unbound), property reads can + // invoke getters, and arithmetic can coerce objects or mix BigInts. + // Classes can evaluate extends, computed keys, decorators and statics. + _ => false, + } +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn accepts_only_inert_initialization_and_deferred_function_bodies() { + for source in [ + "export { answer } from './answer.js'; export * from './unused.js';", + "import './dependency.js'; export const unused = 99;", + "'use strict'; export const values = [1, null, { ok: true }];", + "export function unused(x = effect()) { return effect(x); }", + "export const unused = async () => await import('./lazy.js');", + "export default function () { throw new Error('called'); }", + "export const unused = ({ value: 42 } as const);", + "export interface Shape { x: number }; export type Alias = string;", + ] { + let module = perry_parser::parse_typescript(source, "fixture.ts").unwrap(); + assert!(module_is_pure(&module), "{source}"); + } + } + + #[test] + fn preserves_observable_or_unknown_initialization() { + for source in [ + "console.log('effect'); export const unused = 99;", + "export const unused = effect();", + "export const unused = missing;", + "export const unused = object.getter;", + "export const unused = 1n + 1;", + "export const unused = { [key]: 1 };", + "export const unused = { ...object };", + "export const unused = [...array];", + "export const { unused } = { get unused() { effect(); } };", + "export class Unused { static value = effect(); }", + "export default class extends effect() {}", + "export const unused = import('./lazy.js');", + "await effect(); export const unused = 99;", + "export enum Unused { Value = effect() }", + "using unused = resource();", + "import value from './asset.js' with { type: 'file' };", + ] { + let module = perry_parser::parse_typescript(source, "fixture.ts").unwrap(); + assert!(!module_is_pure(&module), "{source}"); + } + } +} diff --git a/crates/perry/src/commands/compile/collect_modules/reexport_prune/scan.rs b/crates/perry/src/commands/compile/collect_modules/reexport_prune/scan.rs index a24c61736c..d4ea1faea7 100644 --- a/crates/perry/src/commands/compile/collect_modules/reexport_prune/scan.rs +++ b/crates/perry/src/commands/compile/collect_modules/reexport_prune/scan.rs @@ -20,6 +20,7 @@ struct Summary { dependencies: Vec, unknown_exports: bool, unknown_dependencies: bool, + pure: bool, } #[derive(Default)] @@ -31,8 +32,11 @@ pub(super) struct Scanner { } impl Scanner { - pub(super) fn declared_pure(&mut self, path: &Path) -> bool { - self.contracts.is_pure(path) + pub(super) fn module_is_pure(&mut self, path: &Path, ctx: &mut CompilationContext) -> bool { + let canonical = path.canonicalize().unwrap_or_else(|_| path.to_owned()); + self.contracts + .is_pure(&canonical) + .unwrap_or_else(|| self.summary(path, ctx).pure) } fn summary(&mut self, path: &Path, ctx: &mut CompilationContext) -> Summary { @@ -54,6 +58,7 @@ impl Scanner { if let Some(module) = parsed { let defined = ctx.parsed_defines.apply(&module); let module = defined.as_ref().unwrap_or(&module); + result.pure = super::purity::module_is_pure(module); let mut opaque = OpaqueLoads::default(); module.visit_with(&mut opaque); result.unknown_dependencies = opaque.0; @@ -65,6 +70,8 @@ impl Scanner { let mut star = false; match decl { ast::ModuleDecl::Import(import) if !import.type_only => { + result.unknown_dependencies |= + import.with.is_some() || import.phase != ast::ImportPhase::Evaluation; if import.specifiers.is_empty() || import.specifiers.iter().any( |s| !matches!(s, ast::ImportSpecifier::Named(n) if n.is_type_only), @@ -74,10 +81,12 @@ impl Scanner { } } ast::ModuleDecl::ExportAll(export) if !export.type_only => { + result.unknown_dependencies |= export.with.is_some(); dependency = Some(&export.src); star = true; } ast::ModuleDecl::ExportNamed(export) if !export.type_only => { + result.unknown_dependencies |= export.with.is_some(); let mut runtime = export.specifiers.is_empty(); for spec in &export.specifiers { let name = match spec { @@ -252,7 +261,9 @@ impl Scanner { self.droppable.insert(root, false); return false; } - if !self.declared_pure(&canonical) { + // Preserve the source-visible base while inference fills the AST + // summary, just as ordinary collection resolves relative imports. + if !self.module_is_pure(&path, ctx) { self.droppable.insert(root, false); return false; } diff --git a/crates/perry/src/commands/compile/collect_modules/reexport_prune/side_effects.rs b/crates/perry/src/commands/compile/collect_modules/reexport_prune/side_effects.rs index b7a1a463e0..ba3b75cd52 100644 --- a/crates/perry/src/commands/compile/collect_modules/reexport_prune/side_effects.rs +++ b/crates/perry/src/commands/compile/collect_modules/reexport_prune/side_effects.rs @@ -3,12 +3,14 @@ use std::path::{Path, PathBuf}; #[derive(Default)] pub(super) struct Contracts { - files: HashMap, + files: HashMap>, manifests: HashMap>, } impl Contracts { - pub(super) fn is_pure(&mut self, path: &Path) -> bool { + /// None permits AST inference. An explicit effectful/unknown contract + /// vetoes omission even when the current source looks inert. + pub(super) fn is_pure(&mut self, path: &Path) -> Option { if let Some(pure) = self.files.get(path) { return *pure; } @@ -17,7 +19,7 @@ impl Contracts { pure } - fn lookup(&mut self, path: &Path) -> bool { + fn lookup(&mut self, path: &Path) -> Option { // Stop at the owning package, never inherit a parent's contract across // nested node_modules. Type-only package.json files inside dist/ may // omit sideEffects; the owning package's declaration still applies. @@ -28,21 +30,18 @@ impl Contracts { prefix.push(component); if component.as_os_str() == "node_modules" { let Some(name) = components.next() else { - return false; + return Some(false); }; prefix.push(name); if name.as_os_str().to_string_lossy().starts_with('@') { let Some(name) = components.next() else { - return false; + return Some(false); }; prefix.push(name); } package_root = Some(prefix.clone()); } } - let Some(root) = package_root else { - return false; - }; for dir in path.parent().into_iter().flat_map(Path::ancestors) { let manifest = self.manifests.entry(dir.to_owned()).or_insert_with(|| { std::fs::read(dir.join("package.json")) @@ -50,14 +49,14 @@ impl Contracts { .and_then(|bytes| serde_json::from_slice(&bytes).ok()) }); if manifest.is_none() && dir.join("package.json").exists() { - return false; + return Some(false); } if let Some(value) = manifest.as_ref().and_then(|v| v.get("sideEffects")) { - return match value { + return Some(match value { serde_json::Value::Bool(false) => true, serde_json::Value::Array(patterns) => { let Ok(relative) = path.strip_prefix(dir) else { - return false; + return Some(false); }; let relative = relative.to_string_lossy().replace('\\', "/"); patterns.iter().all(|pattern| { @@ -67,13 +66,25 @@ impl Contracts { }) } _ => false, - }; + }); } - if dir == root { + // Canonical workspace-package paths may live outside node_modules. + // Type-only dist manifests do not hide their owning contract. + let boundary = package_root.as_deref().map_or_else( + || { + manifest.as_ref().is_some_and(|value| { + !value + .as_object() + .is_some_and(|object| object.len() == 1 && object.contains_key("type")) + }) + }, + |root| dir == root, + ); + if boundary { break; } } - false + None } } @@ -145,6 +156,54 @@ fn segment(pattern: &[u8], text: &[u8]) -> bool { mod tests { use super::*; + #[test] + fn missing_contracts_allow_inference_but_explicit_contracts_override_it() { + let dir = tempfile::tempdir().unwrap(); + let package = dir.path().join("node_modules/fixture"); + std::fs::create_dir_all(package.join("dist")).unwrap(); + let module = package.join("dist/index.js"); + for (manifest, expected) in [ + (r#"{"name":"fixture"}"#, None), + (r#"{"sideEffects":false}"#, Some(true)), + (r#"{"sideEffects":true}"#, Some(false)), + (r#"{"sideEffects":["**/*.js"]}"#, Some(false)), + (r#"{"sideEffects":["**/*.css"]}"#, Some(true)), + (r#"{"sideEffects":["[ab].js"]}"#, Some(false)), + ("invalid json", Some(false)), + ] { + std::fs::write(package.join("package.json"), manifest).unwrap(); + assert_eq!( + Contracts::default().is_pure(&module), + expected, + "{manifest}" + ); + } + } + + #[test] + fn contracts_respect_workspace_and_nested_package_boundaries() { + let dir = tempfile::tempdir().unwrap(); + std::fs::write(dir.path().join("package.json"), r#"{"sideEffects":false}"#).unwrap(); + let nested = dir.path().join("node_modules/fixture/dist"); + std::fs::create_dir_all(&nested).unwrap(); + let mut contracts = Contracts::default(); + assert_eq!(contracts.is_pure(&dir.path().join("source.js")), Some(true)); + assert_eq!(contracts.is_pure(&nested.join("index.js")), None); + std::fs::write(nested.join("package.json"), r#"{"sideEffects":true}"#).unwrap(); + assert_eq!( + Contracts::default().is_pure(&nested.join("index.js")), + Some(false) + ); + + let dist = dir.path().join("dist/esm"); + std::fs::create_dir_all(&dist).unwrap(); + std::fs::write(dist.join("package.json"), r#"{"type":"module"}"#).unwrap(); + assert_eq!( + Contracts::default().is_pure(&dist.join("index.js")), + Some(true) + ); + } + #[test] fn glob_contracts_fail_closed() { for (pattern, path) in [ diff --git a/crates/perry/tests/class_prototype_symbol_props.rs b/crates/perry/tests/class_prototype_symbol_props.rs new file mode 100644 index 0000000000..7f172e056d --- /dev/null +++ b/crates/perry/tests/class_prototype_symbol_props.rs @@ -0,0 +1,159 @@ +//! Symbol-keyed properties added to a declared class's `.prototype` after the +//! declaration (`C.prototype[S] = f`, `Object.defineProperty(C.prototype, S, ...)`, +//! `Object.assign(C.prototype, { [Symbol.iterator]() {} })`) must be visible on +//! instances and subclass instances, exactly like string keys. drizzle-orm's +//! `applyEffectWrapper` installs effect's `Effectable.Prototype` this way, and +//! `yield* query` needs the copied `[Symbol.iterator]`. + +use std::path::PathBuf; +use std::process::Command; +use std::sync::Once; + +fn perry_bin() -> PathBuf { + PathBuf::from(env!("CARGO_BIN_EXE_perry")) +} + +fn workspace_root() -> PathBuf { + PathBuf::from(env!("CARGO_MANIFEST_DIR")) + .join("../..") + .canonicalize() + .expect("canonicalize workspace root") +} + +fn runtime_dir() -> PathBuf { + if let Some(dir) = std::env::var_os("PERRY_RUNTIME_DIR") { + return PathBuf::from(dir); + } + static BUILD_RUNTIME: Once = Once::new(); + BUILD_RUNTIME.call_once(|| { + let cargo = std::env::var_os("CARGO").unwrap_or_else(|| "cargo".into()); + let build = Command::new(cargo) + .current_dir(workspace_root()) + .arg("build") + .arg("-p") + .arg("perry-runtime-static") + .arg("-p") + .arg("perry-stdlib-static") + .output() + .expect("build static runtime archives"); + assert!( + build.status.success(), + "static runtime build failed\nstdout:\n{}\nstderr:\n{}", + String::from_utf8_lossy(&build.stdout), + String::from_utf8_lossy(&build.stderr) + ); + }); + let target = std::env::var_os("CARGO_TARGET_DIR") + .map(PathBuf::from) + .unwrap_or_else(|| workspace_root().join("target")); + target.join("debug") +} + +const SOURCE: &str = r#" +const t = (name: string, f: () => any) => { try { console.log(name, JSON.stringify(f())) } catch (e: any) { console.log(name, "THROW", e.message) } } +const fn = function (this: any) { return "called" } +const custom = Symbol("custom") +class A1 {}; (A1.prototype as any)[Symbol.iterator] = fn +t("P1", () => typeof (new A1() as any)[Symbol.iterator]) +class A2 {}; Object.assign(A2.prototype, { [Symbol.iterator]: fn }) +t("P2", () => typeof (new A2() as any)[Symbol.iterator]) +class A3 {}; Object.defineProperty(A3.prototype, Symbol.iterator, { value: fn, writable: true, configurable: true }) +t("P3", () => typeof (new A3() as any)[Symbol.iterator]) +class A4 {}; Object.assign(A4.prototype, { [custom]: fn }) +t("P4", () => (new A4() as any)[custom]()) +class A5 {}; (A5.prototype as any)[Symbol.iterator] = fn +class A5s extends A5 {} +t("P5", () => typeof (new A5s() as any)[Symbol.iterator]) +t("P6", () => Symbol.iterator in (new A2() as any)) +class A7 {}; Object.defineProperty(A7.prototype, custom, { get(this: any) { return this.v }, configurable: true }) +t("P7", () => { const a: any = new A7(); a.v = 7; return a[custom] }) +class A8 { [Symbol.iterator]() { return "declared" } } +t("P8", () => (new A8() as any)[Symbol.iterator]()) +class SingleShotGen { called = false; self: any; constructor(self: any) { this.self = self } next(a?: any) { return this.called ? { value: a, done: true } : ((this.called = true), { value: this.self, done: false }) } } +const EffectProto: any = { op: "E", [Symbol.iterator]() { return new SingleShotGen(this) } } +class Raw { execute: () => number; constructor(execute: () => number) { this.execute = execute } } +Object.assign(Raw.prototype, { ...EffectProto }) +class SubRaw extends Raw {} +t("Y1", () => { const r = new Raw(() => 1); function* g(): Generator { const x = yield* r; return x } const it = g(); const a = it.next(); const b = it.next("sent"); return [a.value === r, b.value, b.done] }) +t("Y2", () => { const r = new SubRaw(() => 2); function* g(): Generator { return yield* r } const it = g(); it.next(); return it.next("sub").value }) +class It {}; (It.prototype as any)[Symbol.iterator] = function* () { yield 1; yield 2 } +t("Y3", () => { const out: number[] = []; for (const v of new It() as any) out.push(v); return [out, [...(new It() as any)]] }) +let getterCalls = 0 +class AccessorBase {} +Object.defineProperty(AccessorBase.prototype, custom, { get() { getterCalls++; return 7 }, configurable: true }) +class AccessorChild extends AccessorBase {} +const accessor: any = new AccessorChild() +t("H1", () => [custom in accessor, getterCalls]) +t("H2", () => [accessor[custom], getterCalls]) +class UndefinedBase {} +Object.defineProperty(UndefinedBase.prototype, custom, { value: undefined }) +t("H3", () => custom in new UndefinedBase()) +class Overridden {} +Object.defineProperty(Overridden.prototype, custom, { value: 9 }) +const noPrototype: any = new Overridden() +Object.setPrototypeOf(noPrototype, null) +t("H4", () => [noPrototype[custom], custom in noPrototype]) +const replacementPrototype: any = new Overridden() +Object.setPrototypeOf(replacementPrototype, {}) +t("H5", () => [replacementPrototype[custom], custom in replacementPrototype]) +"#; + +const EXPECTED: &str = "P1 \"function\" +P2 \"function\" +P3 \"function\" +P4 \"called\" +P5 \"function\" +P6 true +P7 7 +P8 \"declared\" +Y1 [true,\"sent\",true] +Y2 \"sub\" +Y3 [[1,2],[1,2]] +H1 [true,0] +H2 [7,1] +H3 true +H4 [null,false] +H5 [null,false] +"; + +#[test] +fn symbol_props_written_to_declared_class_prototypes_reach_instances() { + let dir = tempfile::tempdir().expect("tempdir"); + let entry = dir.path().join("main.ts"); + let output = dir.path().join("main_bin"); + std::fs::write(&entry, SOURCE).expect("write entry"); + let oracle = Command::new("node") + .arg(&entry) + .output() + .expect("run Node oracle"); + assert!(oracle.status.success(), "Node oracle failed: {:?}", oracle); + assert_eq!(String::from_utf8_lossy(&oracle.stdout), EXPECTED); + let compile = Command::new(perry_bin()) + .current_dir(dir.path()) + .arg("compile") + .arg(&entry) + .arg("-o") + .arg(&output) + .arg("--no-cache") + .env("PERRY_NO_AUTO_OPTIMIZE", "1") + .env("PERRY_RUNTIME_DIR", runtime_dir()) + .output() + .expect("run perry compile"); + assert!( + compile.status.success(), + "perry compile failed\nstdout:\n{}\nstderr:\n{}", + String::from_utf8_lossy(&compile.stdout), + String::from_utf8_lossy(&compile.stderr) + ); + let run = Command::new(&output) + .current_dir(dir.path()) + .output() + .expect("run compiled binary"); + assert!( + run.status.success(), + "compiled binary failed\nstdout:\n{}\nstderr:\n{}", + String::from_utf8_lossy(&run.stdout), + String::from_utf8_lossy(&run.stderr) + ); + assert_eq!(String::from_utf8_lossy(&run.stdout), EXPECTED); +} diff --git a/crates/perry/tests/source_graph_export_regressions/issue_10180.rs b/crates/perry/tests/source_graph_export_regressions/issue_10180.rs index 45bd5a088f..b6d1efeb44 100644 --- a/crates/perry/tests/source_graph_export_regressions/issue_10180.rs +++ b/crates/perry/tests/source_graph_export_regressions/issue_10180.rs @@ -7,6 +7,9 @@ use std::process::Command; use super::{perry_bin, runtime_dir}; +#[path = "issue_10180/purity.rs"] +mod purity; + fn write(root: &Path, path: &str, text: &str) { let path = root.join(path); std::fs::create_dir_all(path.parent().unwrap()).unwrap(); @@ -55,6 +58,11 @@ fn fixture(side_effects: Option) -> tempfile::TempDir { fn compile(root: &Path, disabled: bool, collect_only: bool) -> (Vec, String) { let cache = root.join(if disabled { "cache-off" } else { "cache-on" }); let binary = root.join(if disabled { "main-off" } else { "main-on" }); + // A collect-only invocation may follow a complete compile in this fixture. + // Its output assertion must observe this invocation, not the earlier binary. + if binary.exists() { + std::fs::remove_file(&binary).unwrap(); + } let mut command = Command::new(perry_bin()); command .current_dir(root) @@ -455,7 +463,7 @@ fn forwarding_barrels_keep_effectful_dependencies_and_bare_imports() { ); let (paths, output) = compile(dir.path(), false, false); assert_eq!(output, "42\n"); - assert!(contains(&paths, "/fixture/unused.js")); + assert!(!contains(&paths, "/fixture/unused.js")); assert!(contains(&paths, "/fixture/bare.js")); write( diff --git a/crates/perry/tests/source_graph_export_regressions/issue_10180/purity.rs b/crates/perry/tests/source_graph_export_regressions/issue_10180/purity.rs new file mode 100644 index 0000000000..bb437d5dc4 --- /dev/null +++ b/crates/perry/tests/source_graph_export_regressions/issue_10180/purity.rs @@ -0,0 +1,292 @@ +use super::{compile, contains, fixture, perry_bin, runtime_dir, write}; +use std::path::Path; +use std::process::Command; + +fn node_output(root: &Path) -> String { + let output = Command::new("node") + .current_dir(root) + .args(["--experimental-strip-types", "main.ts"]) + .output() + .unwrap(); + assert!(output.status.success(), "{:?}", output); + String::from_utf8(output.stdout) + .unwrap() + .replace("\r\n", "\n") +} + +#[test] +fn inferred_pure_siblings_are_not_compiled_without_package_metadata() { + let dir = fixture(None); + write(dir.path(), "node_modules/fixture/unused.js", "export function unused() { console.log('only when called'); } export const data = [1, { value: 2 }];"); + let (on, output) = compile(dir.path(), false, false); + let (off, baseline) = compile(dir.path(), true, false); + assert_eq!(output, node_output(dir.path())); + assert_eq!(output, baseline); + assert!(!contains(&on, "/fixture/unused.js")); + assert!(contains(&off, "/fixture/unused.js")); + assert_eq!(off.len(), on.len() + 1); +} + +#[test] +fn inferred_purity_applies_to_first_party_forwarding_barrels() { + let dir = fixture(None); + write( + dir.path(), + "main.ts", + "import { used } from './barrel.js'; console.log(used);", + ); + write(dir.path(), "barrel.js", "import { used } from './used.js'; import { unused } from './unused.js'; export { used, unused };"); + write(dir.path(), "used.js", "export const used = 42;"); + write(dir.path(), "unused.js", "export const unused = 99;"); + let (on, output) = compile(dir.path(), false, false); + assert!(!contains(&on, "/unused.js")); + assert_eq!(output, node_output(dir.path())); + assert_eq!(output, compile(dir.path(), true, false).1); +} + +#[cfg(any(unix, windows))] +#[test] +fn inferred_purity_resolves_dependencies_from_the_source_visible_path() { + let dir = fixture(None); + write( + dir.path(), + "main.ts", + "import { used } from './visible/barrel/index.js'; console.log(used);", + ); + write( + dir.path(), + "actual/barrel/index.js", + "export { used } from './used.js'; export { unused } from './unused.js';", + ); + write( + dir.path(), + "actual/barrel/used.js", + "export const used = 42;", + ); + write( + dir.path(), + "actual/barrel/unused.js", + "import '../effect.js'; export const unused = 99;", + ); + write(dir.path(), "actual/effect.js", "export const inert = 0;"); + write(dir.path(), "visible/effect.js", "console.log('visible');"); + let actual = dir.path().join("actual/barrel"); + let alias = dir.path().join("visible/barrel"); + #[cfg(unix)] + std::os::unix::fs::symlink(&actual, &alias).unwrap(); + #[cfg(windows)] + { + // Directory junctions need no administrator/developer-mode privilege. + let output = Command::new("cmd") + .args(["/C", "mklink", "/J"]) + .arg(alias.to_string_lossy().replace('/', "\\")) + .arg(actual.to_string_lossy().replace('/', "\\")) + .output() + .unwrap(); + assert!(output.status.success(), "{:?}", output); + } + let (on, output) = compile(dir.path(), false, false); + assert!(contains(&on, "/actual/barrel/unused.js")); + assert!(contains(&on, "/visible/effect.js")); + assert!(!contains(&on, "/actual/effect.js")); + assert_eq!(output, "visible\n42\n"); + assert_eq!(output, compile(dir.path(), true, false).1); + // Perry deliberately uses the lexical import base through symlinks. + let node = Command::new("node") + .current_dir(dir.path()) + .args([ + "--preserve-symlinks", + "--experimental-strip-types", + "main.ts", + ]) + .output() + .unwrap(); + assert!(node.status.success(), "{:?}", node); + assert_eq!( + output, + String::from_utf8(node.stdout) + .unwrap() + .replace("\r\n", "\n") + ); +} + +#[test] +fn mixed_forwarding_barrels_prune_unused_siblings_and_keep_bare_dependencies() { + for contract in [None, Some(false.into())] { + let dir = fixture(contract); + write(dir.path(), "node_modules/fixture/index.js", "import { used } from './used.js'; import { unused } from './unused.js'; import './shared.js'; export { used, unused }; export * from './extra.js';"); + write( + dir.path(), + "node_modules/fixture/shared.js", + "export const shared = 7;", + ); + write( + dir.path(), + "node_modules/fixture/extra.js", + "export const extra = 8;", + ); + let (on, output) = compile(dir.path(), false, false); + assert!(!contains(&on, "/fixture/unused.js")); + assert!(!contains(&on, "/fixture/extra.js")); + assert!(contains(&on, "/fixture/shared.js")); + assert_eq!(output, node_output(dir.path())); + assert_eq!(output, compile(dir.path(), true, false).1); + } +} + +#[test] +fn mixed_forwarding_barrels_preserve_the_entry_into_a_cycle() { + for contract in [None, Some(false.into())] { + let dir = fixture(contract); + write( + dir.path(), + "node_modules/fixture/index.js", + "import { a } from './a.js'; import './b.js'; export { a };", + ); + write( + dir.path(), + "node_modules/fixture/a.js", + "import { b } from './b.js'; export var a = (b ?? 0) + 1;", + ); + write( + dir.path(), + "node_modules/fixture/b.js", + "import { a } from './a.js'; export var b = (a ?? 0) + 1;", + ); + write( + dir.path(), + "main.ts", + "import { a } from 'fixture'; console.log(a);", + ); + let (_, output) = compile(dir.path(), false, false); + assert_eq!(output, "2\n"); + assert_eq!(output, node_output(dir.path())); + assert_eq!(output, compile(dir.path(), true, false).1); + } +} + +#[test] +fn inferred_purity_retains_effectful_sibling_in_esm_order() { + let dir = fixture(None); + write( + dir.path(), + "node_modules/fixture/used.js", + "console.log('used'); export const used = 42;", + ); + write( + dir.path(), + "node_modules/fixture/unused.js", + "console.log('unused'); export const unused = 99;", + ); + write(dir.path(), "node_modules/fixture/index.js", "export { used } from './used.js'; export { unused } from './unused.js'; export * from './inert.js';"); + write( + dir.path(), + "node_modules/fixture/inert.js", + "export const inert = 0;", + ); + let (on, output) = compile(dir.path(), false, false); + assert!(contains(&on, "/fixture/unused.js")); + assert!(!contains(&on, "/fixture/inert.js")); + assert_eq!(output, "used\nunused\n42\n"); + assert_eq!(output, node_output(dir.path())); + assert_eq!(output, compile(dir.path(), true, false).1); +} + +#[test] +fn inferred_purity_checks_dependencies_and_honors_explicit_contracts() { + let dir = fixture(None); + write( + dir.path(), + "node_modules/external/package.json", + r#"{"name":"external","type":"module","main":"index.js"}"#, + ); + write( + dir.path(), + "node_modules/external/index.js", + "console.log('external');", + ); + write( + dir.path(), + "node_modules/fixture/unused.js", + "import 'external'; export const unused = 99;", + ); + let (on, output) = compile(dir.path(), false, false); + assert!(contains(&on, "/fixture/unused.js")); + assert!(contains(&on, "/external/index.js")); + assert_eq!(output, "external\n42\n"); + assert_eq!(output, node_output(dir.path())); + assert_eq!(output, compile(dir.path(), true, false).1); + + for contract in [serde_json::json!(true), serde_json::json!(["[ab].js"])] { + let dir = fixture(Some(contract)); + let (on, _) = compile(dir.path(), false, true); + assert!(contains(&on, "/fixture/unused.js")); + } +} + +#[test] +fn inferred_pure_dynamic_target_is_available_but_not_eagerly_initialized() { + let dir = fixture(None); + write( + dir.path(), + "node_modules/fixture/lazy.js", + "console.log('lazy init'); export const lazy = 7;", + ); + write(dir.path(), "main.ts", "import { used } from 'fixture'; export async function load() { return import('fixture/lazy.js'); } console.log(used);"); + let (on, output) = compile(dir.path(), false, false); + assert!(!contains(&on, "/fixture/unused.js")); + assert!(contains(&on, "/fixture/lazy.js")); + assert_eq!(output, "42\n"); + assert_eq!(output, node_output(dir.path())); + assert_eq!(output, compile(dir.path(), true, false).1); + compile(dir.path(), false, true); + let graph: serde_json::Value = serde_json::from_slice( + &std::fs::read(dir.path().join("cache-on/module-graph.json")).unwrap(), + ) + .unwrap(); + let lazy = graph["modules"] + .as_array() + .unwrap() + .iter() + .find(|module| { + module["path"] + .as_str() + .unwrap() + .replace('\\', "/") + .ends_with("/fixture/lazy.js") + }) + .unwrap(); + assert_eq!(lazy["init"], "deferred"); +} + +#[test] +fn cached_build_rechecks_inferred_purity_when_omitted_source_gains_an_effect() { + let dir = fixture(None); + let binary = dir.path().join("cached-inferred"); + let run = || { + let output = Command::new(perry_bin()) + .current_dir(dir.path()) + .args(["compile", "main.ts", "--cache-dir", "cache", "-o"]) + .arg(&binary) + .env("PERRY_NO_AUTO_OPTIMIZE", "1") + .env("PERRY_RUNTIME_DIR", runtime_dir()) + .env("PERRY_NO_REEXPORT_PRUNE", "0") + .env_remove("PERRY_NO_CACHE") + .env_remove("PERRY_COLLECT_ONLY") + .output() + .unwrap(); + assert!(output.status.success(), "{:?}", output); + let output = Command::new(&binary).output().unwrap(); + assert!(output.status.success(), "{:?}", output); + String::from_utf8(output.stdout).unwrap() + }; + assert_eq!(run(), "42\n"); + assert_eq!(run(), "42\n"); + write( + dir.path(), + "node_modules/fixture/unused.js", + "console.log('restored'); export const unused = 99;", + ); + assert_eq!(run(), "restored\n42\n"); + assert_eq!(run(), node_output(dir.path())); +} diff --git a/docs/src/cli/flags.md b/docs/src/cli/flags.md index 8e8b915f98..0154c8bf04 100644 --- a/docs/src/cli/flags.md +++ b/docs/src/cli/flags.md @@ -622,18 +622,23 @@ every claimed profile selection. ## Unused re-export collection Collection prunes unused `export { name } from`, `export * from`, and namespace -re-export edges by default when the exporting package declares the file free -of side effects and every static dependency of the omitted target has the same -guarantee. Perry honors `sideEffects: false` and arrays of `*`, `**`, and `?` -globs. Unsupported patterns, missing contracts, CommonJS, unresolved dependencies, +re-export edges by default when the exporting file and every static dependency +of the omitted target are proven free of side effects. Perry honors +`sideEffects: false` and arrays of `*`, `**`, and `?` globs. Without a contract, +Perry recognizes inert declarations such as literal constants, functions, and +pure forwarding barrels, including first-party modules. Calls, binding/property +reads, classes, destructuring, and other unproven initialization retain the module. +Explicit effectful contracts and unsupported patterns override this inference. +CommonJS, unresolved dependencies, and cyclic static dependency trees conservatively retain the edge. Keeping cycles preserves initialization order even when exported variables read each other. This pass does not remove direct imports used by module code or individual declarations. An `import { x }; export { x }` pair is first normalized to a re-export only in barrels whose runtime imports -are all named bindings forwarded through local export lists, and whose entire -static dependency tree has side-effect-free contracts. Moving the complete -import group preserves dependency order in cycles. Mixed barrels retain their imports. +are bare imports or named bindings forwarded through local export lists, and +whose entire static dependency tree is proven free of side effects and cycles. +Bare imports remain in the graph. Cyclic barrels retain their original imports +so normalization cannot change their initialization entry point. Imports with attributes, nonstandard phases, or package aliases retain their original resolution. Namespace and dynamic imports retain the complete exported surface, and existing dynamic initialization stays deferred. diff --git a/scripts/gc_runtime_root_holders.json b/scripts/gc_runtime_root_holders.json index 40b42fb1fa..6ce5171275 100644 --- a/scripts/gc_runtime_root_holders.json +++ b/scripts/gc_runtime_root_holders.json @@ -142,6 +142,12 @@ "verdict": "not_a_gc_pointer", "why": "#9794 allocation-site sampling: bytes remaining until the next sample. A `Cell` countdown, decremented per allocation and reset on fire — a quantity, never an address." }, + { + "file": "crates/perry-runtime/src/arena/from_space.rs", + "name": "SEALED_YOUNG_BYTES", + "verdict": "not_a_gc_pointer", + "why": "Cached young-generation occupancy for the GC due check: a `Cell` holding a validity flag, the heap generation it was measured at (a u64 epoch), and a byte count (Eden bytes outside the current block plus the active survivor space). Quantities only; it never stores a block, object or header address." + }, { "file": "crates/perry-runtime/src/array/header_gc_slots.rs", "name": "DENSE_MOVE_LAYOUT_CLASSIFIED_SLOTS", @@ -307,7 +313,7 @@ "file": "crates/perry-runtime/src/gc/census.rs", "name": "PASS1_MARKED", "verdict": "non_moving_snapshot", - "why": "Real GC header addresses, deliberately untraced so the diagnostic does not keep its observed objects alive. Populated only at the end of mark propagation of a synchronous full cycle; consumed at sweep entry in the same run_to_completion invocation. The intervening full-cycle phases do not relocate or run JS callbacks. The Vec is used for membership comparisons and dropped with the census before sweep. Budgeted and minor cycles skip both boundaries. Pin re-audited 2026-09-05 after #9760 touched `gc/mod.rs`: that change is `mod heap_stats;` plus a `pub(crate) use` re-export and alters no mark/sweep control flow. `heap_stats()` is reached only from `js_bun_jsc_heap_stats` (the JS-facing `bun:jsc.heapStats()`), i.e. from mutator code, never inside a cycle, and its own module contract forbids allocation or collection during its walk. The mark-complete → sweep-entry window is unchanged. Re-audited 2026-09-05 (train125) after #9769 and #9771 touched pinned files. #9769 adds one `reg_scanner!` registration to `gc/mod.rs`; #9771 adds a feature-gated `alloc_census_init()` there and a feature-gated Rust-heap dump inside `take_census`. `alloc-census` is not in the default feature set, and decisively: `census_take_if_armed_at_full_sweep_start` does `PASS1_MARKED.with(|p| p.borrow_mut().take())` BEFORE calling `take_census`, so the snapshot has already left the thread-local by the time #9771's code runs — it cannot affect the window. Neither change alters mark/sweep control flow. Re-audited 2026-09-06 after #9831 touched `gc/policy.rs`. Its hunks are (a) the tiny-parse pressure guard's pricing (`tiny_parse_pressure_headroom_bytes`, `tiny_parse_pressure_due*`, a `Cell` byte-count base) consulted from JSON.parse's mutator-side boundaries (`gc_bump_malloc_trigger`, `gc_collect_pending_suppressed_parse`, `gc_schedule_parse_boundary_collection_if_pressure`), none of which is reachable from inside a cycle, and (b) one extra `Cell` store in `note_collection_finished_arena_occupancy`, which runs from `publish_reclaim_outcome` in the Publish subphase — after `step_sweep` has already consumed the snapshot. Mark/sweep control flow between `census_pass1_if_armed` and `census_take_if_armed_at_full_sweep_start` is untouched. Re-audited 2026-09-05 (train126) after #9755 restructured `gc/cycle.rs`. Its hunks are all root-scan machinery (`RootScanSubphase`, `RootScanCycleState`, the mutable-scanner iteration state), which runs BEFORE mark propagation completes; `gc/mod.rs` gains only a `mod young_log;` declaration. The bracketing is unchanged — `census_pass1_if_armed` is still inside `step_mark_propagation` and `census_take_if_armed_at_full_sweep_start` inside `step_sweep` — and a synchronous full mark-sweep still moves nothing between them. Re-pinned 2026-09-05 for the #9740 hot-TLS conversion of this file: the sole change is `thread_local!` → `crate::perry_thread_local!`, a macro-name swap with identical declaration syntax and `.with()` call sites. No control flow, no phase boundary, and no storage semantics change. Re-audited 2026-09-06 (train128) after #9794's GC diagnostics touched `gc/mod.rs` and `gc/policy.rs`: both gain diagnostic module declarations and counters only — no mark/sweep control flow, and the census bracketing in `step_mark_propagation` / `step_sweep` is unchanged. Re-audited for #9794's GC diagnostics: `gc/mod.rs` gains `mod diag_sites;` / `mod survival_diag;`, a re-export, a `diag_sites::full_started(...)` call at TRIGGER time (before mark propagation begins), and exit-time reporting. Nothing executes between mark-complete and sweep-entry, so the window is unchanged. Re-audited 2026-09-06 for the retained array-growth verifier fix: the cycle.rs change passes the existing non-copying evacuation verifier an explicit all-forwarded policy. That call remains in minor finalization, outside the synchronous full-cycle census window; its root and heap reads do not allocate GC objects, move objects, or invoke JS callbacks. The mark-complete and sweep-entry boundaries are unchanged. Re-audited 2026-09-05 after #9830 touched `gc/policy.rs`. That change is (a) six `thread_local! {` blocks rewritten as `crate::perry_thread_local! {` and (b) one `#[cfg(test)]` accessor listing the trigger path's hot-slot indices. The macro keeps the same storage, the same `.with()` at every read and write, and the same destructor registration (the teardown guard exists exactly when `needs_drop` holds, which is what `std::thread_local!` already decided); no value, predicate or branch in the file changes, so no mark or sweep control flow does. The one new behaviour is on a declaration's FIRST read: `HotKey::resolve_and_cache` takes a mutex and allocates a key through the GLOBAL allocator. Even if a first read landed inside this window it would be sound — the window's contract is that nothing relocates and no JS callback runs, and a mimalloc allocation does neither. `census_pass1_if_armed` is still inside `step_mark_propagation` and `census_take_if_armed_at_full_sweep_start` inside `step_sweep`; the bracketing is untouched. Re-audited 2026-09-06 (train132) after #9860 and #9845 touched `gc/mod.rs`. Both hunks are re-export lists and nothing else: #9860 adds `idle_reclaim_elapsed_starts` / `IDLE_RECLAIM_REARM_MS`, and #9845 adds `owner_is_dead_copied_minor_from_space_of_type`. No mark or sweep control flow changes. #9845's substantive work sits in `gc/oldgen.rs` and `gc/copying.rs`, neither pinned: the copying-minor arm (`finalize_dead_copied_minor_from_space_regexps`) runs on a MINOR, which skips both census boundaries; the full-cycle arm (`collect_dead_registered_regexps_post_trace`, from `with_dead_collection_finalize`) walks the RegExp registry building a Vec of addresses — no GC allocation, no JS callback, so it cannot relocate the snapshot's subjects — and it is reached from the sweep body, i.e. AFTER `census_take_if_armed_at_full_sweep_start` has already `take()`n the snapshot out of the thread-local. The mark-complete -> sweep-entry window is unchanged. Re-audited 2026-09-07 for #9965 after 1ec9e0e8a touched `gc/cycle.rs` and `gc/mod.rs`: `gc/mod.rs:216-217` only declares and imports the failure-attribution module, while `gc/cycle.rs:1414-1417` reads the trigger and diagnostic counters immediately before evacuation verification inside `atomic_finalize_minor_prelude`. Full cycles bypass `MinorPrelude` at `gc/cycle.rs:1192-1196`; evacuation remains guarded by the minor-only context at `gc/cycle.rs:1330-1372`. The snapshot store remains at `gc/cycle.rs:963-964` after synchronous full marking, and its take remains at `gc/cycle.rs:1454-1457` before sweep. No new write, relocation, collection, or JS callback was added to that full-cycle interval, so the PASS1_MARKED window is unaffected. Re-audited 2026-09-07 for the regex census rows: all new work is in `take_census` after `census_take_if_armed_at_full_sweep_start` has taken PASS1_MARKED out of TLS; neither boundary nor the intervening cycle control flow changed. Re-audited 2026-09-08 (train144) after #9976 and #9977 touched pinned files. `gc/mod.rs` gains exactly three lines: `mod copying_phase;` and `mod regex_census;` (declarations) and one `reg_scanner!(regex::site_test::scan_roots_mut)` registration. A scanner registration adds a root SOURCE for the mutable-root walks; it does not move either census boundary and runs nowhere between them. `gc/census.rs` widens `side_tables()` to `pub(super)`, extends it with regex rows and adds a test module — all census REPORTING, which runs from the diagnostic dump, not inside a cycle. Mark/sweep control flow between `census_pass1_if_armed` and `census_take_if_armed_at_full_sweep_start` is untouched. Re-audited 2026-09-08 for #9849 JSON construction deferral. `gc/mod.rs` adds the `json_defer` module/re-export and a trusted-header layout helper used only by already-validated JSON emitters; neither changes or runs in collector phase control flow. `gc/policy.rs` adds JSON completion scheduling, construction-grace checks, and safepoint deferral predicates. These are called from mutator-side JSON allocation/output boundaries and ordinary safepoint entry; they do not alter `step_mark_propagation`, `step_sweep`, or invoke callbacks or relocation between the census boundaries. The mark-complete to sweep-entry window is unchanged. The follow-up adds a cfg(test)-only one-shot boolean for deterministic explicit-pressure fixtures; it is absent from production builds and cannot affect the census window. The first predicate read consumes it, so post-parse accounting exercises normal pricing. Re-audited 2026-09-09 for bounded tiny-JSON completion polling. The policy.rs changes split the mutator-side pending-parse check into an inlined empty fast path plus an outlined debt-service path, and amortize the mutator-side arena-pressure read across 64 bounded parse completions. Neither function is reachable from step_mark_propagation or step_sweep; neither census boundary nor the synchronous full-cycle interval between them changes. Re-audited 2026-09-09 for lazy JSON record batches: policy.rs only widens gc_budgeted_cycle_active visibility from pub(super) to pub(crate). Its body remains a read-only Cell query. The new caller is lazy_get materialization in the mutator; run_to_completion, step_mark_propagation, census snapshot consumption at step_sweep, and the synchronous non-moving window are unchanged. Re-audited 2026-09-09 for completed JSON-output debt: the added gc_service_json_output_sweep function calls the existing trigger check from a rooted mutator boundary and reports whether its malloc-count request remains due. It is not called from any census or collector phase; the synchronous mark-complete to sweep-entry window is unchanged. Re-audited 2026-09-09 for the JSON byte-debt carry: the same mutator-only service helper now distinguishes requests satisfied before its call from those satisfied by its trigger check. The added enum contains no payload, both count reads are scalar, and no census boundary or collector phase changed. Re-audited 2026-09-11 for #10055: gc/mod.rs only registers the weak UTF-16 index scanner during gc_init. It neither marks strings nor allocates GC objects or runs JS; offset vectors use the Rust allocator. The mark-complete to sweep-entry census window and cycle control flow are unchanged. Re-audited 2026-09-11 for #10054: gc/mod.rs adds only the trim-cache mutable-root scanner registration in gc_init. Its scanner visits two existing string slots without allocating or invoking JS. Root scanning still precedes mark completion, and neither census boundary nor the synchronous mark-complete to sweep-entry window changes. Re-audited 2026-09-11 for #10060: the census array classifier now reads the logical element start and bounds its scan by the remaining capacity. The helper only reads the existing GC/header words and performs pointer arithmetic; it cannot allocate, collect, or call JS. This classifier runs in take_census after PASS1_MARKED has been taken out of TLS. Neither census boundary nor the mark-complete to sweep-entry control flow changed. Re-audited for #8512: gc/mod.rs only enables the existing PTY mutable-root scanner on Windows; it changes no mark/sweep phase or census boundary. The scanner visits NaN-boxed slots without running JS callbacks. Re-audited 2026-09-12 for the single regular-expression engine: `gc/mod.rs` changes `mod prefetch;` to `pub(crate) mod prefetch;` so the RegExp owner-table walks can prefetch headers, a visibility change with no new call in collector control flow; `gc/census.rs` changes only its `#[cfg(test)]` `regex_census_tests` module, dropping assertions for the previous engine's cache rows. Neither boundary (`census_pass1_if_armed` in `step_mark_propagation`, `census_take_if_armed_at_full_sweep_start` in `step_sweep`) nor the synchronous mark-complete to sweep-entry interval changes. Re-audited 2026-09-13 after the #10169 fix touched `gc/mod.rs` and `gc/policy.rs`. `gc/mod.rs` gains only `pub(crate) use` re-exports (`policy::note_young_leaf_born_old`, `policy::young_generation_holds_a_nursery`, `promote_in_place::{young_generation_measured_dying, young_generation_measured_retained}`, and cfg(test) survival seeders). `gc/policy.rs` gains a `Cell` thread-local (`GC_YOUNG_LEAF_BORN_OLD`, no pointer), its setter, a pure predicate over `copying_from_space_in_use_bytes` vs the base nursery cap, and a consumed-once branch at the top of `gc_budgeted_due_trigger` that may answer `YoungScavengeCap` ahead of `OldReclaim`. That branch decides WHICH collection a safepoint starts (a minor instead of a full); it runs before any cycle begins and never inside one, so the mark-complete → sweep-entry window of a synchronous full — where PASS1_MARKED is populated and consumed within one `run_to_completion` — is unchanged, and neither hunk adds an allocation, a JS callback, or a relocation to it. Re-audited 2026-09-13 for the heap generation (#10164 cross-call search positions): `gc/mod.rs` only declares `pub(crate) mod heap_generation;`. `gc/cycle.rs` wraps the `Sweep` and `Reclaim` arms of `GcCycleState::step` in a `HeapChange` scope and opens one inside `atomic_finalize_minor_prelude`'s evacuation branch (with a nested one around old-page defrag). Opening and closing a scope only increments two thread-local integer cells (`HEAP_GENERATION`, `OPEN_HEAP_CHANGES`); a first thread-local read may allocate a key through the global allocator, which neither relocates nor runs JS. The `Sweep` scope opens immediately before `step_sweep`, i.e. before `census_take_if_armed_at_full_sweep_start` takes PASS1_MARKED out of TLS, and adds no relocation, collection or JS callback to the synchronous mark-complete to sweep-entry window; the minor-prelude scope is unreachable from a full cycle, which bypasses `MinorPrelude`. Neither boundary nor the intervening control flow changed. Re-audited 2026-09-13 for #10182 block-granular reclamation, which touched `gc/cycle.rs`. Two hunks: (a) in the `RememberedSetRebuild` subphase of AtomicFinalize — INSIDE the window — the require-marked old-to-young rebuild is now constructed with `OldToYoungRememberedRebuildState::new_skipping`, whose cursor never enters blocks the census recorded as holding no reached, pinned or pre-marked object (`BlockCensus::unmarked_blocks`); computing that list reads `arena_block_snapshots()` and allocates one `Vec` through the global allocator. It visits a subset of the same objects the rebuild already walked (every skipped object would have been rejected as unmarked), and it neither allocates a GC object, relocates anything, nor runs a JS callback. (b) In `step_sweep`, `IncrementalSweepState::with_block_skip` runs after `census_take_if_armed_at_full_sweep_start` has already taken PASS1_MARKED out of TLS. Neither boundary moved and the synchronous mark-complete to sweep-entry interval gains no relocation, collection or callback. Re-audited 2026-09-11 for the startup memory profile: gc/mod.rs only retains the pre-main allocator-policy constructor in js_gc_init. The constructor applies process allocation options, without invoking GC or JS. No census boundary, collector phase, or mark-complete to sweep-entry control flow changed. Re-audited 2026-09-13 for #10179: census.rs only adds a native regex cache metadata row and its unit assertion; snapshot consumption and the full-cycle window are unchanged.", + "why": "Real GC header addresses, deliberately untraced so the diagnostic does not keep its observed objects alive. Populated only at the end of mark propagation of a synchronous full cycle; consumed at sweep entry in the same run_to_completion invocation. The intervening full-cycle phases do not relocate or run JS callbacks. The Vec is used for membership comparisons and dropped with the census before sweep. Budgeted and minor cycles skip both boundaries. Pin re-audited 2026-09-05 after #9760 touched `gc/mod.rs`: that change is `mod heap_stats;` plus a `pub(crate) use` re-export and alters no mark/sweep control flow. `heap_stats()` is reached only from `js_bun_jsc_heap_stats` (the JS-facing `bun:jsc.heapStats()`), i.e. from mutator code, never inside a cycle, and its own module contract forbids allocation or collection during its walk. The mark-complete → sweep-entry window is unchanged. Re-audited 2026-09-05 (train125) after #9769 and #9771 touched pinned files. #9769 adds one `reg_scanner!` registration to `gc/mod.rs`; #9771 adds a feature-gated `alloc_census_init()` there and a feature-gated Rust-heap dump inside `take_census`. `alloc-census` is not in the default feature set, and decisively: `census_take_if_armed_at_full_sweep_start` does `PASS1_MARKED.with(|p| p.borrow_mut().take())` BEFORE calling `take_census`, so the snapshot has already left the thread-local by the time #9771's code runs — it cannot affect the window. Neither change alters mark/sweep control flow. Re-audited 2026-09-06 after #9831 touched `gc/policy.rs`. Its hunks are (a) the tiny-parse pressure guard's pricing (`tiny_parse_pressure_headroom_bytes`, `tiny_parse_pressure_due*`, a `Cell` byte-count base) consulted from JSON.parse's mutator-side boundaries (`gc_bump_malloc_trigger`, `gc_collect_pending_suppressed_parse`, `gc_schedule_parse_boundary_collection_if_pressure`), none of which is reachable from inside a cycle, and (b) one extra `Cell` store in `note_collection_finished_arena_occupancy`, which runs from `publish_reclaim_outcome` in the Publish subphase — after `step_sweep` has already consumed the snapshot. Mark/sweep control flow between `census_pass1_if_armed` and `census_take_if_armed_at_full_sweep_start` is untouched. Re-audited 2026-09-05 (train126) after #9755 restructured `gc/cycle.rs`. Its hunks are all root-scan machinery (`RootScanSubphase`, `RootScanCycleState`, the mutable-scanner iteration state), which runs BEFORE mark propagation completes; `gc/mod.rs` gains only a `mod young_log;` declaration. The bracketing is unchanged — `census_pass1_if_armed` is still inside `step_mark_propagation` and `census_take_if_armed_at_full_sweep_start` inside `step_sweep` — and a synchronous full mark-sweep still moves nothing between them. Re-pinned 2026-09-05 for the #9740 hot-TLS conversion of this file: the sole change is `thread_local!` → `crate::perry_thread_local!`, a macro-name swap with identical declaration syntax and `.with()` call sites. No control flow, no phase boundary, and no storage semantics change. Re-audited 2026-09-06 (train128) after #9794's GC diagnostics touched `gc/mod.rs` and `gc/policy.rs`: both gain diagnostic module declarations and counters only — no mark/sweep control flow, and the census bracketing in `step_mark_propagation` / `step_sweep` is unchanged. Re-audited for #9794's GC diagnostics: `gc/mod.rs` gains `mod diag_sites;` / `mod survival_diag;`, a re-export, a `diag_sites::full_started(...)` call at TRIGGER time (before mark propagation begins), and exit-time reporting. Nothing executes between mark-complete and sweep-entry, so the window is unchanged. Re-audited 2026-09-06 for the retained array-growth verifier fix: the cycle.rs change passes the existing non-copying evacuation verifier an explicit all-forwarded policy. That call remains in minor finalization, outside the synchronous full-cycle census window; its root and heap reads do not allocate GC objects, move objects, or invoke JS callbacks. The mark-complete and sweep-entry boundaries are unchanged. Re-audited 2026-09-05 after #9830 touched `gc/policy.rs`. That change is (a) six `thread_local! {` blocks rewritten as `crate::perry_thread_local! {` and (b) one `#[cfg(test)]` accessor listing the trigger path's hot-slot indices. The macro keeps the same storage, the same `.with()` at every read and write, and the same destructor registration (the teardown guard exists exactly when `needs_drop` holds, which is what `std::thread_local!` already decided); no value, predicate or branch in the file changes, so no mark or sweep control flow does. The one new behaviour is on a declaration's FIRST read: `HotKey::resolve_and_cache` takes a mutex and allocates a key through the GLOBAL allocator. Even if a first read landed inside this window it would be sound — the window's contract is that nothing relocates and no JS callback runs, and a mimalloc allocation does neither. `census_pass1_if_armed` is still inside `step_mark_propagation` and `census_take_if_armed_at_full_sweep_start` inside `step_sweep`; the bracketing is untouched. Re-audited 2026-09-06 (train132) after #9860 and #9845 touched `gc/mod.rs`. Both hunks are re-export lists and nothing else: #9860 adds `idle_reclaim_elapsed_starts` / `IDLE_RECLAIM_REARM_MS`, and #9845 adds `owner_is_dead_copied_minor_from_space_of_type`. No mark or sweep control flow changes. #9845's substantive work sits in `gc/oldgen.rs` and `gc/copying.rs`, neither pinned: the copying-minor arm (`finalize_dead_copied_minor_from_space_regexps`) runs on a MINOR, which skips both census boundaries; the full-cycle arm (`collect_dead_registered_regexps_post_trace`, from `with_dead_collection_finalize`) walks the RegExp registry building a Vec of addresses — no GC allocation, no JS callback, so it cannot relocate the snapshot's subjects — and it is reached from the sweep body, i.e. AFTER `census_take_if_armed_at_full_sweep_start` has already `take()`n the snapshot out of the thread-local. The mark-complete -> sweep-entry window is unchanged. Re-audited 2026-09-07 for #9965 after 1ec9e0e8a touched `gc/cycle.rs` and `gc/mod.rs`: `gc/mod.rs:216-217` only declares and imports the failure-attribution module, while `gc/cycle.rs:1414-1417` reads the trigger and diagnostic counters immediately before evacuation verification inside `atomic_finalize_minor_prelude`. Full cycles bypass `MinorPrelude` at `gc/cycle.rs:1192-1196`; evacuation remains guarded by the minor-only context at `gc/cycle.rs:1330-1372`. The snapshot store remains at `gc/cycle.rs:963-964` after synchronous full marking, and its take remains at `gc/cycle.rs:1454-1457` before sweep. No new write, relocation, collection, or JS callback was added to that full-cycle interval, so the PASS1_MARKED window is unaffected. Re-audited 2026-09-07 for the regex census rows: all new work is in `take_census` after `census_take_if_armed_at_full_sweep_start` has taken PASS1_MARKED out of TLS; neither boundary nor the intervening cycle control flow changed. Re-audited 2026-09-08 (train144) after #9976 and #9977 touched pinned files. `gc/mod.rs` gains exactly three lines: `mod copying_phase;` and `mod regex_census;` (declarations) and one `reg_scanner!(regex::site_test::scan_roots_mut)` registration. A scanner registration adds a root SOURCE for the mutable-root walks; it does not move either census boundary and runs nowhere between them. `gc/census.rs` widens `side_tables()` to `pub(super)`, extends it with regex rows and adds a test module — all census REPORTING, which runs from the diagnostic dump, not inside a cycle. Mark/sweep control flow between `census_pass1_if_armed` and `census_take_if_armed_at_full_sweep_start` is untouched. Re-audited 2026-09-08 for #9849 JSON construction deferral. `gc/mod.rs` adds the `json_defer` module/re-export and a trusted-header layout helper used only by already-validated JSON emitters; neither changes or runs in collector phase control flow. `gc/policy.rs` adds JSON completion scheduling, construction-grace checks, and safepoint deferral predicates. These are called from mutator-side JSON allocation/output boundaries and ordinary safepoint entry; they do not alter `step_mark_propagation`, `step_sweep`, or invoke callbacks or relocation between the census boundaries. The mark-complete to sweep-entry window is unchanged. The follow-up adds a cfg(test)-only one-shot boolean for deterministic explicit-pressure fixtures; it is absent from production builds and cannot affect the census window. The first predicate read consumes it, so post-parse accounting exercises normal pricing. Re-audited 2026-09-09 for bounded tiny-JSON completion polling. The policy.rs changes split the mutator-side pending-parse check into an inlined empty fast path plus an outlined debt-service path, and amortize the mutator-side arena-pressure read across 64 bounded parse completions. Neither function is reachable from step_mark_propagation or step_sweep; neither census boundary nor the synchronous full-cycle interval between them changes. Re-audited 2026-09-09 for lazy JSON record batches: policy.rs only widens gc_budgeted_cycle_active visibility from pub(super) to pub(crate). Its body remains a read-only Cell query. The new caller is lazy_get materialization in the mutator; run_to_completion, step_mark_propagation, census snapshot consumption at step_sweep, and the synchronous non-moving window are unchanged. Re-audited 2026-09-09 for completed JSON-output debt: the added gc_service_json_output_sweep function calls the existing trigger check from a rooted mutator boundary and reports whether its malloc-count request remains due. It is not called from any census or collector phase; the synchronous mark-complete to sweep-entry window is unchanged. Re-audited 2026-09-09 for the JSON byte-debt carry: the same mutator-only service helper now distinguishes requests satisfied before its call from those satisfied by its trigger check. The added enum contains no payload, both count reads are scalar, and no census boundary or collector phase changed. Re-audited 2026-09-11 for #10055: gc/mod.rs only registers the weak UTF-16 index scanner during gc_init. It neither marks strings nor allocates GC objects or runs JS; offset vectors use the Rust allocator. The mark-complete to sweep-entry census window and cycle control flow are unchanged. Re-audited 2026-09-11 for #10054: gc/mod.rs adds only the trim-cache mutable-root scanner registration in gc_init. Its scanner visits two existing string slots without allocating or invoking JS. Root scanning still precedes mark completion, and neither census boundary nor the synchronous mark-complete to sweep-entry window changes. Re-audited 2026-09-11 for #10060: the census array classifier now reads the logical element start and bounds its scan by the remaining capacity. The helper only reads the existing GC/header words and performs pointer arithmetic; it cannot allocate, collect, or call JS. This classifier runs in take_census after PASS1_MARKED has been taken out of TLS. Neither census boundary nor the mark-complete to sweep-entry control flow changed. Re-audited for #8512: gc/mod.rs only enables the existing PTY mutable-root scanner on Windows; it changes no mark/sweep phase or census boundary. The scanner visits NaN-boxed slots without running JS callbacks. Re-audited 2026-09-12 for the single regular-expression engine: `gc/mod.rs` changes `mod prefetch;` to `pub(crate) mod prefetch;` so the RegExp owner-table walks can prefetch headers, a visibility change with no new call in collector control flow; `gc/census.rs` changes only its `#[cfg(test)]` `regex_census_tests` module, dropping assertions for the previous engine's cache rows. Neither boundary (`census_pass1_if_armed` in `step_mark_propagation`, `census_take_if_armed_at_full_sweep_start` in `step_sweep`) nor the synchronous mark-complete to sweep-entry interval changes. Re-audited 2026-09-13 after the #10169 fix touched `gc/mod.rs` and `gc/policy.rs`. `gc/mod.rs` gains only `pub(crate) use` re-exports (`policy::note_young_leaf_born_old`, `policy::young_generation_holds_a_nursery`, `promote_in_place::{young_generation_measured_dying, young_generation_measured_retained}`, and cfg(test) survival seeders). `gc/policy.rs` gains a `Cell` thread-local (`GC_YOUNG_LEAF_BORN_OLD`, no pointer), its setter, a pure predicate over `copying_from_space_in_use_bytes` vs the base nursery cap, and a consumed-once branch at the top of `gc_budgeted_due_trigger` that may answer `YoungScavengeCap` ahead of `OldReclaim`. That branch decides WHICH collection a safepoint starts (a minor instead of a full); it runs before any cycle begins and never inside one, so the mark-complete → sweep-entry window of a synchronous full — where PASS1_MARKED is populated and consumed within one `run_to_completion` — is unchanged, and neither hunk adds an allocation, a JS callback, or a relocation to it. Re-audited 2026-09-13 for the heap generation (#10164 cross-call search positions): `gc/mod.rs` only declares `pub(crate) mod heap_generation;`. `gc/cycle.rs` wraps the `Sweep` and `Reclaim` arms of `GcCycleState::step` in a `HeapChange` scope and opens one inside `atomic_finalize_minor_prelude`'s evacuation branch (with a nested one around old-page defrag). Opening and closing a scope only increments two thread-local integer cells (`HEAP_GENERATION`, `OPEN_HEAP_CHANGES`); a first thread-local read may allocate a key through the global allocator, which neither relocates nor runs JS. The `Sweep` scope opens immediately before `step_sweep`, i.e. before `census_take_if_armed_at_full_sweep_start` takes PASS1_MARKED out of TLS, and adds no relocation, collection or JS callback to the synchronous mark-complete to sweep-entry window; the minor-prelude scope is unreachable from a full cycle, which bypasses `MinorPrelude`. Neither boundary nor the intervening control flow changed. Re-audited 2026-09-13 for #10182 block-granular reclamation, which touched `gc/cycle.rs`. Two hunks: (a) in the `RememberedSetRebuild` subphase of AtomicFinalize — INSIDE the window — the require-marked old-to-young rebuild is now constructed with `OldToYoungRememberedRebuildState::new_skipping`, whose cursor never enters blocks the census recorded as holding no reached, pinned or pre-marked object (`BlockCensus::unmarked_blocks`); computing that list reads `arena_block_snapshots()` and allocates one `Vec` through the global allocator. It visits a subset of the same objects the rebuild already walked (every skipped object would have been rejected as unmarked), and it neither allocates a GC object, relocates anything, nor runs a JS callback. (b) In `step_sweep`, `IncrementalSweepState::with_block_skip` runs after `census_take_if_armed_at_full_sweep_start` has already taken PASS1_MARKED out of TLS. Neither boundary moved and the synchronous mark-complete to sweep-entry interval gains no relocation, collection or callback. Re-audited 2026-09-11 for the startup memory profile: gc/mod.rs only retains the pre-main allocator-policy constructor in js_gc_init. The constructor applies process allocation options, without invoking GC or JS. No census boundary, collector phase, or mark-complete to sweep-entry control flow changed. Re-audited 2026-09-13 for #10179: census.rs only adds a native regex cache metadata row and its unit assertion; snapshot consumption and the full-cycle window are unchanged. Re-audited 2026-09-14 for the GC due-check fast path, which touched `gc/mod.rs` and `gc/policy.rs`. `gc/mod.rs` only changes the safepoint re-exports: `gc_runtime_safepoint` becomes cfg(test) and `gc_runtime_safepoint_poll` is added. `gc/policy.rs`: the budgeted step returns a debt-free `GcStepReport` (debt is attached by the FFI and test entry points after the step returns) and moves cycle start/step into an out-of-line `gc_budgeted_start_or_step`; `gc_check_trigger` reuses a repeatable due-trigger answer through `DueTriggerMemo`, placed after its `GC_FLAG_IN_ALLOC` and suppression early returns; the young scavenge cap reuses the old-gen pressure value the due trigger already read and checks the census-seeded flag first. All of it runs from mutator safepoints, allocation-point trigger checks and the host step API, before a cycle starts or between budgeted steps. None of it is reachable between `census_pass1_if_armed` in `step_mark_propagation` and `census_take_if_armed_at_full_sweep_start` in `step_sweep` of a synchronous full: an allocation inside that window reaches `gc_check_trigger` with `GC_FLAG_IN_ALLOC` set and returns before the changed code. No allocation, relocation, collection or JS callback is added to the window. Re-audited 2026-09-14 for the tiny-parse nursery-cap boundary, which touched `gc/policy.rs`. It adds `tiny_parse_generational_collection_due`, a pure predicate (the existing `tiny_parse_pressure_due` OR the existing `young_scavenge_cap_due` read), and calls it instead of `tiny_parse_pressure_due` from `gc_bump_malloc_trigger_inner` and `gc_collect_pending_suppressed_parse_slow` (generational branch only) and from `gc_schedule_parse_boundary_collection_if_pressure`. All three are JSON.parse mutator-side boundaries, none reachable from `step_mark_propagation` or `step_sweep`; the predicate reads counters and allocates nothing. Neither census boundary nor the synchronous mark-complete to sweep-entry interval changed.", "window": { "start": { "file": "crates/perry-runtime/src/gc/census.rs", @@ -324,8 +330,8 @@ "sources": { "crates/perry-runtime/src/gc/census.rs": "5c151725460ffb92a55a6bee781123ef5159263b4ce5958d16570f78216e0d67", "crates/perry-runtime/src/gc/cycle.rs": "fdef083301463ad8cec8142ca86cc4354e289c67e97bbc6caea2e8d16d4bf089", - "crates/perry-runtime/src/gc/mod.rs": "18607409fa1304f578faa223645f69063714e953b81fecbdcb986f64104ee735", - "crates/perry-runtime/src/gc/policy.rs": "aea89274a017156efea3516b4f49124f48a66527a08195b662cfbf61672ac042", + "crates/perry-runtime/src/gc/mod.rs": "9dbdde7594af06d08f45f82e426b220e586429da985e38f1e11e7f01e98a2527", + "crates/perry-runtime/src/gc/policy.rs": "abd08472fe71002a55a32f7a17021ab2c029d34ff2b26eb78340d1983ae939c4", "crates/perry-runtime/src/gc/progress.rs": "a5ad3971bbe4047229ca57325234780daa85921dbc778e1c08dff4ad07ccfb96" } } @@ -404,15 +410,15 @@ }, { "file": "crates/perry-runtime/src/gc/policy.rs", - "name": "GC_YOUNG_LEAF_BORN_OLD", + "name": "GC_TINY_PARSE_PRESSURE_BASE_BYTES", "verdict": "not_a_gc_pointer", - "why": "#10169: a `Cell` scheduling flag — set when a document-sized JSON result was born old under young pressure, consumed by the next `gc_budgeted_due_trigger` decision. Holds no address of any kind." + "why": "#9831: `arena_in_use_bytes()` (a sum of block bump offsets) recorded as each collection ends, read back by the tiny-parse pressure guard to price growth since then. A byte COUNT, never an address or a NaN-boxed value: written only from `note_collection_finished_arena_occupancy` and the test seam, read only by `tiny_parse_pressure_due`/`diag_tiny_parse_forced_collection`. Same shape as its frontier siblings `GC_LAST_COLLECTION_POST_IN_USE_BYTES`/`GC_STEP_BYTES`, with the verdict those still owe." }, { "file": "crates/perry-runtime/src/gc/policy.rs", - "name": "GC_TINY_PARSE_PRESSURE_BASE_BYTES", + "name": "GC_YOUNG_LEAF_BORN_OLD", "verdict": "not_a_gc_pointer", - "why": "#9831: `arena_in_use_bytes()` (a sum of block bump offsets) recorded as each collection ends, read back by the tiny-parse pressure guard to price growth since then. A byte COUNT, never an address or a NaN-boxed value: written only from `note_collection_finished_arena_occupancy` and the test seam, read only by `tiny_parse_pressure_due`/`diag_tiny_parse_forced_collection`. Same shape as its frontier siblings `GC_LAST_COLLECTION_POST_IN_USE_BYTES`/`GC_STEP_BYTES`, with the verdict those still owe." + "why": "#10169: a `Cell` scheduling flag — set when a document-sized JSON result was born old under young pressure, consumed by the next `gc_budgeted_due_trigger` decision. Holds no address of any kind." }, { "file": "crates/perry-runtime/src/gc/survival_diag.rs", @@ -734,6 +740,12 @@ "verdict": "not_a_gc_pointer", "why": "#9754 remembered set: `YoungLog` = two `Vec` (live + recycled spare) holding transition-cache SLOT INDICES whose `key_ptr`/`next_keys` a minor may act on. Indices, not addresses — a `u32` cannot hold a 48-bit pointer. The pointer-bearing entries they index are visited by `scan_transition_cache_roots_mut`, which is registered." }, + { + "file": "crates/perry-runtime/src/object/native_get.rs", + "name": "FORCE_SLOW", + "verdict": "test_only", + "why": "#[cfg(test)] Cell selects the unchanged slow Get for differential tests. Holds only a boolean, never a GC address or JSValue, and is absent from shipped binaries." + }, { "file": "crates/perry-runtime/src/object/native_module.rs", "name": "TEST_BOUND_METHOD_MOVE", @@ -816,6 +828,12 @@ "scanner": "promise::scan_promise_roots_mut (promise/scanners.rs:66, visit_cell_f64_slot) plus its budgeted step twin scan_current_microtask_value_step (scanners.rs:420); registered via reg_budgeted_scanner! at gc/mod.rs:833-838", "why": "Declared in promise/microtasks.rs, scanned from promise/scanners.rs. The scanners.rs:786/913 sites are the mutator-side save/clear the scanner exists to protect, not the coverage mechanism." }, + { + "file": "crates/perry-runtime/src/promise/microtasks/empty.rs", + "name": "CALLED", + "verdict": "test_only", + "why": "Three cfg(test) callback counters increment integer invocation counts only. They never store managed values or heap addresses." + }, { "file": "crates/perry-runtime/src/proxy/put_value.rs", "name": "WRITE_STUB", @@ -985,6 +1003,12 @@ "verdict": "not_a_gc_pointer", "why": "Maps scalar timer handle IDs to the CallbackTimerKind enum so clearTimeout/clearInterval can destroy the matching async_hooks resource. Neither the i64 keys nor the enum values contain a JS heap address." }, + { + "file": "crates/perry-runtime/src/weakref/test_support.rs", + "name": "DELIVERED", + "verdict": "test_only", + "why": "The finalization-boundary test stores the bits of its fixed numeric held value 41.0 and asserts that value after delivery. It cannot retain a managed pointer and is absent from production." + }, { "file": "crates/perry-runtime/src/weakref/test_support.rs", "name": "WEAK_ENTRY_VISITS", @@ -2391,18 +2415,6 @@ "name": "WINDOW_ROOTS", "verdict": "not_a_gc_pointer", "why": "Window-root registry maps numeric window handles to numeric root-widget handles; neither value is a JavaScript heap pointer." - }, - { - "file": "crates/perry-runtime/src/promise/microtasks/empty.rs", - "name": "CALLED", - "verdict": "test_only", - "why": "Three cfg(test) callback counters increment integer invocation counts only. They never store managed values or heap addresses." - }, - { - "file": "crates/perry-runtime/src/weakref/test_support.rs", - "name": "DELIVERED", - "verdict": "test_only", - "why": "The finalization-boundary test stores the bits of its fixed numeric held value 41.0 and asserts that value after delivery. It cannot retain a managed pointer and is absent from production." } ], "_FRONTIER_README": "Identity-pinned debt ratchet over new perry-ui* candidates and otherwise-unclassified core raw/Perry TLS declarations (see the census docstring, “The identity-pinned frontier”). A new uncovered holder fails until it is scanned, receives a researched holders verdict, or is deliberately pinned as debt. Moving a researched false positive to holders graduates it from this list. A fixed or classified holder makes its old frontier pin stale, so the receipt must be deleted.", diff --git a/test-files/test_gap_json_record_loop_clone.ts b/test-files/test_gap_json_record_loop_clone.ts index 363fb7a04b..b57b3bb962 100644 --- a/test-files/test_gap_json_record_loop_clone.ts +++ b/test-files/test_gap_json_record_loop_clone.ts @@ -568,3 +568,100 @@ console.log( "own-named-fields-again:", ownNamedFields(ownNames, 12, ownNames.length), ); + +// --------------------------------------------------------------------------- +// 10. The accumulator as an f64 and the counter as an i32 inside the clone. +// +// Both scalars now live in native storage for the clone's duration and are +// published to their real slots only at a side exit and at the loop's +// exit. Each case below observes one of those publications, or one of the +// IEEE edge cases the double-domain add must reproduce exactly. +// --------------------------------------------------------------------------- + +// `repeat` from an arbitrary starting accumulator. +function repeatFrom(rows: any, count: number, start: any): any { + let sum: any = start; + for (let i = 0; i < count; i++) sum += rows[7].id; + return sum; +} + +const edgeRows: any = JSON.parse( + '[{"id":0},{"id":1},{"id":2},{"id":3},{"id":4},{"id":5},{"id":6},' + + '{"id":-0},{"id":1e308},{"id":-1e308},{"id":2.5}]', +); +// `-0 + -0` is `-0`; any `+0` in the chain makes it `+0`. +const negZero = repeatFrom(edgeRows, 3, -0); +console.log("repeat-negative-zero:", negZero, Object.is(negZero, -0)); +const posZero = repeatFrom(edgeRows, 3, 0); +console.log("repeat-positive-zero:", posZero, Object.is(posZero, -0)); +// A non-Number entry value takes the slow clone and keeps concatenation. +const sevenRows: any = JSON.parse(buildRecords(10, "p")); +console.log("repeat-string-start:", repeatFrom(sevenRows, 3, "s")); + +// Overflow to Infinity, then Infinity + -Infinity = NaN, in source order. +function sequentialFrom(rows: any, count: number, n: number, start: number): number { + let sum = start; + for (let i = 0; i < count; i++) { + const index = i % n; + sum += rows[index].id; + } + return sum; +} +const overflowRows: any = JSON.parse('[{"id":1e308},{"id":1e308},{"id":-1e308}]'); +console.log("overflow-to-infinity:", sequentialFrom(overflowRows, 2, 3, 0)); +console.log("overflow-then-back:", sequentialFrom(overflowRows, 3, 3, 0)); +const infRows: any = JSON.parse('[{"id":1e999},{"id":-1e999}]'); +console.log("infinity-minus-infinity:", sequentialFrom(infRows, 2, 2, 0)); +console.log("nan-start:", sequentialFrom(overflowRows, 3, 3, NaN)); + +// A side exit on the FIRST iteration with a non-zero accumulator: the +// trampoline must publish the entry value, not the untouched pre-loop slot. +const stringAtSeven: any = JSON.parse( + '[{"id":0},{"id":1},{"id":2},{"id":3},{"id":4},{"id":5},{"id":6},{"id":"x"}]', +); +function repeatAfterPrefix(rows: any, count: number): any { + let sum: any = 0; + for (let i = 0; i < 4; i++) sum += rows[i].id; + for (let i = 0; i < count; i++) sum += rows[7].id; + return sum; +} +console.log("repeat-side-exit-after-prefix:", repeatAfterPrefix(stringAtSeven, 3)); + +// A side exit MID-loop with the counter-indexed form: the published +// accumulator is the numeric prefix, and the slow clone runs exactly the +// remaining iterations — both visible in the concatenated result. +const lateString: any = JSON.parse( + '[{"id":1.5},{"id":2},{"id":3},{"id":4},{"id":"five"},{"id":6},{"id":7}]', +); +console.log("scan-side-exit-mid-loop:", scanSum(lateString, lateString.length)); +console.log("sequential-side-exit-mid-loop:", sequentialFrom(lateString, 9, 7, 10)); + +// The `repeat` counter against bounds the i32 materialization must refuse: +// every one takes the slow clone and keeps JavaScript's trip count. +console.log("repeat-fractional-count:", repeatFrom(sevenRows, 2.5, 0)); +console.log("repeat-nan-count:", repeatFrom(sevenRows, NaN, 0)); +console.log("repeat-negative-count:", repeatFrom(sevenRows, -3, 0)); +console.log("repeat-zero-count:", repeatFrom(sevenRows, 0, 1)); +console.log("repeat-string-count:", repeatFrom(sevenRows, "3" as any, 0)); +console.log("repeat-integral-count:", repeatFrom(sevenRows, 3, 0.5)); + +// The `random` recurrence with a side exit well past the first iteration: the +// published counter decides how many iterations the slow clone still runs. +function randomCounted(rows: any, count: number, n: number): string { + let sum: any = 0; + let cursor = 0; + let seen = 0; + for (let i = 0; i < count; i++) { + cursor = (cursor * 17 + 7) % n; + sum += rows[cursor].id; + } + for (let i = 0; i < count; i++) seen++; + return String(sum) + "|" + String(cursor) + "|" + String(seen); +} +// `(17c + 7) % 13` walks 7 -> 9 -> 4 -> 10 -> 8 -> 0 from 0, so index 8 — the +// string one — is read on the FIFTH iteration and every sixth after it. +const randomLate: any = JSON.parse( + '[{"id":0},{"id":1},{"id":2},{"id":3},{"id":4},{"id":5},{"id":6},' + + '{"id":7},{"id":"eight"},{"id":9},{"id":10},{"id":11},{"id":12}]', +); +console.log("random-late-side-exit:", randomCounted(randomLate, 40, 13)); diff --git a/test-files/test_gap_native_property_get.ts b/test-files/test_gap_native_property_get.ts new file mode 100644 index 0000000000..ef60b450bf --- /dev/null +++ b/test-files/test_gap_native_property_get.ts @@ -0,0 +1,110 @@ +// Spec Get observability across ordinary data reads and fallback receivers. +function check(actual: any, expected: any, label: string) { + if (!Object.is(actual, expected)) throw new Error(label); + console.log(label, true); +} + +const proto: any = { value: 7, absent: 9 }; +const object: any = Object.create(proto); +check(Reflect.get(object, "value"), 7, "inherited data"); +object.value = 11; +check(Reflect.get(object, "value"), 11, "shadowed data"); +delete object.value; +check(Reflect.get(object, "value"), 7, "deleted shadow"); +object.absent = undefined; +check(Reflect.get(object, "absent"), undefined, "own undefined"); +Object.freeze(proto); +check(Reflect.get(object, "value"), 7, "frozen prototype"); +Object.freeze(object); +check(Reflect.get(object, "absent"), undefined, "frozen own data"); + +let calls = 0; +const owner: any = { value: 1, marker: 13 }; +Object.defineProperty(owner, "value", { + configurable: true, + get() { calls++; return this.marker; }, +}); +check(Reflect.get(owner, "value"), 13, "own getter result"); +check(calls, 1, "own getter once"); +check(Reflect.get(owner, "value", { marker: 17 }), 17, "distinct receiver"); +check(calls, 2, "distinct getter once"); +const child: any = Object.create(owner); +child.marker = 19; +check(Reflect.get(child, "value"), 19, "inherited getter receiver"); +check(calls, 3, "inherited getter once"); +Object.defineProperty(child, "value", { value: 23 }); +check(Reflect.get(child, "value"), 23, "data shadows getter"); +check(calls, 3, "shadow suppresses getter"); +Object.defineProperty(owner, "setterOnly", { set(value) {} }); +check(Reflect.get(owner, "setterOnly"), undefined, "setter only"); + +let traps = 0; +const proxy: any = new Proxy({ value: 29 }, { + get(target, key, receiver) { + traps++; + return Reflect.get(target, key, receiver); + }, +}); +check(Reflect.get(proxy, "value"), 29, "proxy result"); +check(traps, 1, "proxy once"); +check(Reflect.get(Object.create(proxy), "value"), 29, "proxy prototype"); +check(traps, 2, "prototype proxy once"); +const revoked = Proxy.revocable({}, {}); +revoked.revoke(); +let threw = false; +try { Reflect.get(revoked.proxy, "value"); } catch { threw = true; } +check(threw, true, "revoked proxy throws"); + +let order = ""; +const key: any = { [Symbol.toPrimitive]() { order += "key;"; return "value"; } }; +const observed: any = { get value() { order += "get;"; return 31; } }; +check(Reflect.get(observed, key), 31, "coerced key result"); +check(order, "key;get;", "coercion then getter once"); +const symbol = Symbol("value"); +check(Reflect.get({ [symbol]: 37 }, symbol), 37, "symbol key"); +check(Reflect.get({ "#value": 41 }, "#value"), 41, "private-looking string"); +const longKey = "a property name longer than the inline owned string copy capacity, for borrowed key coverage"; +check(Reflect.get({ [longKey]: 43 }, longKey), 43, "long key"); + +class Base { + static value = 47; + #value = 53; + read() { return this.#value; } +} +class Derived extends Base {} +check(Reflect.get(Derived, "value"), 47, "inherited class static"); +check(new Derived().read(), 53, "private field"); +const expression: any = class { static value = 59; }; +check(Reflect.get(expression, "value"), 59, "class expression static"); +const fn: any = () => 0; +fn.value = 61; +check(Reflect.get(fn, "value"), 61, "closure expando"); +const array: any = [1, 2]; +array.value = 67; +check(Reflect.get(array, "value"), 67, "array expando"); +check(Reflect.get(array, "length"), 2, "array length"); +check(Reflect.get(new Uint8Array([71]), "0"), 71, "typed array index"); +check(Reflect.get(new Date(0), "constructor"), Date, "date constructor"); +const re: any = /x/g; +re.lastIndex = 3; +re.value = 73; +check(Reflect.get(re, "value"), 73, "regexp expando"); +check(Reflect.get(re, "lastIndex"), 3, "regexp lastIndex"); +Object.defineProperty(re, "value", { get() { calls++; return 79; } }); +check(Reflect.get(re, "value"), 79, "regexp accessor"); +check(calls, 4, "regexp getter once"); +let jsonGets = 0; +let jsonCalls = 0; +const jsonPrototype = { + get toJSON() { + jsonGets++; + return function () { jsonCalls++; return this.answer; }; + } +}; +const jsonChild: any = Object.create(jsonPrototype); +jsonChild.answer = 83; +check(JSON.stringify(jsonChild), "83", "inherited toJSON getter receiver"); +check(jsonGets, 1, "toJSON getter once"); +check(jsonCalls, 1, "toJSON method once"); +check(JSON.stringify({ toJSON: 0, value: 89 }), '{"toJSON":0,"value":89}', "noncallable toJSON"); +console.log("native property Get parity passed");