Number operators


To get familiar with stack-oriented operations, you can start from number operations. Let's see how to push a constant number onto the stack. You can append .const to i32, i64, f32 and f64.

  • i32.const
  • i64.const
  • f32.const
  • f64.const

After pushing enough numbers, you can use the operators listed below.

Integer operators

i32 and i64 have the same operators. For example, add is available on 32-bit (i32.add) and 64-bit integers (i64.add). Take i32 for example. The operators in mathematics are:

  • i32.add: sign-agnostic addition
  • i32.sub: sign-agnostic subtraction
  • i32.mul: sign-agnostic multiplication (lower 32-bits)
  • i32.div_s: signed division (result is truncated toward zero)
  • i32.div_u: unsigned division (result is floored)
  • i32.rem_s: signed remainder (result has the sign of the dividend)
  • i32.rem_u: unsigned remainder

The bitwise operators include:

  • i32.and: sign-agnostic bitwise and
  • i32.or: sign-agnostic bitwise inclusive or
  • i32.xor: sign-agnostic bitwise exclusive or
  • i32.shl: sign-agnostic shift left
  • i32.shr_u: zero-replicating (logical) shift right
  • i32.shr_s: sign-replicating (arithmetic) shift right
  • i32.rotl: sign-agnostic rotate left
  • i32.rotr: sign-agnostic rotate right

The comparison operators are listed below. In WebAssembly, all comparison operators yield 32-bit integer results with 1 representing true and 0 representing false.

  • i32.eq: sign-agnostic compare equal
  • i32.ne: sign-agnostic compare unequal
  • i32.lt_s: signed less than
  • i32.le_s: signed less than or equal
  • i32.lt_u: unsigned less than
  • i32.le_u: unsigned less than or equal
  • i32.gt_s: signed greater than
  • i32.ge_s: signed greater than or equal
  • i32.gt_u: unsigned greater than
  • i32.ge_u: unsigned greater than or equal

All the above are binary operators so you have to push two numbers before executing the instruction. For example, if you want to do 1 + 2:

(module
    (import "env" "log" (func $log (param i32)))
    (func $main
        i32.const 1
        i32.const 2
        i32.add
        call $log ;; show 3 in conole
    )
    (start $main)
)

You can see a single line comment here. The text after ;; will be ignored when compiling.

If you've got familiar with stack-oriented operations, you can write concisely as follows:

(module
    (import "env" "log" (func $log (param i32)))
    (func $main
        (i32.add (i32.const 1) (i32.const 2))
        call $log
    )
    (start $main)
)

The instruction inside the parentheses will be executed first. Take a look at Compiling C to WebAssembly. The Wat generated automatically is written the same way.
If you observe the generated code carefully, you can rewrite the above code as follows:

(module
    (import "env" "log" (func $log (param i32)))
    (func $main
        (call $log
            (i32.add (i32.const 1) (i32.const 2))
        )
    )
    (start $main)
)

Simply speaking, do some basic operations first and watch what the automatically-generated Wat is when compiling C to Wat. You will be able to write code more clearly and concisely. In later documents, I'll keep writing basic operations first and use the above style if it's helpful for readability.

Unary operators require one number from the stack.

  • i32.clz: sign-agnostic count leading zero bits (All zero bits are considered leading if the value is zero)
  • i32.ctz: sign-agnostic count trailing zero bits (All zero bits are considered trailing if the value is zero)
  • i32.popcn: sign-agnostic count number of one bits
  • i32.eqz: compare equal to zero (return 1 if operand is zero, 0 otherwise)

Floating point operators

f32 and f64 have the same operators. Take f32 for example. The binary operators include:

  • f32.add: addition
  • f32.sub: subtraction
  • f32.mul: multiplication
  • f32.div: division
  • f32.copysign: copysign
  • f32.eq: compare ordered and equal
  • f32.ne: compare unordered or unequal
  • f32.lt: compare ordered and less than
  • f32.le: compare ordered and less than or equal
  • f32.gt: compare ordered and greater than
  • f32.ge: compare ordered and greater than or equal
  • f32.min: minimum (binary operator); if either operand is nan, returns nan
  • f32.max: maximum (binary operator); if either operand is nan, returns nan

Unary operators are:

  • f32.abs: absolute value
  • f32.neg: negation
  • f32.ceil: ceiling operator
  • f32.floor: floor operator
  • f32.trunc: round to nearest integer towards zero
  • f32.nearest: round to nearest integer, ties to even
  • f32.sqrt: square root

Datatype operators

There are operators for datatype conversions, truncations, reinterpretations, promotions, and demotions.

i32 has the following operators for wrapping, truncating or reinterpreing:

  • i32.wrap/i64: wrap a 64-bit integer to a 32-bit integer
  • i32.trunc_s/f32: truncate a 32-bit float to a signed 32-bit integer
  • i32.trunc_s/f64: truncate a 64-bit float to a signed 32-bit integer
  • i32.trunc_u/f32: truncate a 32-bit float to an unsigned 32-bit integer
  • i32.trunc_u/f64: truncate a 64-bit float to an unsigned 32-bit integer
  • i32.reinterpret/f32: reinterpret the bits of a 32-bit float as a 32-bit integer

One example is shown below. If you remove i32.wrap/i64, one error happens due to data type mismatch.

(module
    (import "env" "log" (func $log (param i32)))
    (func $main
        i32.const 1
        i64.const 2
        i32.wrap/i64
        i32.add
        call $log
    )
    (start $main)
)

i64 has the following operators for extending, truncating or reinterpreing:

  • i64.extend_s/i32: extend a signed 32-bit integer to a 64-bit integer
  • i64.extend_u/i32: extend an unsigned 32-bit integer to a 64-bit integer
  • i64.trunc_s/f32: truncate a 32-bit float to a signed 64-bit integer
  • i64.trunc_s/f64: truncate a 64-bit float to a signed 64-bit integer
  • i64.trunc_u/f32: truncate a 32-bit float to an unsigned 64-bit integer
  • i64.trunc_u/f64: truncate a 64-bit float to an unsigned 64-bit integer
  • i64.reinterpret/f64: reinterpret the bits of a 64-bit float as a 64-bit integer

f32 has the following operators for demoting, converting or reinterpreing:

  • f32.demote/f64: demote a 64-bit float to a 32-bit float
  • f32.convert_s/i32: convert a signed 32-bit integer to a 32-bit float
  • f32.convert_s/i64: convert a signed 64-bit integer to a 32-bit float
  • f32.convert_u/i32: convert an unsigned 32-bit integer to a 32-bit float
  • f32.convert_u/i64: convert an unsigned 64-bit integer to a 32-bit float
  • f32.reinterpret/i32: reinterpret the bits of a 32-bit integer as a 32-bit float

f64 has the following operators for promoting, converting or reinterpreing:

  • f64.promote/f32: promote a 32-bit float to a 64-bit float
  • f64.convert_s/i32: convert a signed 32-bit integer to a 64-bit float
  • f64.convert_s/i64: convert a signed 64-bit integer to a 64-bit float
  • f64.convert_u/i32: convert an unsigned 32-bit integer to a 64-bit float
  • f64.convert_u/i64: convert an unsigned 64-bit integer to a 64-bit float
  • f64.reinterpret/i64: reinterpret the bits of a 64-bit integer as a 64-bit float