alduin 0.0.1

WIP: A toy compiler backend
Documentation
use alduin::compiler::compiled_code::Symbol;
use alduin::compiler::graph::{BaseGraphBuilder, Graph, Signature, Type};
use alduin::compiler::Compiler;
use alduin::rt::Value;

type Isa = alduin::backend::x64::X64;

#[test]
fn empty() {
    let compiler = Compiler::new();
    compiler.compile(Symbol::named("test"), {
        let graph = Graph::new(Signature(vec![], Type::Void));
        let mut builder = BaseGraphBuilder::new(graph);
        let start = builder.new_start();
        builder.bind(start);
        builder.new_return::<()>(None);
        builder.finalize()
    });
    let code = compiler.get(&Symbol::named("test"));
    assert_eq!(code.invoke(&[]), None);
}

#[test]
fn return_constant() {
    let compiler = Compiler::new();
    compiler.compile(Symbol::named("test"), {
        let graph = Graph::new(Signature(vec![], Type::I32));
        let mut builder = BaseGraphBuilder::new(graph);
        let start = builder.new_start();
        builder.bind(start);
        let zero = builder.new_constant(233i32);
        builder.new_return(Some(zero));
        builder.finalize()
    });
    let code = compiler.get(&Symbol::named("test"));
    assert_eq!(code.invoke(&[]), Some(Value::I32(233)));
}

#[test]
fn return_param() {
    let compiler = Compiler::new();
    compiler.compile(Symbol::named("test"), {
        let graph = Graph::new(Signature(vec![Type::I32], Type::I32));
        let mut builder = BaseGraphBuilder::new(graph);
        let start = builder.new_start();
        builder.bind(start);
        let param = builder.new_param::<i32>(0);
        builder.new_return(Some(param));
        builder.finalize()
    });
    let code = compiler.get(&Symbol::named("test"));
    assert_eq!(code.invoke(&[Value::I32(233)]), Some(Value::I32(233)));
}

#[test]
fn boolean_negate() {
    let compiler = Compiler::new();
    compiler.compile(Symbol::named("test"), {
        let graph = Graph::new(Signature(vec![Type::Bool], Type::Bool));
        let mut builder = BaseGraphBuilder::new(graph);
        let start = builder.new_start();
        builder.bind(start);
        let param = builder.new_param::<bool>(0);
        let negation = builder.new_not(param);
        builder.new_return(Some(negation));
        builder.finalize()
    });
    let code = compiler.get(&Symbol::named("test"));
    assert_eq!(code.invoke(&[Value::Bool(true)]), Some(Value::Bool(false)));
    assert_eq!(code.invoke(&[Value::Bool(false)]), Some(Value::Bool(true)));
}

#[test]
fn i32_add() {
    let compiler = Compiler::new();
    compiler.compile(Symbol::named("test"), {
        let graph = Graph::new(Signature(vec![Type::I32, Type::I32], Type::I32));
        let mut builder = BaseGraphBuilder::new(graph);
        let start = builder.new_start();
        builder.bind(start);
        let x = builder.new_param::<i32>(0);
        let y = builder.new_param::<i32>(1);
        let z = builder.new_add(x, y);
        builder.new_return(Some(z));
        builder.finalize()
    });
    let code = compiler.get(&Symbol::named("test"));
    assert_eq!(
        code.invoke(&[Value::I32(200), Value::I32(33)]),
        Some(Value::I32(233))
    );
}

#[test]
fn f32_add() {
    let compiler = Compiler::new();
    compiler.compile(Symbol::named("test"), {
        let graph = Graph::new(Signature(vec![Type::F32, Type::F32], Type::F32));
        let mut builder = BaseGraphBuilder::new(graph);
        let start = builder.new_start();
        builder.bind(start);
        let x = builder.new_param::<f32>(0);
        let y = builder.new_param::<f32>(1);
        let z = builder.new_add(x, y);
        builder.new_return(Some(z));
        builder.finalize()
    });
    let code = compiler.get(&Symbol::named("test"));
    assert_eq!(
        code.invoke(&[Value::F32(200f32), Value::F32(33f32)]),
        Some(Value::F32(233f32))
    );
}

#[test]
fn i32_is_positive_0() {
    let compiler = Compiler::new();
    compiler.compile(Symbol::named("test"), {
        let graph = Graph::new(Signature(vec![Type::I32], Type::Bool));
        let mut builder = BaseGraphBuilder::new(graph);
        let start = builder.new_start();
        builder.bind(start);
        let x = builder.new_param::<i32>(0);
        let zero = builder.new_constant::<i32>(0);
        let y = builder.new_gt_s(x, zero);
        builder.new_return(Some(y));
        builder.finalize()
    });
    let code = compiler.get(&Symbol::named("test"));
    assert_eq!(code.invoke(&[Value::I32(1)]), Some(Value::Bool(true)));
    assert_eq!(code.invoke(&[Value::I32(0)]), Some(Value::Bool(false)));
    assert_eq!(code.invoke(&[Value::I32(-1)]), Some(Value::Bool(false)));
}

