mirror of
https://github.com/vlang/v.git
synced 2023-08-10 21:13:21 +03:00
wasm module: globals, constant expressions and function reference types (#17950)
This commit is contained in:
parent
093e5c68b9
commit
8b2887d80b
140
vlib/wasm/constant.v
Normal file
140
vlib/wasm/constant.v
Normal file
@ -0,0 +1,140 @@
|
|||||||
|
module wasm
|
||||||
|
|
||||||
|
import encoding.leb128
|
||||||
|
|
||||||
|
// constexpr_value returns a constant expression that evaluates to a single value.
|
||||||
|
pub fn constexpr_value[T](v T) ConstExpression {
|
||||||
|
mut expr := ConstExpression{}
|
||||||
|
|
||||||
|
$if T is i64 {
|
||||||
|
expr.i64_const(v)
|
||||||
|
} $else $if T is $int {
|
||||||
|
expr.i32_const(v)
|
||||||
|
} $else $if T is f32 {
|
||||||
|
expr.f32_const(v)
|
||||||
|
} $else $if T is f64 {
|
||||||
|
expr.f64_const(v)
|
||||||
|
} $else {
|
||||||
|
$compile_error('values can only be int, i32, i64, f32, f64')
|
||||||
|
}
|
||||||
|
|
||||||
|
return expr
|
||||||
|
}
|
||||||
|
|
||||||
|
// constexpr_ref_null returns a constant expression that evaluates to a null reference.
|
||||||
|
pub fn constexpr_ref_null(rt RefType) ConstExpression {
|
||||||
|
mut expr := ConstExpression{}
|
||||||
|
|
||||||
|
expr.ref_null(rt)
|
||||||
|
|
||||||
|
return expr
|
||||||
|
}
|
||||||
|
|
||||||
|
// WebAssembly constant expressions are permitted to use a subset of valid instructions.
|
||||||
|
pub struct ConstExpression {
|
||||||
|
mut:
|
||||||
|
call_patches []CallPatch
|
||||||
|
code []u8
|
||||||
|
}
|
||||||
|
|
||||||
|
// i32_const places a constant i32 value on the stack.
|
||||||
|
// WebAssembly instruction: `i32.const`.
|
||||||
|
pub fn (mut expr ConstExpression) i32_const(v i32) {
|
||||||
|
expr.code << 0x41 // i32.const
|
||||||
|
expr.code << leb128.encode_i32(v)
|
||||||
|
}
|
||||||
|
|
||||||
|
// i64_const places a constant i64 value on the stack.
|
||||||
|
// WebAssembly instruction: `i64.const`.
|
||||||
|
pub fn (mut expr ConstExpression) i64_const(v i64) {
|
||||||
|
expr.code << 0x42 // i64.const
|
||||||
|
expr.code << leb128.encode_i64(v)
|
||||||
|
}
|
||||||
|
|
||||||
|
// f32_const places a constant f32 value on the stack.
|
||||||
|
// WebAssembly instruction: `f32.const`.
|
||||||
|
pub fn (mut expr ConstExpression) f32_const(v f32) {
|
||||||
|
expr.code << 0x43 // f32.const
|
||||||
|
push_f32(mut expr.code, v)
|
||||||
|
}
|
||||||
|
|
||||||
|
// f64_const places a constant f64 value on the stack.
|
||||||
|
// WebAssembly instruction: `f64.const`.
|
||||||
|
pub fn (mut expr ConstExpression) f64_const(v f64) {
|
||||||
|
expr.code << 0x44 // f64.const
|
||||||
|
push_f64(mut expr.code, v)
|
||||||
|
}
|
||||||
|
|
||||||
|
// add adds two values on the stack with type `typ`.
|
||||||
|
// WebAssembly instructions: `i32|i64.add`.
|
||||||
|
pub fn (mut expr ConstExpression) add(typ NumType) {
|
||||||
|
assert typ in [.i32_t, .i64_t]
|
||||||
|
|
||||||
|
match typ {
|
||||||
|
.i32_t { expr.code << 0x6A } // i32.add
|
||||||
|
.i64_t { expr.code << 0x7C } // i64.add
|
||||||
|
else {}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// sub subtracts two values on the stack with type `typ`.
|
||||||
|
// WebAssembly instructions: `i32|i64.sub`.
|
||||||
|
pub fn (mut expr ConstExpression) sub(typ NumType) {
|
||||||
|
assert typ in [.i32_t, .i64_t]
|
||||||
|
|
||||||
|
match typ {
|
||||||
|
.i32_t { expr.code << 0x6B } // i32.sub
|
||||||
|
.i64_t { expr.code << 0x7D } // i64.sub
|
||||||
|
else {}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// mul multiplies two values on the stack with type `typ`.
|
||||||
|
// WebAssembly instructions: `i32|i64.mul`.
|
||||||
|
pub fn (mut expr ConstExpression) mul(typ NumType) {
|
||||||
|
assert typ in [.i32_t, .i64_t]
|
||||||
|
|
||||||
|
match typ {
|
||||||
|
.i32_t { expr.code << 0x6C } // i32.mul
|
||||||
|
.i64_t { expr.code << 0x7E } // i64.mul
|
||||||
|
else {}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// global_get places the value of the global at the index `global` on the stack.
|
||||||
|
// Constant expressions are only allowed to refer to imported globals.
|
||||||
|
// WebAssembly instruction: `global.get`.
|
||||||
|
pub fn (mut expr ConstExpression) global_get(global GlobalImportIndex) {
|
||||||
|
expr.code << 0x23 // global.get
|
||||||
|
expr.code << leb128.encode_u32(u32(global))
|
||||||
|
}
|
||||||
|
|
||||||
|
// ref_null places a null reference on the stack.
|
||||||
|
// WebAssembly instruction: `ref.null`.
|
||||||
|
pub fn (mut expr ConstExpression) ref_null(rt RefType) {
|
||||||
|
expr.code << 0xD0 // ref.null
|
||||||
|
expr.code << u8(rt)
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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 expr ConstExpression) ref_func(name string) {
|
||||||
|
expr.code << 0xD2 // ref.func
|
||||||
|
expr.call_patches << FunctionCallPatch{
|
||||||
|
name: name
|
||||||
|
pos: expr.code.len
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// ref_func 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 expr ConstExpression) ref_func_import(mod string, name string) {
|
||||||
|
expr.code << 0xD2 // ref.func
|
||||||
|
expr.call_patches << ImportCallPatch{
|
||||||
|
mod: mod
|
||||||
|
name: name
|
||||||
|
pos: expr.code.len
|
||||||
|
}
|
||||||
|
}
|
@ -1,6 +1,7 @@
|
|||||||
module wasm
|
module wasm
|
||||||
|
|
||||||
import encoding.leb128
|
import encoding.leb128
|
||||||
|
import math.bits
|
||||||
|
|
||||||
fn (mut mod Module) u32(v u32) {
|
fn (mut mod Module) u32(v u32) {
|
||||||
mod.buf << leb128.encode_u32(v)
|
mod.buf << leb128.encode_u32(v)
|
||||||
@ -34,34 +35,71 @@ fn (mut mod Module) function_type(ft FuncType) {
|
|||||||
mod.result_type(ft.results)
|
mod.result_type(ft.results)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn (mut mod Module) patch_calls(ft Function) {
|
fn (mut mod Module) global_type(vt ValType, is_mut bool) {
|
||||||
|
mod.buf << u8(vt)
|
||||||
|
mod.buf << u8(is_mut)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn push_f32(mut buf []u8, v f32) {
|
||||||
|
rv := bits.f32_bits(v)
|
||||||
|
buf << u8(rv >> u32(0))
|
||||||
|
buf << u8(rv >> u32(8))
|
||||||
|
buf << u8(rv >> u32(16))
|
||||||
|
buf << u8(rv >> u32(24))
|
||||||
|
}
|
||||||
|
|
||||||
|
fn push_f64(mut buf []u8, v f64) {
|
||||||
|
rv := bits.f64_bits(v)
|
||||||
|
buf << u8(rv >> u32(0))
|
||||||
|
buf << u8(rv >> u32(8))
|
||||||
|
buf << u8(rv >> u32(16))
|
||||||
|
buf << u8(rv >> u32(24))
|
||||||
|
buf << u8(rv >> u32(32))
|
||||||
|
buf << u8(rv >> u32(40))
|
||||||
|
buf << u8(rv >> u32(48))
|
||||||
|
buf << u8(rv >> u32(56))
|
||||||
|
}
|
||||||
|
|
||||||
|
fn (mod &Module) get_function_idx(patch CallPatch) int {
|
||||||
|
mut idx := -1
|
||||||
|
|
||||||
|
match patch {
|
||||||
|
FunctionCallPatch {
|
||||||
|
ftt := mod.functions[patch.name] or {
|
||||||
|
panic('called function ${patch.name} does not exist')
|
||||||
|
}
|
||||||
|
idx = ftt.idx + mod.fn_imports.len
|
||||||
|
}
|
||||||
|
ImportCallPatch {
|
||||||
|
for fnidx, c in mod.fn_imports {
|
||||||
|
if c.mod == patch.mod && c.name == patch.name {
|
||||||
|
idx = fnidx
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if idx == -1 {
|
||||||
|
panic('called imported function ${patch.mod}.${patch.name} does not exist')
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return idx
|
||||||
|
}
|
||||||
|
|
||||||
|
fn (mut mod Module) patch(ft Function) {
|
||||||
mut ptr := 0
|
mut ptr := 0
|
||||||
|
|
||||||
for patch in ft.call_patches {
|
for patch in ft.call_patches {
|
||||||
mut idx := -1
|
idx := mod.get_function_idx(patch)
|
||||||
|
|
||||||
match patch {
|
|
||||||
FunctionCallPatch {
|
|
||||||
ftt := mod.functions[patch.name] or {
|
|
||||||
panic('called function ${patch.name} does not exist')
|
|
||||||
}
|
|
||||||
idx = ftt.idx + mod.fn_imports.len
|
|
||||||
}
|
|
||||||
ImportCallPatch {
|
|
||||||
for fnidx, c in mod.fn_imports {
|
|
||||||
if c.mod == patch.mod && c.name == patch.name {
|
|
||||||
idx = fnidx
|
|
||||||
break
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if idx == -1 {
|
|
||||||
panic('called imported function ${patch.mod}.${patch.name} does not exist')
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
mod.buf << ft.code[ptr..patch.pos]
|
mod.buf << ft.code[ptr..patch.pos]
|
||||||
mod.buf << 0x10 // call
|
mod.u32(u32(idx))
|
||||||
|
ptr = patch.pos
|
||||||
|
}
|
||||||
|
|
||||||
|
for patch in ft.global_patches {
|
||||||
|
idx := mod.global_imports.len + patch.idx
|
||||||
|
mod.buf << ft.code[ptr..patch.pos]
|
||||||
mod.u32(u32(idx))
|
mod.u32(u32(idx))
|
||||||
ptr = patch.pos
|
ptr = patch.pos
|
||||||
}
|
}
|
||||||
@ -93,12 +131,11 @@ pub fn (mut mod Module) compile() []u8 {
|
|||||||
}
|
}
|
||||||
// https://webassembly.github.io/spec/core/binary/modules.html#import-section
|
// https://webassembly.github.io/spec/core/binary/modules.html#import-section
|
||||||
//
|
//
|
||||||
if mod.fn_imports.len > 0 {
|
if mod.fn_imports.len > 0 || mod.global_imports.len > 0 {
|
||||||
// Types
|
|
||||||
mod.buf << u8(Section.import_section)
|
mod.buf << u8(Section.import_section)
|
||||||
tpatch := mod.patch_start()
|
tpatch := mod.patch_start()
|
||||||
{
|
{
|
||||||
mod.u32(u32(mod.fn_imports.len))
|
mod.u32(u32(mod.fn_imports.len + mod.global_imports.len))
|
||||||
for ft in mod.fn_imports {
|
for ft in mod.fn_imports {
|
||||||
mod.u32(u32(ft.mod.len))
|
mod.u32(u32(ft.mod.len))
|
||||||
mod.buf << ft.mod.bytes()
|
mod.buf << ft.mod.bytes()
|
||||||
@ -107,6 +144,14 @@ pub fn (mut mod Module) compile() []u8 {
|
|||||||
mod.buf << 0x00 // function
|
mod.buf << 0x00 // function
|
||||||
mod.u32(u32(ft.tidx))
|
mod.u32(u32(ft.tidx))
|
||||||
}
|
}
|
||||||
|
for gt in mod.global_imports {
|
||||||
|
mod.u32(u32(gt.mod.len))
|
||||||
|
mod.buf << gt.mod.bytes()
|
||||||
|
mod.u32(u32(gt.name.len))
|
||||||
|
mod.buf << gt.name.bytes()
|
||||||
|
mod.buf << 0x03 // global
|
||||||
|
mod.global_type(gt.typ, gt.is_mut)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
mod.patch_len(tpatch)
|
mod.patch_len(tpatch)
|
||||||
}
|
}
|
||||||
@ -141,6 +186,32 @@ pub fn (mut mod Module) compile() []u8 {
|
|||||||
}
|
}
|
||||||
mod.patch_len(tpatch)
|
mod.patch_len(tpatch)
|
||||||
}
|
}
|
||||||
|
// https://webassembly.github.io/spec/core/binary/modules.html#global-section
|
||||||
|
//
|
||||||
|
if mod.globals.len > 0 {
|
||||||
|
mod.buf << u8(Section.global_section)
|
||||||
|
tpatch := mod.patch_start()
|
||||||
|
{
|
||||||
|
mod.u32(u32(mod.globals.len))
|
||||||
|
for gt in mod.globals {
|
||||||
|
mod.global_type(gt.typ, gt.is_mut)
|
||||||
|
|
||||||
|
{
|
||||||
|
mut ptr := 0
|
||||||
|
for patch in gt.init.call_patches {
|
||||||
|
idx := mod.get_function_idx(patch)
|
||||||
|
|
||||||
|
mod.buf << gt.init.code[ptr..patch.pos]
|
||||||
|
mod.u32(u32(idx))
|
||||||
|
ptr = patch.pos
|
||||||
|
}
|
||||||
|
mod.buf << gt.init.code[ptr..]
|
||||||
|
}
|
||||||
|
mod.buf << 0x0B // END expression opcode
|
||||||
|
}
|
||||||
|
}
|
||||||
|
mod.patch_len(tpatch)
|
||||||
|
}
|
||||||
// https://webassembly.github.io/spec/core/binary/modules.html#export-section
|
// https://webassembly.github.io/spec/core/binary/modules.html#export-section
|
||||||
//
|
//
|
||||||
if mod.functions.len > 0 {
|
if mod.functions.len > 0 {
|
||||||
@ -208,7 +279,7 @@ pub fn (mut mod Module) compile() []u8 {
|
|||||||
mod.u32(1)
|
mod.u32(1)
|
||||||
mod.buf << u8(lt)
|
mod.buf << u8(lt)
|
||||||
}
|
}
|
||||||
mod.patch_calls(ft)
|
mod.patch(ft)
|
||||||
mod.buf << 0x0B // END expression opcode
|
mod.buf << 0x0B // END expression opcode
|
||||||
}
|
}
|
||||||
mod.patch_len(fpatch)
|
mod.patch_len(fpatch)
|
||||||
|
@ -13,16 +13,22 @@ struct FunctionCallPatch {
|
|||||||
|
|
||||||
type CallPatch = FunctionCallPatch | ImportCallPatch
|
type CallPatch = FunctionCallPatch | ImportCallPatch
|
||||||
|
|
||||||
struct Function {
|
struct FunctionGlobalPatch {
|
||||||
|
idx GlobalIndex
|
||||||
|
pos int
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct Function {
|
||||||
tidx int
|
tidx int
|
||||||
idx int
|
idx int
|
||||||
mut:
|
mut:
|
||||||
call_patches []CallPatch
|
call_patches []CallPatch
|
||||||
label int
|
global_patches []FunctionGlobalPatch
|
||||||
export bool
|
label int
|
||||||
mod &Module
|
export bool
|
||||||
code []u8
|
mod &Module = unsafe { nil }
|
||||||
locals []ValType
|
code []u8
|
||||||
|
locals []ValType
|
||||||
pub:
|
pub:
|
||||||
name string
|
name string
|
||||||
}
|
}
|
||||||
|
@ -1,7 +1,6 @@
|
|||||||
module wasm
|
module wasm
|
||||||
|
|
||||||
import encoding.leb128
|
import encoding.leb128
|
||||||
import math.bits
|
|
||||||
|
|
||||||
fn (mut func Function) u32(v u32) {
|
fn (mut func Function) u32(v u32) {
|
||||||
func.code << leb128.encode_u32(v)
|
func.code << leb128.encode_u32(v)
|
||||||
@ -25,7 +24,7 @@ fn (mut func Function) blocktype(typ FuncType) {
|
|||||||
|
|
||||||
// new_local creates a function local and returns it's index.
|
// new_local creates a function local and returns it's index.
|
||||||
// See `local_get`, `local_set`, `local_tee`.
|
// See `local_get`, `local_set`, `local_tee`.
|
||||||
pub fn (mut func Function) new_local(v ValType) int {
|
pub fn (mut func Function) new_local(v ValType) LocalIndex {
|
||||||
ldiff := func.mod.functypes[func.tidx].parameters.len
|
ldiff := func.mod.functypes[func.tidx].parameters.len
|
||||||
ret := func.locals.len + ldiff
|
ret := func.locals.len + ldiff
|
||||||
|
|
||||||
@ -51,49 +50,73 @@ pub fn (mut func Function) i64_const(v i64) {
|
|||||||
// WebAssembly instruction: `f32.const`.
|
// WebAssembly instruction: `f32.const`.
|
||||||
pub fn (mut func Function) f32_const(v f32) {
|
pub fn (mut func Function) f32_const(v f32) {
|
||||||
func.code << 0x43 // f32.const
|
func.code << 0x43 // f32.const
|
||||||
rv := bits.f32_bits(v)
|
push_f32(mut func.code, v)
|
||||||
func.code << u8(rv >> u32(0))
|
|
||||||
func.code << u8(rv >> u32(8))
|
|
||||||
func.code << u8(rv >> u32(16))
|
|
||||||
func.code << u8(rv >> u32(24))
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// f64_const places a constant f64 value on the stack.
|
// f64_const places a constant f64 value on the stack.
|
||||||
// WebAssembly instruction: `f64.const`.
|
// WebAssembly instruction: `f64.const`.
|
||||||
pub fn (mut func Function) f64_const(v f64) {
|
pub fn (mut func Function) f64_const(v f64) {
|
||||||
func.code << 0x44 // f64.const
|
func.code << 0x44 // f64.const
|
||||||
rv := bits.f64_bits(v)
|
push_f64(mut func.code, v)
|
||||||
func.code << u8(rv >> u32(0))
|
|
||||||
func.code << u8(rv >> u32(8))
|
|
||||||
func.code << u8(rv >> u32(16))
|
|
||||||
func.code << u8(rv >> u32(24))
|
|
||||||
func.code << u8(rv >> u32(32))
|
|
||||||
func.code << u8(rv >> u32(40))
|
|
||||||
func.code << u8(rv >> u32(48))
|
|
||||||
func.code << u8(rv >> u32(56))
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// local_get places the value of the local at the index `local` on the stack.
|
// local_get places the value of the local at the index `local` on the stack.
|
||||||
// WebAssembly instruction: `local.get`.
|
// WebAssembly instruction: `local.get`.
|
||||||
pub fn (mut func Function) local_get(local int) {
|
pub fn (mut func Function) local_get(local LocalIndex) {
|
||||||
func.code << 0x20 // local.get
|
func.code << 0x20 // local.get
|
||||||
func.u32(u32(local))
|
func.u32(u32(local))
|
||||||
}
|
}
|
||||||
|
|
||||||
// local_get sets the local at the index `local` to the value on the stack.
|
// local_get sets the local at the index `local` to the value on the stack.
|
||||||
// WebAssembly instruction: `local.set`.
|
// WebAssembly instruction: `local.set`.
|
||||||
pub fn (mut func Function) local_set(local int) {
|
pub fn (mut func Function) local_set(local LocalIndex) {
|
||||||
func.code << 0x21 // local.set
|
func.code << 0x21 // local.set
|
||||||
func.u32(u32(local))
|
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.
|
// 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`.
|
// WebAssembly instruction: `local.tee`.
|
||||||
pub fn (mut func Function) local_tee(local int) {
|
pub fn (mut func Function) local_tee(local LocalIndex) {
|
||||||
func.code << 0x22 // local.tee
|
func.code << 0x22 // local.tee
|
||||||
func.u32(u32(local))
|
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.global_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.global_patches << FunctionGlobalPatch{
|
||||||
|
idx: global
|
||||||
|
pos: func.code.len
|
||||||
|
}
|
||||||
|
}
|
||||||
|
GlobalImportIndex {
|
||||||
|
func.u32(u32(global))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// drop drops the value on the stack
|
// drop drops the value on the stack
|
||||||
// WebAssembly instruction: `drop`.
|
// WebAssembly instruction: `drop`.
|
||||||
pub fn (mut func Function) drop() {
|
pub fn (mut func Function) drop() {
|
||||||
@ -847,9 +870,10 @@ pub fn (mut func Function) c_end_if() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// call calls a locally defined function.
|
// call calls a locally defined function.
|
||||||
// If this function does not exist when calling `compile` on the module, it panic.
|
// If this function does not exist when calling `compile` on the module, it will panic.
|
||||||
// WebAssembly instruction: `call`.
|
// WebAssembly instruction: `call`.
|
||||||
pub fn (mut func Function) call(name string) {
|
pub fn (mut func Function) call(name string) {
|
||||||
|
func.code << 0x10 // call
|
||||||
func.call_patches << FunctionCallPatch{
|
func.call_patches << FunctionCallPatch{
|
||||||
name: name
|
name: name
|
||||||
pos: func.code.len
|
pos: func.code.len
|
||||||
@ -857,9 +881,10 @@ pub fn (mut func Function) call(name string) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// call calls an imported function.
|
// call calls an imported function.
|
||||||
// If the imported function does not exist when calling `compile` on the module, it panic.
|
// If the imported function does not exist when calling `compile` on the module, it will panic.
|
||||||
// WebAssembly instruction: `call`.
|
// WebAssembly instruction: `call`.
|
||||||
pub fn (mut func Function) call_import(mod string, name string) {
|
pub fn (mut func Function) call_import(mod string, name string) {
|
||||||
|
func.code << 0x10 // call
|
||||||
func.call_patches << ImportCallPatch{
|
func.call_patches << ImportCallPatch{
|
||||||
mod: mod
|
mod: mod
|
||||||
name: name
|
name: name
|
||||||
@ -1009,7 +1034,7 @@ pub fn (mut func Function) memory_grow() {
|
|||||||
|
|
||||||
// memory_init copies from a passive memory segment to the memory instance.
|
// memory_init copies from a passive memory segment to the memory instance.
|
||||||
// WebAssembly instruction: `memory.init`.
|
// WebAssembly instruction: `memory.init`.
|
||||||
pub fn (mut func Function) memory_init(idx int) {
|
pub fn (mut func Function) memory_init(idx DataSegmentIndex) {
|
||||||
func.code << 0xFC
|
func.code << 0xFC
|
||||||
func.code << 0x08
|
func.code << 0x08
|
||||||
func.u32(u32(idx))
|
func.u32(u32(idx))
|
||||||
@ -1018,7 +1043,7 @@ pub fn (mut func Function) memory_init(idx int) {
|
|||||||
|
|
||||||
// data_drop prevents further use of a passive memory segment.
|
// data_drop prevents further use of a passive memory segment.
|
||||||
// WebAssembly instruction: `data.drop`.
|
// WebAssembly instruction: `data.drop`.
|
||||||
pub fn (mut func Function) data_drop(idx int) {
|
pub fn (mut func Function) data_drop(idx DataSegmentIndex) {
|
||||||
func.code << 0xFC
|
func.code << 0xFC
|
||||||
func.code << 0x09
|
func.code << 0x09
|
||||||
func.u32(u32(idx))
|
func.u32(u32(idx))
|
||||||
@ -1037,3 +1062,39 @@ pub fn (mut func Function) memory_copy() {
|
|||||||
pub fn (mut func Function) memory_fill() {
|
pub fn (mut func Function) memory_fill() {
|
||||||
func.code << [u8(0xFC), 0x0B, 0x00]
|
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.call_patches << FunctionCallPatch{
|
||||||
|
name: name
|
||||||
|
pos: func.code.len
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// ref_func 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.call_patches << ImportCallPatch{
|
||||||
|
mod: mod
|
||||||
|
name: name
|
||||||
|
pos: func.code.len
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -33,18 +33,39 @@ pub enum ValType as u8 {
|
|||||||
externref_t = 0x6f
|
externref_t = 0x6f
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub enum RefType as u8 {
|
||||||
|
funcref_t = 0x70
|
||||||
|
externref_t = 0x6f
|
||||||
|
}
|
||||||
|
|
||||||
// Module contains the WebAssembly module.
|
// Module contains the WebAssembly module.
|
||||||
// Use the `compile` method to compile the module into a pure byte array.
|
// Use the `compile` method to compile the module into a pure byte array.
|
||||||
[heap]
|
[heap]
|
||||||
pub struct Module {
|
pub struct Module {
|
||||||
mut:
|
mut:
|
||||||
buf []u8
|
buf []u8
|
||||||
functypes []FuncType
|
functypes []FuncType
|
||||||
functions map[string]Function
|
functions map[string]Function
|
||||||
memory ?Memory
|
globals []Global
|
||||||
start ?string
|
memory ?Memory
|
||||||
fn_imports []FunctionImport
|
start ?string
|
||||||
segments []DataSegment
|
fn_imports []FunctionImport
|
||||||
|
global_imports []GlobalImport
|
||||||
|
segments []DataSegment
|
||||||
|
}
|
||||||
|
|
||||||
|
struct Global {
|
||||||
|
typ ValType
|
||||||
|
is_mut bool
|
||||||
|
export_name ?string
|
||||||
|
init ConstExpression
|
||||||
|
}
|
||||||
|
|
||||||
|
struct GlobalImport {
|
||||||
|
mod string
|
||||||
|
name string
|
||||||
|
typ ValType
|
||||||
|
is_mut bool
|
||||||
}
|
}
|
||||||
|
|
||||||
struct FunctionImport {
|
struct FunctionImport {
|
||||||
@ -65,6 +86,11 @@ struct DataSegment {
|
|||||||
data []u8
|
data []u8
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub type LocalIndex = int
|
||||||
|
pub type GlobalIndex = int
|
||||||
|
pub type GlobalImportIndex = int
|
||||||
|
pub type DataSegmentIndex = int
|
||||||
|
|
||||||
[params]
|
[params]
|
||||||
pub struct FuncType {
|
pub struct FuncType {
|
||||||
pub:
|
pub:
|
||||||
@ -138,7 +164,7 @@ pub fn (mut mod Module) commit(func Function, export bool) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// new_data_segment inserts a new data segment at the memory index `pos`.
|
// new_data_segment inserts a new data segment at the memory index `pos`.
|
||||||
pub fn (mut mod Module) new_data_segment(pos int, data []u8) int {
|
pub fn (mut mod Module) new_data_segment(pos int, data []u8) DataSegmentIndex {
|
||||||
len := mod.segments.len
|
len := mod.segments.len
|
||||||
mod.segments << DataSegment{
|
mod.segments << DataSegment{
|
||||||
idx: pos
|
idx: pos
|
||||||
@ -153,3 +179,32 @@ pub fn (mut mod Module) new_passive_data_segment(data []u8) {
|
|||||||
data: data
|
data: data
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// new_global creates a global and returns it's index.
|
||||||
|
// If `export_name` is none, the global will not be exported.
|
||||||
|
// See `global_get`, `global_set`.
|
||||||
|
pub fn (mut mod Module) new_global(export_name ?string, typ ValType, is_mut bool, init ConstExpression) GlobalIndex {
|
||||||
|
len := mod.globals.len
|
||||||
|
mod.globals << Global{
|
||||||
|
typ: typ
|
||||||
|
is_mut: is_mut
|
||||||
|
export_name: export_name
|
||||||
|
init: init
|
||||||
|
}
|
||||||
|
return len
|
||||||
|
}
|
||||||
|
|
||||||
|
// new_global_import imports a new global into the current module and returns it's index.
|
||||||
|
// See `global_import_get`, `global_import_set`.
|
||||||
|
pub fn (mut mod Module) new_global_import(modn string, name string, typ ValType, is_mut bool) GlobalImportIndex {
|
||||||
|
assert !mod.fn_imports.any(it.mod == modn && it.name == name)
|
||||||
|
|
||||||
|
len := mod.global_imports.len
|
||||||
|
mod.global_imports << GlobalImport{
|
||||||
|
mod: modn
|
||||||
|
name: name
|
||||||
|
typ: typ
|
||||||
|
is_mut: is_mut
|
||||||
|
}
|
||||||
|
return len
|
||||||
|
}
|
||||||
|
@ -22,7 +22,7 @@ fn validate(mod []u8) ! {
|
|||||||
proc.close()
|
proc.close()
|
||||||
}
|
}
|
||||||
|
|
||||||
fn test_add() {
|
fn test_block() {
|
||||||
mut m := wasm.Module{}
|
mut m := wasm.Module{}
|
||||||
mut a1 := m.new_function('param', [], [.i32_t])
|
mut a1 := m.new_function('param', [], [.i32_t])
|
||||||
{
|
{
|
||||||
|
@ -22,7 +22,7 @@ fn validate(mod []u8) ! {
|
|||||||
proc.close()
|
proc.close()
|
||||||
}
|
}
|
||||||
|
|
||||||
fn test_add() {
|
fn test_call() {
|
||||||
mut m := wasm.Module{}
|
mut m := wasm.Module{}
|
||||||
mut a1 := m.new_function('const-i32', [], [.i32_t])
|
mut a1 := m.new_function('const-i32', [], [.i32_t])
|
||||||
{
|
{
|
||||||
|
55
vlib/wasm/tests/var_test.v
Normal file
55
vlib/wasm/tests/var_test.v
Normal file
@ -0,0 +1,55 @@
|
|||||||
|
import wasm
|
||||||
|
import os
|
||||||
|
|
||||||
|
const exe = os.find_abs_path_of_executable('wasm-validate') or { exit(0) }
|
||||||
|
|
||||||
|
fn validate(mod []u8) ! {
|
||||||
|
mut proc := os.new_process(exe)
|
||||||
|
proc.set_args(['-'])
|
||||||
|
proc.set_redirect_stdio()
|
||||||
|
proc.run()
|
||||||
|
{
|
||||||
|
os.fd_write(proc.stdio_fd[0], mod.bytestr())
|
||||||
|
os.fd_close(proc.stdio_fd[0])
|
||||||
|
}
|
||||||
|
proc.wait()
|
||||||
|
if proc.status != .exited {
|
||||||
|
return error('wasm-validate exited abormally')
|
||||||
|
}
|
||||||
|
if proc.code != 0 {
|
||||||
|
return error('wasm-validate exited with a non zero exit code')
|
||||||
|
}
|
||||||
|
proc.close()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn test_globals() {
|
||||||
|
mut m := wasm.Module{}
|
||||||
|
|
||||||
|
vsp := m.new_global('__vsp', .i32_t, true, wasm.constexpr_value(10))
|
||||||
|
mut func := m.new_function('vsp', [], [.i32_t])
|
||||||
|
{
|
||||||
|
func.global_get(vsp)
|
||||||
|
func.i32_const(20)
|
||||||
|
func.add(.i32_t)
|
||||||
|
func.global_set(vsp)
|
||||||
|
func.global_get(vsp)
|
||||||
|
}
|
||||||
|
m.commit(func, true)
|
||||||
|
|
||||||
|
fref := m.new_global('__ref', .funcref_t, true, wasm.constexpr_ref_null(.funcref_t))
|
||||||
|
mut func1 := m.new_function('ref', [], [])
|
||||||
|
{
|
||||||
|
func1.ref_func('vsp')
|
||||||
|
func1.global_set(fref)
|
||||||
|
}
|
||||||
|
m.commit(func1, true)
|
||||||
|
|
||||||
|
gimport := m.new_global_import('env', '__import', .f64_t, false)
|
||||||
|
mut func2 := m.new_function('import', [], [.f64_t])
|
||||||
|
{
|
||||||
|
func2.global_get(gimport)
|
||||||
|
}
|
||||||
|
m.commit(func2, true)
|
||||||
|
|
||||||
|
validate(m.compile()) or { panic(err) }
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user