optimized native function calls
This commit is contained in:
parent
9996ca1574
commit
3e497d3ebc
4 changed files with 145 additions and 126 deletions
|
@ -1,8 +1,4 @@
|
|||
[workspace]
|
||||
members = [
|
||||
"talc-lang",
|
||||
"talc-bin",
|
||||
"talc-std",
|
||||
"talc-macros",
|
||||
]
|
||||
members = ["talc-lang", "talc-bin", "talc-std", "talc-macros"]
|
||||
resolver = "2"
|
||||
|
||||
|
|
|
@ -1,11 +1,11 @@
|
|||
use std::{collections::HashMap, sync::{Arc, Mutex, OnceLock}};
|
||||
use std::{collections::HashMap, sync::{Mutex, OnceLock}};
|
||||
|
||||
use lazy_static::lazy_static;
|
||||
|
||||
#[derive(Default)]
|
||||
struct SymbolTable {
|
||||
names: Vec<Arc<str>>,
|
||||
values: HashMap<Arc<str>, Symbol>
|
||||
names: Vec<&'static str>,
|
||||
values: HashMap<&'static str, Symbol>
|
||||
}
|
||||
|
||||
lazy_static! {
|
||||
|
@ -60,7 +60,7 @@ impl Symbol {
|
|||
pub fn get(name: &str) -> Self {
|
||||
let mut table = get_table().lock().expect("couldn't lock symbol table");
|
||||
|
||||
if let Some(sym) = table.values.get(name) {
|
||||
if let Some(sym) = table.values.get(&name) {
|
||||
return *sym
|
||||
}
|
||||
|
||||
|
@ -68,9 +68,9 @@ impl Symbol {
|
|||
assert!(symno <= MAX_SYMBOL, "too many symbols");
|
||||
|
||||
let sym = Symbol(symno as u32);
|
||||
let name: Arc<str> = name.into();
|
||||
let name = String::leak(name.to_owned());
|
||||
|
||||
table.names.push(name.clone());
|
||||
table.names.push(name);
|
||||
table.values.insert(name, sym);
|
||||
|
||||
sym
|
||||
|
@ -99,7 +99,7 @@ impl Symbol {
|
|||
|
||||
/// # Panics
|
||||
/// If the mutex storing the symbol table is poisoned
|
||||
pub fn name(self) -> Arc<str> {
|
||||
pub fn name(self) -> &'static str {
|
||||
let table = get_table().lock().expect("couldn't lock symbol table");
|
||||
table.names.get(self.0 as usize).cloned().expect("symbol does not exist")
|
||||
}
|
||||
|
|
|
@ -404,9 +404,20 @@ impl Vm {
|
|||
if let Value::NativeFunc(nf) = &args[0] {
|
||||
let nf = nf.clone();
|
||||
|
||||
self.call_stack.push(std::mem::take(frame));
|
||||
// safety: frame is restored immediately after
|
||||
// function call ends
|
||||
// ~25% performance improvement in for loop-heavy code
|
||||
unsafe {
|
||||
let f = std::ptr::read(frame);
|
||||
self.call_stack.push(f);
|
||||
}
|
||||
|
||||
let res = (nf.func)(self, args)?;
|
||||
*frame = self.call_stack.pop().expect("no frame to pop");
|
||||
|
||||
unsafe {
|
||||
let f = self.call_stack.pop().expect("no frame to pop");
|
||||
std::ptr::write(frame, f);
|
||||
}
|
||||
|
||||
self.stack.push(res);
|
||||
} else if let Value::Function(func) = &args[0] {
|
||||
|
@ -414,10 +425,10 @@ impl Vm {
|
|||
if self.call_stack.len() + 1 >= self.stack_max {
|
||||
throw!(*SYM_CALL_STACK_OVERFLOW, "call stack overflow")
|
||||
}
|
||||
self.call_stack.push(std::mem::take(frame));
|
||||
|
||||
let func = func.clone();
|
||||
*frame = CallFrame::new(func, args);
|
||||
let new_frame = CallFrame::new(func.clone(), args);
|
||||
let old_frame = std::mem::replace(frame, new_frame);
|
||||
self.call_stack.push(old_frame);
|
||||
} else {
|
||||
unreachable!("already verified by calling get_call_type");
|
||||
}
|
||||
|
|
|
@ -6,12 +6,14 @@ use talc_macros::native_func;
|
|||
use crate::{unpack_args, unpack_varargs};
|
||||
|
||||
pub fn load(vm: &mut Vm) {
|
||||
// begin
|
||||
vm.set_global_name("iter", iter().into());
|
||||
vm.set_global_name("pairs", pairs().into());
|
||||
vm.set_global_name("once", once().into());
|
||||
vm.set_global_name("forever", forever().into());
|
||||
vm.set_global_name("do_forever", forever().into());
|
||||
|
||||
// modify
|
||||
vm.set_global_name("map", map().into());
|
||||
vm.set_global_name("scan", scan().into());
|
||||
vm.set_global_name("tee", tee().into());
|
||||
|
@ -20,15 +22,18 @@ pub fn load(vm: &mut Vm) {
|
|||
vm.set_global_name("take", take().into());
|
||||
vm.set_global_name("skip", skip().into());
|
||||
vm.set_global_name("enumerate", enumerate().into());
|
||||
vm.set_global_name("cycle", cycle().into());
|
||||
vm.set_global_name("step", step().into());
|
||||
vm.set_global_name("rev", rev().into());
|
||||
|
||||
// join
|
||||
vm.set_global_name("zip", zip().into());
|
||||
vm.set_global_name("alternate", alternate().into());
|
||||
vm.set_global_name("intersperse", intersperse().into());
|
||||
vm.set_global_name("cartprod", cartprod().into());
|
||||
vm.set_global_name("cycle", cycle().into());
|
||||
vm.set_global_name("chain", chain().into());
|
||||
vm.set_global_name("step", step().into());
|
||||
vm.set_global_name("rev", rev().into());
|
||||
|
||||
// end
|
||||
vm.set_global_name("list", list().into());
|
||||
vm.set_global_name("table", table().into());
|
||||
vm.set_global_name("len", len().into());
|
||||
|
@ -288,6 +293,118 @@ pub fn enumerate(_: &mut Vm, args: Vec<Value>) -> Result<Value> {
|
|||
Ok(NativeFunc::new(Box::new(f), 0).into())
|
||||
}
|
||||
|
||||
|
||||
#[native_func(1)]
|
||||
pub fn cycle(_: &mut Vm, args: Vec<Value>) -> Result<Value> {
|
||||
let [_, iter] = unpack_args!(args);
|
||||
let iter = iter.to_iter_function()?;
|
||||
|
||||
let record = RefCell::new((Vec::<Rc<RefCell<Value>>>::new(), false, 0));
|
||||
let f = move |vm: &mut Vm, _| {
|
||||
if record.borrow().1 {
|
||||
let mut r = record.borrow_mut();
|
||||
if r.0.is_empty() {
|
||||
return Ok(Value::Nil)
|
||||
}
|
||||
let val = r.0[r.2].clone();
|
||||
r.2 = (r.2 + 1) % r.0.len();
|
||||
return Ok(val.into())
|
||||
}
|
||||
match vmcalliter!(vm; iter.clone())? {
|
||||
Some(v) => {
|
||||
record.borrow_mut().0.push(v.clone());
|
||||
Ok(v.into())
|
||||
},
|
||||
None => {
|
||||
let mut r = record.borrow_mut();
|
||||
r.1 = true;
|
||||
if r.0.is_empty() {
|
||||
Ok(Value::Nil)
|
||||
} else {
|
||||
r.2 = (r.2 + 1) % r.0.len();
|
||||
Ok(r.0[0].clone().into())
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
Ok(NativeFunc::new(Box::new(f), 0).into())
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy, Default)]
|
||||
enum Step {
|
||||
First, Going, #[default] End,
|
||||
}
|
||||
|
||||
#[native_func(2)]
|
||||
pub fn step(_: &mut Vm, args: Vec<Value>) -> Result<Value> {
|
||||
let [_, by, iter] = unpack_args!(args);
|
||||
let iter = iter.to_iter_function()?;
|
||||
let Value::Int(by) = by else {
|
||||
throw!(*SYM_TYPE_ERROR, "step expected integer")
|
||||
};
|
||||
if by <= 0 {
|
||||
throw!(*SYM_TYPE_ERROR, "step expected positive integer")
|
||||
}
|
||||
|
||||
let state = RefCell::new(Step::First);
|
||||
let f = move |vm: &mut Vm, _| match state.take() {
|
||||
Step::First => {
|
||||
match vmcalliter!(vm; iter.clone())? {
|
||||
Some(x) => {
|
||||
*state.borrow_mut() = Step::Going;
|
||||
Ok(x.into())
|
||||
},
|
||||
None => Ok(Value::Nil),
|
||||
}
|
||||
},
|
||||
Step::Going => {
|
||||
for _ in 0..(by-1) {
|
||||
if vmcall!(vm; iter.clone())? == Value::Nil {
|
||||
return Ok(Value::Nil)
|
||||
}
|
||||
}
|
||||
let Some(x) = vmcalliter!(vm; iter.clone())? else {
|
||||
return Ok(Value::Nil)
|
||||
};
|
||||
*state.borrow_mut() = Step::Going;
|
||||
Ok(x.into())
|
||||
},
|
||||
Step::End => Ok(Value::Nil),
|
||||
};
|
||||
Ok(NativeFunc::new(Box::new(f), 0).into())
|
||||
}
|
||||
|
||||
#[native_func(1)]
|
||||
pub fn rev(_: &mut Vm, args: Vec<Value>) -> Result<Value> {
|
||||
let [_, iter] = unpack_args!(args);
|
||||
let iter = iter.to_iter_function()?;
|
||||
|
||||
let lst = RefCell::new(None);
|
||||
let f = move |vm: &mut Vm, _| {
|
||||
if lst.borrow().is_none() {
|
||||
let mut l = Vec::new();
|
||||
while let Some(x) = vmcalliter!(vm; iter.clone())? {
|
||||
l.push(cell_take(x))
|
||||
}
|
||||
l.reverse();
|
||||
*lst.borrow_mut() = Some(l.into_iter());
|
||||
}
|
||||
match lst.borrow_mut().as_mut().unwrap().next() {
|
||||
Some(v) => Ok(v.to_cell()),
|
||||
None => Ok(Value::Nil),
|
||||
}
|
||||
|
||||
};
|
||||
Ok(NativeFunc::new(Box::new(f), 0).into())
|
||||
}
|
||||
|
||||
|
||||
//
|
||||
// join
|
||||
//
|
||||
|
||||
|
||||
#[native_func(2..)]
|
||||
pub fn zip(_: &mut Vm, args: Vec<Value>) -> Result<Value> {
|
||||
let ([_, i1, i2], rest) = unpack_varargs!(args);
|
||||
|
@ -476,111 +593,6 @@ pub fn cartprod(_: &mut Vm, args: Vec<Value>) -> Result<Value> {
|
|||
}
|
||||
|
||||
|
||||
#[native_func(1)]
|
||||
pub fn cycle(_: &mut Vm, args: Vec<Value>) -> Result<Value> {
|
||||
let [_, iter] = unpack_args!(args);
|
||||
let iter = iter.to_iter_function()?;
|
||||
|
||||
let record = RefCell::new((Vec::<Rc<RefCell<Value>>>::new(), false, 0));
|
||||
let f = move |vm: &mut Vm, _| {
|
||||
if record.borrow().1 {
|
||||
let mut r = record.borrow_mut();
|
||||
if r.0.is_empty() {
|
||||
return Ok(Value::Nil)
|
||||
}
|
||||
let val = r.0[r.2].clone();
|
||||
r.2 = (r.2 + 1) % r.0.len();
|
||||
return Ok(val.into())
|
||||
}
|
||||
match vmcalliter!(vm; iter.clone())? {
|
||||
Some(v) => {
|
||||
record.borrow_mut().0.push(v.clone());
|
||||
Ok(v.into())
|
||||
},
|
||||
None => {
|
||||
let mut r = record.borrow_mut();
|
||||
r.1 = true;
|
||||
if r.0.is_empty() {
|
||||
Ok(Value::Nil)
|
||||
} else {
|
||||
r.2 = (r.2 + 1) % r.0.len();
|
||||
Ok(r.0[0].clone().into())
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
Ok(NativeFunc::new(Box::new(f), 0).into())
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy, Default)]
|
||||
enum Step {
|
||||
First, Going, #[default] End,
|
||||
}
|
||||
|
||||
#[native_func(2)]
|
||||
pub fn step(_: &mut Vm, args: Vec<Value>) -> Result<Value> {
|
||||
let [_, by, iter] = unpack_args!(args);
|
||||
let iter = iter.to_iter_function()?;
|
||||
let Value::Int(by) = by else {
|
||||
throw!(*SYM_TYPE_ERROR, "step expected integer")
|
||||
};
|
||||
if by <= 0 {
|
||||
throw!(*SYM_TYPE_ERROR, "step expected positive integer")
|
||||
}
|
||||
|
||||
let state = RefCell::new(Step::First);
|
||||
let f = move |vm: &mut Vm, _| match state.take() {
|
||||
Step::First => {
|
||||
match vmcalliter!(vm; iter.clone())? {
|
||||
Some(x) => {
|
||||
*state.borrow_mut() = Step::Going;
|
||||
Ok(x.into())
|
||||
},
|
||||
None => Ok(Value::Nil),
|
||||
}
|
||||
},
|
||||
Step::Going => {
|
||||
for _ in 0..(by-1) {
|
||||
if vmcall!(vm; iter.clone())? == Value::Nil {
|
||||
return Ok(Value::Nil)
|
||||
}
|
||||
}
|
||||
let Some(x) = vmcalliter!(vm; iter.clone())? else {
|
||||
return Ok(Value::Nil)
|
||||
};
|
||||
*state.borrow_mut() = Step::Going;
|
||||
Ok(x.into())
|
||||
},
|
||||
Step::End => Ok(Value::Nil),
|
||||
};
|
||||
Ok(NativeFunc::new(Box::new(f), 0).into())
|
||||
}
|
||||
|
||||
#[native_func(1)]
|
||||
pub fn rev(_: &mut Vm, args: Vec<Value>) -> Result<Value> {
|
||||
let [_, iter] = unpack_args!(args);
|
||||
let iter = iter.to_iter_function()?;
|
||||
|
||||
let lst = RefCell::new(None);
|
||||
let f = move |vm: &mut Vm, _| {
|
||||
if lst.borrow().is_none() {
|
||||
let mut l = Vec::new();
|
||||
while let Some(x) = vmcalliter!(vm; iter.clone())? {
|
||||
l.push(cell_take(x))
|
||||
}
|
||||
l.reverse();
|
||||
*lst.borrow_mut() = Some(l.into_iter());
|
||||
}
|
||||
match lst.borrow_mut().as_mut().unwrap().next() {
|
||||
Some(v) => Ok(v.to_cell()),
|
||||
None => Ok(Value::Nil),
|
||||
}
|
||||
|
||||
};
|
||||
Ok(NativeFunc::new(Box::new(f), 0).into())
|
||||
}
|
||||
|
||||
|
||||
//
|
||||
// end iteration
|
||||
|
|
Loading…
Reference in a new issue