use super::*;
pub(super) fn emit_list_new(ctx: &mut WasmGenCtx, args: &[Calcit]) -> Result<(), String> {
let count = args.len();
let total_bytes = ((1 + count) * 8) as i32;
let ptr = ctx.alloc_local_typed(ValType::I32);
emit_bump_alloc(ctx, total_bytes, ptr, "list");
ctx.emit(Instruction::LocalGet(ptr));
ctx.emit(f64_const(count as f64));
ctx.emit(Instruction::F64Store(mem_arg_f64(0)));
for (i, arg) in args.iter().enumerate() {
ctx.emit(Instruction::LocalGet(ptr));
emit_expr(ctx, arg)?;
ctx.emit(Instruction::F64Store(mem_arg_f64(((1 + i) * 8) as u64)));
}
ctx.ptr_to_f64(ptr);
Ok(())
}
pub(super) fn emit_list_nth(ctx: &mut WasmGenCtx, args: &[Calcit]) -> Result<(), String> {
expect_arity(2, args, "&list:nth expects 2 args")?;
let ptr = emit_ptr_to_i32(ctx, &args[0])?;
emit_expr(ctx, &args[1])?;
ctx.emit(Instruction::I32TruncF64U);
ctx.emit(Instruction::I32Const(1));
ctx.emit(Instruction::I32Add);
ctx.emit(Instruction::I32Const(8));
ctx.emit(Instruction::I32Mul);
ctx.emit(Instruction::LocalGet(ptr));
ctx.emit(Instruction::I32Add);
ctx.emit(Instruction::F64Load(mem_arg_f64(0)));
Ok(())
}
pub(super) fn emit_list_first(ctx: &mut WasmGenCtx, args: &[Calcit]) -> Result<(), String> {
expect_arity(1, args, "&list:first expects 1 arg")?;
emit_expr(ctx, &args[0])?;
ctx.emit(Instruction::I32TruncF64U);
ctx.emit(Instruction::F64Load(mem_arg_f64(8)));
Ok(())
}
pub(super) fn emit_list_last(ctx: &mut WasmGenCtx, args: &[Calcit]) -> Result<(), String> {
expect_arity(1, args, "&list:last expects 1 arg")?;
let src = emit_ptr_to_i32(ctx, &args[0])?;
let count = emit_load_count_i32(ctx, src);
let last_idx = ctx.i32_offset(count, -1);
ctx.emit(Instruction::LocalGet(src));
ctx.emit(Instruction::I32Const(8));
ctx.emit(Instruction::I32Add);
ctx.emit(Instruction::LocalGet(last_idx));
ctx.emit(Instruction::I32Const(8));
ctx.emit(Instruction::I32Mul);
ctx.emit(Instruction::I32Add);
ctx.emit(Instruction::F64Load(mem_arg_f64(0)));
Ok(())
}
pub(super) fn emit_list_rest(ctx: &mut WasmGenCtx, args: &[Calcit]) -> Result<(), String> {
expect_arity(1, args, "&list:rest expects 1 arg")?;
let src = emit_ptr_to_i32(ctx, &args[0])?;
let old_count = emit_load_count_i32(ctx, src);
let new_count = ctx.i32_offset(old_count, -1);
let dst = emit_alloc_list(ctx, new_count);
let dst_base = emit_addr_offset(ctx, dst, 8);
let src_base = emit_addr_offset(ctx, src, 16);
emit_copy_f64_loop(ctx, dst_base, src_base, new_count);
ctx.ptr_to_f64(dst);
Ok(())
}
pub(super) fn emit_list_append(ctx: &mut WasmGenCtx, args: &[Calcit]) -> Result<(), String> {
expect_arity(2, args, "append expects 2 args")?;
let src = emit_ptr_to_i32(ctx, &args[0])?;
let old_count = emit_load_count_i32(ctx, src);
let elem = ctx.alloc_local();
emit_expr(ctx, &args[1])?;
ctx.emit(Instruction::LocalSet(elem));
let new_count = ctx.i32_offset(old_count, 1);
let dst = emit_alloc_list(ctx, new_count);
let dst_base = emit_addr_offset(ctx, dst, 8);
let src_base = emit_addr_offset(ctx, src, 8);
emit_copy_f64_loop(ctx, dst_base, src_base, old_count);
emit_list_store_elem(ctx, dst, old_count, elem);
ctx.ptr_to_f64(dst);
Ok(())
}
pub(super) fn emit_list_prepend(ctx: &mut WasmGenCtx, args: &[Calcit]) -> Result<(), String> {
expect_arity(2, args, "prepend expects 2 args")?;
let src = emit_ptr_to_i32(ctx, &args[0])?;
let old_count = emit_load_count_i32(ctx, src);
let elem = ctx.alloc_local();
emit_expr(ctx, &args[1])?;
ctx.emit(Instruction::LocalSet(elem));
let new_count = ctx.i32_offset(old_count, 1);
let dst = emit_alloc_list(ctx, new_count);
ctx.emit(Instruction::LocalGet(dst));
ctx.emit(Instruction::LocalGet(elem));
ctx.emit(Instruction::F64Store(mem_arg_f64(8)));
let dst_base = emit_addr_offset(ctx, dst, 16);
let src_base = emit_addr_offset(ctx, src, 8);
emit_copy_f64_loop(ctx, dst_base, src_base, old_count);
ctx.ptr_to_f64(dst);
Ok(())
}
pub(super) fn emit_list_butlast(ctx: &mut WasmGenCtx, args: &[Calcit]) -> Result<(), String> {
expect_arity(1, args, "butlast expects 1 arg")?;
let src = emit_ptr_to_i32(ctx, &args[0])?;
let old_count = emit_load_count_i32(ctx, src);
let new_count = ctx.i32_offset(old_count, -1);
let dst = emit_alloc_list(ctx, new_count);
let dst_base = emit_addr_offset(ctx, dst, 8);
let src_base = emit_addr_offset(ctx, src, 8);
emit_copy_f64_loop(ctx, dst_base, src_base, new_count);
ctx.ptr_to_f64(dst);
Ok(())
}
pub(super) fn emit_list_slice(ctx: &mut WasmGenCtx, args: &[Calcit]) -> Result<(), String> {
if args.len() < 2 || args.len() > 3 {
return Err("&list:slice expects 2-3 args".into());
}
let src = emit_ptr_to_i32(ctx, &args[0])?;
let count = emit_load_count_i32(ctx, src);
let start = ctx.alloc_local_typed(ValType::I32);
emit_expr(ctx, &args[1])?;
ctx.emit(Instruction::I32TruncF64U);
ctx.emit(Instruction::LocalSet(start));
let end = ctx.alloc_local_typed(ValType::I32);
if args.len() == 3 {
emit_expr(ctx, &args[2])?;
ctx.emit(Instruction::I32TruncF64U);
ctx.emit(Instruction::LocalSet(end));
} else {
ctx.emit(Instruction::LocalGet(count));
ctx.emit(Instruction::LocalSet(end));
}
let new_count = ctx.alloc_local_typed(ValType::I32);
ctx.emit(Instruction::LocalGet(end));
ctx.emit(Instruction::LocalGet(start));
ctx.emit(Instruction::I32Sub);
ctx.emit(Instruction::LocalSet(new_count));
let dst = emit_alloc_list(ctx, new_count);
let src_base = ctx.alloc_local_typed(ValType::I32);
ctx.emit(Instruction::LocalGet(src));
ctx.emit(Instruction::I32Const(8));
ctx.emit(Instruction::I32Add);
ctx.emit(Instruction::LocalGet(start));
ctx.emit(Instruction::I32Const(8));
ctx.emit(Instruction::I32Mul);
ctx.emit(Instruction::I32Add);
ctx.emit(Instruction::LocalSet(src_base));
let dst_base = emit_addr_offset(ctx, dst, 8);
emit_copy_f64_loop(ctx, dst_base, src_base, new_count);
ctx.ptr_to_f64(dst);
Ok(())
}
pub(super) fn emit_list_reverse(ctx: &mut WasmGenCtx, args: &[Calcit]) -> Result<(), String> {
expect_arity(1, args, "&list:reverse expects 1 arg")?;
let src = emit_ptr_to_i32(ctx, &args[0])?;
let count = emit_load_count_i32(ctx, src);
let dst = emit_alloc_list(ctx, count);
let i = ctx.alloc_i32(0);
ctx.begin_block();
ctx.begin_loop();
ctx.loop_exit_if_ge(i, count);
ctx.emit(Instruction::LocalGet(dst));
ctx.emit(Instruction::I32Const(8));
ctx.emit(Instruction::I32Add);
ctx.emit(Instruction::LocalGet(i));
ctx.emit(Instruction::I32Const(8));
ctx.emit(Instruction::I32Mul);
ctx.emit(Instruction::I32Add);
ctx.emit(Instruction::LocalGet(src));
ctx.emit(Instruction::I32Const(8));
ctx.emit(Instruction::I32Add);
ctx.emit(Instruction::LocalGet(count));
ctx.emit(Instruction::I32Const(1));
ctx.emit(Instruction::I32Sub);
ctx.emit(Instruction::LocalGet(i));
ctx.emit(Instruction::I32Sub);
ctx.emit(Instruction::I32Const(8));
ctx.emit(Instruction::I32Mul);
ctx.emit(Instruction::I32Add);
ctx.emit(Instruction::F64Load(mem_arg_f64(0)));
ctx.emit(Instruction::F64Store(mem_arg_f64(0)));
ctx.i32_inc(i);
ctx.emit(Instruction::Br(0));
ctx.emit(Instruction::End);
ctx.emit(Instruction::End);
ctx.ptr_to_f64(dst);
Ok(())
}
pub(super) fn emit_list_flatten_f64_local(ctx: &mut WasmGenCtx, outer_f64: u32) -> Result<(), String> {
let outer_ptr = ctx.alloc_local_typed(ValType::I32);
ctx.emit(Instruction::LocalGet(outer_f64));
ctx.emit(Instruction::I32TruncF64U);
ctx.emit(Instruction::LocalSet(outer_ptr));
let outer_count = emit_load_count_i32(ctx, outer_ptr);
let total = ctx.alloc_i32(0);
let i1 = ctx.alloc_i32(0);
ctx.begin_block();
ctx.begin_loop();
ctx.loop_exit_if_ge(i1, outer_count);
let inner_ptr1 = emit_list_load_ptr(ctx, outer_ptr, i1);
let inner_count1 = emit_load_count_i32(ctx, inner_ptr1);
ctx.emit(Instruction::LocalGet(total));
ctx.emit(Instruction::LocalGet(inner_count1));
ctx.emit(Instruction::I32Add);
ctx.emit(Instruction::LocalSet(total));
ctx.i32_inc(i1);
ctx.emit(Instruction::Br(0));
ctx.emit(Instruction::End);
ctx.emit(Instruction::End);
let dst = emit_alloc_list(ctx, total);
let write_idx = ctx.alloc_i32(0);
let i2 = ctx.alloc_i32(0);
ctx.begin_block();
ctx.begin_loop();
ctx.loop_exit_if_ge(i2, outer_count);
let inner_ptr2 = emit_list_load_ptr(ctx, outer_ptr, i2);
let inner_count2 = emit_load_count_i32(ctx, inner_ptr2);
let dst_base = ctx.alloc_local_typed(ValType::I32);
ctx.emit(Instruction::LocalGet(dst));
ctx.emit(Instruction::I32Const(8));
ctx.emit(Instruction::I32Add);
ctx.emit(Instruction::LocalGet(write_idx));
ctx.emit(Instruction::I32Const(8));
ctx.emit(Instruction::I32Mul);
ctx.emit(Instruction::I32Add);
ctx.emit(Instruction::LocalSet(dst_base));
let src_base2 = emit_addr_offset(ctx, inner_ptr2, 8);
emit_copy_f64_loop(ctx, dst_base, src_base2, inner_count2);
ctx.emit(Instruction::LocalGet(write_idx));
ctx.emit(Instruction::LocalGet(inner_count2));
ctx.emit(Instruction::I32Add);
ctx.emit(Instruction::LocalSet(write_idx));
ctx.i32_inc(i2);
ctx.emit(Instruction::Br(0));
ctx.emit(Instruction::End);
ctx.emit(Instruction::End);
ctx.ptr_to_f64(dst);
Ok(())
}
fn emit_list_flatten_one_level(ctx: &mut WasmGenCtx, xs_arg: &Calcit) -> Result<(), String> {
let outer_f64 = ctx.alloc_local();
emit_expr(ctx, xs_arg)?;
ctx.emit(Instruction::LocalSet(outer_f64));
emit_list_flatten_f64_local(ctx, outer_f64)
}
pub(super) fn emit_list_concat(ctx: &mut WasmGenCtx, args: &[Calcit]) -> Result<(), String> {
if args.is_empty() {
return emit_list_new(ctx, &[]);
}
if args.len() == 1 {
return emit_list_flatten_one_level(ctx, &args[0]);
}
if args.len() == 2 {
return emit_list_concat_two(ctx, &args[0], &args[1]);
}
emit_list_concat_two(ctx, &args[0], &args[1])?;
for extra in &args[2..] {
let prev_f64 = ctx.alloc_local();
ctx.emit(Instruction::LocalSet(prev_f64));
let prev = Calcit::Number(0.0); let _ = prev; let src_a_local = prev_f64; let src_a = ctx.alloc_local_typed(ValType::I32);
ctx.emit(Instruction::LocalGet(src_a_local));
ctx.emit(Instruction::I32TruncF64U);
ctx.emit(Instruction::LocalSet(src_a));
let count_a = emit_load_count_i32(ctx, src_a);
let src_b = emit_ptr_to_i32(ctx, extra)?;
let count_b = emit_load_count_i32(ctx, src_b);
let new_count = ctx.alloc_local_typed(ValType::I32);
ctx.emit(Instruction::LocalGet(count_a));
ctx.emit(Instruction::LocalGet(count_b));
ctx.emit(Instruction::I32Add);
ctx.emit(Instruction::LocalSet(new_count));
let dst = emit_alloc_list(ctx, new_count);
let dst_base_a = emit_addr_offset(ctx, dst, 8);
let src_base_a = emit_addr_offset(ctx, src_a, 8);
emit_copy_f64_loop(ctx, dst_base_a, src_base_a, count_a);
let dst_base_b = ctx.alloc_local_typed(ValType::I32);
ctx.emit(Instruction::LocalGet(dst));
ctx.emit(Instruction::I32Const(8));
ctx.emit(Instruction::I32Add);
ctx.emit(Instruction::LocalGet(count_a));
ctx.emit(Instruction::I32Const(8));
ctx.emit(Instruction::I32Mul);
ctx.emit(Instruction::I32Add);
ctx.emit(Instruction::LocalSet(dst_base_b));
let src_base_b = emit_addr_offset(ctx, src_b, 8);
emit_copy_f64_loop(ctx, dst_base_b, src_base_b, count_b);
ctx.ptr_to_f64(dst);
}
Ok(())
}
fn emit_list_concat_two(ctx: &mut WasmGenCtx, a: &Calcit, b: &Calcit) -> Result<(), String> {
let src_a = emit_ptr_to_i32(ctx, a)?;
let count_a = emit_load_count_i32(ctx, src_a);
let src_b = emit_ptr_to_i32(ctx, b)?;
let count_b = emit_load_count_i32(ctx, src_b);
let new_count = ctx.alloc_local_typed(ValType::I32);
ctx.emit(Instruction::LocalGet(count_a));
ctx.emit(Instruction::LocalGet(count_b));
ctx.emit(Instruction::I32Add);
ctx.emit(Instruction::LocalSet(new_count));
let dst = emit_alloc_list(ctx, new_count);
let dst_base_a = emit_addr_offset(ctx, dst, 8);
let src_base_a = emit_addr_offset(ctx, src_a, 8);
emit_copy_f64_loop(ctx, dst_base_a, src_base_a, count_a);
let dst_base_b = ctx.alloc_local_typed(ValType::I32);
ctx.emit(Instruction::LocalGet(dst));
ctx.emit(Instruction::I32Const(8));
ctx.emit(Instruction::I32Add);
ctx.emit(Instruction::LocalGet(count_a));
ctx.emit(Instruction::I32Const(8));
ctx.emit(Instruction::I32Mul);
ctx.emit(Instruction::I32Add);
ctx.emit(Instruction::LocalSet(dst_base_b));
let src_base_b = emit_addr_offset(ctx, src_b, 8);
emit_copy_f64_loop(ctx, dst_base_b, src_base_b, count_b);
ctx.ptr_to_f64(dst);
Ok(())
}
pub(super) fn emit_list_assoc(ctx: &mut WasmGenCtx, args: &[Calcit]) -> Result<(), String> {
expect_arity(3, args, "&list:assoc expects 3 args")?;
let src = emit_ptr_to_i32(ctx, &args[0])?;
let count = emit_load_count_i32(ctx, src);
let idx = ctx.alloc_local_typed(ValType::I32);
emit_expr(ctx, &args[1])?;
ctx.emit(Instruction::I32TruncF64U);
ctx.emit(Instruction::LocalSet(idx));
let val = ctx.alloc_local();
emit_expr(ctx, &args[2])?;
ctx.emit(Instruction::LocalSet(val));
let dst = emit_alloc_list(ctx, count);
let dst_base = emit_addr_offset(ctx, dst, 8);
let src_base = emit_addr_offset(ctx, src, 8);
emit_copy_f64_loop(ctx, dst_base, src_base, count);
emit_list_store_elem(ctx, dst, idx, val);
ctx.ptr_to_f64(dst);
Ok(())
}
pub(super) fn emit_list_assoc_before(ctx: &mut WasmGenCtx, args: &[Calcit]) -> Result<(), String> {
expect_arity(3, args, "&list:assoc-before expects 3 args")?;
let src = emit_ptr_to_i32(ctx, &args[0])?;
let count = emit_load_count_i32(ctx, src);
let idx = ctx.alloc_local_typed(ValType::I32);
emit_expr(ctx, &args[1])?;
ctx.emit(Instruction::I32TruncF64U);
ctx.emit(Instruction::LocalSet(idx));
let val = ctx.alloc_local();
emit_expr(ctx, &args[2])?;
ctx.emit(Instruction::LocalSet(val));
let new_count = ctx.i32_offset(count, 1);
let dst = emit_alloc_list(ctx, new_count);
let before_n = ctx.alloc_local_typed(ValType::I32);
ctx.emit(Instruction::LocalGet(idx));
ctx.emit(Instruction::LocalSet(before_n));
let dst_b1 = emit_addr_offset(ctx, dst, 8);
let src_b1 = emit_addr_offset(ctx, src, 8);
emit_copy_f64_loop(ctx, dst_b1, src_b1, before_n);
emit_list_store_elem(ctx, dst, idx, val);
let after_n = ctx.alloc_local_typed(ValType::I32);
ctx.emit(Instruction::LocalGet(count));
ctx.emit(Instruction::LocalGet(idx));
ctx.emit(Instruction::I32Sub);
ctx.emit(Instruction::LocalSet(after_n));
let dst_b2 = ctx.alloc_local_typed(ValType::I32);
ctx.emit(Instruction::LocalGet(dst));
ctx.emit(Instruction::I32Const(8));
ctx.emit(Instruction::I32Add);
ctx.emit(Instruction::LocalGet(idx));
ctx.emit(Instruction::I32Const(1));
ctx.emit(Instruction::I32Add);
ctx.emit(Instruction::I32Const(8));
ctx.emit(Instruction::I32Mul);
ctx.emit(Instruction::I32Add);
ctx.emit(Instruction::LocalSet(dst_b2));
let src_b2 = ctx.alloc_local_typed(ValType::I32);
ctx.emit(Instruction::LocalGet(src));
ctx.emit(Instruction::I32Const(8));
ctx.emit(Instruction::I32Add);
ctx.emit(Instruction::LocalGet(idx));
ctx.emit(Instruction::I32Const(8));
ctx.emit(Instruction::I32Mul);
ctx.emit(Instruction::I32Add);
ctx.emit(Instruction::LocalSet(src_b2));
emit_copy_f64_loop(ctx, dst_b2, src_b2, after_n);
ctx.ptr_to_f64(dst);
Ok(())
}
pub(super) fn emit_list_assoc_after(ctx: &mut WasmGenCtx, args: &[Calcit]) -> Result<(), String> {
expect_arity(3, args, "&list:assoc-after expects 3 args")?;
let src = emit_ptr_to_i32(ctx, &args[0])?;
let count = emit_load_count_i32(ctx, src);
let idx = ctx.alloc_local_typed(ValType::I32);
emit_expr(ctx, &args[1])?;
ctx.emit(Instruction::I32TruncF64U);
ctx.emit(Instruction::I32Const(1));
ctx.emit(Instruction::I32Add); ctx.emit(Instruction::LocalSet(idx));
let val = ctx.alloc_local();
emit_expr(ctx, &args[2])?;
ctx.emit(Instruction::LocalSet(val));
let new_count = ctx.i32_offset(count, 1);
let dst = emit_alloc_list(ctx, new_count);
let before_n = ctx.alloc_local_typed(ValType::I32);
ctx.emit(Instruction::LocalGet(idx));
ctx.emit(Instruction::LocalSet(before_n));
let dst_b1 = emit_addr_offset(ctx, dst, 8);
let src_b1 = emit_addr_offset(ctx, src, 8);
emit_copy_f64_loop(ctx, dst_b1, src_b1, before_n);
emit_list_store_elem(ctx, dst, idx, val);
let after_n = ctx.alloc_local_typed(ValType::I32);
ctx.emit(Instruction::LocalGet(count));
ctx.emit(Instruction::LocalGet(idx));
ctx.emit(Instruction::I32Sub);
ctx.emit(Instruction::LocalSet(after_n));
let dst_b2 = ctx.alloc_local_typed(ValType::I32);
ctx.emit(Instruction::LocalGet(dst));
ctx.emit(Instruction::I32Const(8));
ctx.emit(Instruction::I32Add);
ctx.emit(Instruction::LocalGet(idx));
ctx.emit(Instruction::I32Const(1));
ctx.emit(Instruction::I32Add);
ctx.emit(Instruction::I32Const(8));
ctx.emit(Instruction::I32Mul);
ctx.emit(Instruction::I32Add);
ctx.emit(Instruction::LocalSet(dst_b2));
let src_b2 = ctx.alloc_local_typed(ValType::I32);
ctx.emit(Instruction::LocalGet(src));
ctx.emit(Instruction::I32Const(8));
ctx.emit(Instruction::I32Add);
ctx.emit(Instruction::LocalGet(idx));
ctx.emit(Instruction::I32Const(8));
ctx.emit(Instruction::I32Mul);
ctx.emit(Instruction::I32Add);
ctx.emit(Instruction::LocalSet(src_b2));
emit_copy_f64_loop(ctx, dst_b2, src_b2, after_n);
ctx.ptr_to_f64(dst);
Ok(())
}
pub(super) fn emit_list_to_set(ctx: &mut WasmGenCtx, args: &[Calcit]) -> Result<(), String> {
expect_arity(1, args, "&list:to-set expects 1 arg")?;
let src = emit_ptr_to_i32(ctx, &args[0])?;
let n = emit_load_count_i32(ctx, src);
let total_slots = ctx.i32_offset(n, 1);
let dst = emit_alloc_with_count(ctx, n, total_slots, "set");
let write_idx = ctx.alloc_i32(0);
let i = ctx.alloc_i32(0);
ctx.begin_block();
ctx.begin_loop();
ctx.loop_exit_if_ge(i, n);
let elem = ctx.alloc_local();
emit_list_load_elem(ctx, src, i);
ctx.emit(Instruction::LocalSet(elem));
let j = ctx.alloc_i32(0);
let found = ctx.alloc_i32(0);
ctx.begin_block();
ctx.begin_loop();
ctx.loop_exit_if_ge(j, write_idx);
emit_list_load_elem(ctx, dst, j);
ctx.emit(Instruction::LocalGet(elem));
ctx.emit(Instruction::F64Eq);
ctx.begin_block_if();
ctx.emit(Instruction::I32Const(1));
ctx.emit(Instruction::LocalSet(found));
ctx.emit(Instruction::Br(2));
ctx.emit(Instruction::End);
ctx.i32_inc(j);
ctx.emit(Instruction::Br(0));
ctx.emit(Instruction::End);
ctx.emit(Instruction::End);
ctx.emit(Instruction::LocalGet(found));
ctx.emit(Instruction::I32Eqz);
ctx.begin_block_if();
{
let write_addr = ctx.alloc_local_typed(ValType::I32);
ctx.emit(Instruction::LocalGet(dst));
ctx.emit(Instruction::LocalGet(write_idx));
ctx.emit(Instruction::I32Const(1));
ctx.emit(Instruction::I32Add);
ctx.emit(Instruction::I32Const(8));
ctx.emit(Instruction::I32Mul);
ctx.emit(Instruction::I32Add);
ctx.emit(Instruction::LocalSet(write_addr));
ctx.emit(Instruction::LocalGet(write_addr));
ctx.emit(Instruction::LocalGet(elem));
ctx.emit(Instruction::F64Store(mem_arg_f64(0)));
ctx.i32_inc(write_idx);
}
ctx.emit(Instruction::End);
ctx.i32_inc(i);
ctx.emit(Instruction::Br(0));
ctx.emit(Instruction::End);
ctx.emit(Instruction::End);
ctx.emit(Instruction::LocalGet(dst));
ctx.ptr_to_f64(write_idx);
ctx.emit(Instruction::F64Store(mem_arg_f64(0)));
ctx.ptr_to_f64(dst);
Ok(())
}
pub(super) fn emit_list_dissoc(ctx: &mut WasmGenCtx, args: &[Calcit]) -> Result<(), String> {
use crate::calcit::CalcitSyntax;
let (list_arg, idx_arg): (&Calcit, Option<&Calcit>) =
if args.len() == 3 && matches!(args[1], Calcit::Syntax(CalcitSyntax::ArgSpread, _)) {
(&args[0], None) } else if args.len() == 2 {
(&args[0], Some(&args[1]))
} else {
return Err("&list:dissoc expects 2 args".into());
};
let src = emit_ptr_to_i32(ctx, list_arg)?;
let count = emit_load_count_i32(ctx, src);
let idx = ctx.alloc_local_typed(ValType::I32);
if let Some(idx_expr) = idx_arg {
emit_expr(ctx, idx_expr)?;
ctx.emit(Instruction::I32TruncF64U);
ctx.emit(Instruction::LocalSet(idx));
} else {
let rest_list_f64 = ctx.alloc_local();
emit_expr(ctx, &args[2])?;
ctx.emit(Instruction::LocalSet(rest_list_f64));
let rest_list_i32 = ctx.alloc_local_typed(ValType::I32);
ctx.emit(Instruction::LocalGet(rest_list_f64));
ctx.emit(Instruction::I32TruncF64U);
ctx.emit(Instruction::LocalSet(rest_list_i32));
ctx.emit(Instruction::LocalGet(rest_list_i32));
ctx.emit(Instruction::I32Const(8));
ctx.emit(Instruction::I32Add);
ctx.emit(Instruction::F64Load(mem_arg_f64(0)));
ctx.emit(Instruction::I32TruncF64U);
ctx.emit(Instruction::LocalSet(idx));
}
let new_count = ctx.i32_offset(count, -1);
let dst = emit_alloc_list(ctx, new_count);
let before_n = ctx.alloc_local_typed(ValType::I32);
ctx.emit(Instruction::LocalGet(idx));
ctx.emit(Instruction::LocalSet(before_n));
let dst_b1 = emit_addr_offset(ctx, dst, 8);
let src_b1 = emit_addr_offset(ctx, src, 8);
emit_copy_f64_loop(ctx, dst_b1, src_b1, before_n);
let after_n = ctx.alloc_local_typed(ValType::I32);
ctx.emit(Instruction::LocalGet(count));
ctx.emit(Instruction::LocalGet(idx));
ctx.emit(Instruction::I32Sub);
ctx.emit(Instruction::I32Const(1));
ctx.emit(Instruction::I32Sub);
ctx.emit(Instruction::LocalSet(after_n));
let dst_b2 = ctx.alloc_local_typed(ValType::I32);
ctx.emit(Instruction::LocalGet(dst));
ctx.emit(Instruction::I32Const(8));
ctx.emit(Instruction::I32Add);
ctx.emit(Instruction::LocalGet(idx));
ctx.emit(Instruction::I32Const(8));
ctx.emit(Instruction::I32Mul);
ctx.emit(Instruction::I32Add);
ctx.emit(Instruction::LocalSet(dst_b2));
let src_b2 = ctx.alloc_local_typed(ValType::I32);
ctx.emit(Instruction::LocalGet(src));
ctx.emit(Instruction::I32Const(8));
ctx.emit(Instruction::I32Add);
ctx.emit(Instruction::LocalGet(idx));
ctx.emit(Instruction::I32Const(1));
ctx.emit(Instruction::I32Add);
ctx.emit(Instruction::I32Const(8));
ctx.emit(Instruction::I32Mul);
ctx.emit(Instruction::I32Add);
ctx.emit(Instruction::LocalSet(src_b2));
emit_copy_f64_loop(ctx, dst_b2, src_b2, after_n);
ctx.ptr_to_f64(dst);
Ok(())
}
pub(super) fn emit_list_q(ctx: &mut WasmGenCtx, args: &[Calcit]) -> Result<(), String> {
expect_arity(1, args, "list?")?;
let list_tag = get_type_tag(ctx, "list");
ctx.emit(f64_const(1.0));
ctx.emit(f64_const(0.0));
emit_type_of(ctx, args)?;
ctx.emit(f64_const(list_tag));
ctx.emit(Instruction::F64Eq);
ctx.emit(Instruction::Select);
Ok(())
}
pub(super) fn emit_list_contains(ctx: &mut WasmGenCtx, args: &[Calcit]) -> Result<(), String> {
expect_arity(2, args, "&list:contains? expects 2 args")?;
let ptr = emit_ptr_to_i32(ctx, &args[0])?;
let count = emit_load_count_i32(ctx, ptr);
ctx.emit(f64_const(1.0));
ctx.emit(f64_const(0.0));
emit_expr(ctx, &args[1])?;
ctx.emit(Instruction::I32TruncF64U);
ctx.emit(Instruction::LocalGet(count));
ctx.emit(Instruction::I32LtU);
ctx.emit(Instruction::Select);
Ok(())
}
pub(super) fn emit_list_includes(ctx: &mut WasmGenCtx, args: &[Calcit]) -> Result<(), String> {
expect_arity(2, args, "&list:includes? expects 2 args")?;
let ptr = emit_ptr_to_i32(ctx, &args[0])?;
let count = emit_load_count_i32(ctx, ptr);
let target = ctx.alloc_local();
emit_expr(ctx, &args[1])?;
ctx.emit(Instruction::LocalSet(target));
let result = ctx.alloc_local();
ctx.emit(f64_const(0.0)); ctx.emit(Instruction::LocalSet(result));
let i = ctx.alloc_i32(0);
ctx.begin_block();
ctx.begin_loop();
ctx.loop_exit_if_ge(i, count);
ctx.emit(Instruction::LocalGet(ptr));
ctx.emit(Instruction::I32Const(8));
ctx.emit(Instruction::I32Add);
ctx.emit(Instruction::LocalGet(i));
ctx.emit(Instruction::I32Const(8));
ctx.emit(Instruction::I32Mul);
ctx.emit(Instruction::I32Add);
ctx.emit(Instruction::F64Load(mem_arg_f64(0)));
ctx.emit(Instruction::LocalGet(target));
ctx.emit(Instruction::F64Eq);
ctx.begin_block_if();
ctx.emit(f64_const(1.0));
ctx.emit(Instruction::LocalSet(result));
ctx.emit(Instruction::Br(2)); ctx.emit(Instruction::End);
ctx.i32_inc(i);
ctx.br_loop();
ctx.end_block_loop();
ctx.emit(Instruction::LocalGet(result));
Ok(())
}
pub(super) const BUF_LIST_INITIAL_CAPACITY: i32 = 8;
pub(super) fn emit_buf_list_new(ctx: &mut WasmGenCtx, args: &[Calcit]) -> Result<(), String> {
if !args.is_empty() {
return Err("&buf-list:new expects 0 args".into());
}
let total_slots = 2 + BUF_LIST_INITIAL_CAPACITY;
let byte_size = total_slots * 8;
let ptr = ctx.alloc_local_typed(ValType::I32);
emit_bump_alloc(ctx, byte_size, ptr, "buf-list");
ctx.emit(Instruction::LocalGet(ptr));
ctx.emit(f64_const(BUF_LIST_INITIAL_CAPACITY as f64));
ctx.emit(Instruction::F64Store(mem_arg_f64(0)));
ctx.emit(Instruction::LocalGet(ptr));
ctx.emit(f64_const(0.0));
ctx.emit(Instruction::F64Store(mem_arg_f64(8)));
ctx.ptr_to_f64(ptr);
Ok(())
}
pub(super) fn emit_buf_list_push(ctx: &mut WasmGenCtx, args: &[Calcit]) -> Result<(), String> {
expect_arity(2, args, "&buf-list:push expects 2 args")?;
let buf_ptr = emit_ptr_to_i32(ctx, &args[0])?;
let count = ctx.alloc_local_typed(ValType::I32);
ctx.emit(Instruction::LocalGet(buf_ptr));
ctx.emit(Instruction::F64Load(mem_arg_f64(8)));
ctx.emit(Instruction::I32TruncF64U);
ctx.emit(Instruction::LocalSet(count));
let capacity = ctx.alloc_local_typed(ValType::I32);
ctx.emit(Instruction::LocalGet(buf_ptr));
ctx.emit(Instruction::F64Load(mem_arg_f64(0)));
ctx.emit(Instruction::I32TruncF64U);
ctx.emit(Instruction::LocalSet(capacity));
ctx.emit(Instruction::LocalGet(count));
ctx.emit(Instruction::LocalGet(capacity));
ctx.emit(Instruction::I32GeU);
ctx.begin_block_if();
{
let new_cap = ctx.alloc_local_typed(ValType::I32);
ctx.emit(Instruction::LocalGet(capacity));
ctx.emit(Instruction::I32Const(2));
ctx.emit(Instruction::I32Mul);
ctx.emit(Instruction::LocalSet(new_cap));
let new_size = ctx.alloc_local_typed(ValType::I32);
ctx.emit(Instruction::LocalGet(new_cap));
ctx.emit(Instruction::I32Const(2));
ctx.emit(Instruction::I32Add);
ctx.emit(Instruction::I32Const(8));
ctx.emit(Instruction::I32Mul);
ctx.emit(Instruction::LocalSet(new_size));
let new_ptr = ctx.alloc_local_typed(ValType::I32);
emit_bump_alloc_dynamic(ctx, new_size, new_ptr, "buf-list");
ctx.emit(Instruction::LocalGet(new_ptr));
ctx.ptr_to_f64(new_cap);
ctx.emit(Instruction::F64Store(mem_arg_f64(0)));
ctx.emit(Instruction::LocalGet(new_ptr));
ctx.ptr_to_f64(count);
ctx.emit(Instruction::F64Store(mem_arg_f64(8)));
let dst_base = emit_addr_offset(ctx, new_ptr, 16);
let src_base = emit_addr_offset(ctx, buf_ptr, 16);
emit_copy_f64_loop(ctx, dst_base, src_base, count);
ctx.emit(Instruction::LocalGet(new_ptr));
ctx.emit(Instruction::LocalSet(buf_ptr));
}
ctx.emit(Instruction::End);
let elem_addr = ctx.alloc_local_typed(ValType::I32);
ctx.emit(Instruction::LocalGet(buf_ptr));
ctx.emit(Instruction::I32Const(16));
ctx.emit(Instruction::I32Add);
ctx.emit(Instruction::LocalGet(count));
ctx.emit(Instruction::I32Const(8));
ctx.emit(Instruction::I32Mul);
ctx.emit(Instruction::I32Add);
ctx.emit(Instruction::LocalSet(elem_addr));
ctx.emit(Instruction::LocalGet(elem_addr));
emit_expr(ctx, &args[1])?;
ctx.emit(Instruction::F64Store(mem_arg_f64(0)));
let new_count = ctx.i32_offset(count, 1);
ctx.emit(Instruction::LocalGet(buf_ptr));
ctx.ptr_to_f64(new_count);
ctx.emit(Instruction::F64Store(mem_arg_f64(8)));
ctx.ptr_to_f64(buf_ptr);
Ok(())
}
pub(super) fn emit_buf_list_concat(ctx: &mut WasmGenCtx, args: &[Calcit]) -> Result<(), String> {
expect_arity(2, args, "&buf-list:concat expects 2 args")?;
let buf_ptr = emit_ptr_to_i32(ctx, &args[0])?;
let list_ptr = emit_ptr_to_i32(ctx, &args[1])?;
let list_count = emit_load_count_i32(ctx, list_ptr);
let i = ctx.alloc_i32(0);
ctx.begin_block(); ctx.begin_loop();
ctx.loop_exit_if_ge(i, list_count);
let b_count = ctx.alloc_local_typed(ValType::I32);
ctx.emit(Instruction::LocalGet(buf_ptr));
ctx.emit(Instruction::F64Load(mem_arg_f64(8)));
ctx.emit(Instruction::I32TruncF64U);
ctx.emit(Instruction::LocalSet(b_count));
let b_cap = ctx.alloc_local_typed(ValType::I32);
ctx.emit(Instruction::LocalGet(buf_ptr));
ctx.emit(Instruction::F64Load(mem_arg_f64(0)));
ctx.emit(Instruction::I32TruncF64U);
ctx.emit(Instruction::LocalSet(b_cap));
ctx.emit(Instruction::LocalGet(b_count));
ctx.emit(Instruction::LocalGet(b_cap));
ctx.emit(Instruction::I32GeU);
ctx.begin_block_if();
{
let new_cap = ctx.alloc_local_typed(ValType::I32);
ctx.emit(Instruction::LocalGet(b_cap));
ctx.emit(Instruction::I32Const(2));
ctx.emit(Instruction::I32Mul);
ctx.emit(Instruction::LocalSet(new_cap));
let needed = ctx.alloc_local_typed(ValType::I32);
ctx.emit(Instruction::LocalGet(b_count));
ctx.emit(Instruction::LocalGet(list_count));
ctx.emit(Instruction::I32Add);
ctx.emit(Instruction::LocalSet(needed));
ctx.emit(Instruction::LocalGet(new_cap));
ctx.emit(Instruction::LocalGet(needed));
ctx.emit(Instruction::I32LtU);
ctx.begin_block_if();
ctx.emit(Instruction::LocalGet(needed));
ctx.emit(Instruction::LocalSet(new_cap));
ctx.emit(Instruction::End);
let new_size = ctx.alloc_local_typed(ValType::I32);
ctx.emit(Instruction::LocalGet(new_cap));
ctx.emit(Instruction::I32Const(2));
ctx.emit(Instruction::I32Add);
ctx.emit(Instruction::I32Const(8));
ctx.emit(Instruction::I32Mul);
ctx.emit(Instruction::LocalSet(new_size));
let new_ptr = ctx.alloc_local_typed(ValType::I32);
emit_bump_alloc_dynamic(ctx, new_size, new_ptr, "buf-list");
ctx.emit(Instruction::LocalGet(new_ptr));
ctx.ptr_to_f64(new_cap);
ctx.emit(Instruction::F64Store(mem_arg_f64(0)));
ctx.emit(Instruction::LocalGet(new_ptr));
ctx.ptr_to_f64(b_count);
ctx.emit(Instruction::F64Store(mem_arg_f64(8)));
let dst_base = emit_addr_offset(ctx, new_ptr, 16);
let src_base = emit_addr_offset(ctx, buf_ptr, 16);
emit_copy_f64_loop(ctx, dst_base, src_base, b_count);
ctx.emit(Instruction::LocalGet(new_ptr));
ctx.emit(Instruction::LocalSet(buf_ptr));
}
ctx.emit(Instruction::End);
ctx.emit(Instruction::LocalGet(buf_ptr));
ctx.emit(Instruction::F64Load(mem_arg_f64(8)));
ctx.emit(Instruction::I32TruncF64U);
ctx.emit(Instruction::LocalSet(b_count));
let elem_addr = ctx.alloc_local_typed(ValType::I32);
ctx.emit(Instruction::LocalGet(buf_ptr));
ctx.emit(Instruction::I32Const(16));
ctx.emit(Instruction::I32Add);
ctx.emit(Instruction::LocalGet(b_count));
ctx.emit(Instruction::I32Const(8));
ctx.emit(Instruction::I32Mul);
ctx.emit(Instruction::I32Add);
ctx.emit(Instruction::LocalSet(elem_addr));
let list_elem_addr = ctx.alloc_local_typed(ValType::I32);
ctx.emit(Instruction::LocalGet(list_ptr));
ctx.emit(Instruction::I32Const(8));
ctx.emit(Instruction::I32Add);
ctx.emit(Instruction::LocalGet(i));
ctx.emit(Instruction::I32Const(8));
ctx.emit(Instruction::I32Mul);
ctx.emit(Instruction::I32Add);
ctx.emit(Instruction::LocalSet(list_elem_addr));
ctx.emit(Instruction::LocalGet(elem_addr));
ctx.emit(Instruction::LocalGet(list_elem_addr));
ctx.emit(Instruction::F64Load(mem_arg_f64(0)));
ctx.emit(Instruction::F64Store(mem_arg_f64(0)));
let new_b_count = ctx.i32_offset(b_count, 1);
ctx.emit(Instruction::LocalGet(buf_ptr));
ctx.ptr_to_f64(new_b_count);
ctx.emit(Instruction::F64Store(mem_arg_f64(8)));
ctx.i32_inc(i);
ctx.br_loop();
ctx.end_block_loop();
ctx.ptr_to_f64(buf_ptr);
Ok(())
}
pub(super) fn emit_buf_list_to_list(ctx: &mut WasmGenCtx, args: &[Calcit]) -> Result<(), String> {
expect_arity(1, args, "&buf-list:to-list expects 1 arg")?;
let buf_ptr = emit_ptr_to_i32(ctx, &args[0])?;
let count = ctx.alloc_local_typed(ValType::I32);
ctx.emit(Instruction::LocalGet(buf_ptr));
ctx.emit(Instruction::F64Load(mem_arg_f64(8)));
ctx.emit(Instruction::I32TruncF64U);
ctx.emit(Instruction::LocalSet(count));
let dst = emit_alloc_list(ctx, count);
let dst_base = emit_addr_offset(ctx, dst, 8);
let src_base = emit_addr_offset(ctx, buf_ptr, 16);
emit_copy_f64_loop(ctx, dst_base, src_base, count);
ctx.ptr_to_f64(dst);
Ok(())
}
pub(super) fn emit_buf_list_count(ctx: &mut WasmGenCtx, args: &[Calcit]) -> Result<(), String> {
expect_arity(1, args, "&buf-list:count expects 1 arg")?;
let buf_ptr = emit_ptr_to_i32(ctx, &args[0])?;
ctx.emit(Instruction::LocalGet(buf_ptr));
ctx.emit(Instruction::F64Load(mem_arg_f64(8))); Ok(())
}
pub(super) fn emit_range(ctx: &mut WasmGenCtx, args: &[Calcit]) -> Result<(), String> {
if args.is_empty() || args.len() > 3 {
return Err("range expects 1, 2, or 3 args".into());
}
if args.len() == 3 {
let start = ctx.alloc_local();
let end = ctx.alloc_local();
let step = ctx.alloc_local();
emit_expr(ctx, &args[0])?;
ctx.emit(Instruction::LocalSet(start));
emit_expr(ctx, &args[1])?;
ctx.emit(Instruction::LocalSet(end));
emit_expr(ctx, &args[2])?;
ctx.emit(Instruction::LocalSet(step));
let raw_count_f = ctx.alloc_local();
ctx.emit(Instruction::LocalGet(end));
ctx.emit(Instruction::LocalGet(start));
ctx.emit(Instruction::F64Sub);
ctx.emit(Instruction::LocalGet(step));
ctx.emit(Instruction::F64Div);
ctx.emit(Instruction::F64Ceil);
ctx.emit(Instruction::LocalSet(raw_count_f));
let count = ctx.alloc_local_typed(ValType::I32);
ctx.emit(Instruction::LocalGet(raw_count_f));
ctx.emit(f64_const(0.0));
ctx.emit(Instruction::F64Gt);
ctx.begin_block_if();
ctx.emit(Instruction::LocalGet(raw_count_f));
ctx.emit(Instruction::I32TruncF64S);
ctx.emit(Instruction::LocalSet(count));
ctx.emit(Instruction::End);
let dst = emit_alloc_list(ctx, count);
let i = ctx.alloc_i32(0);
ctx.begin_block();
ctx.begin_loop();
ctx.loop_exit_if_ge(i, count);
ctx.emit(Instruction::LocalGet(dst));
ctx.emit(Instruction::I32Const(8));
ctx.emit(Instruction::I32Add);
ctx.emit(Instruction::LocalGet(i));
ctx.emit(Instruction::I32Const(8));
ctx.emit(Instruction::I32Mul);
ctx.emit(Instruction::I32Add);
ctx.emit(Instruction::LocalGet(start));
ctx.emit(Instruction::LocalGet(i));
ctx.emit(Instruction::F64ConvertI32U);
ctx.emit(Instruction::LocalGet(step));
ctx.emit(Instruction::F64Mul);
ctx.emit(Instruction::F64Add);
ctx.emit(Instruction::F64Store(mem_arg_f64(0)));
ctx.i32_inc(i);
ctx.emit(Instruction::Br(0));
ctx.emit(Instruction::End);
ctx.emit(Instruction::End);
ctx.ptr_to_f64(dst);
return Ok(());
}
let start = ctx.alloc_local();
let end = ctx.alloc_local();
if args.len() == 1 {
ctx.emit(f64_const(0.0));
ctx.emit(Instruction::LocalSet(start));
emit_expr(ctx, &args[0])?;
ctx.emit(Instruction::LocalSet(end));
} else {
emit_expr(ctx, &args[0])?;
ctx.emit(Instruction::LocalSet(start));
emit_expr(ctx, &args[1])?;
ctx.emit(Instruction::LocalSet(end));
}
let count = ctx.alloc_local_typed(ValType::I32);
let raw_count = ctx.alloc_local_typed(ValType::I32);
ctx.emit(Instruction::LocalGet(end));
ctx.emit(Instruction::I32TruncF64S);
ctx.emit(Instruction::LocalGet(start));
ctx.emit(Instruction::I32TruncF64S);
ctx.emit(Instruction::I32Sub);
ctx.emit(Instruction::LocalSet(raw_count));
ctx.emit(Instruction::LocalGet(raw_count)); ctx.emit(Instruction::I32Const(0)); ctx.emit(Instruction::LocalGet(raw_count));
ctx.emit(Instruction::I32Const(0));
ctx.emit(Instruction::I32GtS); ctx.emit(Instruction::Select);
ctx.emit(Instruction::LocalSet(count));
let dst = emit_alloc_list(ctx, count);
let i = ctx.alloc_i32(0);
ctx.begin_block();
ctx.begin_loop();
ctx.loop_exit_if_ge(i, count);
ctx.emit(Instruction::LocalGet(dst));
ctx.emit(Instruction::I32Const(8));
ctx.emit(Instruction::I32Add);
ctx.emit(Instruction::LocalGet(i));
ctx.emit(Instruction::I32Const(8));
ctx.emit(Instruction::I32Mul);
ctx.emit(Instruction::I32Add);
ctx.emit(Instruction::LocalGet(start));
ctx.ptr_to_f64(i);
ctx.emit(Instruction::F64Add);
ctx.emit(Instruction::F64Store(mem_arg_f64(0)));
ctx.i32_inc(i);
ctx.emit(Instruction::Br(0));
ctx.emit(Instruction::End);
ctx.emit(Instruction::End);
ctx.ptr_to_f64(dst);
Ok(())
}
pub(super) fn emit_list_distinct(ctx: &mut WasmGenCtx, args: &[Calcit]) -> Result<(), String> {
expect_arity(1, args, "&list:distinct expects 1 arg")?;
let src = emit_ptr_to_i32(ctx, &args[0])?;
let n = emit_load_count_i32(ctx, src);
let dst = emit_alloc_list(ctx, n);
let write_idx = ctx.alloc_i32(0);
let i = ctx.alloc_i32(0);
ctx.begin_block();
ctx.begin_loop();
ctx.loop_exit_if_ge(i, n);
let elem = ctx.alloc_local();
emit_list_load_elem(ctx, src, i);
ctx.emit(Instruction::LocalSet(elem));
let j = ctx.alloc_i32(0);
let found = ctx.alloc_i32(0);
ctx.begin_block();
ctx.begin_loop();
ctx.loop_exit_if_ge(j, write_idx);
emit_list_load_elem(ctx, dst, j);
ctx.emit(Instruction::LocalGet(elem));
ctx.emit(Instruction::F64Eq);
ctx.begin_block_if();
ctx.emit(Instruction::I32Const(1));
ctx.emit(Instruction::LocalSet(found));
ctx.emit(Instruction::Br(2)); ctx.emit(Instruction::End);
ctx.i32_inc(j);
ctx.br_loop();
ctx.end_block_loop();
ctx.emit(Instruction::LocalGet(found));
ctx.emit(Instruction::I32Eqz);
ctx.begin_block_if();
{
let write_addr = ctx.alloc_local_typed(ValType::I32);
ctx.emit(Instruction::LocalGet(dst));
ctx.emit(Instruction::LocalGet(write_idx));
ctx.emit(Instruction::I32Const(1));
ctx.emit(Instruction::I32Add);
ctx.emit(Instruction::I32Const(8));
ctx.emit(Instruction::I32Mul);
ctx.emit(Instruction::I32Add);
ctx.emit(Instruction::LocalSet(write_addr));
ctx.emit(Instruction::LocalGet(write_addr));
ctx.emit(Instruction::LocalGet(elem));
ctx.emit(Instruction::F64Store(mem_arg_f64(0)));
ctx.i32_inc(write_idx);
}
ctx.emit(Instruction::End);
ctx.i32_inc(i);
ctx.br_loop();
ctx.end_block_loop();
ctx.emit(Instruction::LocalGet(dst));
ctx.ptr_to_f64(write_idx);
ctx.emit(Instruction::F64Store(mem_arg_f64(0)));
ctx.ptr_to_f64(dst);
Ok(())
}
fn emit_append_from_local(ctx: &mut WasmGenCtx, list_local: u32, elem_local: u32) -> u32 {
let src = ctx.alloc_local_typed(ValType::I32);
ctx.emit(Instruction::LocalGet(list_local));
ctx.emit(Instruction::I32TruncF64U);
ctx.emit(Instruction::LocalSet(src));
let old_count = emit_load_count_i32(ctx, src);
let new_count = ctx.i32_offset(old_count, 1);
let dst = emit_alloc_list(ctx, new_count);
let dst_base = emit_addr_offset(ctx, dst, 8);
let src_base = emit_addr_offset(ctx, src, 8);
emit_copy_f64_loop(ctx, dst_base, src_base, old_count);
emit_list_store_elem(ctx, dst, old_count, elem_local);
let result = ctx.alloc_local();
ctx.ptr_to_f64(dst);
ctx.emit(Instruction::LocalSet(result));
result
}
pub(super) fn emit_conj(ctx: &mut WasmGenCtx, args: &[Calcit]) -> Result<(), String> {
if args.len() < 2 {
return Err("conj expects at least 2 args".into());
}
let acc = ctx.alloc_local();
emit_expr(ctx, &args[0])?;
ctx.emit(Instruction::LocalSet(acc));
for arg in &args[1..] {
let elem = ctx.alloc_local();
emit_expr(ctx, arg)?;
ctx.emit(Instruction::LocalSet(elem));
let new_acc = emit_append_from_local(ctx, acc, elem);
ctx.emit(Instruction::LocalGet(new_acc));
ctx.emit(Instruction::LocalSet(acc));
}
ctx.emit(Instruction::LocalGet(acc));
Ok(())
}
pub(super) fn emit_repeat_from_locals(ctx: &mut WasmGenCtx, x_local: u32, n_local: u32) -> Result<(), String> {
let n_i32 = ctx.alloc_local_typed(ValType::I32);
ctx.emit(Instruction::LocalGet(n_local));
ctx.emit(Instruction::I32TruncF64U);
ctx.emit(Instruction::LocalSet(n_i32));
let dst = emit_alloc_list(ctx, n_i32);
let i = ctx.alloc_i32(0);
ctx.begin_block();
ctx.begin_loop();
ctx.loop_exit_if_ge(i, n_i32);
let addr = ctx.alloc_local_typed(ValType::I32);
ctx.emit(Instruction::LocalGet(dst));
ctx.emit(Instruction::LocalGet(i));
ctx.emit(Instruction::I32Const(1));
ctx.emit(Instruction::I32Add);
ctx.emit(Instruction::I32Const(8));
ctx.emit(Instruction::I32Mul);
ctx.emit(Instruction::I32Add);
ctx.emit(Instruction::LocalSet(addr));
ctx.emit(Instruction::LocalGet(addr));
ctx.emit(Instruction::LocalGet(x_local));
ctx.emit(Instruction::F64Store(mem_arg_f64(0)));
ctx.i32_inc(i);
ctx.emit(Instruction::Br(0));
ctx.emit(Instruction::End);
ctx.emit(Instruction::End);
ctx.ptr_to_f64(dst);
Ok(())
}
pub(super) fn emit_repeat(ctx: &mut WasmGenCtx, args: &[Calcit]) -> Result<(), String> {
expect_arity(2, args, "repeat")?;
let x = ctx.alloc_local();
emit_expr(ctx, &args[0])?;
ctx.emit(Instruction::LocalSet(x));
let n = ctx.alloc_local();
emit_expr(ctx, &args[1])?;
ctx.emit(Instruction::LocalSet(n));
emit_repeat_from_locals(ctx, x, n)
}
pub(super) fn emit_interleave_from_locals(ctx: &mut WasmGenCtx, xs_f64: u32, ys_f64: u32) -> Result<(), String> {
let xs_ptr = ctx.alloc_local_typed(ValType::I32);
ctx.emit(Instruction::LocalGet(xs_f64));
ctx.emit(Instruction::I32TruncF64U);
ctx.emit(Instruction::LocalSet(xs_ptr));
let ys_ptr = ctx.alloc_local_typed(ValType::I32);
ctx.emit(Instruction::LocalGet(ys_f64));
ctx.emit(Instruction::I32TruncF64U);
ctx.emit(Instruction::LocalSet(ys_ptr));
let xs_count = emit_load_count_i32(ctx, xs_ptr);
let ys_count = emit_load_count_i32(ctx, ys_ptr);
let min_count = ctx.alloc_local_typed(ValType::I32);
ctx.emit(Instruction::LocalGet(xs_count));
ctx.emit(Instruction::LocalGet(ys_count));
ctx.emit(Instruction::LocalGet(xs_count));
ctx.emit(Instruction::LocalGet(ys_count));
ctx.emit(Instruction::I32LtU);
ctx.emit(Instruction::Select);
ctx.emit(Instruction::LocalSet(min_count));
let result_count = ctx.alloc_local_typed(ValType::I32);
ctx.emit(Instruction::LocalGet(min_count));
ctx.emit(Instruction::I32Const(2));
ctx.emit(Instruction::I32Mul);
ctx.emit(Instruction::LocalSet(result_count));
let dst = emit_alloc_list(ctx, result_count);
let i = ctx.alloc_i32(0);
ctx.begin_block();
ctx.begin_loop();
ctx.loop_exit_if_ge(i, min_count);
let elem_xs = ctx.alloc_local();
emit_list_load_elem(ctx, xs_ptr, i);
ctx.emit(Instruction::LocalSet(elem_xs));
let elem_ys = ctx.alloc_local();
emit_list_load_elem(ctx, ys_ptr, i);
ctx.emit(Instruction::LocalSet(elem_ys));
ctx.emit(Instruction::LocalGet(dst));
ctx.emit(Instruction::LocalGet(i));
ctx.emit(Instruction::I32Const(2));
ctx.emit(Instruction::I32Mul);
ctx.emit(Instruction::I32Const(1));
ctx.emit(Instruction::I32Add);
ctx.emit(Instruction::I32Const(8));
ctx.emit(Instruction::I32Mul);
ctx.emit(Instruction::I32Add);
ctx.emit(Instruction::LocalGet(elem_xs));
ctx.emit(Instruction::F64Store(mem_arg_f64(0)));
ctx.emit(Instruction::LocalGet(dst));
ctx.emit(Instruction::LocalGet(i));
ctx.emit(Instruction::I32Const(2));
ctx.emit(Instruction::I32Mul);
ctx.emit(Instruction::I32Const(2));
ctx.emit(Instruction::I32Add);
ctx.emit(Instruction::I32Const(8));
ctx.emit(Instruction::I32Mul);
ctx.emit(Instruction::I32Add);
ctx.emit(Instruction::LocalGet(elem_ys));
ctx.emit(Instruction::F64Store(mem_arg_f64(0)));
ctx.i32_inc(i);
ctx.emit(Instruction::Br(0));
ctx.emit(Instruction::End);
ctx.emit(Instruction::End);
ctx.ptr_to_f64(dst);
Ok(())
}
pub(super) fn emit_interleave(ctx: &mut WasmGenCtx, args: &[Calcit]) -> Result<(), String> {
expect_arity(2, args, "interleave")?;
let xs = ctx.alloc_local();
emit_expr(ctx, &args[0])?;
ctx.emit(Instruction::LocalSet(xs));
let ys = ctx.alloc_local();
emit_expr(ctx, &args[1])?;
ctx.emit(Instruction::LocalSet(ys));
emit_interleave_from_locals(ctx, xs, ys)
}
pub(super) fn emit_join_from_locals(ctx: &mut WasmGenCtx, xs_f64: u32, sep_f64: u32) -> Result<(), String> {
let xs_ptr = ctx.alloc_local_typed(ValType::I32);
ctx.emit(Instruction::LocalGet(xs_f64));
ctx.emit(Instruction::I32TruncF64U);
ctx.emit(Instruction::LocalSet(xs_ptr));
let count = emit_load_count_i32(ctx, xs_ptr);
let result_count = ctx.alloc_local_typed(ValType::I32);
ctx.emit(Instruction::LocalGet(count));
ctx.emit(Instruction::I32Const(0));
ctx.emit(Instruction::I32Eq);
ctx.begin_block_if();
ctx.emit(Instruction::I32Const(0));
ctx.emit(Instruction::LocalSet(result_count));
ctx.emit(Instruction::Else);
ctx.emit(Instruction::LocalGet(count));
ctx.emit(Instruction::I32Const(2));
ctx.emit(Instruction::I32Mul);
ctx.emit(Instruction::I32Const(1));
ctx.emit(Instruction::I32Sub);
ctx.emit(Instruction::LocalSet(result_count));
ctx.emit(Instruction::End);
let dst = emit_alloc_list(ctx, result_count);
let i = ctx.alloc_i32(0);
let write_idx = ctx.alloc_i32(0);
ctx.begin_block();
ctx.begin_loop();
ctx.loop_exit_if_ge(i, count);
ctx.emit(Instruction::LocalGet(i));
ctx.emit(Instruction::I32Const(0));
ctx.emit(Instruction::I32GtU);
ctx.begin_block_if();
let sep_addr = ctx.alloc_local_typed(ValType::I32);
ctx.emit(Instruction::LocalGet(dst));
ctx.emit(Instruction::LocalGet(write_idx));
ctx.emit(Instruction::I32Const(1));
ctx.emit(Instruction::I32Add);
ctx.emit(Instruction::I32Const(8));
ctx.emit(Instruction::I32Mul);
ctx.emit(Instruction::I32Add);
ctx.emit(Instruction::LocalSet(sep_addr));
ctx.emit(Instruction::LocalGet(sep_addr));
ctx.emit(Instruction::LocalGet(sep_f64));
ctx.emit(Instruction::F64Store(mem_arg_f64(0)));
ctx.i32_inc(write_idx);
ctx.emit(Instruction::End);
let elem = ctx.alloc_local();
emit_list_load_elem(ctx, xs_ptr, i);
ctx.emit(Instruction::LocalSet(elem));
let elem_addr = ctx.alloc_local_typed(ValType::I32);
ctx.emit(Instruction::LocalGet(dst));
ctx.emit(Instruction::LocalGet(write_idx));
ctx.emit(Instruction::I32Const(1));
ctx.emit(Instruction::I32Add);
ctx.emit(Instruction::I32Const(8));
ctx.emit(Instruction::I32Mul);
ctx.emit(Instruction::I32Add);
ctx.emit(Instruction::LocalSet(elem_addr));
ctx.emit(Instruction::LocalGet(elem_addr));
ctx.emit(Instruction::LocalGet(elem));
ctx.emit(Instruction::F64Store(mem_arg_f64(0)));
ctx.i32_inc(write_idx);
ctx.i32_inc(i);
ctx.emit(Instruction::Br(0));
ctx.emit(Instruction::End);
ctx.emit(Instruction::End);
ctx.ptr_to_f64(dst);
Ok(())
}
pub(super) fn emit_join(ctx: &mut WasmGenCtx, args: &[Calcit]) -> Result<(), String> {
expect_arity(2, args, "join")?;
let xs = ctx.alloc_local();
emit_expr(ctx, &args[0])?;
ctx.emit(Instruction::LocalSet(xs));
let sep = ctx.alloc_local();
emit_expr(ctx, &args[1])?;
ctx.emit(Instruction::LocalSet(sep));
emit_join_from_locals(ctx, xs, sep)
}