mirror of
https://github.com/vlang/v.git
synced 2023-08-10 21:13:21 +03:00
1188 lines
31 KiB
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
|
|
})
|
|
}
|