use super::*;
pub(super) fn emit_map_new(ctx: &mut WasmGenCtx, args: &[Calcit]) -> Result<(), String> {
if args.len() % 2 != 0 {
return Err("&{} expects even number of args (key-value pairs)".into());
}
let count = args.len() / 2;
let count_local = ctx.alloc_local_typed(ValType::I32);
ctx.emit(Instruction::I32Const(count as i32));
ctx.emit(Instruction::LocalSet(count_local));
let total_slots = ctx.alloc_local_typed(ValType::I32);
ctx.emit(Instruction::LocalGet(count_local));
ctx.emit(Instruction::I32Const(2));
ctx.emit(Instruction::I32Mul);
ctx.emit(Instruction::I32Const(1));
ctx.emit(Instruction::I32Add);
ctx.emit(Instruction::LocalSet(total_slots));
let root = emit_alloc_with_count(ctx, count_local, total_slots, "map");
for i in 0..count {
ctx.emit(Instruction::LocalGet(root));
emit_expr(ctx, &args[i * 2])?;
ctx.emit(Instruction::F64Store(mem_arg_f64((8 + i * 16) as u64)));
ctx.emit(Instruction::LocalGet(root));
emit_expr(ctx, &args[i * 2 + 1])?;
ctx.emit(Instruction::F64Store(mem_arg_f64((16 + i * 16) as u64)));
}
ctx.emit(Instruction::LocalGet(root));
ctx.call_rt("__rt_map_root_from_flat");
let hashed_root = ctx.alloc_local_typed(ValType::I32);
ctx.emit(Instruction::LocalSet(hashed_root));
let ptr = emit_alloc_map_with_root(ctx, count_local, hashed_root);
ctx.ptr_to_f64(ptr);
Ok(())
}
pub(super) fn emit_map_get_op(ctx: &mut WasmGenCtx, args: &[Calcit]) -> Result<(), String> {
expect_arity(2, args, "&map:get")?;
let ptr = emit_ptr_to_i32(ctx, &args[0])?;
let target = ctx.alloc_local();
emit_expr(ctx, &args[1])?;
ctx.emit(Instruction::LocalSet(target));
ctx.emit(Instruction::LocalGet(ptr));
ctx.emit(Instruction::LocalGet(target));
ctx.call_rt("__rt_map_get_value");
Ok(())
}
pub(super) fn emit_map_contains(ctx: &mut WasmGenCtx, args: &[Calcit]) -> Result<(), String> {
expect_arity(2, args, "&map:contains?")?;
let ptr = emit_ptr_to_i32(ctx, &args[0])?;
let target = ctx.alloc_local();
emit_expr(ctx, &args[1])?;
ctx.emit(Instruction::LocalSet(target));
let found_idx = emit_runtime_lookup_i32_f64_to_i32(ctx, "__rt_map_contains_key", ptr, target);
ctx.emit(f64_const(1.0));
ctx.emit(f64_const(0.0));
ctx.emit(Instruction::LocalGet(found_idx));
ctx.emit(Instruction::Select);
Ok(())
}
pub(super) fn emit_map_includes(ctx: &mut WasmGenCtx, args: &[Calcit]) -> Result<(), String> {
expect_arity(2, args, "&map:includes?")?;
let ptr = emit_ptr_to_i32(ctx, &args[0])?;
let target = ctx.alloc_local();
emit_expr(ctx, &args[1])?;
ctx.emit(Instruction::LocalSet(target));
let found_idx = emit_runtime_lookup_i32_f64_to_i32(ctx, "__rt_map_contains_value", ptr, target);
ctx.emit(f64_const(1.0));
ctx.emit(f64_const(0.0));
ctx.emit(Instruction::LocalGet(found_idx));
ctx.emit(Instruction::Select);
Ok(())
}
pub(super) fn emit_map_assoc(ctx: &mut WasmGenCtx, args: &[Calcit]) -> Result<(), String> {
expect_arity(3, args, "&map:assoc")?;
let src = emit_ptr_to_i32(ctx, &args[0])?;
let key = ctx.alloc_local();
emit_expr(ctx, &args[1])?;
ctx.emit(Instruction::LocalSet(key));
let val = ctx.alloc_local();
emit_expr(ctx, &args[2])?;
ctx.emit(Instruction::LocalSet(val));
ctx.emit(Instruction::LocalGet(src));
ctx.emit(Instruction::LocalGet(key));
ctx.emit(Instruction::LocalGet(val));
ctx.call_rt("__rt_map_assoc");
ctx.emit(Instruction::F64ConvertI32U);
Ok(())
}
pub(super) fn emit_map_dissoc(ctx: &mut WasmGenCtx, args: &[Calcit]) -> Result<(), String> {
use crate::calcit::CalcitSyntax;
if args.len() > 2 && !matches!(args[1], Calcit::Syntax(CalcitSyntax::ArgSpread, _)) {
let acc = ctx.alloc_local();
emit_expr(ctx, &args[0])?;
ctx.emit(Instruction::LocalSet(acc));
for key_expr in &args[1..] {
let src_ptr = ctx.alloc_local_typed(ValType::I32);
ctx.emit(Instruction::LocalGet(acc));
ctx.emit(Instruction::I32TruncF64U);
ctx.emit(Instruction::LocalSet(src_ptr));
let key_local = ctx.alloc_local();
emit_expr(ctx, key_expr)?;
ctx.emit(Instruction::LocalSet(key_local));
ctx.emit(Instruction::LocalGet(src_ptr));
ctx.emit(Instruction::LocalGet(key_local));
ctx.call_rt("__rt_map_dissoc");
ctx.emit(Instruction::F64ConvertI32U);
ctx.emit(Instruction::LocalSet(acc));
}
ctx.emit(Instruction::LocalGet(acc));
return Ok(());
}
let (map_arg, key_arg_opt) = 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("&map:dissoc expects 2+ args".into());
};
let src = emit_ptr_to_i32(ctx, map_arg)?;
let target = ctx.alloc_local();
if let Some(key_expr) = key_arg_opt {
emit_expr(ctx, key_expr)?;
ctx.emit(Instruction::LocalSet(target));
} 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::LocalSet(target));
}
ctx.emit(Instruction::LocalGet(src));
ctx.emit(Instruction::LocalGet(target));
ctx.call_rt("__rt_map_dissoc");
ctx.emit(Instruction::F64ConvertI32U);
Ok(())
}
pub(super) fn emit_map_to_pairs(ctx: &mut WasmGenCtx, args: &[Calcit]) -> Result<(), String> {
expect_arity(1, args, "to-pairs")?;
emit_map_to_pair_list(ctx, args, "set")
}
pub(super) fn emit_map_to_list(ctx: &mut WasmGenCtx, args: &[Calcit]) -> Result<(), String> {
expect_arity(1, args, "&map:to-list")?;
emit_map_to_pair_list(ctx, args, "list")
}
pub(super) fn emit_map_keys(ctx: &mut WasmGenCtx, args: &[Calcit]) -> Result<(), String> {
expect_arity(1, args, "&map:keys")?;
let map_ptr = emit_ptr_to_i32(ctx, &args[0])?;
let count = emit_load_count_i32(ctx, map_ptr);
let flat_ptr = emit_runtime_lookup_i32_to_i32(ctx, "__rt_map_linearize", map_ptr);
let ts = ctx.i32_offset(count, 1);
let dst = emit_alloc_with_count(ctx, count, ts, "list");
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(flat_ptr));
ctx.emit(Instruction::I32Const(8));
ctx.emit(Instruction::I32Add);
ctx.emit(Instruction::LocalGet(i));
ctx.emit(Instruction::I32Const(16));
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_map_vals(ctx: &mut WasmGenCtx, args: &[Calcit]) -> Result<(), String> {
if args.len() != 1 {
return Err("&map:vals expects 1 arg".into());
}
let map_ptr = emit_ptr_to_i32(ctx, &args[0])?;
let count = emit_load_count_i32(ctx, map_ptr);
let flat_ptr = emit_runtime_lookup_i32_to_i32(ctx, "__rt_map_linearize", map_ptr);
let ts = ctx.i32_offset(count, 1);
let dst = emit_alloc_with_count(ctx, count, ts, "list");
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(flat_ptr));
ctx.emit(Instruction::I32Const(16));
ctx.emit(Instruction::I32Add);
ctx.emit(Instruction::LocalGet(i));
ctx.emit(Instruction::I32Const(16));
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_map_to_pair_list(ctx: &mut WasmGenCtx, args: &[Calcit], outer_tag: &str) -> Result<(), String> {
let map_ptr = emit_ptr_to_i32(ctx, &args[0])?;
let count = emit_load_count_i32(ctx, map_ptr);
let flat_ptr = emit_runtime_lookup_i32_to_i32(ctx, "__rt_map_linearize", map_ptr);
let outer_ts = ctx.i32_offset(count, 1);
let outer = emit_alloc_with_count(ctx, count, outer_ts, outer_tag);
let i = ctx.alloc_i32(0);
ctx.begin_block();
ctx.begin_loop();
ctx.loop_exit_if_ge(i, count);
let pair = ctx.alloc_local_typed(ValType::I32);
emit_bump_alloc(ctx, 24, pair, "list");
ctx.emit(Instruction::LocalGet(pair));
ctx.emit(f64_const(2.0));
ctx.emit(Instruction::F64Store(mem_arg_f64(0)));
ctx.emit(Instruction::LocalGet(pair));
ctx.emit(Instruction::LocalGet(flat_ptr));
ctx.emit(Instruction::I32Const(8));
ctx.emit(Instruction::I32Add);
ctx.emit(Instruction::LocalGet(i));
ctx.emit(Instruction::I32Const(16));
ctx.emit(Instruction::I32Mul);
ctx.emit(Instruction::I32Add);
ctx.emit(Instruction::F64Load(mem_arg_f64(0)));
ctx.emit(Instruction::F64Store(mem_arg_f64(8)));
ctx.emit(Instruction::LocalGet(pair));
ctx.emit(Instruction::LocalGet(flat_ptr));
ctx.emit(Instruction::I32Const(16));
ctx.emit(Instruction::I32Add);
ctx.emit(Instruction::LocalGet(i));
ctx.emit(Instruction::I32Const(16));
ctx.emit(Instruction::I32Mul);
ctx.emit(Instruction::I32Add);
ctx.emit(Instruction::F64Load(mem_arg_f64(0)));
ctx.emit(Instruction::F64Store(mem_arg_f64(16)));
ctx.emit(Instruction::LocalGet(outer));
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.ptr_to_f64(pair);
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(outer);
Ok(())
}
pub(super) fn emit_map_merge(ctx: &mut WasmGenCtx, args: &[Calcit]) -> Result<(), String> {
if args.len() != 2 {
return Err("&merge expects 2 args".into());
}
let a = emit_ptr_to_i32(ctx, &args[0])?;
let a_count = emit_load_count_i32(ctx, a);
let a_flat = emit_runtime_lookup_i32_to_i32(ctx, "__rt_map_linearize", a);
let b = emit_ptr_to_i32(ctx, &args[1])?;
let b_count = emit_load_count_i32(ctx, b);
let b_flat = emit_runtime_lookup_i32_to_i32(ctx, "__rt_map_linearize", b);
let max_count = ctx.alloc_local_typed(ValType::I32);
ctx.emit(Instruction::LocalGet(a_count));
ctx.emit(Instruction::LocalGet(b_count));
ctx.emit(Instruction::I32Add);
ctx.emit(Instruction::LocalSet(max_count));
let total_slots = ctx.alloc_local_typed(ValType::I32);
ctx.emit(Instruction::LocalGet(max_count));
ctx.emit(Instruction::I32Const(2));
ctx.emit(Instruction::I32Mul);
ctx.emit(Instruction::I32Const(1));
ctx.emit(Instruction::I32Add);
ctx.emit(Instruction::LocalSet(total_slots));
let dst_root = emit_alloc_with_count(ctx, max_count, total_slots, "map");
let write_idx = ctx.alloc_i32(0);
let ai = ctx.alloc_i32(0);
ctx.begin_block();
ctx.begin_loop();
ctx.loop_exit_if_ge(ai, a_count);
let ak = ctx.alloc_local();
let av = ctx.alloc_local();
let a_addr = ctx.alloc_local_typed(ValType::I32);
ctx.emit(Instruction::LocalGet(a_flat));
ctx.emit(Instruction::I32Const(8));
ctx.emit(Instruction::I32Add);
ctx.emit(Instruction::LocalGet(ai));
ctx.emit(Instruction::I32Const(16));
ctx.emit(Instruction::I32Mul);
ctx.emit(Instruction::I32Add);
ctx.emit(Instruction::LocalTee(a_addr));
ctx.emit(Instruction::F64Load(mem_arg_f64(0)));
ctx.emit(Instruction::LocalSet(ak));
ctx.emit(Instruction::LocalGet(a_addr));
ctx.emit(Instruction::F64Load(mem_arg_f64(8)));
ctx.emit(Instruction::LocalSet(av));
let out_addr = ctx.alloc_local_typed(ValType::I32);
ctx.emit(Instruction::LocalGet(dst_root));
ctx.emit(Instruction::I32Const(8));
ctx.emit(Instruction::I32Add);
ctx.emit(Instruction::LocalGet(write_idx));
ctx.emit(Instruction::I32Const(16));
ctx.emit(Instruction::I32Mul);
ctx.emit(Instruction::I32Add);
ctx.emit(Instruction::LocalSet(out_addr));
ctx.emit(Instruction::LocalGet(out_addr));
ctx.emit(Instruction::LocalGet(ak));
ctx.emit(Instruction::F64Store(mem_arg_f64(0)));
ctx.emit(Instruction::LocalGet(out_addr));
ctx.emit(Instruction::LocalGet(av));
ctx.emit(Instruction::F64Store(mem_arg_f64(8)));
ctx.i32_inc(write_idx);
ctx.i32_inc(ai);
ctx.emit(Instruction::Br(0));
ctx.emit(Instruction::End);
ctx.emit(Instruction::End);
let bi = ctx.alloc_i32(0);
ctx.begin_block();
ctx.begin_loop();
ctx.loop_exit_if_ge(bi, b_count);
let bk = ctx.alloc_local();
let bv = ctx.alloc_local();
let b_addr = ctx.alloc_local_typed(ValType::I32);
ctx.emit(Instruction::LocalGet(b_flat));
ctx.emit(Instruction::I32Const(8));
ctx.emit(Instruction::I32Add);
ctx.emit(Instruction::LocalGet(bi));
ctx.emit(Instruction::I32Const(16));
ctx.emit(Instruction::I32Mul);
ctx.emit(Instruction::I32Add);
ctx.emit(Instruction::LocalTee(b_addr));
ctx.emit(Instruction::F64Load(mem_arg_f64(0)));
ctx.emit(Instruction::LocalSet(bk));
ctx.emit(Instruction::LocalGet(b_addr));
ctx.emit(Instruction::F64Load(mem_arg_f64(8)));
ctx.emit(Instruction::LocalSet(bv));
let found = ctx.alloc_i32(0);
let di = ctx.alloc_i32(0);
ctx.begin_block();
ctx.begin_loop();
ctx.loop_exit_if_ge(di, write_idx);
let d_addr = ctx.alloc_local_typed(ValType::I32);
ctx.emit(Instruction::LocalGet(dst_root));
ctx.emit(Instruction::I32Const(8));
ctx.emit(Instruction::I32Add);
ctx.emit(Instruction::LocalGet(di));
ctx.emit(Instruction::I32Const(16));
ctx.emit(Instruction::I32Mul);
ctx.emit(Instruction::I32Add);
ctx.emit(Instruction::LocalTee(d_addr));
ctx.emit(Instruction::F64Load(mem_arg_f64(0)));
ctx.emit(Instruction::LocalGet(bk));
ctx.emit(Instruction::F64Eq);
ctx.begin_block_if();
ctx.emit(Instruction::LocalGet(d_addr));
ctx.emit(Instruction::LocalGet(bv));
ctx.emit(Instruction::F64Store(mem_arg_f64(8)));
ctx.emit(Instruction::I32Const(1));
ctx.emit(Instruction::LocalSet(found));
ctx.emit(Instruction::Br(2));
ctx.emit(Instruction::End);
ctx.i32_inc(di);
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 out_addr = ctx.alloc_local_typed(ValType::I32);
ctx.emit(Instruction::LocalGet(dst_root));
ctx.emit(Instruction::I32Const(8));
ctx.emit(Instruction::I32Add);
ctx.emit(Instruction::LocalGet(write_idx));
ctx.emit(Instruction::I32Const(16));
ctx.emit(Instruction::I32Mul);
ctx.emit(Instruction::I32Add);
ctx.emit(Instruction::LocalSet(out_addr));
ctx.emit(Instruction::LocalGet(out_addr));
ctx.emit(Instruction::LocalGet(bk));
ctx.emit(Instruction::F64Store(mem_arg_f64(0)));
ctx.emit(Instruction::LocalGet(out_addr));
ctx.emit(Instruction::LocalGet(bv));
ctx.emit(Instruction::F64Store(mem_arg_f64(8)));
ctx.i32_inc(write_idx);
}
ctx.emit(Instruction::End);
ctx.i32_inc(bi);
ctx.emit(Instruction::Br(0));
ctx.emit(Instruction::End);
ctx.emit(Instruction::End);
ctx.emit(Instruction::LocalGet(dst_root));
ctx.ptr_to_f64(write_idx);
ctx.emit(Instruction::F64Store(mem_arg_f64(0)));
let dst = emit_runtime_lookup_i32_to_i32(ctx, "__rt_map_from_flat", dst_root);
ctx.ptr_to_f64(dst);
Ok(())
}
pub(super) fn emit_map_diff_new(ctx: &mut WasmGenCtx, args: &[Calcit]) -> Result<(), String> {
if args.len() != 2 {
return Err("&map:diff-new expects 2 args".into());
}
let a = emit_ptr_to_i32(ctx, &args[0])?;
let a_count = emit_load_count_i32(ctx, a);
let a_flat = emit_runtime_lookup_i32_to_i32(ctx, "__rt_map_linearize", a);
let b = emit_ptr_to_i32(ctx, &args[1])?;
let b_count = emit_load_count_i32(ctx, b);
let b_flat = emit_runtime_lookup_i32_to_i32(ctx, "__rt_map_linearize", b);
let total_slots = ctx.alloc_local_typed(ValType::I32);
ctx.emit(Instruction::LocalGet(a_count));
ctx.emit(Instruction::I32Const(2));
ctx.emit(Instruction::I32Mul);
ctx.emit(Instruction::I32Const(1));
ctx.emit(Instruction::I32Add);
ctx.emit(Instruction::LocalSet(total_slots));
let dst_root = emit_alloc_with_count(ctx, a_count, total_slots, "map");
let write_idx = ctx.alloc_i32(0);
let bi = ctx.alloc_i32(0);
ctx.begin_block();
ctx.begin_loop();
ctx.loop_exit_if_ge(bi, a_count);
let bk = ctx.alloc_local();
let bv = ctx.alloc_local();
let bkv_addr = ctx.alloc_local_typed(ValType::I32);
ctx.emit(Instruction::LocalGet(a_flat));
ctx.emit(Instruction::I32Const(8));
ctx.emit(Instruction::I32Add);
ctx.emit(Instruction::LocalGet(bi));
ctx.emit(Instruction::I32Const(16));
ctx.emit(Instruction::I32Mul);
ctx.emit(Instruction::I32Add);
ctx.emit(Instruction::LocalTee(bkv_addr));
ctx.emit(Instruction::F64Load(mem_arg_f64(0)));
ctx.emit(Instruction::LocalSet(bk));
ctx.emit(Instruction::LocalGet(bkv_addr));
ctx.emit(Instruction::F64Load(mem_arg_f64(8)));
ctx.emit(Instruction::LocalSet(bv));
let found_in_a = ctx.alloc_i32(0);
let ai = ctx.alloc_i32(0);
ctx.begin_block();
ctx.begin_loop();
ctx.loop_exit_if_ge(ai, b_count);
let akv_addr = ctx.alloc_local_typed(ValType::I32);
ctx.emit(Instruction::LocalGet(b_flat));
ctx.emit(Instruction::I32Const(8));
ctx.emit(Instruction::I32Add);
ctx.emit(Instruction::LocalGet(ai));
ctx.emit(Instruction::I32Const(16));
ctx.emit(Instruction::I32Mul);
ctx.emit(Instruction::I32Add);
ctx.emit(Instruction::LocalTee(akv_addr));
ctx.emit(Instruction::F64Load(mem_arg_f64(0)));
ctx.emit(Instruction::LocalGet(bk));
ctx.emit(Instruction::F64Eq);
ctx.begin_block_if();
ctx.emit(Instruction::I32Const(1));
ctx.emit(Instruction::LocalSet(found_in_a));
ctx.emit(Instruction::Br(2));
ctx.emit(Instruction::End);
ctx.i32_inc(ai);
ctx.emit(Instruction::Br(0));
ctx.emit(Instruction::End);
ctx.emit(Instruction::End);
ctx.emit(Instruction::LocalGet(found_in_a));
ctx.emit(Instruction::I32Eqz);
ctx.begin_block_if();
{
let addr = ctx.alloc_local_typed(ValType::I32);
ctx.emit(Instruction::LocalGet(dst_root));
ctx.emit(Instruction::I32Const(8));
ctx.emit(Instruction::I32Add);
ctx.emit(Instruction::LocalGet(write_idx));
ctx.emit(Instruction::I32Const(16));
ctx.emit(Instruction::I32Mul);
ctx.emit(Instruction::I32Add);
ctx.emit(Instruction::LocalSet(addr));
ctx.emit(Instruction::LocalGet(addr));
ctx.emit(Instruction::LocalGet(bk));
ctx.emit(Instruction::F64Store(mem_arg_f64(0)));
ctx.emit(Instruction::LocalGet(addr));
ctx.emit(Instruction::LocalGet(bv));
ctx.emit(Instruction::F64Store(mem_arg_f64(8)));
ctx.i32_inc(write_idx);
}
ctx.emit(Instruction::End);
ctx.i32_inc(bi);
ctx.emit(Instruction::Br(0));
ctx.emit(Instruction::End);
ctx.emit(Instruction::End);
ctx.emit(Instruction::LocalGet(dst_root));
ctx.ptr_to_f64(write_idx);
ctx.emit(Instruction::F64Store(mem_arg_f64(0)));
let dst = emit_runtime_lookup_i32_to_i32(ctx, "__rt_map_from_flat", dst_root);
ctx.ptr_to_f64(dst);
Ok(())
}
pub(super) fn emit_map_diff_keys(ctx: &mut WasmGenCtx, args: &[Calcit]) -> Result<(), String> {
if args.len() != 2 {
return Err("&map:diff-keys expects 2 args".into());
}
let a = emit_ptr_to_i32(ctx, &args[0])?;
let a_count = emit_load_count_i32(ctx, a);
let a_flat = emit_runtime_lookup_i32_to_i32(ctx, "__rt_map_linearize", a);
let b = emit_ptr_to_i32(ctx, &args[1])?;
let b_count = emit_load_count_i32(ctx, b);
let b_flat = emit_runtime_lookup_i32_to_i32(ctx, "__rt_map_linearize", b);
let total_slots = ctx.i32_offset(a_count, 1);
let dst = emit_alloc_with_count(ctx, a_count, total_slots, "set");
let write_idx = ctx.alloc_i32(0);
let ai = ctx.alloc_i32(0);
ctx.begin_block();
ctx.begin_loop();
ctx.loop_exit_if_ge(ai, a_count);
let ak = ctx.alloc_local();
ctx.emit(Instruction::LocalGet(a_flat));
ctx.emit(Instruction::I32Const(8));
ctx.emit(Instruction::I32Add);
ctx.emit(Instruction::LocalGet(ai));
ctx.emit(Instruction::I32Const(16));
ctx.emit(Instruction::I32Mul);
ctx.emit(Instruction::I32Add);
ctx.emit(Instruction::F64Load(mem_arg_f64(0)));
ctx.emit(Instruction::LocalSet(ak));
let found = ctx.alloc_i32(0);
let bi = ctx.alloc_i32(0);
ctx.begin_block();
ctx.begin_loop();
ctx.loop_exit_if_ge(bi, b_count);
ctx.emit(Instruction::LocalGet(b_flat));
ctx.emit(Instruction::I32Const(8));
ctx.emit(Instruction::I32Add);
ctx.emit(Instruction::LocalGet(bi));
ctx.emit(Instruction::I32Const(16));
ctx.emit(Instruction::I32Mul);
ctx.emit(Instruction::I32Add);
ctx.emit(Instruction::F64Load(mem_arg_f64(0)));
ctx.emit(Instruction::LocalGet(ak));
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(bi);
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();
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::LocalGet(ak));
ctx.emit(Instruction::F64Store(mem_arg_f64(0)));
ctx.i32_inc(write_idx);
ctx.emit(Instruction::End);
ctx.i32_inc(ai);
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_map_common_keys(ctx: &mut WasmGenCtx, args: &[Calcit]) -> Result<(), String> {
if args.len() != 2 {
return Err("&map:common-keys expects 2 args".into());
}
let a = emit_ptr_to_i32(ctx, &args[0])?;
let a_count = emit_load_count_i32(ctx, a);
let a_flat = emit_runtime_lookup_i32_to_i32(ctx, "__rt_map_linearize", a);
let b = emit_ptr_to_i32(ctx, &args[1])?;
let b_count = emit_load_count_i32(ctx, b);
let b_flat = emit_runtime_lookup_i32_to_i32(ctx, "__rt_map_linearize", b);
let total_slots = ctx.i32_offset(a_count, 1);
let dst = emit_alloc_with_count(ctx, a_count, total_slots, "set");
let write_idx = ctx.alloc_i32(0);
let ai = ctx.alloc_i32(0);
ctx.begin_block();
ctx.begin_loop();
ctx.loop_exit_if_ge(ai, a_count);
let ak = ctx.alloc_local();
ctx.emit(Instruction::LocalGet(a_flat));
ctx.emit(Instruction::I32Const(8));
ctx.emit(Instruction::I32Add);
ctx.emit(Instruction::LocalGet(ai));
ctx.emit(Instruction::I32Const(16));
ctx.emit(Instruction::I32Mul);
ctx.emit(Instruction::I32Add);
ctx.emit(Instruction::F64Load(mem_arg_f64(0)));
ctx.emit(Instruction::LocalSet(ak));
let found = ctx.alloc_i32(0);
let bi = ctx.alloc_i32(0);
ctx.begin_block();
ctx.begin_loop();
ctx.loop_exit_if_ge(bi, b_count);
ctx.emit(Instruction::LocalGet(b_flat));
ctx.emit(Instruction::I32Const(8));
ctx.emit(Instruction::I32Add);
ctx.emit(Instruction::LocalGet(bi));
ctx.emit(Instruction::I32Const(16));
ctx.emit(Instruction::I32Mul);
ctx.emit(Instruction::I32Add);
ctx.emit(Instruction::F64Load(mem_arg_f64(0)));
ctx.emit(Instruction::LocalGet(ak));
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(bi);
ctx.emit(Instruction::Br(0));
ctx.emit(Instruction::End);
ctx.emit(Instruction::End);
ctx.emit(Instruction::LocalGet(found));
ctx.begin_block_if();
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::LocalGet(ak));
ctx.emit(Instruction::F64Store(mem_arg_f64(0)));
ctx.i32_inc(write_idx);
ctx.emit(Instruction::End);
ctx.i32_inc(ai);
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_map_destruct(ctx: &mut WasmGenCtx, args: &[Calcit]) -> Result<(), String> {
if args.len() != 1 {
return Err("&map:destruct expects 1 arg".into());
}
let src = emit_ptr_to_i32(ctx, &args[0])?;
let count = emit_load_count_i32(ctx, src);
let result = ctx.alloc_local();
ctx.emit(Instruction::LocalGet(count));
ctx.emit(Instruction::I32Eqz);
ctx.begin_block_if();
ctx.emit(f64_const(0.0));
ctx.emit(Instruction::LocalSet(result));
ctx.emit(Instruction::Else);
{
let flat = emit_runtime_lookup_i32_to_i32(ctx, "__rt_map_linearize", src);
let key0 = ctx.alloc_local();
let val0 = ctx.alloc_local();
ctx.emit(Instruction::LocalGet(flat));
ctx.emit(Instruction::I32Const(8));
ctx.emit(Instruction::I32Add);
ctx.emit(Instruction::F64Load(mem_arg_f64(0)));
ctx.emit(Instruction::LocalSet(key0));
ctx.emit(Instruction::LocalGet(flat));
ctx.emit(Instruction::I32Const(8));
ctx.emit(Instruction::I32Add);
ctx.emit(Instruction::F64Load(mem_arg_f64(8)));
ctx.emit(Instruction::LocalSet(val0));
let rest_map = ctx.alloc_local();
ctx.emit(Instruction::LocalGet(src));
ctx.emit(Instruction::LocalGet(key0));
ctx.call_rt("__rt_map_dissoc");
ctx.emit(Instruction::F64ConvertI32U);
ctx.emit(Instruction::LocalSet(rest_map));
let list_ptr = ctx.alloc_local_typed(ValType::I32);
emit_bump_alloc(ctx, 32, list_ptr, "list"); ctx.emit(Instruction::LocalGet(list_ptr));
ctx.emit(f64_const(3.0));
ctx.emit(Instruction::F64Store(mem_arg_f64(0)));
ctx.emit(Instruction::LocalGet(list_ptr));
ctx.emit(Instruction::LocalGet(key0));
ctx.emit(Instruction::F64Store(mem_arg_f64(8)));
ctx.emit(Instruction::LocalGet(list_ptr));
ctx.emit(Instruction::LocalGet(val0));
ctx.emit(Instruction::F64Store(mem_arg_f64(16)));
ctx.emit(Instruction::LocalGet(list_ptr));
ctx.emit(Instruction::LocalGet(rest_map));
ctx.emit(Instruction::F64Store(mem_arg_f64(24)));
ctx.ptr_to_f64(list_ptr);
ctx.emit(Instruction::LocalSet(result));
}
ctx.emit(Instruction::End);
ctx.emit(Instruction::LocalGet(result));
Ok(())
}
pub(super) fn emit_map_merge_non_nil(ctx: &mut WasmGenCtx, args: &[Calcit]) -> Result<(), String> {
if args.len() != 2 {
return Err("&merge-non-nil expects 2 args".into());
}
let a = emit_ptr_to_i32(ctx, &args[0])?;
let a_count = emit_load_count_i32(ctx, a);
let a_flat = emit_runtime_lookup_i32_to_i32(ctx, "__rt_map_linearize", a);
let b = emit_ptr_to_i32(ctx, &args[1])?;
let b_count = emit_load_count_i32(ctx, b);
let b_flat = emit_runtime_lookup_i32_to_i32(ctx, "__rt_map_linearize", b);
let max_count = ctx.alloc_local_typed(ValType::I32);
ctx.emit(Instruction::LocalGet(a_count));
ctx.emit(Instruction::LocalGet(b_count));
ctx.emit(Instruction::I32Add);
ctx.emit(Instruction::LocalSet(max_count));
let total_slots = ctx.alloc_local_typed(ValType::I32);
ctx.emit(Instruction::LocalGet(max_count));
ctx.emit(Instruction::I32Const(2));
ctx.emit(Instruction::I32Mul);
ctx.emit(Instruction::I32Const(1));
ctx.emit(Instruction::I32Add);
ctx.emit(Instruction::LocalSet(total_slots));
let dst_root = emit_alloc_with_count(ctx, max_count, total_slots, "map");
let write_idx = ctx.alloc_i32(0);
let ai = ctx.alloc_i32(0);
ctx.begin_block();
ctx.begin_loop();
ctx.loop_exit_if_ge(ai, a_count);
{
let ak = ctx.alloc_local();
let av = ctx.alloc_local();
let a_addr = ctx.alloc_local_typed(ValType::I32);
ctx.emit(Instruction::LocalGet(a_flat));
ctx.emit(Instruction::I32Const(8));
ctx.emit(Instruction::I32Add);
ctx.emit(Instruction::LocalGet(ai));
ctx.emit(Instruction::I32Const(16));
ctx.emit(Instruction::I32Mul);
ctx.emit(Instruction::I32Add);
ctx.emit(Instruction::LocalTee(a_addr));
ctx.emit(Instruction::F64Load(mem_arg_f64(0)));
ctx.emit(Instruction::LocalSet(ak));
ctx.emit(Instruction::LocalGet(a_addr));
ctx.emit(Instruction::F64Load(mem_arg_f64(8)));
ctx.emit(Instruction::LocalSet(av));
let d_addr = ctx.alloc_local_typed(ValType::I32);
ctx.emit(Instruction::LocalGet(dst_root));
ctx.emit(Instruction::I32Const(8));
ctx.emit(Instruction::I32Add);
ctx.emit(Instruction::LocalGet(write_idx));
ctx.emit(Instruction::I32Const(16));
ctx.emit(Instruction::I32Mul);
ctx.emit(Instruction::I32Add);
ctx.emit(Instruction::LocalTee(d_addr));
ctx.emit(Instruction::LocalGet(ak));
ctx.emit(Instruction::F64Store(mem_arg_f64(0)));
ctx.emit(Instruction::LocalGet(d_addr));
ctx.emit(Instruction::LocalGet(av));
ctx.emit(Instruction::F64Store(mem_arg_f64(8)));
ctx.i32_inc(write_idx);
}
ctx.i32_inc(ai);
ctx.emit(Instruction::Br(0));
ctx.emit(Instruction::End);
ctx.emit(Instruction::End);
let bi = ctx.alloc_i32(0);
ctx.begin_block();
ctx.begin_loop();
ctx.loop_exit_if_ge(bi, b_count);
{
let bk = ctx.alloc_local();
let bv = ctx.alloc_local();
let b_addr = ctx.alloc_local_typed(ValType::I32);
ctx.emit(Instruction::LocalGet(b_flat));
ctx.emit(Instruction::I32Const(8));
ctx.emit(Instruction::I32Add);
ctx.emit(Instruction::LocalGet(bi));
ctx.emit(Instruction::I32Const(16));
ctx.emit(Instruction::I32Mul);
ctx.emit(Instruction::I32Add);
ctx.emit(Instruction::LocalTee(b_addr));
ctx.emit(Instruction::F64Load(mem_arg_f64(0)));
ctx.emit(Instruction::LocalSet(bk));
ctx.emit(Instruction::LocalGet(b_addr));
ctx.emit(Instruction::F64Load(mem_arg_f64(8)));
ctx.emit(Instruction::LocalSet(bv));
ctx.emit(Instruction::LocalGet(bv));
ctx.emit(f64_const(0.0));
ctx.emit(Instruction::F64Ne);
ctx.begin_block_if();
{
let found_di = ctx.alloc_local_typed(ValType::I32);
ctx.emit(Instruction::I32Const(-1i32 as u32 as i32));
ctx.emit(Instruction::LocalSet(found_di));
let di = ctx.alloc_i32(0);
ctx.begin_block();
ctx.begin_loop();
ctx.loop_exit_if_ge(di, write_idx);
{
let dk_addr = ctx.alloc_local_typed(ValType::I32);
ctx.emit(Instruction::LocalGet(dst_root));
ctx.emit(Instruction::I32Const(8));
ctx.emit(Instruction::I32Add);
ctx.emit(Instruction::LocalGet(di));
ctx.emit(Instruction::I32Const(16));
ctx.emit(Instruction::I32Mul);
ctx.emit(Instruction::I32Add);
ctx.emit(Instruction::LocalTee(dk_addr));
ctx.emit(Instruction::F64Load(mem_arg_f64(0)));
ctx.emit(Instruction::LocalGet(bk));
ctx.emit(Instruction::F64Eq);
ctx.begin_block_if();
ctx.emit(Instruction::LocalGet(di));
ctx.emit(Instruction::LocalSet(found_di));
ctx.emit(Instruction::Br(2)); ctx.emit(Instruction::End);
}
ctx.i32_inc(di);
ctx.emit(Instruction::Br(0));
ctx.emit(Instruction::End);
ctx.emit(Instruction::End);
ctx.emit(Instruction::LocalGet(found_di));
ctx.emit(Instruction::I32Const(0));
ctx.emit(Instruction::I32GeS);
ctx.begin_block_if();
{
let upd_addr = ctx.alloc_local_typed(ValType::I32);
ctx.emit(Instruction::LocalGet(dst_root));
ctx.emit(Instruction::I32Const(8));
ctx.emit(Instruction::I32Add);
ctx.emit(Instruction::LocalGet(found_di));
ctx.emit(Instruction::I32Const(16));
ctx.emit(Instruction::I32Mul);
ctx.emit(Instruction::I32Add);
ctx.emit(Instruction::LocalTee(upd_addr));
ctx.emit(Instruction::LocalGet(bv));
ctx.emit(Instruction::F64Store(mem_arg_f64(8)));
}
ctx.emit(Instruction::Else);
{
let new_addr = ctx.alloc_local_typed(ValType::I32);
ctx.emit(Instruction::LocalGet(dst_root));
ctx.emit(Instruction::I32Const(8));
ctx.emit(Instruction::I32Add);
ctx.emit(Instruction::LocalGet(write_idx));
ctx.emit(Instruction::I32Const(16));
ctx.emit(Instruction::I32Mul);
ctx.emit(Instruction::I32Add);
ctx.emit(Instruction::LocalTee(new_addr));
ctx.emit(Instruction::LocalGet(bk));
ctx.emit(Instruction::F64Store(mem_arg_f64(0)));
ctx.emit(Instruction::LocalGet(new_addr));
ctx.emit(Instruction::LocalGet(bv));
ctx.emit(Instruction::F64Store(mem_arg_f64(8)));
ctx.i32_inc(write_idx);
}
ctx.emit(Instruction::End);
}
ctx.emit(Instruction::End); }
ctx.i32_inc(bi);
ctx.emit(Instruction::Br(0));
ctx.emit(Instruction::End);
ctx.emit(Instruction::End);
ctx.emit(Instruction::LocalGet(dst_root));
ctx.ptr_to_f64(write_idx);
ctx.emit(Instruction::F64Store(mem_arg_f64(0)));
let dst = emit_runtime_lookup_i32_to_i32(ctx, "__rt_map_from_flat", dst_root);
ctx.ptr_to_f64(dst);
Ok(())
}
pub(super) fn emit_zipmap_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 = ctx.alloc_local();
emit_map_new(ctx, &[])?;
ctx.emit(Instruction::LocalSet(result));
let i = ctx.alloc_i32(0);
ctx.begin_block();
ctx.begin_loop();
ctx.loop_exit_if_ge(i, min_count);
let k = ctx.alloc_local();
ctx.emit(Instruction::LocalGet(xs_ptr));
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::F64Load(mem_arg_f64(0)));
ctx.emit(Instruction::LocalSet(k));
let v = ctx.alloc_local();
ctx.emit(Instruction::LocalGet(ys_ptr));
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::F64Load(mem_arg_f64(0)));
ctx.emit(Instruction::LocalSet(v));
let result_i32 = ctx.alloc_local_typed(ValType::I32);
ctx.emit(Instruction::LocalGet(result));
ctx.emit(Instruction::I32TruncF64U);
ctx.emit(Instruction::LocalSet(result_i32));
ctx.emit(Instruction::LocalGet(result_i32));
ctx.emit(Instruction::LocalGet(k));
ctx.emit(Instruction::LocalGet(v));
ctx.call_rt("__rt_map_assoc");
ctx.emit(Instruction::F64ConvertI32U);
ctx.emit(Instruction::LocalSet(result));
ctx.i32_inc(i);
ctx.emit(Instruction::Br(0));
ctx.emit(Instruction::End);
ctx.emit(Instruction::End);
ctx.emit(Instruction::LocalGet(result));
Ok(())
}
pub(super) fn emit_zipmap(ctx: &mut WasmGenCtx, args: &[Calcit]) -> Result<(), String> {
if args.len() != 2 {
return Err(format!("zipmap expects 2 args, got {}", args.len()));
}
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_zipmap_from_locals(ctx, xs, ys)
}