gex-emit tier-1: dying_after fills only the statements it counted

A block with values but no statements defaults their last use to
statement 0, which the counting pass skips (start has one slot) and the
fill pass then wrote at vals[0] of an empty Vec: panic_bounds_check in
the page's tier-1 emitter. Muck (Unity) in the page crashed so on every
d7c4176b-based tree (edger, mk2: pid 1 trapped inside the shim in
tier1::dying_after). From 64b129ce's counted fill.

Co-Authored-By: Claude Opus 5.5 (1M context) <[email protected]>
Claude-Session: https://claude.ai/code/session_ea9f71488b6e448da93d6c2dabf4879c
This commit is contained in:
velzieandClaude Opus 5.5 committed 2026-10-11 18:06:44 -04:00
1 parent a2d4c96444
commit 6ac92f459d
1 file changed
+14 -1
+14 -1
View File
@@ -5277,8 +5277,12 @@ fn dying_after(block: &Block) -> Dying {
}
let mut fill = start.clone();
let mut vals = vec![0u32; *start.last().unwrap_or(&0) as usize];
// Only the statements counted above (`i + 1` within `start`): a block
// with values but no statements defaults them to statement 0, which it
// does not have -- filling it anyway wrote past `vals` (Muck in the page,
// a tier-1 panic_bounds_check here).
for (v, lu) in last.into_iter().enumerate() {
if let Some(i) = lu {
if let Some(i) = lu.filter(|&i| i + 1 < start.len()) {
if let Some(at) = fill.get_mut(i) {
vals[*at as usize] = v as u32;
*at += 1;
@@ -5650,6 +5654,15 @@ fn atomic_cmpxchg_op(ty: Ty, sz: MemSz) -> u32 {
mod tests {
use super::*;
#[test]
fn dying_after_a_block_with_values_and_no_statements() {
let mut b = Block::new(0x1000);
b.n_values = 3;
let d = dying_after(&b);
assert_eq!(d.start, vec![0]);
assert!(d.vals.is_empty());
}
#[test]
fn atomic_groups_are_seven_wide() {
assert_eq!(atomic_rmw_op(RmwOp::Add, Ty::I32, MemSz::B4), 0x1e);