Web Assembly

Web Assembly (wasm) is a portable binary format designed for the web.

http://webassembly.org/

Creating a wasm module

A WASM Module can be created from its text representation, a corresponding tuple structure, a bytes object or binary file, or from another Module object:

>>> from ppci import wasm
>>> code = '(module (func $truth (result i32) (i32.const 42) (return)))'
>>> m1 = wasm.Module(code)
>>> m2 = wasm.Module(m1.to_bytes())

The read_wasm() function is a thin wrapper around the Module constructor.

Exporting a wasm module

A wasm module can be exported to text or binary form:

>>> code = '(module (func $truth (result i32) (i32.const 42) (return)))'
>>> m = wasm.Module(code)
>>> m.to_string()
'(module\n  (type $0 (func (result i32)))\n  (func $truth (type $0)\n    i32.const 42\n    return)\n)\n'
>>> m.to_bytes()
b'\x00asm\x01\x00\x00\x00\x01\x05\x01`\x00\x01\x7f\x03\x02\x01\x00\n\x07\x01\x05\x00A*\x0f\x0b'

And can also be “displayed” in these forms:

>>> m.show()
(module
  (type $0 (func (result i32)))
  (func $truth (type $0)
    i32.const 42
    return)
)

>>> m.show_bytes()
00000000  00 61 73 6d 01 00 00 00  01 05 01 60 00 01 7f 03  |.asm.......`....|
00000010  02 01 00 0a 07 01 05 00  41 2a 0f 0b              |........A*..|

Running wasm

Wasm can be executed in node:

wasm.run_wasm_in_node(m)

Or in the browser by exporing an html file:

wasm.export_wasm_example('~/wasm.html', code, m)

Inside a jupyter notebook, the WASM can be run directly:

wasm.run_wasm_in_notebook(m)

Running in the Python process:

>>> code = '(module (func (export truth) (result i32) (i32.const 42) (return)))'
>>> m1 = wasm.Module(code)
>>> imports = {}  # Python function imports can be added here
>>> loaded = wasm.instantiate(m1, imports)
>>> loaded.exports.truth()
42

Running WASI modules can be done from command line:

$ python -m ppci.cli.wabt run --target native coremark-wasi.wasm

Note that you can pass arguments to the WASI executable by using double dash:

$ python -m ppci.cli.wabt run –target native coremark-wasi.wasm – -h

Converting between wasm and ir

With the ppci.wasm.wasm_to_ir() class it is possible to translate wasm code to ir-code. It is also possible to translate ir-code into wasm with the ppci.wasm.ir_to_wasm() function. This allows, for instance, running C3 on the web, or Web Assembly on a microprocessor.

The basic functionality is there, but more work is needed e.g. a stdlib for this functionality to be generally useful.

>>> from ppci import wasm
>>> from ppci.wasm.arch import WasmArchitecture
>>> code = '(module (func $truth (result i32) (i32.const 42) (return)))'
>>> m1 = wasm.Module(code)
>>> arch = WasmArchitecture()
>>> ir = wasm.wasm_to_ir(m1, arch.info.get_type_info('ptr'))
>>> m2 = wasm.ir_to_wasm(ir)

Module reference

Tools for representing, loading and exporting WASM (Web Assembly), and for converting between PPCI-IR and WASM.

class ppci.wasm.WASMComponent(*input)

Base class for representing components of a WASM module, from the Module to Imports, Funct and Instruction. These components can be shown as text or written as bytes.

Each component can be instantiated using:

  • its attributes (the most direct method).
  • a tuple representing an S-expression.
  • a string representing an S-expression.
  • a bytes object representing the binary form of a component.
  • a file object that contains the binary form of a component.
show()

Print the S-expression of the component.

to_string()

Get the textual representation (S-expression) of this component.

to_tuple()

Get the component’s tuple representation (by exporting to string and parsing the s-expression).

class ppci.wasm.Instruction(*input)

Class ro represent an instruction (an opcode plus arguments).

to_string()

Get the textual representation (S-expression) of this component.

class ppci.wasm.BlockInstruction(*input)

An instruction that represents a block of instructions. (block, loop or if). The args consists of a single element indicating the result type. It can optionally have an id.

to_string()

Get the textual representation (S-expression) of this component.

class ppci.wasm.Module(*input)

Class to represent a WASM module; the toplevel unit of code.

The Module is a collection of definitions, which can be provided as text, tuples, or Definition objects.

add_definition(d)

Add a definition to the module.

get_definitions_per_section()

Get a dictionary that maps section names to definitions. Note that the ‘code’ section is named ‘func’. Note that one can also use e.g. module['type'] to get all typedefs.

show_bytes()

Show the binary representation of this WASM module.

show_interface()

Show the (signature of) imports and exports in a human friendly manner.

to_bytes()

Get the bytes that represent the binary WASM for this module.

to_file(f)

Write this wasm module to file

to_string()

Get the textual representation (S-expression) of this component.

class ppci.wasm.Definition(*input)

Base class for definition components.

A “definition” is a toplevel element in a WASM module that defines a type, function, import, etc.

class ppci.wasm.Type(*input)

Defines the signature of a WASM function that is imported or defined in this module.

Flat form and abbreviations:

  • In the flat form, a module has type definitions, and these are refered to with “type uses”: (type $xx).
  • A type use can be given to define the type rather than reference it, this is resolved by the Module class.
  • Multiple anonymous params may be combined: e.g. (param i32 i32), this is resoved by this class, and to_string() applies this abbreviation.

Attributes:

  • id: the id (str/int) of this definition in the type name/index space.
  • params: a list of parameter tuples ($id, type), where id can be int.
  • result: a list if type strings (0 or 1 elements in v1).
to_string()

Get the textual representation (S-expression) of this component.

class ppci.wasm.Import(*input)

Import objects (from other wasm modules or from the host environment). Imports are handled at runtime by the host environment.

Flat form and abbreviations:

  • Imports in flat form have a shape (import "foo" "bar" ...).
  • An import can be defined as func/table/memory/global that is “marked” as an import (e.g. (memory (import "foo" "bar") 1). This is resolved by the Module class.

Attributes:

  • modname: module to import from, as interpreted by host system.
  • name: name of object to import, as interpreted by host system.
  • kind: ‘func’, ‘table’, ‘memory’, or ‘global’.
  • id: the id to refer to the imported object.
  • info: a tuple who’s content depends on the kind:
    • func: (ref, ) to the type (signature).
    • table: (‘funcref’, min, max), where max can be None.
    • memory: (min, max), where max can be None.
    • global: (typ, mutable)
to_string()

Get the textual representation (S-expression) of this component.

class ppci.wasm.Table(*input)

A resizable typed array of references (e.g. to functions) that could not otherwise be stored as raw bytes in Memory (for safety and portability reasons). Only one default table can exist in v1.

A practical use-case is to store “function pointers” for e.g. callbacks. Tables allow doing that without actually exposing the memory location.

Flat form and abbreviations:

  • Since v1 mandates a single table, the id is usually omitted.
  • Elements can be specified inline, this is resolved by the Module class.
  • The call_indirect instruction has one arg that specifies the signature, i.e. no support for inline typeuse.

Attributes:

  • id: the id of this table definition in the table name/index space.
  • kind: the kind of data stored in the table, only ‘funcref’ in v1.
  • min: the minimum (initial) table size.
  • max: the maximum table size, or None.
to_string()

Get the textual representation (S-expression) of this component.

class ppci.wasm.Memory(*input)

Declares initial (and max) sizes of linear memory, expressed in WASM pages (64KiB). Only one default memory can exist in v1.

Flat form and abbreviations:

  • Since v1 mandates a single memory, the id is usually omitted.
  • Data can be specified inline, this is resolved by the Module class.

Attributes:

  • id: the id of this memory definition in the memory name/index space.
  • min: the minimum (initial) memory size.
  • max: the maximum memory size, or None.
to_string()

Get the textual representation (S-expression) of this component.

class ppci.wasm.Global(*input)

A global variable.

Attributes:

  • id: the id of this global definition in the globals name/index space.
  • typ: the value type of the global.
  • mutable: whether this global is mutable (can hurt performance).
  • init: an instruction to initialize the global (e.g. a i32.const).
to_string()

Get the textual representation (S-expression) of this component.

class ppci.wasm.Export(*input)

Export an object defined in this module.

Flat form and abbreviations:

  • Export in flat form have a shape (export "foo" ...).
  • An export can be defined as func/table/memory/global that is “marked” as an export (e.g. (memory (export "bar") 1). This is resolved by the Module class.

Attributes:

  • name: the name by which to export this value.
  • kind: the kind of value to export (‘func’, ‘table’, or ‘memory’).
  • ref: a reference to the thing being exported (in the name/index space corresponding to kind).
to_string()

Get the textual representation (S-expression) of this component.

class ppci.wasm.Start(*input)

Define the index of the function to call at init-time. The func must have zero params and return values. There must be at most 1 start definition.

Attributes:

  • ref: the reference to the function to mark as the start function.
to_string()

Get the textual representation (S-expression) of this component.

class ppci.wasm.Func(*input)

The definition (i.e. instructions) of a function.

Flat form and abbreviations:

  • In the flat form, it refers to a type (not define params inline).
  • Inline signatures are resolved by …
  • Imported functions can be defined as e.g. (func $add (import "foo" "bar")), which resolves into an Import instead of a Func.
  • Multiple anonymous locals may be combined. This is resolved by this class and to_string() applies this abbreviation.

Attributes:

  • id: the id of this func definition in the func name/index space.
  • ref: the reference to the type (i.e. signature).
  • locals: a list of ($id, typ) tuples. The id can be None to indicate implicit id’s (note that the id is offset by the parameters).
  • instructions: a list of instructions (may be given as tuples).
to_string()

Render function def as text

class ppci.wasm.Elem(*input)

Define elements to populate a table.

Flat form and abbreviations:

  • Elements can be defined inline inside Table expressions, this is resolved by the Module class.

Attributes:

  • ref: the table id that this element applies to.
  • offset: the element offset, expressed as an instruction list (i.e. [i32.const, end])
  • refs: a list of function references.
to_string()

Get the textual representation (S-expression) of this component.

class ppci.wasm.Data(*input)

Data to include in the module.

Flat form and abbreviations:

  • Data can be defined inline inside Memory expressions, this is resolved by the Module class.

Attributes:

  • ref: the memory id that this data applies to.
  • offset: the byte offset, expressed as an instruction (i.e. i32.const)
  • data: the binary data as a bytes object.
to_string()

Get the textual representation (S-expression) of this component.

class ppci.wasm.Custom(*input)

Custom binary data.

to_string()

Get the textual representation (S-expression) of this component.

class ppci.wasm.Ref(space, index=None, name=None)

This is a reference to an object in one of the 5 spaces.

space must be one of ‘type’, ‘func’, ‘memory’, ‘table’, ‘global’, ‘local’ index can be none

is_zero

Check if we refer to element 0

ppci.wasm.wasm_to_ir(wasm_module: ppci.wasm.components.Module, ptr_info, reporter=None) → ppci.ir.Module

Convert a WASM module into a PPCI native module.

Parameters:
  • wasm_module (ppci.wasm.Module) – The wasm-module to compile
  • ptr_infoppci.arch.arch_info.TypeInfo size and alignment information for pointers.
Returns:

An IR-module.

ppci.wasm.ir_to_wasm(ir_module: ppci.ir.Module, reporter=None) → ppci.wasm.components.Module

Compiles ir-code to a wasm module.

Parameters:
  • ir_module (ir.Module) – The ir-module to compile
  • reporter – optionally report compilation steps
Returns:

A wasm module.

class ppci.wasm.WasmArchitecture

Web assembly architecture description

ppci.wasm.run_wasm_in_node(wasm, silent=False)

Load a WASM module in node. Just make sure that your module has a main function.

ppci.wasm.export_wasm_example(filename, code, wasm, main_js='')

Generate an html file for the given code and wasm module.

ppci.wasm.run_wasm_in_notebook(wasm)

Load a WASM module in the Jupyter notebook.

ppci.wasm.has_node

Check if nodejs is available

ppci.wasm.instantiate(module, imports=None, target='native', reporter=None, cache_file=None)

Instantiate a wasm module.

Parameters:
  • module (ppci.wasm.Module) – The wasm-module to instantiate
  • imports – A collection of functions available to the wasm module.
  • target – Use ‘native’ to compile wasm to machine code. Use ‘python’ to generate python code. This option is slower but more reliable.
  • reporter – A reporter which can record detailed compilation information.
  • cache_file – a file to use as cache
ppci.wasm.execute_wasm(module, args, target='python', function=None, function_args=(), reporter=None)

Execute the given wasm module.

ppci.wasm.read_wasm(input) → ppci.wasm.components.Module

Read wasm in the form of a string, tuple, bytes or file object. Returns a wasm Module object.

ppci.wasm.read_wat(f) → ppci.wasm.components.Module

Read wasm module from file handle

ppci.wasm.wasmify(func, target='native')

Convert a Python function to a WASM function, compiled to native code. Assumes that all variables are floats. Can be used as a decorator, like Numba!