Big progress by Claude

This commit is contained in:
2026-09-25 19:31:15 +00:00
parent 183c4e35c9
commit e93ce94ac7
15 changed files with 1577 additions and 470 deletions
+85 -22
View File
@@ -41,6 +41,9 @@ impl std::fmt::Debug for RegInstr {
pub struct RegFn {
pub name: AtomRef,
pub arg_count: usize,
/// Locals and args are not renamed: local `k` is value id `k`, and arg `k`
/// is value id `var_count + k`.
pub var_count: usize,
pub val_count: usize,
pub cpool: Vec<JsValue>,
pub code: Vec<RegInstr>,
@@ -52,7 +55,8 @@ impl RegFn {
let mut blocks = find_basic_blocks(&flat_fn.code);
let mut out = vec![RegInstr::nop(); flat_fn.code.len()];
let mut next_val = flat_fn.var_count; // Reserve values for locals
// Reserve values for locals, then args
let mut next_val = flat_fn.var_count + flat_fn.arg_count;
let mut entry_stacks: Vec<Option<Vec<usize>>> = vec![None; blocks.len()];
let mut worklist = vec![];
@@ -64,7 +68,7 @@ impl RegFn {
let block = &mut blocks[block_idx];
let mut stack: Vec<usize> = entry_stacks[block_idx].clone().unwrap_or_default();
walk_block(block, &flat_fn.code, &mut stack, &mut next_val, &mut out);
walk_block(block, &flat_fn.code, flat_fn.var_count, &mut stack, &mut next_val, &mut out);
for succ_idx in block.succ.clone() {
let succ_stack = &mut entry_stacks[succ_idx];
@@ -82,6 +86,7 @@ impl RegFn {
Self {
name: flat_fn.name,
arg_count: flat_fn.arg_count,
var_count: flat_fn.var_count,
val_count: next_val,
cpool: flat_fn.cpool,
code: out,
@@ -93,6 +98,7 @@ impl RegFn {
fn walk_block(
block: &BasicBlock,
code: &[Instr],
var_count: usize,
stack: &mut Vec<usize>,
next_val: &mut usize,
out: &mut [RegInstr],
@@ -123,39 +129,37 @@ fn walk_block(
out_instr.args.push(v);
}
// Locals are not renamed, local read/writes need special handling
// Handle read of a local value
match is_local_read(instr) {
Some(loc) => {
stack.push(loc);
out_instr.op = OpCode::Nop;
out_instr.imm = ImmOperand::None;
out[instr_idx] = out_instr;
continue;
}
None if instr.op == OpCode::GetLoc0Loc1 => {
stack.push(0);
stack.push(1);
out_instr.op = OpCode::Nop;
// Locals and args are not renamed, their read/writes need special handling
// Handle read of a local or arg value
match is_var_read(instr, var_count) {
// A read takes the var's value at this point. The stack holds a
// copy, never the var itself.
Some(var) => {
let v = fresh_val();
stack.push(v);
out_instr.op = OpCode::SetLoc;
out_instr.imm = ImmOperand::None;
out_instr.dsts.push(v);
out_instr.args.push(var);
out[instr_idx] = out_instr;
continue;
}
None => (),
}
// Handle write of a local value
match is_local_write(instr) {
// Handle write of a local or arg value
match is_var_write(instr, var_count) {
Some(loc) => {
out_instr.op = OpCode::SetLoc;
out_instr.imm = ImmOperand::None;
out_instr.dsts.push(loc);
out[instr_idx] = out_instr;
// set_loc also leaves the value it stored on the stack.
if n_push == 1 {
stack.push(loc);
stack.push(out_instr.args[0]);
}
out[instr_idx] = out_instr;
continue;
}
None if instr.op == OpCode::SetLocUninitialized => {
@@ -167,6 +171,39 @@ fn walk_block(
None => (),
}
// set_home_object binds [[HomeObject]], which only `super` reads, and
// super is not supported. It returns both its operands unchanged, so on
// values it is nothing at all: hand them straight back.
if instr.op == OpCode::SetHomeObject {
stack.push(out_instr.args[1]);
stack.push(out_instr.args[0]);
out_instr.op = OpCode::Nop;
out_instr.imm = ImmOperand::None;
out[instr_idx] = out_instr;
continue;
}
// dup only copies a stack slot; it computes nothing. The copy is the
// same value, so whatever is done through it is done to the original.
if instr.op == OpCode::Dup {
stack.push(out_instr.args[0]);
stack.push(out_instr.args[0]);
out_instr.op = OpCode::Nop;
out_instr.imm = ImmOperand::None;
out[instr_idx] = out_instr;
continue;
}
// swap only reorders the stack; it computes nothing.
if instr.op == OpCode::Swap {
stack.push(out_instr.args[0]);
stack.push(out_instr.args[1]);
out_instr.op = OpCode::Nop;
out_instr.imm = ImmOperand::None;
out[instr_idx] = out_instr;
continue;
}
// A field write is a define whose result lands back in the object's own
// value, so later reads of that object see the field. This is what lets
// a chain of writes to one local build a single object type, and it is
@@ -203,7 +240,10 @@ fn extra_pops(instr: &Instr) -> usize {
}
}
fn is_local_read(instr: &Instr) -> Option<usize> {
/// The value id of the local or arg that `instr` reads, if any.
fn is_var_read(instr: &Instr, var_count: usize) -> Option<usize> {
let arg = |k: usize| Some(var_count + k);
match instr.op {
OpCode::GetLoc0 => Some(0),
OpCode::GetLoc1 => Some(1),
@@ -215,11 +255,24 @@ fn is_local_read(instr: &Instr) -> Option<usize> {
_ => unreachable!(),
},
OpCode::GetArg0 => arg(0),
OpCode::GetArg1 => arg(1),
OpCode::GetArg2 => arg(2),
OpCode::GetArg3 => arg(3),
OpCode::GetArg => match instr.imm {
ImmOperand::Arg(k) => arg(k as usize),
_ => unreachable!(),
},
_ => None,
}
}
fn is_local_write(instr: &Instr) -> Option<usize> {
/// The value id of the local or arg that `instr` writes, if any.
fn is_var_write(instr: &Instr, var_count: usize) -> Option<usize> {
let arg = |k: usize| Some(var_count + k);
match instr.op {
OpCode::PutLoc0 | OpCode::SetLoc0 => Some(0),
OpCode::PutLoc1 | OpCode::SetLoc1 => Some(1),
@@ -236,6 +289,16 @@ fn is_local_write(instr: &Instr) -> Option<usize> {
_ => unreachable!(),
},
OpCode::PutArg0 | OpCode::SetArg0 => arg(0),
OpCode::PutArg1 | OpCode::SetArg1 => arg(1),
OpCode::PutArg2 | OpCode::SetArg2 => arg(2),
OpCode::PutArg3 | OpCode::SetArg3 => arg(3),
OpCode::PutArg | OpCode::SetArg => match instr.imm {
ImmOperand::Arg(k) => arg(k as usize),
_ => unreachable!(),
},
_ => None,
}
}