1. Function Definition and Export
(module
(func
(export "main") ;; Name to export
(result i32) ;; Return Type
i32.const 42 ;; Return value
)
)
The main
function can now be called from the host environment, for example: Python running Wasmer.
2. Module Local Function Definition
;; Signature: average(x: f32, y: f32) -> f32
(func $average ;; Name inside module
(param $x f32) ;; Parameter names and types
(param $y f32)
(result f32) ;; Return type
local.get $x
local.get $y
f32.add
f32.const 2.0
f32.div ;; Return (x + y) / 2.0
)
The following code calls the previous function:
;; average(7.25, 3.75)
f32.const 7.25 ;; Push arguments on top of stack
f32.const 3.75
call $average ;; Call function, result is placed on top of stack
3. Parameters and Local Variables
;; Signature: sum(x: i32, y: i32, z: i32) -> i32
(func $sum
(param $x i32) ;; Parameter names and types
(param $y i32)
(param $z i32)
(result i32) ;; Return type
(local $a i32) ;; Local variable names and types
(local $b i32)
local.get $x
local.get $y
i32.add
local.set $a ;; a = x + y
local.get $z
local.get $a
i32.add
local.set $b ;; b = z + a
local.get $b ;; Return b
)
4. Function Import
(module
;; Import from module "example" the function called "myfun"
(import "example" "myfun"
(func $myfun ;; Name inside module
(param i32) ;; Parameter types
(param i32)
(result i32))) ;; Return type
;; ...
)
Imports from the hosting environment the named function. The function can now be called like a module local function.
5. Global Variables
(module
;; $t as a mutable global variable of type i32
;; with initial value 0
(global $t (mut i32) (i32.const 0))
(func $inc_t
global.get $t
i32.const 1
i32.add
global.set $t ;; t = t + 1
)
;; ...
)
6. Array Literals
(local $_temp i32)
(local $a i32)
;; ...
;; a = [1 + 1, 2 * 3, 13];
i32.const 0
call $new
local.set $_temp
local.get $_temp
local.get $_temp
local.get $_temp
local.get $_temp
i32.const 1
i32.const 1
i32.add ;; 1 + 1
call $add
drop
i32.const 2
i32.const 3
i32.mul ;; 2 * 3
call $add
drop
i32.const 13 ;; 13
call $add
drop
local.set $a
7. String Literals
(local $_temp i32)
(local $s i32)
;; ...
;; s = "ABC\n";
i32.const 0
call $new
local.set $_temp
local.get $_temp
local.get $_temp
local.get $_temp
local.get $_temp
local.get $_temp
i32.const 65 ;; 'A'
call $add
drop
i32.const 66 ;; 'B'
call $add
drop
i32.const 67 ;; 'C'
call $add
drop
i32.const 10 ;; '\n'
call $add
drop
local.set $s
The following C# program demonstrates how to get a list with all the code points from an arbitrary Unicode string.
using System;
using System.Collections.Generic;
class CodePoints {
public static IList<int> AsCodePoints(String str) {
var result = new List<int>(str.Length);
for (var i = 0; i < str.Length; i++) {
result.Add(char.ConvertToUtf32(str, i));
if (char.IsHighSurrogate(str, i)) {
i++;
}
}
return result;
}
public static void Main() {
var codes = AsCodePoints("¡Ñoño!");
foreach (var code in codes) {
Console.WriteLine(code);
}
}
}
8. If Statement
(local $x i32)
;; ...
;; if (1 < 2) {
;; x = 3;
;; }
i32.const 1
i32.const 2
i32.lt_s ;; 1 < 2
if
i32.const 3
local.set $x ;; x = 3;
end
9. If/Else Statement
(local $x i32)
;; ...
;; if (1 < 2) {
;; x = 3;
;; } else {
;; x = 4;
;; }
i32.const 1
i32.const 2
i32.lt_s ;; 1 < 2
if
i32.const 3
local.set $x ;; x = 3;
else
i32.const 4
local.set $x ;; x = 4;
end
10. If/Elif/Else Statement
(local $x i32)
;; ...
;; if (1 < 2) {
;; x = 3;
;; } elif (4 < 5) {
;; x = 6;
;; } else {
;; x = 7;
;; }
i32.const 1
i32.const 2
i32.lt_s ;; 1 < 2
if
i32.const 3
local.set $x ;; x = 3;
else
i32.const 4
i32.const 5
i32.lt_s ;; 4 < 5
if
i32.const 6
local.set $x ;; x = 6;
else
i32.const 7
local.set $x ;; x = 7;
end
end
11. Neg Operator
(local $x i32)
;; ...
;; x = - 5;
i32.const 0 ;; Always push 0
i32.const 5 ;; Compute operand
i32.sub ;; Get negated value
local.set $x ;; x = - 5
12. Not Operator
(local $x i32)
;; ...
;; x = not 2;
i32.const 2 ;; Compute operand
i32.eqz ;; Pop top of stack and compare to 0. If it's equal
;; push true (1), if not push false (0).
local.set $x ;; x = not 2;
13. Short-circuit And Operator
(local $x i32)
;; ...
;; x = 2 and 3;
i32.const 2 ;; Compute first operand
if (result i32) ;; Result of IF placed on top of stack
i32.const 3 ;; Compute second operand
i32.eqz ;; Two consecutive EQZ instructions convert
i32.eqz ;; top of stack to true (1) or false (0)
else
i32.const 0 ;; false
end
local.set $x ;; x = 2 and 3;
14. Short-circuit Or Operator
(local $x i32)
;; ...
;; x = 2 or 3;
i32.const 2 ;; Compute first operand
if (result i32) ;; Result of IF placed on top of stack
i32.const 1 ;; true
else
i32.const 3 ;; Compute second operand
i32.eqz ;; Two consecutive EQZ instructions convert
i32.eqz ;; top of stack to true (1) or false (0)
end
local.set $x ;; x = 2 or 3;
15. Loop/break Statements
;; n = 5;
;; r = 1;
;; i = 1;
;; loop {
;; if (i > n) {
;; break;
;; }
;; r = r * i;
;; i = i + 1;
;; }
(local $n i32)
(local $r i32)
(local $i i32)
i32.const 5
local.set $n ;; n = 5;
i32.const 1
local.set $r ;; r = 1;
i32.const 1
local.set $i ;; i = 1;
block $00001 ;; Target for “break”
loop $00002 ;; Target for “continue”
local.get $i
local.get $n
i32.gt_s ;; i > n
if
br $00001 ;; break (exit block)
end
local.get $r
local.get $i
i32.mul
local.set $r ;; r = r * i;
local.get $i
i32.const 1
i32.add
local.set $i ;; i = i + 1;
br $00002 ;; continue (go to start of loop)
end ;; End of loop
end ;; End of block
There might be nested loops. Thus, you need to keep track of the most recent (nested) label for the current block instruction, which is the target for the break instruction. A dedicated stack can be used for this purpose.
|
The following C# code shows a method that generates unique labels that can be used in loops:
class WATVisitor {
int labelCounter = 0;
public String GenerateLabel() {
return String.Format("${0:00000}", labelCounter++);
}
// Rest of the class goes here
}
16. Return Statement
;; return 2 * 3;
i32.const 2
i32.const 3
i32.mul
return