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
202 changes: 202 additions & 0 deletions PERF_RUN_LOG.md

Large diffs are not rendered by default.

1 change: 1 addition & 0 deletions crates/perry-codegen/src/codegen/closure.rs
Original file line number Diff line number Diff line change
Expand Up @@ -323,6 +323,7 @@ pub(super) fn compile_closure(
arena_state_slot: None,
class_keys_slots: HashMap::new(),
cached_lengths: HashMap::new(),
hoisted_array_index_gets: HashMap::new(),
bounded_index_pairs: Vec::new(),
i32_counter_slots: HashMap::new(),
index_used_locals: native_facts.index_used_locals(),
Expand Down
2 changes: 2 additions & 0 deletions crates/perry-codegen/src/codegen/entry.rs
Original file line number Diff line number Diff line change
Expand Up @@ -425,6 +425,7 @@ pub(super) fn compile_module_entry(
arena_state_slot: None,
class_keys_slots: HashMap::new(),
cached_lengths: HashMap::new(),
hoisted_array_index_gets: HashMap::new(),
bounded_index_pairs: Vec::new(),
i32_counter_slots: HashMap::new(),
index_used_locals: main_native_facts.index_used_locals(),
Expand Down Expand Up @@ -864,6 +865,7 @@ pub(super) fn compile_module_entry(
arena_state_slot: None,
class_keys_slots: HashMap::new(),
cached_lengths: HashMap::new(),
hoisted_array_index_gets: HashMap::new(),
bounded_index_pairs: Vec::new(),
i32_counter_slots: HashMap::new(),
index_used_locals: init_native_facts.index_used_locals(),
Expand Down
1 change: 1 addition & 0 deletions crates/perry-codegen/src/codegen/function.rs
Original file line number Diff line number Diff line change
Expand Up @@ -225,6 +225,7 @@ pub(super) fn compile_function(
arena_state_slot: None,
class_keys_slots: HashMap::new(),
cached_lengths: HashMap::new(),
hoisted_array_index_gets: HashMap::new(),
bounded_index_pairs: Vec::new(),
i32_counter_slots: HashMap::new(),
index_used_locals: native_facts.index_used_locals(),
Expand Down
2 changes: 2 additions & 0 deletions crates/perry-codegen/src/codegen/method.rs
Original file line number Diff line number Diff line change
Expand Up @@ -212,6 +212,7 @@ pub(super) fn compile_method(
arena_state_slot: None,
class_keys_slots: HashMap::new(),
cached_lengths: HashMap::new(),
hoisted_array_index_gets: HashMap::new(),
bounded_index_pairs: Vec::new(),
i32_counter_slots: HashMap::new(),
index_used_locals: native_facts.index_used_locals(),
Expand Down Expand Up @@ -722,6 +723,7 @@ pub(super) fn compile_static_method(
arena_state_slot: None,
class_keys_slots: HashMap::new(),
cached_lengths: HashMap::new(),
hoisted_array_index_gets: HashMap::new(),
bounded_index_pairs: Vec::new(),
i32_counter_slots: HashMap::new(),
index_used_locals: native_facts.index_used_locals(),
Expand Down
160 changes: 133 additions & 27 deletions crates/perry-codegen/src/expr/index_get.rs
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ use crate::types::{DOUBLE, I1, I16, I32, I64, I8, PTR};
use super::arrays_finds::lower_buffer_index_get_i32;
#[allow(unused_imports)]
use super::{
buffer_access_materialization_reason, buffer_alias_metadata_suffix,
buffer_access_materialization_reason, buffer_alias_metadata_suffix, can_lower_expr_as_i32,
emit_layout_note_slot_on_block, emit_shadow_slot_clear, emit_shadow_slot_update_for_expr,
emit_string_literal_global, emit_typed_feedback_register_site, emit_v8_export_call,
emit_v8_member_method_call, emit_write_barrier, emit_write_barrier_slot_on_block,
Expand Down Expand Up @@ -186,13 +186,55 @@ fn lower_class_method_bind(
))
}

fn lower_guarded_array_index_get(
pub(crate) fn lower_guarded_array_index_get(
ctx: &mut FnCtx<'_>,
arr_box: &str,
idx_box: &str,
idx_i32: &str,
block_prefix: &str,
require_numeric_layout: bool,
skipped_fast_pass_count: Option<&str>,
) -> Result<String> {
lower_guarded_array_index_get_impl(
ctx,
arr_box,
Some(idx_box),
idx_i32,
block_prefix,
require_numeric_layout,
skipped_fast_pass_count,
false,
)
}

pub(crate) fn lower_guarded_array_index_get_trusted_i32(
ctx: &mut FnCtx<'_>,
arr_box: &str,
idx_i32: &str,
block_prefix: &str,
skipped_fast_pass_count: Option<&str>,
) -> Result<String> {
lower_guarded_array_index_get_impl(
ctx,
arr_box,
None,
idx_i32,
block_prefix,
true,
skipped_fast_pass_count,
true,
)
}

fn lower_guarded_array_index_get_impl(
ctx: &mut FnCtx<'_>,
arr_box: &str,
idx_box: Option<&str>,
idx_i32: &str,
block_prefix: &str,
require_numeric_layout: bool,
skipped_fast_pass_count: Option<&str>,
trusted_i32_index: bool,
) -> Result<String> {
let contract = if require_numeric_layout {
TypedFeedbackContract::numeric_array_get_index()
Expand All @@ -214,34 +256,58 @@ fn lower_guarded_array_index_get(

let guard_ok = {
let blk = ctx.block();
let guard_fn = if require_numeric_layout {
"js_typed_feedback_numeric_array_index_get_guard"
let use_i32_numeric_guard = require_numeric_layout && trusted_i32_index;
let guard_i32 = if use_i32_numeric_guard {
blk.call(
I32,
"js_typed_feedback_numeric_array_index_get_guard_i32",
&[
(I64, &feedback_site_id),
(DOUBLE, arr_box),
(I32, idx_i32),
(I32, "1"),
],
)
} else {
"js_typed_feedback_plain_array_index_get_guard"
let guard_fn = if require_numeric_layout {
"js_typed_feedback_numeric_array_index_get_guard"
} else {
"js_typed_feedback_plain_array_index_get_guard"
};
let idx_box =
idx_box.expect("non-i32 array index guard path requires a boxed index value");
blk.call(
I32,
guard_fn,
&[
(I64, &feedback_site_id),
(DOUBLE, arr_box),
(DOUBLE, idx_box),
(I32, idx_i32),
(I32, "1"),
],
)
};
let guard_i32 = blk.call(
I32,
guard_fn,
&[
(I64, &feedback_site_id),
(DOUBLE, arr_box),
(DOUBLE, idx_box),
(I32, idx_i32),
(I32, "1"),
],
);
blk.icmp_ne(I32, &guard_i32, "0")
};
ctx.block().cond_br(&guard_ok, &fast_label, &fallback_label);

ctx.current_block = fallback_idx;
let fallback_idx_box;
let fallback_idx_ref = match idx_box {
Some(idx_box) => idx_box,
None => {
fallback_idx_box = ctx.block().sitofp(I32, idx_i32, DOUBLE);
&fallback_idx_box
}
};
let fallback_val = ctx.block().call(
DOUBLE,
"js_typed_feedback_array_index_get_fallback_boxed",
&[
(I64, &feedback_site_id),
(DOUBLE, arr_box),
(DOUBLE, idx_box),
(DOUBLE, fallback_idx_ref),
],
);
let fallback_end_label = ctx.block().label.clone();
Expand Down Expand Up @@ -287,6 +353,12 @@ fn lower_guarded_array_index_get(

ctx.current_block = fast_idx;
let fast_blk = ctx.block();
if let Some(count) = skipped_fast_pass_count {
fast_blk.call_void(
"js_typed_feedback_record_array_guard_fast_passes",
&[(I64, &feedback_site_id), (I64, count)],
);
}
let arr_bits = fast_blk.bitcast_double_to_i64(arr_box);
let arr_handle = fast_blk.and(I64, &arr_bits, POINTER_MASK_I64);
let idx_i64 = fast_blk.zext(I32, idx_i32, I64);
Expand Down Expand Up @@ -809,6 +881,17 @@ pub(crate) fn lower(ctx: &mut FnCtx<'_>, expr: &Expr) -> Result<String> {
}
let require_numeric_layout =
expr_has_numeric_pointer_free_array_layout(ctx, object);
if let (Expr::LocalGet(arr_id), Expr::LocalGet(idx_id)) =
(object.as_ref(), index.as_ref())
{
if let Some(slot) = ctx
.hoisted_array_index_gets
.get(&(*arr_id, *idx_id))
.cloned()
{
return Ok(ctx.block().load(DOUBLE, &slot));
}
}
// Bounded-index fast path (mirrors the IndexSet
// optimization in the same file): if the surrounding
// for-loop registered `(counter_id, arr_id)` as
Expand All @@ -832,23 +915,45 @@ pub(crate) fn lower(ctx: &mut FnCtx<'_>, expr: &Expr) -> Result<String> {
ctx.block().fptosi(DOUBLE, &idx_double, I32)
};
if require_numeric_layout {
let idx_double = ctx.block().sitofp(I32, &idx_i32, DOUBLE);
return lower_guarded_array_index_get(
ctx,
&arr_box,
&idx_double,
&idx_i32,
"bidx.num",
true,
return lower_guarded_array_index_get_trusted_i32(
ctx, &arr_box, &idx_i32, "bidx.num", None,
);
}
return lower_bounded_array_index_get(ctx, &arr_box, &idx_i32);
}
}

let arr_box = lower_expr(ctx, object)?;
let idx_double = lower_expr(ctx, index)?;
let idx_i32 = ctx.block().fptosi(DOUBLE, &idx_double, I32);
let i32_slots = ctx.i32_counter_slots.clone();
let flat_const_arrays = ctx.flat_const_arrays.clone();
let array_row_aliases = ctx.array_row_aliases.clone();
let integer_locals = ctx.integer_locals.clone();
let use_i32_index = can_lower_expr_as_i32(
index,
&i32_slots,
&flat_const_arrays,
&array_row_aliases,
&integer_locals,
ctx.clamp3_functions,
ctx.clamp_u8_functions,
ctx.integer_returning_functions,
ctx.i32_identity_functions,
);
if use_i32_index && require_numeric_layout {
let idx_i32 = lower_expr_as_i32(ctx, index)?;
return lower_guarded_array_index_get_trusted_i32(
ctx, &arr_box, &idx_i32, "arr", None,
);
}
let (idx_double, idx_i32) = if use_i32_index {
let idx_i32 = lower_expr_as_i32(ctx, index)?;
let idx_double = ctx.block().sitofp(I32, &idx_i32, DOUBLE);
(idx_double, idx_i32)
} else {
let idx_double = lower_expr(ctx, index)?;
let idx_i32 = ctx.block().fptosi(DOUBLE, &idx_double, I32);
(idx_double, idx_i32)
};
if !require_numeric_layout
&& !matches!(index.as_ref(), Expr::Integer(_) | Expr::Number(_))
{
Expand All @@ -861,6 +966,7 @@ pub(crate) fn lower(ctx: &mut FnCtx<'_>, expr: &Expr) -> Result<String> {
&idx_i32,
"arr",
require_numeric_layout,
None,
);
}
// Generic dynamic object access: stringify the index (no-op
Expand Down
7 changes: 7 additions & 0 deletions crates/perry-codegen/src/expr/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -93,6 +93,7 @@ pub(crate) use i32_fast_path::{
try_flat_const_2d_int, try_lower_flat_const_index_get,
};
pub(crate) use index::lower_index_set_fast;
pub(crate) use index_get::lower_guarded_array_index_get_trusted_i32;
pub(crate) use nanbox_inline::{
i32_bool_to_nanbox, nanbox_bigint_inline, nanbox_pointer_inline, nanbox_pointer_inline_pub,
nanbox_string_inline,
Expand Down Expand Up @@ -599,6 +600,12 @@ pub(crate) struct FnCtx<'a> {
/// call that LLVM can't prove won't modify the length).
pub cached_lengths: std::collections::HashMap<u32, String>,

/// Loop-local replacements for an invariant numeric array read that was
/// guarded once in a loop prebody. Keyed as `(array_local_id,
/// index_local_id)` and active only while lowering the loop body whose
/// prebody filled the slot.
pub hoisted_array_index_gets: std::collections::HashMap<(u32, u32), String>,

/// `(counter_local_id, array_local_id)` pairs that are guaranteed
/// inbounds inside the current loop nest — populated by
/// `lower_for` when it detects the same `for (...; i < arr.length;
Expand Down
10 changes: 10 additions & 0 deletions crates/perry-codegen/src/runtime_decls/objects.rs
Original file line number Diff line number Diff line change
Expand Up @@ -82,6 +82,16 @@ pub fn declare_phase_b_objects(module: &mut LlModule) {
module.declare_function("js_typed_feedback_record_guard_pass", VOID, &[I64]);
module.declare_function("js_typed_feedback_record_guard_fail", VOID, &[I64]);
module.declare_function("js_typed_feedback_record_fallback_call", VOID, &[I64]);
module.declare_function(
"js_typed_feedback_record_array_guard_fast_passes",
VOID,
&[I64, I64],
);
module.declare_function(
"js_typed_feedback_numeric_array_index_get_guard_i32",
I32,
&[I64, DOUBLE, I32, I32],
);
module.declare_function(
"js_typed_feedback_observe_property_get",
VOID,
Expand Down
Loading