use crate::testlang::{TestInstructionSet, TestLang};
use crate::{DialectInstructionSet, IR};
use zhc_utils::{assert_display_is, iter::CollectInVec, svec};
#[test]
fn test_construction() {
let mut store: IR<TestLang> = IR::empty();
let (lhs_id, v0) = store.add_op(TestInstructionSet::IntInput { pos: 0 }, svec![]);
let (rhs_id, v1) = store.add_op(TestInstructionSet::IntInput { pos: 1 }, svec![]);
let (join_id, v2) = store.add_op(TestInstructionSet::Add, svec![v0[0], v1[0]]);
let (split_id, v3) = store.add_op(TestInstructionSet::DivRem, svec![v2[0], v0[0]]);
let (ulhs_id, v4) = store.add_op(TestInstructionSet::Inc, svec![v3[0]]);
let (urhs_id, v5) = store.add_op(TestInstructionSet::Inc, svec![v3[1]]);
let (final_add_id, v6) = store.add_op(TestInstructionSet::Add, svec![v4[0], v5[0]]);
let (effect_id, _) = store.add_op(TestInstructionSet::Return, svec![v3[0]]);
let lhs = store.get_op(lhs_id);
let p0 = store.get_val(v0[0]);
let rhs = store.get_op(rhs_id);
let p1 = store.get_val(v1[0]);
let join = store.get_op(join_id);
let p2 = store.get_val(v2[0]);
let split = store.get_op(split_id);
let p3 = store.get_val(v3[0]);
let p4 = store.get_val(v3[1]);
let ulhs = store.get_op(ulhs_id);
let p5 = store.get_val(v4[0]);
let urhs = store.get_op(urhs_id);
let p6 = store.get_val(v5[0]);
let final_add = store.get_op(final_add_id);
let p7 = store.get_val(v6[0]);
let effect = store.get_op(effect_id);
assert_display_is!(
store.format(),
r#"
%0 : Int = int_input<pos: 0>();
%1 : Int = int_input<pos: 1>();
%2 : Int = add(%0 : Int, %1 : Int);
%3 : Int, %4 : Int = div_rem(%2 : Int, %0 : Int);
%5 : Int = inc(%3 : Int);
%6 : Int = inc(%4 : Int);
return(%3 : Int);
%7 : Int = add(%5 : Int, %6 : Int);
"#
);
assert_eq!(store.n_ops(), 8);
assert_eq!(store.n_vals(), 8);
assert!(lhs.is_active());
assert_eq!(lhs.get_depth(), 0);
assert_eq!(lhs.get_args_iter().covec(), []);
assert_eq!(lhs.get_returns_iter().covec(), [p0.clone()]);
assert!(rhs.is_active());
assert_eq!(rhs.get_depth(), 0);
assert_eq!(rhs.get_args_iter().covec(), []);
assert_eq!(rhs.get_returns_iter().covec(), [p1.clone()]);
assert!(p0.is_active());
assert_eq!(p0.get_origin().opref, lhs);
assert_eq!(p0.get_users_iter().covec(), [join.clone(), split.clone()]);
assert!(p1.is_active());
assert_eq!(p1.get_origin().opref, rhs);
assert_eq!(p1.get_users_iter().covec(), [join.clone()]);
assert!(join.is_active());
assert_eq!(join.get_depth(), 1);
assert_eq!(join.get_args_iter().covec(), [p0.clone(), p1.clone()]);
assert_eq!(join.get_returns_iter().covec(), [p2.clone()]);
assert!(p2.is_active());
assert_eq!(p2.get_origin().opref, join);
assert_eq!(p2.get_users_iter().covec(), [split.clone()]);
assert!(split.is_active());
assert_eq!(split.get_depth(), 2);
assert_eq!(split.get_args_iter().covec(), [p2.clone(), p0.clone()]);
assert_eq!(split.get_returns_iter().covec(), [p3.clone(), p4.clone()]);
assert!(p3.is_active());
assert_eq!(p3.get_origin().opref, split);
assert_eq!(p3.get_users_iter().covec(), [ulhs.clone(), effect.clone()]);
assert!(p4.is_active());
assert_eq!(p4.get_origin().opref, split);
assert_eq!(p4.get_users_iter().covec(), [urhs.clone()]);
assert!(ulhs.is_active());
assert_eq!(ulhs.get_depth(), 3);
assert_eq!(ulhs.get_args_iter().covec(), [p3.clone()]);
assert_eq!(ulhs.get_returns_iter().covec(), [p5.clone()]);
assert!(p5.is_active());
assert_eq!(p5.get_origin().opref, ulhs);
assert_eq!(p5.get_users_iter().covec(), [final_add.clone()]);
assert!(urhs.is_active());
assert_eq!(urhs.get_depth(), 3);
assert_eq!(urhs.get_args_iter().covec(), [p4.clone()]);
assert_eq!(urhs.get_returns_iter().covec(), [p6.clone()]);
assert!(p6.is_active());
assert_eq!(p6.get_origin().opref, urhs);
assert_eq!(p6.get_users_iter().covec(), [final_add.clone()]);
assert!(final_add.is_active());
assert_eq!(final_add.get_depth(), 4);
assert_eq!(final_add.get_args_iter().covec(), [p5.clone(), p6.clone()]);
assert_eq!(final_add.get_returns_iter().covec(), [p7.clone()]);
assert!(p7.is_active());
assert_eq!(p7.get_origin().opref, final_add);
assert_eq!(p7.get_users_iter().covec(), []);
assert!(effect.is_active());
assert_eq!(effect.get_depth(), 3);
assert_eq!(effect.get_args_iter().covec(), [p3.clone()]);
assert_eq!(effect.get_returns_iter().covec(), []);
}
#[test]
fn test_reaches_self() {
let mut store: IR<TestLang> = IR::empty();
let (lhs_id, _) = store.add_op(TestInstructionSet::IntInput { pos: 0 }, svec![]);
let lhs = store.get_op(lhs_id);
assert_display_is!(
store.format(),
r#"
%0 : Int = int_input<pos: 0>();
"#
);
assert!(lhs.reaches(&lhs));
}
#[test]
fn test_reaches_base() {
let mut store: IR<TestLang> = IR::empty();
let (lhs_id, v0) = store.add_op(TestInstructionSet::IntInput { pos: 0 }, svec![]);
let (ulhs_id, _) = store.add_op(TestInstructionSet::Inc, svec![v0[0]]);
let lhs = store.get_op(lhs_id);
let ulhs = store.get_op(ulhs_id);
assert_display_is!(
store.format(),
r#"
%0 : Int = int_input<pos: 0>();
%1 : Int = inc(%0 : Int);
"#
);
assert!(lhs.reaches(&ulhs));
}
#[test]
fn test_reaches_chain() {
let mut store: IR<TestLang> = IR::empty();
let (lhs_id, v0) = store.add_op(TestInstructionSet::IntInput { pos: 0 }, svec![]);
let (_, v1) = store.add_op(TestInstructionSet::Inc, svec![v0[0]]);
let (_, v2) = store.add_op(TestInstructionSet::Inc, svec![v1[0]]);
let (_, v3) = store.add_op(TestInstructionSet::Inc, svec![v2[0]]);
let (_, v4) = store.add_op(TestInstructionSet::Inc, svec![v3[0]]);
let (ulhs_id, _) = store.add_op(TestInstructionSet::Inc, svec![v4[0]]);
let lhs = store.get_op(lhs_id);
let ulhs = store.get_op(ulhs_id);
assert_display_is!(
store.format(),
r#"
%0 : Int = int_input<pos: 0>();
%1 : Int = inc(%0 : Int);
%2 : Int = inc(%1 : Int);
%3 : Int = inc(%2 : Int);
%4 : Int = inc(%3 : Int);
%5 : Int = inc(%4 : Int);
"#
);
assert!(lhs.reaches(&ulhs));
}
#[test]
fn test_reaches_happy_path() {
let mut store: IR<TestLang> = IR::empty();
let (lhs_id, v0) = store.add_op(TestInstructionSet::IntInput { pos: 0 }, svec![]);
let (rhs_id, _) = store.add_op(TestInstructionSet::IntInput { pos: 1 }, svec![]);
let _ = store.add_op(TestInstructionSet::Inc, svec![v0[0]]);
let lhs = store.get_op(lhs_id);
let rhs = store.get_op(rhs_id);
assert_display_is!(
store.format(),
r#"
%0 : Int = int_input<pos: 0>();
%1 : Int = int_input<pos: 1>();
%2 : Int = inc(%0 : Int);
"#
);
assert!(!lhs.reaches(&rhs));
}
#[test]
fn test_get_reaching_iter_simple() {
let mut store: IR<TestLang> = IR::empty();
let (inp1_id, v0) = store.add_op(TestInstructionSet::IntInput { pos: 0 }, svec![]);
let (inp2_id, v1) = store.add_op(TestInstructionSet::IntInput { pos: 1 }, svec![]);
let (add_id, v2) = store.add_op(TestInstructionSet::Add, svec![v0[0], v1[0]]);
let (inc_id, _) = store.add_op(TestInstructionSet::Inc, svec![v2[0]]);
let inc_op = store.get_op(inc_id);
let reaching_ops: Vec<_> = inc_op.get_reaching_iter().map(|op| op.get_id()).collect();
assert_eq!(reaching_ops.len(), 3);
assert!(reaching_ops.contains(&inp1_id));
assert!(reaching_ops.contains(&inp2_id));
assert!(reaching_ops.contains(&add_id));
}
#[test]
fn test_get_reaching_iter_complex() {
let mut store: IR<TestLang> = IR::empty();
let (inp_id, v0) = store.add_op(TestInstructionSet::IntInput { pos: 0 }, svec![]);
let (inc1_id, v1) = store.add_op(TestInstructionSet::Inc, svec![v0[0]]);
let (inc2_id, v2) = store.add_op(TestInstructionSet::Inc, svec![v1[0]]);
let (inc3_id, v3) = store.add_op(TestInstructionSet::Inc, svec![v0[0]]); let (add_id, _) = store.add_op(TestInstructionSet::Add, svec![v2[0], v3[0]]);
let add_op = store.get_op(add_id);
let reaching_ops: Vec<_> = add_op.get_reaching_iter().map(|op| op.get_id()).collect();
assert_eq!(reaching_ops.len(), 4);
assert!(reaching_ops.contains(&inp_id));
assert!(reaching_ops.contains(&inc1_id));
assert!(reaching_ops.contains(&inc2_id));
assert!(reaching_ops.contains(&inc3_id));
}
#[test]
fn test_get_reaching_iter_diamond() {
let mut store: IR<TestLang> = IR::empty();
let (a_id, a_vals) = store.add_op(TestInstructionSet::IntInput { pos: 0 }, svec![]); let (b_id, b_vals) = store.add_op(TestInstructionSet::Inc, svec![a_vals[0]]); let (c_id, c_vals) = store.add_op(TestInstructionSet::Inc, svec![a_vals[0]]); let (d_id, _) = store.add_op(TestInstructionSet::Add, svec![b_vals[0], c_vals[0]]);
let d_op = store.get_op(d_id);
let reaching_ops: Vec<_> = d_op.get_reaching_iter().map(|op| op.get_id()).collect();
assert_eq!(reaching_ops.len(), 3);
assert!(reaching_ops.contains(&a_id));
assert!(reaching_ops.contains(&b_id));
assert!(reaching_ops.contains(&c_id));
assert!(!reaching_ops.contains(&d_id));
}
#[test]
fn test_get_reaching_iter_input() {
let mut store: IR<TestLang> = IR::empty();
let (inp_id, _) = store.add_op(TestInstructionSet::IntInput { pos: 0 }, svec![]);
let inp_op = store.get_op(inp_id);
let reaching_ops: Vec<_> = inp_op.get_reaching_iter().map(|op| op.get_id()).collect();
assert_eq!(reaching_ops.len(), 0);
}
#[test]
fn test_get_reaching_iter_disconnected_subgraphs() {
let mut store: IR<TestLang> = IR::empty();
let (inp1_id, v0) = store.add_op(TestInstructionSet::IntInput { pos: 0 }, svec![]);
let (inc1_id, v1) = store.add_op(TestInstructionSet::Inc, svec![v0[0]]);
let (inc2_id, _v2) = store.add_op(TestInstructionSet::Inc, svec![v1[0]]);
let (inp2_id, v3) = store.add_op(TestInstructionSet::IntInput { pos: 1 }, svec![]);
let (inc3_id, v4) = store.add_op(TestInstructionSet::Inc, svec![v3[0]]);
let (inc4_id, _v5) = store.add_op(TestInstructionSet::Inc, svec![v4[0]]);
let inc2_op = store.get_op(inc2_id);
let inc2_reaching: Vec<_> = inc2_op.get_reaching_iter().map(|op| op.get_id()).collect();
assert_eq!(inc2_reaching.len(), 2); assert!(inc2_reaching.contains(&inp1_id));
assert!(inc2_reaching.contains(&inc1_id));
assert!(!inc2_reaching.contains(&inp2_id));
assert!(!inc2_reaching.contains(&inc3_id));
assert!(!inc2_reaching.contains(&inc4_id));
let inc4_op = store.get_op(inc4_id);
let inc4_reaching: Vec<_> = inc4_op.get_reaching_iter().map(|op| op.get_id()).collect();
assert_eq!(inc4_reaching.len(), 2); assert!(inc4_reaching.contains(&inp2_id));
assert!(inc4_reaching.contains(&inc3_id));
assert!(!inc4_reaching.contains(&inp1_id));
assert!(!inc4_reaching.contains(&inc1_id));
assert!(!inc4_reaching.contains(&inc2_id));
assert!(!inc2_op.reaches(&inc4_op));
assert!(!inc4_op.reaches(&inc2_op));
assert!(!store.get_op(inc1_id).reaches(&store.get_op(inc3_id)));
assert!(!store.get_op(inc3_id).reaches(&store.get_op(inc1_id)));
}
#[test]
fn test_get_reached_iter_simple() {
let mut store: IR<TestLang> = IR::empty();
let (inp1_id, v0) = store.add_op(TestInstructionSet::IntInput { pos: 0 }, svec![]);
let (inp2_id, v1) = store.add_op(TestInstructionSet::IntInput { pos: 1 }, svec![]);
let (add_id, v2) = store.add_op(TestInstructionSet::Add, svec![v0[0], v1[0]]);
let (inc_id, _) = store.add_op(TestInstructionSet::Inc, svec![v2[0]]);
let add_op = store.get_op(add_id);
let reached_ops: Vec<_> = add_op.get_reached_iter().map(|op| op.get_id()).collect();
assert_eq!(reached_ops.len(), 1);
assert!(reached_ops.contains(&inc_id));
assert!(!reached_ops.contains(&inp1_id));
assert!(!reached_ops.contains(&inp2_id));
assert!(!reached_ops.contains(&add_id));
}
#[test]
fn test_get_reached_iter_complex() {
let mut store: IR<TestLang> = IR::empty();
let (inp_id, v0) = store.add_op(TestInstructionSet::IntInput { pos: 0 }, svec![]);
let (inc1_id, v1) = store.add_op(TestInstructionSet::Inc, svec![v0[0]]);
let (inc2_id, v2) = store.add_op(TestInstructionSet::Inc, svec![v1[0]]);
let (inc3_id, v3) = store.add_op(TestInstructionSet::Inc, svec![v0[0]]); let (add_id, _) = store.add_op(TestInstructionSet::Add, svec![v2[0], v3[0]]);
let inp_op = store.get_op(inp_id);
let reached_ops: Vec<_> = inp_op.get_reached_iter().map(|op| op.get_id()).collect();
assert_eq!(reached_ops.len(), 4);
assert!(reached_ops.contains(&inc1_id));
assert!(reached_ops.contains(&inc2_id));
assert!(reached_ops.contains(&inc3_id));
assert!(reached_ops.contains(&add_id));
}
#[test]
fn test_get_reached_iter_diamond() {
let mut store: IR<TestLang> = IR::empty();
let (a_id, a_vals) = store.add_op(TestInstructionSet::IntInput { pos: 0 }, svec![]); let (b_id, b_vals) = store.add_op(TestInstructionSet::Inc, svec![a_vals[0]]); let (c_id, c_vals) = store.add_op(TestInstructionSet::Inc, svec![a_vals[0]]); let (d_id, _) = store.add_op(TestInstructionSet::Add, svec![b_vals[0], c_vals[0]]);
let a_op = store.get_op(a_id);
let reached_ops: Vec<_> = a_op.get_reached_iter().map(|op| op.get_id()).collect();
assert_eq!(reached_ops.len(), 3);
assert!(reached_ops.contains(&b_id));
assert!(reached_ops.contains(&c_id));
assert!(reached_ops.contains(&d_id));
assert!(!reached_ops.contains(&a_id));
}
#[test]
fn test_get_reached_iter_effect() {
let mut store: IR<TestLang> = IR::empty();
let (_, vals) = store.add_op(TestInstructionSet::IntInput { pos: 0 }, svec![]);
let (ret_id, _) = store.add_op(TestInstructionSet::Return, vals);
let ret_op = store.get_op(ret_id);
let reached_ops: Vec<_> = ret_op.get_reached_iter().map(|op| op.get_id()).collect();
assert_eq!(reached_ops.len(), 0);
}
#[test]
fn test_get_reached_iter_disconnected_subgraphs() {
let mut store: IR<TestLang> = IR::empty();
let (inp1_id, v0) = store.add_op(TestInstructionSet::IntInput { pos: 0 }, svec![]);
let (inc1_id, v1) = store.add_op(TestInstructionSet::Inc, svec![v0[0]]);
let (inc2_id, _v2) = store.add_op(TestInstructionSet::Inc, svec![v1[0]]);
let (inp2_id, v3) = store.add_op(TestInstructionSet::IntInput { pos: 1 }, svec![]);
let (inc3_id, v4) = store.add_op(TestInstructionSet::Inc, svec![v3[0]]);
let (inc4_id, _v5) = store.add_op(TestInstructionSet::Inc, svec![v4[0]]);
let inp1_op = store.get_op(inp1_id);
let inp1_reached: Vec<_> = inp1_op.get_reached_iter().map(|op| op.get_id()).collect();
assert_eq!(inp1_reached.len(), 2); assert!(inp1_reached.contains(&inc1_id));
assert!(inp1_reached.contains(&inc2_id));
assert!(!inp1_reached.contains(&inp2_id));
assert!(!inp1_reached.contains(&inc3_id));
assert!(!inp1_reached.contains(&inc4_id));
let inp2_op = store.get_op(inp2_id);
let inp2_reached: Vec<_> = inp2_op.get_reached_iter().map(|op| op.get_id()).collect();
assert_eq!(inp2_reached.len(), 2); assert!(inp2_reached.contains(&inc3_id));
assert!(inp2_reached.contains(&inc4_id));
assert!(!inp2_reached.contains(&inp1_id));
assert!(!inp2_reached.contains(&inc1_id));
assert!(!inp2_reached.contains(&inc2_id));
assert!(!inp1_op.reaches(&store.get_op(inc3_id)));
assert!(!inp2_op.reaches(&store.get_op(inc1_id)));
assert!(!store.get_op(inc1_id).reaches(&store.get_op(inc3_id)));
assert!(!store.get_op(inc3_id).reaches(&store.get_op(inc1_id)));
}
#[test]
fn test_get_reached_iter_branching() {
let mut store: IR<TestLang> = IR::empty();
let (inp_id, vals) = store.add_op(TestInstructionSet::IntInput { pos: 0 }, svec![]);
let (ret1_id, _) = store.add_op(TestInstructionSet::Return, vals.clone());
let (inc_id, inc_vals) = store.add_op(TestInstructionSet::Inc, vals);
let (ret2_id, _) = store.add_op(TestInstructionSet::Return, inc_vals);
let inp_op = store.get_op(inp_id);
let reached_ops: Vec<_> = inp_op.get_reached_iter().map(|op| op.get_id()).collect();
assert_eq!(reached_ops.len(), 3);
assert!(reached_ops.contains(&ret1_id));
assert!(reached_ops.contains(&inc_id));
assert!(reached_ops.contains(&ret2_id));
}
#[test]
fn test_get_reached_iter_convergent() {
let mut store: IR<TestLang> = IR::empty();
let (inp1_id, v0) = store.add_op(TestInstructionSet::IntInput { pos: 0 }, svec![]);
let (inp2_id, v1) = store.add_op(TestInstructionSet::IntInput { pos: 1 }, svec![]);
let (add_id, _) = store.add_op(TestInstructionSet::Add, svec![v0[0], v1[0]]);
let inp1_op = store.get_op(inp1_id);
let inp1_reached: Vec<_> = inp1_op.get_reached_iter().map(|op| op.get_id()).collect();
let inp2_op = store.get_op(inp2_id);
let inp2_reached: Vec<_> = inp2_op.get_reached_iter().map(|op| op.get_id()).collect();
assert_eq!(inp1_reached.len(), 1);
assert_eq!(inp2_reached.len(), 1);
assert!(inp1_reached.contains(&add_id));
assert!(inp2_reached.contains(&add_id));
}
#[test]
#[should_panic]
fn test_delete_op_in_use() {
let mut store: IR<TestLang> = IR::empty();
let (lhs_id, v0) = store.add_op(TestInstructionSet::IntInput { pos: 0 }, svec![]);
let (_, v1) = store.add_op(TestInstructionSet::IntInput { pos: 1 }, svec![]);
let _ = store.add_op(TestInstructionSet::Add, svec![v0[0], v1[0]]);
store.delete_op(lhs_id);
}
#[test]
fn test_delete_op() {
let mut store: IR<TestLang> = IR::empty();
let (_, v0) = store.add_op(TestInstructionSet::IntInput { pos: 0 }, svec![]);
let (rhs_id, v1) = store.add_op(TestInstructionSet::IntInput { pos: 1 }, svec![]);
let (join_id, v2) = store.add_op(TestInstructionSet::Add, svec![v0[0], v1[0]]);
store.delete_op(join_id);
store.delete_op(rhs_id);
assert_display_is!(
store.format(),
r#"
%0 : Int = int_input<pos: 0>();
"#
);
assert!(store.raw_get_val(v2[0]).is_inactive());
assert!(store.raw_get_val(v1[0]).is_inactive());
assert!(store.raw_get_op(join_id).is_inactive());
assert!(store.raw_get_op(rhs_id).is_inactive());
}
#[test]
#[should_panic(expected = "Tried to replace a value with one it reaches.")]
fn test_replace_val_use_wrong() {
let mut store: IR<TestLang> = IR::empty();
let (_, v0) = store.add_op(TestInstructionSet::IntInput { pos: 0 }, svec![]);
let (_, v1) = store.add_op(TestInstructionSet::Inc, svec![v0[0]]);
assert_display_is!(
store.format(),
r#"
%0 : Int = int_input<pos: 0>();
%1 : Int = inc(%0 : Int);
"#
);
store.replace_val_use(v0[0], v1[0]);
}
#[test]
#[should_panic(expected = "Tried to replace a value with one it reaches.")]
fn test_replace_val_use_wrong_longer() {
let mut store: IR<TestLang> = IR::empty();
let (_, v0) = store.add_op(TestInstructionSet::IntInput { pos: 0 }, svec![]);
let (_, v1) = store.add_op(TestInstructionSet::Inc, svec![v0[0]]);
assert_display_is!(
store.format(),
r#"
%0 : Int = int_input<pos: 0>();
%1 : Int = inc(%0 : Int);
"#
);
store.replace_val_use(v0[0], v1[0]);
}
#[test]
fn test_replace_val_use() {
let mut store: IR<TestLang> = IR::empty();
let (_inp1_id, v0) = store.add_op(TestInstructionSet::IntInput { pos: 0 }, svec![]);
let (_inp2_id, v1) = store.add_op(TestInstructionSet::IntInput { pos: 1 }, svec![]);
let (inc_id, _v2) = store.add_op(TestInstructionSet::Inc, svec![v0[0]]);
assert_display_is!(
store.format(),
r#"
%0 : Int = int_input<pos: 0>();
%1 : Int = int_input<pos: 1>();
%2 : Int = inc(%0 : Int);
"#
);
store.replace_val_use(v0[0], v1[0]);
assert_display_is!(
store.format(),
r#"
%0 : Int = int_input<pos: 0>();
%1 : Int = int_input<pos: 1>();
%2 : Int = inc(%1 : Int);
"#
);
let inc = store.get_op(inc_id);
let v0 = store.get_val(v0[0]);
let v1 = store.get_val(v1[0]);
assert_eq!(v0.get_users_iter().covec(), []);
assert_eq!(v1.get_users_iter().covec(), [inc.clone()]);
}
#[test]
fn test_replace_val_use_make_shallower() {
let mut store: IR<TestLang> = IR::empty();
let (_inp1_id, v0) = store.add_op(TestInstructionSet::IntInput { pos: 0 }, svec![]);
let (_inp2_id, v1) = store.add_op(TestInstructionSet::IntInput { pos: 1 }, svec![]);
let (_, v2) = store.add_op(TestInstructionSet::Inc, svec![v1[0]]);
let (_, v3) = store.add_op(TestInstructionSet::Inc, svec![v2[0]]);
let (_, v4) = store.add_op(TestInstructionSet::Inc, svec![v3[0]]);
let (last_id, _v5) = store.add_op(TestInstructionSet::Inc, svec![v4[0]]);
assert_display_is!(
store.format(),
r#"
%0 : Int = int_input<pos: 0>();
%1 : Int = int_input<pos: 1>();
%2 : Int = inc(%1 : Int);
%3 : Int = inc(%2 : Int);
%4 : Int = inc(%3 : Int);
%5 : Int = inc(%4 : Int);
"#
);
let last = store.get_op(last_id);
assert_eq!(last.get_depth(), 4);
store.replace_val_use(v4[0], v0[0]);
assert_display_is!(
store.format(),
r#"
%0 : Int = int_input<pos: 0>();
%1 : Int = int_input<pos: 1>();
%2 : Int = inc(%1 : Int);
%5 : Int = inc(%0 : Int);
%3 : Int = inc(%2 : Int);
%4 : Int = inc(%3 : Int);
"#
);
let last = store.get_op(last_id);
assert_eq!(last.get_depth(), 1);
}
#[test]
fn test_replace_val_use_make_deeper() {
let mut store: IR<TestLang> = IR::empty();
let (_inp1_id, v0) = store.add_op(TestInstructionSet::IntInput { pos: 0 }, svec![]);
let (_inp2_id, v1) = store.add_op(TestInstructionSet::IntInput { pos: 1 }, svec![]);
let (_, v2) = store.add_op(TestInstructionSet::Inc, svec![v1[0]]);
let (_, v3) = store.add_op(TestInstructionSet::Inc, svec![v2[0]]);
let (_, v4) = store.add_op(TestInstructionSet::Inc, svec![v0[0]]);
let (last_id, _v5) = store.add_op(TestInstructionSet::Inc, svec![v4[0]]);
assert_display_is!(
store.format(),
r#"
%0 : Int = int_input<pos: 0>();
%1 : Int = int_input<pos: 1>();
%2 : Int = inc(%1 : Int);
%4 : Int = inc(%0 : Int);
%3 : Int = inc(%2 : Int);
%5 : Int = inc(%4 : Int);
"#
);
let last = store.get_op(last_id);
assert_eq!(last.get_depth(), 2);
store.replace_val_use(v0[0], v3[0]);
assert_display_is!(
store.format(),
r#"
%0 : Int = int_input<pos: 0>();
%1 : Int = int_input<pos: 1>();
%2 : Int = inc(%1 : Int);
%3 : Int = inc(%2 : Int);
%4 : Int = inc(%3 : Int);
%5 : Int = inc(%4 : Int);
"#
);
let last = store.get_op(last_id);
assert_eq!(last.get_depth(), 4);
}
#[test]
#[should_panic(expected = "Signature Error")]
fn test_add_op_type_mismatch() {
let mut store: IR<TestLang> = IR::empty();
let (_, bool_val) = store.add_op(TestInstructionSet::BoolConstant { val: true }, svec![]);
store.add_op(TestInstructionSet::Inc, svec![bool_val[0]]);
}
#[test]
#[should_panic(expected = "Signature Error")]
fn test_add_op_wrong_arg_count() {
let mut store: IR<TestLang> = IR::empty();
let (_, int_vals) = store.add_op(TestInstructionSet::IntInput { pos: 0 }, svec![]);
store.add_op(TestInstructionSet::Add, svec![int_vals[0]]);
}
#[test]
#[should_panic(expected = "Unknown valid")]
fn test_add_op_with_deleted_value() {
let mut store: IR<TestLang> = IR::empty();
let (op_id, vals) = store.add_op(TestInstructionSet::IntInput { pos: 0 }, svec![]);
store.delete_op(op_id);
store.add_op(TestInstructionSet::Inc, svec![vals[0]]);
}
#[test]
#[should_panic(expected = "Tried to get a dead op")]
fn test_get_deleted_op() {
let mut store: IR<TestLang> = IR::empty();
let (op_id, _) = store.add_op(TestInstructionSet::IntInput { pos: 0 }, svec![]);
store.delete_op(op_id);
store.get_op(op_id);
}
#[test]
#[should_panic(expected = "Tried to get a dead val")]
fn test_get_deleted_val() {
let mut store: IR<TestLang> = IR::empty();
let (op_id, vals) = store.add_op(TestInstructionSet::IntInput { pos: 0 }, svec![]);
store.delete_op(op_id);
store.get_val(vals[0]);
}
#[test]
#[should_panic(expected = "Tried to delete an already inactive operation")]
fn test_double_deletion() {
let mut store: IR<TestLang> = IR::empty();
let (op_id, _) = store.add_op(TestInstructionSet::IntInput { pos: 0 }, svec![]);
store.delete_op(op_id);
store.delete_op(op_id); }
#[test]
fn test_replace_val_use_self() {
let mut store: IR<TestLang> = IR::empty();
let (_, vals) = store.add_op(TestInstructionSet::IntInput { pos: 0 }, svec![]);
let (inc_id, _) = store.add_op(TestInstructionSet::Inc, svec![vals[0]]);
let inc_before = format!("{:?}", store.get_op(inc_id));
store.replace_val_use(vals[0], vals[0]); let inc_after = format!("{:?}", store.get_op(inc_id));
assert_eq!(inc_before, inc_after);
assert_display_is!(
store.format(),
r#"
%0 : Int = int_input<pos: 0>();
%1 : Int = inc(%0 : Int);
"#
);
}
#[test]
fn test_same_value_multiple_args() {
let mut store: IR<TestLang> = IR::empty();
let (_, vals) = store.add_op(TestInstructionSet::IntInput { pos: 0 }, svec![]);
let (add_id, _) = store.add_op(TestInstructionSet::Add, svec![vals[0], vals[0]]);
let val = store.get_val(vals[0]);
let add_op = store.get_op(add_id);
assert_eq!(val.get_users_iter().count(), 1);
assert_eq!(
add_op
.get_args_iter()
.filter(|v| v.get_id() == vals[0])
.count(),
2
);
}
#[test]
fn test_diamond_dependencies() {
let mut store: IR<TestLang> = IR::empty();
let (_, a_vals) = store.add_op(TestInstructionSet::IntInput { pos: 0 }, svec![]); let (_b_id, b_vals) = store.add_op(TestInstructionSet::Inc, svec![a_vals[0]]); let (_c_id, c_vals) = store.add_op(TestInstructionSet::Inc, svec![a_vals[0]]); let (d_id, _) = store.add_op(TestInstructionSet::Add, svec![b_vals[0], c_vals[0]]);
let a_val = store.get_val(a_vals[0]);
let d_op = store.get_op(d_id);
assert_eq!(a_val.get_users_iter().count(), 2);
assert_eq!(d_op.get_depth(), 2);
}
#[test]
fn test_independent_subgraphs() {
let mut store: IR<TestLang> = IR::empty();
let (_, vals1) = store.add_op(TestInstructionSet::IntInput { pos: 0 }, svec![]);
let (inc1_id, _) = store.add_op(TestInstructionSet::Inc, svec![vals1[0]]);
let (_, vals2) = store.add_op(TestInstructionSet::IntInput { pos: 1 }, svec![]);
let (inc2_id, _) = store.add_op(TestInstructionSet::Inc, svec![vals2[0]]);
let inc1 = store.get_op(inc1_id);
let inc2 = store.get_op(inc2_id);
assert!(!inc1.reaches(&inc2));
assert!(!inc2.reaches(&inc1));
}
#[test]
fn test_multi_return_different_users() {
let mut store: IR<TestLang> = IR::empty();
let (_, inp1) = store.add_op(TestInstructionSet::IntInput { pos: 0 }, svec![]);
let (_, inp2) = store.add_op(TestInstructionSet::IntInput { pos: 1 }, svec![]);
let (_, div_vals) = store.add_op(TestInstructionSet::DivRem, svec![inp1[0], inp2[0]]);
let (_, _) = store.add_op(TestInstructionSet::Inc, svec![div_vals[0]]); let (_, _) = store.add_op(TestInstructionSet::Inc, svec![div_vals[1]]);
let quot = store.get_val(div_vals[0]);
let rem = store.get_val(div_vals[1]);
assert_eq!(quot.get_users_iter().count(), 1);
assert_eq!(rem.get_users_iter().count(), 1);
assert_ne!(
quot.get_users_iter().next().unwrap().get_id(),
rem.get_users_iter().next().unwrap().get_id()
);
}
#[test]
fn test_iteration_with_deleted_elements() {
let mut store: IR<TestLang> = IR::empty();
let (op1_id, _) = store.add_op(TestInstructionSet::IntInput { pos: 0 }, svec![]);
let (op2_id, _) = store.add_op(TestInstructionSet::IntInput { pos: 1 }, svec![]);
let (op3_id, _) = store.add_op(TestInstructionSet::IntInput { pos: 2 }, svec![]);
store.delete_op(op2_id);
let active_ops = store.walk_ops_linear().map(|op| op.get_id()).covec();
assert_eq!(active_ops.len(), 2);
assert!(active_ops.contains(&op1_id));
assert!(!active_ops.contains(&op2_id));
assert!(active_ops.contains(&op3_id));
let all_ops = store.raw_walk_ops_linear().map(|op| op.get_id()).covec();
assert_eq!(all_ops.len(), 3);
}
#[test]
fn test_user_consistency_after_deletion() {
let mut store: IR<TestLang> = IR::empty();
let (_inp_id, vals) = store.add_op(TestInstructionSet::IntInput { pos: 0 }, svec![]);
let (inc1_id, _) = store.add_op(TestInstructionSet::Inc, svec![vals[0]]);
let (_inc2_id, _) = store.add_op(TestInstructionSet::Inc, svec![vals[0]]);
assert_eq!(store.get_val(vals[0]).get_users_iter().count(), 2);
store.delete_op(inc1_id);
let remaining_users: Vec<_> = store
.get_val(vals[0])
.raw_get_uses_iter()
.map(|op| op.opref.get_id())
.collect();
assert_eq!(remaining_users.len(), 2);
let active_users: Vec<_> = store
.get_val(vals[0])
.get_users_iter()
.collect();
assert_eq!(active_users.len(), 1);
}
#[test]
fn test_replacement_cascade_multiple_levels() {
let mut store: IR<TestLang> = IR::empty();
let (_, inp1) = store.add_op(TestInstructionSet::IntInput { pos: 0 }, svec![]);
let (_, inp2) = store.add_op(TestInstructionSet::IntInput { pos: 1 }, svec![]);
let (inc1_id, inc1_vals) = store.add_op(TestInstructionSet::Inc, svec![inp1[0]]);
let (inc2_id, inc2_vals) = store.add_op(TestInstructionSet::Inc, svec![inc1_vals[0]]);
let (inc3_id, _) = store.add_op(TestInstructionSet::Inc, svec![inc2_vals[0]]);
let depths_before = [
store.get_op(inc1_id).get_depth(),
store.get_op(inc2_id).get_depth(),
store.get_op(inc3_id).get_depth(),
];
store.replace_val_use(inp1[0], inp2[0]);
let depths_after = [
store.get_op(inc1_id).get_depth(),
store.get_op(inc2_id).get_depth(),
store.get_op(inc3_id).get_depth(),
];
assert_eq!(depths_before, depths_after);
}
#[test]
fn test_replacement_deeper_chain() {
let mut store: IR<TestLang> = IR::empty();
let (_, inp1) = store.add_op(TestInstructionSet::IntInput { pos: 0 }, svec![]);
let (_, inp2) = store.add_op(TestInstructionSet::IntInput { pos: 1 }, svec![]);
let (_, inc1_vals) = store.add_op(TestInstructionSet::Inc, svec![inp2[0]]);
let (inc2_id, _) = store.add_op(TestInstructionSet::Inc, svec![inp1[0]]);
assert_eq!(store.get_op(inc2_id).get_depth(), 1);
store.replace_val_use(inp1[0], inc1_vals[0]);
assert_eq!(store.get_op(inc2_id).get_depth(), 2); }
#[test]
fn test_has_id_with_deleted_elements() {
let mut store: IR<TestLang> = IR::empty();
let (op_id, vals) = store.add_op(TestInstructionSet::IntInput { pos: 0 }, svec![]);
assert!(store.has_opid(op_id));
assert!(store.has_valid(vals[0]));
store.delete_op(op_id);
assert!(!store.has_opid(op_id));
assert!(!store.has_valid(vals[0]));
assert!(store.raw_has_opid(op_id));
assert!(store.raw_has_valid(vals[0]));
}
#[test]
fn test_empty_ir_operations() {
let store: IR<TestLang> = IR::empty();
assert_eq!(store.n_ops(), 0);
assert_eq!(store.n_vals(), 0);
assert_eq!(store.walk_ops_linear().count(), 0);
let topo_ops: Vec<_> = store.raw_walk_ops_topo().collect();
assert_eq!(topo_ops.len(), 0);
}
#[test]
fn test_topological_order_with_deletions() {
let mut store: IR<TestLang> = IR::empty();
let (op1_id, vals1) = store.add_op(TestInstructionSet::IntInput { pos: 0 }, svec![]);
let (op2_id, vals2) = store.add_op(TestInstructionSet::Inc, svec![vals1[0]]);
let (op3_id, _vals3) = store.add_op(TestInstructionSet::Inc, svec![vals2[0]]);
let (op4_id, _) = store.add_op(TestInstructionSet::Inc, svec![vals1[0]]);
store.delete_op(op3_id); store.delete_op(op2_id);
let all_topo: Vec<_> = store.raw_topological_opwalker().collect();
assert_eq!(all_topo.len(), 4);
assert!(all_topo.contains(&op1_id));
assert!(all_topo.contains(&op2_id));
assert!(all_topo.contains(&op3_id));
assert!(all_topo.contains(&op4_id));
let op1_pos = all_topo.iter().position(|&id| id == op1_id).unwrap();
let op2_pos = all_topo.iter().position(|&id| id == op2_id).unwrap();
let op3_pos = all_topo.iter().position(|&id| id == op3_id).unwrap();
let op4_pos = all_topo.iter().position(|&id| id == op4_id).unwrap();
assert!(op1_pos < op2_pos);
assert!(op1_pos < op4_pos);
assert!(op2_pos < op3_pos);
}
#[test]
#[should_panic(expected = "Tried to replace a value with one of different type")]
fn test_replace_val_different_types() {
let mut store: IR<TestLang> = IR::empty();
let (_, int_vals) = store.add_op(TestInstructionSet::IntInput { pos: 0 }, svec![]);
let (_, bool_vals) = store.add_op(TestInstructionSet::BoolConstant { val: true }, svec![]);
store.replace_val_use(int_vals[0], bool_vals[0]);
}
#[test]
fn test_unreachable_after_replacement() {
let mut store: IR<TestLang> = IR::empty();
let (_, inp1) = store.add_op(TestInstructionSet::IntInput { pos: 0 }, svec![]);
let (_, inp2) = store.add_op(TestInstructionSet::IntInput { pos: 1 }, svec![]);
let (inc1_id, inc1_vals) = store.add_op(TestInstructionSet::Inc, svec![inp1[0]]);
let (_ret_id, _) = store.add_op(TestInstructionSet::Return, svec![inc1_vals[0]]);
store.replace_val_use(inc1_vals[0], inp2[0]);
assert!(store.has_opid(inc1_id));
assert_eq!(store.get_val(inc1_vals[0]).get_users_iter().count(), 0);
store.delete_op(inc1_id);
assert!(!store.has_opid(inc1_id));
}
#[test]
fn test_is_effect() {
let mut ir = IR::<TestLang>::empty();
let (_, inp1) = ir.add_op(TestInstructionSet::IntInput { pos: 0 }, svec![]);
let (_, inp2) = ir.add_op(TestInstructionSet::IntInput { pos: 1 }, svec![]);
let (add_op, add) = ir.add_op(TestInstructionSet::Add, svec![inp1[0], inp2[0]]);
let (ret_op, _) = ir.add_op(TestInstructionSet::Return, svec![add[0]]);
assert!(ir.get_op(ret_op).is_effect());
assert!(!ir.get_op(add_op).is_effect());
}
#[test]
fn test_signature_consistency() {
let mut ir = IR::<TestLang>::empty();
let (_, inp1) = ir.add_op(TestInstructionSet::IntInput { pos: 0 }, svec![]);
let (_, inp2) = ir.add_op(TestInstructionSet::IntInput { pos: 1 }, svec![]);
let (add_op, _) = ir.add_op(TestInstructionSet::Add, svec![inp1[0], inp2[0]]);
let op_ref = ir.get_op(add_op);
assert_eq!(op_ref.signature, &op_ref.operation.get_signature());
}
#[test]
fn test_batch_delete_empty() {
let mut ir = IR::<TestLang>::empty();
let (op1, _) = ir.add_op(TestInstructionSet::IntInput { pos: 0 }, svec![]);
ir.batch_delete_op(std::iter::empty());
assert!(ir.has_opid(op1));
assert_eq!(ir.n_ops(), 1);
}
#[test]
fn test_batch_delete_single() {
let mut ir = IR::<TestLang>::empty();
let (op1, vals1) = ir.add_op(TestInstructionSet::IntInput { pos: 0 }, svec![]);
let (op2, _) = ir.add_op(TestInstructionSet::Return, vals1);
ir.batch_delete_op(std::iter::once(op2));
assert!(ir.has_opid(op1));
assert!(!ir.has_opid(op2));
assert_eq!(ir.n_ops(), 1);
}
#[test]
fn test_batch_delete_dependency_chain() {
let mut ir = IR::<TestLang>::empty();
let (op1, vals1) = ir.add_op(TestInstructionSet::IntInput { pos: 0 }, svec![]);
let (op2, vals2) = ir.add_op(TestInstructionSet::Inc, vals1);
let (op3, vals3) = ir.add_op(TestInstructionSet::Inc, vals2);
let (op4, _) = ir.add_op(TestInstructionSet::Return, vals3);
ir.batch_delete_op([op2, op3, op4].into_iter());
assert!(ir.has_opid(op1));
assert!(!ir.has_opid(op2));
assert!(!ir.has_opid(op3));
assert!(!ir.has_opid(op4));
assert_eq!(ir.n_ops(), 1);
}
#[test]
fn test_batch_delete_order_independence() {
let mut ir = IR::<TestLang>::empty();
let (op1, vals1) = ir.add_op(TestInstructionSet::IntInput { pos: 0 }, svec![]);
let (op2, vals2) = ir.add_op(TestInstructionSet::Inc, vals1);
let (op3, _) = ir.add_op(TestInstructionSet::Return, vals2);
ir.batch_delete_op([op2, op3].into_iter());
assert!(ir.has_opid(op1));
assert!(!ir.has_opid(op2));
assert!(!ir.has_opid(op3));
assert_eq!(ir.n_ops(), 1);
}
#[test]
fn test_batch_delete_diamond_pattern() {
let mut ir = IR::<TestLang>::empty();
let (op1, vals1) = ir.add_op(TestInstructionSet::IntInput { pos: 0 }, svec![]);
let (op2, vals2) = ir.add_op(TestInstructionSet::Inc, vals1.clone());
let (op3, vals3) = ir.add_op(TestInstructionSet::Inc, vals1);
let (op4, _) = ir.add_op(TestInstructionSet::Add, svec![vals2[0], vals3[0]]);
ir.batch_delete_op([op2, op3, op4].into_iter());
assert!(ir.has_opid(op1));
assert!(!ir.has_opid(op2));
assert!(!ir.has_opid(op3));
assert!(!ir.has_opid(op4));
assert_eq!(ir.n_ops(), 1);
}
#[test]
fn test_batch_delete_independent_operations() {
let mut ir = IR::<TestLang>::empty();
let (op1, vals1) = ir.add_op(TestInstructionSet::IntInput { pos: 0 }, svec![]);
let (op2, vals2) = ir.add_op(TestInstructionSet::IntInput { pos: 1 }, svec![]);
let (op3, _) = ir.add_op(TestInstructionSet::Return, vals1);
let (op4, _) = ir.add_op(TestInstructionSet::Return, vals2);
ir.batch_delete_op([op3, op4].into_iter());
assert!(ir.has_opid(op1));
assert!(ir.has_opid(op2));
assert!(!ir.has_opid(op3));
assert!(!ir.has_opid(op4));
assert_eq!(ir.n_ops(), 2);
}
#[test]
#[should_panic(expected = "Tried to delete an operation whose return values are still in use")]
fn test_batch_delete_with_external_users() {
let mut ir = IR::<TestLang>::empty();
let (_op1, vals1) = ir.add_op(TestInstructionSet::IntInput { pos: 0 }, svec![]);
let (op2, vals2) = ir.add_op(TestInstructionSet::Inc, vals1);
let (op3, _) = ir.add_op(TestInstructionSet::Return, vals2.clone());
let (_op4, _) = ir.add_op(TestInstructionSet::Return, vals2);
ir.batch_delete_op([op2, op3].into_iter());
}
#[test]
fn test_batch_delete_partial_dependency_closure() {
let mut ir = IR::<TestLang>::empty();
let (op1, vals1) = ir.add_op(TestInstructionSet::IntInput { pos: 0 }, svec![]);
let (op2, vals2) = ir.add_op(TestInstructionSet::Inc, vals1);
let (op3, vals3) = ir.add_op(TestInstructionSet::Inc, vals2.clone());
let (op4, _) = ir.add_op(TestInstructionSet::Return, vals3);
let (op5, _) = ir.add_op(TestInstructionSet::Return, vals2);
ir.batch_delete_op([op3, op4].into_iter());
assert!(ir.has_opid(op1));
assert!(ir.has_opid(op2));
assert!(!ir.has_opid(op3));
assert!(!ir.has_opid(op4));
assert!(ir.has_opid(op5));
assert_eq!(ir.n_ops(), 3);
}
#[test]
#[should_panic(expected = "Tried to get a dead op")]
fn test_batch_delete_already_deleted() {
let mut ir = IR::<TestLang>::empty();
let (_, vals1) = ir.add_op(TestInstructionSet::IntInput { pos: 0 }, svec![]);
let (op2, _) = ir.add_op(TestInstructionSet::Return, vals1);
ir.delete_op(op2);
ir.batch_delete_op(std::iter::once(op2));
}
#[test]
fn test_val_origin_position_tracking() {
let mut store: IR<TestLang> = IR::empty();
let (_input_id, v_input) = store.add_op(TestInstructionSet::IntInput { pos: 0 }, svec![]);
let (divrem_id, v_divrem) =
store.add_op(TestInstructionSet::DivRem, svec![v_input[0], v_input[0]]);
let quotient = store.get_val(v_divrem[0]);
let remainder = store.get_val(v_divrem[1]);
assert_eq!(quotient.get_origin().opref.get_id(), divrem_id);
assert_eq!(remainder.get_origin().opref.get_id(), divrem_id);
assert_eq!(quotient.get_origin().position, 0);
assert_eq!(remainder.get_origin().position, 1);
}
#[test]
fn test_val_use_position_tracking() {
let mut store: IR<TestLang> = IR::empty();
let (_input1_id, v1) = store.add_op(TestInstructionSet::IntInput { pos: 0 }, svec![]);
let (_input2_id, v2) = store.add_op(TestInstructionSet::IntInput { pos: 1 }, svec![]);
let (add_id, _v_add) = store.add_op(TestInstructionSet::Add, svec![v1[0], v2[0]]);
let val1 = store.get_val(v1[0]);
let val2 = store.get_val(v2[0]);
assert_eq!(val1.users.len(), 1);
assert_eq!(val2.users.len(), 1);
assert_eq!(val1.users[0].opid, add_id);
assert_eq!(val2.users[0].opid, add_id);
assert_eq!(val1.users[0].position, 0);
assert_eq!(val2.users[0].position, 1);
}
#[test]
fn test_position_tracking_with_multiple_uses() {
let mut store: IR<TestLang> = IR::empty();
let (_input_id, v_input) = store.add_op(TestInstructionSet::IntInput { pos: 0 }, svec![]);
let (add1_id, _v_add1) = store.add_op(TestInstructionSet::Add, svec![v_input[0], v_input[0]]);
let (divrem_id, _v_divrem) =
store.add_op(TestInstructionSet::DivRem, svec![v_input[0], v_input[0]]);
let input_val = store.get_val(v_input[0]);
assert_eq!(input_val.users.len(), 4);
let mut uses = input_val.users.iter().collect::<Vec<_>>();
uses.sort_by_key(|u| (u.opid, u.position));
let add1_uses: Vec<_> = uses.iter().filter(|u| u.opid == add1_id).collect();
let divrem_uses: Vec<_> = uses.iter().filter(|u| u.opid == divrem_id).collect();
assert_eq!(add1_uses.len(), 2);
assert_eq!(divrem_uses.len(), 2);
assert!(add1_uses.iter().any(|u| u.position == 0));
assert!(add1_uses.iter().any(|u| u.position == 1));
assert!(divrem_uses.iter().any(|u| u.position == 0));
assert!(divrem_uses.iter().any(|u| u.position == 1));
}
#[test]
fn test_position_tracking_with_multi_return_multi_use() {
let mut store: IR<TestLang> = IR::empty();
let (_input1_id, v1) = store.add_op(TestInstructionSet::IntInput { pos: 0 }, svec![]);
let (_input2_id, v2) = store.add_op(TestInstructionSet::IntInput { pos: 1 }, svec![]);
let (divrem_id, v_divrem) = store.add_op(TestInstructionSet::DivRem, svec![v1[0], v2[0]]);
let (add_id, _v_add) = store.add_op(TestInstructionSet::Add, svec![v_divrem[0], v_divrem[1]]);
let quotient = store.get_val(v_divrem[0]);
let remainder = store.get_val(v_divrem[1]);
assert_eq!(quotient.get_origin().opref.get_id(), divrem_id);
assert_eq!(quotient.get_origin().position, 0);
assert_eq!(remainder.get_origin().opref.get_id(), divrem_id);
assert_eq!(remainder.get_origin().position, 1);
assert_eq!(quotient.users.len(), 1);
assert_eq!(remainder.users.len(), 1);
assert_eq!(quotient.users[0].opid, add_id);
assert_eq!(quotient.users[0].position, 0);
assert_eq!(remainder.users[0].opid, add_id);
assert_eq!(remainder.users[0].position, 1);
}
#[test]
fn test_position_consistency_after_replacement() {
let mut store: IR<TestLang> = IR::empty();
let (_input1_id, v1) = store.add_op(TestInstructionSet::IntInput { pos: 0 }, svec![]);
let (_input2_id, v2) = store.add_op(TestInstructionSet::IntInput { pos: 1 }, svec![]);
let (_input3_id, v3) = store.add_op(TestInstructionSet::IntInput { pos: 2 }, svec![]);
let (add_id, _v_add) = store.add_op(TestInstructionSet::Add, svec![v1[0], v2[0]]);
store.replace_val_use(v1[0], v3[0]);
let val3 = store.get_val(v3[0]);
let val1 = store.get_val(v1[0]);
assert!(
val3.users
.iter()
.any(|u| u.opid == add_id && u.position == 0)
);
assert!(!val1.users.iter().any(|u| u.opid == add_id));
}
#[test]
fn test_position_tracking_single_argument_operation() {
let mut store: IR<TestLang> = IR::empty();
let (_input_id, v_input) = store.add_op(TestInstructionSet::IntInput { pos: 0 }, svec![]);
let (inc_id, _v_inc) = store.add_op(TestInstructionSet::Inc, svec![v_input[0]]);
let input_val = store.get_val(v_input[0]);
assert_eq!(input_val.users.len(), 1);
assert_eq!(input_val.users[0].opid, inc_id);
assert_eq!(input_val.users[0].position, 0);
}
#[test]
fn test_position_tracking_single_return_operation() {
let mut store: IR<TestLang> = IR::empty();
let (_input_id, v_input) = store.add_op(TestInstructionSet::IntInput { pos: 0 }, svec![]);
let (add_id, v_add) = store.add_op(TestInstructionSet::Add, svec![v_input[0], v_input[0]]);
let result_val = store.get_val(v_add[0]);
assert_eq!(result_val.get_origin().opref.get_id(), add_id);
assert_eq!(result_val.get_origin().position, 0);
}
#[test]
fn test_position_tracking_zero_argument_operation() {
let mut store: IR<TestLang> = IR::empty();
let (input_id, v_input) = store.add_op(TestInstructionSet::IntInput { pos: 0 }, svec![]);
let input_val = store.get_val(v_input[0]);
assert_eq!(input_val.get_origin().opref.get_id(), input_id);
assert_eq!(input_val.get_origin().position, 0);
assert_eq!(input_val.users.len(), 0); }
#[test]
fn test_position_tracking_after_deletion() {
let mut store: IR<TestLang> = IR::empty();
let (_input1_id, v1) = store.add_op(TestInstructionSet::IntInput { pos: 0 }, svec![]);
let (_input2_id, v2) = store.add_op(TestInstructionSet::IntInput { pos: 1 }, svec![]);
let (_add1_id, v_add1) = store.add_op(TestInstructionSet::Add, svec![v1[0], v2[0]]);
let (add2_id, _v_add2) = store.add_op(TestInstructionSet::Add, svec![v_add1[0], v1[0]]);
let intermediate_val = store.get_val(v_add1[0]);
assert_eq!(intermediate_val.users.len(), 1);
assert_eq!(intermediate_val.users[0].opid, add2_id);
assert_eq!(intermediate_val.users[0].position, 0);
store.delete_op(add2_id);
let intermediate_val_after = store.get_val(v_add1[0]);
assert_eq!(intermediate_val_after.users.len(), 1);
let active_users: Vec<_> = intermediate_val_after.get_users_iter().collect();
assert_eq!(active_users.len(), 0);
}
#[test]
fn test_val_origin_and_val_use_equality() {
use crate::{ValOrigin, ValUse};
let mut store: IR<TestLang> = IR::empty();
let (op1_id, _v1) = store.add_op(TestInstructionSet::IntInput { pos: 0 }, svec![]);
let (op2_id, _v2) = store.add_op(TestInstructionSet::IntInput { pos: 1 }, svec![]);
let origin1 = ValOrigin {
opid: op1_id,
position: 0,
};
let origin2 = ValOrigin {
opid: op1_id,
position: 0,
};
let origin3 = ValOrigin {
opid: op1_id,
position: 1,
};
let origin4 = ValOrigin {
opid: op2_id,
position: 0,
};
assert_eq!(origin1, origin2);
assert_ne!(origin1, origin3);
assert_ne!(origin1, origin4);
let use1 = ValUse {
opid: op1_id,
position: 0,
};
let use2 = ValUse {
opid: op1_id,
position: 0,
};
let use3 = ValUse {
opid: op1_id,
position: 1,
};
let use4 = ValUse {
opid: op2_id,
position: 0,
};
assert_eq!(use1, use2);
assert_ne!(use1, use3);
assert_ne!(use1, use4);
let origin_clone = origin1.clone();
let use_clone = use1.clone();
assert_eq!(origin1, origin_clone);
assert_eq!(use1, use_clone);
}
#[test]
fn test_val_origin_and_val_use_debug() {
use crate::{ValOrigin, ValUse};
let mut store: IR<TestLang> = IR::empty();
let (op_id, _v) = store.add_op(TestInstructionSet::IntInput { pos: 0 }, svec![]);
let origin = ValOrigin {
opid: op_id,
position: 2,
};
let use_val = ValUse {
opid: op_id,
position: 3,
};
let origin_debug = format!("{:?}", origin);
let use_debug = format!("{:?}", use_val);
assert!(origin_debug.contains("ValOrigin"));
assert!(origin_debug.contains("opid"));
assert!(origin_debug.contains("position"));
assert!(origin_debug.contains("2"));
assert!(use_debug.contains("ValUse"));
assert!(use_debug.contains("opid"));
assert!(use_debug.contains("position"));
assert!(use_debug.contains("3"));
}
#[test]
fn test_position_tracking_with_iterators() {
let mut store: IR<TestLang> = IR::empty();
let (_input1_id, v1) = store.add_op(TestInstructionSet::IntInput { pos: 0 }, svec![]);
let (_input2_id, v2) = store.add_op(TestInstructionSet::IntInput { pos: 1 }, svec![]);
let (divrem_id, v_divrem) = store.add_op(TestInstructionSet::DivRem, svec![v1[0], v2[0]]);
let (add_id, _v_add) = store.add_op(TestInstructionSet::Add, svec![v_divrem[0], v_divrem[1]]);
let add_op = store.get_op(add_id);
let args: Vec<_> = add_op.get_args_iter().collect();
assert_eq!(args.len(), 2);
assert_eq!(args[0].get_origin().opref.get_id(), divrem_id);
assert_eq!(args[0].get_origin().position, 0);
assert_eq!(args[1].get_origin().opref.get_id(), divrem_id);
assert_eq!(args[1].get_origin().position, 1);
let divrem_op = store.get_op(divrem_id);
let returns: Vec<_> = divrem_op.get_returns_iter().collect();
assert_eq!(returns.len(), 2);
assert_eq!(returns[0].get_origin().position, 0);
assert_eq!(returns[1].get_origin().position, 1);
let quotient_users: Vec<_> = returns[0].get_users_iter().collect();
let remainder_users: Vec<_> = returns[1].get_users_iter().collect();
assert_eq!(quotient_users.len(), 1);
assert_eq!(remainder_users.len(), 1);
assert_eq!(quotient_users[0].get_id(), add_id);
assert_eq!(remainder_users[0].get_id(), add_id);
let quotient_uses: Vec<_> = returns[0].get_uses_iter().collect();
let remainder_uses: Vec<_> = returns[1].get_uses_iter().collect();
assert_eq!(quotient_uses.len(), 1);
assert_eq!(remainder_uses.len(), 1);
assert_eq!(quotient_uses[0].opref.get_id(), add_id);
assert_eq!(quotient_uses[0].position, 0);
assert_eq!(remainder_uses[0].opref.get_id(), add_id);
assert_eq!(remainder_uses[0].position, 1);
assert_eq!(returns[0].users.len(), 1);
assert_eq!(returns[0].users[0].opid, add_id);
assert_eq!(returns[0].users[0].position, 0);
assert_eq!(returns[1].users.len(), 1);
assert_eq!(returns[1].users[0].opid, add_id);
assert_eq!(returns[1].users[0].position, 1);
}