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
5 changes: 4 additions & 1 deletion crates/perry-codegen/src/expr/literals_vars.rs
Original file line number Diff line number Diff line change
Expand Up @@ -724,10 +724,13 @@ pub(crate) fn lower(ctx: &mut FnCtx<'_>, expr: &Expr) -> Result<String> {
UpdateOp::Increment => blk.fadd(&old, "1.0"),
UpdateOp::Decrement => blk.fsub(&old, "1.0"),
};
// GC_STORE_AUDIT(STACK/ROOT): update writes a local alloca or registered module-global root slot.
if storage_is_root {
// Module globals are registered mutable GC roots and route
// through the root helper; the raw store below is stack-only.
emit_root_nanbox_store_on_block(blk, &new, &storage);
} else {
// GC_STORE_AUDIT(STACK): update writes a function-local alloca;
// module globals use the root helper.
blk.store(DOUBLE, &new, &storage);
}
// Keep the parallel i32 counter slot in sync (if active).
Expand Down
2 changes: 2 additions & 0 deletions crates/perry-codegen/src/expr/property_set.rs
Original file line number Diff line number Diff line change
Expand Up @@ -276,6 +276,8 @@ pub(crate) fn lower(ctx: &mut FnCtx<'_>, expr: &Expr) -> Result<String> {
// Guarded raw-f64 slots are pointer-free by typed
// shape descriptor; non-number writes miss the
// guard and use the boxed setter fallback.
// GC_STORE_AUDIT(POINTER_FREE): typed raw-f64 class
// slots contain numbers only.
blk.store(DOUBLE, &val_double, &field_ptr);
} else {
let field_addr = blk.ptrtoint(&field_ptr, I64);
Expand Down
3 changes: 3 additions & 0 deletions crates/perry-codegen/src/lower_call/new.rs
Original file line number Diff line number Diff line change
Expand Up @@ -315,6 +315,7 @@ pub(crate) fn lower_new(ctx: &mut FnCtx<'_>, class_name: &str, args: &[Expr]) ->
// ---- Fast path: bump and return data + aligned ----
ctx.current_block = fast_idx;
let blk = ctx.block();
// GC_STORE_AUDIT(INIT): inline arena bump offset is allocator metadata, not a JS heap edge.
blk.store(I64, &new_offset, &offset_field_ptr);
// data ptr is at byte offset 0 in InlineArenaState
let data_ptr = blk.load(PTR, &state_ptr);
Expand Down Expand Up @@ -350,6 +351,7 @@ pub(crate) fn lower_new(ctx: &mut FnCtx<'_>, class_name: &str, args: &[Expr]) ->
| (GC_FLAG_ARENA << 8)
| (GC_LAYOUT_POINTER_FREE << 16)
| ((total_size as u64) << 32);
// GC_STORE_AUDIT(INIT): inline headers initialize freshly allocated unpublished object storage.
blk.store(I64, &gc_packed.to_string(), &raw);

// Write ObjectHeader at raw + 8.
Expand All @@ -367,6 +369,7 @@ pub(crate) fn lower_new(ctx: &mut FnCtx<'_>, class_name: &str, args: &[Expr]) ->
// above is an i64 (carries the ArrayHeader address); store as
// i64 since the underlying memory is 8 bytes either way.
let oh_addr_3 = blk.gep(I8, &raw, &[(I64, "24")]);
// GC_STORE_AUDIT(INIT): keys_array edge is installed before publishing the new object.
blk.store(I64, &keys_ptr, &oh_addr_3);

// User pointer = raw + 8 (the ObjectHeader address — what the
Expand Down
5 changes: 5 additions & 0 deletions crates/perry-runtime/src/array/alloc.rs
Original file line number Diff line number Diff line change
Expand Up @@ -83,6 +83,7 @@ pub extern "C" fn js_array_alloc_with_length(capacity: u32) -> *mut ArrayHeader
(*ptr).capacity = actual_capacity;
let elements_ptr = (ptr as *mut u8).add(std::mem::size_of::<ArrayHeader>()) as *mut u64;
for i in 0..capacity as usize {
// GC_STORE_AUDIT(POINTER_FREE): TAG_HOLE is a non-pointer sentinel for fresh array slots.
std::ptr::write(elements_ptr.add(i), crate::value::TAG_HOLE);
}
clear_array_numeric_layout(ptr);
Expand Down Expand Up @@ -148,6 +149,7 @@ pub extern "C" fn js_array_from_f64(elements: *const f64, count: u32) -> *mut Ar
unsafe {
(*arr).length = count;
let arr_elements = (arr as *mut u8).add(std::mem::size_of::<ArrayHeader>()) as *mut f64;
// GC_STORE_AUDIT(BARRIERED): bulk array initialization is followed by layout/barrier rebuild.
ptr::copy_nonoverlapping(elements, arr_elements, count as usize);
rebuild_array_layout(arr);
}
Expand Down Expand Up @@ -186,10 +188,12 @@ pub(crate) unsafe fn js_array_from_arraylike(
for i in 0..len {
// Pre-init to undefined in case the key lookup returns the
// wrong type / produces a sentinel we want to coerce.
// GC_STORE_AUDIT(POINTER_FREE): undefined prefill is a non-pointer sentinel.
*elements.add(i as usize) = undefined;
let key_str = i.to_string();
let key = crate::string::js_string_from_bytes(key_str.as_ptr(), key_str.len() as u32);
let v = crate::object::js_object_get_field_by_name_f64(obj, key);
// GC_STORE_AUDIT(BARRIERED): arraylike element write is immediately recorded via note_array_slot.
*elements.add(i as usize) = v;
note_array_slot(arr, i as usize, v.to_bits());
}
Expand Down Expand Up @@ -229,6 +233,7 @@ pub(crate) unsafe fn js_array_from_string_codepoints(
let s_ref = ch.encode_utf8(&mut buf);
let s_ptr = crate::string::js_string_from_bytes(s_ref.as_ptr(), s_ref.len() as u32);
let value = crate::value::js_nanbox_string(s_ptr as i64);
// GC_STORE_AUDIT(BARRIERED): string codepoint array slot is immediately recorded via note_array_slot.
*elements.add(i) = value;
note_array_slot(arr, i, value.to_bits());
}
Expand Down
4 changes: 4 additions & 0 deletions crates/perry-runtime/src/array/concat_reverse.rs
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,7 @@ pub extern "C" fn js_array_concat(
};
let dst_elements =
(result as *mut u8).add(std::mem::size_of::<ArrayHeader>()) as *mut f64;
// GC_STORE_AUDIT(BARRIERED): concat bulk copy is followed by exact layout/barrier rebuild.
ptr::copy_nonoverlapping(
src_elements,
dst_elements.add(dest_len as usize),
Expand Down Expand Up @@ -142,6 +143,7 @@ pub extern "C" fn js_array_reverse(arr: *mut ArrayHeader) -> *mut ArrayHeader {
let mut j = len - 1;
while i < j {
let tmp = *elements.add(i);
// GC_STORE_AUDIT(BARRIERED): reverse slot swap is followed by layout/barrier rebuild.
*elements.add(i) = *elements.add(j);
*elements.add(j) = tmp;
i += 1;
Expand All @@ -167,6 +169,7 @@ pub extern "C" fn js_array_fill(arr: *mut ArrayHeader, value: f64) -> *mut Array
}
let elements = (arr as *mut u8).add(std::mem::size_of::<ArrayHeader>()) as *mut f64;
for i in 0..len {
// GC_STORE_AUDIT(BARRIERED): fill slot writes are followed by layout/barrier rebuild.
*elements.add(i) = value;
}
rebuild_array_layout(arr);
Expand Down Expand Up @@ -226,6 +229,7 @@ pub extern "C" fn js_array_fill_range(
}
let elements = (arr as *mut u8).add(std::mem::size_of::<ArrayHeader>()) as *mut f64;
for i in s..e {
// GC_STORE_AUDIT(BARRIERED): fill range writes are followed by layout/barrier rebuild.
*elements.add(i as usize) = value;
}
rebuild_array_layout(arr);
Expand Down
5 changes: 5 additions & 0 deletions crates/perry-runtime/src/array/flat_clone.rs
Original file line number Diff line number Diff line change
Expand Up @@ -383,6 +383,7 @@ pub extern "C" fn js_array_clone(src: *const ArrayHeader) -> *mut ArrayHeader {
(src as *const u8).add(std::mem::size_of::<ArrayHeader>()) as *const f64;
let dst_elements =
(result as *mut u8).add(std::mem::size_of::<ArrayHeader>()) as *mut f64;
// GC_STORE_AUDIT(BARRIERED): clone bulk copy is followed by exact layout/barrier rebuild.
ptr::copy_nonoverlapping(src_elements, dst_elements, len as usize);
(*result).length = len;
rebuild_array_layout_exact(result);
Expand Down Expand Up @@ -444,12 +445,14 @@ pub extern "C" fn js_array_entries(arr: *const ArrayHeader) -> *mut ArrayHeader
let pair = js_array_alloc(2);
(*pair).length = 2;
let pair_elems = (pair as *mut u8).add(std::mem::size_of::<ArrayHeader>()) as *mut f64;
// GC_STORE_AUDIT(BARRIERED): entries pair slots are immediately recorded via note_array_slot.
*pair_elems.add(0) = i as f64;
*pair_elems.add(1) = *src_elements.add(i);
note_array_slot(pair, 0, (i as f64).to_bits());
note_array_slot(pair, 1, (*src_elements.add(i)).to_bits());
// NaN-box the inner array pointer so the outer storage slot keeps tag info.
let pair_value = crate::value::js_nanbox_pointer(pair as i64);
// GC_STORE_AUDIT(BARRIERED): outer entries slot is immediately recorded via note_array_slot.
*dst_elements.add(i) = pair_value;
note_array_slot(result, i, pair_value.to_bits());
}
Expand Down Expand Up @@ -482,6 +485,7 @@ pub extern "C" fn js_array_keys(arr: *const ArrayHeader) -> *mut ArrayHeader {
(*result).length = len;
let dst_elements = (result as *mut u8).add(std::mem::size_of::<ArrayHeader>()) as *mut f64;
for i in 0..len as usize {
// GC_STORE_AUDIT(POINTER_FREE): keys array stores numeric indices only.
*dst_elements.add(i) = i as f64;
}
result
Expand Down Expand Up @@ -516,6 +520,7 @@ pub extern "C" fn js_array_values(arr: *const ArrayHeader) -> *mut ArrayHeader {
(arr as *const u8).add(std::mem::size_of::<ArrayHeader>()) as *const f64;
let dst_elements =
(result as *mut u8).add(std::mem::size_of::<ArrayHeader>()) as *mut f64;
// GC_STORE_AUDIT(BARRIERED): values bulk copy is followed by layout/barrier rebuild.
ptr::copy_nonoverlapping(src_elements, dst_elements, len as usize);
(*result).length = len;
rebuild_array_layout(result);
Expand Down
6 changes: 6 additions & 0 deletions crates/perry-runtime/src/array/header.rs
Original file line number Diff line number Diff line change
Expand Up @@ -418,6 +418,7 @@ unsafe fn rebuild_array_numeric_raw_f64(arr: *mut ArrayHeader) -> bool {
clear_array_numeric_layout(arr);
return false;
};
// GC_STORE_AUDIT(POINTER_FREE): raw-f64 layout rewrite stores numeric payloads only.
std::ptr::write(elements.add(i) as *mut f64, number);
}

Expand Down Expand Up @@ -497,6 +498,7 @@ pub(crate) unsafe fn note_array_numeric_index_write(
};
if array_has_raw_f64_layout_flag(arr) && index < (*arr).length as usize {
let elements = array_elements_ptr(arr) as *mut f64;
// GC_STORE_AUDIT(POINTER_FREE): raw-f64 numeric slot update cannot contain a heap pointer.
std::ptr::write(elements.add(index), number);
return number.to_bits();
}
Expand Down Expand Up @@ -554,6 +556,7 @@ pub(crate) unsafe fn array_numeric_raw_f64_set_inbounds(
return false;
}
let elements_ptr = array_elements_ptr(arr) as *mut f64;
// GC_STORE_AUDIT(POINTER_FREE): raw-f64 numeric field store is layout-noted below.
std::ptr::write(elements_ptr.add(index as usize), value);
note_array_numeric_index_write(arr, index as usize, value_bits);
crate::gc::layout_note_slot(arr as usize, index as usize, value_bits);
Expand All @@ -580,6 +583,7 @@ pub(crate) unsafe fn array_numeric_raw_f64_push_inbounds(
return false;
};
let elements_ptr = array_elements_ptr(arr) as *mut f64;
// GC_STORE_AUDIT(POINTER_FREE): raw-f64 push stores numeric payloads only.
std::ptr::write(elements_ptr.add(length as usize), number);
crate::gc::layout_note_slot(arr as usize, length as usize, number.to_bits());
(*arr).length = length + 1;
Expand Down Expand Up @@ -681,6 +685,7 @@ pub(crate) unsafe fn gc_element_slot_range(
#[inline]
pub(crate) unsafe fn note_array_slot(arr: *mut ArrayHeader, index: usize, value_bits: u64) {
let value_bits = canonicalize_array_numeric_store_bits(arr, value_bits);
// GC_STORE_AUDIT(BARRIERED): shared helper notes layout and emits the array slot barrier below.
std::ptr::write(array_elements_ptr(arr).add(index), value_bits);
note_array_numeric_index_write(arr, index, value_bits);
crate::gc::layout_note_slot(arr as usize, index, value_bits);
Expand All @@ -695,6 +700,7 @@ pub(crate) unsafe fn note_array_slot_layout_only(
value_bits: u64,
) {
let value_bits = canonicalize_array_numeric_store_bits(arr, value_bits);
// GC_STORE_AUDIT(INIT): layout-only helper is restricted to fresh/suppressed caller sites.
std::ptr::write(array_elements_ptr(arr).add(index), value_bits);
note_array_numeric_index_write(arr, index, value_bits);
crate::gc::layout_note_slot(arr as usize, index, value_bits);
Expand Down
9 changes: 9 additions & 0 deletions crates/perry-runtime/src/array/immutable.rs
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ pub extern "C" fn js_array_to_reversed(arr: *const ArrayHeader) -> *mut ArrayHea
let src = (arr as *const u8).add(std::mem::size_of::<ArrayHeader>()) as *const f64;
let dst = (new_arr as *mut u8).add(std::mem::size_of::<ArrayHeader>()) as *mut f64;
for i in 0..len {
// GC_STORE_AUDIT(BARRIERED): reversed copy initializes a fresh array rebuilt below.
*dst.add(i) = *src.add(len - 1 - i);
}
rebuild_array_layout(new_arr);
Expand All @@ -47,6 +48,7 @@ pub extern "C" fn js_array_to_sorted_default(arr: *const ArrayHeader) -> *mut Ar
(*new_arr).length = len as u32;
let src = (arr as *const u8).add(std::mem::size_of::<ArrayHeader>()) as *const f64;
let dst = (new_arr as *mut u8).add(std::mem::size_of::<ArrayHeader>()) as *mut f64;
// GC_STORE_AUDIT(BARRIERED): sorted clone copy initializes a fresh array rebuilt below.
std::ptr::copy_nonoverlapping(src, dst, len);
rebuild_array_layout(new_arr);
// Sort the copy in-place using default sort
Expand Down Expand Up @@ -78,6 +80,7 @@ pub extern "C" fn js_array_to_sorted_with_comparator(
(*new_arr).length = len as u32;
let src = (arr as *const u8).add(std::mem::size_of::<ArrayHeader>()) as *const f64;
let dst = (new_arr as *mut u8).add(std::mem::size_of::<ArrayHeader>()) as *mut f64;
// GC_STORE_AUDIT(BARRIERED): comparator sorted clone copy initializes a fresh array rebuilt below.
std::ptr::copy_nonoverlapping(src, dst, len);
rebuild_array_layout(new_arr);
// Sort the copy in-place
Expand Down Expand Up @@ -130,15 +133,18 @@ pub extern "C" fn js_array_to_spliced(
let dst = (new_arr as *mut u8).add(std::mem::size_of::<ArrayHeader>()) as *mut f64;

// Copy elements before start
// GC_STORE_AUDIT(BARRIERED): toSpliced result writes are followed by layout/barrier rebuild.
for i in 0..s as usize {
*dst.add(i) = *src.add(i);
}
// Copy inserted items
// GC_STORE_AUDIT(BARRIERED): inserted toSpliced items are included in the rebuild below.
for i in 0..items_count as usize {
*dst.add(s as usize + i) = *items.add(i);
}
// Copy elements after deleted range
let after_start = (s + dc) as usize;
// GC_STORE_AUDIT(BARRIERED): toSpliced tail copy is included in the rebuild below.
for i in after_start..len as usize {
*dst.add(s as usize + items_count as usize + i - after_start) = *src.add(i);
}
Expand Down Expand Up @@ -178,6 +184,7 @@ pub extern "C" fn js_array_with(
(*new_arr).length = len as u32;
let src = (arr as *const u8).add(std::mem::size_of::<ArrayHeader>()) as *const f64;
let dst = (new_arr as *mut u8).add(std::mem::size_of::<ArrayHeader>()) as *mut f64;
// GC_STORE_AUDIT(BARRIERED): with() unchanged copy is followed by layout/barrier rebuild.
std::ptr::copy_nonoverlapping(src, dst, len as usize);
rebuild_array_layout(new_arr);
return new_arr;
Expand All @@ -186,6 +193,7 @@ pub extern "C" fn js_array_with(
let new_arr = js_array_alloc(len as u32);
(*new_arr).length = len as u32;
let dst = (new_arr as *mut u8).add(std::mem::size_of::<ArrayHeader>()) as *mut f64;
// GC_STORE_AUDIT(BARRIERED): with() clone and replacement are followed by layout/barrier rebuild.
std::ptr::copy_nonoverlapping(src, dst, len as usize);
*dst.add(idx as usize) = value;
rebuild_array_layout(new_arr);
Expand Down Expand Up @@ -246,6 +254,7 @@ pub extern "C" fn js_array_copy_within(
}

// Use memmove semantics (handles overlapping regions)
// GC_STORE_AUDIT(BARRIERED): copyWithin mutates array slots and rebuilds layout/barriers below.
std::ptr::copy(
elements.add(s as usize),
elements.add(t as usize),
Expand Down
5 changes: 5 additions & 0 deletions crates/perry-runtime/src/array/indexing.rs
Original file line number Diff line number Diff line change
Expand Up @@ -232,6 +232,7 @@ pub extern "C" fn js_array_set_f64_unchecked(arr: *mut ArrayHeader, index: u32,
let value = canonicalize_array_numeric_store_value(arr, value);
let value_bits = value.to_bits();
let elements_ptr = (arr as *mut u8).add(std::mem::size_of::<ArrayHeader>()) as *mut f64;
// GC_STORE_AUDIT(BARRIERED): unchecked array set is immediately recorded via note_array_slot.
ptr::write(elements_ptr.add(index as usize), value);
note_array_slot(arr, index as usize, value_bits);
}
Expand Down Expand Up @@ -285,6 +286,7 @@ pub extern "C" fn js_array_set_f64(arr: *mut ArrayHeader, index: u32, value: f64
let value = canonicalize_array_numeric_store_value(arr, value);
let value_bits = value.to_bits();
let elements_ptr = (arr as *mut u8).add(std::mem::size_of::<ArrayHeader>()) as *mut f64;
// GC_STORE_AUDIT(BARRIERED): array set is immediately recorded via note_array_slot.
ptr::write(elements_ptr.add(index as usize), value);
note_array_slot(arr, index as usize, value_bits);
}
Expand Down Expand Up @@ -332,6 +334,7 @@ pub extern "C" fn js_array_set_f64_extend(
let value = canonicalize_array_numeric_store_value(arr, value);
let value_bits = value.to_bits();
let elements_ptr = (arr as *mut u8).add(std::mem::size_of::<ArrayHeader>()) as *mut f64;
// GC_STORE_AUDIT(BARRIERED): in-bounds extending set is immediately recorded via note_array_slot.
ptr::write(elements_ptr.add(index as usize), value);
note_array_slot(arr, index as usize, value_bits);
return arr;
Expand All @@ -356,13 +359,15 @@ pub extern "C" fn js_array_set_f64_extend(
let elements_ptr = (arr as *mut u8).add(std::mem::size_of::<ArrayHeader>()) as *mut f64;
let hole = f64::from_bits(crate::value::TAG_HOLE);
for i in length..index {
// GC_STORE_AUDIT(BARRIERED): sparse gap sentinel is immediately recorded via note_array_slot.
ptr::write(elements_ptr.add(i as usize), hole);
note_array_slot(arr, i as usize, crate::value::TAG_HOLE);
}

// Set the value
let value = canonicalize_array_numeric_store_value(arr, value);
let value_bits = value.to_bits();
// GC_STORE_AUDIT(BARRIERED): extending set value is immediately recorded via note_array_slot.
ptr::write(elements_ptr.add(index as usize), value);
note_array_slot(arr, index as usize, value_bits);
(*arr).length = new_length;
Expand Down
1 change: 1 addition & 0 deletions crates/perry-runtime/src/array/iter_methods.rs
Original file line number Diff line number Diff line change
Expand Up @@ -77,6 +77,7 @@ pub extern "C" fn js_array_map(
} else {
js_closure_call2(callback, element, i as f64)
};
// GC_STORE_AUDIT(INIT): map result is unpublished; slot layout is noted immediately below.
ptr::write(result_elements.add(i), mapped);
let mapped_bits = mapped.to_bits();
if length <= 64 {
Expand Down
1 change: 1 addition & 0 deletions crates/perry-runtime/src/array/jsvalue_api.rs
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@ pub extern "C" fn js_array_from_jsvalue(elements: *const u64, count: u32) -> *mu
// Each u64 contains NaN-boxed JSValue bits, store as f64 bits
for i in 0..count as usize {
let bits = *elements.add(i);
// GC_STORE_AUDIT(BARRIERED): JSValue array initialization is followed by layout/barrier rebuild.
ptr::write(arr_elements.add(i), f64::from_bits(bits));
}
rebuild_array_layout(arr);
Expand Down
Loading