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
9 changes: 9 additions & 0 deletions changelog.d/6916-repsel-p4a3-ptr-numarray.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
**Representation-selection Phase 4a.3 — `Ptr<NumArray>` guard-free numeric-array element access (#6904)**

Completes the layer deferred from #6915 (RFC §4 `Array<number>` row / §5.7):

- `collectors/ptr_numarray.rs` proves function-local `number[]` bindings under provenance (`new Array(<static n>)` / empty `[]`), containment (numeric-key element reads, numeric-by-construction writes, `.length`, numeric `push`, bare `return` — everything else disqualifies, including every length-shrinking/reordering mutator), the density lattice `Dense ⊒ HolesOK ⊒ Boxed`, and a module-wide barrier kill (Phase 3b's §5.2 scan plus any indexed write through a `.prototype` object). The #6915 stale-binding finding is an explicit structural eligibility term: containment excludes every path that could leave the local on a growth-forwarded stub.
- At sites with a per-site in-bounds proof (static index range vs the allocation length, or a bounded-loop fact), element access lowers to slot reload → mask → `gep` → `load`/`store double` — no guard tier, no bounds arms, no barrier, no note. Guard-free reads are ToNumber-context only (`HolesOK` canonicalizes `TAG_HOLE` to the quiet NaN, bit-exact with `ToNumber(undefined)`; bare hole-observing reads stay on the guarded tiers), and guard-free stores require a canonical-raw-f64 RHS. Everything unproven falls back to the #6915 guarded tiers.
- `PERRY_PTR_NUMARRAY_LOCALS` (default on) gates the whole phase and is keyed into the object cache.

Post-`opt -O3` structural proof: the #6904 histogram inner loop is load/fcmp/select/fadd/store with zero guard instructions, zero runtime calls, and zero bounds checks. Two new gap files cover promotion, every disqualification class, and the barrier-module behavior — byte-exact vs Node under flag on/off, `PERRY_GC_FORCE_EVACUATE=1`, and `PERRY_GEN_GC=0`.
26 changes: 26 additions & 0 deletions crates/perry-codegen/src/collectors/hir_facts.rs
Original file line number Diff line number Diff line change
Expand Up @@ -127,6 +127,13 @@ pub(crate) struct ShapeStabilityFacts {
/// and unguarded direct method dispatch
/// (`lower_call/property_get/dynamic_dispatch.rs`).
pub shape_proven_ptr_locals: HashMap<u32, super::PtrShapeLocal>,
/// Representation-selection Phase 4a.3: function-locals proven to satisfy
/// the `Ptr<NumArray>` invariants (raw-f64-or-hole slots, never-shrinking
/// length, no stale-binding path) for their entire lifetime
/// (`collectors/ptr_numarray.rs`). Consumers: guard-free element access
/// in `expr/index_get.rs` / `expr/index_set.rs` at sites with an
/// additional per-site in-bounds proof.
pub num_array_locals: HashMap<u32, super::NumArrayLocal>,
}

#[derive(Debug, Clone, Default)]
Expand Down Expand Up @@ -309,6 +316,13 @@ impl TypeFacts {
self.shape_stability.shape_proven_ptr_locals.get(&local_id)
}

/// Representation-selection Phase 4a.3: the numeric-array proof for a
/// local, when it is a proven `Ptr<NumArray>` local
/// (`collectors/ptr_numarray.rs`).
pub(crate) fn num_array_local(&self, local_id: u32) -> Option<&super::NumArrayLocal> {
self.shape_stability.num_array_locals.get(&local_id)
}

pub(crate) fn proves_scalar_replacement(&self, local_id: u32) -> bool {
self.shape_stability
.scalar_replaceable_object_locals
Expand Down Expand Up @@ -447,6 +461,17 @@ pub(crate) fn collect_type_facts(
module_dispatch,
&not_bigint_locals,
);
// Representation-selection Phase 4a.3: `Ptr<NumArray>` locals. Gated on
// `PERRY_PTR_NUMARRAY_LOCALS`, the module-wide §5.2 barrier scan, and the
// array-specific prototype-indexed-write kill inside the collector.
let num_array_locals = super::ptr_numarray::collect_num_array_locals(
stmts,
boxed_vars,
module_globals,
module_dispatch,
compile_time_constants,
&integer_locals,
);
let graph = TypeFacts {
representation: RepresentationFacts {
integer_locals: integer_locals.clone(),
Expand Down Expand Up @@ -485,6 +510,7 @@ pub(crate) fn collect_type_facts(
shape_stability: ShapeStabilityFacts {
scalar_replaceable_object_locals,
shape_proven_ptr_locals,
num_array_locals,
},
materialization_hazards,
};
Expand Down
2 changes: 2 additions & 0 deletions crates/perry-codegen/src/collectors/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ mod local_refs;
mod mutation;
mod not_bigint_locals;
mod pointer_locals;
mod ptr_numarray;
mod ptr_shape;
mod refs;
mod scalar_method_dispatch;
Expand Down Expand Up @@ -61,6 +62,7 @@ pub(crate) use integer_locals::{
pub(crate) use local_refs::{expr_contains_local_get, mark_all_candidate_refs_in_expr};
pub(crate) use mutation::has_any_mutation;
pub(crate) use pointer_locals::collect_pointer_typed_locals;
pub(crate) use ptr_numarray::{NumArrayDensity, NumArrayLocal};
pub(crate) use ptr_shape::PtrShapeLocal;
pub(crate) use refs::{
collect_let_ids, collect_ref_ids_in_expr, collect_ref_ids_in_stmts, is_clamp_call,
Expand Down
Loading
Loading