#[test]
fn i32_is_positive_1() {
    let compiler = Compiler::new();
    compiler.compile(Symbol::named("test"), {
        let graph = Graph::new(Signature(vec![Type::I32], Type::Bool));
        let mut builder = BaseGraphBuilder::new(graph);
        let start = builder.new_start();
        builder.bind(start);
        let x = builder.new_param::<i32>(0);
        let zero = builder.new_constant::<i32>(0);
        let y = builder.new_gt_s(x, zero);
        let v = builder.new_constant(false);
        let r = builder.new_variable(v.cast());
        builder
            .r#if(y)
            .then_(|builder| {
                let v = builder.new_constant(true);
                r.set(builder.control(), v.cast());
            })
            .else_(|builder| {
                let v = builder.new_constant(false);
                r.set(builder.control(), v.cast());
            })
            .finish();
        builder.new_return::<bool>(Some(r.get().cast()));
        builder.finalize()
    });
    let code = compiler.get(&Symbol::named("test"));
    assert_eq!(code.invoke(&[Value::I32(1)]), Some(Value::Bool(true)));
    assert_eq!(code.invoke(&[Value::I32(0)]), Some(Value::Bool(false)));
    assert_eq!(code.invoke(&[Value::I32(-1)]), Some(Value::Bool(false)));
}

#[test]
fn i32_is_positive_2() {
    let compiler = Compiler::new();
    compiler.compile(Symbol::named("test"), {
        let graph = Graph::new(Signature(vec![Type::I32], Type::Bool));
        let mut builder = BaseGraphBuilder::new(graph);
        let start = builder.new_start();
        builder.bind(start);
        let x = builder.new_param::<i32>(0);
        let zero = builder.new_constant::<i32>(0);
        let y = builder.new_gt_s(x, zero);
        let v = builder.new_constant(false);
        let r = builder.new_variable(v.cast());
        builder
            .r#if(y)
            .then_(|builder| {
                let v = builder.new_constant(true);
                r.set(builder.control(), v.cast());
            })
            .finish();
        builder.new_return::<bool>(Some(r.get().cast()));
        builder.finalize()
    });
    let code = compiler.get(&Symbol::named("test"));
    assert_eq!(code.invoke(&[Value::I32(1)]), Some(Value::Bool(true)));
    assert_eq!(code.invoke(&[Value::I32(0)]), Some(Value::Bool(false)));
    assert_eq!(code.invoke(&[Value::I32(-1)]), Some(Value::Bool(false)));
}

#[test]
fn i32_is_positive_3() {
    let compiler = Compiler::new();
    compiler.compile(Symbol::named("test"), {
        let graph = Graph::new(Signature(vec![Type::I32], Type::Bool));
        let mut builder = BaseGraphBuilder::new(graph);
        let start = builder.new_start();
        builder.bind(start);
        let x = builder.new_param::<i32>(0);
        let zero = builder.new_constant::<i32>(0);
        let y = builder.new_gt_s(x, zero);
        let v = builder.new_constant(false);
        let r = builder.new_variable(v.cast());
        builder
            .r#if(y)
            .then_(|builder| {
                let v = builder.new_constant(true);
                r.set(builder.control(), v.cast());
            })
            .else_(|_| {})
            .finish();
        builder.new_return::<bool>(Some(r.get().cast()));
        builder.finalize()
    });
    let code = compiler.get(&Symbol::named("test"));
    assert_eq!(code.invoke(&[Value::I32(1)]), Some(Value::Bool(true)));
    assert_eq!(code.invoke(&[Value::I32(0)]), Some(Value::Bool(false)));
    assert_eq!(code.invoke(&[Value::I32(-1)]), Some(Value::Bool(false)));
}

