working on the block language to JIT compiler
This commit is contained in:
parent
9b0f93c92b
commit
f5f8ed545c
5 changed files with 446 additions and 2 deletions
194
src/block_compiler.rs
Normal file
194
src/block_compiler.rs
Normal file
|
@ -0,0 +1,194 @@
|
|||
// Copyright (c) 2022 Weird Constructor <weirdconstructor@gmail.com>
|
||||
// This file is a part of HexoDSP. Released under GPL-3.0-or-later.
|
||||
// See README.md and COPYING for details.
|
||||
|
||||
use std::cell::RefCell;
|
||||
use std::rc::Rc;
|
||||
use std::collections::HashMap;
|
||||
|
||||
use synfx_dsp_jit::ASTNode;
|
||||
use crate::blocklang::*;
|
||||
|
||||
#[derive(Debug)]
|
||||
struct JASTNode {
|
||||
id: usize,
|
||||
typ: String,
|
||||
lbl: String,
|
||||
nodes: Vec<(String, String, ASTNodeRef)>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct ASTNodeRef(Rc<RefCell<JASTNode>>);
|
||||
|
||||
impl BlockASTNode for ASTNodeRef {
|
||||
fn from(id: usize, typ: &str, lbl: &str) -> ASTNodeRef {
|
||||
ASTNodeRef(Rc::new(RefCell::new(JASTNode {
|
||||
id,
|
||||
typ: typ.to_string(),
|
||||
lbl: lbl.to_string(),
|
||||
nodes: vec![],
|
||||
})))
|
||||
}
|
||||
|
||||
fn add_node(&self, in_port: String, out_port: String, node: ASTNodeRef) {
|
||||
self.0.borrow_mut().nodes.push((in_port, out_port, node));
|
||||
}
|
||||
}
|
||||
|
||||
impl ASTNodeRef {
|
||||
pub fn first_child_ref(&self) -> Option<ASTNodeRef> {
|
||||
self.0.borrow().nodes.get(0).map(|n| n.2.clone())
|
||||
}
|
||||
|
||||
pub fn first_child(&self) -> Option<(String, String, ASTNodeRef)> {
|
||||
self.0.borrow().nodes.get(0).cloned()
|
||||
}
|
||||
|
||||
pub fn nth_child(&self, i: usize) -> Option<(String, String, ASTNodeRef)> {
|
||||
self.0.borrow().nodes.get(i).cloned()
|
||||
}
|
||||
|
||||
pub fn walk_dump(&self, input: &str, output: &str, indent: usize) -> String {
|
||||
let indent_str = " ".repeat(indent + 1);
|
||||
|
||||
let out_port =
|
||||
if output.len() > 0 { format!("(out: {})", output) }
|
||||
else { "".to_string() };
|
||||
let in_port =
|
||||
if input.len() > 0 { format!("(in: {})", input) }
|
||||
else { "".to_string() };
|
||||
|
||||
let mut s = format!(
|
||||
"{}{}#{}[{}] {}{}\n",
|
||||
indent_str, self.0.borrow().id, self.0.borrow().typ,
|
||||
self.0.borrow().lbl, out_port, in_port);
|
||||
|
||||
for (inp, out, n) in &self.0.borrow().nodes {
|
||||
s += &n.walk_dump(&inp, &out, indent + 1);
|
||||
}
|
||||
|
||||
s
|
||||
}
|
||||
}
|
||||
|
||||
type BlkASTRef = Rc<RefCell<BlkASTNode>>;
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
enum BlkASTNode {
|
||||
Root { child: BlkASTRef },
|
||||
Area { childs: Vec<BlkASTRef> },
|
||||
Set { var: String, expr: BlkASTRef },
|
||||
Get { id: usize, use_count: usize, var: String, expr: BlkASTRef },
|
||||
Node { id: usize, use_count: usize, typ: String, lbl: String, childs: Vec<BlkASTRef> },
|
||||
}
|
||||
|
||||
impl BlkASTNode {
|
||||
pub fn new_root(child: BlkASTRef) -> BlkASTRef {
|
||||
Rc::new(RefCell::new(BlkASTNode::Root { child }))
|
||||
}
|
||||
|
||||
pub fn new_area(childs: Vec<BlkASTRef>) -> BlkASTRef {
|
||||
Rc::new(RefCell::new(BlkASTNode::Area { childs }))
|
||||
}
|
||||
|
||||
pub fn new_set(var: &str, expr: BlkASTRef) -> BlkASTRef {
|
||||
Rc::new(RefCell::new(BlkASTNode::Set { var: var.to_string(), expr }))
|
||||
}
|
||||
|
||||
pub fn new_node(id: usize, typ: &str, lbl: &str, childs: Vec<BlkASTRef>) -> BlkASTRef {
|
||||
Rc::new(RefCell::new(BlkASTNode::Node { id, typ: typ.to_string(), lbl: lbl.to_string(), use_count: 1, childs }))
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub enum BlkJITCompileError {
|
||||
UnknownError,
|
||||
BadTree(ASTNodeRef),
|
||||
}
|
||||
|
||||
pub struct Block2JITCompiler {
|
||||
id_node_map: HashMap<usize, BlkASTRef>,
|
||||
}
|
||||
|
||||
// 1. compile the weird tree into a graph
|
||||
// - make references where IDs go
|
||||
// - add a use count to each node, so that we know when to make temporary variables
|
||||
|
||||
impl Block2JITCompiler {
|
||||
pub fn new() -> Self {
|
||||
Self {
|
||||
id_node_map: HashMap::new(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn trans2bjit(&self, node: &ASTNodeRef) -> Result<BlkASTRef, BlkJITCompileError> {
|
||||
match &node.0.borrow().typ[..] {
|
||||
"<r>" => {
|
||||
if let Some(first) = node.first_child_ref() {
|
||||
let child = self.trans2bjit(&first)?;
|
||||
Ok(BlkASTNode::new_root(child))
|
||||
} else {
|
||||
Err(BlkJITCompileError::BadTree(node.clone()))
|
||||
}
|
||||
}
|
||||
"<a>" => {
|
||||
let mut childs = vec![];
|
||||
|
||||
let mut i = 0;
|
||||
while let Some((_in, _out, child)) = node.nth_child(i) {
|
||||
let child = self.trans2bjit(&child)?;
|
||||
childs.push(child);
|
||||
i += 1;
|
||||
}
|
||||
|
||||
Ok(BlkASTNode::new_area(childs))
|
||||
}
|
||||
"<res>" => {
|
||||
// TODO: handle results properly, like remembering the most recent result
|
||||
// and append it to the end of the statements block. so that a temporary
|
||||
// variable is created.
|
||||
if let Some(first) = node.first_child_ref() {
|
||||
self.trans2bjit(&first)
|
||||
} else {
|
||||
Err(BlkJITCompileError::BadTree(node.clone()))
|
||||
}
|
||||
}
|
||||
"set" => {
|
||||
if let Some(first) = node.first_child_ref() {
|
||||
let expr = self.trans2bjit(&first)?;
|
||||
Ok(BlkASTNode::new_set(&node.0.borrow().lbl, expr))
|
||||
|
||||
} else {
|
||||
Err(BlkJITCompileError::BadTree(node.clone()))
|
||||
}
|
||||
}
|
||||
optype => {
|
||||
let mut childs = vec![];
|
||||
|
||||
let mut i = 0;
|
||||
while let Some((_in, _out, child)) = node.nth_child(i) {
|
||||
let child = self.trans2bjit(&child)?;
|
||||
childs.push(child);
|
||||
i += 1;
|
||||
}
|
||||
|
||||
Ok(BlkASTNode::new_node(
|
||||
node.0.borrow().id,
|
||||
&node.0.borrow().typ,
|
||||
&node.0.borrow().lbl,
|
||||
childs))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn compile(&self, fun: &BlockFun) -> Result<ASTNode, BlkJITCompileError> {
|
||||
let tree = fun.generate_tree::<ASTNodeRef>("zero").unwrap();
|
||||
println!("{}", tree.walk_dump("", "", 0));
|
||||
|
||||
let blkast = self.trans2bjit(&tree);
|
||||
println!("R: {:#?}", blkast);
|
||||
|
||||
Err(BlkJITCompileError::UnknownError)
|
||||
}
|
||||
}
|
236
src/blocklang_def.rs
Normal file
236
src/blocklang_def.rs
Normal file
|
@ -0,0 +1,236 @@
|
|||
// Copyright (c) 2022 Weird Constructor <weirdconstructor@gmail.com>
|
||||
// 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 std::cell::RefCell;
|
||||
use std::rc::Rc;
|
||||
|
||||
pub fn setup_hxdsp_block_language() -> Rc<RefCell<BlockLanguage>> {
|
||||
let mut lang = BlockLanguage::new();
|
||||
|
||||
lang.define(BlockType {
|
||||
category: "source".to_string(),
|
||||
name: "phse".to_string(),
|
||||
rows: 1,
|
||||
inputs: vec![Some("f".to_string())],
|
||||
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(),
|
||||
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,
|
||||
});
|
||||
|
||||
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,
|
||||
});
|
||||
|
||||
lang.define(BlockType {
|
||||
category: "literals".to_string(),
|
||||
name: "2π".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,
|
||||
});
|
||||
|
||||
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,
|
||||
});
|
||||
|
||||
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,
|
||||
});
|
||||
|
||||
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,
|
||||
});
|
||||
|
||||
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,
|
||||
});
|
||||
|
||||
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,
|
||||
});
|
||||
|
||||
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,
|
||||
});
|
||||
|
||||
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,
|
||||
});
|
||||
|
||||
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,
|
||||
});
|
||||
}
|
||||
|
||||
lang.define_identifier("in1");
|
||||
lang.define_identifier("in2");
|
||||
lang.define_identifier("israte");
|
||||
lang.define_identifier("srate");
|
||||
lang.define_identifier("alpha");
|
||||
lang.define_identifier("beta");
|
||||
lang.define_identifier("delta");
|
||||
lang.define_identifier("gamma");
|
||||
lang.define_identifier("&sig1");
|
||||
lang.define_identifier("&sig2");
|
||||
|
||||
Rc::new(RefCell::new(lang))
|
||||
}
|
|
@ -325,6 +325,7 @@ pub mod scope_handle;
|
|||
pub mod wblockdsp;
|
||||
pub mod blocklang;
|
||||
pub mod blocklang_def;
|
||||
mod block_compiler;
|
||||
mod util;
|
||||
|
||||
pub use cell_dir::CellDir;
|
||||
|
|
|
@ -8,6 +8,7 @@ use super::{
|
|||
};
|
||||
use crate::blocklang::*;
|
||||
use crate::blocklang_def;
|
||||
use crate::block_compiler::Block2JITCompiler;
|
||||
use crate::dsp::tracker::{PatternData, Tracker};
|
||||
use crate::dsp::{node_factory, Node, NodeId, NodeInfo, ParamId, SAtom};
|
||||
use crate::monitor::{new_monitor_processor, MinMaxMonitorSamples, Monitor, MON_SIG_CNT};
|
||||
|
@ -699,6 +700,9 @@ impl NodeConfigurator {
|
|||
if let Ok(block_fun) = block_fun.lock() {
|
||||
if *generation != block_fun.generation() {
|
||||
*generation = block_fun.generation();
|
||||
let mut compiler = Block2JITCompiler::new();
|
||||
compiler.compile(&block_fun);
|
||||
|
||||
// let ast = block_compiler::compile(block_fun);
|
||||
if let Some(cod) = self.code_engines.get_mut(id) {
|
||||
use synfx_dsp_jit::build::*;
|
||||
|
|
|
@ -27,9 +27,18 @@ fn check_blocklang_1() {
|
|||
let mut block_fun = block_fun.lock().expect("matrix lock");
|
||||
|
||||
block_fun.instanciate_at(0, 0, 0, "get", Some("in1".to_string()));
|
||||
block_fun.instanciate_at(0, 0, 1, "get", Some("in1".to_string()));
|
||||
block_fun.instanciate_at(0, 0, 1, "value", Some("0.3".to_string()));
|
||||
block_fun.instanciate_at(0, 1, 0, "+", None);
|
||||
block_fun.instanciate_at(0, 0, 1, "set", Some("&sig1".to_string()));
|
||||
block_fun.instanciate_at(0, 2, 0, "set", Some("&sig1".to_string()));
|
||||
|
||||
block_fun.instanciate_at(0, 3, 0, "get", Some("in1".to_string()));
|
||||
block_fun.instanciate_at(0, 3, 1, "get", Some("in1".to_string()));
|
||||
block_fun.instanciate_at(0, 4, 0, "-", None);
|
||||
block_fun.instanciate_at(0, 5, 0, "->3", None);
|
||||
block_fun.instanciate_at(0, 6, 1, "set", Some("*a".to_string()));
|
||||
block_fun.instanciate_at(0, 6, 2, "set", Some("x".to_string()));
|
||||
block_fun.instanciate_at(0, 6, 0, "->", None);
|
||||
block_fun.instanciate_at(0, 7, 0, "->2", None);
|
||||
}
|
||||
|
||||
matrix.check_block_function(0);
|
||||
|
|
Loading…
Reference in a new issue