Skip to content

feat(vm): implement polymorphic inline cache (PIC) for property access#4740

Open
akash-R-A-J wants to merge 5 commits intoboa-dev:mainfrom
akash-R-A-J:feat/pic-inline-cache
Open

feat(vm): implement polymorphic inline cache (PIC) for property access#4740
akash-R-A-J wants to merge 5 commits intoboa-dev:mainfrom
akash-R-A-J:feat/pic-inline-cache

Conversation

@akash-R-A-J
Copy link

This Pull Request fixes/closes #4656.

Currently Boa uses a monomorphic inline cache for property access sites, storing a single (Shape → Slot) mapping. When the shape does not match, the cache is reset, which causes frequent cache invalidation when accessing the same property across objects with different shapes.

This PR upgrades the inline cache to a polymorphic inline cache (PIC) to better support polymorphic property access patterns commonly seen in JavaScript.

It changes the following:

  • Replace the monomorphic inline cache with a polymorphic inline cache (PIC) that stores multiple (Shape → Slot) mappings per access site using a small fixed-capacity cache (PIC_CAPACITY = 4).
  • Add support for a megamorphic state, where the cache is disabled after observing too many shapes to avoid unnecessary overhead.
  • Update inline cache lookup and insertion logic to support multiple cached shapes and opportunistic cleanup of stale weak shape references.
  • Update disassembly output to correctly display the cached shape information for PIC.
  • Add tests covering polymorphic and megamorphic inline cache behavior.

This change reduces inline cache thrashing for polymorphic property access patterns and aligns Boa's inline cache design more closely with modern JavaScript engines.

@akash-R-A-J akash-R-A-J requested a review from a team as a code owner February 26, 2026 14:00
@github-actions
Copy link

github-actions bot commented Feb 26, 2026

Test262 conformance changes

Test result main count PR count difference
Total 52,862 52,862 0
Passed 49,505 49,504 -1
Ignored 2,261 2,262 +1
Failed 1,096 1,096 0
Panics 0 0 0
Conformance 93.65% 93.65% -0.00%

@akash-R-A-J
Copy link
Author

hii @jedel1043, could you please review this pr? happy to make any changes or address feedback. thanks!

@jedel1043
Copy link
Member

Thanks!

Before reviewing anything, can you run https://github.com/boa-dev/data/blob/main/bench/bench-v8/combined.js? This should report benchmark results for your current PR's head commit, and you can compare that with a run against the main branch

@akash-R-A-J
Copy link
Author

hey @jedel1043, I tried running the V8 combined benchmark (combined.js) on both the main branch and this PR's branch.

Both runs currently overflow the stack on Windows when executing:

  • target/release/boa.exe combined.js
  • target/debug/boa.exe combined.js

I also tried increasing the stack size (RUST_MIN_STACK=33554432) and rerunning the benchmark, but it still crashes with the same stack overflow.

If there is a recommended way to run this benchmark on Windows, I'd be happy to try again and report the results.

@akash-R-A-J
Copy link
Author

hey @jedel1043, here's the report after running both branches on wsl2.

After running on main branch:

boa_,main

After running on this PR's branch:

boa_pic

@jedel1043
Copy link
Member

Wow, Splay got a very biiiiig perf boost

use std::cell::Cell;

use boa_gc::GcRefCell;
use boa_gc::{GcRefCell, Trace as BoaTrace, custom_trace};
Copy link
Member

Choose a reason for hiding this comment

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

Why import Trace as BoaTrace?

Copy link
Author

Choose a reason for hiding this comment

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

The alias was introduced during development while wiring up the Trace implementation to avoid confusion with other imports. After the refactor it was no longer necessary, so it has been removed in the latest update.

}

#[derive(Clone, Debug, Finalize)]
pub(crate) struct PicEntries(pub(crate) ArrayVec<PicEntry, PIC_CAPACITY>);
Copy link
Member

Choose a reason for hiding this comment

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

Maybe it would be better to implement Trace on ArrayVec in the boa_gc crate? Should be basically the same as the custom_trace! you implemented.

Copy link
Author

Choose a reason for hiding this comment

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

That makes sense. Initially, I used custom_trace! on the PIC container to keep the changes local, since ArrayVec did not implement Trace. I’ve now implemented Trace for ArrayVec in boa_gc as suggested and updated the PIC implementation to use that instead.

@jedel1043 jedel1043 added performance Performance related changes and issues vm Issues and PRs related to the Boa Virtual Machine. waiting-on-author Waiting on PR changes from the author labels Feb 27, 2026
@akash-R-A-J akash-R-A-J force-pushed the feat/pic-inline-cache branch from d48eaa9 to 207831a Compare February 28, 2026 07:16
@akash-R-A-J
Copy link
Author

Hey @jedel1043, thanks for the review and the feedback! After incorporating the suggested changes, the benchmark score improved further (179 → 192 → 206). The largest improvement, as you noted earlier, continues to appear in the Splay benchmark.

Latest benchmark on this PR branch:

latest_report

Comment on lines +69 to +73
if entries.len() < PIC_CAPACITY {
entries.push(PicEntry {
shape: shape.into(),
slot,
});
Copy link
Member

Choose a reason for hiding this comment

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

let shape_addr = shape.to_addr_usize();

// If the shape already exists, update its slot.
// This handles cases where property transitions preserve the shape but change the slot.
Copy link
Member

Choose a reason for hiding this comment

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

Is this possible? IIRC any property transition will change the shape, so it should be impossible to have two shapes that are the same without them having the same slots.

#[cfg(test)]
mod tests;

pub(crate) const PIC_CAPACITY: usize = 4;
Copy link
Member

Choose a reason for hiding this comment

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

thought: we might have to experiment with this value in the future. Increasing it might increase performance, but it might also cause more cache misses which could decrease performance.

@jedel1043 jedel1043 added this to the v1.0.0 milestone Feb 28, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

performance Performance related changes and issues vm Issues and PRs related to the Boa Virtual Machine. waiting-on-author Waiting on PR changes from the author

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Proposal: Polymorphic Inline Cache (PIC) for Property Access

2 participants