use std::collections::HashMap;
use relon_codegen_llvm::LlvmAotEvaluator;
use relon_eval_api::{Evaluator, Value};
use relon_ir::ir::{Func, IrType, Module as IrModule, Op, TaggedOp};
use relon_ir::stdlib::stdlib_function_count;
use relon_parser::TokenRange;
fn tagged(op: Op) -> TaggedOp {
TaggedOp {
op,
range: TokenRange::default(),
}
}
fn build_fib_helper() -> Func {
let fib_call_idx = stdlib_function_count();
let cond_body = vec![
tagged(Op::LocalGet(0)),
tagged(Op::ConstI64(2)),
tagged(Op::Lt(IrType::I64)),
];
let then_body = vec![tagged(Op::LocalGet(0))];
let else_body = vec![
tagged(Op::LocalGet(0)),
tagged(Op::ConstI64(1)),
tagged(Op::Sub(IrType::I64)),
tagged(Op::Call {
fn_index: fib_call_idx,
arg_count: 1,
param_tys: vec![IrType::I64],
ret_ty: IrType::I64,
}),
tagged(Op::LocalGet(0)),
tagged(Op::ConstI64(2)),
tagged(Op::Sub(IrType::I64)),
tagged(Op::Call {
fn_index: fib_call_idx,
arg_count: 1,
param_tys: vec![IrType::I64],
ret_ty: IrType::I64,
}),
tagged(Op::Add(IrType::I64)),
];
let mut body = cond_body;
body.push(tagged(Op::If {
result_ty: IrType::I64,
then_body,
else_body,
}));
body.push(tagged(Op::Return));
Func {
name: "fib".to_string(),
params: vec![IrType::I64],
ret: IrType::I64,
body,
range: TokenRange::default(),
}
}
fn build_entry_calling_fib() -> Func {
let fib_call_idx = stdlib_function_count();
let body = vec![
tagged(Op::LocalGet(0)),
tagged(Op::Call {
fn_index: fib_call_idx,
arg_count: 1,
param_tys: vec![IrType::I64],
ret_ty: IrType::I64,
}),
tagged(Op::Return),
];
Func {
name: "run_main".to_string(),
params: vec![IrType::I64],
ret: IrType::I64,
body,
range: TokenRange::default(),
}
}
fn build_recursive_fib_module() -> IrModule {
let fib = build_fib_helper();
let entry = build_entry_calling_fib();
IrModule {
imports: vec![],
funcs: vec![fib, entry],
entry_func_index: Some(1),
closure_table: vec![],
}
}
#[test]
fn synthetic_recursive_fib_ten() {
let ir = build_recursive_fib_module();
let ev = LlvmAotEvaluator::from_ir_direct(ir, vec!["n".to_string()]).expect("compile");
let r = ev.run_main_legacy_i64(&[10]).expect("fast path");
assert_eq!(r, 55, "fib(10) should be 55, got {r}");
}
#[test]
fn synthetic_recursive_fib_through_evaluator_trait() {
let ir = build_recursive_fib_module();
let ev = LlvmAotEvaluator::from_ir_direct(ir, vec!["n".to_string()]).expect("compile");
let mut args = HashMap::new();
args.insert("n".to_string(), Value::Int(10));
let result = ev.run_main(args).expect("run_main");
assert_eq!(result, Value::Int(55));
}
#[test]
fn synthetic_recursive_fib_base_cases() {
let ir = build_recursive_fib_module();
let ev = LlvmAotEvaluator::from_ir_direct(ir, vec!["n".to_string()]).expect("compile");
assert_eq!(ev.run_main_legacy_i64(&[0]).unwrap(), 0);
assert_eq!(ev.run_main_legacy_i64(&[1]).unwrap(), 1);
assert_eq!(ev.run_main_legacy_i64(&[2]).unwrap(), 1);
assert_eq!(ev.run_main_legacy_i64(&[3]).unwrap(), 2);
}
#[test]
fn synthetic_recursive_fib_ir_dump_has_helper() {
let ir = build_recursive_fib_module();
let ev = LlvmAotEvaluator::from_ir_direct(ir, vec!["n".to_string()]).expect("compile");
let dump = ev.emit_ir_dump();
assert!(
dump.contains("relon_helper_fib"),
"IR dump missing the fib helper symbol:\n{dump}"
);
assert!(
dump.contains("relon_llvm_entry"),
"IR dump missing entry symbol:\n{dump}"
);
}