Nearly finished the bjit2jit compiler.

This commit is contained in:
Weird Constructor 2022-08-02 05:07:18 +02:00
parent 41b0b3b7fc
commit 4d5a4ef865
6 changed files with 305 additions and 216 deletions

View file

@ -96,7 +96,7 @@ enum BlkASTNode {
},
Literal {
value: f64,
}
},
}
impl BlkASTNode {
@ -169,13 +169,7 @@ impl BlkASTNode {
lbl: &str,
childs: Vec<(Option<String>, BlkASTRef)>,
) -> BlkASTRef {
Rc::new(BlkASTNode::Node {
id,
out,
typ: typ.to_string(),
lbl: lbl.to_string(),
childs,
})
Rc::new(BlkASTNode::Node { id, out, typ: typ.to_string(), lbl: lbl.to_string(), childs })
}
}
@ -187,6 +181,9 @@ pub enum BlkJITCompileError {
ASTMissingOutputLabel(usize),
NoTmpVarForOutput(usize, String),
BadLiteralNumber(String),
NodeWithoutID(String),
UnknownType(String),
TooManyInputs(String, usize),
}
pub struct Block2JITCompiler {
@ -260,9 +257,7 @@ impl Block2JITCompiler {
Err(BlkJITCompileError::BadTree(node.clone()))
}
}
"value" => {
Ok(BlkASTNode::new_literal(&node.0.borrow().lbl)?)
}
"value" => Ok(BlkASTNode::new_literal(&node.0.borrow().lbl)?),
"set" => {
if let Some((_in, out, first)) = node.first_child() {
let out = if out.len() > 0 { Some(out) } else { None };
@ -283,7 +278,6 @@ impl Block2JITCompiler {
area.push(BlkASTNode::new_get(0, &tmp_var));
self.store_idout_var(id, "", &tmp_var);
Ok(BlkASTNode::new_area(area))
} else {
Err(BlkJITCompileError::BadTree(node.clone()))
}
@ -327,11 +321,7 @@ impl Block2JITCompiler {
&tmp_var,
BlkASTNode::new_get(0, &format!("%{}", i)),
));
self.store_idout_var(
id,
&oname,
&tmp_var,
);
self.store_idout_var(id, &oname, &tmp_var);
} else {
return Err(BlkJITCompileError::NoOutputAtIdx(optype.to_string(), i));
}
@ -371,20 +361,44 @@ impl Block2JITCompiler {
stmt.push(self.bjit2jit(&c)?);
}
Ok(stmts(&stmt[..]))
},
}
BlkASTNode::Set { var, expr } => {
let e = self.bjit2jit(&expr)?;
Ok(assign(var, e))
}
BlkASTNode::Get { id, var: varname } => {
Ok(var(varname))
BlkASTNode::Get { id, var: varname } => Ok(var(varname)),
BlkASTNode::Node { id, out, typ, lbl, childs } => match &typ[..] {
"if" => Err(BlkJITCompileError::UnknownError),
"zero" => Ok(literal(0.0)),
node => {
if *id == 0 {
return Err(BlkJITCompileError::NodeWithoutID(typ.to_string()));
}
let lang = self.lang.clone();
let mut args = vec![];
if let Some(inputs) = lang.borrow().get_type_inputs(typ) {
if childs.len() > inputs.len() {
return Err(BlkJITCompileError::TooManyInputs(typ.to_string(), *id));
}
for input_name in inputs.iter() {
for (inp, c) in childs.iter() {
if inp == input_name {
args.push(self.bjit2jit(&c)?);
}
}
}
} else {
return Err(BlkJITCompileError::UnknownType(typ.to_string()));
}
Ok(call(typ, *id as u64, &args[..]))
}
},
BlkASTNode::Node { id, out, typ, lbl, childs } => {
Err(BlkJITCompileError::UnknownError)
}
BlkASTNode::Literal { value } => {
Ok(literal(*value))
}
BlkASTNode::Literal { value } => Ok(literal(*value)),
}
}
@ -416,21 +430,23 @@ mod test {
};
}
use synfx_dsp_jit::{get_standard_library, DSPNodeContext, JIT, ASTFun, DSPFunction};
use synfx_dsp_jit::{get_standard_library, ASTFun, DSPFunction, DSPNodeContext, JIT};
fn new_jit_fun<F: FnMut(&mut BlockFun)>(mut f: F) -> (Rc<RefCell<DSPNodeContext>>, Box<DSPFunction>) {
fn new_jit_fun<F: FnMut(&mut BlockFun)>(
mut f: F,
) -> (Rc<RefCell<DSPNodeContext>>, Box<DSPFunction>) {
use crate::block_compiler::{BlkJITCompileError, Block2JITCompiler};
use crate::blocklang::BlockFun;
use crate::blocklang_def;
let lang = blocklang_def::setup_hxdsp_block_language();
let lib = get_standard_library();
let lang = blocklang_def::setup_hxdsp_block_language(lib.clone());
let mut bf = BlockFun::new(lang.clone());
f(&mut bf);
let mut compiler = Block2JITCompiler::new(bf.block_language());
let ast = compiler.compile(&bf).expect("blk2jit compiles");
let lib = get_standard_library();
let ctx = DSPNodeContext::new_ref();
let jit = JIT::new(lib, ctx.clone());
let mut fun = jit.compile(ASTFun::new(ast)).expect("jit compiles");
@ -440,7 +456,6 @@ mod test {
(ctx, fun)
}
#[test]
fn check_blocklang_sig1() {
let (ctx, mut fun) = new_jit_fun(|bf| {
@ -460,4 +475,36 @@ mod test {
ctx.borrow_mut().free();
}
#[test]
fn check_blocklang_accum_shift() {
let (ctx, mut fun) = new_jit_fun(|bf| {
bf.instanciate_at(0, 1, 1, "accum", None);
bf.shift_port(0, 1, 1, 1, false);
bf.instanciate_at(0, 0, 2, "value", Some("0.01".to_string()));
bf.instanciate_at(0, 0, 1, "get", Some("*reset".to_string()));
});
fun.exec_2in_2out(0.0, 0.0);
fun.exec_2in_2out(0.0, 0.0);
fun.exec_2in_2out(0.0, 0.0);
let (s1, s2, ret) = fun.exec_2in_2out(0.0, 0.0);
assert_float_eq!(ret, 0.04);
let reset_idx = ctx.borrow().get_persistent_variable_index_by_name("*reset").unwrap();
fun.access_persistent_var(reset_idx).map(|reset| *reset = 1.0);
let (s1, s2, ret) = fun.exec_2in_2out(0.0, 0.0);
assert_float_eq!(ret, 0.0);
fun.access_persistent_var(reset_idx).map(|reset| *reset = 0.0);
let (s1, s2, ret) = fun.exec_2in_2out(0.0, 0.0);
let (s1, s2, ret) = fun.exec_2in_2out(0.0, 0.0);
let (s1, s2, ret) = fun.exec_2in_2out(0.0, 0.0);
let (s1, s2, ret) = fun.exec_2in_2out(0.0, 0.0);
let (s1, s2, ret) = fun.exec_2in_2out(0.0, 0.0);
assert_float_eq!(ret, 0.05);
ctx.borrow_mut().free();
}
}

View file

@ -907,15 +907,21 @@ impl BlockLanguage {
Some(&typ.outputs)
}
pub fn get_type_inputs(&self, typ: &str) -> Option<&[Option<String>]> {
let typ = self.types.get(typ)?;
Some(&typ.inputs)
}
pub fn get_output_name_at_index(&self, typ: &str, idx: usize) -> Option<String> {
let outs = self.get_type_outputs(typ)?;
let mut i = 0;
for o in outs.iter() {
if let Some(outname) = o {
if i == idx {
return Some(outname.to_string());
if let Some(outs) = self.get_type_outputs(typ) {
let mut i = 0;
for o in outs.iter() {
if let Some(outname) = o {
if i == idx {
return Some(outname.to_string());
}
i += 1;
}
i += 1;
}
}

View file

@ -2,11 +2,14 @@
// This file is a part of HexoDSP. Released under GPL-3.0-or-later.
// See README.md and COPYING for details.
use crate::blocklang::{BlockLanguage, BlockUserInput, BlockType};
use crate::blocklang::{BlockLanguage, BlockType, BlockUserInput};
use std::cell::RefCell;
use std::rc::Rc;
use synfx_dsp_jit::DSPNodeTypeLibrary;
pub fn setup_hxdsp_block_language() -> Rc<RefCell<BlockLanguage>> {
pub fn setup_hxdsp_block_language(
dsp_lib: Rc<RefCell<DSPNodeTypeLibrary>>,
) -> Rc<RefCell<BlockLanguage>> {
let mut lang = BlockLanguage::new();
lang.define(BlockType {
@ -17,222 +20,248 @@ pub fn setup_hxdsp_block_language() -> Rc<RefCell<BlockLanguage>> {
outputs: vec![Some("".to_string())],
area_count: 0,
user_input: BlockUserInput::None,
description: "A phasor, returns a saw tooth wave to scan through things or use as modulator.".to_string(),
description:
"A phasor, returns a saw tooth wave to scan through things or use as modulator."
.to_string(),
color: 2,
});
lang.define(BlockType {
category: "literals".to_string(),
name: "zero".to_string(),
rows: 1,
inputs: vec![],
outputs: vec![Some("".to_string())],
area_count: 0,
user_input: BlockUserInput::None,
description: "The 0.0 value".to_string(),
color: 1,
category: "literals".to_string(),
name: "zero".to_string(),
rows: 1,
inputs: vec![],
outputs: vec![Some("".to_string())],
area_count: 0,
user_input: BlockUserInput::None,
description: "The 0.0 value".to_string(),
color: 1,
});
lang.define(BlockType {
category: "literals".to_string(),
name: "π".to_string(),
rows: 1,
inputs: vec![],
outputs: vec![Some("".to_string())],
area_count: 0,
user_input: BlockUserInput::None,
description: "The PI number".to_string(),
color: 1,
category: "literals".to_string(),
name: "π".to_string(),
rows: 1,
inputs: vec![],
outputs: vec![Some("".to_string())],
area_count: 0,
user_input: BlockUserInput::None,
description: "The PI number".to_string(),
color: 1,
});
lang.define(BlockType {
category: "literals".to_string(),
name: "".to_string(),
rows: 1,
inputs: vec![],
outputs: vec![Some("".to_string())],
area_count: 0,
user_input: BlockUserInput::None,
description: "2 * PI == TAU".to_string(),
color: 1,
category: "literals".to_string(),
name: "".to_string(),
rows: 1,
inputs: vec![],
outputs: vec![Some("".to_string())],
area_count: 0,
user_input: BlockUserInput::None,
description: "2 * PI == TAU".to_string(),
color: 1,
});
lang.define(BlockType {
category: "literals".to_string(),
name: "SR".to_string(),
rows: 1,
inputs: vec![],
outputs: vec![Some("".to_string())],
area_count: 0,
user_input: BlockUserInput::None,
description: "The sample rate".to_string(),
color: 1,
category: "literals".to_string(),
name: "value".to_string(),
rows: 1,
inputs: vec![],
outputs: vec![Some("".to_string())],
area_count: 0,
user_input: BlockUserInput::Float,
description: "A literal value, typed in by the user.".to_string(),
color: 1,
});
lang.define(BlockType {
category: "literals".to_string(),
name: "value".to_string(),
rows: 1,
inputs: vec![],
outputs: vec![Some("".to_string())],
area_count: 0,
user_input: BlockUserInput::Float,
description: "A literal value, typed in by the user.".to_string(),
color: 1,
category: "routing".to_string(),
name: "->".to_string(),
rows: 1,
inputs: vec![Some("".to_string())],
outputs: vec![Some("".to_string())],
area_count: 0,
user_input: BlockUserInput::None,
description: "Forwards the value one block".to_string(),
color: 6,
});
lang.define(BlockType {
category: "routing".to_string(),
name: "->".to_string(),
rows: 1,
inputs: vec![Some("".to_string())],
outputs: vec![Some("".to_string())],
area_count: 0,
user_input: BlockUserInput::None,
description: "Forwards the value one block".to_string(),
color: 6,
category: "routing".to_string(),
name: "->2".to_string(),
rows: 2,
inputs: vec![Some("".to_string())],
outputs: vec![Some("".to_string()), Some("".to_string())],
area_count: 0,
user_input: BlockUserInput::None,
description: "Forwards the value one block and sends it to multiple destinations"
.to_string(),
color: 6,
});
lang.define(BlockType {
category: "routing".to_string(),
name: "->2".to_string(),
rows: 2,
inputs: vec![Some("".to_string())],
outputs: vec![Some("".to_string()), Some("".to_string())],
area_count: 0,
user_input: BlockUserInput::None,
description: "Forwards the value one block and sends it to multiple destinations".to_string(),
color: 6,
category: "routing".to_string(),
name: "->3".to_string(),
rows: 3,
inputs: vec![Some("".to_string())],
outputs: vec![Some("".to_string()), Some("".to_string()), Some("".to_string())],
area_count: 0,
user_input: BlockUserInput::None,
description: "Forwards the value one block and sends it to multiple destinations"
.to_string(),
color: 6,
});
lang.define(BlockType {
category: "routing".to_string(),
name: "->3".to_string(),
rows: 3,
inputs: vec![Some("".to_string())],
outputs: vec![Some("".to_string()), Some("".to_string()), Some("".to_string())],
area_count: 0,
user_input: BlockUserInput::None,
description: "Forwards the value one block and sends it to multiple destinations".to_string(),
color: 6,
category: "variables".to_string(),
name: "set".to_string(),
rows: 1,
inputs: vec![Some("".to_string())],
outputs: vec![],
area_count: 0,
user_input: BlockUserInput::Identifier,
description: "Stores into a variable".to_string(),
color: 2,
});
lang.define(BlockType {
category: "variables".to_string(),
name: "set".to_string(),
rows: 1,
inputs: vec![Some("".to_string())],
outputs: vec![],
area_count: 0,
user_input: BlockUserInput::Identifier,
description: "Stores into a variable".to_string(),
color: 2,
category: "variables".to_string(),
name: "get".to_string(),
rows: 1,
inputs: vec![],
outputs: vec![Some("".to_string())],
area_count: 0,
user_input: BlockUserInput::Identifier,
description: "Loads a variable".to_string(),
color: 12,
});
lang.define(BlockType {
category: "variables".to_string(),
name: "get".to_string(),
rows: 1,
inputs: vec![],
outputs: vec![Some("".to_string())],
area_count: 0,
user_input: BlockUserInput::Identifier,
description: "Loads a variable".to_string(),
color: 12,
category: "variables".to_string(),
name: "if".to_string(),
rows: 1,
inputs: vec![Some("".to_string())],
outputs: vec![Some("".to_string())],
area_count: 2,
user_input: BlockUserInput::None,
description: "Divides the controlflow based on a true (>= 0.5) \
or false (< 0.5) input value."
.to_string(),
color: 0,
});
lang.define(BlockType {
category: "variables".to_string(),
name: "if".to_string(),
rows: 1,
inputs: vec![Some("".to_string())],
outputs: vec![Some("".to_string())],
area_count: 2,
user_input: BlockUserInput::None,
description: "Divides the controlflow based on a true (>= 0.5) \
or false (< 0.5) input value.".to_string(),
color: 0,
});
// lang.define(BlockType {
// category: "nodes".to_string(),
// name: "1pole".to_string(),
// rows: 2,
// inputs: vec![Some("in".to_string()), Some("f".to_string())],
// outputs: vec![Some("lp".to_string()), Some("hp".to_string())],
// area_count: 0,
// user_input: BlockUserInput::None,
// description: "Runs a simple one pole filter on the input".to_string(),
// color: 8,
// });
//
// lang.define(BlockType {
// category: "nodes".to_string(),
// name: "svf".to_string(),
// rows: 3,
// inputs: vec![Some("in".to_string()), Some("f".to_string()), Some("r".to_string())],
// outputs: vec![Some("lp".to_string()), Some("bp".to_string()), Some("hp".to_string())],
// area_count: 0,
// user_input: BlockUserInput::None,
// description: "Runs a state variable filter on the input".to_string(),
// color: 8,
// });
//
// lang.define(BlockType {
// category: "functions".to_string(),
// name: "sin".to_string(),
// rows: 1,
// inputs: vec![Some("".to_string())],
// outputs: vec![Some("".to_string())],
// area_count: 0,
// user_input: BlockUserInput::None,
// description: "Calculates the sine of the input".to_string(),
// color: 16,
// });
//
// lang.define(BlockType {
// category: "nodes".to_string(),
// name: "delay".to_string(),
// rows: 2,
// inputs: vec![Some("in".to_string()), Some("t".to_string())],
// outputs: vec![Some("".to_string())],
// area_count: 0,
// user_input: BlockUserInput::None,
// description: "Runs a linearly interpolated delay on the input".to_string(),
// color: 8,
// });
lang.define(BlockType {
category: "nodes".to_string(),
name: "1pole".to_string(),
rows: 2,
inputs: vec![Some("in".to_string()), Some("f".to_string())],
outputs: vec![Some("lp".to_string()), Some("hp".to_string())],
area_count: 0,
user_input: BlockUserInput::None,
description: "Runs a simple one pole filter on the input".to_string(),
color: 8,
});
lang.define(BlockType {
category: "nodes".to_string(),
name: "svf".to_string(),
rows: 3,
inputs: vec![Some("in".to_string()), Some("f".to_string()), Some("r".to_string())],
outputs: vec![Some("lp".to_string()), Some("bp".to_string()), Some("hp".to_string())],
area_count: 0,
user_input: BlockUserInput::None,
description: "Runs a state variable filter on the input".to_string(),
color: 8,
});
lang.define(BlockType {
category: "functions".to_string(),
name: "sin".to_string(),
rows: 1,
inputs: vec![Some("".to_string())],
outputs: vec![Some("".to_string())],
area_count: 0,
user_input: BlockUserInput::None,
description: "Calculates the sine of the input".to_string(),
color: 16,
});
lang.define(BlockType {
category: "nodes".to_string(),
name: "delay".to_string(),
rows: 2,
inputs: vec![Some("in".to_string()), Some("t".to_string())],
outputs: vec![Some("".to_string())],
area_count: 0,
user_input: BlockUserInput::None,
description: "Runs a linearly interpolated delay on the input".to_string(),
color: 8,
});
lang.define(BlockType {
category: "arithmetics".to_string(),
name: "/%".to_string(),
rows: 2,
inputs: vec![Some("a".to_string()), Some("b".to_string())],
outputs: vec![Some("div".to_string()), Some("rem".to_string())],
area_count: 0,
user_input: BlockUserInput::None,
description: "Computes the integer division and remainder of a / b".to_string(),
color: 8,
category: "arithmetics".to_string(),
name: "/%".to_string(),
rows: 2,
inputs: vec![Some("a".to_string()), Some("b".to_string())],
outputs: vec![Some("div".to_string()), Some("rem".to_string())],
area_count: 0,
user_input: BlockUserInput::None,
description: "Computes the integer division and remainder of a / b".to_string(),
color: 8,
});
for fun_name in &["+", "-", "*", "/"] {
lang.define(BlockType {
category: "arithmetics".to_string(),
name: fun_name.to_string(),
rows: 2,
inputs:
if fun_name == &"-" || fun_name == &"/" {
vec![Some("a".to_string()), Some("b".to_string())]
} else {
vec![Some("".to_string()), Some("".to_string())]
},
outputs: vec![Some("".to_string())],
area_count: 0,
user_input: BlockUserInput::None,
description: "A binary arithmetics operation".to_string(),
color: 4,
category: "arithmetics".to_string(),
name: fun_name.to_string(),
rows: 2,
inputs: if fun_name == &"-" || fun_name == &"/" {
vec![Some("a".to_string()), Some("b".to_string())]
} else {
vec![Some("".to_string()), Some("".to_string())]
},
outputs: vec![Some("".to_string())],
area_count: 0,
user_input: BlockUserInput::None,
description: "A binary arithmetics operation".to_string(),
color: 4,
});
}
dsp_lib.borrow().for_each(|node_type| -> Result<(), ()> {
let max_ports = node_type.input_count().max(node_type.output_count());
let is_stateful = node_type.is_stateful();
let mut inputs = vec![];
let mut outputs = vec![];
let mut i = 0;
while let Some(name) = node_type.input_names(i) {
inputs.push(Some(name[0..(name.len().min(2))].to_string()));
i += 1;
}
let mut i = 0;
while let Some(name) = node_type.output_names(i) {
outputs.push(Some(name[0..(name.len().min(2))].to_string()));
i += 1;
}
lang.define(BlockType {
category: if is_stateful { "nodes".to_string() } else { "functions".to_string() },
name: node_type.name().to_string(),
rows: max_ports,
area_count: 0,
user_input: BlockUserInput::None,
description: node_type.documentation().to_string(),
color: if is_stateful { 8 } else { 16 },
inputs,
outputs,
});
Ok(())
}).expect("seriously no error here");
lang.define_identifier("in1");
lang.define_identifier("in2");
lang.define_identifier("israte");

View file

@ -281,7 +281,9 @@ impl NodeConfigurator {
let mut scopes = vec![];
scopes.resize_with(MAX_SCOPES, || ScopeHandle::new_shared());
let lang = blocklang_def::setup_hxdsp_block_language();
let code_engines = vec![CodeEngine::new(); MAX_AVAIL_CODE_ENGINES];
let lang = blocklang_def::setup_hxdsp_block_language(code_engines[0].get_lib());
let mut block_functions = vec![];
block_functions.resize_with(MAX_AVAIL_CODE_ENGINES, || {
(0, Arc::new(Mutex::new(BlockFun::new(lang.clone()))))
@ -304,7 +306,7 @@ impl NodeConfigurator {
node2idx: HashMap::new(),
trackers: vec![Tracker::new(); MAX_AVAIL_TRACKERS],
#[cfg(feature = "synfx-dsp-jit")]
code_engines: vec![CodeEngine::new(); MAX_AVAIL_CODE_ENGINES],
code_engines,
block_functions,
scopes,
},

View file

@ -45,6 +45,10 @@ impl CodeEngine {
Self { lib, dsp_ctx: DSPNodeContext::new_ref(), update_prod, return_cons }
}
pub fn get_lib(&self) -> Rc<RefCell<DSPNodeTypeLibrary>> {
self.lib.clone()
}
pub fn upload(&mut self, ast: Box<ASTNode>) -> Result<(), JITCompileError> {
let jit = JIT::new(self.lib.clone(), self.dsp_ctx.clone());
let fun = jit.compile(ASTFun::new(ast))?;

View file

@ -38,3 +38,4 @@ fn check_node_code_1() {
let res = run_for_ms(&mut node_exec, 25.0);
assert_decimated_feq!(res.0, 50, vec![0.3; 10]);
}