Improve infer
This commit is contained in:
+80
-49
@@ -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,55 +91,44 @@ 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 = ®_fns[req.fn_id];
|
let f = ®_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 code = &f.code[block.start..block.end];
|
||||||
|
let code = code.iter().chain(&block.postfix);
|
||||||
|
|
||||||
|
for instr in code {
|
||||||
let is_ret = matches!(instr.op, Return | ReturnAsync | ReturnUndef);
|
let is_ret = matches!(instr.op, Return | ReturnAsync | ReturnUndef);
|
||||||
|
|
||||||
if instr.dsts.is_empty() && !is_ret {
|
if instr.dsts.is_empty() && !is_ret {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TODO: Some of these ops may not every show up by this point
|
||||||
let ty = match instr.op {
|
let ty = match instr.op {
|
||||||
// ──────────────────────────────────── Literals ────────────────────────────────────
|
// ──────────────────────────────────── Literals ────────────────────────────────────
|
||||||
PushMinus1 | Push0 | Push1 | Push2 | Push3 | Push4 | Push5 | Push6 | Push7 | PushI8
|
PushMinus1 | Push0 | Push1 | Push2 | Push3 | Push4 | Push5 | Push6 | Push7
|
||||||
| PushI16 | PushI32 => Type::Int,
|
| PushI8 | PushI16 | PushI32 => Type::Int,
|
||||||
|
|
||||||
PushTrue | PushFalse => Type::Bool,
|
PushTrue | PushFalse => Type::Bool,
|
||||||
|
|
||||||
// TODO: Fclosure will disappear soon
|
PushConst | PushConst8 => match instr.imm {
|
||||||
PushConst | PushConst8 | Fclosure | Fclosure8 => match instr.imm {
|
|
||||||
ImmOperand::Const(k) => match f.cpool.get(k as usize) {
|
ImmOperand::Const(k) => match f.cpool.get(k as usize) {
|
||||||
Some(JsValue::Int(_)) => Type::Int,
|
Some(JsValue::Int(_)) => Type::Int,
|
||||||
Some(JsValue::Float(_)) => Type::Float,
|
Some(JsValue::Float(_)) => Type::Float,
|
||||||
@@ -124,7 +140,7 @@ fn walk_fn(
|
|||||||
},
|
},
|
||||||
|
|
||||||
// ─────────────────────────────────── 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)
|
||||||
@@ -133,25 +149,24 @@ fn walk_fn(
|
|||||||
_ => Type::Any,
|
_ => Type::Any,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Neg | Plus | Inc | Dec => match val_types[instr.args[0]] {
|
Neg | Plus | Inc | Dec => match val_types[instr.args[0]] {
|
||||||
Type::Int => Type::Int,
|
Type::Int => Type::Int,
|
||||||
Type::Float => Type::Float,
|
Type::Float => Type::Float,
|
||||||
_ => Type::Any,
|
_ => Type::Any,
|
||||||
},
|
},
|
||||||
|
Div => Type::Float,
|
||||||
|
|
||||||
Lt | Lte | Gt | Gte | Eq | Neq | StrictEq | StrictNeq | Not => Type::Bool,
|
Lt | Lte | Gt | Gte | Eq | Neq | StrictEq | StrictNeq | Not => Type::Bool,
|
||||||
|
|
||||||
// ────────────────────────────────── Value Movers ──────────────────────────────────
|
// ────────────────────────────────── Value Movers ──────────────────────────────────
|
||||||
Dup | SetLoc => val_types[instr.args[0]].clone(),
|
Dup | SetLoc | SetName => val_types[instr.args[0]].clone(),
|
||||||
|
|
||||||
// ──────────────────────────────────── Arguments ───────────────────────────────────
|
// ──────────────────────────────────── Arguments ───────────────────────────────────
|
||||||
OpCode::GetArg0 => req.arg_types.first().cloned().unwrap_or(Type::Undefined),
|
GetArg0 => req.arg_types.first().cloned().unwrap_or(Type::Undefined),
|
||||||
OpCode::GetArg1 => req.arg_types.get(1).cloned().unwrap_or(Type::Undefined),
|
GetArg1 => req.arg_types.get(1).cloned().unwrap_or(Type::Undefined),
|
||||||
OpCode::GetArg2 => req.arg_types.get(2).cloned().unwrap_or(Type::Undefined),
|
GetArg2 => req.arg_types.get(2).cloned().unwrap_or(Type::Undefined),
|
||||||
OpCode::GetArg3 => req.arg_types.get(3).cloned().unwrap_or(Type::Undefined),
|
GetArg3 => req.arg_types.get(3).cloned().unwrap_or(Type::Undefined),
|
||||||
|
GetArg => match instr.imm {
|
||||||
OpCode::GetArg => match instr.imm {
|
|
||||||
ImmOperand::Arg(k) => req
|
ImmOperand::Arg(k) => req
|
||||||
.arg_types
|
.arg_types
|
||||||
.get(k as usize)
|
.get(k as usize)
|
||||||
@@ -161,9 +176,9 @@ fn walk_fn(
|
|||||||
},
|
},
|
||||||
|
|
||||||
// ──────────────────────────────────── Objects ─────────────────────────────────────
|
// ──────────────────────────────────── Objects ─────────────────────────────────────
|
||||||
OpCode::Object => Type::Empty,
|
Object => Type::Empty,
|
||||||
OpCode::DefineField => {
|
DefineField => {
|
||||||
let atom = match instr.imm {
|
let field = match instr.imm {
|
||||||
ImmOperand::Atom(atom) => atom,
|
ImmOperand::Atom(atom) => atom,
|
||||||
_ => unreachable!(),
|
_ => unreachable!(),
|
||||||
};
|
};
|
||||||
@@ -171,14 +186,26 @@ fn walk_fn(
|
|||||||
let arg0_ty = val_types[instr.args[0]].clone();
|
let arg0_ty = val_types[instr.args[0]].clone();
|
||||||
let arg1_ty = val_types[instr.args[1]].clone();
|
let arg1_ty = val_types[instr.args[1]].clone();
|
||||||
|
|
||||||
ReturnOf((FnKind::DefineField(atom), vec![arg0_ty, arg1_ty]))
|
ReturnOf((FnKind::DefineField(field), vec![arg0_ty, arg1_ty]))
|
||||||
}
|
}
|
||||||
|
GetField => {
|
||||||
|
let field = match instr.imm {
|
||||||
|
ImmOperand::Atom(atom) => atom,
|
||||||
|
_ => unreachable!(),
|
||||||
|
};
|
||||||
|
|
||||||
|
let ty = val_types[instr.args[0]].clone();
|
||||||
|
get_field(&ty, field, cache)
|
||||||
|
}
|
||||||
|
|
||||||
|
// ──────────────────────────────── Function Calls ──────────────────────────────────
|
||||||
|
Call | Call0 | Call1 | Call2 | Call3 | TailCall => todo!(),
|
||||||
|
|
||||||
// ────────────────────────────────────── Returns ───────────────────────────────────
|
// ────────────────────────────────────── Returns ───────────────────────────────────
|
||||||
Return | ReturnAsync => val_types[instr.args[0]].clone(),
|
Return | ReturnAsync => val_types[instr.args[0]].clone(),
|
||||||
ReturnUndef => Type::Empty,
|
ReturnUndef => Type::Empty,
|
||||||
|
|
||||||
_ => unreachable!(),
|
_ => unreachable!("{instr:?}"),
|
||||||
};
|
};
|
||||||
|
|
||||||
if is_ret {
|
if is_ret {
|
||||||
@@ -189,14 +216,18 @@ fn walk_fn(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(InferResult {
|
|
||||||
val_types,
|
|
||||||
ret_type,
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn atom_to_str(a: &AtomRef, atoms: &[Atom]) -> String {
|
cache.insert(
|
||||||
|
req,
|
||||||
|
InferResult {
|
||||||
|
val_types,
|
||||||
|
ret_type,
|
||||||
|
},
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
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
|
||||||
|
|||||||
Reference in New Issue
Block a user