Skip to content

Implementing type imports and exports in Binaryen #17

Open
@vouillon

Description

@vouillon

I have started implementing type imports and exports in Binaryen. Binaryen provides a tool, wasm-merge which allows to link together several Wasm modules, connecting exports and imports. Type imports and exports can be useful in this context even if no Wasm engine supports them yet.

Here are some thoughts and remarks I have gathered in the process.

Linking process

The proposal currently does not say anything about how imports and exports are matched. The read the imports phase of the WebAssembly JavaScript Interface will need to be updated.

Module component ordering in the text format

The imported types should occur before other types.

Abbreviations in the text format

Similarly to other module components, we can have the following abbreviations:

  • for exports: (type $t (export "t") ...);
  • for imports: (type $t (import "foo" "bar" (sub ht))).

One should require that the size of recursive groups containing an imported type should be one. Or, maybe, that the imported types should occur in the same recursive group in the same order?

Casts in the extern hierarchy

At the moment, there is no non-trivial casts in the extern hierarchy. This could change with type imports. Maybe one should disallow casts in this type hierarchy to leave more implementation freedom.

Interaction with shared annotations

Given a type import (import "foo" "bar" (type $t (sub ht))), we can probably consider that the type $t is shared iff the bound ht is shared.

Type imports as composite types

Types are typically handled very differently from other module components by Wasm engines and tools. They don't keep a table of type definitions. Instead, types are directly canonicalized, type indices being replaced by references to canonical types. It does not make much sense to consider that low indices correspond to imports in this context. Instead, I have basically implemented type imports by adding another kind of composite type: (import "foo" "bar" (sub ht)). Note that this works well with canonicalization: two imports can be considered equivalent if they have the same names and bounds.

It might actually make sense to do the same for the specification and the binary format. This could resolve the issue of repeated / interleaved sections, since type imports would be in the type section. This might also make it possible to relax the restriction of import bounds to abstract heap types. One could possibly go one step further and handle type imports as subtypes (sub ht (import "foo" "bar")), using the same encoding for the supertype and the import bound.

Types no longer closed under intersection

Binaryen makes some assumptions on types that no longer hold with type imports. In particular, there are several places in the code that assume that types are closed under intersection. Or that two heap types that are not in a subtyping relation (either $t ≤ $t' or $t' ≤ $t) have no common value.

I don't think this is an issue for this proposal. But implementers need to be aware that some assumptions they may have made previously might no longer be valid.

As a concrete example, Binaryen replaces the type of a cast by the greatest upper bound of this type and the type of the input expression. This is no longer correct: if $t is an imported type and $t' is not, their greatest upper bound is none, but both types might actually become equal after instantiation. Disallowing casts for all imported typed would not avoid this cast optimization issue, since casting to a concrete type an expression with an imported type would still be allowed. Also, imported types do not need to be directly involved. These two composite types can become equal after instantiation, depending on the definitions of $u1 and $u2:

(type $t1 (struct (field $u1)))
(type $t2 (struct (field $u2)))

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions