diff --git a/compiler/src/emit/functions.rs b/compiler/src/emit/functions.rs index be8714d..4557ebe 100644 --- a/compiler/src/emit/functions.rs +++ b/compiler/src/emit/functions.rs @@ -2,8 +2,9 @@ //! //! There are four kinds: //! -//! - **A JS function**, as infer walked it. Infer walks a JS function once for each set of -//! arg types that it is called with, and each walk is one C function (see `js`). +//! - **A JS function**, as infer walked it. Infer walks a JS function once for each context, +//! which is the function in one call site, and each set of arg types. Each walk is one C +//! function (see `js`). //! - **A lookup**, which gets a field that some objects of a class do not have. It gives the //! object's own field if the object has it, else it looks on the object's prototype, and so //! on along the prototype chain. At the end of the chain, the field is undefined. @@ -19,7 +20,7 @@ use crate::{infer::Type, libqjs::parse::AtomRef, regify::Operand, transform::PRO #[derive(Clone, Copy)] pub enum Function { - Js(usize), // a JS function, as one walk of infer: the walk's index + Js(usize), // a JS function, as one walk: the walk's index Lookup(usize, AtomRef), // a lookup of a field on the objects of a class: the class, and the field Start, Complete, @@ -171,7 +172,7 @@ fn complete_call(program: &Program) -> Option { Some(host_call(program, walk, &args)) } -/// A call of a JS function's walk, with each arg converted to its param's C type +/// A call of a walk's C function, with each arg converted to its param's C type fn host_call(program: &Program, walk: usize, args: &[(String, Member)]) -> String { let args: Vec = (args.iter().enumerate()) .map(|(i, (value, member))| { diff --git a/compiler/src/emit/instr.rs b/compiler/src/emit/instr.rs index 41ff5af..9a7c863 100644 --- a/compiler/src/emit/instr.rs +++ b/compiler/src/emit/instr.rs @@ -11,7 +11,7 @@ use super::{ values::{base, int, strict_eq, to_int32, to_number, truthy, undefined, unit_literal}, }; use crate::{ - infer::{InstrRef, ObjAddr, Type}, + infer::{CallSite, InstrRef, ObjAddr, Type}, libqjs::parse::{ImmOperand, JsValue, opcodes::OpCode}, native::NATIVES, regify::{Operand, RegInstr}, @@ -82,19 +82,21 @@ impl JsFunction<'_> { } } - /// Instr `idx` of this JS function - fn here(&self, idx: usize) -> InstrRef { - InstrRef { - fn_idx: self.program.cache.walks[self.walk].req.fn_idx, + /// The call site of the call at instr `idx` + fn call_site(&self, idx: usize) -> CallSite { + Some(InstrRef { + fn_idx: self.context.fn_idx, instr_idx: idx, - } + }) } - /// The class of the objects that instr `idx` makes, in this walk + /// The class of the objects that instr `idx` makes, in this walk's context pub(super) fn class_here(&self, idx: usize) -> usize { - let in_call = self.program.cache.walks[self.walk].req.in_call; - let made_at = self.here(idx); - self.program.types.class(ObjAddr { made_at, in_call }) + let obj = ObjAddr { + context: self.context, + instr: idx, + }; + self.program.types.class(obj) } /// `close_loc x`: a local that closures share gets a new cell with the same value. @@ -466,7 +468,7 @@ impl JsFunction<'_> { let mut types: Vec = instr.args.iter().map(|&s| self.ty(s)).collect(); types[0] = Type::Ref(program.types.classes[class].objs[0]); - let walk = (program.cache).find(fn_idx, Some(self.here(idx)), &types, program.fns); + let walk = (program.cache).find(fn_idx, self.call_site(idx), &types, program.fns); let mut args = vec![]; for i in 0..program.fns[fn_idx].arg_count { @@ -481,7 +483,7 @@ impl JsFunction<'_> { } let call = format!("js{walk}({})", args.join(", ")); - let ret = &program.cache.walks[walk].ret; + let ret = program.cache.ret(walk); // A function that never returns gives no value if *ret == Type::Unknown { diff --git a/compiler/src/emit/js.rs b/compiler/src/emit/js.rs index 872f0bd..2fac623 100644 --- a/compiler/src/emit/js.rs +++ b/compiler/src/emit/js.rs @@ -1,8 +1,9 @@ //! A JS function as a C function //! -//! Infer walks each JS function once for each set of arg types that it is called with. Each -//! walk is one C function, `js`, in which every value has the type that infer found in -//! that walk. +//! Infer walks a JS function once for each context, which is the function in one call site, +//! and each set of arg types. Each walk is one C function, `js`, in which every value +//! has the type that infer found in that walk. What the walk returns is in memory too (see +//! `TypeCache::ret`). //! //! Each local, arg and reg is a C local, of the C type of its type. A local or arg that a //! closure shares is a pointer to a cell (see transform/closure.rs). The cell is made when the @@ -20,16 +21,17 @@ use super::{ values::truthy, }; use crate::{ - infer::Type, + infer::{Context, Type}, libqjs::parse::opcodes::OpCode, regify::{BasicBlock, Operand, RegFn}, }; -/// A JS function's walk, as its C function is written +/// A walk of a JS function, as its C function is written pub struct JsFunction<'a> { pub program: &'a Program<'a>, pub body: Body<'a>, pub walk: usize, + pub context: Context, // the walk's function, and the call site that it is called from pub f: &'a RegFn, shared: Vec, // the locals and args that closures share: each is a pointer to a cell } @@ -37,8 +39,7 @@ pub struct JsFunction<'a> { /// `static js()`. A shared arg's param is named `p`, because its value /// goes into a cell named `a` pub fn signature(program: &Program, walk: usize) -> String { - let w = &program.cache.walks[walk]; - let f = &program.fns[w.req.fn_idx]; + let f = program.js_fn(walk); let shared = f.captured_slots(); let params: Vec = (0..f.arg_count) @@ -58,19 +59,20 @@ pub fn signature(program: &Program, walk: usize) -> String { } else { params.join(", ") }; - let ret = program.types.of(&w.ret); + let ret = program.types.of(program.cache.ret(walk)); format!("static {} js{walk}({params})", ret.c_name()) } /// The body of `js`: its locals, args and regs, then each block that can run pub fn body(program: &Program, walk: usize) -> String { let w = &program.cache.walks[walk]; - let f = &program.fns[w.req.fn_idx]; + let f = program.js_fn(walk); let mut js = JsFunction { program, body: Body::new(program), walk, + context: w.context, f, shared: f.captured_slots(), }; @@ -134,10 +136,9 @@ impl JsFunction<'_> { self.set(dst, value, CType::One(member)); } - /// Return `value`, of C type `from` + /// Return `value`, of C type `from`, as the C type of what the walk returns pub fn ret(&mut self, value: &str, from: CType) { - let walk = &self.program.cache.walks[self.walk]; - let to = self.program.types.of(&walk.ret); + let to = self.program.types.of(self.program.cache.ret(self.walk)); self.body.put("return ", value, from, to); } diff --git a/compiler/src/emit/program.rs b/compiler/src/emit/program.rs index 66d9819..ea25d4d 100644 --- a/compiler/src/emit/program.rs +++ b/compiler/src/emit/program.rs @@ -81,7 +81,7 @@ impl<'a> Program<'a> { } } - /// One C function for each walk of a JS function in infer's last round, one for each + /// One C function for each walk in infer's last round, one for each /// lookup, and the host's two entry points fn add_functions(&mut self) { let walks = (0..self.cache.walks.len()).filter(|&walk| self.cache.is_live(walk)); @@ -142,7 +142,7 @@ impl<'a> Program<'a> { continue; }; - let cpool = &self.fns[self.cache.walks[walk].req.fn_idx].cpool; + let cpool = &self.js_fn(walk).cpool; for instr in self.instrs(walk) { let text = match instr.op { @@ -183,9 +183,10 @@ impl<'a> Program<'a> { fn value_types(&self, function: &Function) -> Vec { match *function { Function::Js(walk) => { - let f = &self.fns[self.cache.walks[walk].req.fn_idx]; - let slots = slots(f).into_iter().map(|slot| self.slot_type(walk, slot)); - slots.chain([self.cache.walks[walk].ret.clone()]).collect() + let slots = slots(self.js_fn(walk)) + .into_iter() + .map(|slot| self.slot_type(walk, slot)); + slots.chain([self.cache.ret(walk).clone()]).collect() } Function::Lookup(class, field) => vec![self.lookup_type(class, field)], Function::Start | Function::Complete => vec![], @@ -196,7 +197,7 @@ impl<'a> Program<'a> { fn instrs(&self, walk: usize) -> impl Iterator + use<'a> { let (cache, fns) = (self.cache, self.fns); let walk = &cache.walks[walk]; - let f = &fns[walk.req.fn_idx]; + let f = &fns[walk.context.fn_idx]; let blocks = f.blocks.iter().zip(&walk.reachable); let blocks = blocks.filter(|(_, reachable)| **reachable); blocks.flat_map(|(block, _)| &f.code[block.start..block.end]) @@ -214,7 +215,12 @@ pub struct Case { pub first: bool, // the first case of its operands: an `if`. The others are `else if` } -impl Program<'_> { +impl<'a> Program<'a> { + /// The JS function of a walk + pub fn js_fn(&self, walk: usize) -> &'a RegFn { + &self.fns[self.cache.walks[walk].context.fn_idx] + } + /// The type of a local, arg or reg of a JS function, in one walk pub fn slot_type(&self, walk: usize, slot: Operand) -> Type { let addr = self.cache.addr(walk, slot, self.fns); diff --git a/compiler/src/infer/memory.rs b/compiler/src/infer/memory.rs index b390203..6f0019f 100644 --- a/compiler/src/infer/memory.rs +++ b/compiler/src/infer/memory.rs @@ -17,36 +17,39 @@ pub struct InstrRef { pub instr_idx: usize, } -/// The call instr that a request comes from. `None` for the entry fn, which the host calls +/// The call instr that a function is called from. `None` for a call from the host pub type CallSite = Option; -/// The objects that one instr makes, in the requests from one call site +/// A function, in one call site. Without polymorphism, infer would walk each context once. +/// Objects, and the bindings that closures share, are named by their context, so that every +/// walk of a context sees the same ones (see `TypeCache`) #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, PartialOrd, Ord)] -pub struct ObjAddr { - pub made_at: InstrRef, - pub in_call: CallSite, +pub struct Context { + pub fn_idx: usize, + pub call_site: CallSite, } -/// A location in the program, which memory holds a type for. A location that an object -/// can hold is named like an object, by where it is made and the call site of the request. -/// One that only its walk can see is named by the walk +/// The objects that one instr makes, in one context +#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, PartialOrd, Ord)] +pub struct ObjAddr { + pub context: Context, + pub instr: usize, +} + +/// A location in the program, which memory holds a type for /// -/// A request key can hold an address, in a `Type::Ref`. Every part of an address comes from -/// the program text (or the walks, which come from the keys), so there are finitely many -/// addresses and keys, and infer ends +/// Every location is named by its context and a place in its function, except the ones that +/// only one walk can see, which are named by the walk. A type can hold an object's address, +/// and a walk's arg types can hold that type, so objects are never named by a walk: that +/// could make new walks without end. Every part of an address comes from the program text or +/// from a walk, so there are finitely many, and infer ends #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, PartialOrd, Ord)] pub enum Addr { - Object(ObjAddr), // an object + Object(ObjAddr), // an object Field(ObjAddr, AtomRef), // a field of an object, unless the field shares another location - Captured { - fn_idx: usize, - slot: Operand, - in_call: CallSite, - }, // a binding that a closure shares (see transform/closure.rs) - Binding { - walk: usize, - slot: Operand, - }, // a binding that only its walk sees + Shared { context: Context, slot: Operand }, // a binding that closures share (see transform/closure.rs). The closures are objects, so it is named like them + Private { walk: usize, slot: Operand }, // a binding that only its walk sees + Return(usize), // what a walk returns } /// What each location holds, by its address. A location that nothing has reached holds @@ -226,9 +229,9 @@ impl Memory { /// Make its field `name` the location `addr` (see `define_field_ref`). Such a field is /// always made when the object is made, so it is defined /// - /// An address stands for all the objects that one instr makes in one call site. Two - /// walks from that call site can share two locations into the field: walks of one fn - /// with different closures as their env, for example. A field is one location, so those + /// An address stands for all the objects that one instr makes in one context. Two walks + /// of that context can share two locations into the field: walks with different + /// closures as their env, for example. A field is one location, so those /// two are one location too: they are merged, as the types of all its objects' fields are /// joined. Their C types are then one type as well pub fn share_field(&mut self, obj: ObjAddr, name: AtomRef, addr: Addr) { diff --git a/compiler/src/infer/mod.rs b/compiler/src/infer/mod.rs index 63878a3..8e8a1ec 100644 --- a/compiler/src/infer/mod.rs +++ b/compiler/src/infer/mod.rs @@ -1,11 +1,23 @@ -//! Infers a type for every location +//! Infers a type for every location in the program //! -//! A request is a fn, the call site that calls it, and the types of its args. Every location -//! has an address (see `Addr`), and memory holds one type for each address. An object is -//! named by the instr that makes it and the call site of the request that runs the instr. A -//! binding is named by its walk, or like an object if a closure shares it. A walk reads -//! memory and the returns of other requests, which only grow, so infer walks the program in -//! rounds until one changes nothing +//! **The base model.** A context is a function in one call site. Infer walks each context: +//! it goes over the function's instrs, and joins the type of each result into memory, which +//! holds one type for each location (see `Addr`). An object is named by the instr that makes +//! it and its context. A call walks the callee in its own context, and what the callee +//! returns is a location too. A walk reads memory, and memory only grows, so infer walks the +//! program in rounds, until a round changes nothing. +//! +//! **Polymorphism.** A function can be called with args of different types. Each set of arg +//! types gets a walk of its own, so a walk is a context and the types of its args. This adds +//! three things to the base model: +//! +//! 1. A walk's arg types are part of its key. An arg passes only its kind, not a known value, +//! so that there are finitely many keys (see `key`). +//! 2. A binding that no closure shares is named by its walk, so that each walk has its own +//! types (see `binding_addr`). Objects and shared bindings stay named by their context. +//! 3. A walk can go stale. When an arg's type grows, its call has a new key, so the call +//! makes a new walk. Only the walks of the last round are part of the program (see +//! `TypeCache::is_live`). mod memory; mod types; @@ -18,53 +30,40 @@ use crate::{ regify::{Operand, RegFn}, }; -pub use memory::{Addr, Builtins, CallSite, InstrRef, Memory, ObjAddr}; +pub use memory::{Addr, Builtins, CallSite, Context, InstrRef, Memory, ObjAddr}; pub use types::{LENGTH_FIELD, Shape, Type, join}; -/// A fn, the call it comes from, and the values of its args. Values compare by their -/// types, and an object by its address, so there are finitely many requests. The call site -/// is part of the key because it names the objects that the walk makes -#[derive(Debug, Clone, Hash, PartialEq, Eq)] -pub struct InferRequest { - pub fn_idx: usize, - pub in_call: CallSite, - pub arg_types: Vec, // one for each param -} - -impl InferRequest { - /// The request of a call of `fn_idx` from `in_call` with args of these types. An arg it - /// does not pass is undefined, and an arg it passes beyond its params is never read. - /// Only an arg's kind crosses a call, not a known value, else every literal passed would - /// make a new walk - fn new(fn_idx: usize, in_call: CallSite, arg_types: &[Type], fns: &[RegFn]) -> Self { - let params = (0..fns[fn_idx].arg_count) - .map(|i| arg_types.get(i).map_or(Type::Undefined, Type::widen)); - Self { - fn_idx, - in_call, - arg_types: params.collect(), - } - } -} - -/// A request and what its walks found. Its index in `TypeCache::walks` names its bindings, -/// whose types are in memory +/// A context, and the types of its args: what a walk walks. What the walk found is in memory #[derive(Debug)] pub struct Walk { - pub req: InferRequest, - pub ret: Type, // what it returns + pub context: Context, + pub arg_types: Vec, // one for each param pub reachable: Vec, // the blocks that can run round: usize, // the last round that walked it. A walk that the last round did not reach is stale - walking: bool, // it is on the call stack: a recursive call gives `ret` as it is + walking: bool, // it is on the call stack: a recursive call gives what it returned so far } #[derive(Debug)] pub struct TypeCache { pub walks: Vec, - ids: HashMap, // the walk of each request + ids: HashMap<(Context, Vec), usize>, // the walk of each context and arg types pub memory: Memory, round: usize, - changes: usize, // new walks and growths of a return. With the changes of memory, these tell whether a round found anything new +} + +/// The key of a call of `fn_idx` from `call_site` with args of these types. An arg that the +/// call does not pass is undefined, and an arg that it passes beyond the params is never +/// read. Only an arg's kind passes, not a known value, else every literal that a call passes +/// would make a new walk +fn key( + fn_idx: usize, + call_site: CallSite, + arg_types: &[Type], + fns: &[RegFn], +) -> (Context, Vec) { + let params = + (0..fns[fn_idx].arg_count).map(|i| arg_types.get(i).map_or(Type::Undefined, Type::widen)); + (Context { fn_idx, call_site }, params.collect()) } impl TypeCache { @@ -72,11 +71,11 @@ impl TypeCache { pub fn find( &self, fn_idx: usize, - in_call: CallSite, + call_site: CallSite, arg_types: &[Type], fns: &[RegFn], ) -> usize { - self.ids[&InferRequest::new(fn_idx, in_call, arg_types, fns)] + self.ids[&key(fn_idx, call_site, arg_types, fns)] } /// Whether the last round walked `w`. Only such walks are part of the program @@ -84,15 +83,21 @@ impl TypeCache { self.walks[w].round == self.round } - /// The address of a local, arg or reg of walk `w` - pub fn addr(&self, w: usize, slot: Operand, fns: &[RegFn]) -> Addr { - let req = &self.walks[w].req; - binding_addr(w, req, &fns[req.fn_idx].captured_slots(), slot) + /// What walk `w` returns + pub fn ret(&self, w: usize) -> &Type { + self.memory.get(Addr::Return(w)) } - /// Everything that can change, counted. Infer is done when a round leaves it the same + /// The address of a local, arg or reg of walk `w` + pub fn addr(&self, w: usize, slot: Operand, fns: &[RegFn]) -> Addr { + let context = self.walks[w].context; + binding_addr(w, context, &fns[context.fn_idx].captured_slots(), slot) + } + + /// Everything that can change, counted: memory, and the walks. Infer is done when a round + /// leaves it the same fn progress(&self) -> usize { - self.changes + self.memory.changes() + self.memory.changes() + self.walks.len() } /// The host's call of `__complete` with an op's handle: each closure that the binding can @@ -112,66 +117,54 @@ impl TypeCache { } } - /// Infer a call of `fn_idx` from `in_call`, walking it unless this round walked it + /// Infer a call of `fn_idx` from `call_site`, walking it unless this round walked it /// already. Gives what it returns fn call( &mut self, fn_idx: usize, - in_call: CallSite, + call_site: CallSite, arg_types: &[Type], fns: &[RegFn], ) -> Type { - let req = InferRequest::new(fn_idx, in_call, arg_types, fns); + let (context, arg_types) = key(fn_idx, call_site, arg_types, fns); - let w = match self.ids.get(&req) { + let w = match self.ids.get(&(context, arg_types.clone())) { Some(&w) => w, None => { - self.ids.insert(req.clone(), self.walks.len()); + self.ids + .insert((context, arg_types.clone()), self.walks.len()); self.walks.push(Walk { - req, - ret: Type::Unknown, + context, + arg_types, reachable: vec![], round: 0, walking: false, }); - self.changes += 1; self.walks.len() - 1 } }; - if self.walks[w].walking || self.is_live(w) { - return self.walks[w].ret.clone(); + if !self.walks[w].walking && !self.is_live(w) { + self.walks[w].walking = true; + let reachable = walk::walk_fn(w, fns, self); + + let walk = &mut self.walks[w]; + walk.walking = false; + walk.round = self.round; + walk.reachable = reachable; } - self.walks[w].walking = true; - let (ret, reachable) = walk::walk_fn(w, fns, self); - - let walk = &mut self.walks[w]; - let joined = types::join(&walk.ret, &ret); - - if joined != walk.ret { - walk.ret = joined; - self.changes += 1; - } - - walk.walking = false; - walk.round = self.round; - walk.reachable = reachable; - walk.ret.clone() + self.ret(w).clone() } } -/// The address of a local, arg or reg of walk `w` of `req`, given the slots of its fn that a -/// closure shares. A binding that a closure shares is named like an object, any other by -/// the walk -fn binding_addr(w: usize, req: &InferRequest, captured: &[Operand], slot: Operand) -> Addr { - match captured.contains(&slot) { - true => Addr::Captured { - fn_idx: req.fn_idx, - slot, - in_call: req.in_call, - }, - false => Addr::Binding { walk: w, slot }, +/// The address of a local, arg or reg of walk `w` of `context`, given the slots of its +/// function that closures share. A shared binding is named by its context, like the closures +/// that share it. Any other binding is named by its walk +fn binding_addr(w: usize, context: Context, shared: &[Operand], slot: Operand) -> Addr { + match shared.contains(&slot) { + true => Addr::Shared { context, slot }, + false => Addr::Private { walk: w, slot }, } } @@ -189,7 +182,6 @@ pub fn infer_types(fns: &[RegFn], bindings: &ModuleBindings) -> TypeCache { ids: HashMap::new(), memory: Memory::default(), round: 0, - changes: 0, }; cache.memory.builtins = Builtins { @@ -215,11 +207,15 @@ pub fn infer_types(fns: &[RegFn], bindings: &ModuleBindings) -> TypeCache { } /// The address of module binding `local` (see `flatten::ModuleBindings`): a local of the -/// entry, which the module fn captures +/// entry, which the module's function shares with its closures pub fn module_binding(entry: usize, local: usize) -> Addr { - Addr::Captured { + let context = Context { fn_idx: entry, + call_site: None, + }; + + Addr::Shared { + context, slot: Operand::Local(local), - in_call: None, } } diff --git a/compiler/src/infer/walk.rs b/compiler/src/infer/walk.rs index 8404c18..4b8828e 100644 --- a/compiler/src/infer/walk.rs +++ b/compiler/src/infer/walk.rs @@ -1,4 +1,5 @@ -//! One walk: the types that one request gives the locals, args and regs of its fn +//! One walk: the types that one context and its arg types give the locals, args and regs of +//! a function //! //! The walk goes over the reachable blocks until nothing changes. Each instr has a rule, //! which gives the type of its result from the types of its operands. The result joins into @@ -6,8 +7,8 @@ //! reached the instr yet use super::{ - InferRequest, TypeCache, binding_addr, - memory::{Addr, InstrRef, ObjAddr}, + TypeCache, binding_addr, + memory::{Addr, Context, InstrRef, ObjAddr}, types::{ BaseType, EMPTY_STRING, LENGTH_FIELD, Shape, Type, decide_pair, is_base, join, join_all, loose_eq, pairs, strict_eq, truthiness, @@ -20,19 +21,24 @@ use crate::{ transform::{FIRST_ARG, THIS_ARG}, }; -/// Walk `w`. Gives what it returns, and the blocks that can run -pub fn walk_fn(w: usize, fns: &[RegFn], cache: &mut TypeCache) -> (Type, Vec) { - let req = cache.walks[w].req.clone(); - let f = &fns[req.fn_idx]; +/// Walk `w`. Gives the blocks that can run +pub fn walk_fn(w: usize, fns: &[RegFn], cache: &mut TypeCache) -> Vec { + let context = cache.walks[w].context; + let f = &fns[context.fn_idx]; + + // The args get their types from the walk's key + for (i, ty) in cache.walks[w].arg_types.clone().iter().enumerate() { + let addr = binding_addr(w, context, &f.captured_slots(), Operand::Arg(i)); + cache.memory.join(addr, ty); + } let walker = Walker { w, + context, f, fns, - captured: f.captured_slots(), - req, + shared: f.captured_slots(), cache, - ret: Type::Unknown, reachable: vec![false; f.blocks.len()], }; @@ -41,37 +47,28 @@ pub fn walk_fn(w: usize, fns: &[RegFn], cache: &mut TypeCache) -> (Type, Vec { w: usize, - req: InferRequest, + context: Context, f: &'a RegFn, fns: &'a [RegFn], - captured: Vec, // the slots of `f` that a closure shares + shared: Vec, // the slots of `f` that closures share cache: &'a mut TypeCache, - ret: Type, reachable: Vec, } impl Walker<'_> { fn addr(&self, slot: Operand) -> Addr { - binding_addr(self.w, &self.req, &self.captured, slot) + binding_addr(self.w, self.context, &self.shared, slot) } - /// The address of the objects that the instr at `idx` makes in this walk + /// The address of the objects that the instr at `idx` makes, in this context fn here(&self, idx: usize) -> ObjAddr { ObjAddr { - made_at: InstrRef { - fn_idx: self.req.fn_idx, - instr_idx: idx, - }, - in_call: self.req.in_call, + context: self.context, + instr: idx, } } - fn run(mut self) -> (Type, Vec) { - // The args get their values from the request - for (i, ty) in self.req.arg_types.iter().enumerate() { - self.cache.memory.join(self.addr(Operand::Arg(i)), ty); - } - + fn run(mut self) -> Vec { self.reachable[0] = true; // A return only changes once its block is reachable, and a read only gives more @@ -87,7 +84,7 @@ impl Walker<'_> { } if (self.cache.progress(), &self.reachable) == (before.0, &before.1) { - return (self.ret, self.reachable); + return self.reachable; } } } @@ -146,7 +143,7 @@ impl Walker<'_> { let ty = self.rule(instr, idx, &args); if matches!(instr.op, Return | ReturnAsync | ReturnUndef) { - self.ret = join(&self.ret, &ty); + self.cache.memory.join(Addr::Return(self.w), &ty); } for &dst in &instr.dsts { @@ -381,8 +378,8 @@ impl Walker<'_> { /// `new` gives what the constructor returns if that is an object, else the new object /// itself, `this` fn call(&mut self, instr: &RegInstr, idx: usize, args: &[Type]) -> Type { - let in_call = Some(InstrRef { - fn_idx: self.req.fn_idx, + let call_site = Some(InstrRef { + fn_idx: self.context.fn_idx, instr_idx: idx, }); @@ -402,7 +399,7 @@ impl Walker<'_> { let mut call_args = args.to_vec(); call_args[0] = callee.clone(); - self.cache.call(fn_idx, in_call, &call_args, self.fns) + self.cache.call(fn_idx, call_site, &call_args, self.fns) }) .collect(); diff --git a/compiler/src/transform/closure.rs b/compiler/src/transform/closure.rs index fa29e0e..d975dcb 100644 --- a/compiler/src/transform/closure.rs +++ b/compiler/src/transform/closure.rs @@ -67,7 +67,7 @@ //! The owner's code does not change for this: `get_loc k` and `put_loc k` always use the //! current location of `k`. This module keeps `close_loc` as it is. Then: //! - infer can ignore it. The old and new locations of a binding have the same address -//! (`Addr::Captured`), so their types are joined +//! (`Addr::Shared`), so their types are joined //! - `escape.rs` must give each iteration a new location if a closure can see the old one //! after the `close_loc` //!