Skip to content
Closed
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
1 change: 1 addition & 0 deletions changelog.d/8786-closure-captured-packed-loops.md
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Optimize immutable closure-captured packed Array and Array-subclass loops, including nested arrays derived from guarded indexed reads, while retaining generic side exits for rebinding, layout changes, and moving GC.
20 changes: 20 additions & 0 deletions crates/perry-codegen/src/expr/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1611,6 +1611,22 @@ pub(crate) struct StablePackedLoopFact {
pub array_local_id: u32,
pub side_exit_label: String,
pub descriptor: String,
/// Boxed bound passed to the runtime guard (`-1` requests live length).
pub bound: String,
/// Live-length versions must observe growth as well as shrink. The
/// iteration guard compares its refreshed bound with this admitted value
/// and side-exits when they differ.
pub admitted_bound: String,
pub live_length_bound: bool,
/// Captured receivers cannot keep a raw address across calls in the loop
/// body. They reload the closure slot and revalidate before the first
/// indexed effect of every iteration.
pub revalidate_each_iteration: bool,
/// A nested receiver derived from an outer guarded read may have pure
/// compiler temporaries before its first indexed use. Revalidate at that
/// use, after those temporaries, so none of their runtime loads can leave a
/// stale raw address.
pub revalidate_before_indexed_read: bool,
pub live_receiver_handle: Option<String>,
/// Admission scanned the complete indexed range and proved every value is
/// an untagged IEEE Number. This is requested only when the indexed value
Expand All @@ -1619,6 +1635,10 @@ pub(crate) struct StablePackedLoopFact {
/// Preheader-derived numeric storage bases. Admission proved the complete
/// range is raw f64 and the call-free clone keeps these addresses stable.
pub numeric_access: Option<StablePackedNumericAccess>,
/// Immutable locals initialized from this loop's guarded direct indexed
/// read. They may seed a nested candidate only while this fast-loop fact
/// is active.
pub derived_locals: std::collections::HashSet<u32>,
}

#[derive(Debug, Clone, Copy, PartialEq, Eq)]
Expand Down
8 changes: 8 additions & 0 deletions crates/perry-codegen/src/runtime_decls/strings.rs
Original file line number Diff line number Diff line change
Expand Up @@ -703,6 +703,14 @@ pub fn declare_phase_b_strings(module: &mut LlModule) {
I32,
&[DOUBLE, DOUBLE, I32, PTR],
);
// #8773: same complete packed-loop admission, returning the validated
// live receiver address. Captured bindings may hold an array-growth
// forwarding stub and cannot be rewritten like a compiler-private local.
module.declare_function(
"js_packed_arraylike_loop_guard_live",
I64,
&[DOUBLE, DOUBLE, I32, PTR],
);
// Issue #957: tag-aware dynamic index write. Used by `Expr::IndexUpdate`
// codegen to write back the incremented value without rebuilding the
// IndexSet dispatch tree. Routes to `js_array_set_index_or_string` for
Expand Down
1 change: 1 addition & 0 deletions crates/perry-codegen/src/stmt/let_stmt.rs
Original file line number Diff line number Diff line change
Expand Up @@ -120,6 +120,7 @@ pub(crate) fn lower_let(
}
}
if let Some(init_expr) = init {
super::stable_packed_loop::record_derived_local(ctx, id, init_expr, mutable);
crate::expr::record_local_value_alias_for_write(ctx, id, init_expr);
record_array_length_snapshot(ctx, id, init_expr);
ctx.guarded_discriminant_aliases.remove(&id);
Expand Down
5 changes: 5 additions & 0 deletions crates/perry-codegen/src/stmt/loops.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5412,6 +5412,11 @@ pub(super) fn lower_for_after_init_with_i32_bound(
// Body block.
ctx.current_block = body_idx;
super::versioned_indexed_loop::emit_iteration_guard(ctx);
let loop_counter_id = match init {
Some(Stmt::Let { id, .. }) => Some(*id),
_ => None,
};
super::stable_packed_loop::emit_iteration_guard(ctx, loop_counter_id)?;
if let Some(cond) = condition {
let mut guarded =
crate::expr::guarded_buffer_indices_for_condition(ctx, cond, loop_proof_scope_id);
Expand Down
Loading
Loading