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
1 change: 1 addition & 0 deletions changelog.d/11819-typed-region-guard-storage-byte.md
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
- **perry-codegen**: a counted loop over a receiver not declared a plain `Array` (a `Float64Array`, but equally a `Buffer`) no longer fails LLVM IR parsing with "use of undefined value '@PERRY_TA_VIEW_GUARD'". #10741's typed region guard still loaded that process-wide global after #10516 had removed it; it now reads the receiver's own storage byte (header byte 10) like the other #10516 sites. This failed four modules of the OpenCode v1.18.30 build, including `id.ts`.
17 changes: 12 additions & 5 deletions crates/perry-codegen/src/expr/index_get/guarded_array.rs
Original file line number Diff line number Diff line change
Expand Up @@ -357,8 +357,8 @@ pub(crate) fn emit_array_region_guard(
/// The `Float64Array` twin of [`emit_array_region_guard`]'s dense facts
/// (#10741): a heap pointer whose GC header names a typed array (never
/// forwarded: typed arrays live in the non-moving space), of element kind
/// `Float64`, with inline storage (`PERRY_TA_VIEW_GUARD == 0`: no aliasing
/// view exists, so every typed array's elements start at payload `+16`), and
/// `Float64`, with inline storage (its own storage byte, header byte 10, is
/// `TA_STORAGE_INLINE` — #10516 — so its elements start at payload `+16`), and
/// the same bounds against its `length` (payload `+0`). On a pass it stores
/// the element base into `base_slot`; F-body's reads and stores are then the
/// same raw `f64` slots as a dense array's, its reads canonicalising a NaN
Expand Down Expand Up @@ -399,9 +399,16 @@ pub(crate) fn emit_typed_f64_region_guard(
let kind_ptr = blk.inttoptr(I64, &kind_addr);
let kind = blk.load(I8, &kind_ptr);
let is_f64 = blk.icmp_eq(I8, &kind, "7"); // KIND_FLOAT64
let vg = blk.load(I64, "@PERRY_TA_VIEW_GUARD");
let no_views = blk.icmp_eq(I64, &vg, "0");
let ok = blk.and(I1, &is_ta, &no_views);

// #10516: the receiver's own storage byte (header byte 10,
// `TA_STORAGE_INLINE` = 0) licenses `data == header + 16`. The
// process-wide `PERRY_TA_VIEW_GUARD` this used to read no longer
// exists, so referencing it left the symbol undefined.
let storage_addr = blk.add(I64, &handle, "10");
let storage_ptr = blk.inttoptr(I64, &storage_addr);
let storage = blk.load(I8, &storage_ptr);
let inline_storage = blk.icmp_eq(I8, &storage, "0");
let ok = blk.and(I1, &is_ta, &inline_storage);
let ok = blk.and(I1, &ok, &is_f64);
blk.cond_br(&ok, &len_label, &join_label);
handle
Expand Down
27 changes: 27 additions & 0 deletions crates/perry-codegen/src/expr/region_array_loop_tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -277,6 +277,33 @@ fn a_float64array_receiver_reads_canonicalise_nan() {
);
}

#[test]
fn a_typed_region_guard_names_only_symbols_that_exist() {
// #10516 removed the process-wide `PERRY_TA_VIEW_GUARD` in favour of the
// typed array header's own storage byte. The typed region guard, merged
// after it (#10741), still loaded the removed global, and the text
// assertions above cannot see a reference to a symbol nothing declares.
// Any receiver not declared a plain Array takes this guard -- OpenCode's
// id.ts loops over Buffers -- so ask LLVM, the one check that sees an
// undefined value.
for (name, elem) in [
("rarr_typed_verify", Type::Named("Float64Array".to_string())),
("rarr_buffer_verify", Type::Named("Buffer".to_string())),
] {
let ir = probe_ir(name, elem, physics_body(), None);
assert!(
ir.contains("rloop.ta"),
"{name}: the loop must take the typed region guard for this test to mean anything:\n{ir}"
);
assert!(
!ir.contains("@PERRY_TA_VIEW_GUARD"),
"{name}: the guard reads the receiver's storage byte, not a removed global:\n{ir}"
);
crate::testing::verify_ir(&ir, name)
.unwrap_or_else(|e| panic!("{name}: LLVM rejected the module: {e}\n{ir}"));
}
}

#[test]
fn a_call_in_the_body_sets_the_dirty_flag_and_the_next_iteration_rechecks() {
let mut body = physics_body();
Expand Down
Loading