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
5 changes: 5 additions & 0 deletions crates/perry-codegen/src/expr/i32_fast_path.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1588,6 +1588,11 @@ fn lower_expr_native_u32(ctx: &mut FnCtx<'_>, e: &Expr) -> Result<LoweredValue>
if let Some(lowered) = lower_packed_u32_loop_index_get(ctx, e)? {
return Ok(lowered);
}
if let Expr::IndexGet { object, index } = e {
if let Some(lowered) = super::try_lower_proven_view_checked_u32_load(ctx, object, index)? {
return Ok(lowered);
}
}
if let Some(lowered) = crate::expr::lower_expr_value(ctx, e)? {
let value = match lowered.rep {
NativeRep::I32 | NativeRep::U32 | NativeRep::BufferLen => Some(lowered.value),
Expand Down
3 changes: 3 additions & 0 deletions crates/perry-codegen/src/expr/index_get.rs
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,9 @@ use inline_dyn_typed_array::lower_inline_dyn_typed_array_get;
/// object: a type-confused, `unbox`ed-pointer-plus-wrong-offset write,
/// not merely a missed optimization.
fn is_width_tracked_typed_array_receiver(ctx: &FnCtx<'_>, object: &Expr) -> bool {
if matches!(object, Expr::LocalGet(id) if ctx.buffer_view_slots.contains_key(id)) {
return true;
}
// This predicate selects only runtime-validated typed-array helpers (or a
// `buffer_view_slots` proof that invalidates on assignment), as documented
// above. Preserve the declared kind as a hint for that dynamic fallback;
Expand Down
3 changes: 3 additions & 0 deletions crates/perry-codegen/src/expr/index_set.rs
Original file line number Diff line number Diff line change
Expand Up @@ -128,6 +128,9 @@ fn lower_value_for_dynamic_index_set(
/// typed-array object (data at byte 16): a type-confused write, not a missed
/// optimization.
fn is_width_tracked_typed_array_receiver(ctx: &FnCtx<'_>, object: &Expr) -> bool {
if matches!(object, Expr::LocalGet(id) if ctx.buffer_view_slots.contains_key(id)) {
return true;
}
let ty = match object {
Expr::LocalGet(id) => ctx.local_type_hint(id).cloned(),
_ => crate::type_analysis::static_type_of(ctx, object),
Expand Down
49 changes: 48 additions & 1 deletion crates/perry-codegen/src/expr/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -104,8 +104,9 @@ pub(crate) use pod_record::{
try_lower_pod_field_set,
};
pub(crate) use proven_view_access::{
index_is_exact_i32_shape, local_is_proven_int_store_view,
index_is_exact_i32_shape, is_proven_u32_view_read, local_is_proven_int_store_view,
try_lower_proven_view_checked_f64_load, try_lower_proven_view_checked_store,
try_lower_proven_view_checked_u32_load,
};
pub(crate) use range_facts::{
bounds_for_buffer_access_width, effective_alias_state_for_access,
Expand Down Expand Up @@ -1643,6 +1644,10 @@ pub(crate) struct VersionedIndexedLoopFact {

#[derive(Clone, Debug)]
pub(crate) struct StablePackedNumericAccess {
/// One preheader-derived element-zero base for a mode-2 Array-subclass
/// prefix proven wholly inline or wholly spilled. When present, indexed
/// reads need no per-element storage-kind selection.
pub contiguous_base: Option<String>,
/// Whether the admitted receiver is a plain Array rather than an
/// Array-subclass object.
pub is_plain: String,
Expand All @@ -1667,6 +1672,10 @@ pub(crate) struct StablePackedReadCache {
/// pointer dirties the associated proof before entering the callee, and a
/// dirty cache is never loaded.
pub value_slot: String,
/// Canonical unsigned entity index paired with `value_slot`. Present only
/// when admission proved every element is an exact `u32`; consumers can
/// then reuse the native index without repeating ToUint32 conversion.
pub u32_slot: Option<String>,
/// Compile-time source-order state. The first lowered occurrence only
/// populates the slots; later occurrences emit a runtime hit/miss test.
pub has_producer: bool,
Expand Down Expand Up @@ -1712,13 +1721,31 @@ pub(crate) struct StablePackedLoopFact {
/// an untagged IEEE Number. This is requested only when the indexed value
/// appears below a numeric operator in the cloned body.
pub numeric_elements: bool,
/// The current guarded typed-array clone uses `array[counter]` as an
/// element key. Its first source occurrence validates and canonicalizes
/// the value to `u32`; repeated occurrences reuse those native bits.
pub u32_index_elements: bool,
/// Minimum immutable length of every pairwise-distinct admitted component
/// column. The entity guard checks its canonical index against this once,
/// allowing every component access in the iteration to be unchecked.
pub u32_component_bound: Option<String>,
/// Equal-length component admission makes an out-of-range entity a
/// no-effect iteration: every typed-array read is `undefined` and every
/// store is ignored. Branch directly to this loop's update rather than
/// restarting the generic clone and replaying earlier effects.
pub u32_out_of_bounds_label: Option<String>,
/// 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>,
/// Immutable locals initialized from a proven Uint32Array view read in
/// this clone. Their ordinary JS slot still stores the exact Number, while
/// native stores may consume it with ToUint32 semantics without falling
/// back to the dynamic typed-array setter.
pub u32_view_derived_locals: std::collections::HashMap<u32, String>,
}

#[derive(Debug, Clone, Copy, PartialEq, Eq)]
Expand Down Expand Up @@ -3668,6 +3695,26 @@ pub(crate) fn lower_expr_value(ctx: &mut FnCtx<'_>, expr: &Expr) -> Result<Optio
);
Ok(Some(lowered))
}
Expr::LocalGet(id)
if crate::stmt::stable_packed_loop::u32_view_derived_local_slot(ctx, *id).is_some() =>
{
let slot = crate::stmt::stable_packed_loop::u32_view_derived_local_slot(ctx, *id)
.expect("guarded derived-u32 slot");
let lowered = LoweredValue::u32(ctx.block().load(I32, &slot));
ctx.record_lowered_value(
"LocalGet",
Some(*id),
"stable_packed_u32_view_derived_local",
&lowered,
None,
None,
None,
false,
false,
Vec::new(),
);
Ok(Some(lowered))
}
Expr::LocalGet(id) if is_plain_f64_local(ctx, *id) => {
let slot = ctx
.locals
Expand Down
Loading
Loading