working on the block language to JIT compiler

This commit is contained in:
Weird Constructor 2022-07-31 12:18:42 +02:00
parent 9b0f93c92b
commit f5f8ed545c
5 changed files with 446 additions and 2 deletions

194
src/block_compiler.rs Normal file
View 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
View 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: "".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))
}

View file

@ -325,6 +325,7 @@ pub mod scope_handle;
pub mod wblockdsp; pub mod wblockdsp;
pub mod blocklang; pub mod blocklang;
pub mod blocklang_def; pub mod blocklang_def;
mod block_compiler;
mod util; mod util;
pub use cell_dir::CellDir; pub use cell_dir::CellDir;

View file

@ -8,6 +8,7 @@ use super::{
}; };
use crate::blocklang::*; use crate::blocklang::*;
use crate::blocklang_def; use crate::blocklang_def;
use crate::block_compiler::Block2JITCompiler;
use crate::dsp::tracker::{PatternData, Tracker}; use crate::dsp::tracker::{PatternData, Tracker};
use crate::dsp::{node_factory, Node, NodeId, NodeInfo, ParamId, SAtom}; use crate::dsp::{node_factory, Node, NodeId, NodeInfo, ParamId, SAtom};
use crate::monitor::{new_monitor_processor, MinMaxMonitorSamples, Monitor, MON_SIG_CNT}; 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 let Ok(block_fun) = block_fun.lock() {
if *generation != block_fun.generation() { if *generation != block_fun.generation() {
*generation = block_fun.generation(); *generation = block_fun.generation();
let mut compiler = Block2JITCompiler::new();
compiler.compile(&block_fun);
// let ast = block_compiler::compile(block_fun); // let ast = block_compiler::compile(block_fun);
if let Some(cod) = self.code_engines.get_mut(id) { if let Some(cod) = self.code_engines.get_mut(id) {
use synfx_dsp_jit::build::*; use synfx_dsp_jit::build::*;

View file

@ -27,9 +27,18 @@ fn check_blocklang_1() {
let mut block_fun = block_fun.lock().expect("matrix lock"); 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, 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, 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); matrix.check_block_function(0);