hara-native 0.1.13

HAL-free native host runtime and package launcher for Hara
Documentation
use super::*;

fn compile(operations: Vec<TraceOp>, vectors: Vec<Vec<i64>>) -> (NativeBackend, NativeTrace) {
    let trace = Trace {
        function: 0,
        header: 0,
        resume_ip: 0,
        operations,
        vectors,
    };
    let mut backend = NativeBackend::default();
    let compiled = backend.compile(&trace).unwrap();
    (backend, compiled)
}

#[test]
fn cranelift_backend_executes_guarded_host_code() {
    let (mut backend, mut compiled) = compile(
        vec![
            TraceOp::GuardLocalI64 { local: 0 },
            TraceOp::LoadLocal { local: 0 },
            TraceOp::ConstantI64(1),
            TraceOp::BinaryI64(IntrinsicOp::Add),
            TraceOp::StoreLocal { local: 0 },
            TraceOp::LoopBackedge,
        ],
        vec![],
    );
    let mut locals = [TraceValue::I64(2)];
    assert_eq!(
        backend.enter(&mut compiled, &mut locals, 10),
        TraceOutcome::Completed { iterations: 10 }
    );
    assert_eq!(locals[0], TraceValue::I64(12));
}

#[test]
fn native_backend_multiplies_and_side_exits_on_overflow() {
    let (mut backend, mut compiled) = compile(
        vec![
            TraceOp::GuardLocalI64 { local: 0 },
            TraceOp::LoadLocal { local: 0 },
            TraceOp::ConstantI64(3),
            TraceOp::BinaryI64(IntrinsicOp::Multiply),
            TraceOp::StoreLocal { local: 0 },
            TraceOp::LoopBackedge,
        ],
        vec![],
    );
    let mut locals = [TraceValue::I64(2)];
    assert_eq!(
        backend.enter(&mut compiled, &mut locals, 3),
        TraceOutcome::Completed { iterations: 3 }
    );
    assert_eq!(locals[0], TraceValue::I64(54));
    locals[0] = TraceValue::I64(i64::MAX);
    assert!(matches!(
        backend.enter(&mut compiled, &mut locals, 1),
        TraceOutcome::SideExit {
            reason: ExitReason::Overflow,
            ..
        }
    ));
}

#[test]
fn cranelift_backend_indexes_vector_constants_and_exits_on_bounds() {
    let (mut backend, mut compiled) = compile(
        vec![
            TraceOp::ConstantVectorI64 { vector: 0 },
            TraceOp::LoadLocal { local: 0 },
            TraceOp::VectorNthI64,
            TraceOp::StoreLocal { local: 1 },
            TraceOp::LoopBackedge,
        ],
        vec![vec![10, 20, 30]],
    );
    let mut locals = [TraceValue::I64(2), TraceValue::I64(0)];
    assert_eq!(
        backend.enter(&mut compiled, &mut locals, 1),
        TraceOutcome::Completed { iterations: 1 }
    );
    assert_eq!(locals[1], TraceValue::I64(30));
    locals[0] = TraceValue::I64(-1);
    assert!(matches!(
        backend.enter(&mut compiled, &mut locals, 1),
        TraceOutcome::SideExit {
            reason: ExitReason::IndexOutOfBounds,
            ..
        }
    ));
}

#[test]
fn cranelift_backend_indexes_guarded_vector_locals() {
    let (mut backend, mut compiled) = compile(
        vec![
            TraceOp::GuardLocalVectorI64 { local: 0 },
            TraceOp::LoadLocal { local: 0 },
            TraceOp::LoadLocal { local: 1 },
            TraceOp::VectorNthI64,
            TraceOp::StoreLocal { local: 2 },
            TraceOp::LoopBackedge,
        ],
        vec![],
    );
    let mut locals = [
        TraceValue::Indexed(Box::new(crate::core::Value::Vector(
            [10, 20, 30]
                .into_iter()
                .map(crate::core::Value::Number)
                .collect(),
        ))),
        TraceValue::I64(1),
        TraceValue::I64(0),
    ];
    assert_eq!(
        backend.enter(&mut compiled, &mut locals, 1),
        TraceOutcome::Completed { iterations: 1 }
    );
    assert_eq!(locals[2], TraceValue::I64(20));
    assert!(matches!(locals[0], TraceValue::Indexed(_)));
}

#[test]
fn native_backend_restores_the_iteration_checkpoint_on_failure() {
    let trace = Trace {
        function: 0,
        header: 4,
        resume_ip: 4,
        operations: vec![
            TraceOp::LoadLocal { local: 0 },
            TraceOp::ConstantI64(1),
            TraceOp::BinaryI64(IntrinsicOp::Add),
            TraceOp::StoreLocal { local: 0 },
            TraceOp::LoadLocal { local: 0 },
            TraceOp::ConstantI64(0),
            TraceOp::BinaryI64(IntrinsicOp::Divide),
            TraceOp::Pop,
            TraceOp::LoopBackedge,
        ],
        vectors: vec![],
    };
    let mut backend = NativeBackend::default();
    let mut compiled = backend.compile(&trace).unwrap();
    let mut locals = [TraceValue::I64(9)];
    assert!(
        matches!(backend.enter(&mut compiled, &mut locals, 1), TraceOutcome::SideExit { reason: ExitReason::DivisionByZero, snapshot: ExitSnapshot { locals, .. }, .. } if locals == vec![TraceValue::I64(9)])
    );
}