#[test]
fn fibonacci() {
    let mut compiler = Compiler::new();
    compiler.compile(Symbol::named("test"), {
        let graph = Graph::new(Signature(vec![Type::I32], Type::I32));
        let mut builder = BaseGraphBuilder::new(graph);
        let start = builder.new_start();
        builder.bind(start);

        let param = builder.new_param::<i32>(0);
        let n = builder.new_variable(param.cast());
        let one = builder.new_constant::<i32>(1);
        let x = builder.new_variable(one.cast());
        let y = builder.new_variable(one.cast());
        let z = builder.new_variable(one.cast());

        let end_block = builder.new_region(&[]);

        builder.r#loop().body(|builder| {
            let cond = builder.new_le_s::<i32>(n.get().cast(), one);
            let loop_body = builder.new_region(&[]);
            builder.new_branch(cond, end_block, loop_body);

            builder.bind(loop_body);

            z.set(
                builder.control(),
                builder
                    .new_add::<i32>(x.get().cast(), y.get().cast())
                    .cast(),
            );
            x.set(builder.control(), y.get());
            y.set(builder.control(), z.get());
            n.set(
                builder.control(),
                builder.new_sub::<i32>(n.get().cast(), one).cast(),
            );
        });

        builder.bind(end_block);
        builder.new_return(Some(z.get()));

        builder.finalize()
    });

    compiler.finalize::<Isa>();

    let code = compiler.get(&Symbol::named("test"));

    fn fib(n: i32) -> i32 {
        if n <= 1 {
            1
        } else {
            fib(n - 1) + fib(n - 2)
        }
    }
    for i in 0..10 {
        assert_eq!(code.invoke(&[Value::I32(i)]), Some(Value::I32(fib(i))));
    }
}

#[test]
fn func_call() {
    let mut compiler = Compiler::new();

    compiler.compile(Symbol::named("add_one"), {
        let graph = Graph::new(Signature(vec![Type::I32], Type::I32));
        let mut builder = BaseGraphBuilder::new(graph);
        let start = builder.new_start();
        builder.bind(start);

        let param = builder.new_param::<i32>(0);
        let one = builder.new_constant::<i32>(1);
        let ret = builder.new_add(param, one);
        builder.new_return(Some(ret));

        builder.finalize()
    });

    compiler.compile(Symbol::named("add_two"), {
        let graph = Graph::new(Signature(vec![Type::I32], Type::I32));
        let mut builder = BaseGraphBuilder::new(graph);
        let start = builder.new_start();
        builder.bind(start);

        let param = builder.new_param::<i32>(0);
        let x = builder.new_call::<i32>(Symbol::named("add_one"), &[param.cast()]);
        let one = builder.new_constant::<i32>(1);
        let ret = builder.new_add(one, x);
        builder.new_return(Some(ret));

        builder.finalize()
    });

    compiler.finalize::<Isa>();

    let code = compiler.get(&Symbol::named("add_two"));

    assert_eq!(code.invoke(&[Value::I32(233)]), Some(Value::I32(235)));
}

#[test]
fn unused_value() {
    let mut compiler = Compiler::new();

    compiler.compile(Symbol::named("add_one"), {
        let graph = Graph::new(Signature(vec![Type::I32], Type::I32));
        let mut builder = BaseGraphBuilder::new(graph);
        let start = builder.new_start();
        builder.bind(start);

        let param = builder.new_param::<i32>(0);
        let one = builder.new_constant::<i32>(1);
        let _ret = builder.new_add(param, one); // Unused instruction
        let ret = builder.new_add(param, one);
        builder.new_return(Some(ret));

        builder.finalize()
    });

    compiler.finalize::<Isa>();

    let code = compiler.get(&Symbol::named("add_one"));

    assert_eq!(code.invoke(&[Value::I32(233)]), Some(Value::I32(234)));
}

#[test]
fn unused_func_call() {
    let mut compiler = Compiler::new();

    compiler.compile(Symbol::named("add_one"), {
        let graph = Graph::new(Signature(vec![Type::I32], Type::I32));
        let mut builder = BaseGraphBuilder::new(graph);
        let start = builder.new_start();
        builder.bind(start);

        let param = builder.new_param::<i32>(0);
        let one = builder.new_constant::<i32>(1);
        let ret = builder.new_add(param, one);
        builder.new_return(Some(ret));

        builder.finalize()
    });

    compiler.compile(Symbol::named("add_two"), {
        let graph = Graph::new(Signature(vec![Type::I32], Type::I32));
        let mut builder = BaseGraphBuilder::new(graph);
        let start = builder.new_start();
        builder.bind(start);

        let param = builder.new_param::<i32>(0);
        let _x = builder.new_call::<i32>(Symbol::named("add_one"), &[param.cast()]); // redundant call
        let x = builder.new_call::<i32>(Symbol::named("add_one"), &[param.cast()]);
        let _x = builder.new_call::<i32>(Symbol::named("add_one"), &[param.cast()]); // redundant call
        let one = builder.new_constant::<i32>(1);
        let ret = builder.new_add(one, x);
        builder.new_return(Some(ret));

        builder.finalize()
    });

    compiler.finalize::<Isa>();

    let code = compiler.get(&Symbol::named("add_two"));

    assert_eq!(code.invoke(&[Value::I32(233)]), Some(Value::I32(235)));
}