wasm_milkir

    WebAssembly dialect adapter for MilkIR extension operations

    wasm
    webassembly
    milkir
    compiler
    dialect
    Download zip
    Author
    Version
    0.14.0
    License
    Apache-2.0
    Last updated
    13 days ago
    Downloads
    169

    #wasm_milkir

    WebAssembly dialect adapter for MilkIR extension operations.

    wasm_milkir provides the Wasm-specific opcode set, extension descriptors, and builder helpers used with MilkIR. Frontends encode Wasm operations through typed WasmOpcode constructors, then a lowering adapter checks and decodes those extensions before machine lowering.

    Typed Wasm builders only emit IR data; they do not attach validator closures to the MilkIR function. Generic MilkIR finalization checks the extension envelope and its explicit signature, while verify_function applies context-free schema contracts. Operations whose contracts depend on module or linker metadata must use an explicit WasmValidationContext and verify_function_with_context. Wasm lowering requires the same context and validates it immediately before instruction selection.

    Typed constructors, serialized opcode names, and immediate layouts are defined once in wasm_opcodes.schema. During development, dev_build runs the deterministic tools/generate_wasm_opcodes.py generator to refresh the committed dialect_generated.mbt source. Published packages include that generated MoonBit source, so downstream builds neither load the schema at runtime nor execute development build rules.

    #Packages

    • Milky2018/wasm_milkir: Wasm opcode encoding/decoding and builder helpers for Milky2018/milkir.
    • Milky2018/wasm_milkir/frontend: portable Wasm-to-MilkIR translation with an explicit embedding environment.
    • Milky2018/wasm_milkir/native: contextual Wasm dialect validation and streaming lowering into a native target sink.

    #How it fits

    MilkIR represents common SSA operations directly. This package represents WebAssembly operations that need additional immediates or lowering semantics as typed MilkIR extensions. The extension descriptor lets a lowering pipeline validate the encoded operation before decoding it.

    Common arithmetic, calls, references, and SIMD operations use MilkIR's semantic opcode families directly. In particular, the Wasm frontend consumes SIMD memidx, alignment, and offset fields while constructing the effective address; the resulting MilkIR VectorOp carries only vector load/store semantics.

    Linear-memory base pointers use MilkIR GlobalValue declarations rather than a Wasm extension opcode. memory_base_context_field identifies the memory index, the declaration records Heap(memidx) provenance, and the frontend chooses Stable only when the embedding guarantees that the base cannot move during the function invocation. Contextual validation checks that the memory exists; wasm_milkir/native resolves the opaque field through its embedding environment.

    The frontend package translates decoded wasm_core modules into MilkIR. Its EmbeddingEnvironment requires an explicit runtime symbol prefix and optional runtime-layout descriptions; product-specific defaults belong to the runtime adapter that invokes the frontend. This keeps generated symbols and VM-context layouts configurable without making the reusable translator depend on Wasmoon.

    #Example: map Wasm reference types to MilkIR

    ///|
    test "map Wasm reference spelling to generic MilkIR references" {
    inspect(wasm_funcref_type().to_string(), content="callable_ref")
    inspect(wasm_externref_type().to_string(), content="opaque_ref")
    }

    #Example: encode a Wasm memory operation

    ///|
    test "build a Wasm memory.size extension instruction" {
    let builder = @milkir.FunctionBuilder::FunctionBuilder("memory_size")
    let symbols = RuntimeSymbols::with_runtime_prefix("example.runtime")
    let vmctx = builder.add_param(I64)
    builder.add_result(I32)
    let size = memory_size(builder, symbols, vmctx, 0)
    builder.return_([size])
    let func = builder.get_function()
    inspect(func.blocks.length(), content="1")
    verify_function(func)
    match func.blocks[0].instructions[1].opcode {
    Call(Direct(symbol, _)) =>
    inspect(symbol.name, content="example.runtime.memory_size")
    _ => inspect(false, content="true")
    }
    }

    #Example: validate and decode a Wasm extension

    ///|
    test "validate and decode a typed Wasm extension operation" {
    let opcode = WasmOpcode::RefTest(3, true)
    let ext = encode(opcode)
    let desc = descriptor(opcode)
    inspect(ext.matches_descriptor(desc), content="true")
    inspect(decode(ext) == Some(opcode), content="true")
    let malformed = @milkir.ExtOp(
    "wasm",
    "ref_test",
    FixedArray::makei(2, fn(i) { if i == 0 { 3 } else { 2 } }),
    )
    debug_inspect(
    decode_error(malformed),
    content=(
    #|Some("malformed Wasm MilkIR extension 'ref_test': immediate 1 is a bool flag encoded as 0 or 1, got 2")
    ),
    )
    }

    RuntimeSymbols

    type RuntimeSymbols derive(Eq,
    Debug
    )

    Embedding-specific external names for WebAssembly runtime helpers.

    RuntimeSymbols::equal

    RuntimeSymbols::not_equal

    fn RuntimeSymbols::not_equal(x : RuntimeSymbols, y : RuntimeSymbols) -> Bool

    RuntimeSymbols::symbol_name

    fn RuntimeSymbols::symbol_name(self : RuntimeSymbols, helper : WasmRuntimeHelper) -> String

    RuntimeSymbols::with_runtime_prefix

    fn RuntimeSymbols::with_runtime_prefix(prefix : String) -> RuntimeSymbols

    WasmDefinedTypeContract

    pub(all) enum WasmDefinedTypeContract {
    Function(WasmFunctionContract)
    Struct(Array[WasmFieldContract])
    Array(WasmFieldContract)
    } derive(Eq,
    Debug
    )

    The contextual contract for a WebAssembly indexed type.

    WasmDefinedTypeContract::equal

    WasmDefinedTypeContract::not_equal

    WasmFieldContract

    pub(all) struct WasmFieldContract {
    storage : WasmStorageContract
    mutable_ : Bool
    defaultable : Bool
    } derive(Eq,
    Debug
    )

    The contextual contract for one WebAssembly struct field or array element.

    WasmFieldContract::equal

    WasmFieldContract::not_equal

    fn WasmFieldContract::not_equal(x : WasmFieldContract, y : WasmFieldContract) -> Bool

    WasmFunctionContract

    pub(all) struct WasmFunctionContract {
    params : Array[
    Type
    ]
    results : Array[
    Type
    ]
    } derive(Eq,
    Debug
    )

    The lowered MilkIR carrier contract for a WebAssembly function type.

    WasmFunctionContract::equal

    WasmFunctionContract::not_equal

    WasmOpcode

    pub(all) enum WasmOpcode {
    WasmCall(Int)
    WasmCallIndirect(Int, Int)
    CallRef(Int)
    ReturnCall(Int)
    ReturnCallIndirect(Int, Int)
    ReturnCallRef(Int)
    GetFuncRef(Int)
    StructNew(Int)
    StructNewDefault(Int)
    StructGet(Int, Int)
    StructGetS(Int, Int, Int)
    StructGetU(Int, Int, Int)
    StructSet(Int, Int)
    ArrayNew(Int)
    ArrayNewDefault(Int)
    ArrayNewFixed(Int, Int)
    ArrayGet(Int)
    ArrayGetS(Int, Int)
    ArrayGetU(Int, Int)
    ArraySet(Int)
    ArrayLen
    ArrayFill(Int)
    ArrayCopy(Int, Int)
    ArrayNewData(Int, Int)
    ArrayNewElem(Int, Int)
    ArrayInitData(Int, Int)
    ArrayInitElem(Int, Int)
    I31New
    I31GetS
    I31GetU
    RefTest(Int, Bool)
    RefCast(Int, Bool)
    AnyConvertExtern
    ExternConvertAny
    RefEq
    Throw(Int)
    ThrowRef
    TryTableBegin(Int)
    TryTableEnd(Int)
    GetExceptionTag
    GetExceptionValue(Int)
    GetExceptionValueCount
    Delegate(Int)
    SpillLocalsForThrow(Int)
    GetSpilledLocal(Int)
    } derive(Eq, Hash,
    Debug
    )

    WasmOpcode::equal

    fn WasmOpcode::equal(WasmOpcode, WasmOpcode) -> Bool

    WasmOpcode::hash

    fn WasmOpcode::hash(self : WasmOpcode) -> Int

    WasmOpcode::hash_combine

    fn WasmOpcode::hash_combine(WasmOpcode, Hasher) -> Unit

    WasmOpcode::not_equal

    fn WasmOpcode::not_equal(x : WasmOpcode, y : WasmOpcode) -> Bool

    WasmRuntimeHelper

    pub(all) enum WasmRuntimeHelper {
    MemoryGrow
    MemorySize
    MemoryFill
    MemoryCopy
    MemoryInit
    DataDrop
    TableGrow
    TableFill
    TableCopy
    TableInit
    ElemDrop
    CancelPoll
    GcRefTest
    GcRefCast
    GcStructGet
    GcStructSet
    GcArrayGet
    GcArraySet
    GcArrayLen
    GcArrayFill
    GcArrayCopy
    GcArrayNewData
    GcArrayNewElem
    GcArrayInitData
    GcArrayInitElem
    GcTypeCheckSubtype
    GcRegisterStructInline
    GcRegisterArrayInline
    GcAllocStructSlow
    GcAllocArrayFromValuesSlow
    GcAllocArraySlow
    GcStructGetV128
    GcStructSetV128
    GcArrayGetV128
    GcArraySetV128
    GcArrayFillV128
    GcAllocStructWideSlow
    GcAllocArrayWideSlow
    GcAllocArrayFromSlotsSlow
    ExceptionTryBegin
    ExceptionTryEnd
    ExceptionThrow
    ExceptionThrowTag
    ExceptionThrowRef
    ExceptionDelegate
    ExceptionGetTag
    ExceptionGetValue
    ExceptionGetValueCount
    ExceptionSigsetjmp
    ExceptionSpillLocals
    ExceptionGetSpilledLocal
    } derive(Eq,
    Debug
    )

    Runtime operations required by WebAssembly lowering.

    The helper identity stays typed throughout lowering. An embedding assigns its external symbol spelling only when the operation enters MilkIR or native lowering.

    WasmRuntimeHelper::equal

    WasmRuntimeHelper::not_equal

    fn WasmRuntimeHelper::not_equal(x : WasmRuntimeHelper, y : WasmRuntimeHelper) -> Bool

    WasmStorageContract

    pub(all) enum WasmStorageContract {
    Value(
    Type
    )
    Packed8
    Packed16
    } derive(Eq,
    Debug
    )

    The lowered MilkIR carrier and packed representation of a WebAssembly field.

    WasmStorageContract::equal

    WasmStorageContract::not_equal

    WasmTableContract

    pub(all) struct WasmTableContract {
    index_type :
    Type

    element_type :
    Type

    } derive(Eq,
    Debug
    )

    The contextual contract for a WebAssembly table.

    WasmTableContract::equal

    WasmTableContract::not_equal

    fn WasmTableContract::not_equal(x : WasmTableContract, y : WasmTableContract) -> Bool

    WasmValidationContext

    type WasmValidationContext

    Module and linker metadata required to validate Wasm extension operations.

    The context is supplied explicitly at the Wasm adapter seam and is never stored in MilkIR. Linked direct functions use their remapped global index; indexed types, tables, tags, and segments use module-local indices.

    WasmValidationContext::empty

    Construct a context that rejects every module-indexed operation.

    This is useful for lowering functions that contain only context-free Wasm operations; it is not a fallback for module-produced MilkIR.

    WasmValidationContext::new

    Construct an explicit contextual-validation adapter.

    WasmValidationContext::validate_extension

    Validate one Wasm extension against module and linker metadata.

    WasmValidationContext::validate_global_value

    WasmValidationResolvers

    pub(all) struct WasmValidationResolvers {
    function_contract : (Int) -> WasmFunctionContract?
    defined_type_contract : (Int) -> WasmDefinedTypeContract?
    table_contract : (Int) -> WasmTableContract?
    tag_contract : (Int) -> WasmFunctionContract?
    memory_exists : (Int) -> Bool
    data_segment_exists : (Int) -> Bool
    element_segment_type : (Int) ->
    Type
    ?
    }

    Resolver interface used to construct a contextual-validation adapter.

    EXTERNREF_TAG

    let EXTERNREF_TAG : Int64

    Tag for externref values: bit 62 set.

    FUNCREF_TAG

    let FUNCREF_TAG : Int64

    Tag for funcref pointer values: bit 61 set.

    NULL_REF

    let NULL_REF : Int64

    Null reference value in the lowered reference representation.

    WASM_DIALECT

    let WASM_DIALECT : String

    Serialized dialect name used by Wasm MilkIR extension operations.

    array_copy

    fn array_copy(builder :
    FunctionBuilder
    , dst_type_idx : Int, src_type_idx : Int, dst :
    Value
    , dst_offset :
    Value
    , src :
    Value
    , src_offset :
    Value
    , count :
    Value
    ) -> Unit

    array_get_s

    fn array_get_s(builder :
    FunctionBuilder
    , type_idx : Int, array_ref :
    Value
    , index :
    Value
    , byte_width : Int) ->
    Value

    array_get_u

    fn array_get_u(builder :
    FunctionBuilder
    , type_idx : Int, array_ref :
    Value
    , index :
    Value
    , byte_width : Int) ->
    Value

    array_init_data

    fn array_init_data(builder :
    FunctionBuilder
    , type_idx : Int, data_idx : Int, array_ref :
    Value
    , arr_offset :
    Value
    , data_offset :
    Value
    , length :
    Value
    ) -> Unit

    array_init_elem

    fn array_init_elem(builder :
    FunctionBuilder
    , type_idx : Int, elem_idx : Int, array_ref :
    Value
    , arr_offset :
    Value
    , elem_offset :
    Value
    , length :
    Value
    ) -> Unit

    array_new_data

    fn array_new_data(builder :
    FunctionBuilder
    , type_idx : Int, data_idx : Int, data_offset :
    Value
    , length :
    Value
    ) ->
    Value

    array_new_default

    array_new_elem

    fn array_new_elem(builder :
    FunctionBuilder
    , type_idx : Int, elem_idx : Int, elem_offset :
    Value
    , length :
    Value
    ) ->
    Value

    array_new_fixed

    fn array_new_fixed(builder :
    FunctionBuilder
    , type_idx : Int, count : Int, elements : Array[
    Value
    ]) ->
    Value

    array_set

    call_indirect_multi

    data_drop

    fn data_drop(builder :
    FunctionBuilder
    , runtime_symbols : RuntimeSymbols, vmctx :
    Value
    , data_idx : Int) -> Unit

    decode_error

    fn decode_error(ext :
    ExtOp
    ) -> String?

    decode_memory_base_context_field

    fn decode_memory_base_context_field(field :
    ContextField
    ) -> Int?

    decode_opcode

    fn decode_opcode(opcode :
    Opcode
    ) -> WasmOpcode?

    delegate

    fn delegate(builder :
    FunctionBuilder
    , depth : Int) -> Unit

    descriptor_by_name

    fn descriptor_by_name(name : String) ->
    ExtOpDescriptor
    ?

    elem_drop

    fn elem_drop(builder :
    FunctionBuilder
    , runtime_symbols : RuntimeSymbols, vmctx :
    Value
    , elem_idx : Int) -> Unit

    emit_throw

    fn emit_throw(builder :
    FunctionBuilder
    , tag_idx : Int, values : Array[
    Value
    ]) -> Unit

    emit_throw_ref

    encode

    get_exception_value

    get_exception_value_count

    get_func_ref

    get_spilled_local

    memory_base_context_field

    fn memory_base_context_field(memory_index : Int) ->
    ContextField

    memory_copy

    fn memory_copy(builder :
    FunctionBuilder
    , runtime_symbols : RuntimeSymbols, vmctx :
    Value
    , dst_memidx : Int, src_memidx : Int, dst :
    Value
    , src :
    Value
    , size :
    Value
    ) -> Unit

    memory_grow

    fn memory_grow(builder :
    FunctionBuilder
    , runtime_symbols : RuntimeSymbols, vmctx :
    Value
    , memidx : Int, delta :
    Value
    , max_pages? : Int, is_memory64? : Bool) ->
    Value

    memory_init

    fn memory_init(builder :
    FunctionBuilder
    , runtime_symbols : RuntimeSymbols, vmctx :
    Value
    , memidx : Int, data_idx : Int, dst :
    Value
    , src :
    Value
    , size :
    Value
    ) -> Unit

    memory_size

    fn memory_size(builder :
    FunctionBuilder
    , runtime_symbols : RuntimeSymbols, vmctx :
    Value
    , memidx : Int, is_memory64? : Bool) ->
    Value

    ref_cast

    fn ref_cast(builder :
    FunctionBuilder
    , type_idx : Int, nullable : Bool, ref_val :
    Value
    , result_type :
    Type
    ) ->
    Value

    ref_test

    fn ref_test(builder :
    FunctionBuilder
    , type_idx : Int, nullable : Bool, ref_val :
    Value
    ) ->
    Value

    return_call_indirect_multi

    fn return_call_indirect_multi(builder :
    FunctionBuilder
    , type_idx : Int, table_idx : Int, callee :
    Value
    , args : Array[
    Value
    ]) -> Unit

    return_call_multi

    fn return_call_multi(builder :
    FunctionBuilder
    , func_idx : Int, args : Array[
    Value
    ]) -> Unit

    return_call_ref_multi

    fn return_call_ref_multi(builder :
    FunctionBuilder
    , type_idx : Int, func_ref :
    Value
    , args : Array[
    Value
    ]) -> Unit

    spill_locals_for_throw

    fn spill_locals_for_throw(builder :
    FunctionBuilder
    , locals : Array[
    Value
    ]) -> Unit

    struct_get

    fn struct_get(builder :
    FunctionBuilder
    , type_idx : Int, field_idx : Int, struct_ref :
    Value
    , field_type :
    Type
    ) ->
    Value

    struct_get_s

    fn struct_get_s(builder :
    FunctionBuilder
    , type_idx : Int, field_idx : Int, struct_ref :
    Value
    , byte_width : Int) ->
    Value

    struct_get_u

    fn struct_get_u(builder :
    FunctionBuilder
    , type_idx : Int, field_idx : Int, struct_ref :
    Value
    , byte_width : Int) ->
    Value

    struct_new_default

    fn struct_new_default(builder :
    FunctionBuilder
    , type_idx : Int) ->
    Value

    struct_set

    fn struct_set(builder :
    FunctionBuilder
    , type_idx : Int, field_idx : Int, struct_ref :
    Value
    , value :
    Value
    ) -> Unit

    table_copy

    fn table_copy(builder :
    FunctionBuilder
    , runtime_symbols : RuntimeSymbols, vmctx :
    Value
    , dst_table_idx : Int, src_table_idx : Int, dst :
    Value
    , src :
    Value
    , size :
    Value
    ) -> Unit

    table_grow

    fn table_grow(builder :
    FunctionBuilder
    , runtime_symbols : RuntimeSymbols, vmctx :
    Value
    , table_idx : Int, delta :
    Value
    , init_value :
    Value
    , is_table64? : Bool) ->
    Value

    table_init

    fn table_init(builder :
    FunctionBuilder
    , runtime_symbols : RuntimeSymbols, vmctx :
    Value
    , table_idx : Int, elem_idx : Int, dst :
    Value
    , src :
    Value
    , size :
    Value
    ) -> Unit

    try_table_begin

    fn try_table_begin(builder :
    FunctionBuilder
    , handler_id : Int) ->
    Value

    try_table_end

    fn try_table_end(builder :
    FunctionBuilder
    , handler_id : Int) -> Unit

    validate_extension

    fn validate_extension(view :
    ExtensionInstView
    ) -> String?

    Validate one read-only Wasm extension instruction at an adapter seam.

    validate_global_value

    fn validate_global_value(data :
    GlobalValueData
    ) -> String?

    verify_function

    Verify core MilkIR invariants and every extension against the Wasm schema.

    verify_function_with_context

    fn verify_function_with_context(func :
    Function
    , context : WasmValidationContext) -> Unit raise
    VerifyError

    Verify core MilkIR, local Wasm schema, and module-contextual contracts.

    wasm_externref_type

    fn wasm_externref_type() ->
    Type

    wasm_funcref_type

    fn wasm_funcref_type() ->
    Type