| # wasm2c: Convert wasm files to C source and header |
| |
| `wasm2c` takes a WebAssembly module and produces an equivalent C source and |
| header. Some examples: |
| |
| ```sh |
| # parse binary file test.wasm and write test.c and test.h |
| $ wasm2c test.wasm -o test.c |
| |
| # parse test.wasm, write test.c and test.h, but ignore the debug names, if any |
| $ wasm2c test.wasm --no-debug-names -o test.c |
| ``` |
| |
| The C code produced targets the C99 standard. If, however, the Wasm module uses |
| Wasm threads/atomics, the code produced targets the C11 standard. |
| |
| ## Tutorial: .wat -> .wasm -> .c |
| |
| Let's look at a simple example of a factorial function. |
| |
| ```wasm |
| (memory $mem 1) |
| (func (export "fac") (param $x i32) (result i32) |
| (if (result i32) (i32.eq (local.get $x) (i32.const 0)) |
| (then (i32.const 1)) |
| (else |
| (i32.mul (local.get $x) (call 0 (i32.sub (local.get $x) (i32.const 1)))) |
| ) |
| ) |
| ) |
| ``` |
| |
| Save this to `fac.wat`. We can convert this to a `.wasm` file by using the |
| `wat2wasm` tool: |
| |
| ```sh |
| $ wat2wasm fac.wat -o fac.wasm |
| ``` |
| |
| We can then convert it to a C source and header by using the `wasm2c` tool: |
| |
| ```sh |
| $ wasm2c fac.wasm -o fac.c |
| ``` |
| |
| This generates two files, `fac.c` and `fac.h`. We'll take a closer look at |
| these files below, but first let's show a simple example of how to use these |
| files. |
| |
| ## Using the generated module |
| |
| To actually use our `fac` module, we'll use create a new file, `main.c`, that |
| include `fac.h`, initializes the module, and calls `fac`. |
| |
| `wasm2c` generates a few C symbols based on the `fac.wasm` module. |
| The first is `w2c_fac`, a type that represents an instance of the |
| `fac` module. `wasm2c` generates functions that construct and free a |
| `w2c_fac` instance: `wasm2c_fac_instantiate` and |
| `wasm2c_fac_free`. Finally, `wasm2c` generates the exported `fac` |
| function itself (`w2c_fac_fac`), which acts on a `w2c_fac` instance. |
| |
| All the exported symbols shared a common module ID (`fac`) which, by default, is |
| based on the name section in the module or the name of input file. This prefix |
| can be overridden using the `-n/--module-name` command line flag. |
| |
| ```c |
| #include <stdio.h> |
| #include <stdlib.h> |
| |
| #include "fac.h" |
| |
| int main(int argc, char** argv) { |
| /* Make sure there is at least one command-line argument. */ |
| if (argc < 2) { |
| printf("Invalid argument. Expected '%s NUMBER'\n", argv[0]); |
| return 1; |
| } |
| |
| /* Convert the argument from a string to an int. We'll implicitly cast the int |
| to a `u32`, which is what `fac` expects. */ |
| u32 x = atoi(argv[1]); |
| |
| /* Initialize the Wasm runtime. */ |
| wasm_rt_init(); |
| |
| /* Declare an instance of the `fac` module. */ |
| w2c_fac fac; |
| |
| /* Construct the module instance. */ |
| wasm2c_fac_instantiate(&fac); |
| |
| /* Call `fac`, using the mangled name. */ |
| u32 result = w2c_fac_fac(&fac, x); |
| |
| /* Print the result. */ |
| printf("fac(%u) -> %u\n", x, result); |
| |
| /* Free the fac module. */ |
| wasm2c_fac_free(&fac); |
| |
| /* Free the Wasm runtime state. */ |
| wasm_rt_free(); |
| |
| return 0; |
| } |
| |
| ``` |
| |
| ## Compiling the wasm2c output |
| |
| To compile the executable, we need to use `main.c` and the generated `fac.c`. |
| We'll also include `wasm-rt-impl.c` and `wasm-rt-mem-impl.c`, which have implementations of the various |
| `wasm_rt_*` functions used by `fac.c` and `fac.h`. |
| |
| ```sh |
| $ cc -o fac main.c fac.c wasm2c/wasm-rt-impl.c wasm2c/wasm-rt-mem-impl.c -Iwasm2c -lm |
| ``` |
| |
| A note on compiling with optimization: wasm2c relies on certain |
| behavior from the C compiler to maintain conformance with the |
| WebAssembly specification, especially with regards to requirements to |
| convert "signaling" to "quiet" floating-point NaN values and for |
| infinite recursion to produce a trap. When compiling with optimization |
| (e.g. `-O2` or `-O3`), it's necessary to disable some optimizations to |
| preserve conformance. With GCC 11, adding the command-line arguments |
| `-fno-optimize-sibling-calls -frounding-math -fsignaling-nans` appears |
| to be sufficient. With clang 14, just `-fno-optimize-sibling-calls |
| -frounding-math` appears to be sufficient. |
| |
| Now let's test it out! |
| |
| ```sh |
| $ ./fac 1 |
| fac(1) -> 1 |
| $ ./fac 5 |
| fac(5) -> 120 |
| $ ./fac 10 |
| fac(10) -> 3628800 |
| ``` |
| |
| You can take a look at the all of these files in |
| [wasm2c/examples/fac](/wasm2c/examples/fac). |
| |
| ### Enabling extra sanity checks |
| |
| Wasm2c provides a macro `WASM_RT_SANITY_CHECKS` that if defined enables |
| additional sanity checks in the produced Wasm2c code. Note that this may have a |
| high performance overhead, and is thus only recommended for debug builds. |
| |
| ### Enabling Segue (a Linux x86_64 target specific optimization) |
| |
| Wasm2c can use the "Segue" optimization if allowed. The segue optimization uses |
| an x86 segment register to store the location of Wasm's linear memory, when |
| compiling a Wasm module with clang, running on x86_64 Linux, the macro |
| `WASM_RT_ALLOW_SEGUE` is defined, and the flag `-mfsgsbase` is passed to clang. |
| Segue is not used if |
| |
| 1. The Wasm module uses a more than a single unshared imported or exported |
| memory |
| 2. The wasm2c code is compiled with GCC. Segue requires intrinsics for |
| (rd|wr)gsbase, "address namespaces" for accessing pointers, and support for |
| memcpy on pointers with custom "address namespaces". GCC does not support the |
| memcpy requirement. |
| 3. The code is compiled for Windows as Windows doesn't restore the segment |
| register on context switch. |
| |
| The wasm2c generated code automatically sets the unused segment register (the |
| `%gs` register on x86_64 Linux) during the function calls into wasm2c generated |
| module, restores it after calls to external modules etc. Any host function |
| written in C would continue to work without changes as C code does not modify |
| the unused segment register `%gs` (See |
| [here](https://www.kernel.org/doc/html/next/x86/x86_64/fsgs.html) for details). |
| However, any host functions written in assembly that clobber the free segment |
| register must restore the value of this register prior to executing or returning |
| control to wasm2c generated code. |
| |
| As an additional optimization, if the host program does not use the `%gs` |
| segment register for any other purpose (which is typically the case in most |
| programs), you can additionally allow wasm2c to unconditionally overwrite the |
| value of the `%gs` register without restoring the old value. This can be done |
| defining the macro `WASM_RT_SEGUE_FREE_SEGMENT`. |
| |
| You can test the performance of the Segue optimization by running Dhrystone with |
| and without Segue: |
| |
| ```bash |
| cd wasm2c/benchmarks/segue && make |
| ``` |
| |
| ## Looking at the generated header, `fac.h` |
| |
| The generated header file looks something like this: |
| |
| ```c |
| /* Automatically generated by wasm2c */ |
| #ifndef FAC_H_GENERATED_ |
| #define FAC_H_GENERATED_ |
| |
| ... |
| |
| #include "wasm-rt.h" |
| |
| ... |
| #ifndef WASM_RT_CORE_TYPES_DEFINED |
| #define WASM_RT_CORE_TYPES_DEFINED |
| |
| ... |
| |
| #endif |
| |
| #ifdef __cplusplus |
| extern "C" { |
| #endif |
| |
| typedef struct w2c_fac { |
| char dummy_member; |
| } w2c_fac; |
| |
| void wasm2c_fac_instantiate(w2c_fac*); |
| void wasm2c_fac_free(w2c_fac*); |
| wasm_rt_func_type_t wasm2c_fac_get_func_type(uint32_t param_count, uint32_t result_count, ...); |
| |
| /* export: 'fac' */ |
| u32 w2c_fac_fac(w2c_fac*, u32); |
| |
| #ifdef __cplusplus |
| } |
| #endif |
| |
| #endif /* FAC_H_GENERATED_ */ |
| |
| ``` |
| |
| Let's look at each section. The outer `#ifndef` is standard C |
| boilerplate for a header. This `WASM_RT_CORE_TYPES_DEFINED` section |
| contains a number of definitions required for all WebAssembly |
| modules. The `extern "C"` part makes sure to not mangle the symbols if |
| using this header in C++. |
| |
| The included `wasm-rt.h` file also includes a number of relevant definitions. |
| First is the `wasm_rt_trap_t` enum, which is used to give the reason a trap |
| occurred. |
| |
| ```c |
| typedef enum { |
| WASM_RT_TRAP_NONE, |
| WASM_RT_TRAP_OOB, |
| WASM_RT_TRAP_INT_OVERFLOW, |
| WASM_RT_TRAP_DIV_BY_ZERO, |
| WASM_RT_TRAP_INVALID_CONVERSION, |
| WASM_RT_TRAP_UNREACHABLE, |
| WASM_RT_TRAP_CALL_INDIRECT, |
| WASM_RT_TRAP_UNCAUGHT_EXCEPTION, |
| WASM_RT_TRAP_EXHAUSTION, |
| } wasm_rt_trap_t; |
| ``` |
| |
| Next is the `wasm_rt_type_t` enum, which is used for specifying function |
| signatures. Six WebAssembly value types are included: |
| |
| ```c |
| typedef enum { |
| WASM_RT_I32, |
| WASM_RT_I64, |
| WASM_RT_F32, |
| WASM_RT_F64, |
| WASM_RT_FUNCREF, |
| WASM_RT_EXTERNREF, |
| } wasm_rt_type_t; |
| |
| Next is `wasm_rt_function_ptr_t`, the function signature for a generic function |
| callback. Since a WebAssembly table can contain functions of any given |
| signature, it is necessary to convert them to a canonical form: |
| |
| ```c |
| typedef void (*wasm_rt_function_ptr_t)(void); |
| ``` |
| |
| Next is the definition for a function reference (in WebAssembly 1.0, |
| this was the type of all table elements, but funcrefs can now also be |
| used as ordinary values, and tables can alternately be declared as |
| type externref). In this structure, `wasm_rt_func_type_t` is an opaque |
| 256-bit ID that can be looked up via the `Z_[modname]_get_func_type` |
| function. (A demonstration of this can be found in the `callback` |
| example.) `module_instance` is the pointer to the function's |
| originating module instance, which will be passed in when the func is |
| called. |
| |
| ```c |
| typedef struct { |
| wasm_rt_func_type_t func_type; |
| wasm_rt_function_ptr_t func; |
| void* module_instance; |
| } wasm_rt_funcref_t; |
| |
| ``` |
| |
| Next is the definition of a memory instance. The `data` field is a pointer to |
| `size` bytes of linear memory. The `size` field of `wasm_rt_memory_t` is the |
| current size of the memory instance in bytes, `pages` is the current |
| size in pages, and `page_size` contains the page size in bytes (65,536 by default). |
| `max_pages` is the maximum number of pages specified by the module or allowed |
| by the memory index type (`is64` is true for memories that can grow to 2^64 bytes; |
| `false` for memories limited to 2^32 bytes). |
| |
| ```c |
| typedef struct { |
| uint8_t* data; |
| uint32_t page_size; |
| uint64_t pages, max_pages; |
| uint64_t size; |
| bool is64; |
| } wasm_rt_memory_t; |
| ``` |
| |
| This is followed by the definition of a shared memory instance. This is similar |
| to a regular memory instance, but represents memory that can be used by multiple |
| Wasm instances, and thus enforces a minimum amount of memory order on |
| operations. The Shared memory definition has one additional member, `mem_lock`, |
| which is a lock that is used during memory grow operations for thread safety. |
| |
| ```c |
| typedef struct { |
| _Atomic volatile uint8_t* data; |
| uint64_t pages, max_pages; |
| uint64_t size; |
| bool is64; |
| mtx_t mem_lock; |
| } wasm_rt_shared_memory_t; |
| ``` |
| |
| Next is the definition of a table instance. The `data` field is a pointer to |
| `size` elements. Like a memory instance, `size` is the current size of a table, |
| and `max_size` is the maximum size of the table, or `0xffffffff` if there is no |
| limit. |
| |
| ```c |
| typedef struct { |
| wasm_rt_funcref_t* data; |
| uint32_t max_size; |
| uint32_t size; |
| } wasm_rt_funcref_table_t; |
| ``` |
| |
| ## Symbols that must be defined by the embedder |
| |
| Next in `wasm-rt.h` are a collection of function declarations that must be implemented by |
| the embedder (i.e. you) before this C source can be used. |
| |
| A C implementation of these functions is defined in |
| [`wasm-rt-impl.h`](wasm-rt-impl.h) and [`wasm-rt-impl.c`](wasm-rt-impl.c). |
| |
| ```c |
| void wasm_rt_init(void); |
| bool wasm_rt_is_initialized(void); |
| void wasm_rt_free(void); |
| void wasm_rt_trap(wasm_rt_trap_t) __attribute__((noreturn)); |
| const char* wasm_rt_strerror(wasm_rt_trap_t trap); |
| void wasm_rt_allocate_memory(wasm_rt_memory_t*, uint32_t initial_pages, uint32_t max_pages, bool is64, uint32_t page_size); |
| uint32_t wasm_rt_grow_memory(wasm_rt_memory_t*, uint32_t pages); |
| void wasm_rt_free_memory(wasm_rt_memory_t*); |
| void wasm_rt_allocate_memory_shared(wasm_rt_shared_memory_t*, uint32_t initial_pages, uint32_t max_pages, bool is64, uint32_t page_size); |
| uint32_t wasm_rt_grow_memory_shared(wasm_rt_shared_memory_t*, uint32_t pages); |
| void wasm_rt_free_memory_shared(wasm_rt_shared_memory_t*); |
| void wasm_rt_allocate_funcref_table(wasm_rt_table_t*, uint32_t elements, uint32_t max_elements); |
| void wasm_rt_allocate_externref_table(wasm_rt_externref_table_t*, uint32_t elements, uint32_t max_elements); |
| void wasm_rt_free_funcref_table(wasm_rt_table_t*); |
| void wasm_rt_free_externref_table(wasm_rt_table_t*); |
| uint32_t wasm_rt_call_stack_depth; /* on platforms that don't use the signal handler to detect exhaustion */ |
| void wasm_rt_init_thread(void); |
| void wasm_rt_free_thread(void); |
| ``` |
| |
| `wasm_rt_init` must be called by the embedder before anything else, to |
| initialize the runtime. `wasm_rt_free` frees any global |
| state. `wasm_rt_is_initialized` can be used to confirm that the |
| runtime has been initialized. |
| |
| `wasm_rt_trap` is a function that is called when the module traps. Some possible |
| implementations are to throw a C++ exception, or to just abort the program |
| execution. The default runtime included in wasm2c unwinds the stack using |
| `longjmp`. The host can overide this call to `longjmp` by compiling the runtime |
| with `WASM_RT_TRAP_HANDLER` defined to the name of a trap handler function. The |
| handler function should be a function taking a `wasm_rt_trap_t` as a parameter |
| and returning `void`. e.g. `-DWASM_RT_TRAP_HANDLER=my_trap_handler` |
| |
| `wasm_rt_allocate_memory` initializes a memory instance, and allocates |
| at least enough space for the given number of initial pages, each of |
| size `page_size` (which must be `WASM_DEFAULT_PAGE_SIZE`, equal to 64 |
| KiB, unless using the custom-page-sizes feature). The memory must be |
| cleared to zero. The `is64` parameter indicates if the memory is |
| indexed with an i32 or i64 address. |
| |
| `wasm_rt_grow_memory` must grow the given memory instance by the given number of |
| pages. If there isn't enough memory to do so, or the new page count would be |
| greater than the maximum page count, the function must fail by returning |
| `0xffffffff`. If the function succeeds, it must return the previous size of the |
| memory instance, in pages. The host can optionally be notified of failures by |
| compiling the runtime with `WASM_RT_GROW_FAILED_HANDLER` defined to the name of |
| a handler function. The handler function should be a function taking no |
| arguments and returning `void` . e.g. |
| `-DWASM_RT_GROW_FAILED_HANDLER=my_growfail_handler` |
| |
| `wasm_rt_free_memory` frees the memory instance. |
| |
| `wasm_rt_allocate_memory_shared` initializes a memory instance that can be |
| shared by different Wasm threads. Its operation is otherwise similar to |
| `wasm_rt_allocate_memory`. |
| |
| `wasm_rt_grow_memory_shared` must grow the given shared memory instance by the |
| given number of pages. It's operation is otherwise similar to |
| `wasm_rt_grow_memory`. |
| |
| `wasm_rt_free_memory_shared` frees the shared memory instance. |
| |
| `wasm_rt_allocate_funcref_table` and the similar `..._externref_table` |
| initialize a table instance of the given type, and allocate at least |
| enough space for the given number of initial elements. The elements |
| must be cleared to zero. |
| |
| `wasm_rt_free_funcref_table` and `..._externref_table` free the table instance. |
| |
| `wasm_rt_call_stack_depth` is the current stack call depth. Since this is |
| shared between modules, it must be defined only once, by the embedder. |
| It is only used on platforms that don't use the signal handler to detect |
| exhaustion. |
| |
| `wasm_rt_init_thread` and `wasm_rt_free_thread` are used to initialize |
| and free the runtime state for a given thread (other than the one that |
| called `wasm_rt_init`). An example can be found in |
| `wasm2c/examples/threads`. |
| |
| ### Runtime support for exception handling |
| |
| Several additional symbols must be defined if wasm2c is being run with support |
| for exceptions (`--enable-exceptions`). These are defined in |
| `wasm-rt-exceptions.h`. These symbols are: |
| |
| ```c |
| void wasm_rt_load_exception(const char* tag, uint32_t size, const void* values); |
| WASM_RT_NO_RETURN void wasm_rt_throw(void); |
| WASM_RT_UNWIND_TARGET |
| WASM_RT_UNWIND_TARGET* wasm_rt_get_unwind_target(void); |
| void wasm_rt_set_unwind_target(WASM_RT_UNWIND_TARGET* target); |
| uint32_t wasm_rt_exception_tag(void); |
| uint32_t wasm_rt_exception_size(void); |
| void* wasm_rt_exception(void); |
| wasm_rt_try(target) |
| ``` |
| |
| A C implementation of these functions is also available in |
| [`wasm-rt-exceptions-impl.c`](wasm-rt-exceptions-impl.c). |
| |
| `wasm_rt_load_exception` sets the active exception to a given tag, size, and contents. |
| |
| `wasm_rt_throw` throws the active exception. |
| |
| `WASM_RT_UNWIND_TARGET` is the type of an unwind target if an |
| exception is thrown and caught. |
| |
| `wasm_rt_get_unwind_target` gets the current unwind target if an exception is thrown. |
| |
| `wasm_rt_set_unwind_target` sets the unwind target if an exception is thrown. |
| |
| Three functions provide access to the active exception: |
| `wasm_rt_exception_tag`, `wasm_rt_exception_size`, and |
| `wasm_rt_exception` return its tag, size, and contents, respectively. |
| |
| `wasm_rt_try(target)` is a macro that captures the current calling |
| environment as an unwind target and stores it into `target`, which |
| must be of type `WASM_RT_UNWIND_TARGET`. |
| |
| ## Exported symbols |
| |
| Finally, `fac.h` defines the module instance type (which in the case |
| of `fac` is essentially empty), and the exported symbols provided by |
| the module. In our example, the only function we exported was |
| `fac`. |
| |
| `wasm2c_fac_instantiate(w2c_fac*)` creates an instance of |
| the module and must be called before the module instance can be |
| used. `wasm2c_fac_free(w2c_fac*)` frees the instance. |
| `wasm2c_fac_get_func_type` can be used to look up a function type ID |
| at runtime. It is a variadic function where the first two arguments |
| give the number of parameters and results, and the following arguments |
| are the types from the wasm_rt_type_t enum described above. The |
| `callback` example demonstrates using this to pass a host function to |
| a WebAssembly module dynamically at runtime. |
| |
| ```c |
| typedef struct w2c_fac { |
| char dummy_member; |
| } w2c_fac; |
| |
| void wasm2c_fac_instantiate(w2c_fac*); |
| void wasm2c_fac_free(w2c_fac*); |
| wasm_rt_func_type_t wasm2c_fac_get_func_type(uint32_t param_count, uint32_t result_count, ...); |
| |
| /* export: 'fac' */ |
| u32 w2c_fac_fac(w2c_fac*, u32); |
| ``` |
| |
| ## Handling other kinds of imports and exports of modules |
| |
| Exported functions are handled by declaring a prefixed equivalent |
| function in the header. If a module imports a function, `wasm2c` |
| declares the function in the output header file, and the host function |
| is responsible for defining the function. |
| |
| Exports of other kinds (globals, memories, tables) are handled |
| differently, since they are part of the module instance, and each |
| instance can have its own exports. For these cases, `wasm2c` provides |
| a function that takes in a module instance as argument, and returns |
| the corresponding export. For example, if `fac` exported a memory as |
| such: |
| |
| ```wasm |
| (export "mem" (memory $mem)) |
| ``` |
| |
| then `wasm2c` would declare the following function in the header: |
| |
| ```c |
| /* export: 'mem' */ |
| wasm_rt_memory_t* w2c_fac_mem(w2c_fac* instance); |
| |
| ``` |
| |
| which would be defined as: |
| ```c |
| /* export: 'mem' */ |
| wasm_rt_memory_t* w2c_fac_mem(w2c_fac* instance) { |
| return &instance->w2c_mem; |
| } |
| ``` |
| |
| ## A quick look at `fac.c` |
| |
| The contents of `fac.c` are internals, but it is useful to see a little about |
| how it works. |
| |
| The first few hundred lines define macros that are used to implement the |
| various WebAssembly instructions. Their implementations may be interesting to |
| the curious reader, but are out of scope for this document. |
| |
| Following those definitions are various initialization functions (`init`, `free`, |
| `init_func_types`, `init_globals`, `init_memory`, `init_table`, and |
| `init_exports`.) In our example, most of these functions are empty, since the |
| module doesn't use any globals, memory or tables. |
| |
| The most interesting part is the definition of the function `fac`: |
| |
| ```c |
| static u32 w2c_fac_fac_0(w2c_fac* instance, u32 var_p0) { |
| FUNC_PROLOGUE; |
| u32 var_i0, var_i1, var_i2; |
| var_i0 = var_p0; |
| var_i1 = 0u; |
| var_i0 = var_i0 == var_i1; |
| if (var_i0) { |
| var_i0 = 1u; |
| } else { |
| var_i0 = var_p0; |
| var_i1 = var_p0; |
| var_i2 = 1u; |
| var_i1 -= var_i2; |
| var_i1 = w2c_fac_fac_0(instance, var_i1); |
| var_i0 *= var_i1; |
| } |
| FUNC_EPILOGUE; |
| return var_i0; |
| } |
| ``` |
| |
| If you look at the original WebAssembly text in the flat format, you can see |
| that there is a 1-1 mapping in the output: |
| |
| ```wasm |
| (func $fac (param $x i32) (result i32) |
| local.get $x |
| i32.const 0 |
| i32.eq |
| if (result i32) |
| i32.const 1 |
| else |
| local.get $x |
| local.get $x |
| i32.const 1 |
| i32.sub |
| call 0 |
| i32.mul |
| end) |
| ``` |
| |
| This looks different than the factorial function above because it is using the |
| "flat format" instead of the "folded format". You can use `wat-desugar` to |
| convert between the two to be sure: |
| |
| ```sh |
| $ wat-desugar fac-flat.wat --fold -o fac-folded.wat |
| ``` |
| |
| ```wasm |
| (module |
| (func (;0;) (param i32) (result i32) |
| (if (result i32) ;; label = @1 |
| (i32.eq |
| (local.get 0) |
| (i32.const 0)) |
| (then |
| (i32.const 1)) |
| (else |
| (i32.mul |
| (local.get 0) |
| (call 0 |
| (i32.sub |
| (local.get 0) |
| (i32.const 1))))))) |
| (export "fac" (func 0)) |
| (type (;0;) (func (param i32) (result i32)))) |
| ``` |
| |
| The formatting is different and the variable and function names are gone, but |
| the structure is the same. |
| |
| ## Create multiple instances of a module |
| |
| Since information about the execution context, such as memories, is encapsulated |
| in the module instance structure, and a pointer to the structure is being passed through |
| function calls, multiple instances of the same module can be instantiated alongside |
| one another. |
| |
| We can take a look at another version of the `main` function for a `rot13` example. By |
| declaring two sets of context information, two instances of `rot13` can be instantiated |
| in the same address space. |
| |
| ```c |
| #include <assert.h> |
| #include <stdio.h> |
| #include <stdlib.h> |
| |
| #include "rot13.h" |
| |
| /* Define structure to hold the imports */ |
| typedef struct w2c_host { |
| wasm_rt_memory_t memory; |
| char* input; |
| } w2c_host; |
| |
| /* Accessor to access the memory member of the host */ |
| wasm_rt_memory_t* w2c_host_mem(w2c_host* instance) { |
| return &instance->memory; |
| } |
| |
| int main(int argc, char** argv) { |
| /* Make sure there is at least one command-line argument. */ |
| if (argc < 2) { |
| printf("Invalid argument. Expected '%s WORD...'\n", argv[0]); |
| return 1; |
| } |
| /* Initialize the Wasm runtime. */ |
| wasm_rt_init(); |
| |
| /* Create two `host` instances to store the memory and current string */ |
| w2c_host host_1, host_2; |
| wasm_rt_allocate_memory(&host_1.memory, 1, 1, false, WASM_DEFAULT_PAGE_SIZE); |
| wasm_rt_allocate_memory(&host_2.memory, 1, 1, false, WASM_DEFAULT_PAGE_SIZE); |
| |
| /* Construct the `rot13` module instances */ |
| w2c_rot13 rot13_1, rot13_2; |
| wasm2c_rot13_instantiate(&rot13_1, &host_1); |
| wasm2c_rot13_instantiate(&rot13_2, &host_2); |
| |
| /* Call `rot13` on the first two arguments. */ |
| assert(argc > 2); |
| host_1.input = argv[1]; |
| w2c_rot13_rot13(&rot13_1); |
| host_2.input = argv[2]; |
| w2c_rot13_rot13(&rot13_2); |
| |
| /* Free the rot13 instances. */ |
| wasm2c_rot13_free(&rot13_1); |
| wasm2c_rot13_free(&rot13_2); |
| |
| /* Free the Wasm runtime state. */ |
| wasm_rt_free(); |
| |
| return 0; |
| } |
| |
| /* Fill the wasm buffer with the input to be rot13'd. |
| * |
| * params: |
| * instance: An instance of the w2c_host structure |
| * ptr: The wasm memory address of the buffer to fill data. |
| * size: The size of the buffer in wasm memory. |
| * result: |
| * The number of bytes filled into the buffer. (Must be <= size). |
| */ |
| u32 w2c_host_fill_buf(w2c_host* instance, u32 ptr, u32 size) { |
| for (size_t i = 0; i < size; ++i) { |
| if (instance->input[i] == 0) { |
| return i; |
| } |
| instance->memory.data[ptr + i] = instance->input[i]; |
| } |
| return size; |
| } |
| |
| /* Called when the wasm buffer has been rot13'd. |
| * |
| * params: |
| * w2c_host: An instance of the w2c_host structure |
| * ptr: The wasm memory address of the buffer. |
| * size: The size of the buffer in wasm memory. |
| */ |
| void w2c_host_buf_done(w2c_host* instance, u32 ptr, u32 size) { |
| /* The output buffer is not necessarily null-terminated, so use the %*.s |
| * printf format to limit the number of characters printed. */ |
| printf("%s -> %.*s\n", instance->input, (int)size, &instance->memory.data[ptr]); |
| } |
| ``` |