///|
fn i32_type() -> @milkir.Type {
I32
}
///|
fn i64_type() -> @milkir.Type {
I64
}
///|
pub fn wasm_funcref_type() -> @milkir.Type {
CallableRef
}
///|
pub fn wasm_externref_type() -> @milkir.Type {
OpaqueRef
}
///|
fn external_symbol(
builder : @milkir.FunctionBuilder,
name : String,
) -> @milkir.ExternalSymbol {
builder.get_function().declare_external_symbol(name)
}
///|
fn call_runtime(
builder : @milkir.FunctionBuilder,
symbol : String,
result_ty : @milkir.Type?,
args : Array[@milkir.Value],
) -> @milkir.Value? {
builder.call_symbol(external_symbol(builder, symbol), result_ty, args)
}
///|
fn emit_wasm_inst(
builder : @milkir.FunctionBuilder,
ty : @milkir.Type,
opcode : WasmOpcode,
operands : Array[@milkir.Value],
) -> @milkir.Value {
builder.emit_ext_inst(ty, encode(opcode), operands)
}
///|
fn emit_void_wasm_inst(
builder : @milkir.FunctionBuilder,
opcode : WasmOpcode,
operands : Array[@milkir.Value],
) -> Unit {
builder.emit_void_ext_inst(encode(opcode), operands)
}
///|
fn append_multi_result_inst(
builder : @milkir.FunctionBuilder,
opcode : WasmOpcode,
result_types : Array[@milkir.Type],
args : Array[@milkir.Value],
) -> Array[@milkir.Value] {
if result_types.length() == 0 {
emit_void_wasm_inst(builder, opcode, args)
return []
}
builder.emit_multi_ext_inst(result_types, encode(opcode), args)
}
///|
pub fn load_mem_base(
builder : @milkir.FunctionBuilder,
vmctx : @milkir.Value,
memidx : Int,
stability : @milkir.GlobalValueStability,
) -> @milkir.Value {
let global_value = builder
.get_function()
.declare_global_value(
@milkir.GlobalValueData::context_field(
memory_base_context_field(memidx),
stability,
Heap(memidx),
),
)
builder.bitcast(I64, builder.global_value(global_value, vmctx))
}
///|
pub fn memory_grow(
builder : @milkir.FunctionBuilder,
runtime_symbols : RuntimeSymbols,
vmctx : @milkir.Value,
memidx : Int,
delta : @milkir.Value,
max_pages? : Int,
is_memory64? : Bool = false,
) -> @milkir.Value {
let max_pages_value = max_pages.unwrap_or(-1)
let call_result = call_runtime(
builder,
runtime_symbols.symbol_name(MemoryGrow),
Some(I32),
[
vmctx,
builder.iconst_i32(memidx),
delta,
builder.iconst_i32(max_pages_value),
],
).unwrap()
if is_memory64 {
builder.sextend(I64, call_result)
} else {
call_result
}
}
///|
pub fn memory_size(
builder : @milkir.FunctionBuilder,
runtime_symbols : RuntimeSymbols,
vmctx : @milkir.Value,
memidx : Int,
is_memory64? : Bool = false,
) -> @milkir.Value {
let call_result = call_runtime(
builder,
runtime_symbols.symbol_name(MemorySize),
Some(I32),
[vmctx, builder.iconst_i32(memidx)],
).unwrap()
if is_memory64 {
builder.uextend(I64, call_result)
} else {
call_result
}
}
///|
pub fn memory_fill(
builder : @milkir.FunctionBuilder,
runtime_symbols : RuntimeSymbols,
vmctx : @milkir.Value,
memidx : Int,
dst : @milkir.Value,
val : @milkir.Value,
size : @milkir.Value,
) -> Unit {
call_runtime(builder, runtime_symbols.symbol_name(MemoryFill), None, [
vmctx,
builder.iconst_i32(memidx),
dst,
val,
size,
])
|> ignore
}
///|
pub fn memory_copy(
builder : @milkir.FunctionBuilder,
runtime_symbols : RuntimeSymbols,
vmctx : @milkir.Value,
dst_memidx : Int,
src_memidx : Int,
dst : @milkir.Value,
src : @milkir.Value,
size : @milkir.Value,
) -> Unit {
call_runtime(builder, runtime_symbols.symbol_name(MemoryCopy), None, [
vmctx,
builder.iconst_i32(dst_memidx),
builder.iconst_i32(src_memidx),
dst,
src,
size,
])
|> ignore
}
///|
pub fn memory_init(
builder : @milkir.FunctionBuilder,
runtime_symbols : RuntimeSymbols,
vmctx : @milkir.Value,
memidx : Int,
data_idx : Int,
dst : @milkir.Value,
src : @milkir.Value,
size : @milkir.Value,
) -> Unit {
call_runtime(builder, runtime_symbols.symbol_name(MemoryInit), None, [
vmctx,
builder.iconst_i32(memidx),
builder.iconst_i32(data_idx),
dst,
src,
size,
])
|> ignore
}
///|
pub fn data_drop(
builder : @milkir.FunctionBuilder,
runtime_symbols : RuntimeSymbols,
vmctx : @milkir.Value,
data_idx : Int,
) -> Unit {
call_runtime(builder, runtime_symbols.symbol_name(DataDrop), None, [
vmctx,
builder.iconst_i32(data_idx),
])
|> ignore
}
///|
pub fn table_grow(
builder : @milkir.FunctionBuilder,
runtime_symbols : RuntimeSymbols,
vmctx : @milkir.Value,
table_idx : Int,
delta : @milkir.Value,
init_value : @milkir.Value,
is_table64? : Bool = false,
) -> @milkir.Value {
let runtime_delta = if delta.ty == I64 {
delta
} else {
builder.uextend(I64, delta)
}
let call_result = call_runtime(
builder,
runtime_symbols.symbol_name(TableGrow),
Some(I64),
[vmctx, builder.iconst_i32(table_idx), runtime_delta, init_value],
).unwrap()
if is_table64 {
call_result
} else {
builder.ireduce(I32, call_result)
}
}
///|
pub fn table_fill(
builder : @milkir.FunctionBuilder,
runtime_symbols : RuntimeSymbols,
vmctx : @milkir.Value,
table_idx : Int,
dst : @milkir.Value,
val : @milkir.Value,
size : @milkir.Value,
) -> Unit {
call_runtime(builder, runtime_symbols.symbol_name(TableFill), None, [
vmctx,
builder.iconst_i32(table_idx),
dst,
val,
size,
])
|> ignore
}
///|
pub fn table_copy(
builder : @milkir.FunctionBuilder,
runtime_symbols : RuntimeSymbols,
vmctx : @milkir.Value,
dst_table_idx : Int,
src_table_idx : Int,
dst : @milkir.Value,
src : @milkir.Value,
size : @milkir.Value,
) -> Unit {
call_runtime(builder, runtime_symbols.symbol_name(TableCopy), None, [
vmctx,
builder.iconst_i32(dst_table_idx),
builder.iconst_i32(src_table_idx),
dst,
src,
size,
])
|> ignore
}
///|
pub fn table_init(
builder : @milkir.FunctionBuilder,
runtime_symbols : RuntimeSymbols,
vmctx : @milkir.Value,
table_idx : Int,
elem_idx : Int,
dst : @milkir.Value,
src : @milkir.Value,
size : @milkir.Value,
) -> Unit {
call_runtime(builder, runtime_symbols.symbol_name(TableInit), None, [
vmctx,
builder.iconst_i32(table_idx),
builder.iconst_i32(elem_idx),
dst,
src,
size,
])
|> ignore
}
///|
pub fn elem_drop(
builder : @milkir.FunctionBuilder,
runtime_symbols : RuntimeSymbols,
vmctx : @milkir.Value,
elem_idx : Int,
) -> Unit {
call_runtime(builder, runtime_symbols.symbol_name(ElemDrop), None, [
vmctx,
builder.iconst_i32(elem_idx),
])
|> ignore
}
///|
pub fn get_func_ref(
builder : @milkir.FunctionBuilder,
func_idx : Int,
) -> @milkir.Value {
emit_wasm_inst(builder, wasm_funcref_type(), GetFuncRef(func_idx), [])
}
///|
pub fn call_multi(
builder : @milkir.FunctionBuilder,
func_idx : Int,
result_types : Array[@milkir.Type],
args : Array[@milkir.Value],
) -> Array[@milkir.Value] {
append_multi_result_inst(builder, WasmCall(func_idx), result_types, args)
}
///|
pub fn return_call_multi(
builder : @milkir.FunctionBuilder,
func_idx : Int,
args : Array[@milkir.Value],
) -> Unit {
emit_void_wasm_inst(builder, ReturnCall(func_idx), args)
}
///|
pub fn return_call_indirect_multi(
builder : @milkir.FunctionBuilder,
type_idx : Int,
table_idx : Int,
callee : @milkir.Value,
args : Array[@milkir.Value],
) -> Unit {
let all_args : Array[@milkir.Value] = [callee]
for arg in args {
all_args.push(arg)
}
emit_void_wasm_inst(
builder,
ReturnCallIndirect(type_idx, table_idx),
all_args,
)
}
///|
pub fn return_call_ref_multi(
builder : @milkir.FunctionBuilder,
type_idx : Int,
func_ref : @milkir.Value,
args : Array[@milkir.Value],
) -> Unit {
let all_args : Array[@milkir.Value] = [func_ref]
for arg in args {
all_args.push(arg)
}
emit_void_wasm_inst(builder, ReturnCallRef(type_idx), all_args)
}
///|
pub fn call_indirect_multi(
builder : @milkir.FunctionBuilder,
type_idx : Int,
table_idx : Int,
result_types : Array[@milkir.Type],
callee : @milkir.Value,
args : Array[@milkir.Value],
) -> Array[@milkir.Value] {
let all_args : Array[@milkir.Value] = [callee]
for arg in args {
all_args.push(arg)
}
append_multi_result_inst(
builder,
WasmCallIndirect(type_idx, table_idx),
result_types,
all_args,
)
}
///|
pub fn call_ref_multi(
builder : @milkir.FunctionBuilder,
type_idx : Int,
result_types : Array[@milkir.Type],
func_ref : @milkir.Value,
args : Array[@milkir.Value],
) -> Array[@milkir.Value] {
let all_args : Array[@milkir.Value] = [func_ref]
for arg in args {
all_args.push(arg)
}
append_multi_result_inst(builder, CallRef(type_idx), result_types, all_args)
}
///|
pub fn struct_new(
builder : @milkir.FunctionBuilder,
type_idx : Int,
fields : Array[@milkir.Value],
) -> @milkir.Value {
emit_wasm_inst(builder, Ref, StructNew(type_idx), fields)
}
///|
pub fn struct_new_default(
builder : @milkir.FunctionBuilder,
type_idx : Int,
) -> @milkir.Value {
emit_wasm_inst(builder, Ref, StructNewDefault(type_idx), [])
}
///|
pub fn struct_get(
builder : @milkir.FunctionBuilder,
type_idx : Int,
field_idx : Int,
struct_ref : @milkir.Value,
field_type : @milkir.Type,
) -> @milkir.Value {
emit_wasm_inst(builder, field_type, StructGet(type_idx, field_idx), [
struct_ref,
])
}
///|
pub fn struct_get_s(
builder : @milkir.FunctionBuilder,
type_idx : Int,
field_idx : Int,
struct_ref : @milkir.Value,
byte_width : Int,
) -> @milkir.Value {
emit_wasm_inst(builder, I32, StructGetS(type_idx, field_idx, byte_width), [
struct_ref,
])
}
///|
pub fn struct_get_u(
builder : @milkir.FunctionBuilder,
type_idx : Int,
field_idx : Int,
struct_ref : @milkir.Value,
byte_width : Int,
) -> @milkir.Value {
emit_wasm_inst(builder, I32, StructGetU(type_idx, field_idx, byte_width), [
struct_ref,
])
}
///|
pub fn struct_set(
builder : @milkir.FunctionBuilder,
type_idx : Int,
field_idx : Int,
struct_ref : @milkir.Value,
value : @milkir.Value,
) -> Unit {
emit_void_wasm_inst(builder, StructSet(type_idx, field_idx), [
struct_ref, value,
])
}
///|
pub fn array_new(
builder : @milkir.FunctionBuilder,
type_idx : Int,
init_value : @milkir.Value,
length : @milkir.Value,
) -> @milkir.Value {
emit_wasm_inst(builder, Ref, ArrayNew(type_idx), [init_value, length])
}
///|
pub fn array_new_default(
builder : @milkir.FunctionBuilder,
type_idx : Int,
length : @milkir.Value,
) -> @milkir.Value {
emit_wasm_inst(builder, Ref, ArrayNewDefault(type_idx), [length])
}
///|
pub fn array_new_fixed(
builder : @milkir.FunctionBuilder,
type_idx : Int,
count : Int,
elements : Array[@milkir.Value],
) -> @milkir.Value {
emit_wasm_inst(builder, Ref, ArrayNewFixed(type_idx, count), elements)
}
///|
pub fn array_get(
builder : @milkir.FunctionBuilder,
type_idx : Int,
array_ref : @milkir.Value,
index : @milkir.Value,
element_type : @milkir.Type,
) -> @milkir.Value {
emit_wasm_inst(builder, element_type, ArrayGet(type_idx), [array_ref, index])
}
///|
pub fn array_get_s(
builder : @milkir.FunctionBuilder,
type_idx : Int,
array_ref : @milkir.Value,
index : @milkir.Value,
byte_width : Int,
) -> @milkir.Value {
emit_wasm_inst(builder, I32, ArrayGetS(type_idx, byte_width), [
array_ref, index,
])
}
///|
pub fn array_get_u(
builder : @milkir.FunctionBuilder,
type_idx : Int,
array_ref : @milkir.Value,
index : @milkir.Value,
byte_width : Int,
) -> @milkir.Value {
emit_wasm_inst(builder, I32, ArrayGetU(type_idx, byte_width), [
array_ref, index,
])
}
///|
pub fn array_set(
builder : @milkir.FunctionBuilder,
type_idx : Int,
array_ref : @milkir.Value,
index : @milkir.Value,
value : @milkir.Value,
) -> Unit {
emit_void_wasm_inst(builder, ArraySet(type_idx), [array_ref, index, value])
}
///|
pub fn array_len(
builder : @milkir.FunctionBuilder,
array_ref : @milkir.Value,
) -> @milkir.Value {
emit_wasm_inst(builder, i32_type(), ArrayLen, [array_ref])
}
///|
pub fn array_fill(
builder : @milkir.FunctionBuilder,
type_idx : Int,
array_ref : @milkir.Value,
offset : @milkir.Value,
value : @milkir.Value,
count : @milkir.Value,
) -> Unit {
emit_void_wasm_inst(builder, ArrayFill(type_idx), [
array_ref, offset, value, count,
])
}
///|
pub fn array_copy(
builder : @milkir.FunctionBuilder,
dst_type_idx : Int,
src_type_idx : Int,
dst : @milkir.Value,
dst_offset : @milkir.Value,
src : @milkir.Value,
src_offset : @milkir.Value,
count : @milkir.Value,
) -> Unit {
emit_void_wasm_inst(builder, ArrayCopy(dst_type_idx, src_type_idx), [
dst, dst_offset, src, src_offset, count,
])
}
///|
pub fn array_new_data(
builder : @milkir.FunctionBuilder,
type_idx : Int,
data_idx : Int,
data_offset : @milkir.Value,
length : @milkir.Value,
) -> @milkir.Value {
emit_wasm_inst(builder, Ref, ArrayNewData(type_idx, data_idx), [
data_offset, length,
])
}
///|
pub fn array_new_elem(
builder : @milkir.FunctionBuilder,
type_idx : Int,
elem_idx : Int,
elem_offset : @milkir.Value,
length : @milkir.Value,
) -> @milkir.Value {
emit_wasm_inst(builder, Ref, ArrayNewElem(type_idx, elem_idx), [
elem_offset, length,
])
}
///|
pub fn array_init_data(
builder : @milkir.FunctionBuilder,
type_idx : Int,
data_idx : Int,
array_ref : @milkir.Value,
arr_offset : @milkir.Value,
data_offset : @milkir.Value,
length : @milkir.Value,
) -> Unit {
emit_void_wasm_inst(builder, ArrayInitData(type_idx, data_idx), [
array_ref, arr_offset, data_offset, length,
])
}
///|
pub fn array_init_elem(
builder : @milkir.FunctionBuilder,
type_idx : Int,
elem_idx : Int,
array_ref : @milkir.Value,
arr_offset : @milkir.Value,
elem_offset : @milkir.Value,
length : @milkir.Value,
) -> Unit {
emit_void_wasm_inst(builder, ArrayInitElem(type_idx, elem_idx), [
array_ref, arr_offset, elem_offset, length,
])
}
///|
pub fn i31_new(
builder : @milkir.FunctionBuilder,
value : @milkir.Value,
) -> @milkir.Value {
emit_wasm_inst(builder, Ref, I31New, [value])
}
///|
pub fn i31_get_s(
builder : @milkir.FunctionBuilder,
i31_ref : @milkir.Value,
) -> @milkir.Value {
emit_wasm_inst(builder, i32_type(), I31GetS, [i31_ref])
}
///|
pub fn i31_get_u(
builder : @milkir.FunctionBuilder,
i31_ref : @milkir.Value,
) -> @milkir.Value {
emit_wasm_inst(builder, i32_type(), I31GetU, [i31_ref])
}
///|
pub fn ref_test(
builder : @milkir.FunctionBuilder,
type_idx : Int,
nullable : Bool,
ref_val : @milkir.Value,
) -> @milkir.Value {
emit_wasm_inst(builder, i32_type(), RefTest(type_idx, nullable), [ref_val])
}
///|
pub fn ref_cast(
builder : @milkir.FunctionBuilder,
type_idx : Int,
nullable : Bool,
ref_val : @milkir.Value,
result_type : @milkir.Type,
) -> @milkir.Value {
emit_wasm_inst(builder, result_type, RefCast(type_idx, nullable), [ref_val])
}
///|
pub fn any_convert_extern(
builder : @milkir.FunctionBuilder,
extern_ref : @milkir.Value,
) -> @milkir.Value {
emit_wasm_inst(builder, Ref, AnyConvertExtern, [extern_ref])
}
///|
pub fn extern_convert_any(
builder : @milkir.FunctionBuilder,
any_ref : @milkir.Value,
) -> @milkir.Value {
emit_wasm_inst(builder, OpaqueRef, ExternConvertAny, [any_ref])
}
///|
pub fn ref_eq(
builder : @milkir.FunctionBuilder,
ref1 : @milkir.Value,
ref2 : @milkir.Value,
) -> @milkir.Value {
emit_wasm_inst(builder, i32_type(), RefEq, [ref1, ref2])
}
///|
pub fn emit_throw(
builder : @milkir.FunctionBuilder,
tag_idx : Int,
values : Array[@milkir.Value],
) -> Unit {
emit_void_wasm_inst(builder, Throw(tag_idx), values)
}
///|
pub fn emit_throw_ref(
builder : @milkir.FunctionBuilder,
exnref : @milkir.Value,
) -> Unit {
emit_void_wasm_inst(builder, ThrowRef, [exnref])
}
///|
pub fn try_table_begin(
builder : @milkir.FunctionBuilder,
handler_id : Int,
) -> @milkir.Value {
emit_wasm_inst(builder, i32_type(), TryTableBegin(handler_id), [])
}
///|
pub fn try_table_end(
builder : @milkir.FunctionBuilder,
handler_id : Int,
) -> Unit {
emit_void_wasm_inst(builder, TryTableEnd(handler_id), [])
}
///|
pub fn get_exception_tag(builder : @milkir.FunctionBuilder) -> @milkir.Value {
emit_wasm_inst(builder, i32_type(), GetExceptionTag, [])
}
///|
pub fn get_exception_value(
builder : @milkir.FunctionBuilder,
idx : Int,
) -> @milkir.Value {
emit_wasm_inst(builder, i64_type(), GetExceptionValue(idx), [])
}
///|
pub fn get_exception_value_count(
builder : @milkir.FunctionBuilder,
) -> @milkir.Value {
emit_wasm_inst(builder, i32_type(), GetExceptionValueCount, [])
}
///|
pub fn delegate(builder : @milkir.FunctionBuilder, depth : Int) -> Unit {
emit_void_wasm_inst(builder, Delegate(depth), [])
}
///|
pub fn spill_locals_for_throw(
builder : @milkir.FunctionBuilder,
locals : Array[@milkir.Value],
) -> Unit {
emit_void_wasm_inst(builder, SpillLocalsForThrow(locals.length()), locals)
}
///|
pub fn get_spilled_local(
builder : @milkir.FunctionBuilder,
idx : Int,
) -> @milkir.Value {
emit_wasm_inst(builder, i64_type(), GetSpilledLocal(idx), [])
}