///|
fn wasm_value_type_to_milkir(ty : @types.ValueType) -> @milkir.Type {
match ty {
I32 => I32
I64 => I64
F32 => F32
F64 => F64
V128 => V128
FuncRef | RefFunc | RefFuncTyped(_) | RefNullFuncTyped(_) | NullFuncRef =>
@wasm_milkir.wasm_funcref_type()
ExternRef | RefExtern | NullExternRef => @wasm_milkir.wasm_externref_type()
_ => Ref
}
}
///|
fn get_func_type(
mod_ : @types.Module,
func_local_idx : Int,
) -> @types.FuncType raise LowerError {
if func_local_idx < 0 || func_local_idx >= mod_.funcs.length() {
raise FunctionIndexOutOfRange(index=func_local_idx)
}
let type_idx = mod_.funcs[func_local_idx]
if type_idx < 0 || type_idx >= mod_.types.length() {
raise TypeIndexOutOfRange(index=type_idx)
}
match mod_.types[type_idx].composite {
Func(func_type) => func_type
_ => raise NonFunctionType(index=type_idx)
}
}
///|
fn get_code(
mod_ : @types.Module,
func_local_idx : Int,
) -> @types.FunctionCode raise LowerError {
if func_local_idx < 0 || func_local_idx >= mod_.codes.length() {
raise MissingCode(index=func_local_idx)
}
mod_.codes[func_local_idx]
}
///|
fn module_memory_is_64(mod_ : @types.Module, memidx : Int) -> Bool? {
if memidx < 0 || memidx >= mod_.memories.length() {
None
} else {
Some(mod_.memories[memidx].is_memory64)
}
}
///|
fn first_native_capability_gap(
mod_ : @types.Module,
body : Array[@types.Instruction],
) -> String? {
for instr in body {
match instr {
MemoryFill(memidx) =>
if module_memory_is_64(mod_, memidx) == Some(true) {
return Some(
"memory64 memory.fill requires a reusable runtime helper ABI",
)
}
MemoryCopy(dst_memidx, src_memidx) => {
if module_memory_is_64(mod_, dst_memidx) == Some(true) {
return Some(
"memory64 memory.copy destination requires a reusable runtime helper ABI",
)
}
if module_memory_is_64(mod_, src_memidx) == Some(true) {
return Some(
"memory64 memory.copy source requires a reusable runtime helper ABI",
)
}
}
MemoryInit(memidx, _) =>
if module_memory_is_64(mod_, memidx) == Some(true) {
return Some(
"memory64 memory.init requires a reusable runtime helper ABI",
)
}
Block(_, nested) | Loop(_, nested) =>
if first_native_capability_gap(mod_, nested) is Some(reason) {
return Some(reason)
}
If(_, then_body, else_body) => {
if first_native_capability_gap(mod_, then_body) is Some(reason) {
return Some(reason)
}
if first_native_capability_gap(mod_, else_body) is Some(reason) {
return Some(reason)
}
return Some(
"general structured if requires block and label-stack lowering",
)
}
Br(_)
| BrIf(_)
| BrTable(_, _)
| BrOnNull(_)
| BrOnNonNull(_)
| TryTable(_, _, _) =>
return Some("structured branches require label-stack lowering")
_ => ()
}
}
None
}
///|
fn native_lowering_rejection_reason(
mod_ : @types.Module,
code : @types.FunctionCode,
) -> String {
first_native_capability_gap(mod_, code.body).unwrap_or(
"native linear frontend does not support this function body shape",
)
}
///|
fn function_name(mod_ : @types.Module, func_local_idx : Int) -> String {
match mod_.func_names.get(func_local_idx) {
Some(name) => name
None => "wasm_func_\{func_local_idx}"
}
}
///|
pub fn lower_function(
env : EmbeddingEnvironment,
mod_ : @types.Module,
func_local_idx : Int,
) -> @milkir.Function raise LowerError {
if try_lower_linear_function(env, mod_, func_local_idx) is Some(func) {
return func
}
let code = get_code(mod_, func_local_idx)
raise UnsupportedNativeLowering(
index=func_local_idx,
reason=native_lowering_rejection_reason(mod_, code),
)
}
///|
pub fn try_lower_native_function(
env : EmbeddingEnvironment,
mod_ : @types.Module,
func_local_idx : Int,
) -> @milkir.Function? raise LowerError {
try_lower_linear_function(env, mod_, func_local_idx)
}