Rewrite with working async

This commit is contained in:
2026-10-16 08:00:06 +00:00
parent d0c2f291e9
commit 1e4647347f
36 changed files with 5106 additions and 654 deletions
+115 -52
View File
@@ -42,11 +42,16 @@ pub fn walk_block(
continue;
}
if try_handle_misc(instr, stack, out_instr) {
if try_handle_shuffle(instr, stack, out_instr) {
continue;
}
handle_normal_case(instr, stack, &mut fresh_reg, out_instr);
if instr.op == OpCode::DefineFieldRef {
let prev = std::mem::replace(&mut out[instr_idx - 1], RegInstr::nop());
resolve_location(&prev, &mut out[instr_idx]);
}
}
}
@@ -163,61 +168,53 @@ fn try_handle_named_write(instr: &Instr, stack: &mut Vec<Operand>, out: &mut Reg
true
}
fn try_handle_misc(instr: &Instr, stack: &mut Vec<Operand>, out_instr: &mut RegInstr) -> bool {
use OpCode::*;
*out_instr = match instr.op {
// Duplicate stack top, out_instr becomes `nop`
Dup => {
let top = stack[stack.len() - 1].clone();
stack.push(top);
RegInstr {
dsts: vec![],
op: Nop,
imm: ImmOperand::None,
args: vec![],
}
}
// Duplicate the top 2 elems in stack, out_instr becomes `nop`
Dup2 => {
let a = stack[stack.len() - 1].clone();
let b = stack[stack.len() - 2].clone();
stack.push(b);
stack.push(a);
RegInstr {
dsts: vec![],
op: Nop,
imm: ImmOperand::None,
args: vec![],
}
}
// Swap top 2 elems in stack, out_instr becomes `nop`
Swap => {
let a = stack.pop().unwrap();
let b = stack.pop().unwrap();
stack.push(a);
stack.push(b);
RegInstr {
dsts: vec![],
op: Nop,
imm: ImmOperand::None,
args: vec![],
}
}
_ => return false,
/// Handles an instr that only moves stack values around: it pops `n` values, and pushes some
/// of them again. No value is made, so `out_instr` becomes `nop`
fn try_handle_shuffle(instr: &Instr, stack: &mut Vec<Operand>, out_instr: &mut RegInstr) -> bool {
let Some((n, pushes)) = shuffle(instr.op) else {
return false;
};
let popped = stack.split_off(stack.len() - n);
stack.extend(pushes.iter().map(|&i| popped[i]));
*out_instr = RegInstr::nop();
true
}
/// The shuffles: how many values an instr pops, and which of them it pushes, in stack order
/// (bottom first). The numbers index the popped values, bottom first
fn shuffle(op: OpCode) -> Option<(usize, &'static [usize])> {
use OpCode::*;
Some(match op {
Drop => (1, &[]), // a ->
Nip => (2, &[1]), // a b -> b
Nip1 => (3, &[1, 2]), // a b c -> b c
Dup => (1, &[0, 0]), // a -> a a
Dup1 => (2, &[0, 0, 1]), // a b -> a a b
Dup2 => (2, &[0, 1, 0, 1]), // a b -> a b a b
Dup3 => (3, &[0, 1, 2, 0, 1, 2]), // a b c -> a b c a b c
Insert2 => (2, &[1, 0, 1]), // obj a -> a obj a
Insert3 => (3, &[2, 0, 1, 2]), // obj prop a -> a obj prop a
Insert4 => (4, &[3, 0, 1, 2, 3]), // this obj prop a -> a this obj prop a
Perm3 => (3, &[1, 0, 2]), // obj a b -> a obj b
Perm4 => (4, &[2, 0, 1, 3]), // obj prop a b -> a obj prop b
Perm5 => (5, &[3, 0, 1, 2, 4]), // this obj prop a b -> a this obj prop b
Swap => (2, &[1, 0]), // a b -> b a
Swap2 => (4, &[2, 3, 0, 1]), // a b c d -> c d a b
Rot3l => (3, &[1, 2, 0]), // x a b -> a b x
Rot3r => (3, &[2, 0, 1]), // a b x -> x a b
Rot4l => (4, &[1, 2, 3, 0]), // x a b c -> a b c x
Rot5l => (5, &[1, 2, 3, 4, 0]), // x a b c d -> a b c d x
// Binds [[HomeObject]], which only `super` reads. `super` is not supported
SetHomeObject => (2, &[0, 1]),
// Makes the key a property key. A key is kept as it is (see infer)
ToPropkey2 => (2, &[0, 1]),
_ => return None,
})
}
/// Handle a normal instruction with simulated stack pushes/pops
fn handle_normal_case(
instr: &Instr,
@@ -225,6 +222,8 @@ fn handle_normal_case(
mut fresh_reg: impl FnMut() -> Operand,
out_instr: &mut RegInstr,
) {
use OpCode::*;
// op and imm carried as-is
*out_instr = RegInstr {
dsts: vec![],
@@ -244,12 +243,22 @@ fn handle_normal_case(
let n_pop = info.n_pop as usize + extra_pops;
let n_push = info.n_push as usize;
// Simulate this instr's pops
// Simulate this instr's pops, top first
for _ in 0..n_pop {
let x = stack.pop().unwrap();
out_instr.args.push(x);
}
if matches!(instr.op, Call | CallMethod | CallConstructor) {
out_instr.args = call_operands(instr.op, std::mem::take(&mut out_instr.args));
}
// These leave the object they wrote on the stack: the same value, not a new one
if matches!(instr.op, DefineField | DefineFieldRef) {
stack.push(out_instr.args[1]);
return;
}
// Simulate this instr's pushes
for _ in 0..n_push {
let x = fresh_reg();
@@ -257,3 +266,57 @@ fn handle_normal_case(
out_instr.dsts.push(x);
}
}
/// Put a call's popped operands in the order of the callee's params: the callee (which is its
/// env), `this`, then the args in source order
///
/// `call_method` has `this` at the bottom of its frame, below the callee. `call_constructor`
/// has the callee at the bottom and `this` above it (see transform/new.rs). `call` has `this`
/// on top, where transform pushed it
fn call_operands(op: OpCode, mut popped: Vec<Operand>) -> Vec<Operand> {
let (callee, this) = match op {
OpCode::CallMethod => {
let this = popped.pop().unwrap();
(popped.pop().unwrap(), this)
}
OpCode::CallConstructor => {
let callee = popped.pop().unwrap();
(callee, popped.pop().unwrap())
}
_ => {
let callee = popped.pop().unwrap();
(callee, popped.remove(0))
}
};
// What remains are the args, last arg first
popped.reverse();
[callee, this].into_iter().chain(popped).collect()
}
/// `define_field_ref x` defines `x` as the location that its value was read from (see
/// transform/closure.rs), and the instr before it is that read. Its value arg becomes the
/// location:
/// - a local or arg: the field is that binding
/// - the reg of an object `o`, for a read `get_field x` of `o`: the field is `o.x`, which is
/// a location itself (a capture that the owner gives to its own child)
///
/// The read itself is then not used, so it becomes `nop`
fn resolve_location(prev: &RegInstr, out_instr: &mut RegInstr) {
let value = out_instr.args[0];
assert_eq!(
prev.dsts,
[value],
"define_field_ref must follow the read of its value"
);
out_instr.args[0] = match prev.op {
OpCode::SetLoc => prev.args[0],
OpCode::GetField => {
assert!(prev.imm.atom() == out_instr.imm.atom());
prev.args[0]
}
op => panic!("define_field_ref after {op:?}, which reads no location"),
};
}