1
0
mirror of https://github.com/vlang/v.git synced 2023-08-10 21:13:21 +03:00
v/vlib/wasm/instructions.v

1188 lines
31 KiB
V

// Copyright (c) 2023 l-m.dev. All rights reserved.
// Use of this source code is governed by an MIT license
// that can be found in the LICENSE file.
module wasm
import encoding.leb128
fn (mut func Function) u32(v u32) {
func.code << leb128.encode_u32(v)
}
fn (mut func Function) blocktype(typ FuncType) {
if typ.parameters.len == 0 {
if typ.results.len == 0 {
func.code << 0x40 // empty type
return
} else if typ.results.len == 1 {
func.code << u8(typ.results[0]) // encode a single result type
return
}
}
// encode full type
tidx := func.mod.new_functype(typ)
func.code << leb128.encode_i32(tidx)
}
pub type PatchPos = int
// patch_pos returns a `PatchPos` for use with `patch`.
pub fn (func Function) patch_pos() PatchPos {
return func.code.len
}
// patch "patches" the code generated starting from the last `patch_pos` call in `begin` to `loc`.
//
// ```v
// start := func.patch_pos()
//
// ...
//
// patch_block := func.patch_pos()
// {
// func.i32_const(10)
// func.local_set(idx)
// }
// func.patch(start, patch_block) // will patch code to the `start`.
// // func.code[patch_block..]
// ```
pub fn (mut func Function) patch(loc PatchPos, begin PatchPos) {
if loc == begin {
return
}
assert loc < begin
v := func.code[begin..].clone()
func.code.trim(begin)
func.code.insert(int(loc), v)
for mut patch in func.patches {
if patch.pos >= begin {
patch.pos -= begin - loc
} else if patch.pos >= loc {
patch.pos += func.code.len - begin
}
}
func.patches.sort(a.pos < b.pos)
/*
lenn := begin
diff := loc - begin
for mut patch in func.patches {
if patch.pos >= begin {
patch.pos += diff
}
if patch.pos <= lenn {
continue
}
delta := patch.pos - lenn
patch.pos = loc + delta
}
func.patches.sort(a.pos < b.pos)*/
}
// new_local creates a function local and returns it's index.
// See `local_get`, `local_set`, `local_tee`.
pub fn (mut func Function) new_local(v ValType) LocalIndex {
ret := func.locals.len
func.locals << FunctionLocal{
typ: v
}
return ret
}
// new_local_named creates a function local with a name and returns it's index.
// The `name` is used in debug information, where applicable.
// See `local_get`, `local_set`, `local_tee`.
pub fn (mut func Function) new_local_named(v ValType, name string) LocalIndex {
ret := func.locals.len
func.locals << FunctionLocal{
typ: v
name: name
}
return ret
}
// i32_const places a constant i32 value on the stack.
// WebAssembly instruction: `i32.const`.
pub fn (mut func Function) i32_const(v i32) {
func.code << 0x41 // i32.const
func.code << leb128.encode_i32(v)
}
// i64_const places a constant i64 value on the stack.
// WebAssembly instruction: `i64.const`.
pub fn (mut func Function) i64_const(v i64) {
func.code << 0x42 // i64.const
func.code << leb128.encode_i64(v)
}
// f32_const places a constant f32 value on the stack.
// WebAssembly instruction: `f32.const`.
pub fn (mut func Function) f32_const(v f32) {
func.code << 0x43 // f32.const
push_f32(mut func.code, v)
}
// f64_const places a constant f64 value on the stack.
// WebAssembly instruction: `f64.const`.
pub fn (mut func Function) f64_const(v f64) {
func.code << 0x44 // f64.const
push_f64(mut func.code, v)
}
// local_get places the value of the local at the index `local` on the stack.
// WebAssembly instruction: `local.get`.
pub fn (mut func Function) local_get(local LocalIndex) {
func.code << 0x20 // local.get
func.u32(u32(local))
}
// local_get sets the local at the index `local` to the value on the stack.
// WebAssembly instruction: `local.set`.
pub fn (mut func Function) local_set(local LocalIndex) {
func.code << 0x21 // local.set
func.u32(u32(local))
}
// local_tee sets the local at the index `local` to the value on the stack, then places it's value on the stack.
// WebAssembly instruction: `local.tee`.
pub fn (mut func Function) local_tee(local LocalIndex) {
func.code << 0x22 // local.tee
func.u32(u32(local))
}
type GlobalIndices = GlobalImportIndex | GlobalIndex
// global_get places the value of the global at the index `global` on the stack.
// WebAssembly instruction: `global.get`.
pub fn (mut func Function) global_get(global GlobalIndices) {
func.code << 0x23 // global.get
match global {
GlobalIndex {
func.patches << FunctionGlobalPatch{
idx: global
pos: func.code.len
}
}
GlobalImportIndex {
func.u32(u32(global))
}
}
}
// global_set sets the global at the index `global` to the value on the stack.
// WebAssembly instruction: `global.set`.
pub fn (mut func Function) global_set(global GlobalIndices) {
func.code << 0x24 // global.set
match global {
GlobalIndex {
func.patches << FunctionGlobalPatch{
idx: global
pos: func.code.len
}
}
GlobalImportIndex {
func.u32(u32(global))
}
}
}
// drop drops the value on the stack
// WebAssembly instruction: `drop`.
pub fn (mut func Function) drop() {
func.code << 0x1A
}
// c_select selects one of its first two operands based on an i32 condition.
// WebAssembly instruction: `select`.
pub fn (mut func Function) c_select() {
func.code << 0x1B
}
// add adds two values on the stack with type `typ`.
// WebAssembly instructions: `i32|i64|f32|f64.add`.
pub fn (mut func Function) add(typ NumType) {
match typ {
.i32_t { func.code << 0x6A } // i32.add
.i64_t { func.code << 0x7C } // i64.add
.f32_t { func.code << 0x92 } // f32.add
.f64_t { func.code << 0xA0 } // f64.add
}
}
// sub subtracts two values on the stack with type `typ`.
// WebAssembly instructions: `i32|i64|f32|f64.sub`.
pub fn (mut func Function) sub(typ NumType) {
match typ {
.i32_t { func.code << 0x6B } // i32.sub
.i64_t { func.code << 0x7D } // i64.sub
.f32_t { func.code << 0x93 } // f32.sub
.f64_t { func.code << 0xA1 } // f64.sub
}
}
// mul multiplies two values on the stack with type `typ`.
// WebAssembly instructions: `i32|i64|f32|f64.mul`.
pub fn (mut func Function) mul(typ NumType) {
match typ {
.i32_t { func.code << 0x6C } // i32.mul
.i64_t { func.code << 0x7E } // i64.mul
.f32_t { func.code << 0x94 } // f32.mul
.f64_t { func.code << 0xA2 } // f64.mul
}
}
// div divides two values on the stack with type `typ`, with respect to `is_signed`.
// WebAssembly instructions: `i32|i64.div_s`, `i32|i64.div_u`, `f32|f64.div`.
pub fn (mut func Function) div(typ NumType, is_signed bool) {
match typ {
.i32_t {
if is_signed {
func.code << 0x6D // i32.div_s
} else {
func.code << 0x6E // i32.div_u
}
}
.i64_t {
if is_signed {
func.code << 0x7F // i64.div_s
} else {
func.code << 0x80 // i64.div_u
}
}
.f32_t {
func.code << 0x95 // f32.div
}
.f64_t {
func.code << 0xA3 // f64.div
}
}
}
// rem takes the remainder of two values on the stack with type `typ`, with respect to `is_signed`.
// WebAssembly instructions: `i32|i64.rem_s`, `i32|i64.rem_u`.
pub fn (mut func Function) rem(typ NumType, is_signed bool) {
assert typ in [.i32_t, .i64_t]
match typ {
.i32_t {
if is_signed {
func.code << 0x6F // i32.rem_s
} else {
func.code << 0x70 // i32.rem_u
}
}
.i64_t {
if is_signed {
func.code << 0x81 // i64.rem_s
} else {
func.code << 0x82 // i64.rem_u
}
}
else {}
}
}
// and takes the bitwise and of two values on the stack with type `typ`.
// WebAssembly instruction: `i32|i64.and`.
pub fn (mut func Function) b_and(typ NumType) {
assert typ in [.i32_t, .i64_t]
match typ {
.i32_t { func.code << 0x71 } // i32.and
.i64_t { func.code << 0x83 } // i64.and
else {}
}
}
// or takes the bitwise or of two values on the stack with type `typ`.
// WebAssembly instruction: `i32|i64.or`.
pub fn (mut func Function) b_or(typ NumType) {
assert typ in [.i32_t, .i64_t]
match typ {
.i32_t { func.code << 0x72 } // i32.or
.i64_t { func.code << 0x84 } // i64.or
else {}
}
}
// xor takes the bitwise xor of two values on the stack with type `typ`.
// WebAssembly instruction: `i32|i64.xor`.
pub fn (mut func Function) b_xor(typ NumType) {
assert typ in [.i32_t, .i64_t]
match typ {
.i32_t { func.code << 0x73 } // i32.xor
.i64_t { func.code << 0x85 } // i64.xor
else {}
}
}
// shl performs bitwise left-shift on a value with type `typ`.
// WebAssembly instruction: `i32|i64.shl`.
pub fn (mut func Function) b_shl(typ NumType) {
assert typ in [.i32_t, .i64_t]
match typ {
.i32_t { func.code << 0x74 } // i32.shl
.i64_t { func.code << 0x86 } // i64.shl
else {}
}
}
// shr performs bitwise right-shift on a value with type `typ`, with respect to `is_signed`.
// WebAssembly instructions: `i32|i64.shr_s`, `i32|i64.shr_u`.
pub fn (mut func Function) b_shr(typ NumType, is_signed bool) {
assert typ in [.i32_t, .i64_t]
match typ {
.i32_t {
if is_signed {
func.code << 0x75 // i32.shr_s
} else {
func.code << 0x76 // i32.shr_u
}
}
.i64_t {
if is_signed {
func.code << 0x87 // i64.shr_s
} else {
func.code << 0x88 // i64.shr_u
}
}
else {}
}
}
// clz counts the amount of leading zeros in the numbers binary representation.
// WebAssembly instruction: `i32|i64.clz`.
pub fn (mut func Function) clz(typ NumType) {
assert typ in [.i32_t, .i64_t]
match typ {
.i32_t { func.code << 0x67 } // i32.clz
.i64_t { func.code << 0x79 } // i64.clz
else {}
}
}
// ctz counts the amount of trailing zeros in the numbers binary representation.
// WebAssembly instruction: `i32|i64.ctz`.
pub fn (mut func Function) ctz(typ NumType) {
assert typ in [.i32_t, .i64_t]
match typ {
.i32_t { func.code << 0x68 } // i32.ctz
.i64_t { func.code << 0x7A } // i64.ctz
else {}
}
}
// popcnt counts the amount of 1s in a numbers binary representation.
// WebAssembly instruction: `i32|i64.popcnt`.
pub fn (mut func Function) popcnt(typ NumType) {
assert typ in [.i32_t, .i64_t]
match typ {
.i32_t { func.code << 0x69 } // i32.popcnt
.i64_t { func.code << 0x7B } // i64.popcnt
else {}
}
}
// rotl performs bitwise left-rotate on a value with type `typ`.
// WebAssembly instruction: `i32|i64.rotl`.
pub fn (mut func Function) rotl(typ NumType) {
assert typ in [.i32_t, .i64_t]
match typ {
.i32_t { func.code << 0x77 } // i32.rotl
.i64_t { func.code << 0x89 } // i64.rotl
else {}
}
}
// rotr performs bitwise right-rotate on a value with type `typ`.
// WebAssembly instruction: `i32|i64.rotr`.
pub fn (mut func Function) rotr(typ NumType) {
assert typ in [.i32_t, .i64_t]
match typ {
.i32_t { func.code << 0x78 } // i32.rotr
.i64_t { func.code << 0xA8 } // i64.rotr
else {}
}
}
// abs gets the absolute value of a float with type `typ`.
// WebAssembly instruction: `f32|f64.abs`.
pub fn (mut func Function) abs(typ NumType) {
assert typ in [.f32_t, .f64_t]
match typ {
.f32_t { func.code << 0x8B } // f32.abs
.f64_t { func.code << 0x99 } // f64.abs
else {}
}
}
// neg negates the value of a float with type `typ`.
// WebAssembly instruction: `f32|f64.neg`.
pub fn (mut func Function) neg(typ NumType) {
assert typ in [.f32_t, .f64_t]
match typ {
.f32_t { func.code << 0x8C } // f32.neg
.f64_t { func.code << 0x9A } // f64.neg
else {}
}
}
// ceil rounds up the value of a float with type `typ` to the nearest integer.
// WebAssembly instruction: `f32|f64.ceil`.
pub fn (mut func Function) ceil(typ NumType) {
assert typ in [.f32_t, .f64_t]
match typ {
.f32_t { func.code << 0x8D } // f32.ceil
.f64_t { func.code << 0x9B } // f64.ceil
else {}
}
}
// floor rounds down the value of a float with type `typ` to the nearest integer.
// WebAssembly instruction: `f32|f64.floor`.
pub fn (mut func Function) floor(typ NumType) {
assert typ in [.f32_t, .f64_t]
match typ {
.f32_t { func.code << 0x8E } // f32.floor
.f64_t { func.code << 0x9C } // f64.floor
else {}
}
}
// trunc discards the fractional part of the value of a float with type `typ`.
// WebAssembly instruction: `f32|f64.trunc`.
pub fn (mut func Function) trunc(typ NumType) {
assert typ in [.f32_t, .f64_t]
match typ {
.f32_t { func.code << 0x8F } // f32.trunc
.f64_t { func.code << 0x9D } // f64.trunc
else {}
}
}
// nearest rounds the value of a float with type `typ` to the nearest integer.
// WebAssembly instruction: `f32|f64.nearest`.
pub fn (mut func Function) nearest(typ NumType) {
assert typ in [.f32_t, .f64_t]
match typ {
.f32_t { func.code << 0x90 } // f32.nearest
.f64_t { func.code << 0x9E } // f64.nearest
else {}
}
}
// sqrt performs the square root on the value of a float with type `typ`.
// WebAssembly instruction: `f32|f64.sqrt`.
pub fn (mut func Function) sqrt(typ NumType) {
assert typ in [.f32_t, .f64_t]
match typ {
.f32_t { func.code << 0x91 } // f32.sqrt
.f64_t { func.code << 0x9F } // f64.sqrt
else {}
}
}
// min gets the smaller value of two floats with type `typ`.
// WebAssembly instruction: `f32|f64.min`.
pub fn (mut func Function) min(typ NumType) {
assert typ in [.f32_t, .f64_t]
match typ {
.f32_t { func.code << 0x96 } // f32.min
.f64_t { func.code << 0xA4 } // f64.min
else {}
}
}
// max gets the higher value of two floats with type `typ`.
// WebAssembly instruction: `f32|f64.max`.
pub fn (mut func Function) max(typ NumType) {
assert typ in [.f32_t, .f64_t]
match typ {
.f32_t { func.code << 0x97 } // f32.max
.f64_t { func.code << 0xA5 } // f64.max
else {}
}
}
// copysign copies the sign bit of one float value to another float, both with type `typ`.
// WebAssembly instruction: `f32|f64.copysign`.
pub fn (mut func Function) copysign(typ NumType) {
assert typ in [.f32_t, .f64_t]
match typ {
.f32_t { func.code << 0x98 } // f32.copysign
.f64_t { func.code << 0xA6 } // f64.copysign
else {}
}
}
// eqz checks if the value with type `typ` is equal to zero, places an i32 boolean value on the stack.
// WebAssembly instruction: `i32|i64.eqz`.
pub fn (mut func Function) eqz(typ NumType) {
assert typ in [.i32_t, .i64_t]
match typ {
.i32_t { func.code << 0x45 } // i32.eqz
.i64_t { func.code << 0x50 } // i64.eqz
else {}
}
}
// eq checks if two values with type `typ` are equal, places an i32 boolean value on the stack.
// WebAssembly instruction: `i32|i64|f32|f64.eq`.
pub fn (mut func Function) eq(typ NumType) {
match typ {
.i32_t { func.code << 0x46 } // i32.eq
.i64_t { func.code << 0x51 } // i64.eq
.f32_t { func.code << 0x5B } // f32.eq
.f64_t { func.code << 0x61 } // f64.eq
}
}
// ne checks if two values with type `typ` are not equal, places an i32 boolean value on the stack.
// WebAssembly instruction: `i32|i64|f32|f64.ne`.
pub fn (mut func Function) ne(typ NumType) {
match typ {
.i32_t { func.code << 0x47 } // i32.ne
.i64_t { func.code << 0x52 } // i64.ne
.f32_t { func.code << 0x5C } // f32.ne
.f64_t { func.code << 0x62 } // f64.ne
}
}
// lt checks if two values with type `typ` with respect to `is_signed` are less than another, places an i32 boolean value on the stack.
// WebAssembly instructions: `i32|i64.lt_s`, `i32|i64.lt_u`, `f32|f64.lt`.
pub fn (mut func Function) lt(typ NumType, is_signed bool) {
match typ {
.i32_t {
if is_signed {
func.code << 0x48 // i32.lt_s
} else {
func.code << 0x49 // i32.lt_u
}
}
.i64_t {
if is_signed {
func.code << 0x53 // i64.lt_s
} else {
func.code << 0x54 // i64.lt_u
}
}
.f32_t {
func.code << 0x5D // f32.lt
}
.f64_t {
func.code << 0x63 // f64.lt
}
}
}
// gt checks if two values with type `typ` with respect to `is_signed` are greater than another, places an i32 boolean value on the stack.
// WebAssembly instructions: `i32|i64.gt_s`, `i32|i64.gt_u`, `f32|f64.gt`.
pub fn (mut func Function) gt(typ NumType, is_signed bool) {
match typ {
.i32_t {
if is_signed {
func.code << 0x4A // i32.gt_s
} else {
func.code << 0x4B // i32.gt_u
}
}
.i64_t {
if is_signed {
func.code << 0x55 // i64.gt_s
} else {
func.code << 0x56 // i64.gt_u
}
}
.f32_t {
func.code << 0x5E
} // f32.gt
.f64_t {
func.code << 0x64
} // f64.gt
}
}
// le checks if two values with type `typ` with respect to `is_signed` are less than or equal to another, places an i32 boolean value on the stack.
// WebAssembly instructions: `i32|i64.le_s`, `i32|i64.le_u`, `f32|f64.le`.
pub fn (mut func Function) le(typ NumType, is_signed bool) {
match typ {
.i32_t {
if is_signed {
func.code << 0x4C // i32.le_s
} else {
func.code << 0x4D // i32.le_u
}
}
.i64_t {
if is_signed {
func.code << 0x57 // i64.le_s
} else {
func.code << 0x58 // i64.le_u
}
}
.f32_t {
func.code << 0x5F
} // f32.le
.f64_t {
func.code << 0x65
} // f64.le
}
}
// ge checks if two values with type `typ` with respect to `is_signed` are greater than or equal to another, places an i32 boolean value on the stack.
// WebAssembly instructions: `i32|i64.ge_s`, `i32|i64.ge_u`, `f32|f64.ge`.
pub fn (mut func Function) ge(typ NumType, is_signed bool) {
match typ {
.i32_t {
if is_signed {
func.code << 0x4E // i32.ge_s
} else {
func.code << 0x4F // i32.ge_u
}
}
.i64_t {
if is_signed {
func.code << 0x59 // i64.ge_s
} else {
func.code << 0x5A // i64.ge_u
}
}
.f32_t {
func.code << 0x60
} // f32.ge
.f64_t {
func.code << 0x66
} // f64.ge
}
}
// sign_extend8 extends the value of a 8-bit integer of type `typ`.
// WebAssembly instruction: `i32|i64.extend8_s`.
pub fn (mut func Function) sign_extend8(typ ValType) {
assert typ in [.i32_t, .i64_t]
match typ {
.i32_t { func.code << 0xC0 } // i32.extend8_s
.i64_t { func.code << 0xC2 } // i64.extend8_s
else {}
}
}
// sign_extend16 extends the value of a 16-bit integer of type `typ`.
// WebAssembly instruction: `i32|i64.extend16_s`.
pub fn (mut func Function) sign_extend16(typ ValType) {
assert typ in [.i32_t, .i64_t]
match typ {
.i32_t { func.code << 0xC1 } // i32.extend16_s
.i64_t { func.code << 0xC3 } // i64.extend16_s
else {}
}
}
// sign_extend32_i64 extends the value of a 32-bit integer of type i64.
// WebAssembly instruction: `i64.extend64_s`.
pub fn (mut func Function) sign_extend32_i64() {
func.code << 0xC4 // i64.extend64_s
}
// cast casts a value of type `a` with respect to `is_signed`, to type `b`.
// A generic utility function over a large amount of WebAssembly instructions.
// Note: This function uses non-trapping float conversion operators, see `cast_trapping` to use opcodes that cause a runtime exception.
// WebAssembly instructions:
// - `i32|i64.trunc_sat_f32_s`, `i32|i64.trunc_sat_f64_s`.
// - `f32.demote_f64`, `f64.promote_f32`.
// - `i32.wrap_i64`, `i64.extend_i32_s`, `i64.extend_i32_u`.
// - `f32|f64.convert_i32_s`, `f32|f64.convert_i32_u`.
// - `f32|f64.convert_i64_s`, `f32|f64.convert_i64_u`.
pub fn (mut func Function) cast(a NumType, is_signed bool, b NumType) {
if a in [.f32_t, .f64_t] {
if a == .f32_t {
match b {
.i32_t {
func.code << 0xFC // sat opcode
func.code << 0x00 // i32.trunc_sat_f32_s
}
.i64_t {
func.code << 0xFC // sat opcode
func.code << 0x04 // i64.trunc_sat_f32_s
}
.f64_t {
func.code << 0xBB // f64.promote_f32
}
else {}
}
} else {
match b {
.i32_t {
func.code << 0xFC // sat opcode
func.code << 0x02 // i32.trunc_sat_f64_s
}
.i64_t {
func.code << 0xFC // sat opcode
func.code << 0x06 // i64.trunc_sat_f64_s
}
.f32_t {
func.code << 0xB6 // f32.demote_f64
}
else {}
}
}
return
}
if a == .i64_t && b == .i32_t {
func.code << 0xA7 // i32.wrap_i64
return
}
if is_signed {
match a {
.i32_t {
match b {
.i64_t {
func.code << 0xAC // i64.extend_i32_s
}
.f32_t {
func.code << 0xB2 // f32.convert_i32_s
}
.f64_t {
func.code << 0xB7 // f64.convert_i32_s
}
else {}
}
}
.i64_t {
match b {
.f32_t {
func.code << 0xB4 // f32.convert_i64_s
}
.f64_t {
func.code << 0xB9 // f64.convert_i64_s
}
else {}
}
}
else {}
}
} else {
match a {
.i32_t {
match b {
.i64_t {
func.code << 0xAD // i64.extend_i32_u
}
.f32_t {
func.code << 0xB3 // f32.convert_i32_u
}
.f64_t {
func.code << 0xB8 // f64.convert_i32_u
}
else {}
}
}
.i64_t {
match b {
.f32_t {
func.code << 0xB5 // f32.convert_i64_u
}
.f64_t {
func.code << 0xBA // f64.convert_i64_u
}
else {}
}
}
else {}
}
}
}
// cast_trapping casts a value of type `a` with respect to `is_signed`, to type `b`.
// A generic utility function over a large amount of WebAssembly instructions.
// Note: This function uses trapping float conversion operators, see `cast` to use opcodes that do NOT cause a runtime exception.
// WebAssembly instructions:
// - `i32|i64.trunc_f32_s`, `i32|i64.trunc_f64_s`.
// - See function `cast` for the rest.
pub fn (mut func Function) cast_trapping(a NumType, is_signed bool, b NumType) {
if a in [.f32_t, .f64_t] {
if a == .f32_t {
match b {
.i32_t {
func.code << 0xA8 // i32.trunc_f32_s
return
}
.i64_t {
func.code << 0xAE // i64.trunc_f32_s
return
}
else {}
}
} else {
match b {
.i32_t {
func.code << 0xAA // i32.trunc_f64_s
return
}
.i64_t {
func.code << 0xB0 // i64.trunc_f64_s
return
}
else {}
}
}
}
func.cast(a, is_signed, b)
}
// reinterpret returns a value which has the same bit-pattern as its operand value, in its result type.
// WebAssembly instruction: `f32.reinterpret_i32`, `i32.reinterpret_f32`, `f64.reinterpret_i64`, `i64.reinterpret_f64`.
pub fn (mut func Function) reinterpret(a NumType) {
match a {
.f32_t { func.code << 0xBC } // i32.reinterpret_f32
.i32_t { func.code << 0xBE } // f32.reinterpret_i32
.f64_t { func.code << 0xBD } // i64.reinterpret_f64
.i64_t { func.code << 0xBF } // f64.reinterpret_i64
}
}
// unreachable denotes a point in code that should not be reachable, it is an unconditional trap.
// WebAssembly instruction: `unreachable`.
pub fn (mut func Function) unreachable() {
func.code << 0x00
}
// nop instruction, does nothing.
// WebAssembly instruction: `nop`.
pub fn (mut func Function) nop() {
func.code << 0x01
}
pub type LabelIndex = int
// c_block creates a label that can later be branched out of with `c_br` and `c_br_if`.
// Blocks are strongly typed, you must supply a list of types for `parameters` and `results`.
// All blocks must be ended, see the `c_end` function.
pub fn (mut func Function) c_block(parameters []ValType, results []ValType) LabelIndex {
func.label++
func.code << 0x02
func.blocktype(parameters: parameters, results: results)
return func.label
}
// c_loop creates a label that can later be branched to with `c_br` and `c_br_if`.
// Loops are strongly typed, you must supply a list of types for `parameters` and `results`.
// All loops must be ended, see the `c_end` function.
pub fn (mut func Function) c_loop(parameters []ValType, results []ValType) LabelIndex {
func.label++
func.code << 0x03
func.blocktype(parameters: parameters, results: results)
return func.label
}
// c_if opens an if expression. It executes a statement if the last item on the stack is true.
// It creates a label that can later be branched out of with `c_br` and `c_br_if`.
// If expressions are strongly typed, you must supply a list of types for `parameters` and `results`.
// Call `c_else` to open the else case of an if expression, or close it by calling `c_end_if`.
// All if expressions must be ended, see the `c_end` function.
pub fn (mut func Function) c_if(parameters []ValType, results []ValType) LabelIndex {
func.label++
func.code << 0x04
func.blocktype(parameters: parameters, results: results)
return func.label
}
// c_else opens the else case of an if expression, it must be closed by calling `c_end`.
pub fn (mut func Function) c_else(label LabelIndex) {
assert func.label == label, 'c_else: called with an invalid label ${label}'
func.code << 0x05
}
// c_return returns from a function.
// WebAssembly instruction: `return`.
pub fn (mut func Function) c_return() {
func.code << 0x0F // return
}
// c_end ends the block, loop or if expression with the label passed in at `label`.
pub fn (mut func Function) c_end(label LabelIndex) {
assert func.label == label, 'c_end: called with an invalid label ${label}'
func.label--
assert func.label >= 0, 'c_end: negative label index, unbalanced calls'
func.code << 0x0B // END expression opcode
}
// c_br branches to a loop or block with the label passed in at `label`.
// WebAssembly instruction: `br`.
pub fn (mut func Function) c_br(label LabelIndex) {
v := func.label - label
assert v >= 0, 'c_br: malformed label index'
func.code << 0x0C // br
func.u32(u32(v))
}
// c_br_if branches to a loop or block with the label passed in at `label`, based on an i32 condition.
// WebAssembly instruction: `br_if`.
pub fn (mut func Function) c_br_if(label LabelIndex) {
v := func.label - label
assert v >= 0, 'c_br_if: malformed label index'
func.code << 0x0D // br_if
func.u32(u32(v))
}
// call calls a locally defined function.
// If this function does not exist when calling `compile` on the module, it will panic.
// WebAssembly instruction: `call`.
pub fn (mut func Function) call(name string) {
func.code << 0x10 // call
func.patches << CallPatch(FunctionCallPatch{
name: name
pos: func.code.len
})
}
// call calls an imported function.
// If the imported function does not exist when calling `compile` on the module, it will panic.
// WebAssembly instruction: `call`.
pub fn (mut func Function) call_import(mod string, name string) {
func.code << 0x10 // call
func.patches << CallPatch(ImportCallPatch{
mod: mod
name: name
pos: func.code.len
})
}
// load loads a value with type `typ` from memory.
// WebAssembly instruction: `i32|i64|f32|f64.load`.
pub fn (mut func Function) load(typ NumType, align int, offset int) {
match typ {
.i32_t { func.code << 0x28 } // i32.load
.i64_t { func.code << 0x29 } // i64.load
.f32_t { func.code << 0x2A } // f32.load
.f64_t { func.code << 0x2B } // f64.load
}
func.u32(u32(align))
func.u32(u32(offset))
}
// load8 loads a 8-bit value with type `typ` with respect to `is_signed` from memory.
// WebAssembly instructions: `i32|i64.load8_s`, `i32|i64.load8_u`.
pub fn (mut func Function) load8(typ NumType, is_signed bool, align int, offset int) {
assert typ in [.i32_t, .i64_t]
match typ {
.i32_t {
if is_signed {
func.code << 0x2C // i32.load8_s
} else {
func.code << 0x2D // i32.load8_u
}
}
.i64_t {
if is_signed {
func.code << 0x30 // i64.load8_s
} else {
func.code << 0x31 // i64.load8_u
}
}
else {}
}
func.u32(u32(align))
func.u32(u32(offset))
}
// load16 loads a 16-bit value with type `typ` with respect to `is_signed` from memory.
// WebAssembly instructions: `i32|i64.load16_s`, `i32|i64.load16_u`.
pub fn (mut func Function) load16(typ NumType, is_signed bool, align int, offset int) {
assert typ in [.i32_t, .i64_t]
match typ {
.i32_t {
if is_signed {
func.code << 0x2E // i32.load16_s
} else {
func.code << 0x2F // i32.load16_u
}
}
.i64_t {
if is_signed {
func.code << 0x32 // i64.load16_s
} else {
func.code << 0x33 // i64.load16_u
}
}
else {}
}
func.u32(u32(align))
func.u32(u32(offset))
}
// load32_i64 loads a 32-bit value of type i64 with respect to `is_signed` from memory.
// WebAssembly instructions: `i64.load32_s`, `i64.load32_u`.
pub fn (mut func Function) load32_i64(is_signed bool, align int, offset int) {
if is_signed {
func.code << 0x34 // i64.load32_s
} else {
func.code << 0x35 // i64.load32_u
}
func.u32(u32(align))
func.u32(u32(offset))
}
// store stores a value with type `typ` into memory.
// WebAssembly instruction: `i32|i64|f32|f64.store`.
pub fn (mut func Function) store(typ NumType, align int, offset int) {
match typ {
.i32_t { func.code << 0x36 } // i32.store
.i64_t { func.code << 0x37 } // i64.store
.f32_t { func.code << 0x38 } // f32.store
.f64_t { func.code << 0x39 } // f64.store
}
func.u32(u32(align))
func.u32(u32(offset))
}
// store8 stores a 8-bit value with type `typ` into memory.
// WebAssembly instruction: `i32|i64.store8`.
pub fn (mut func Function) store8(typ NumType, align int, offset int) {
assert typ in [.i32_t, .i64_t]
match typ {
.i32_t { func.code << 0x3A } // i32.store8
.i64_t { func.code << 0x3C } // i64.store8
else {}
}
func.u32(u32(align))
func.u32(u32(offset))
}
// store16 stores a 16-bit value with type `typ` into memory.
// WebAssembly instruction: `i32|i64.store16`.
pub fn (mut func Function) store16(typ NumType, align int, offset int) {
assert typ in [.i32_t, .i64_t]
match typ {
.i32_t { func.code << 0x3B } // i32.store16
.i64_t { func.code << 0x3D } // i64.store16
else {}
}
func.u32(u32(align))
func.u32(u32(offset))
}
// store16 stores a 32-bit value of type i64 into memory.
// WebAssembly instruction: `i64.store32`.
pub fn (mut func Function) store32_i64(align int, offset int) {
func.code << 0x3E // i64.store32
func.u32(u32(align))
func.u32(u32(offset))
}
// memory_size gets the size of the memory instance.
// WebAssembly instruction: `memory.size`.
pub fn (mut func Function) memory_size() {
func.code << 0x3F
func.code << 0x00
}
// memory_grow increases the size of the memory instance.
// WebAssembly instruction: `memory.grow`.
pub fn (mut func Function) memory_grow() {
func.code << 0x40
func.code << 0x00
}
// memory_init copies from a passive memory segment to the memory instance.
// WebAssembly instruction: `memory.init`.
pub fn (mut func Function) memory_init(idx DataSegmentIndex) {
func.code << 0xFC
func.code << 0x08
func.u32(u32(idx))
func.code << 0x00
}
// data_drop prevents further use of a passive memory segment.
// WebAssembly instruction: `data.drop`.
pub fn (mut func Function) data_drop(idx DataSegmentIndex) {
func.code << 0xFC
func.code << 0x09
func.u32(u32(idx))
}
// memory_copy copies one region of memory to another.
// Similar to `memcpy` and `memmove`, memory regions can overlap.
// WebAssembly instruction: `memory.copy`.
pub fn (mut func Function) memory_copy() {
func.code << [u8(0xFC), 0x0A, 0x00, 0x00]
}
// memory_fill sets a memory region to a byte value.
// Similar to `memset`.
// WebAssembly instruction: `memory.copy`.
pub fn (mut func Function) memory_fill() {
func.code << [u8(0xFC), 0x0B, 0x00]
}
// ref_null places a null reference on the stack.
// WebAssembly instruction: `ref.null`.
pub fn (mut func Function) ref_null(rt RefType) {
func.code << 0xD0 // ref.null
func.code << u8(rt)
}
// ref_is_null checks if the reference value on the stack is null, places an i32 boolean value on the stack.
// WebAssembly instruction: `ref_is_null`.
pub fn (mut func Function) ref_is_null(rt RefType) {
func.code << 0xD1 // ref_is_null
}
// ref_func places a reference to a function with `name` on the stack.
// If this function does not exist when calling `compile` on the module, it will panic.
// WebAssembly instruction: `ref.func`.
pub fn (mut func Function) ref_func(name string) {
func.code << 0xD2 // ref.func
func.patches << CallPatch(FunctionCallPatch{
name: name
pos: func.code.len
})
}
// ref_func_import places a reference to an imported function with `name` on the stack.
// If the imported function does not exist when calling `compile` on the module, it will panic.
// WebAssembly instruction: `ref.func`.
pub fn (mut func Function) ref_func_import(mod string, name string) {
func.code << 0xD2 // ref.func
func.patches << CallPatch(ImportCallPatch{
mod: mod
name: name
pos: func.code.len
})
}