use super::*;
pub(super) fn emit_set_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, "set");
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.emit(Instruction::LocalGet(ptr));
ctx.emit(Instruction::F64ConvertI32U);
Ok(())
}
pub(super) enum SetOpKind {
Union,
Difference,
Include,
}
pub(super) fn emit_set_op_variadic(ctx: &mut WasmGenCtx, args: &[Calcit], kind: SetOpKind) -> Result<(), String> {
if args.is_empty() {
return Err("set op variadic requires at least 1 arg (base)".into());
}
if args.len() == 1 {
return emit_expr(ctx, &args[0]);
}
if args.len() == 2 {
return match kind {
SetOpKind::Union => emit_set_union(ctx, args),
SetOpKind::Difference => emit_set_difference(ctx, args),
SetOpKind::Include => emit_set_include(ctx, args),
};
}
let acc = ctx.alloc_local();
{
let first_two = [args[0].clone(), args[1].clone()];
match kind {
SetOpKind::Union => emit_set_union(ctx, &first_two)?,
SetOpKind::Difference => emit_set_difference(ctx, &first_two)?,
SetOpKind::Include => emit_set_include(ctx, &first_two)?,
}
ctx.emit(Instruction::LocalSet(acc));
}
for extra in &args[2..] {
let extra_val = ctx.alloc_local();
emit_expr(ctx, extra)?;
ctx.emit(Instruction::LocalSet(extra_val));
let acc_ptr = ctx.alloc_local_typed(ValType::I32);
ctx.emit(Instruction::LocalGet(acc));
ctx.emit(Instruction::I32TruncF64U);
ctx.emit(Instruction::LocalSet(acc_ptr));
let new_dst = match kind {
SetOpKind::Union => {
let extra_ptr = ctx.alloc_local_typed(ValType::I32);
ctx.emit(Instruction::LocalGet(extra_val));
ctx.emit(Instruction::I32TruncF64U);
ctx.emit(Instruction::LocalSet(extra_ptr));
emit_set_union_from_ptrs(ctx, acc_ptr, extra_ptr)?
}
SetOpKind::Difference => {
let extra_ptr = ctx.alloc_local_typed(ValType::I32);
ctx.emit(Instruction::LocalGet(extra_val));
ctx.emit(Instruction::I32TruncF64U);
ctx.emit(Instruction::LocalSet(extra_ptr));
emit_set_difference_from_ptrs(ctx, acc_ptr, extra_ptr)?
}
SetOpKind::Include => emit_set_include_from_ptrs(ctx, acc_ptr, extra_val)?,
};
ctx.emit(Instruction::LocalGet(new_dst));
ctx.emit(Instruction::LocalSet(acc));
}
ctx.emit(Instruction::LocalGet(acc));
Ok(())
}
pub(super) fn emit_set_union_from_ptrs(ctx: &mut WasmGenCtx, a: u32, b: u32) -> Result<u32, String> {
let a_count = emit_load_count_i32(ctx, a);
let b_count = emit_load_count_i32(ctx, 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(1));
ctx.emit(Instruction::I32Add);
ctx.emit(Instruction::LocalSet(total_slots));
let dst = emit_alloc_with_count(ctx, max_count, total_slots, "set");
let db = emit_addr_offset(ctx, dst, 8);
let sb = emit_addr_offset(ctx, a, 8);
emit_copy_f64_loop(ctx, db, sb, a_count);
let write_idx = ctx.alloc_local_typed(ValType::I32);
ctx.emit(Instruction::LocalGet(a_count));
ctx.emit(Instruction::LocalSet(write_idx));
let bi = ctx.alloc_local_typed(ValType::I32);
ctx.emit(Instruction::I32Const(0));
ctx.emit(Instruction::LocalSet(bi));
ctx.emit(Instruction::Block(wasm_encoder::BlockType::Empty));
ctx.emit(Instruction::Loop(wasm_encoder::BlockType::Empty));
ctx.emit(Instruction::LocalGet(bi));
ctx.emit(Instruction::LocalGet(b_count));
ctx.emit(Instruction::I32GeU);
ctx.emit(Instruction::BrIf(1));
let be = ctx.alloc_local();
ctx.emit(Instruction::LocalGet(b));
ctx.emit(Instruction::I32Const(8));
ctx.emit(Instruction::I32Add);
ctx.emit(Instruction::LocalGet(bi));
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(be));
let found = emit_runtime_lookup_i32_f64_to_i32(ctx, "__rt_set_find_elem", dst, be);
ctx.emit(Instruction::LocalGet(found));
ctx.emit(Instruction::I32Const(-1));
ctx.emit(Instruction::I32Eq);
ctx.emit(Instruction::If(wasm_encoder::BlockType::Empty));
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(be));
ctx.emit(Instruction::F64Store(mem_arg_f64(0)));
ctx.emit(Instruction::LocalGet(write_idx));
ctx.emit(Instruction::I32Const(1));
ctx.emit(Instruction::I32Add);
ctx.emit(Instruction::LocalSet(write_idx));
ctx.emit(Instruction::End);
ctx.emit(Instruction::LocalGet(bi));
ctx.emit(Instruction::I32Const(1));
ctx.emit(Instruction::I32Add);
ctx.emit(Instruction::LocalSet(bi));
ctx.emit(Instruction::Br(0));
ctx.emit(Instruction::End);
ctx.emit(Instruction::End);
ctx.emit(Instruction::LocalGet(dst));
ctx.emit(Instruction::LocalGet(write_idx));
ctx.emit(Instruction::F64ConvertI32U);
ctx.emit(Instruction::F64Store(mem_arg_f64(0)));
let result = ctx.alloc_local();
ctx.emit(Instruction::LocalGet(dst));
ctx.emit(Instruction::F64ConvertI32U);
ctx.emit(Instruction::LocalSet(result));
Ok(result)
}
pub(super) fn emit_set_difference_from_ptrs(ctx: &mut WasmGenCtx, a: u32, b: u32) -> Result<u32, String> {
let a_count = emit_load_count_i32(ctx, a);
let total_slots = ctx.alloc_local_typed(ValType::I32);
ctx.emit(Instruction::LocalGet(a_count));
ctx.emit(Instruction::I32Const(1));
ctx.emit(Instruction::I32Add);
ctx.emit(Instruction::LocalSet(total_slots));
let dst = emit_alloc_with_count(ctx, a_count, total_slots, "set");
let write_idx = ctx.alloc_local_typed(ValType::I32);
ctx.emit(Instruction::I32Const(0));
ctx.emit(Instruction::LocalSet(write_idx));
let ai = ctx.alloc_local_typed(ValType::I32);
ctx.emit(Instruction::I32Const(0));
ctx.emit(Instruction::LocalSet(ai));
ctx.emit(Instruction::Block(wasm_encoder::BlockType::Empty));
ctx.emit(Instruction::Loop(wasm_encoder::BlockType::Empty));
ctx.emit(Instruction::LocalGet(ai));
ctx.emit(Instruction::LocalGet(a_count));
ctx.emit(Instruction::I32GeU);
ctx.emit(Instruction::BrIf(1));
let elem = ctx.alloc_local();
ctx.emit(Instruction::LocalGet(a));
ctx.emit(Instruction::I32Const(8));
ctx.emit(Instruction::I32Add);
ctx.emit(Instruction::LocalGet(ai));
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(elem));
let found_idx = emit_runtime_lookup_i32_f64_to_i32(ctx, "__rt_set_find_elem", b, elem);
ctx.emit(Instruction::LocalGet(found_idx));
ctx.emit(Instruction::I32Const(-1));
ctx.emit(Instruction::I32Eq);
ctx.emit(Instruction::If(wasm_encoder::BlockType::Empty));
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(elem));
ctx.emit(Instruction::F64Store(mem_arg_f64(0)));
ctx.emit(Instruction::LocalGet(write_idx));
ctx.emit(Instruction::I32Const(1));
ctx.emit(Instruction::I32Add);
ctx.emit(Instruction::LocalSet(write_idx));
ctx.emit(Instruction::End);
ctx.emit(Instruction::LocalGet(ai));
ctx.emit(Instruction::I32Const(1));
ctx.emit(Instruction::I32Add);
ctx.emit(Instruction::LocalSet(ai));
ctx.emit(Instruction::Br(0));
ctx.emit(Instruction::End);
ctx.emit(Instruction::End);
ctx.emit(Instruction::LocalGet(dst));
ctx.emit(Instruction::LocalGet(write_idx));
ctx.emit(Instruction::F64ConvertI32U);
ctx.emit(Instruction::F64Store(mem_arg_f64(0)));
let result = ctx.alloc_local();
ctx.emit(Instruction::LocalGet(dst));
ctx.emit(Instruction::F64ConvertI32U);
ctx.emit(Instruction::LocalSet(result));
Ok(result)
}
pub(super) fn emit_set_include_from_ptrs(ctx: &mut WasmGenCtx, src: u32, elem: u32) -> Result<u32, String> {
let count = emit_load_count_i32(ctx, src);
let found_idx = emit_runtime_lookup_i32_f64_to_i32(ctx, "__rt_set_find_elem", src, elem);
let dst = ctx.alloc_local_typed(ValType::I32);
ctx.emit(Instruction::LocalGet(src));
ctx.emit(Instruction::LocalSet(dst));
ctx.emit(Instruction::LocalGet(found_idx));
ctx.emit(Instruction::I32Const(-1));
ctx.emit(Instruction::I32Eq);
ctx.emit(Instruction::If(wasm_encoder::BlockType::Empty));
{
let nc = ctx.alloc_local_typed(ValType::I32);
ctx.emit(Instruction::LocalGet(count));
ctx.emit(Instruction::I32Const(1));
ctx.emit(Instruction::I32Add);
ctx.emit(Instruction::LocalSet(nc));
let ts = ctx.alloc_local_typed(ValType::I32);
ctx.emit(Instruction::LocalGet(nc));
ctx.emit(Instruction::I32Const(1));
ctx.emit(Instruction::I32Add);
ctx.emit(Instruction::LocalSet(ts));
let d = emit_alloc_with_count(ctx, nc, ts, "set");
ctx.emit(Instruction::LocalGet(d));
ctx.emit(Instruction::LocalSet(dst));
let db = emit_addr_offset(ctx, d, 8);
let sb = emit_addr_offset(ctx, src, 8);
emit_copy_f64_loop(ctx, db, sb, count);
ctx.emit(Instruction::LocalGet(d));
ctx.emit(Instruction::I32Const(8));
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::LocalGet(elem));
ctx.emit(Instruction::F64Store(mem_arg_f64(0)));
}
ctx.emit(Instruction::End);
let result = ctx.alloc_local();
ctx.emit(Instruction::LocalGet(dst));
ctx.emit(Instruction::F64ConvertI32U);
ctx.emit(Instruction::LocalSet(result));
Ok(result)
}
pub(super) fn emit_set_includes(ctx: &mut WasmGenCtx, args: &[Calcit]) -> Result<(), String> {
if args.len() != 2 {
return Err("&set:includes? expects 2 args".into());
}
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_set_find_elem", ptr, target);
ctx.emit(f64_const(1.0));
ctx.emit(f64_const(0.0));
ctx.emit(Instruction::LocalGet(found_idx));
ctx.emit(Instruction::I32Const(-1));
ctx.emit(Instruction::I32Ne);
ctx.emit(Instruction::Select);
Ok(())
}
pub(super) fn emit_set_to_list(ctx: &mut WasmGenCtx, args: &[Calcit]) -> Result<(), String> {
if args.len() != 1 {
return Err("&set:to-list expects 1 arg".into());
}
let src = emit_ptr_to_i32(ctx, &args[0])?;
let count = emit_load_count_i32(ctx, src);
let total_slots = ctx.alloc_local_typed(ValType::I32);
ctx.emit(Instruction::LocalGet(count));
ctx.emit(Instruction::I32Const(1));
ctx.emit(Instruction::I32Add);
ctx.emit(Instruction::LocalSet(total_slots));
let dst = emit_alloc_with_count(ctx, count, total_slots, "list");
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);
ctx.emit(Instruction::LocalGet(dst));
ctx.emit(Instruction::F64ConvertI32U);
Ok(())
}
pub(super) fn emit_set_include(ctx: &mut WasmGenCtx, args: &[Calcit]) -> Result<(), String> {
if args.len() != 2 {
return Err("&include expects 2 args".into());
}
let src = emit_ptr_to_i32(ctx, &args[0])?;
let count = emit_load_count_i32(ctx, src);
let elem = ctx.alloc_local();
emit_expr(ctx, &args[1])?;
ctx.emit(Instruction::LocalSet(elem));
let found_idx = emit_runtime_lookup_i32_f64_to_i32(ctx, "__rt_set_find_elem", src, elem);
let dst = ctx.alloc_local_typed(ValType::I32);
ctx.emit(Instruction::LocalGet(src));
ctx.emit(Instruction::LocalSet(dst));
ctx.emit(Instruction::LocalGet(found_idx));
ctx.emit(Instruction::I32Const(-1));
ctx.emit(Instruction::I32Eq);
ctx.emit(Instruction::If(wasm_encoder::BlockType::Empty));
{
let nc = ctx.alloc_local_typed(ValType::I32);
ctx.emit(Instruction::LocalGet(count));
ctx.emit(Instruction::I32Const(1));
ctx.emit(Instruction::I32Add);
ctx.emit(Instruction::LocalSet(nc));
let ts = ctx.alloc_local_typed(ValType::I32);
ctx.emit(Instruction::LocalGet(nc));
ctx.emit(Instruction::I32Const(1));
ctx.emit(Instruction::I32Add);
ctx.emit(Instruction::LocalSet(ts));
let d = emit_alloc_with_count(ctx, nc, ts, "set");
ctx.emit(Instruction::LocalGet(d));
ctx.emit(Instruction::LocalSet(dst));
let db = emit_addr_offset(ctx, d, 8);
let sb = emit_addr_offset(ctx, src, 8);
emit_copy_f64_loop(ctx, db, sb, count);
ctx.emit(Instruction::LocalGet(d));
ctx.emit(Instruction::I32Const(8));
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::LocalGet(elem));
ctx.emit(Instruction::F64Store(mem_arg_f64(0)));
}
ctx.emit(Instruction::End);
ctx.emit(Instruction::LocalGet(dst));
ctx.emit(Instruction::F64ConvertI32U);
Ok(())
}
pub(super) fn emit_set_exclude(ctx: &mut WasmGenCtx, args: &[Calcit]) -> Result<(), String> {
if args.len() != 2 {
return Err("&exclude expects 2 args".into());
}
let src = emit_ptr_to_i32(ctx, &args[0])?;
let count = emit_load_count_i32(ctx, src);
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_set_find_elem", src, target);
let dst = ctx.alloc_local_typed(ValType::I32);
ctx.emit(Instruction::LocalGet(src));
ctx.emit(Instruction::LocalSet(dst));
ctx.emit(Instruction::LocalGet(found_idx));
ctx.emit(Instruction::I32Const(-1));
ctx.emit(Instruction::I32Ne);
ctx.emit(Instruction::If(wasm_encoder::BlockType::Empty));
{
let nc = ctx.alloc_local_typed(ValType::I32);
ctx.emit(Instruction::LocalGet(count));
ctx.emit(Instruction::I32Const(1));
ctx.emit(Instruction::I32Sub);
ctx.emit(Instruction::LocalSet(nc));
let ts = ctx.alloc_local_typed(ValType::I32);
ctx.emit(Instruction::LocalGet(nc));
ctx.emit(Instruction::I32Const(1));
ctx.emit(Instruction::I32Add);
ctx.emit(Instruction::LocalSet(ts));
let d = emit_alloc_with_count(ctx, nc, ts, "set");
ctx.emit(Instruction::LocalGet(d));
ctx.emit(Instruction::LocalSet(dst));
let before_n = ctx.alloc_local_typed(ValType::I32);
ctx.emit(Instruction::LocalGet(found_idx));
ctx.emit(Instruction::LocalSet(before_n));
let db1 = emit_addr_offset(ctx, d, 8);
let sb1 = emit_addr_offset(ctx, src, 8);
emit_copy_f64_loop(ctx, db1, sb1, before_n);
let after_n = ctx.alloc_local_typed(ValType::I32);
ctx.emit(Instruction::LocalGet(count));
ctx.emit(Instruction::LocalGet(found_idx));
ctx.emit(Instruction::I32Sub);
ctx.emit(Instruction::I32Const(1));
ctx.emit(Instruction::I32Sub);
ctx.emit(Instruction::LocalSet(after_n));
let db2 = ctx.alloc_local_typed(ValType::I32);
ctx.emit(Instruction::LocalGet(d));
ctx.emit(Instruction::I32Const(8));
ctx.emit(Instruction::I32Add);
ctx.emit(Instruction::LocalGet(found_idx));
ctx.emit(Instruction::I32Const(8));
ctx.emit(Instruction::I32Mul);
ctx.emit(Instruction::I32Add);
ctx.emit(Instruction::LocalSet(db2));
let sb2 = ctx.alloc_local_typed(ValType::I32);
ctx.emit(Instruction::LocalGet(src));
ctx.emit(Instruction::I32Const(8));
ctx.emit(Instruction::I32Add);
ctx.emit(Instruction::LocalGet(found_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(sb2));
emit_copy_f64_loop(ctx, db2, sb2, after_n);
}
ctx.emit(Instruction::End);
ctx.emit(Instruction::LocalGet(dst));
ctx.emit(Instruction::F64ConvertI32U);
Ok(())
}
pub(super) fn emit_set_difference(ctx: &mut WasmGenCtx, args: &[Calcit]) -> Result<(), String> {
if args.len() != 2 {
return Err("&difference expects 2 args".into());
}
let a = emit_ptr_to_i32(ctx, &args[0])?;
let a_count = emit_load_count_i32(ctx, a);
let b = emit_ptr_to_i32(ctx, &args[1])?;
let total_slots = ctx.alloc_local_typed(ValType::I32);
ctx.emit(Instruction::LocalGet(a_count));
ctx.emit(Instruction::I32Const(1));
ctx.emit(Instruction::I32Add);
ctx.emit(Instruction::LocalSet(total_slots));
let dst = emit_alloc_with_count(ctx, a_count, total_slots, "set");
let write_idx = ctx.alloc_local_typed(ValType::I32);
ctx.emit(Instruction::I32Const(0));
ctx.emit(Instruction::LocalSet(write_idx));
let ai = ctx.alloc_local_typed(ValType::I32);
ctx.emit(Instruction::I32Const(0));
ctx.emit(Instruction::LocalSet(ai));
ctx.emit(Instruction::Block(wasm_encoder::BlockType::Empty));
ctx.emit(Instruction::Loop(wasm_encoder::BlockType::Empty));
ctx.emit(Instruction::LocalGet(ai));
ctx.emit(Instruction::LocalGet(a_count));
ctx.emit(Instruction::I32GeU);
ctx.emit(Instruction::BrIf(1));
let elem = ctx.alloc_local();
ctx.emit(Instruction::LocalGet(a));
ctx.emit(Instruction::I32Const(8));
ctx.emit(Instruction::I32Add);
ctx.emit(Instruction::LocalGet(ai));
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(elem));
let found_idx = emit_runtime_lookup_i32_f64_to_i32(ctx, "__rt_set_find_elem", b, elem);
ctx.emit(Instruction::LocalGet(found_idx));
ctx.emit(Instruction::I32Const(-1));
ctx.emit(Instruction::I32Eq);
ctx.emit(Instruction::If(wasm_encoder::BlockType::Empty));
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(elem));
ctx.emit(Instruction::F64Store(mem_arg_f64(0)));
ctx.emit(Instruction::LocalGet(write_idx));
ctx.emit(Instruction::I32Const(1));
ctx.emit(Instruction::I32Add);
ctx.emit(Instruction::LocalSet(write_idx));
ctx.emit(Instruction::End);
ctx.emit(Instruction::LocalGet(ai));
ctx.emit(Instruction::I32Const(1));
ctx.emit(Instruction::I32Add);
ctx.emit(Instruction::LocalSet(ai));
ctx.emit(Instruction::Br(0));
ctx.emit(Instruction::End);
ctx.emit(Instruction::End);
ctx.emit(Instruction::LocalGet(dst));
ctx.emit(Instruction::LocalGet(write_idx));
ctx.emit(Instruction::F64ConvertI32U);
ctx.emit(Instruction::F64Store(mem_arg_f64(0)));
ctx.emit(Instruction::LocalGet(dst));
ctx.emit(Instruction::F64ConvertI32U);
Ok(())
}
pub(super) fn emit_set_union(ctx: &mut WasmGenCtx, args: &[Calcit]) -> Result<(), String> {
if args.len() != 2 {
return Err("&union expects 2 args".into());
}
let a = emit_ptr_to_i32(ctx, &args[0])?;
let a_count = emit_load_count_i32(ctx, a);
let b = emit_ptr_to_i32(ctx, &args[1])?;
let b_count = emit_load_count_i32(ctx, 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(1));
ctx.emit(Instruction::I32Add);
ctx.emit(Instruction::LocalSet(total_slots));
let dst = emit_alloc_with_count(ctx, max_count, total_slots, "set");
let db = emit_addr_offset(ctx, dst, 8);
let sb = emit_addr_offset(ctx, a, 8);
emit_copy_f64_loop(ctx, db, sb, a_count);
let write_idx = ctx.alloc_local_typed(ValType::I32);
ctx.emit(Instruction::LocalGet(a_count));
ctx.emit(Instruction::LocalSet(write_idx));
let bi = ctx.alloc_local_typed(ValType::I32);
ctx.emit(Instruction::I32Const(0));
ctx.emit(Instruction::LocalSet(bi));
ctx.emit(Instruction::Block(wasm_encoder::BlockType::Empty));
ctx.emit(Instruction::Loop(wasm_encoder::BlockType::Empty));
ctx.emit(Instruction::LocalGet(bi));
ctx.emit(Instruction::LocalGet(b_count));
ctx.emit(Instruction::I32GeU);
ctx.emit(Instruction::BrIf(1));
let be = ctx.alloc_local();
ctx.emit(Instruction::LocalGet(b));
ctx.emit(Instruction::I32Const(8));
ctx.emit(Instruction::I32Add);
ctx.emit(Instruction::LocalGet(bi));
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(be));
let found = ctx.alloc_local_typed(ValType::I32);
ctx.emit(Instruction::I32Const(0));
ctx.emit(Instruction::LocalSet(found));
let di = ctx.alloc_local_typed(ValType::I32);
ctx.emit(Instruction::I32Const(0));
ctx.emit(Instruction::LocalSet(di));
ctx.emit(Instruction::Block(wasm_encoder::BlockType::Empty));
ctx.emit(Instruction::Loop(wasm_encoder::BlockType::Empty));
ctx.emit(Instruction::LocalGet(di));
ctx.emit(Instruction::LocalGet(write_idx));
ctx.emit(Instruction::I32GeU);
ctx.emit(Instruction::BrIf(1));
ctx.emit(Instruction::LocalGet(dst));
ctx.emit(Instruction::I32Const(8));
ctx.emit(Instruction::I32Add);
ctx.emit(Instruction::LocalGet(di));
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(be));
ctx.emit(Instruction::F64Eq);
ctx.emit(Instruction::If(wasm_encoder::BlockType::Empty));
ctx.emit(Instruction::I32Const(1));
ctx.emit(Instruction::LocalSet(found));
ctx.emit(Instruction::Br(2));
ctx.emit(Instruction::End);
ctx.emit(Instruction::LocalGet(di));
ctx.emit(Instruction::I32Const(1));
ctx.emit(Instruction::I32Add);
ctx.emit(Instruction::LocalSet(di));
ctx.emit(Instruction::Br(0));
ctx.emit(Instruction::End);
ctx.emit(Instruction::End);
ctx.emit(Instruction::LocalGet(found));
ctx.emit(Instruction::I32Eqz);
ctx.emit(Instruction::If(wasm_encoder::BlockType::Empty));
{
let addr = 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(addr));
ctx.emit(Instruction::LocalGet(addr));
ctx.emit(Instruction::LocalGet(be));
ctx.emit(Instruction::F64Store(mem_arg_f64(0)));
ctx.emit(Instruction::LocalGet(write_idx));
ctx.emit(Instruction::I32Const(1));
ctx.emit(Instruction::I32Add);
ctx.emit(Instruction::LocalSet(write_idx));
}
ctx.emit(Instruction::End);
ctx.emit(Instruction::LocalGet(bi));
ctx.emit(Instruction::I32Const(1));
ctx.emit(Instruction::I32Add);
ctx.emit(Instruction::LocalSet(bi));
ctx.emit(Instruction::Br(0));
ctx.emit(Instruction::End);
ctx.emit(Instruction::End);
ctx.emit(Instruction::LocalGet(dst));
ctx.emit(Instruction::LocalGet(write_idx));
ctx.emit(Instruction::F64ConvertI32U);
ctx.emit(Instruction::F64Store(mem_arg_f64(0)));
ctx.emit(Instruction::LocalGet(dst));
ctx.emit(Instruction::F64ConvertI32U);
Ok(())
}
pub(super) fn emit_set_intersection(ctx: &mut WasmGenCtx, args: &[Calcit]) -> Result<(), String> {
if args.len() != 2 {
return Err("&set:intersection expects 2 args".into());
}
let a = emit_ptr_to_i32(ctx, &args[0])?;
let a_count = emit_load_count_i32(ctx, a);
let b = emit_ptr_to_i32(ctx, &args[1])?;
let total_slots = ctx.alloc_local_typed(ValType::I32);
ctx.emit(Instruction::LocalGet(a_count));
ctx.emit(Instruction::I32Const(1));
ctx.emit(Instruction::I32Add);
ctx.emit(Instruction::LocalSet(total_slots));
let dst = emit_alloc_with_count(ctx, a_count, total_slots, "set");
let write_idx = ctx.alloc_local_typed(ValType::I32);
ctx.emit(Instruction::I32Const(0));
ctx.emit(Instruction::LocalSet(write_idx));
let ai = ctx.alloc_local_typed(ValType::I32);
ctx.emit(Instruction::I32Const(0));
ctx.emit(Instruction::LocalSet(ai));
ctx.emit(Instruction::Block(wasm_encoder::BlockType::Empty));
ctx.emit(Instruction::Loop(wasm_encoder::BlockType::Empty));
ctx.emit(Instruction::LocalGet(ai));
ctx.emit(Instruction::LocalGet(a_count));
ctx.emit(Instruction::I32GeU);
ctx.emit(Instruction::BrIf(1));
let elem = ctx.alloc_local();
ctx.emit(Instruction::LocalGet(a));
ctx.emit(Instruction::I32Const(8));
ctx.emit(Instruction::I32Add);
ctx.emit(Instruction::LocalGet(ai));
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(elem));
let found_idx = emit_runtime_lookup_i32_f64_to_i32(ctx, "__rt_set_find_elem", b, elem);
ctx.emit(Instruction::LocalGet(found_idx));
ctx.emit(Instruction::I32Const(-1));
ctx.emit(Instruction::I32Ne);
ctx.emit(Instruction::If(wasm_encoder::BlockType::Empty));
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(elem));
ctx.emit(Instruction::F64Store(mem_arg_f64(0)));
ctx.emit(Instruction::LocalGet(write_idx));
ctx.emit(Instruction::I32Const(1));
ctx.emit(Instruction::I32Add);
ctx.emit(Instruction::LocalSet(write_idx));
ctx.emit(Instruction::End);
ctx.emit(Instruction::LocalGet(ai));
ctx.emit(Instruction::I32Const(1));
ctx.emit(Instruction::I32Add);
ctx.emit(Instruction::LocalSet(ai));
ctx.emit(Instruction::Br(0));
ctx.emit(Instruction::End);
ctx.emit(Instruction::End);
ctx.emit(Instruction::LocalGet(dst));
ctx.emit(Instruction::LocalGet(write_idx));
ctx.emit(Instruction::F64ConvertI32U);
ctx.emit(Instruction::F64Store(mem_arg_f64(0)));
ctx.emit(Instruction::LocalGet(dst));
ctx.emit(Instruction::F64ConvertI32U);
Ok(())
}
pub(super) fn emit_set_destruct(ctx: &mut WasmGenCtx, args: &[Calcit]) -> Result<(), String> {
if args.len() != 1 {
return Err("&set: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.emit(Instruction::If(wasm_encoder::BlockType::Empty));
ctx.emit(f64_const(0.0));
ctx.emit(Instruction::LocalSet(result));
ctx.emit(Instruction::Else);
{
let elem0 = ctx.alloc_local();
ctx.emit(Instruction::LocalGet(src));
ctx.emit(Instruction::F64Load(mem_arg_f64(8)));
ctx.emit(Instruction::LocalSet(elem0));
let found_idx = emit_runtime_lookup_i32_f64_to_i32(ctx, "__rt_set_find_elem", src, elem0);
let nc = ctx.alloc_local_typed(ValType::I32);
ctx.emit(Instruction::LocalGet(count));
ctx.emit(Instruction::I32Const(1));
ctx.emit(Instruction::I32Sub);
ctx.emit(Instruction::LocalSet(nc));
let ts = ctx.alloc_local_typed(ValType::I32);
ctx.emit(Instruction::LocalGet(nc));
ctx.emit(Instruction::I32Const(1));
ctx.emit(Instruction::I32Add);
ctx.emit(Instruction::LocalSet(ts));
let rest_set_ptr = emit_alloc_with_count(ctx, nc, ts, "set");
let db1 = emit_addr_offset(ctx, rest_set_ptr, 8);
let sb1 = emit_addr_offset(ctx, src, 8);
emit_copy_f64_loop(ctx, db1, sb1, found_idx);
let after_n = ctx.alloc_local_typed(ValType::I32);
ctx.emit(Instruction::LocalGet(nc));
ctx.emit(Instruction::LocalGet(found_idx));
ctx.emit(Instruction::I32Sub);
ctx.emit(Instruction::LocalSet(after_n));
let db2 = ctx.alloc_local_typed(ValType::I32);
ctx.emit(Instruction::LocalGet(rest_set_ptr));
ctx.emit(Instruction::LocalGet(found_idx));
ctx.emit(Instruction::I32Const(8));
ctx.emit(Instruction::I32Mul);
ctx.emit(Instruction::I32Add);
ctx.emit(Instruction::I32Const(8));
ctx.emit(Instruction::I32Add);
ctx.emit(Instruction::LocalSet(db2));
let sb2 = ctx.alloc_local_typed(ValType::I32);
ctx.emit(Instruction::LocalGet(src));
ctx.emit(Instruction::LocalGet(found_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::I32Const(8));
ctx.emit(Instruction::I32Add);
ctx.emit(Instruction::LocalSet(sb2));
emit_copy_f64_loop(ctx, db2, sb2, after_n);
let rest_set_f64 = ctx.alloc_local();
ctx.emit(Instruction::LocalGet(rest_set_ptr));
ctx.emit(Instruction::F64ConvertI32U);
ctx.emit(Instruction::LocalSet(rest_set_f64));
let list_ptr = ctx.alloc_local_typed(ValType::I32);
emit_bump_alloc(ctx, 24, list_ptr, "list"); ctx.emit(Instruction::LocalGet(list_ptr));
ctx.emit(f64_const(2.0));
ctx.emit(Instruction::F64Store(mem_arg_f64(0)));
ctx.emit(Instruction::LocalGet(list_ptr));
ctx.emit(Instruction::LocalGet(elem0));
ctx.emit(Instruction::F64Store(mem_arg_f64(8)));
ctx.emit(Instruction::LocalGet(list_ptr));
ctx.emit(Instruction::LocalGet(rest_set_f64));
ctx.emit(Instruction::F64Store(mem_arg_f64(16)));
ctx.emit(Instruction::LocalGet(list_ptr));
ctx.emit(Instruction::F64ConvertI32U);
ctx.emit(Instruction::LocalSet(result));
}
ctx.emit(Instruction::End);
ctx.emit(Instruction::LocalGet(result));
Ok(())
}