From f5f8ed545c17aeca547f8b6871462a2ea260789a Mon Sep 17 00:00:00 2001 From: Weird Constructor Date: Sun, 31 Jul 2022 12:18:42 +0200 Subject: [PATCH] working on the block language to JIT compiler --- src/block_compiler.rs | 194 +++++++++++++++++++++++++++++++++ src/blocklang_def.rs | 236 +++++++++++++++++++++++++++++++++++++++++ src/lib.rs | 1 + src/nodes/node_conf.rs | 4 + tests/blocklang.rs | 13 ++- 5 files changed, 446 insertions(+), 2 deletions(-) create mode 100644 src/block_compiler.rs create mode 100644 src/blocklang_def.rs diff --git a/src/block_compiler.rs b/src/block_compiler.rs new file mode 100644 index 0000000..1317b78 --- /dev/null +++ b/src/block_compiler.rs @@ -0,0 +1,194 @@ +// Copyright (c) 2022 Weird Constructor +// 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>); + +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 { + 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>; + +#[derive(Debug, Clone)] +enum BlkASTNode { + Root { child: BlkASTRef }, + Area { childs: Vec }, + 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 }, +} + +impl BlkASTNode { + pub fn new_root(child: BlkASTRef) -> BlkASTRef { + Rc::new(RefCell::new(BlkASTNode::Root { child })) + } + + pub fn new_area(childs: Vec) -> 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 { + 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, +} + +// 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 { + match &node.0.borrow().typ[..] { + "" => { + if let Some(first) = node.first_child_ref() { + let child = self.trans2bjit(&first)?; + Ok(BlkASTNode::new_root(child)) + } else { + Err(BlkJITCompileError::BadTree(node.clone())) + } + } + "" => { + 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)) + } + "" => { + // 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 { + let tree = fun.generate_tree::("zero").unwrap(); + println!("{}", tree.walk_dump("", "", 0)); + + let blkast = self.trans2bjit(&tree); + println!("R: {:#?}", blkast); + + Err(BlkJITCompileError::UnknownError) + } +} diff --git a/src/blocklang_def.rs b/src/blocklang_def.rs new file mode 100644 index 0000000..822826a --- /dev/null +++ b/src/blocklang_def.rs @@ -0,0 +1,236 @@ +// Copyright (c) 2022 Weird Constructor +// 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> { + 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)) +} diff --git a/src/lib.rs b/src/lib.rs index 234960f..6cae805 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -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; diff --git a/src/nodes/node_conf.rs b/src/nodes/node_conf.rs index bd7bc21..f896e10 100644 --- a/src/nodes/node_conf.rs +++ b/src/nodes/node_conf.rs @@ -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::*; diff --git a/tests/blocklang.rs b/tests/blocklang.rs index 6e02cd9..d111384 100644 --- a/tests/blocklang.rs +++ b/tests/blocklang.rs @@ -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);