1use std::cell::RefCell;
6use std::collections::HashMap;
7use std::rc::Rc;
8
9use super::language::*;
10#[cfg(feature = "synfx-dsp-jit")]
11use synfx_dsp_jit::{ASTNode, JITCompileError};
12
13#[derive(Debug)]
14struct JASTNode {
15 id: usize,
16 typ: String,
17 lbl: String,
18 nodes: Vec<(String, String, ASTNodeRef)>,
19}
20
21#[derive(Debug, Clone)]
24pub struct ASTNodeRef(Rc<RefCell<JASTNode>>);
25
26impl BlockASTNode for ASTNodeRef {
27 fn from(id: usize, typ: &str, lbl: &str) -> ASTNodeRef {
28 ASTNodeRef(Rc::new(RefCell::new(JASTNode {
29 id,
30 typ: typ.to_string(),
31 lbl: lbl.to_string(),
32 nodes: vec![],
33 })))
34 }
35
36 fn add_node(&self, in_port: String, out_port: String, node: ASTNodeRef) {
37 self.0.borrow_mut().nodes.push((in_port, out_port, node));
38 }
39}
40
41impl ASTNodeRef {
42 pub fn first_child_ref(&self) -> Option<ASTNodeRef> {
44 self.0.borrow().nodes.get(0).map(|n| n.2.clone())
45 }
46
47 pub fn first_child(&self) -> Option<(String, String, ASTNodeRef)> {
49 self.0.borrow().nodes.get(0).cloned()
50 }
51
52 pub fn nth_child(&self, i: usize) -> Option<(String, String, ASTNodeRef)> {
54 self.0.borrow().nodes.get(i).cloned()
55 }
56
57 pub fn walk_dump(&self, input: &str, output: &str, indent: usize) -> String {
63 let indent_str = " ".repeat(indent + 1);
64
65 let out_port = if output.len() > 0 { format!("(out: {})", output) } else { "".to_string() };
66 let in_port = if input.len() > 0 { format!("(in: {})", input) } else { "".to_string() };
67
68 let mut s = format!(
69 "{}{}#{}[{}] {}{}\n",
70 indent_str,
71 self.0.borrow().id,
72 self.0.borrow().typ,
73 self.0.borrow().lbl,
74 out_port,
75 in_port
76 );
77
78 for (inp, out, n) in &self.0.borrow().nodes {
79 s += &n.walk_dump(&inp, &out, indent + 1);
80 }
81
82 s
83 }
84}
85
86type BlkASTRef = Rc<BlkASTNode>;
87
88#[derive(Debug, Clone)]
89enum BlkASTNode {
90 Area {
91 childs: Vec<BlkASTRef>,
92 },
93 Set {
94 var: String,
95 expr: BlkASTRef,
96 },
97 Get {
98 id: usize,
99 var: String,
100 },
101 Node {
102 id: usize,
103 out: Option<String>,
104 typ: String,
105 lbl: String,
106 childs: Vec<(Option<String>, BlkASTRef)>,
107 },
108 Literal {
109 value: f64,
110 },
111}
112
113impl BlkASTNode {
114 pub fn dump(&self, indent: usize, inp: Option<&str>) -> String {
115 let mut indent_str = " ".repeat(indent + 1);
116
117 if let Some(inp) = inp {
118 indent_str += &format!("{}<= ", inp);
119 } else {
120 indent_str += "<= ";
121 }
122
123 match self {
124 BlkASTNode::Area { childs } => {
125 let mut s = format!("{}Area\n", indent_str);
126 for c in childs.iter() {
127 s += &c.dump(indent + 1, None);
128 }
129 s
130 }
131 BlkASTNode::Set { var, expr } => {
132 format!("{}set '{}'=\n", indent_str, var) + &expr.dump(indent + 1, None)
133 }
134 BlkASTNode::Get { id, var } => {
135 format!("{}get '{}' (id={})\n", indent_str, var, id)
136 }
137 BlkASTNode::Literal { value } => {
138 format!("{}{}\n", indent_str, value)
139 }
140 BlkASTNode::Node { id, out, typ, lbl, childs } => {
141 let lbl = if *typ == *lbl { "".to_string() } else { format!("[{}]", lbl) };
142
143 let mut s = if let Some(out) = out {
144 format!("{}{}{} (id={}/{})\n", indent_str, typ, lbl, id, out)
145 } else {
146 format!("{}{}{} (id={})\n", indent_str, typ, lbl, id)
147 };
148 for (inp, c) in childs.iter() {
149 s += &format!("{}", c.dump(indent + 1, inp.as_ref().map(|s| &s[..])));
150 }
151 s
152 }
153 }
154 }
155
156 pub fn new_area(childs: Vec<BlkASTRef>) -> BlkASTRef {
157 Rc::new(BlkASTNode::Area { childs })
158 }
159
160 pub fn new_set(var: &str, expr: BlkASTRef) -> BlkASTRef {
161 Rc::new(BlkASTNode::Set { var: var.to_string(), expr })
162 }
163
164 pub fn new_get(id: usize, var: &str) -> BlkASTRef {
165 Rc::new(BlkASTNode::Get { id, var: var.to_string() })
166 }
167
168 pub fn new_literal(val: &str) -> Result<BlkASTRef, BlkJITCompileError> {
169 if let Ok(value) = val.parse::<f64>() {
170 Ok(Rc::new(BlkASTNode::Literal { value }))
171 } else {
172 Err(BlkJITCompileError::BadLiteralNumber(val.to_string()))
173 }
174 }
175
176 pub fn new_node(
177 id: usize,
178 out: Option<String>,
179 typ: &str,
180 lbl: &str,
181 childs: Vec<(Option<String>, BlkASTRef)>,
182 ) -> BlkASTRef {
183 Rc::new(BlkASTNode::Node { id, out, typ: typ.to_string(), lbl: lbl.to_string(), childs })
184 }
185}
186
187#[derive(Debug, Clone)]
188pub enum BlkJITCompileError {
189 UnknownError,
190 NoSynfxDSPJit,
191 BadTree(ASTNodeRef),
192 NoOutputAtIdx(String, usize),
193 ASTMissingOutputLabel(usize),
194 NoTmpVarForOutput(usize, String),
195 BadLiteralNumber(String),
196 NodeWithoutID(String),
197 UnknownType(String),
198 TooManyInputs(String, usize),
199 WrongNumberOfChilds(String, usize, usize),
200 UnassignedInput(String, usize, String),
201 #[cfg(feature = "synfx-dsp-jit")]
202 JITCompileError(JITCompileError),
203}
204
205pub struct Block2JITCompiler {
206 idout_var_map: HashMap<String, String>,
207 lang: Rc<RefCell<BlockLanguage>>,
208 tmpvar_counter: usize,
209}
210
211#[cfg(not(feature = "synfx-dsp-jit"))]
212pub enum ASTNode {
213 NoSynfxDSPJit,
214}
215
216impl Block2JITCompiler {
217 pub fn new(lang: Rc<RefCell<BlockLanguage>>) -> Self {
218 Self { idout_var_map: HashMap::new(), lang, tmpvar_counter: 0 }
219 }
220
221 pub fn next_tmpvar_name(&mut self, extra: &str) -> String {
222 self.tmpvar_counter += 1;
223 format!("_tmp{}_{}_", self.tmpvar_counter, extra)
224 }
225
226 pub fn store_idout_var(&mut self, id: usize, out: &str, v: &str) {
227 self.idout_var_map.insert(format!("{}/{}", id, out), v.to_string());
228 }
229
230 pub fn get_var_for_idout(&self, id: usize, out: &str) -> Option<&str> {
231 self.idout_var_map.get(&format!("{}/{}", id, out)).map(|s| &s[..])
232 }
233
234 fn trans2bjit(
235 &mut self,
236 node: &ASTNodeRef,
237 my_out: Option<String>,
238 ) -> Result<BlkASTRef, BlkJITCompileError> {
239 let id = node.0.borrow().id;
240
241 if let Some(out) = &my_out {
242 if let Some(tmpvar) = self.get_var_for_idout(id, out) {
243 return Ok(BlkASTNode::new_get(0, tmpvar));
244 }
245 } else if let Some(tmpvar) = self.get_var_for_idout(id, "") {
246 return Ok(BlkASTNode::new_get(0, tmpvar));
247 }
248
249 match &node.0.borrow().typ[..] {
250 "<a>" => {
251 let mut childs = vec![];
252
253 let mut i = 0;
254 while let Some((_in, out, child)) = node.nth_child(i) {
255 let out = if out.len() > 0 { Some(out) } else { None };
256 let child = self.trans2bjit(&child, out)?;
257 childs.push(child);
258 i += 1;
259 }
260
261 Ok(BlkASTNode::new_area(childs))
262 }
263 "<r>" => {
267 if let Some((_in, out, first)) = node.first_child() {
268 let out = if out.len() > 0 { Some(out) } else { None };
269 let childs =
270 vec![self.trans2bjit(&first, out)?, BlkASTNode::new_get(0, "_res_")];
271 Ok(BlkASTNode::new_area(childs))
272 } else {
273 Err(BlkJITCompileError::BadTree(node.clone()))
274 }
275 }
276 "->" => {
277 if let Some((_in, out, first)) = node.first_child() {
278 let out = if out.len() > 0 { Some(out) } else { None };
279 self.trans2bjit(&first, out)
280 } else {
281 Err(BlkJITCompileError::BadTree(node.clone()))
282 }
283 }
284 "value" => Ok(BlkASTNode::new_literal(&node.0.borrow().lbl)?),
285 "set" | "<res>" => {
286 if let Some((_in, out, first)) = node.first_child() {
287 let out = if out.len() > 0 { Some(out) } else { None };
288 let expr = self.trans2bjit(&first, out)?;
289 if &node.0.borrow().typ[..] == "<res>" {
290 Ok(BlkASTNode::new_set("_res_", expr))
291 } else {
292 Ok(BlkASTNode::new_set(&node.0.borrow().lbl, expr))
293 }
294 } else {
295 Err(BlkJITCompileError::BadTree(node.clone()))
296 }
297 }
298 "get" => Ok(BlkASTNode::new_get(id, &node.0.borrow().lbl)),
299 "->2" | "->3" => {
300 if let Some((_in, out, first)) = node.first_child() {
301 let out = if out.len() > 0 { Some(out) } else { None };
302 let mut area = vec![];
303 let tmp_var = self.next_tmpvar_name("");
304 let expr = self.trans2bjit(&first, out)?;
305 area.push(BlkASTNode::new_set(&tmp_var, expr));
306 area.push(BlkASTNode::new_get(0, &tmp_var));
307 self.store_idout_var(id, "", &tmp_var);
308 Ok(BlkASTNode::new_area(area))
309 } else {
310 Err(BlkJITCompileError::BadTree(node.clone()))
311 }
312 }
313 optype => {
314 let mut childs = vec![];
315
316 let mut i = 0;
317 while let Some((inp, out, child)) = node.nth_child(i) {
318 let out = if out.len() > 0 { Some(out) } else { None };
319
320 let child = self.trans2bjit(&child, out)?;
321 if inp.len() > 0 {
322 childs.push((Some(inp.to_string()), child));
323 } else {
324 childs.push((None, child));
325 }
326 i += 1;
327 }
328
329 let cnt = self.lang.borrow().type_output_count(optype);
333 if cnt > 1 {
334 let mut area = vec![];
335
336 let oname = self.lang.borrow().get_output_name_at_index(optype, 0);
337
338 if let Some(oname) = oname {
339 let tmp_var = self.next_tmpvar_name(&oname);
340
341 area.push(BlkASTNode::new_set(
342 &tmp_var,
343 BlkASTNode::new_node(
344 id,
345 my_out.clone(),
346 &node.0.borrow().typ,
347 &node.0.borrow().lbl,
348 childs,
349 ),
350 ));
351 self.store_idout_var(id, &oname, &tmp_var);
352 } else {
353 return Err(BlkJITCompileError::NoOutputAtIdx(optype.to_string(), 0));
354 }
355
356 for i in 1..cnt {
357 let oname = self.lang.borrow().get_output_name_at_index(optype, i);
358
359 if let Some(oname) = oname {
360 let tmp_var = self.next_tmpvar_name(&oname);
361
362 area.push(BlkASTNode::new_set(
363 &tmp_var,
364 BlkASTNode::new_get(0, &format!("%{}", i)),
365 ));
366
367 self.store_idout_var(id, &oname, &tmp_var);
368 } else {
369 return Err(BlkJITCompileError::NoOutputAtIdx(optype.to_string(), i));
370 }
371 }
372
373 if let Some(out) = &my_out {
374 if let Some(tmpvar) = self.get_var_for_idout(id, out) {
375 area.push(BlkASTNode::new_get(0, tmpvar));
376 } else {
377 return Err(BlkJITCompileError::NoTmpVarForOutput(id, out.to_string()));
378 }
379 } else {
380 return Err(BlkJITCompileError::ASTMissingOutputLabel(id));
381 }
382
383 Ok(BlkASTNode::new_area(area))
384 } else {
385 Ok(BlkASTNode::new_node(
386 id,
387 my_out,
388 &node.0.borrow().typ,
389 &node.0.borrow().lbl,
390 childs,
391 ))
392 }
393 }
394 }
395 }
396
397 #[cfg(feature = "synfx-dsp-jit")]
398 fn bjit2jit(&mut self, ast: &BlkASTRef) -> Result<Box<ASTNode>, BlkJITCompileError> {
399 use synfx_dsp_jit::build::*;
400
401 match &**ast {
402 BlkASTNode::Area { childs } => {
403 let mut stmt = vec![];
404 for c in childs.iter() {
405 stmt.push(self.bjit2jit(&c)?);
406 }
407 Ok(stmts(&stmt[..]))
408 }
409 BlkASTNode::Set { var, expr } => {
410 let e = self.bjit2jit(&expr)?;
411 Ok(assign(var, e))
412 }
413 BlkASTNode::Get { var: varname, .. } => Ok(var(varname)),
414 BlkASTNode::Node { id, typ, childs, .. } => match &typ[..] {
415 "if" => Err(BlkJITCompileError::UnknownError),
416 "zero" => Ok(literal(0.0)),
417 _ => {
418 if *id == 0 {
419 return Err(BlkJITCompileError::NodeWithoutID(typ.to_string()));
420 }
421
422 let lang = self.lang.clone();
423
424 let mut args = vec![];
425
426 if let Some(inputs) = lang.borrow().get_type_inputs(typ) {
427 if childs.len() > inputs.len() {
428 return Err(BlkJITCompileError::TooManyInputs(typ.to_string(), *id));
429 }
430
431 if inputs.len() > 0 && inputs[0] == Some("".to_string()) {
432 if inputs.len() != childs.len() {
433 return Err(BlkJITCompileError::WrongNumberOfChilds(
434 typ.to_string(),
435 *id,
436 childs.len(),
437 ));
438 }
439
440 for (_inp, c) in childs.iter() {
442 args.push(self.bjit2jit(&c)?);
443 }
444 } else {
445 for input_name in inputs.iter() {
447 let mut found = false;
448 for (inp, c) in childs.iter() {
449 println!("FOFOFO '{:?}' = '{:?}'", inp, input_name);
450 if inp == input_name {
451 args.push(self.bjit2jit(&c)?);
452 found = true;
453 break;
454 }
455 }
456
457 if !found {
458 return Err(BlkJITCompileError::UnassignedInput(
459 typ.to_string(),
460 *id,
461 format!("{:?}", input_name),
462 ));
463 }
464 }
465 }
466 } else {
467 return Err(BlkJITCompileError::UnknownType(typ.to_string()));
468 }
469
470 match &typ[..] {
471 "+" | "*" | "-" | "/" => {
472 if args.len() != 2 {
473 return Err(BlkJITCompileError::WrongNumberOfChilds(
474 typ.to_string(),
475 *id,
476 args.len(),
477 ));
478 }
479
480 let a = args.remove(0);
481 let b = args.remove(0);
482
483 match &typ[..] {
484 "+" => Ok(op_add(a, b)),
485 "*" => Ok(op_mul(a, b)),
486 "-" => Ok(op_sub(a, b)),
487 "/" => Ok(op_div(a, b)),
488 _ => Err(BlkJITCompileError::UnknownType(typ.to_string())),
489 }
490 }
491 _ => Ok(call(typ, *id as u64, &args[..])),
492 }
493 }
494 },
495 BlkASTNode::Literal { value } => Ok(literal(*value)),
496 }
497 }
498
499 pub fn compile(&mut self, fun: &BlockFun) -> Result<Box<ASTNode>, BlkJITCompileError> {
500 #[cfg(feature = "synfx-dsp-jit")]
501 {
502 let tree = fun.generate_tree::<ASTNodeRef>("zero").unwrap();
503 println!("{}", tree.walk_dump("", "", 0));
504
505 let blkast = self.trans2bjit(&tree, None)?;
506 println!("R: {}", blkast.dump(0, None));
507
508 self.bjit2jit(&blkast)
509 }
510 #[cfg(not(feature = "synfx-dsp-jit"))]
511 {
512 Err(BlkJITCompileError::NoSynfxDSPJit)
513 }
514 }
515}
516
517#[cfg(feature = "synfx-dsp-jit")]
518#[cfg(test)]
519mod test {
520 use super::*;
521
522 macro_rules! assert_float_eq {
523 ($a:expr, $b:expr) => {
524 if ($a - $b).abs() > 0.0001 {
525 panic!(
526 r#"assertion failed: `(left == right)`
527 left: `{:?}`,
528 right: `{:?}`"#,
529 $a, $b
530 )
531 }
532 };
533 }
534
535 fn put_n(bf: &mut BlockFun, a: usize, x: i64, y: i64, s: &str) {
536 bf.instanciate_at(a, x, y, s, None).expect("no put error");
537 }
538
539 fn put_v(bf: &mut BlockFun, a: usize, x: i64, y: i64, s: &str, v: &str) {
540 bf.instanciate_at(a, x, y, s, Some(v.to_string())).expect("no put error");
541 }
542
543 use synfx_dsp_jit::{get_standard_library, ASTFun, DSPFunction, DSPNodeContext, JIT};
544
545 fn new_jit_fun<F: FnMut(&mut BlockFun)>(
546 mut f: F,
547 ) -> (Rc<RefCell<DSPNodeContext>>, Box<DSPFunction>) {
548 let lib = get_standard_library();
549 let lang = crate::wblockdsp::setup_hxdsp_block_language(lib.clone());
550 let mut bf = BlockFun::new(lang.clone());
551
552 f(&mut bf);
553
554 let mut compiler = Block2JITCompiler::new(bf.block_language());
555 let ast = compiler.compile(&bf).expect("blk2jit compiles");
556 let ctx = DSPNodeContext::new_ref();
557 let jit = JIT::new(lib, ctx.clone());
558 let mut fun = jit.compile(ASTFun::new(ast)).expect("jit compiles");
559
560 fun.init(44100.0, None);
561
562 (ctx, fun)
563 }
564
565 #[test]
566 fn check_blocklang_sig1() {
567 let (ctx, mut fun) = new_jit_fun(|bf| {
568 put_v(bf, 0, 0, 1, "value", "0.3");
569 put_v(bf, 0, 1, 1, "set", "&sig1");
570 put_v(bf, 0, 0, 2, "value", "-0.3");
571 put_v(bf, 0, 1, 2, "set", "&sig2");
572 put_v(bf, 0, 0, 3, "value", "-1.3");
573 });
574
575 let (s1, s2, ret) = fun.exec_2in_2out(0.0, 0.0);
576
577 assert_float_eq!(s1, 0.3);
578 assert_float_eq!(s2, -0.3);
579 assert_float_eq!(ret, -1.3);
580
581 ctx.borrow_mut().free();
582 }
583
584 #[test]
585 fn check_blocklang_accum_shift() {
586 let (ctx, mut fun) = new_jit_fun(|bf| {
587 put_n(bf, 0, 1, 1, "accum");
588 bf.shift_port(0, 1, 1, 1, false);
589 put_v(bf, 0, 0, 2, "value", "0.01");
590 put_v(bf, 0, 0, 1, "get", "*reset");
591 });
592
593 fun.exec_2in_2out(0.0, 0.0);
594 fun.exec_2in_2out(0.0, 0.0);
595 fun.exec_2in_2out(0.0, 0.0);
596 let (_s1, _s2, ret) = fun.exec_2in_2out(0.0, 0.0);
597 assert_float_eq!(ret, 0.04);
598
599 let reset_idx = ctx.borrow().get_persistent_variable_index_by_name("*reset").unwrap();
600 fun.access_persistent_var(reset_idx).map(|reset| *reset = 1.0);
601
602 let (_s1, _s2, ret) = fun.exec_2in_2out(0.0, 0.0);
603 assert_float_eq!(ret, 0.0);
604
605 fun.access_persistent_var(reset_idx).map(|reset| *reset = 0.0);
606
607 fun.exec_2in_2out(0.0, 0.0);
608 fun.exec_2in_2out(0.0, 0.0);
609 fun.exec_2in_2out(0.0, 0.0);
610 fun.exec_2in_2out(0.0, 0.0);
611 let (_s1, _s2, ret) = fun.exec_2in_2out(0.0, 0.0);
612 assert_float_eq!(ret, 0.05);
613
614 ctx.borrow_mut().free();
615 }
616
617 #[test]
618 fn check_blocklang_arithmetics() {
619 let (ctx, mut fun) = new_jit_fun(|bf| {
621 put_v(bf, 0, 0, 1, "value", "0.50");
622 put_v(bf, 0, 0, 2, "value", "0.01");
623 put_n(bf, 0, 1, 1, "+");
624 bf.shift_port(0, 1, 1, 1, true);
625 put_v(bf, 0, 1, 3, "value", "2.0");
626 put_n(bf, 0, 2, 2, "*");
627 });
628
629 let (_s1, _s2, ret) = fun.exec_2in_2out(0.0, 0.0);
630 assert_float_eq!(ret, 1.02);
631 ctx.borrow_mut().free();
632
633 let (ctx, mut fun) = new_jit_fun(|bf| {
635 put_v(bf, 0, 0, 1, "value", "0.50");
636 put_v(bf, 0, 0, 2, "value", "0.01");
637 put_n(bf, 0, 1, 1, "-");
638 bf.shift_port(0, 1, 1, 1, true);
639 put_v(bf, 0, 1, 3, "value", "2.0");
640 put_n(bf, 0, 2, 2, "/");
641 });
642
643 let (_s1, _s2, ret) = fun.exec_2in_2out(0.0, 0.0);
644 assert_float_eq!(ret, (0.5 - 0.01) / 2.0);
645 ctx.borrow_mut().free();
646
647 let (ctx, mut fun) = new_jit_fun(|bf| {
649 put_v(bf, 0, 0, 1, "value", "0.50");
650 put_v(bf, 0, 0, 2, "value", "0.01");
651 put_n(bf, 0, 1, 1, "-");
652 bf.shift_port(0, 1, 1, 1, false);
653 });
654
655 let (_s1, _s2, ret) = fun.exec_2in_2out(0.0, 0.0);
656 assert_float_eq!(ret, 0.01 - 0.5);
657 ctx.borrow_mut().free();
658
659 let (ctx, mut fun) = new_jit_fun(|bf| {
661 put_v(bf, 0, 0, 1, "value", "0.50");
662 put_v(bf, 0, 0, 2, "value", "0.01");
663 put_n(bf, 0, 1, 1, "/");
664 bf.shift_port(0, 1, 1, 1, false);
665 });
666
667 let (_s1, _s2, ret) = fun.exec_2in_2out(0.0, 0.0);
668 assert_float_eq!(ret, 0.01 / 0.5);
669 ctx.borrow_mut().free();
670
671 let (ctx, mut fun) = new_jit_fun(|bf| {
673 put_v(bf, 0, 0, 1, "value", "0.50");
674 put_v(bf, 0, 0, 2, "value", "0.0");
675 put_n(bf, 0, 1, 1, "/");
676 });
677
678 let (_s1, _s2, ret) = fun.exec_2in_2out(0.0, 0.0);
679 assert_float_eq!(ret, 0.5 / 0.0);
680 ctx.borrow_mut().free();
681 }
682
683 #[test]
684 fn check_blocklang_divrem() {
685 let (ctx, mut fun) = new_jit_fun(|bf| {
687 put_v(bf, 0, 0, 1, "value", "0.3");
688 put_v(bf, 0, 0, 2, "value", "-0.4");
689 put_n(bf, 0, 1, 1, "/%");
690 put_v(bf, 0, 2, 2, "set", "&sig1");
691 });
692
693 let (s1, _, ret) = fun.exec_2in_2out(0.0, 0.0);
694
695 assert_float_eq!(s1, 0.3);
696 assert_float_eq!(ret, -0.75);
697 ctx.borrow_mut().free();
698
699 let (ctx, mut fun) = new_jit_fun(|bf| {
701 put_v(bf, 0, 0, 1, "value", "0.3");
702 put_v(bf, 0, 0, 2, "value", "-0.4");
703 put_n(bf, 0, 1, 1, "/%");
704 put_v(bf, 0, 2, 1, "set", "&sig1");
705 });
706
707 let (s1, _, ret) = fun.exec_2in_2out(0.0, 0.0);
708
709 assert_float_eq!(ret, 0.3);
710 assert_float_eq!(s1, -0.75);
711 ctx.borrow_mut().free();
712
713 let (ctx, mut fun) = new_jit_fun(|bf| {
715 put_v(bf, 0, 0, 1, "value", "0.3");
716 put_v(bf, 0, 0, 2, "value", "-0.4");
717 put_n(bf, 0, 1, 1, "/%");
718 put_v(bf, 0, 2, 2, "set", "&sig1");
719 bf.shift_port(0, 1, 1, 0, true);
720 });
721
722 let (s1, _, ret) = fun.exec_2in_2out(0.0, 0.0);
723
724 assert_float_eq!(ret, 0.3);
725 assert_float_eq!(s1, -0.75);
726 ctx.borrow_mut().free();
727
728 let (ctx, mut fun) = new_jit_fun(|bf| {
730 put_v(bf, 0, 0, 1, "value", "0.3");
731 put_v(bf, 0, 0, 2, "value", "-0.4");
732 put_n(bf, 0, 1, 1, "/%");
733 put_v(bf, 0, 2, 1, "set", "&sig1");
734 bf.shift_port(0, 1, 1, 0, true);
735 });
736
737 let (s1, _, ret) = fun.exec_2in_2out(0.0, 0.0);
738
739 assert_float_eq!(s1, 0.3);
740 assert_float_eq!(ret, -0.75);
741 ctx.borrow_mut().free();
742
743 let (ctx, mut fun) = new_jit_fun(|bf| {
745 put_v(bf, 0, 0, 1, "value", "0.3");
746 put_v(bf, 0, 0, 2, "value", "-0.4");
747 put_n(bf, 0, 1, 1, "/%");
748 put_v(bf, 0, 2, 1, "set", "&sig1");
749 bf.shift_port(0, 1, 1, 0, false);
750 });
751
752 let (s1, _, ret) = fun.exec_2in_2out(0.0, 0.0);
753
754 assert_float_eq!(s1, -1.33333);
755 assert_float_eq!(ret, -0.1);
756 ctx.borrow_mut().free();
757
758 let (ctx, mut fun) = new_jit_fun(|bf| {
760 put_v(bf, 0, 0, 1, "value", "0.3");
761 put_v(bf, 0, 0, 2, "value", "-0.4");
762 put_n(bf, 0, 1, 1, "/%");
763 put_v(bf, 0, 2, 1, "set", "&sig1");
764 bf.shift_port(0, 1, 1, 0, false);
765 bf.shift_port(0, 1, 1, 0, true);
766 });
767
768 let (s1, _, ret) = fun.exec_2in_2out(0.0, 0.0);
769
770 assert_float_eq!(ret, -1.33333);
771 assert_float_eq!(s1, -0.1);
772 ctx.borrow_mut().free();
773 }
774}