diff --git a/som-interpreter-bc/src/block.rs b/som-interpreter-bc/src/block.rs index 6adfeb24..448b2679 100644 --- a/som-interpreter-bc/src/block.rs +++ b/som-interpreter-bc/src/block.rs @@ -1,10 +1,13 @@ +use std::cell::RefCell; use std::fmt; +use std::rc::Rc; use som_core::bytecode::Bytecode; use crate::class::Class; use crate::compiler::Literal; use crate::frame::Frame; +use crate::method::Method; use crate::universe::Universe; use crate::value::Value; use crate::SOMRef; @@ -18,6 +21,7 @@ pub struct Block { pub literals: Vec, pub body: Vec, pub nb_params: usize, + pub inline_cache: Rc)>>>>, } impl Block { diff --git a/som-interpreter-bc/src/compiler.rs b/som-interpreter-bc/src/compiler.rs index d2600dc0..c95df8fe 100644 --- a/som-interpreter-bc/src/compiler.rs +++ b/som-interpreter-bc/src/compiler.rs @@ -411,12 +411,17 @@ fn compile_method(outer: &mut dyn GenCtxt, defn: &ast::MethodDef) -> Option MethodKind::NotImplemented(defn.signature.clone()), ast::MethodBody::Body { .. } => { - let env = MethodEnv { - locals: ctxt.inner.locals.iter().map(|_| Value::Nil).collect(), - literals: ctxt.inner.literals.into_iter().collect(), - body: ctxt.inner.body.unwrap_or_default(), - }; - MethodKind::Defined(env) + let body = ctxt.inner.body.unwrap_or_default(); + let locals = ctxt.inner.locals.iter().map(|_| Value::Nil).collect(); + let literals = ctxt.inner.literals.into_iter().collect(); + let inline_cache = RefCell::new(vec![None; body.len()]); + + MethodKind::Defined(MethodEnv { + body, + locals, + literals, + inline_cache, + }) } }, holder: Weak::new(), @@ -449,12 +454,20 @@ fn compile_block(outer: &mut dyn GenCtxt, defn: &ast::Block) -> Option { ctxt.push_instr(Bytecode::ReturnLocal); } + let frame = None; + let locals = ctxt.locals.into_iter().map(|_| Value::Nil).collect(); + let literals = ctxt.literals.into_iter().collect(); + let body = ctxt.body.unwrap_or_default(); + let nb_params = ctxt.args.len(); + let inline_cache = Rc::new(RefCell::new(vec![None; body.len()])); + let block = Block { - frame: None, - locals: ctxt.locals.into_iter().map(|_| Value::Nil).collect(), - literals: ctxt.literals.into_iter().collect(), - body: ctxt.body.unwrap_or_default(), - nb_params: ctxt.args.len(), + frame, + locals, + literals, + body, + nb_params, + inline_cache, }; // println!("(system) compiled block !"); diff --git a/som-interpreter-bc/src/interpreter.rs b/som-interpreter-bc/src/interpreter.rs index 311a511f..e33193be 100644 --- a/som-interpreter-bc/src/interpreter.rs +++ b/som-interpreter-bc/src/interpreter.rs @@ -5,9 +5,11 @@ use std::time::Instant; use som_core::bytecode::Bytecode; use crate::block::Block; +use crate::class::Class; use crate::compiler::Literal; use crate::frame::{Frame, FrameKind}; -use crate::method::MethodKind; +use crate::interner::Interned; +use crate::method::{Method, MethodKind}; use crate::universe::Universe; use crate::value::Value; use crate::SOMRef; @@ -51,6 +53,7 @@ impl Interpreter { None => return Some(self.stack.pop().unwrap_or(Value::Nil)), }; + let bytecode_idx = frame.borrow().bytecode_idx; let opt_bytecode = frame.borrow().get_current_bytecode(); let bytecode = match opt_bytecode { Some(bytecode) => bytecode, @@ -115,12 +118,12 @@ impl Interpreter { Literal::Block(blk) => Block::clone(&blk), _ => return None, }; - block.frame.replace(Rc::clone(frame)); + block.frame.replace(Rc::clone(&frame)); self.stack.push(Value::Block(Rc::new(block))); } Bytecode::PushConstant(idx) => { let literal = frame.borrow().lookup_constant(idx as usize).unwrap(); - let value = convert_literal(frame, literal).unwrap(); + let value = convert_literal(&frame, literal).unwrap(); self.stack.push(value); } Bytecode::PushGlobal(idx) => { @@ -187,142 +190,33 @@ impl Interpreter { } Bytecode::Send(idx) => { let literal = frame.borrow().lookup_constant(idx as usize).unwrap(); - let symbol = match literal { - Literal::Symbol(sym) => sym, - _ => { - return None; - } + let Literal::Symbol(symbol) = literal else { + return None; }; let signature = universe.lookup_symbol(symbol); let nb_params = nb_params(signature); - let method = self - .stack - .iter() - .nth_back(nb_params) - .unwrap() - .lookup_method(universe, symbol); - - if let Some(method) = method { - match method.kind() { - MethodKind::Defined(_) => { - let mut args = Vec::with_capacity(nb_params + 1); - - for _ in 0..nb_params { - let arg = self.stack.pop().unwrap(); - args.push(arg); - } - let self_value = self.stack.pop().unwrap(); - args.push(self_value.clone()); - - args.reverse(); - - let holder = method.holder.upgrade().unwrap(); - self.push_frame(FrameKind::Method { - self_value, - method, - holder, - }); - - let frame = self.current_frame().unwrap(); - frame.borrow_mut().args = args; - } - MethodKind::Primitive(func) => { - func(self, universe); - } - MethodKind::NotImplemented(err) => { - let self_value = self.stack.iter().nth_back(nb_params).unwrap(); - println!( - "{}>>#{}", - self_value.class(&universe).borrow().name(), - method.signature() - ); - panic!("Primitive `#{}` not implemented", err) - } - } - } else { - let mut args = Vec::with_capacity(nb_params + 1); - - for _ in 0..nb_params { - let arg = self.stack.pop().unwrap(); - args.push(arg); - } - let self_value = self.stack.pop().unwrap(); - - args.reverse(); + let method = { + let receiver = self.stack.iter().nth_back(nb_params)?; + let receiver_class = receiver.class(universe); + resolve_method(frame, &receiver_class, symbol, bytecode_idx) + }; - universe.does_not_understand(self, self_value, symbol, args) - .expect( - "A message cannot be handled and `doesNotUnderstand:arguments:` is not defined on receiver" - ); - } + do_send(self, universe, method, symbol, nb_params); } Bytecode::SuperSend(idx) => { let literal = frame.borrow().lookup_constant(idx as usize).unwrap(); - let symbol = match literal { - Literal::Symbol(sym) => sym, - _ => { - return None; - } + let Literal::Symbol(symbol) = literal else { + return None; }; let signature = universe.lookup_symbol(symbol); let nb_params = nb_params(signature); + let method = { + let holder = frame.borrow().get_method_holder(); + let super_class = holder.borrow().super_class()?; + resolve_method(frame, &super_class, symbol, bytecode_idx) + }; - let method = frame - .borrow() - .get_method_holder() - .borrow() - .super_class() - .unwrap() - .borrow() - .lookup_method(symbol); - - if let Some(method) = method { - match method.kind() { - MethodKind::Defined(_) => { - let mut args = Vec::with_capacity(nb_params + 1); - - for _ in 0..nb_params { - let arg = self.stack.pop().unwrap(); - args.push(arg); - } - let self_value = self.stack.pop().unwrap(); - args.push(self_value.clone()); - - args.reverse(); - - let holder = method.holder.upgrade().unwrap(); - self.push_frame(FrameKind::Method { - self_value, - method, - holder, - }); - - let frame = self.current_frame().unwrap(); - frame.borrow_mut().args = args; - } - MethodKind::Primitive(func) => { - func(self, universe); - } - MethodKind::NotImplemented(err) => { - panic!("Primitive `#{}` not implemented", err) - } - } - } else { - let mut args = Vec::with_capacity(nb_params + 1); - - for _ in 0..nb_params { - let arg = self.stack.pop().unwrap(); - args.push(arg); - } - let self_value = self.stack.pop().unwrap(); - - args.reverse(); - - universe.does_not_understand(self, self_value, symbol, args) - .expect( - "A message cannot be handled and `doesNotUnderstand:arguments:` is not defined on receiver" - ); - } + do_send(self, universe, method, symbol, nb_params); } Bytecode::ReturnLocal => { let value = self.stack.pop().unwrap(); @@ -363,6 +257,126 @@ impl Interpreter { } } + fn do_send( + interpreter: &mut Interpreter, + universe: &mut Universe, + method: Option>, + symbol: Interned, + nb_params: usize, + ) { + let Some(method) = method else { + let mut args = Vec::with_capacity(nb_params + 1); + + for _ in 0..nb_params { + let arg = interpreter.stack.pop().unwrap(); + args.push(arg); + } + let self_value = interpreter.stack.pop().unwrap(); + + args.reverse(); + + universe.does_not_understand(interpreter, self_value, symbol, args) + .expect( + "A message cannot be handled and `doesNotUnderstand:arguments:` is not defined on receiver" + ); + + return; + }; + + match method.kind() { + MethodKind::Defined(_) => { + let mut args = Vec::with_capacity(nb_params + 1); + + for _ in 0..nb_params { + let arg = interpreter.stack.pop().unwrap(); + args.push(arg); + } + let self_value = interpreter.stack.pop().unwrap(); + args.push(self_value.clone()); + + args.reverse(); + + let holder = method.holder.upgrade().unwrap(); + let frame = interpreter.push_frame(FrameKind::Method { + self_value, + method, + holder, + }); + frame.borrow_mut().args = args; + } + MethodKind::Primitive(func) => { + func(interpreter, universe); + } + MethodKind::NotImplemented(err) => { + let self_value = interpreter.stack.iter().nth_back(nb_params).unwrap(); + println!( + "{}>>#{}", + self_value.class(&universe).borrow().name(), + method.signature(), + ); + panic!("Primitive `#{}` not implemented", err) + } + } + } + + fn resolve_method( + frame: &SOMRef, + class: &SOMRef, + signature: Interned, + bytecode_idx: usize, + ) -> Option> { + match frame.borrow().kind() { + FrameKind::Block { block } => { + let mut inline_cache = block.inline_cache.borrow_mut(); + + // SAFETY: this access is actually safe because the bytecode compiler + // makes sure the cache has as many entries as there are bytecode instructions, + // therefore we can avoid doing any redundant bounds checks here. + let maybe_found = unsafe { inline_cache.get_unchecked_mut(bytecode_idx) }; + + match maybe_found { + Some((receiver, method)) if *receiver == class.as_ptr() => { + Some(Rc::clone(method)) + } + place @ None => { + let found = class.borrow().lookup_method(signature); + *place = found + .clone() + .map(|method| (class.as_ptr() as *const _, method)); + found + } + _ => class.borrow().lookup_method(signature), + } + } + FrameKind::Method { method, .. } => { + if let MethodKind::Defined(env) = method.kind() { + let mut inline_cache = env.inline_cache.borrow_mut(); + + // SAFETY: this access is actually safe because the bytecode compiler + // makes sure the cache has as many entries as there are bytecode instructions, + // therefore we can avoid doing any redundant bounds checks here. + let maybe_found = unsafe { inline_cache.get_unchecked_mut(bytecode_idx) }; + + match maybe_found { + Some((receiver, method)) if *receiver == class.as_ptr() => { + Some(Rc::clone(method)) + } + place @ None => { + let found = class.borrow().lookup_method(signature); + *place = found + .clone() + .map(|method| (class.as_ptr() as *const _, method)); + found + } + _ => class.borrow().lookup_method(signature), + } + } else { + class.borrow().lookup_method(signature) + } + } + } + } + fn convert_literal(frame: &SOMRef, literal: Literal) -> Option { let value = match literal { Literal::Symbol(sym) => Value::Symbol(sym), diff --git a/som-interpreter-bc/src/method.rs b/som-interpreter-bc/src/method.rs index a54d8e81..db27e064 100644 --- a/som-interpreter-bc/src/method.rs +++ b/som-interpreter-bc/src/method.rs @@ -1,3 +1,4 @@ +use std::cell::RefCell; use std::fmt; use std::rc::Rc; @@ -17,6 +18,7 @@ pub struct MethodEnv { pub locals: Vec, pub literals: Vec, pub body: Vec, + pub inline_cache: RefCell)>>>, } /// The kind of a class method.