Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions changelog.d/9270-stable-packed-forwarded-receiver.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
### Fixed

- Make stable packed loops consume the guard's live Array address after cross-call growth instead of reading a stale forwarding stub.
46 changes: 19 additions & 27 deletions crates/perry-codegen/src/stmt/stable_packed_loop.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1592,21 +1592,21 @@ pub(super) fn lower(
),
(PTR, descriptor.as_str()),
];
if candidate.capture_index.is_some() {
let live_raw =
ctx.block()
.call(I64, "js_packed_arraylike_loop_guard_live", &guard_args);
(
ctx.block().icmp_ne(I64, &live_raw, "0"),
Some(live_raw),
None,
)
} else {
let guard = ctx
.block()
.call(I32, "js_packed_arraylike_loop_guard", &guard_args);
(ctx.block().icmp_ne(I32, &guard, "0"), None, None)
}
// An ordinary local can already name an Array growth forwarding
// stub when the array was grown across a call boundary. The guard
// follows that edge and validates the live target, so codegen must
// consume the live address it returns instead of reloading and
// de-tagging the stale local for direct fast-clone reads (#9117).
// Captured receivers need the same result because their reload is
// itself a runtime call that can move the target.
let live_raw =
ctx.block()
.call(I64, "js_packed_arraylike_loop_guard_live", &guard_args);
(
ctx.block().icmp_ne(I64, &live_raw, "0"),
Some(live_raw),
None,
)
};
// The conservative column matcher proves the cloned body call-free, and
// `fast_raw` below reloads the rooted derived receiver after every
Expand All @@ -1633,18 +1633,10 @@ pub(super) fn lower(
descriptor_word(ctx, &descriptor, 6)
};
let bound_i32 = ctx.block().trunc(I64, &bound64, I32);
// A capture reload is itself a runtime call, so its admission returns the
// post-call live address. Ordinary addressable bindings retain the old
// guard/reload sequence; their reload is a plain load and their clone must
// still pass the call-free scan unless it has explicit access revalidation.
let fast_raw = if let Some(live_raw) = admitted_live_raw {
live_raw
} else {
let fast_receiver = crate::expr::lower_expr(ctx, &Expr::LocalGet(candidate.array_id))?;
let fast_bits = ctx.block().bitcast_double_to_i64(&fast_receiver);
ctx.block()
.and(I64, &fast_bits, crate::nanbox::POINTER_MASK_I64)
};
// Every admission returns the post-guard LIVE receiver. Besides captured
// reloads, this is required for ordinary locals that still hold an Array
// growth forwarding stub (#9117).
let fast_raw = admitted_live_raw.expect("every packed-loop admission returns a live receiver");
let fast_scan_start = ctx.func.num_blocks();
let installed_typed_array_views = typed_array_admission
.as_ref()
Expand Down
4 changes: 2 additions & 2 deletions crates/perry/tests/issue_8655_array_subclass_indexing.rs
Original file line number Diff line number Diff line change
Expand Up @@ -111,8 +111,8 @@ fn wolf_ecs_loop_has_a_fallback_free_versioned_fast_copy() {
let system = function_ir(&ir, "__system(double");

assert!(
system.contains("call i32 @js_packed_arraylike_loop_guard("),
"the original #8655 fixture must enter through the loop preheader proof"
system.contains("call i64 @js_packed_arraylike_loop_guard_live("),
"the original #8655 fixture must enter through the live-receiver loop preheader proof"
);
assert!(system.contains("stable_packed.loop.fast.preheader"));
assert!(
Expand Down
4 changes: 2 additions & 2 deletions crates/perry/tests/issue_8690_loop_versioned_arraylike.rs
Original file line number Diff line number Diff line change
Expand Up @@ -133,10 +133,10 @@ console.log(checksum);
let read_only = function_ir(&ir, "__scan(");
assert_eq!(
read_only
.matches("call i32 @js_packed_arraylike_loop_guard(")
.matches("call i64 @js_packed_arraylike_loop_guard_live(")
.count(),
3,
"the outer fast/slow copies each own a preheader-versioned inner loop"
"the outer fast/slow copies each own a live-receiver preheader-versioned inner loop"
);
assert!(read_only.contains("stable_packed.loop.fast.preheader"));
assert!(read_only.contains("stable_packed.loop.slow.preheader"));
Expand Down
Loading