Files
jsrt/compiler/src/transform/expand.rs
T
2026-10-13 11:52:02 +00:00

158 lines
4.3 KiB
Rust

//! Process instrs that are a simple sequence of other instrs
//!
//! Useless instrs are replaced with `nop` since simply dropping instrs can break jump targets
use super::{ENV_ARG, PROTO_FIELD};
use crate::{
libqjs::parse::{Atom, ImmOperand as Imm, Instr, JsValue, opcodes::OpCode},
native,
};
#[allow(non_snake_case)]
pub fn try_replace_expansion(instr: &Instr, out: &mut Vec<Instr>) -> bool {
use OpCode::*;
let seq = match (instr.op, instr.imm) {
(Call0, _) => [
(Nop, Imm::None),
(Nop, Imm::None),
(Undefined, Imm::None),
(Call, Imm::Npop(1)),
],
(Call1, _) => [
(Nop, Imm::None),
(Nop, Imm::None),
(Undefined, Imm::None),
(Call, Imm::Npop(2)),
],
(Call2, _) => [
(Nop, Imm::None),
(Nop, Imm::None),
(Undefined, Imm::None),
(Call, Imm::Npop(3)),
],
(Call3, _) => [
(Nop, Imm::None),
(Nop, Imm::None),
(Undefined, Imm::None),
(Call, Imm::Npop(4)),
],
(Call, Imm::Npop(n)) => [
(Nop, Imm::None),
(Nop, Imm::None),
(Undefined, Imm::None),
(Call, Imm::Npop(n + 1)),
],
(TailCall, Imm::Npop(n)) => [
(Nop, Imm::None),
(Undefined, Imm::None),
(Call, Imm::Npop(n + 1)),
(Return, Imm::None),
],
(TailCallMethod, imm) => [
(Nop, Imm::None),
(Nop, Imm::None),
(CallMethod, imm),
(Return, Imm::None),
],
(GetField2, imm) => [
(Nop, Imm::None),
(Nop, Imm::None),
(Dup, Imm::None),
(GetField, imm),
],
(GetLoc0Loc1, _) => [
(Nop, Imm::None),
(Nop, Imm::None),
(GetLoc0, Imm::None),
(GetLoc1, Imm::None),
],
(IncLoc, imm) => [
(Nop, Imm::None),
(GetLoc, imm),
(Inc, Imm::None),
(PutLoc, imm),
],
(DecLoc, imm) => [
(Nop, Imm::None),
(GetLoc, imm),
(Dec, Imm::None),
(PutLoc, imm),
],
// The added value is on the stack, the local is the left operand
(AddLoc, imm) => [
(GetLoc, imm),
(Swap, Imm::None),
(Add, Imm::None),
(PutLoc, imm),
],
(SetProto, _) => [
(Nop, Imm::None),
(Nop, Imm::None),
(Nop, Imm::None),
(DefineField, Imm::Atom(PROTO_FIELD)),
],
(DefineMethod, Imm::AtomU8 { atom, arg }) => {
assert!(arg & 3 == 0, "getters and setters are not supported");
[
(Nop, Imm::None),
(Nop, Imm::None),
(Nop, Imm::None),
(DefineField, Imm::Atom(atom)),
]
}
// The fn itself, which is its env
(SpecialObject, Imm::U8(2)) => [
(Nop, Imm::None),
(Nop, Imm::None),
(Nop, Imm::None),
(GetArg, Imm::Arg(ENV_ARG)),
],
// [[HomeObject]], which only `super` reads. `super` is not supported
(SpecialObject, Imm::U8(4)) => unimplemented!(),
// A class constructor called without `new` throws. That is not checked for the moment
(CheckCtor, _) => [
(Nop, Imm::None),
(Nop, Imm::None),
(Nop, Imm::None),
(Nop, Imm::None),
],
// arguments, new.target, import.meta
(SpecialObject, _) => unimplemented!(),
_ => return false,
};
for (op, imm) in seq {
out.push(Instr {
pc: instr.pc,
op,
imm,
});
}
true
}
/// Replace a read of a global with the constant it is (see `native`)
pub fn replace_get_var(
instr: &Instr,
atoms: &[Atom],
cpool: &mut Vec<JsValue>,
out: &mut Vec<Instr>,
) -> bool {
if instr.op != OpCode::GetVar {
return false;
}
cpool.push(native::global(instr.imm.atom(), atoms));
out.push(Instr {
pc: instr.pc,
op: OpCode::PushConst,
imm: Imm::Const(cpool.len() as u32 - 1),
});
true
}