For WebAssembly execution, the Fluence node uses FCE
, which allows running multi-module WebAssembly applications with the help of interface types (IT). This section contains some of the internal details of using interface types and linking modules in FCE
.
Module linking
FCE
uses the so-called shared-nothing linking scheme. This scheme implies that any module exposes only exported functions and keeps its memory or table inaccessible to other modules. The modules are linked only through the export and import of functions. When loading a module, FCE
expects that functions declared in the module as imported have been already loaded into FCE
. Otherwise, such a module cannot be loaded.
Interface types (IT)
FCE
uses the modified wasmer-interface-types crate for IT execution. This crate is based on the previous revision (before Nov 2020) of the IT proposal with some additions such as swap
and dup
instructions.
If you are interested in the details of interface types, you can use the show
command of fce
, which prints the IT section of the compiled Wasm binary:
# obtain interface-types section of greeting.wasm
~ $ fce show -i artifacts/greeting.wasm
;; Types
(@interface type (func
(param $size: i32)
(result i32))) ;; 0
(@interface type (func
(param $pointer: i32 $size: i32) )) ;; 1
(@interface type (func
(result i32))) ;; 2
(@interface type (func
(result i32))) ;; 3
(@interface type (func
(param $result_size: i32) )) ;; 4
(@interface type (func
(param $result_ptr: i32) )) ;; 5
(@interface type (record $CallParameters (
field $call_id: string
field $user_name: string
field $application_id: string
))) ;; 6
(@interface type (func
(param $name: string)
(result string))) ;; 7
(@interface type (func
(param $name: string)
(result string))) ;; 8
;; Adapters
(@interface func (type 7)
arg.get 0
string.size
call-core 0
arg.get 0
string.lower_memory
call-core 6
call-core 3
call-core 2
string.lift_memory
call-core 3
call-core 2
call-core 1)
;; Exports
(@interface export "allocate" (func 0))
(@interface export "deallocate" (func 1))
(@interface export "get_result_size" (func 2))
(@interface export "get_result_ptr" (func 3))
(@interface export "set_result_size" (func 4))
(@interface export "set_result_ptr" (func 5))
(@interface export "greeting" (func 8))
;; Implementations
(@interface implement (func 8) (func 7))
Also, REPL
can print the trace of each IT instruction in the following way:
# specify printing each interface type intruction in `REPL`, run fce-repl and call the greeting function
~ $ RUST_LOG="wasmer_interface_types_fl=trace,info" fce-repl Config.toml
Welcome to the Fluence FaaS REPL
app service's created with service id = bfd21de5-bc89-4be5-87db-762ebb52de1c
elapsed time 42.916353ms
1> call greeting greeting "user"
[host] arg.get: pushing String("user") on the stack
[host] string.size: pushing 4 on the stack
[host] call-core: calling allocate with arguments: [I32(4)]
[host] call-core: call to 0 succeeded with result [I32(1114224)]
[host] arg.get: pushing String("user") on the stack
[host] string.lower_memory: pushing 1114224, 4 on the stack
[host] call-core: calling greeting with arguments: [I32(1114224), I32(4)]
[host] call-core: call to 6 succeeded with result []
[host] call-core: calling get_result_ptr with arguments: []
[host] call-core: call to 3 succeeded with result [I32(1114240)]
[host] call-core: calling get_result_size with arguments: []
[host] call-core: call to 2 succeeded with result [I32(8)]
[host] string.lift_memory: pushing "Hi, user" on the stack
[host] call-core: calling get_result_ptr with arguments: []
[host] call-core: call to 3 succeeded with result [I32(1114240)]
[host] call-core: calling get_result_size with arguments: []
[host] call-core: call to 2 succeeded with result [I32(8)]
[host] call-core: calling deallocate with arguments: [I32(1114240), I32(8)]
[host] call-core: call to 1 succeeded with result []
result: String("Hi, user")
elapsed time: 366.173µs
Updated a day ago