Wrote bjit to jit translator and got JIT code running in the Code node already

This commit is contained in:
Weird Constructor 2022-08-01 03:56:38 +02:00
parent bf79157f8a
commit c0dad77577
5 changed files with 109 additions and 73 deletions

View file

@ -72,13 +72,10 @@ impl ASTNodeRef {
}
}
type BlkASTRef = Rc<RefCell<BlkASTNode>>;
type BlkASTRef = Rc<BlkASTNode>;
#[derive(Debug, Clone)]
enum BlkASTNode {
Root {
child: BlkASTRef,
},
Area {
childs: Vec<BlkASTRef>,
},
@ -88,13 +85,11 @@ enum BlkASTNode {
},
Get {
id: usize,
use_count: usize,
var: String,
},
Node {
id: usize,
out: Option<String>,
use_count: usize,
typ: String,
lbl: String,
childs: Vec<(Option<String>, BlkASTRef)>,
@ -115,60 +110,53 @@ impl BlkASTNode {
}
match self {
BlkASTNode::Root { child } => {
format!("{}Root\n", indent_str) + &child.borrow().dump(indent + 1, None)
}
BlkASTNode::Area { childs } => {
let mut s = format!("{}Area\n", indent_str);
for c in childs.iter() {
s += &c.borrow().dump(indent + 1, None);
s += &c.dump(indent + 1, None);
}
s
}
BlkASTNode::Set { var, expr } => {
format!("{}set '{}'=\n", indent_str, var) + &expr.borrow().dump(indent + 1, None)
format!("{}set '{}'=\n", indent_str, var) + &expr.dump(indent + 1, None)
}
BlkASTNode::Get { id, use_count, var } => {
format!("{}get '{}' (id={}, use={})\n", indent_str, var, id, use_count)
BlkASTNode::Get { id, var } => {
format!("{}get '{}' (id={})\n", indent_str, var, id)
}
BlkASTNode::Literal { value } => {
format!("{}{}\n", indent_str, value)
}
BlkASTNode::Node { id, out, use_count, typ, lbl, childs } => {
BlkASTNode::Node { id, out, typ, lbl, childs } => {
let lbl = if *typ == *lbl { "".to_string() } else { format!("[{}]", lbl) };
let mut s = if let Some(out) = out {
format!("{}{}{} (id={}/{}, use={})\n", indent_str, typ, lbl, id, out, use_count)
format!("{}{}{} (id={}/{})\n", indent_str, typ, lbl, id, out)
} else {
format!("{}{}{} (id={}, use={})\n", indent_str, typ, lbl, id, use_count)
format!("{}{}{} (id={})\n", indent_str, typ, lbl, id)
};
for (inp, c) in childs.iter() {
s += &format!("{}", c.borrow().dump(indent + 1, inp.as_ref().map(|s| &s[..])));
s += &format!("{}", c.dump(indent + 1, inp.as_ref().map(|s| &s[..])));
}
s
}
}
}
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 }))
Rc::new(BlkASTNode::Area { childs })
}
pub fn new_set(var: &str, expr: BlkASTRef) -> BlkASTRef {
Rc::new(RefCell::new(BlkASTNode::Set { var: var.to_string(), expr }))
Rc::new(BlkASTNode::Set { var: var.to_string(), expr })
}
pub fn new_get(id: usize, var: &str) -> BlkASTRef {
Rc::new(RefCell::new(BlkASTNode::Get { id, var: var.to_string(), use_count: 1 }))
Rc::new(BlkASTNode::Get { id, var: var.to_string() })
}
pub fn new_literal(val: &str) -> Result<BlkASTRef, BlkJITCompileError> {
if let Ok(value) = val.parse::<f64>() {
Ok(Rc::new(RefCell::new(BlkASTNode::Literal { value })))
Ok(Rc::new(BlkASTNode::Literal { value }))
} else {
Err(BlkJITCompileError::BadLiteralNumber(val.to_string()))
}
@ -181,14 +169,13 @@ impl BlkASTNode {
lbl: &str,
childs: Vec<(Option<String>, BlkASTRef)>,
) -> BlkASTRef {
Rc::new(RefCell::new(BlkASTNode::Node {
Rc::new(BlkASTNode::Node {
id,
out,
typ: typ.to_string(),
lbl: lbl.to_string(),
use_count: 1,
childs,
}))
})
}
}
@ -236,26 +223,6 @@ impl Block2JITCompiler {
node: &ASTNodeRef,
my_out: Option<String>,
) -> Result<BlkASTRef, BlkJITCompileError> {
// TODO: Deal with multiple outputs.
// If we encounter a node with multiple outputs, assign each output
// to a temporary variable and save that.
// Store the name of the temporary in a id+output mapping.
// => XXX
// That means: If we have a single output, things are easy, just plug them into
// the JIT ast:
// outer(inner())
// But if we have multiple outputs:
// assign(a = inner())
// assign(b = %1)
// outer_x(a)
// outer_y(b)
// TODO: Filter out -> nodes from the AST
// TODO: For ->2 and ->3, save the input in some variable
// and reserve a id+output variable for this.
// XXX: SSA form of cranelift should take care of the rest!
let id = node.0.borrow().id;
if let Some(out) = &my_out {
@ -394,13 +361,40 @@ impl Block2JITCompiler {
}
}
pub fn compile(&mut self, fun: &BlockFun) -> Result<ASTNode, BlkJITCompileError> {
pub fn bjit2jit(&mut self, ast: &BlkASTRef) -> Result<Box<ASTNode>, BlkJITCompileError> {
use synfx_dsp_jit::build::*;
match &**ast {
BlkASTNode::Area { childs } => {
let mut stmt = vec![];
for c in childs.iter() {
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::Node { id, out, typ, lbl, childs } => {
Err(BlkJITCompileError::UnknownError)
}
BlkASTNode::Literal { value } => {
Ok(literal(*value))
}
}
}
pub fn compile(&mut self, fun: &BlockFun) -> Result<Box<ASTNode>, BlkJITCompileError> {
let tree = fun.generate_tree::<ASTNodeRef>("zero").unwrap();
println!("{}", tree.walk_dump("", "", 0));
let blkast = self.trans2bjit(&tree, None);
println!("R: {}", blkast.unwrap().borrow().dump(0, None));
let blkast = self.trans2bjit(&tree, None)?;
println!("R: {}", blkast.dump(0, None));
Err(BlkJITCompileError::UnknownError)
self.bjit2jit(&blkast)
}
}

View file

@ -110,6 +110,7 @@ impl DspNode for Code {
for frame in 0..ctx.nframes() {
let (s1, s2, ret) = backend.process(0.0, 0.0, 0.0, 0.0, 0.0, 0.0);
println!("OUT: {}", ret);
out.write(frame, ret);
}

View file

@ -11,6 +11,7 @@ use crate::nodes::{NodeConfigurator, NodeGraphOrdering, NodeProg, MAX_ALLOCATED_
pub use crate::CellDir;
use crate::ScopeHandle;
use crate::blocklang::BlockFun;
use crate::block_compiler::BlkJITCompileError;
use std::collections::{HashMap, HashSet};
@ -600,7 +601,7 @@ impl Matrix {
/// Checks the block function for the id `id`. If the block function did change,
/// updates are then sent to the audio thread.
/// See also [get_block_function].
pub fn check_block_function(&mut self, id: usize) {
pub fn check_block_function(&mut self, id: usize) -> Result<(), BlkJITCompileError> {
self.config.check_block_function(id)
}

View file

@ -8,7 +8,7 @@ use super::{
};
use crate::blocklang::*;
use crate::blocklang_def;
use crate::block_compiler::Block2JITCompiler;
use crate::block_compiler::{Block2JITCompiler, BlkJITCompileError};
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};
@ -695,33 +695,36 @@ impl NodeConfigurator {
/// Checks the block function for the id `id`. If the block function did change,
/// updates are then sent to the audio thread.
/// See also [get_block_function].
pub fn check_block_function(&mut self, id: usize) {
pub fn check_block_function(&mut self, id: usize) -> Result<(), BlkJITCompileError> {
if let Some((generation, block_fun)) = self.block_functions.get_mut(id) {
if let Ok(block_fun) = block_fun.lock() {
if *generation != block_fun.generation() {
*generation = block_fun.generation();
let mut compiler = Block2JITCompiler::new(block_fun.block_language());
compiler.compile(&block_fun);
let ast = 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::*;
cod.upload(stmts(&[
assign(
"*phase",
op_add(var("*phase"), op_mul(literal(440.0), var("israte"))),
),
_if(
op_gt(var("*phase"), literal(1.0)),
assign("*phase", op_sub(var("*phase"), literal(1.0))),
None,
),
var("*phase"),
]));
cod.upload(ast);
// stmts(&[
// assign(
// "*phase",
// op_add(var("*phase"), op_mul(literal(440.0), var("israte"))),
// ),
// _if(
// op_gt(var("*phase"), literal(1.0)),
// assign("*phase", op_sub(var("*phase"), literal(1.0))),
// None,
// ),
// var("*phase"),
// ]));
}
}
}
}
Ok(())
}
/// Retrieve a handle to the block function `id`. In case you modify the block function,

View file

@ -5,9 +5,8 @@
mod common;
use common::*;
#[test]
fn check_blocklang_1() {
let (node_conf, mut node_exec) = new_node_engine();
fn setup() -> (Matrix, NodeExecutor) {
let (node_conf, node_exec) = new_node_engine();
let mut matrix = Matrix::new(node_conf, 3, 3);
let mut chain = MatrixCellChain::new(CellDir::B);
@ -20,7 +19,45 @@ fn check_blocklang_1() {
.unwrap();
matrix.sync().unwrap();
let code = NodeId::Code(0);
(matrix, node_exec)
}
#[test]
fn check_blocklang_1() {
let (mut matrix, mut node_exec) = setup();
let block_fun = matrix.get_block_function(0).expect("block fun exists");
{
let mut block_fun = block_fun.lock().expect("matrix lock");
block_fun.instanciate_at(0, 0, 1, "value", Some("0.3".to_string()));
// block_fun.instanciate_at(0, 1, 1, "set", Some("&sig1".to_string()));
}
matrix.check_block_function(0).expect("no compile error");
let res = run_for_ms(&mut node_exec, 25.0);
println!("RES: {:?}", res.1);
assert_decimated_feq!(
res.0,
50,
vec![
0.1,
0.1,
0.1,
0.1,
0.1,
0.1,
0.1,
0.1,
0.1,
0.1,
0.1,
]
);
}
#[test]
fn check_blocklang_2() {
let (mut matrix, mut node_exec) = setup();
let block_fun = matrix.get_block_function(0).expect("block fun exists");
{
@ -55,7 +92,7 @@ fn check_blocklang_1() {
block_fun.instanciate_at(0, 4, 4, "set", Some("*ap".to_string()));
}
matrix.check_block_function(0);
matrix.check_block_function(0).expect("no compile error");
let res = run_for_ms(&mut node_exec, 25.0);
assert_decimated_feq!(