Improve infer

This commit is contained in:
UditDey
2026-09-13 16:49:36 +00:00
parent 61c4971fa6
commit 5d687e308e
+132 -101
View File
@@ -12,7 +12,6 @@ use crate::{
pub enum FnKind { pub enum FnKind {
Id(usize), Id(usize),
DefineField(AtomRef), DefineField(AtomRef),
PutField(AtomRef),
} }
#[derive(Debug, Clone, Hash, PartialEq, Eq)] #[derive(Debug, Clone, Hash, PartialEq, Eq)]
@@ -42,16 +41,44 @@ fn join_ty(a: &Type, b: &Type) -> Type {
} }
} }
fn get_field(ty: &Type, field: AtomRef, cache: &mut TypeCache) -> Type {
match ty {
Type::ReturnOf((fn_kind, args)) => match fn_kind {
FnKind::Id(id) => {
let ret_ty = cache
.get(&InferRequest {
fn_id: *id,
arg_types: args.clone(),
})
.unwrap()
.ret_type
.clone();
get_field(&ret_ty, field, cache)
}
FnKind::DefineField(atom) => {
if *atom == field {
args[0].clone()
} else {
Type::Undefined
}
}
},
Type::Any => Type::Any,
_ => Type::Undefined,
}
}
#[derive(Debug, Hash, PartialEq, Eq)] #[derive(Debug, Hash, PartialEq, Eq)]
pub struct InferRequest { pub struct InferRequest {
fn_id: usize, pub fn_id: usize,
arg_types: Vec<Type>, pub arg_types: Vec<Type>,
} }
#[derive(Debug)] #[derive(Debug)]
pub struct InferResult { pub struct InferResult {
val_types: Vec<Type>, pub val_types: Vec<Type>,
ret_type: Type, pub ret_type: Type,
} }
pub type TypeCache = HashMap<InferRequest, InferResult>; pub type TypeCache = HashMap<InferRequest, InferResult>;
@@ -64,139 +91,143 @@ pub fn infer_types(reg_fns: &[RegFn], atoms: &[Atom]) -> TypeCache {
.unwrap(); .unwrap();
let mut cache = TypeCache::with_capacity(reg_fns.len()); let mut cache = TypeCache::with_capacity(reg_fns.len());
let mut worklist = vec![];
worklist.push(InferRequest { let req = InferRequest {
fn_id: main_fn_id, fn_id: main_fn_id,
arg_types: vec![], arg_types: vec![],
}); };
while let Some(req) = worklist.pop() { walk_fn(req, reg_fns, &mut cache);
match walk_fn(&req, reg_fns, &mut cache) {
Ok(res) => {
cache.insert(req, res);
}
Err(new_req) => {
worklist.push(new_req);
worklist.push(req);
}
}
}
cache cache
} }
fn walk_fn( fn walk_fn(req: InferRequest, reg_fns: &[RegFn], cache: &mut TypeCache) {
req: &InferRequest,
reg_fns: &[RegFn],
cache: &mut TypeCache,
) -> Result<InferResult, InferRequest> {
use OpCode::*; use OpCode::*;
let f = &reg_fns[req.fn_id]; let f = &reg_fns[req.fn_id];
let mut val_types = vec![Type::Unknown; f.val_count]; let mut val_types = vec![Type::Unknown; f.val_count];
let mut ret_type = Type::Unknown; let mut ret_type = Type::Unknown;
for instr in &f.code { for block in &f.blocks {
let is_ret = matches!(instr.op, Return | ReturnAsync | ReturnUndef); let code = &f.code[block.start..block.end];
let code = code.iter().chain(&block.postfix);
if instr.dsts.is_empty() && !is_ret { for instr in code {
continue; let is_ret = matches!(instr.op, Return | ReturnAsync | ReturnUndef);
}
let ty = match instr.op { if instr.dsts.is_empty() && !is_ret {
// ──────────────────────────────────── Literals ──────────────────────────────────── continue;
PushMinus1 | Push0 | Push1 | Push2 | Push3 | Push4 | Push5 | Push6 | Push7 | PushI8 }
| PushI16 | PushI32 => Type::Int,
PushTrue | PushFalse => Type::Bool, // TODO: Some of these ops may not every show up by this point
let ty = match instr.op {
// ──────────────────────────────────── Literals ────────────────────────────────────
PushMinus1 | Push0 | Push1 | Push2 | Push3 | Push4 | Push5 | Push6 | Push7
| PushI8 | PushI16 | PushI32 => Type::Int,
// TODO: Fclosure will disappear soon PushTrue | PushFalse => Type::Bool,
PushConst | PushConst8 | Fclosure | Fclosure8 => match instr.imm {
ImmOperand::Const(k) => match f.cpool.get(k as usize) { PushConst | PushConst8 => match instr.imm {
Some(JsValue::Int(_)) => Type::Int, ImmOperand::Const(k) => match f.cpool.get(k as usize) {
Some(JsValue::Float(_)) => Type::Float, Some(JsValue::Int(_)) => Type::Int,
Some(JsValue::BoolTrue) | Some(JsValue::BoolFalse) => Type::Bool, Some(JsValue::Float(_)) => Type::Float,
Some(&JsValue::FnById(id)) => Type::Fn(FnKind::Id(id)), Some(JsValue::BoolTrue) | Some(JsValue::BoolFalse) => Type::Bool,
Some(&JsValue::FnById(id)) => Type::Fn(FnKind::Id(id)),
_ => unreachable!(),
},
_ => unreachable!(), _ => unreachable!(),
}, },
_ => unreachable!(),
},
// ─────────────────────────────────── Arithmetic ─────────────────────────────────── // ─────────────────────────────────── Arithmetic ───────────────────────────────────
Add | Sub | Mul | Div | Mod | Pow => { Add | Sub | Mul | Mod | Pow => {
match (&val_types[instr.args[0]], &val_types[instr.args[1]]) { match (&val_types[instr.args[0]], &val_types[instr.args[1]]) {
(Type::Int, Type::Int) => Type::Int, (Type::Int, Type::Int) => Type::Int,
(Type::Float, Type::Float) (Type::Float, Type::Float)
| (Type::Int, Type::Float) | (Type::Int, Type::Float)
| (Type::Float, Type::Int) => Type::Float, | (Type::Float, Type::Int) => Type::Float,
_ => Type::Any, _ => Type::Any,
}
} }
} Neg | Plus | Inc | Dec => match val_types[instr.args[0]] {
Type::Int => Type::Int,
Type::Float => Type::Float,
_ => Type::Any,
},
Div => Type::Float,
Neg | Plus | Inc | Dec => match val_types[instr.args[0]] { Lt | Lte | Gt | Gte | Eq | Neq | StrictEq | StrictNeq | Not => Type::Bool,
Type::Int => Type::Int,
Type::Float => Type::Float,
_ => Type::Any,
},
Lt | Lte | Gt | Gte | Eq | Neq | StrictEq | StrictNeq | Not => Type::Bool, // ────────────────────────────────── Value Movers ──────────────────────────────────
Dup | SetLoc | SetName => val_types[instr.args[0]].clone(),
// ────────────────────────────────── Value Movers ────────────────────────────────── // ──────────────────────────────────── Arguments ───────────────────────────────────
Dup | SetLoc => val_types[instr.args[0]].clone(), GetArg0 => req.arg_types.first().cloned().unwrap_or(Type::Undefined),
GetArg1 => req.arg_types.get(1).cloned().unwrap_or(Type::Undefined),
// ──────────────────────────────────── Arguments ─────────────────────────────────── GetArg2 => req.arg_types.get(2).cloned().unwrap_or(Type::Undefined),
OpCode::GetArg0 => req.arg_types.first().cloned().unwrap_or(Type::Undefined), GetArg3 => req.arg_types.get(3).cloned().unwrap_or(Type::Undefined),
OpCode::GetArg1 => req.arg_types.get(1).cloned().unwrap_or(Type::Undefined), GetArg => match instr.imm {
OpCode::GetArg2 => req.arg_types.get(2).cloned().unwrap_or(Type::Undefined), ImmOperand::Arg(k) => req
OpCode::GetArg3 => req.arg_types.get(3).cloned().unwrap_or(Type::Undefined), .arg_types
.get(k as usize)
OpCode::GetArg => match instr.imm { .cloned()
ImmOperand::Arg(k) => req .unwrap_or(Type::Undefined),
.arg_types
.get(k as usize)
.cloned()
.unwrap_or(Type::Undefined),
_ => unreachable!(),
},
// ──────────────────────────────────── Objects ─────────────────────────────────────
OpCode::Object => Type::Empty,
OpCode::DefineField => {
let atom = match instr.imm {
ImmOperand::Atom(atom) => atom,
_ => unreachable!(), _ => unreachable!(),
}; },
let arg0_ty = val_types[instr.args[0]].clone(); // ──────────────────────────────────── Objects ─────────────────────────────────────
let arg1_ty = val_types[instr.args[1]].clone(); Object => Type::Empty,
DefineField => {
let field = match instr.imm {
ImmOperand::Atom(atom) => atom,
_ => unreachable!(),
};
ReturnOf((FnKind::DefineField(atom), vec![arg0_ty, arg1_ty])) let arg0_ty = val_types[instr.args[0]].clone();
} let arg1_ty = val_types[instr.args[1]].clone();
// ────────────────────────────────────── Returns ─────────────────────────────────── ReturnOf((FnKind::DefineField(field), vec![arg0_ty, arg1_ty]))
Return | ReturnAsync => val_types[instr.args[0]].clone(), }
ReturnUndef => Type::Empty, GetField => {
let field = match instr.imm {
ImmOperand::Atom(atom) => atom,
_ => unreachable!(),
};
_ => unreachable!(), let ty = val_types[instr.args[0]].clone();
}; get_field(&ty, field, cache)
}
if is_ret { // ──────────────────────────────── Function Calls ──────────────────────────────────
ret_type = join_ty(&ret_type, &ty); Call | Call0 | Call1 | Call2 | Call3 | TailCall => todo!(),
} else {
for &dst in &instr.dsts { // ────────────────────────────────────── Returns ───────────────────────────────────
val_types[dst] = join_ty(&val_types[dst], &ty); Return | ReturnAsync => val_types[instr.args[0]].clone(),
ReturnUndef => Type::Empty,
_ => unreachable!("{instr:?}"),
};
if is_ret {
ret_type = join_ty(&ret_type, &ty);
} else {
for &dst in &instr.dsts {
val_types[dst] = join_ty(&val_types[dst], &ty);
}
} }
} }
} }
Ok(InferResult { cache.insert(
val_types, req,
ret_type, InferResult {
}) val_types,
ret_type,
},
);
} }
pub fn atom_to_str(a: &AtomRef, atoms: &[Atom]) -> String { fn atom_to_str(a: &AtomRef, atoms: &[Atom]) -> String {
match a { match a {
AtomRef::TaggedInt(n) => n.to_string(), AtomRef::TaggedInt(n) => n.to_string(),
AtomRef::Predefined(i) => atom_names::PREDEFINED_ATOMS AtomRef::Predefined(i) => atom_names::PREDEFINED_ATOMS