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]
|
[workspace]
|
||||||
members = [
|
members = ["talc-lang", "talc-bin", "talc-std", "talc-macros"]
|
||||||
"talc-lang",
|
|
||||||
"talc-bin",
|
|
||||||
"talc-std",
|
|
||||||
"talc-macros",
|
|
||||||
]
|
|
||||||
resolver = "2"
|
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;
|
use lazy_static::lazy_static;
|
||||||
|
|
||||||
#[derive(Default)]
|
#[derive(Default)]
|
||||||
struct SymbolTable {
|
struct SymbolTable {
|
||||||
names: Vec<Arc<str>>,
|
names: Vec<&'static str>,
|
||||||
values: HashMap<Arc<str>, Symbol>
|
values: HashMap<&'static str, Symbol>
|
||||||
}
|
}
|
||||||
|
|
||||||
lazy_static! {
|
lazy_static! {
|
||||||
|
@ -60,7 +60,7 @@ impl Symbol {
|
||||||
pub fn get(name: &str) -> Self {
|
pub fn get(name: &str) -> Self {
|
||||||
let mut table = get_table().lock().expect("couldn't lock symbol table");
|
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
|
return *sym
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -68,9 +68,9 @@ impl Symbol {
|
||||||
assert!(symno <= MAX_SYMBOL, "too many symbols");
|
assert!(symno <= MAX_SYMBOL, "too many symbols");
|
||||||
|
|
||||||
let sym = Symbol(symno as u32);
|
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);
|
table.values.insert(name, sym);
|
||||||
|
|
||||||
sym
|
sym
|
||||||
|
@ -99,7 +99,7 @@ impl Symbol {
|
||||||
|
|
||||||
/// # Panics
|
/// # Panics
|
||||||
/// If the mutex storing the symbol table is poisoned
|
/// 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");
|
let table = get_table().lock().expect("couldn't lock symbol table");
|
||||||
table.names.get(self.0 as usize).cloned().expect("symbol does not exist")
|
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] {
|
if let Value::NativeFunc(nf) = &args[0] {
|
||||||
let nf = nf.clone();
|
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)?;
|
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);
|
self.stack.push(res);
|
||||||
} else if let Value::Function(func) = &args[0] {
|
} else if let Value::Function(func) = &args[0] {
|
||||||
|
@ -414,10 +425,10 @@ impl Vm {
|
||||||
if self.call_stack.len() + 1 >= self.stack_max {
|
if self.call_stack.len() + 1 >= self.stack_max {
|
||||||
throw!(*SYM_CALL_STACK_OVERFLOW, "call stack overflow")
|
throw!(*SYM_CALL_STACK_OVERFLOW, "call stack overflow")
|
||||||
}
|
}
|
||||||
self.call_stack.push(std::mem::take(frame));
|
|
||||||
|
|
||||||
let func = func.clone();
|
let new_frame = CallFrame::new(func.clone(), args);
|
||||||
*frame = CallFrame::new(func, args);
|
let old_frame = std::mem::replace(frame, new_frame);
|
||||||
|
self.call_stack.push(old_frame);
|
||||||
} else {
|
} else {
|
||||||
unreachable!("already verified by calling get_call_type");
|
unreachable!("already verified by calling get_call_type");
|
||||||
}
|
}
|
||||||
|
|
|
@ -6,12 +6,14 @@ use talc_macros::native_func;
|
||||||
use crate::{unpack_args, unpack_varargs};
|
use crate::{unpack_args, unpack_varargs};
|
||||||
|
|
||||||
pub fn load(vm: &mut Vm) {
|
pub fn load(vm: &mut Vm) {
|
||||||
|
// begin
|
||||||
vm.set_global_name("iter", iter().into());
|
vm.set_global_name("iter", iter().into());
|
||||||
vm.set_global_name("pairs", pairs().into());
|
vm.set_global_name("pairs", pairs().into());
|
||||||
vm.set_global_name("once", once().into());
|
vm.set_global_name("once", once().into());
|
||||||
vm.set_global_name("forever", forever().into());
|
vm.set_global_name("forever", forever().into());
|
||||||
vm.set_global_name("do_forever", forever().into());
|
vm.set_global_name("do_forever", forever().into());
|
||||||
|
|
||||||
|
// modify
|
||||||
vm.set_global_name("map", map().into());
|
vm.set_global_name("map", map().into());
|
||||||
vm.set_global_name("scan", scan().into());
|
vm.set_global_name("scan", scan().into());
|
||||||
vm.set_global_name("tee", tee().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("take", take().into());
|
||||||
vm.set_global_name("skip", skip().into());
|
vm.set_global_name("skip", skip().into());
|
||||||
vm.set_global_name("enumerate", enumerate().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("zip", zip().into());
|
||||||
vm.set_global_name("alternate", alternate().into());
|
vm.set_global_name("alternate", alternate().into());
|
||||||
vm.set_global_name("intersperse", intersperse().into());
|
vm.set_global_name("intersperse", intersperse().into());
|
||||||
vm.set_global_name("cartprod", cartprod().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("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("list", list().into());
|
||||||
vm.set_global_name("table", table().into());
|
vm.set_global_name("table", table().into());
|
||||||
vm.set_global_name("len", len().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())
|
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..)]
|
#[native_func(2..)]
|
||||||
pub fn zip(_: &mut Vm, args: Vec<Value>) -> Result<Value> {
|
pub fn zip(_: &mut Vm, args: Vec<Value>) -> Result<Value> {
|
||||||
let ([_, i1, i2], rest) = unpack_varargs!(args);
|
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
|
// end iteration
|
||||||
|
|
Loading…
Reference in a new issue