mirror of
https://github.com/vlang/v.git
synced 2023-08-10 21:13:21 +03:00
wasm: add basic debuginfo through name
section (#18130)
This commit is contained in:
parent
5bdf94a7bc
commit
3a06b55388
@ -28,15 +28,15 @@ fn main() {
|
|||||||
mut ifexpr := m.new_function('if_expr', [.i32_t], [.i64_t])
|
mut ifexpr := m.new_function('if_expr', [.i32_t], [.i64_t])
|
||||||
{
|
{
|
||||||
ifexpr.local_get(0)
|
ifexpr.local_get(0)
|
||||||
ifexpr.c_if([], [.i64_t])
|
if_blk := ifexpr.c_if([], [.i64_t])
|
||||||
{
|
{
|
||||||
ifexpr.i64_const(5000)
|
ifexpr.i64_const(5000)
|
||||||
}
|
}
|
||||||
ifexpr.c_else()
|
ifexpr.c_else(if_blk)
|
||||||
{
|
{
|
||||||
ifexpr.i64_const(-5000)
|
ifexpr.i64_const(-5000)
|
||||||
}
|
}
|
||||||
ifexpr.c_end_if()
|
ifexpr.c_end(if_blk)
|
||||||
}
|
}
|
||||||
m.commit(ifexpr, true)
|
m.commit(ifexpr, true)
|
||||||
print(m.compile().bytestr())
|
print(m.compile().bytestr())
|
||||||
|
@ -6,11 +6,11 @@ fn main() {
|
|||||||
{
|
{
|
||||||
fac.local_get(0)
|
fac.local_get(0)
|
||||||
fac.eqz(.i64_t)
|
fac.eqz(.i64_t)
|
||||||
fac.c_if([], [.i64_t])
|
bif := fac.c_if([], [.i64_t])
|
||||||
{
|
{
|
||||||
fac.i64_const(1)
|
fac.i64_const(1)
|
||||||
}
|
}
|
||||||
fac.c_else()
|
fac.c_else(bif)
|
||||||
{
|
{
|
||||||
{
|
{
|
||||||
fac.local_get(0)
|
fac.local_get(0)
|
||||||
@ -23,7 +23,7 @@ fn main() {
|
|||||||
}
|
}
|
||||||
fac.mul(.i64_t)
|
fac.mul(.i64_t)
|
||||||
}
|
}
|
||||||
fac.c_end_if()
|
fac.c_end(bif)
|
||||||
}
|
}
|
||||||
m.commit(fac, true)
|
m.commit(fac, true)
|
||||||
print(m.compile().bytestr())
|
print(m.compile().bytestr())
|
||||||
|
@ -2,14 +2,15 @@ import wasm
|
|||||||
|
|
||||||
fn main() {
|
fn main() {
|
||||||
mut m := wasm.Module{}
|
mut m := wasm.Module{}
|
||||||
|
m.enable_debug('vlang')
|
||||||
m.new_function_import('wasi_unstable', 'proc_exit', [.i32_t], [])
|
m.new_function_import('wasi_unstable', 'proc_exit', [.i32_t], [])
|
||||||
m.new_function_import('wasi_unstable', 'fd_write', [.i32_t, .i32_t, .i32_t, .i32_t],
|
m.new_function_import('wasi_unstable', 'fd_write', [.i32_t, .i32_t, .i32_t, .i32_t],
|
||||||
[.i32_t])
|
[.i32_t])
|
||||||
m.assign_memory('memory', true, 1, none)
|
m.assign_memory('memory', true, 1, none)
|
||||||
|
|
||||||
m.new_data_segment(0, [u8(8), 0, 0, 0]) // pointer to string
|
m.new_data_segment('CIOVec.str', 0, [u8(8), 0, 0, 0]) // pointer to string
|
||||||
m.new_data_segment(4, [u8(13), 0, 0, 0]) // length of string
|
m.new_data_segment('CIOVec.len', 4, [u8(13), 0, 0, 0]) // length of string
|
||||||
m.new_data_segment(8, 'Hello, WASI!\n'.bytes())
|
m.new_data_segment(none, 8, 'Hello, WASI!\n'.bytes())
|
||||||
|
|
||||||
mut func := m.new_function('_start', [], [])
|
mut func := m.new_function('_start', [], [])
|
||||||
{
|
{
|
||||||
|
@ -13,6 +13,8 @@ V developers seeking to build high-performance web applications.
|
|||||||
The module is designed to generate a `[]u8`, which can be written to a `.wasm` file
|
The module is designed to generate a `[]u8`, which can be written to a `.wasm` file
|
||||||
or executed in memory.
|
or executed in memory.
|
||||||
|
|
||||||
|
Examples are present in `examples/wasm_codegen`.
|
||||||
|
|
||||||
```v
|
```v
|
||||||
import wasm
|
import wasm
|
||||||
import os
|
import os
|
||||||
|
@ -1,3 +1,6 @@
|
|||||||
|
// 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
|
module wasm
|
||||||
|
|
||||||
import encoding.leb128
|
import encoding.leb128
|
||||||
@ -21,6 +24,34 @@ pub fn constexpr_value[T](v T) ConstExpression {
|
|||||||
return expr
|
return expr
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// constexpr_value_zero returns a constant expression that evaluates to zero.
|
||||||
|
pub fn constexpr_value_zero(v ValType) ConstExpression {
|
||||||
|
mut expr := ConstExpression{}
|
||||||
|
|
||||||
|
match v {
|
||||||
|
.i32_t {
|
||||||
|
expr.i32_const(0)
|
||||||
|
}
|
||||||
|
.i64_t {
|
||||||
|
expr.i64_const(0)
|
||||||
|
}
|
||||||
|
.f32_t {
|
||||||
|
expr.f32_const(0.0)
|
||||||
|
}
|
||||||
|
.f64_t {
|
||||||
|
expr.f64_const(0.0)
|
||||||
|
}
|
||||||
|
.funcref_t, .externref_t {
|
||||||
|
expr.ref_null(RefType(v))
|
||||||
|
}
|
||||||
|
.v128_t {
|
||||||
|
panic('type `v128_t` not permitted in a constant expression')
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return expr
|
||||||
|
}
|
||||||
|
|
||||||
// constexpr_ref_null returns a constant expression that evaluates to a null reference.
|
// constexpr_ref_null returns a constant expression that evaluates to a null reference.
|
||||||
pub fn constexpr_ref_null(rt RefType) ConstExpression {
|
pub fn constexpr_ref_null(rt RefType) ConstExpression {
|
||||||
mut expr := ConstExpression{}
|
mut expr := ConstExpression{}
|
||||||
|
@ -1,3 +1,6 @@
|
|||||||
|
// 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
|
module wasm
|
||||||
|
|
||||||
import encoding.leb128
|
import encoding.leb128
|
||||||
@ -89,16 +92,16 @@ fn (mod &Module) get_function_idx(patch CallPatch) int {
|
|||||||
fn (mut mod Module) patch(ft Function) {
|
fn (mut mod Module) patch(ft Function) {
|
||||||
mut ptr := 0
|
mut ptr := 0
|
||||||
|
|
||||||
for patch in ft.call_patches {
|
for patch in ft.patches {
|
||||||
idx := mod.get_function_idx(patch)
|
mut idx := 0
|
||||||
|
match patch {
|
||||||
mod.buf << ft.code[ptr..patch.pos]
|
CallPatch {
|
||||||
mod.u32(u32(idx))
|
idx = mod.get_function_idx(patch)
|
||||||
ptr = patch.pos
|
}
|
||||||
}
|
FunctionGlobalPatch {
|
||||||
|
idx = mod.global_imports.len + patch.idx
|
||||||
for patch in ft.global_patches {
|
}
|
||||||
idx := mod.global_imports.len + patch.idx
|
}
|
||||||
mod.buf << ft.code[ptr..patch.pos]
|
mod.buf << ft.code[ptr..patch.pos]
|
||||||
mod.u32(u32(idx))
|
mod.u32(u32(idx))
|
||||||
ptr = patch.pos
|
ptr = patch.pos
|
||||||
@ -107,6 +110,29 @@ fn (mut mod Module) patch(ft Function) {
|
|||||||
mod.buf << ft.code[ptr..]
|
mod.buf << ft.code[ptr..]
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// name
|
||||||
|
pub fn (mut mod Module) name(name string) {
|
||||||
|
mod.u32(u32(name.len))
|
||||||
|
mod.buf << name.bytes()
|
||||||
|
}
|
||||||
|
|
||||||
|
// start_subsection
|
||||||
|
pub fn (mut mod Module) start_subsection(sec Subsection) int {
|
||||||
|
mod.buf << u8(sec)
|
||||||
|
return mod.patch_start()
|
||||||
|
}
|
||||||
|
|
||||||
|
// start_section
|
||||||
|
pub fn (mut mod Module) start_section(sec Section) int {
|
||||||
|
mod.buf << u8(sec)
|
||||||
|
return mod.patch_start()
|
||||||
|
}
|
||||||
|
|
||||||
|
// end_section
|
||||||
|
pub fn (mut mod Module) end_section(tpatch int) {
|
||||||
|
mod.patch_len(tpatch)
|
||||||
|
}
|
||||||
|
|
||||||
// compile serialises the WebAssembly module into a byte array.
|
// compile serialises the WebAssembly module into a byte array.
|
||||||
// The returned byte array can be written out into a `.wasm`, or executed in memory.
|
// The returned byte array can be written out into a `.wasm`, or executed in memory.
|
||||||
pub fn (mut mod Module) compile() []u8 {
|
pub fn (mut mod Module) compile() []u8 {
|
||||||
@ -118,22 +144,19 @@ pub fn (mut mod Module) compile() []u8 {
|
|||||||
// https://webassembly.github.io/spec/core/binary/modules.html#type-section
|
// https://webassembly.github.io/spec/core/binary/modules.html#type-section
|
||||||
//
|
//
|
||||||
if mod.functypes.len > 0 {
|
if mod.functypes.len > 0 {
|
||||||
// Types
|
tpatch := mod.start_section(.type_section)
|
||||||
mod.buf << u8(Section.type_section)
|
|
||||||
tpatch := mod.patch_start()
|
|
||||||
{
|
{
|
||||||
mod.u32(u32(mod.functypes.len))
|
mod.u32(u32(mod.functypes.len))
|
||||||
for ft in mod.functypes {
|
for ft in mod.functypes {
|
||||||
mod.function_type(ft)
|
mod.function_type(ft)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
mod.patch_len(tpatch)
|
mod.end_section(tpatch)
|
||||||
}
|
}
|
||||||
// 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 || mod.global_imports.len > 0 {
|
if mod.fn_imports.len > 0 || mod.global_imports.len > 0 {
|
||||||
mod.buf << u8(Section.import_section)
|
tpatch := mod.start_section(.import_section)
|
||||||
tpatch := mod.patch_start()
|
|
||||||
{
|
{
|
||||||
mod.u32(u32(mod.fn_imports.len + mod.global_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 {
|
||||||
@ -153,26 +176,24 @@ pub fn (mut mod Module) compile() []u8 {
|
|||||||
mod.global_type(gt.typ, gt.is_mut)
|
mod.global_type(gt.typ, gt.is_mut)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
mod.patch_len(tpatch)
|
mod.end_section(tpatch)
|
||||||
}
|
}
|
||||||
// https://webassembly.github.io/spec/core/binary/modules.html#binary-funcsec
|
// https://webassembly.github.io/spec/core/binary/modules.html#binary-funcsec
|
||||||
//
|
//
|
||||||
if mod.functions.len > 0 {
|
if mod.functions.len > 0 {
|
||||||
mod.buf << u8(Section.function_section)
|
tpatch := mod.start_section(.function_section)
|
||||||
tpatch := mod.patch_start()
|
|
||||||
{
|
{
|
||||||
mod.u32(u32(mod.functions.len))
|
mod.u32(u32(mod.functions.len))
|
||||||
for _, ft in mod.functions {
|
for _, ft in mod.functions {
|
||||||
mod.u32(u32(ft.tidx))
|
mod.u32(u32(ft.tidx))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
mod.patch_len(tpatch)
|
mod.end_section(tpatch)
|
||||||
}
|
}
|
||||||
// https://webassembly.github.io/spec/core/binary/modules.html#binary-memsec
|
// https://webassembly.github.io/spec/core/binary/modules.html#binary-memsec
|
||||||
//
|
//
|
||||||
if memory := mod.memory {
|
if memory := mod.memory {
|
||||||
mod.buf << u8(Section.memory_section)
|
tpatch := mod.start_section(.memory_section)
|
||||||
tpatch := mod.patch_start()
|
|
||||||
{
|
{
|
||||||
mod.u32(1)
|
mod.u32(1)
|
||||||
if max := memory.max {
|
if max := memory.max {
|
||||||
@ -184,13 +205,12 @@ pub fn (mut mod Module) compile() []u8 {
|
|||||||
mod.u32(memory.min)
|
mod.u32(memory.min)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
mod.patch_len(tpatch)
|
mod.end_section(tpatch)
|
||||||
}
|
}
|
||||||
// https://webassembly.github.io/spec/core/binary/modules.html#global-section
|
// https://webassembly.github.io/spec/core/binary/modules.html#global-section
|
||||||
//
|
//
|
||||||
if mod.globals.len > 0 {
|
if mod.globals.len > 0 {
|
||||||
mod.buf << u8(Section.global_section)
|
tpatch := mod.start_section(.global_section)
|
||||||
tpatch := mod.patch_start()
|
|
||||||
{
|
{
|
||||||
mod.u32(u32(mod.globals.len))
|
mod.u32(u32(mod.globals.len))
|
||||||
for gt in mod.globals {
|
for gt in mod.globals {
|
||||||
@ -210,13 +230,12 @@ pub fn (mut mod Module) compile() []u8 {
|
|||||||
mod.buf << 0x0B // END expression opcode
|
mod.buf << 0x0B // END expression opcode
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
mod.patch_len(tpatch)
|
mod.end_section(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 {
|
{
|
||||||
mod.buf << u8(Section.export_section)
|
tpatch := mod.start_section(.export_section)
|
||||||
tpatch := mod.patch_start()
|
|
||||||
{
|
{
|
||||||
lpatch := mod.patch_start()
|
lpatch := mod.patch_start()
|
||||||
mut lsz := 0
|
mut lsz := 0
|
||||||
@ -225,59 +244,64 @@ pub fn (mut mod Module) compile() []u8 {
|
|||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
lsz++
|
lsz++
|
||||||
mod.u32(u32(ft.name.len))
|
mod.name(ft.name)
|
||||||
mod.buf << ft.name.bytes()
|
|
||||||
mod.buf << 0x00 // function
|
mod.buf << 0x00 // function
|
||||||
mod.u32(u32(ft.idx + mod.fn_imports.len))
|
mod.u32(u32(ft.idx + mod.fn_imports.len))
|
||||||
}
|
}
|
||||||
if memory := mod.memory {
|
if memory := mod.memory {
|
||||||
if memory.export {
|
if memory.export {
|
||||||
lsz++
|
lsz++
|
||||||
mod.u32(u32(memory.name.len))
|
mod.name(memory.name)
|
||||||
mod.buf << memory.name.bytes()
|
|
||||||
mod.buf << 0x02 // function
|
mod.buf << 0x02 // function
|
||||||
mod.u32(0)
|
mod.u32(0)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
for idx, gbl in mod.globals {
|
||||||
|
if !gbl.export {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
lsz++
|
||||||
|
mod.name(gbl.name)
|
||||||
|
mod.buf << 0x03 // global
|
||||||
|
mod.u32(u32(idx + mod.global_imports.len))
|
||||||
|
}
|
||||||
mod.patch_u32(lpatch, u32(lsz))
|
mod.patch_u32(lpatch, u32(lsz))
|
||||||
}
|
}
|
||||||
mod.patch_len(tpatch)
|
mod.end_section(tpatch)
|
||||||
}
|
}
|
||||||
// https://webassembly.github.io/spec/core/binary/modules.html#binary-startsec
|
// https://webassembly.github.io/spec/core/binary/modules.html#binary-startsec
|
||||||
//
|
//
|
||||||
if start := mod.start {
|
if start := mod.start {
|
||||||
ftt := mod.functions[start] or { panic('start function ${start} does not exist') }
|
ftt := mod.functions[start] or { panic('start function ${start} does not exist') }
|
||||||
mod.buf << u8(Section.start_section)
|
tpatch := mod.start_section(.start_section)
|
||||||
tpatch := mod.patch_start()
|
|
||||||
{
|
{
|
||||||
mod.u32(u32(ftt.idx + mod.fn_imports.len))
|
mod.u32(u32(ftt.idx + mod.fn_imports.len))
|
||||||
}
|
}
|
||||||
mod.patch_len(tpatch)
|
mod.end_section(tpatch)
|
||||||
}
|
}
|
||||||
// https://webassembly.github.io/spec/core/binary/modules.html#data-count-section
|
// https://webassembly.github.io/spec/core/binary/modules.html#data-count-section
|
||||||
//
|
//
|
||||||
if mod.segments.len > 0 {
|
if mod.segments.len > 0 {
|
||||||
mod.buf << u8(Section.data_count_section)
|
tpatch := mod.start_section(.data_count_section)
|
||||||
tpatch := mod.patch_start()
|
|
||||||
{
|
{
|
||||||
mod.u32(u32(mod.segments.len))
|
mod.u32(u32(mod.segments.len))
|
||||||
}
|
}
|
||||||
mod.patch_len(tpatch)
|
mod.end_section(tpatch)
|
||||||
}
|
}
|
||||||
// https://webassembly.github.io/spec/core/binary/modules.html#binary-codesec
|
// https://webassembly.github.io/spec/core/binary/modules.html#binary-codesec
|
||||||
//
|
//
|
||||||
if mod.functions.len > 0 {
|
if mod.functions.len > 0 {
|
||||||
mod.buf << u8(Section.code_section)
|
tpatch := mod.start_section(.code_section)
|
||||||
tpatch := mod.patch_start()
|
|
||||||
{
|
{
|
||||||
mod.u32(u32(mod.functions.len))
|
mod.u32(u32(mod.functions.len))
|
||||||
for _, ft in mod.functions {
|
for _, ft in mod.functions {
|
||||||
fpatch := mod.patch_start()
|
fpatch := mod.patch_start()
|
||||||
|
rloc := ft.locals[mod.functypes[ft.tidx].parameters.len..]
|
||||||
{
|
{
|
||||||
mod.u32(u32(ft.locals.len))
|
mod.u32(u32(rloc.len))
|
||||||
for lt in ft.locals {
|
for lt in rloc {
|
||||||
mod.u32(1)
|
mod.u32(1)
|
||||||
mod.buf << u8(lt)
|
mod.buf << u8(lt.typ)
|
||||||
}
|
}
|
||||||
mod.patch(ft)
|
mod.patch(ft)
|
||||||
mod.buf << 0x0B // END expression opcode
|
mod.buf << 0x0B // END expression opcode
|
||||||
@ -285,13 +309,12 @@ pub fn (mut mod Module) compile() []u8 {
|
|||||||
mod.patch_len(fpatch)
|
mod.patch_len(fpatch)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
mod.patch_len(tpatch)
|
mod.end_section(tpatch)
|
||||||
}
|
}
|
||||||
// https://webassembly.github.io/spec/core/binary/modules.html#data-section
|
// https://webassembly.github.io/spec/core/binary/modules.html#data-section
|
||||||
//
|
//
|
||||||
if mod.segments.len > 0 {
|
if mod.segments.len > 0 {
|
||||||
mod.buf << u8(Section.data_section)
|
tpatch := mod.start_section(.data_section)
|
||||||
tpatch := mod.patch_start()
|
|
||||||
{
|
{
|
||||||
mod.u32(u32(mod.segments.len))
|
mod.u32(u32(mod.segments.len))
|
||||||
for _, seg in mod.segments {
|
for _, seg in mod.segments {
|
||||||
@ -308,7 +331,129 @@ pub fn (mut mod Module) compile() []u8 {
|
|||||||
mod.buf << seg.data
|
mod.buf << seg.data
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
mod.patch_len(tpatch)
|
mod.end_section(tpatch)
|
||||||
|
}
|
||||||
|
// https://webassembly.github.io/spec/core/appendix/custom.html#name-section
|
||||||
|
//
|
||||||
|
if mod.debug {
|
||||||
|
tpatch := mod.start_section(.custom_section)
|
||||||
|
mod.name('name')
|
||||||
|
if mod_name := mod.mod_name {
|
||||||
|
mpatch := mod.start_subsection(.name_module)
|
||||||
|
{
|
||||||
|
mod.name(mod_name)
|
||||||
|
}
|
||||||
|
mod.end_section(mpatch)
|
||||||
|
}
|
||||||
|
{
|
||||||
|
mpatch := mod.start_subsection(.name_function)
|
||||||
|
{
|
||||||
|
mod.u32(u32(mod.functions.len + mod.fn_imports.len))
|
||||||
|
mut idx := 0
|
||||||
|
for f in mod.fn_imports {
|
||||||
|
mod.u32(u32(idx))
|
||||||
|
mod.name('${f.mod}.${f.name}')
|
||||||
|
idx++
|
||||||
|
}
|
||||||
|
for n, _ in mod.functions {
|
||||||
|
mod.u32(u32(idx))
|
||||||
|
mod.name(n)
|
||||||
|
idx++
|
||||||
|
}
|
||||||
|
}
|
||||||
|
mod.end_section(mpatch)
|
||||||
|
}
|
||||||
|
{
|
||||||
|
mpatch := mod.start_subsection(.name_local)
|
||||||
|
{
|
||||||
|
fpatch := mod.patch_start()
|
||||||
|
mut fcount := 0
|
||||||
|
mut idx := mod.fn_imports.len // after imports
|
||||||
|
for _, ft in mod.functions {
|
||||||
|
// only add entry if it contains a local with an assigned name
|
||||||
|
if ft.locals.any(it.name != none) {
|
||||||
|
mod.u32(u32(idx)) // function idx
|
||||||
|
|
||||||
|
mut lcount := 0
|
||||||
|
lcpatch := mod.patch_start()
|
||||||
|
for lidx, loc in ft.locals {
|
||||||
|
if name := loc.name {
|
||||||
|
mod.u32(u32(lidx))
|
||||||
|
mod.name(name)
|
||||||
|
lcount++
|
||||||
|
}
|
||||||
|
}
|
||||||
|
mod.patch_u32(lcpatch, u32(lcount))
|
||||||
|
fcount++
|
||||||
|
}
|
||||||
|
idx++
|
||||||
|
}
|
||||||
|
mod.patch_u32(fpatch, u32(fcount))
|
||||||
|
}
|
||||||
|
mod.end_section(mpatch)
|
||||||
|
}
|
||||||
|
{
|
||||||
|
mpatch := mod.start_subsection(.name_type)
|
||||||
|
{
|
||||||
|
fpatch := mod.patch_start()
|
||||||
|
mut fcount := 0
|
||||||
|
for idx, ft in mod.functypes {
|
||||||
|
if name := ft.name {
|
||||||
|
mod.u32(u32(idx))
|
||||||
|
mod.name(name)
|
||||||
|
fcount++
|
||||||
|
}
|
||||||
|
}
|
||||||
|
mod.patch_u32(fpatch, u32(fcount))
|
||||||
|
}
|
||||||
|
mod.end_section(mpatch)
|
||||||
|
}
|
||||||
|
if memory := mod.memory {
|
||||||
|
mpatch := mod.start_subsection(.name_memory)
|
||||||
|
{
|
||||||
|
mod.u32(u32(1)) // one memory in vec
|
||||||
|
mod.u32(u32(0)) // 0 idx
|
||||||
|
mod.name(memory.name) // memory name
|
||||||
|
}
|
||||||
|
mod.end_section(mpatch)
|
||||||
|
}
|
||||||
|
if mod.globals.len != 0 || mod.global_imports.len != 0 {
|
||||||
|
mpatch := mod.start_subsection(.name_global)
|
||||||
|
{
|
||||||
|
fpatch := mod.patch_start()
|
||||||
|
mut fcount := 0
|
||||||
|
for gbl in mod.global_imports {
|
||||||
|
mod.u32(u32(fcount))
|
||||||
|
mod.name('${gbl.mod}.${gbl.name}')
|
||||||
|
fcount++
|
||||||
|
}
|
||||||
|
for gbl in mod.globals {
|
||||||
|
mod.u32(u32(fcount))
|
||||||
|
mod.name(gbl.name)
|
||||||
|
fcount++
|
||||||
|
}
|
||||||
|
mod.patch_u32(fpatch, u32(fcount))
|
||||||
|
}
|
||||||
|
mod.end_section(mpatch)
|
||||||
|
}
|
||||||
|
if mod.segments.any(it.name != none) {
|
||||||
|
mpatch := mod.start_subsection(.name_data)
|
||||||
|
{
|
||||||
|
fpatch := mod.patch_start()
|
||||||
|
mut fcount := 0
|
||||||
|
for idx, ds in mod.segments {
|
||||||
|
if name := ds.name {
|
||||||
|
mod.u32(u32(idx))
|
||||||
|
mod.name(name)
|
||||||
|
fcount++
|
||||||
|
}
|
||||||
|
}
|
||||||
|
mod.patch_u32(fpatch, u32(fcount))
|
||||||
|
}
|
||||||
|
mod.end_section(mpatch)
|
||||||
|
}
|
||||||
|
|
||||||
|
mod.end_section(tpatch)
|
||||||
}
|
}
|
||||||
|
|
||||||
return mod.buf
|
return mod.buf
|
||||||
|
@ -1,34 +1,46 @@
|
|||||||
|
// 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
|
module wasm
|
||||||
|
|
||||||
struct ImportCallPatch {
|
struct ImportCallPatch {
|
||||||
mod string
|
mod string
|
||||||
name string
|
name string
|
||||||
pos int
|
mut:
|
||||||
|
pos int
|
||||||
}
|
}
|
||||||
|
|
||||||
struct FunctionCallPatch {
|
struct FunctionCallPatch {
|
||||||
name string
|
name string
|
||||||
pos int
|
mut:
|
||||||
|
pos int
|
||||||
}
|
}
|
||||||
|
|
||||||
type CallPatch = FunctionCallPatch | ImportCallPatch
|
type CallPatch = FunctionCallPatch | ImportCallPatch
|
||||||
|
|
||||||
struct FunctionGlobalPatch {
|
struct FunctionGlobalPatch {
|
||||||
idx GlobalIndex
|
idx GlobalIndex
|
||||||
|
mut:
|
||||||
pos int
|
pos int
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type FunctionPatch = CallPatch | FunctionGlobalPatch
|
||||||
|
|
||||||
|
struct FunctionLocal {
|
||||||
|
typ ValType
|
||||||
|
name ?string
|
||||||
|
}
|
||||||
|
|
||||||
pub struct Function {
|
pub struct Function {
|
||||||
tidx int
|
tidx int
|
||||||
idx int
|
idx int
|
||||||
mut:
|
mut:
|
||||||
call_patches []CallPatch
|
patches []FunctionPatch // sorted
|
||||||
global_patches []FunctionGlobalPatch
|
label int
|
||||||
label int
|
export bool
|
||||||
export bool
|
mod &Module = unsafe { nil }
|
||||||
mod &Module = unsafe { nil }
|
code []u8
|
||||||
code []u8
|
locals []FunctionLocal
|
||||||
locals []ValType
|
|
||||||
pub:
|
pub:
|
||||||
name string
|
name string
|
||||||
}
|
}
|
||||||
|
@ -1,3 +1,6 @@
|
|||||||
|
// 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
|
module wasm
|
||||||
|
|
||||||
import encoding.leb128
|
import encoding.leb128
|
||||||
@ -22,13 +25,85 @@ fn (mut func Function) blocktype(typ FuncType) {
|
|||||||
func.code << leb128.encode_i32(tidx)
|
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.
|
// 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) LocalIndex {
|
pub fn (mut func Function) new_local(v ValType) LocalIndex {
|
||||||
ldiff := func.mod.functypes[func.tidx].parameters.len
|
ret := func.locals.len
|
||||||
ret := func.locals.len + ldiff
|
func.locals << FunctionLocal{
|
||||||
|
typ: v
|
||||||
|
}
|
||||||
|
return ret
|
||||||
|
}
|
||||||
|
|
||||||
func.locals << v
|
// 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
|
return ret
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -89,7 +164,7 @@ pub fn (mut func Function) global_get(global GlobalIndices) {
|
|||||||
func.code << 0x23 // global.get
|
func.code << 0x23 // global.get
|
||||||
match global {
|
match global {
|
||||||
GlobalIndex {
|
GlobalIndex {
|
||||||
func.global_patches << FunctionGlobalPatch{
|
func.patches << FunctionGlobalPatch{
|
||||||
idx: global
|
idx: global
|
||||||
pos: func.code.len
|
pos: func.code.len
|
||||||
}
|
}
|
||||||
@ -106,7 +181,7 @@ pub fn (mut func Function) global_set(global GlobalIndices) {
|
|||||||
func.code << 0x24 // global.set
|
func.code << 0x24 // global.set
|
||||||
match global {
|
match global {
|
||||||
GlobalIndex {
|
GlobalIndex {
|
||||||
func.global_patches << FunctionGlobalPatch{
|
func.patches << FunctionGlobalPatch{
|
||||||
idx: global
|
idx: global
|
||||||
pos: func.code.len
|
pos: func.code.len
|
||||||
}
|
}
|
||||||
@ -798,20 +873,22 @@ pub fn (mut func Function) nop() {
|
|||||||
func.code << 0x01
|
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`.
|
// 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`.
|
// 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.
|
// All blocks must be ended, see the `c_end` function.
|
||||||
pub fn (mut func Function) c_block(parameters []ValType, results []ValType) int {
|
pub fn (mut func Function) c_block(parameters []ValType, results []ValType) LabelIndex {
|
||||||
func.label++
|
func.label++
|
||||||
func.code << 0x02
|
func.code << 0x02
|
||||||
func.blocktype(parameters: parameters, results: results)
|
func.blocktype(parameters: parameters, results: results)
|
||||||
return func.label
|
return func.label
|
||||||
}
|
}
|
||||||
|
|
||||||
// c_loop creates a label that can later be branched to of with `c_br` and `c_br_if`.
|
// 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`.
|
// 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.
|
// All loops must be ended, see the `c_end` function.
|
||||||
pub fn (mut func Function) c_loop(parameters []ValType, results []ValType) int {
|
pub fn (mut func Function) c_loop(parameters []ValType, results []ValType) LabelIndex {
|
||||||
func.label++
|
func.label++
|
||||||
func.code << 0x03
|
func.code << 0x03
|
||||||
func.blocktype(parameters: parameters, results: results)
|
func.blocktype(parameters: parameters, results: results)
|
||||||
@ -819,16 +896,20 @@ pub fn (mut func Function) c_loop(parameters []ValType, results []ValType) int {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// c_if opens an if expression. It executes a statement if the last item on the stack is true.
|
// 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`.
|
// 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`.
|
// 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 by calling with `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) {
|
pub fn (mut func Function) c_if(parameters []ValType, results []ValType) LabelIndex {
|
||||||
|
func.label++
|
||||||
func.code << 0x04
|
func.code << 0x04
|
||||||
func.blocktype(parameters: parameters, results: results)
|
func.blocktype(parameters: parameters, results: results)
|
||||||
|
return func.label
|
||||||
}
|
}
|
||||||
|
|
||||||
// c_else starts the else case of an if expression, it must be closed by calling `c_end_if`.
|
// 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() {
|
pub fn (mut func Function) c_else(label LabelIndex) {
|
||||||
|
assert func.label == label, 'c_else: called with an invalid label ${label}'
|
||||||
func.code << 0x05
|
func.code << 0x05
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -839,7 +920,7 @@ pub fn (mut func Function) c_return() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// c_end ends the block or loop with the label passed in at `label`.
|
// c_end ends the block or loop with the label passed in at `label`.
|
||||||
pub fn (mut func Function) c_end(label int) {
|
pub fn (mut func Function) c_end(label LabelIndex) {
|
||||||
assert func.label == label, 'c_end: called with an invalid label ${label}'
|
assert func.label == label, 'c_end: called with an invalid label ${label}'
|
||||||
func.label--
|
func.label--
|
||||||
assert func.label >= 0, 'c_end: negative label index, unbalanced calls'
|
assert func.label >= 0, 'c_end: negative label index, unbalanced calls'
|
||||||
@ -848,7 +929,7 @@ pub fn (mut func Function) c_end(label int) {
|
|||||||
|
|
||||||
// c_br branches to a loop or block with the label passed in at `label`.
|
// c_br branches to a loop or block with the label passed in at `label`.
|
||||||
// WebAssembly instruction: `br`.
|
// WebAssembly instruction: `br`.
|
||||||
pub fn (mut func Function) c_br(label int) {
|
pub fn (mut func Function) c_br(label LabelIndex) {
|
||||||
v := func.label - label
|
v := func.label - label
|
||||||
assert v >= 0, 'c_br: malformed label index'
|
assert v >= 0, 'c_br: malformed label index'
|
||||||
func.code << 0x0C // br
|
func.code << 0x0C // br
|
||||||
@ -857,7 +938,7 @@ pub fn (mut func Function) c_br(label int) {
|
|||||||
|
|
||||||
// c_br_if branches to a loop or block with the label passed in at `label`, based on an i32 condition.
|
// 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`.
|
// WebAssembly instruction: `br_if`.
|
||||||
pub fn (mut func Function) c_br_if(label int) {
|
pub fn (mut func Function) c_br_if(label LabelIndex) {
|
||||||
v := func.label - label
|
v := func.label - label
|
||||||
assert v >= 0, 'c_br_if: malformed label index'
|
assert v >= 0, 'c_br_if: malformed label index'
|
||||||
func.code << 0x0D // br_if
|
func.code << 0x0D // br_if
|
||||||
@ -874,10 +955,10 @@ pub fn (mut func Function) c_end_if() {
|
|||||||
// 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.code << 0x10 // call
|
||||||
func.call_patches << FunctionCallPatch{
|
func.patches << CallPatch(FunctionCallPatch{
|
||||||
name: name
|
name: name
|
||||||
pos: func.code.len
|
pos: func.code.len
|
||||||
}
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
// call calls an imported function.
|
// call calls an imported function.
|
||||||
@ -885,11 +966,11 @@ pub fn (mut func Function) call(name string) {
|
|||||||
// 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.code << 0x10 // call
|
||||||
func.call_patches << ImportCallPatch{
|
func.patches << CallPatch(ImportCallPatch{
|
||||||
mod: mod
|
mod: mod
|
||||||
name: name
|
name: name
|
||||||
pos: func.code.len
|
pos: func.code.len
|
||||||
}
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
// load loads a value with type `typ` from memory.
|
// load loads a value with type `typ` from memory.
|
||||||
@ -1081,20 +1162,20 @@ pub fn (mut func Function) ref_is_null(rt RefType) {
|
|||||||
// WebAssembly instruction: `ref.func`.
|
// WebAssembly instruction: `ref.func`.
|
||||||
pub fn (mut func Function) ref_func(name string) {
|
pub fn (mut func Function) ref_func(name string) {
|
||||||
func.code << 0xD2 // ref.func
|
func.code << 0xD2 // ref.func
|
||||||
func.call_patches << FunctionCallPatch{
|
func.patches << CallPatch(FunctionCallPatch{
|
||||||
name: name
|
name: name
|
||||||
pos: func.code.len
|
pos: func.code.len
|
||||||
}
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
// ref_func places a reference to an imported function with `name` on the stack.
|
// 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.
|
// If the imported function does not exist when calling `compile` on the module, it will panic.
|
||||||
// WebAssembly instruction: `ref.func`.
|
// WebAssembly instruction: `ref.func`.
|
||||||
pub fn (mut func Function) ref_func_import(mod string, name string) {
|
pub fn (mut func Function) ref_func_import(mod string, name string) {
|
||||||
func.code << 0xD2 // ref.func
|
func.code << 0xD2 // ref.func
|
||||||
func.call_patches << ImportCallPatch{
|
func.patches << CallPatch(ImportCallPatch{
|
||||||
mod: mod
|
mod: mod
|
||||||
name: name
|
name: name
|
||||||
pos: func.code.len
|
pos: func.code.len
|
||||||
}
|
})
|
||||||
}
|
}
|
||||||
|
@ -1,3 +1,6 @@
|
|||||||
|
// 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
|
module wasm
|
||||||
|
|
||||||
enum Section as u8 {
|
enum Section as u8 {
|
||||||
@ -16,6 +19,20 @@ enum Section as u8 {
|
|||||||
data_count_section
|
data_count_section
|
||||||
}
|
}
|
||||||
|
|
||||||
|
enum Subsection as u8 {
|
||||||
|
name_module
|
||||||
|
name_function
|
||||||
|
name_local
|
||||||
|
// see: https://github.com/WebAssembly/extended-name-section
|
||||||
|
name_label
|
||||||
|
name_type
|
||||||
|
name_table
|
||||||
|
name_memory
|
||||||
|
name_global
|
||||||
|
name_elem
|
||||||
|
name_data
|
||||||
|
}
|
||||||
|
|
||||||
pub enum NumType as u8 {
|
pub enum NumType as u8 {
|
||||||
i32_t = 0x7f
|
i32_t = 0x7f
|
||||||
i64_t = 0x7e
|
i64_t = 0x7e
|
||||||
@ -52,13 +69,17 @@ mut:
|
|||||||
fn_imports []FunctionImport
|
fn_imports []FunctionImport
|
||||||
global_imports []GlobalImport
|
global_imports []GlobalImport
|
||||||
segments []DataSegment
|
segments []DataSegment
|
||||||
|
debug bool
|
||||||
|
mod_name ?string
|
||||||
}
|
}
|
||||||
|
|
||||||
struct Global {
|
struct Global {
|
||||||
typ ValType
|
typ ValType
|
||||||
is_mut bool
|
is_mut bool
|
||||||
export_name ?string
|
name string
|
||||||
init ConstExpression
|
export bool
|
||||||
|
mut:
|
||||||
|
init ConstExpression
|
||||||
}
|
}
|
||||||
|
|
||||||
struct GlobalImport {
|
struct GlobalImport {
|
||||||
@ -84,6 +105,7 @@ struct Memory {
|
|||||||
struct DataSegment {
|
struct DataSegment {
|
||||||
idx ?int
|
idx ?int
|
||||||
data []u8
|
data []u8
|
||||||
|
name ?string
|
||||||
}
|
}
|
||||||
|
|
||||||
pub type LocalIndex = int
|
pub type LocalIndex = int
|
||||||
@ -91,11 +113,11 @@ pub type GlobalIndex = int
|
|||||||
pub type GlobalImportIndex = int
|
pub type GlobalImportIndex = int
|
||||||
pub type DataSegmentIndex = int
|
pub type DataSegmentIndex = int
|
||||||
|
|
||||||
[params]
|
|
||||||
pub struct FuncType {
|
pub struct FuncType {
|
||||||
pub:
|
pub:
|
||||||
parameters []ValType
|
parameters []ValType
|
||||||
results []ValType
|
results []ValType
|
||||||
|
name ?string
|
||||||
}
|
}
|
||||||
|
|
||||||
fn (mut mod Module) new_functype(ft FuncType) int {
|
fn (mut mod Module) new_functype(ft FuncType) int {
|
||||||
@ -115,16 +137,41 @@ pub fn (mut mod Module) new_function(name string, parameters []ValType, results
|
|||||||
assert name !in mod.functions.keys()
|
assert name !in mod.functions.keys()
|
||||||
|
|
||||||
idx := mod.functions.len
|
idx := mod.functions.len
|
||||||
tidx := mod.new_functype(FuncType{parameters, results})
|
tidx := mod.new_functype(FuncType{parameters, results, none})
|
||||||
|
|
||||||
return Function{
|
return Function{
|
||||||
name: name
|
name: name
|
||||||
tidx: tidx
|
tidx: tidx
|
||||||
idx: idx
|
idx: idx
|
||||||
mod: mod
|
mod: mod
|
||||||
|
locals: parameters.map(FunctionLocal{}) // specifying it's ValType doesn't matter
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// new_debug_function creates a function struct with extra debug information.
|
||||||
|
// `argument_names` must be the same length as the parameters in the function type `typ`.
|
||||||
|
pub fn (mut mod Module) new_debug_function(name string, typ FuncType, argument_names []?string) Function {
|
||||||
|
assert name !in mod.functions.keys()
|
||||||
|
assert typ.parameters.len == argument_names.len
|
||||||
|
|
||||||
|
idx := mod.functions.len
|
||||||
|
tidx := mod.new_functype(typ)
|
||||||
|
|
||||||
|
return Function{
|
||||||
|
name: name
|
||||||
|
tidx: tidx
|
||||||
|
idx: idx
|
||||||
|
mod: mod
|
||||||
|
locals: argument_names.map(FunctionLocal{ name: it }) // specifying it's ValType doesn't matter
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// enable_debug sets whether to emit debug information for not.
|
||||||
|
pub fn (mut mod Module) enable_debug(mod_name ?string) {
|
||||||
|
mod.debug = true
|
||||||
|
mod.mod_name = mod_name
|
||||||
|
}
|
||||||
|
|
||||||
// assign_memory assigns memory to the current module.
|
// assign_memory assigns memory to the current module.
|
||||||
pub fn (mut mod Module) assign_memory(name string, export bool, min u32, max ?u32) {
|
pub fn (mut mod Module) assign_memory(name string, export bool, min u32, max ?u32) {
|
||||||
mod.memory = Memory{
|
mod.memory = Memory{
|
||||||
@ -144,7 +191,20 @@ pub fn (mut mod Module) assign_start(name string) {
|
|||||||
pub fn (mut mod Module) new_function_import(modn string, name string, parameters []ValType, results []ValType) {
|
pub fn (mut mod Module) new_function_import(modn string, name string, parameters []ValType, results []ValType) {
|
||||||
assert !mod.fn_imports.any(it.mod == modn && it.name == name)
|
assert !mod.fn_imports.any(it.mod == modn && it.name == name)
|
||||||
|
|
||||||
tidx := mod.new_functype(FuncType{parameters, results})
|
tidx := mod.new_functype(FuncType{parameters, results, none})
|
||||||
|
|
||||||
|
mod.fn_imports << FunctionImport{
|
||||||
|
mod: modn
|
||||||
|
name: name
|
||||||
|
tidx: tidx
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// new_function_import_debug imports a new function into the current module with extra debug information.
|
||||||
|
pub fn (mut mod Module) new_function_import_debug(modn string, name string, typ FuncType) {
|
||||||
|
assert !mod.fn_imports.any(it.mod == modn && it.name == name)
|
||||||
|
|
||||||
|
tidx := mod.new_functype(typ)
|
||||||
|
|
||||||
mod.fn_imports << FunctionImport{
|
mod.fn_imports << FunctionImport{
|
||||||
mod: modn
|
mod: modn
|
||||||
@ -164,38 +224,42 @@ 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) DataSegmentIndex {
|
// `name` is optional, it is used for debug info.
|
||||||
|
pub fn (mut mod Module) new_data_segment(name ?string, pos int, data []u8) DataSegmentIndex {
|
||||||
len := mod.segments.len
|
len := mod.segments.len
|
||||||
mod.segments << DataSegment{
|
mod.segments << DataSegment{
|
||||||
idx: pos
|
idx: pos
|
||||||
data: data
|
data: data
|
||||||
|
name: name
|
||||||
}
|
}
|
||||||
return len
|
return len
|
||||||
}
|
}
|
||||||
|
|
||||||
// new_passive_data_segment inserts a new passive data segment.
|
// new_passive_data_segment inserts a new passive data segment.
|
||||||
pub fn (mut mod Module) new_passive_data_segment(data []u8) {
|
// `name` is optional, it is used for debug info.
|
||||||
|
pub fn (mut mod Module) new_passive_data_segment(name ?string, data []u8) {
|
||||||
mod.segments << DataSegment{
|
mod.segments << DataSegment{
|
||||||
data: data
|
data: data
|
||||||
|
name: name
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// new_global creates a global and returns it's index.
|
// 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`.
|
// See `global_get`, `global_set`.
|
||||||
pub fn (mut mod Module) new_global(export_name ?string, typ ValType, is_mut bool, init ConstExpression) GlobalIndex {
|
pub fn (mut mod Module) new_global(name string, export bool, typ ValType, is_mut bool, init ConstExpression) GlobalIndex {
|
||||||
len := mod.globals.len
|
len := mod.globals.len
|
||||||
mod.globals << Global{
|
mod.globals << Global{
|
||||||
typ: typ
|
typ: typ
|
||||||
is_mut: is_mut
|
is_mut: is_mut
|
||||||
export_name: export_name
|
name: name
|
||||||
|
export: export
|
||||||
init: init
|
init: init
|
||||||
}
|
}
|
||||||
return len
|
return len
|
||||||
}
|
}
|
||||||
|
|
||||||
// new_global_import imports a new global into the current module and returns it's index.
|
// new_global_import imports a new global into the current module and returns it's index.
|
||||||
// See `global_import_get`, `global_import_set`.
|
// See `global_get`, `global_set`.
|
||||||
pub fn (mut mod Module) new_global_import(modn string, name string, typ ValType, is_mut bool) GlobalImportIndex {
|
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)
|
assert !mod.fn_imports.any(it.mod == modn && it.name == name)
|
||||||
|
|
||||||
@ -208,3 +272,9 @@ pub fn (mut mod Module) new_global_import(modn string, name string, typ ValType,
|
|||||||
}
|
}
|
||||||
return len
|
return len
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// assign_global_init assigns a global with the constant expression `init`.
|
||||||
|
// See `new_global`.
|
||||||
|
pub fn (mut mod Module) assign_global_init(global GlobalIndex, init ConstExpression) {
|
||||||
|
mod.globals[global].init = init
|
||||||
|
}
|
||||||
|
@ -61,11 +61,11 @@ fn test_call() {
|
|||||||
{
|
{
|
||||||
fac.local_get(0)
|
fac.local_get(0)
|
||||||
fac.eqz(.i64_t)
|
fac.eqz(.i64_t)
|
||||||
fac.c_if([], [.i64_t])
|
ifs := fac.c_if([], [.i64_t])
|
||||||
{
|
{
|
||||||
fac.i64_const(1)
|
fac.i64_const(1)
|
||||||
}
|
}
|
||||||
fac.c_else()
|
fac.c_else(ifs)
|
||||||
{
|
{
|
||||||
{
|
{
|
||||||
fac.local_get(0)
|
fac.local_get(0)
|
||||||
@ -78,7 +78,7 @@ fn test_call() {
|
|||||||
}
|
}
|
||||||
fac.mul(.i64_t)
|
fac.mul(.i64_t)
|
||||||
}
|
}
|
||||||
fac.c_end_if()
|
fac.c_end(ifs)
|
||||||
}
|
}
|
||||||
m.commit(fac, true)
|
m.commit(fac, true)
|
||||||
|
|
||||||
|
59
vlib/wasm/tests/debug_test.v
Normal file
59
vlib/wasm/tests/debug_test.v
Normal file
@ -0,0 +1,59 @@
|
|||||||
|
module main
|
||||||
|
|
||||||
|
import wasm
|
||||||
|
|
||||||
|
fn test_debug() {
|
||||||
|
mut m := wasm.Module{}
|
||||||
|
m.enable_debug('hello VLANG!')
|
||||||
|
|
||||||
|
// FuncType with debuginfo/name
|
||||||
|
func_type := wasm.FuncType{[.i32_t, .i32_t], [.i32_t], 'fn_<int_int>_int'}
|
||||||
|
|
||||||
|
// []?string, named function parameters
|
||||||
|
param_names := [?string('a'), 'b']
|
||||||
|
|
||||||
|
mut a1 := m.new_debug_function('add', func_type, param_names)
|
||||||
|
{
|
||||||
|
loc := a1.new_local_named(.i32_t, 'intermediary')
|
||||||
|
a1.local_get(0)
|
||||||
|
a1.local_get(1)
|
||||||
|
a1.add(.i32_t)
|
||||||
|
a1.local_set(loc)
|
||||||
|
a1.local_get(loc)
|
||||||
|
}
|
||||||
|
m.commit(a1, false)
|
||||||
|
validate(m.compile()) or { panic(err) }
|
||||||
|
}
|
||||||
|
|
||||||
|
fn test_wasi_debug() {
|
||||||
|
mut m := wasm.Module{}
|
||||||
|
m.enable_debug('hello!')
|
||||||
|
m.new_global('__vsp', true, .i32_t, true, wasm.constexpr_value(10))
|
||||||
|
|
||||||
|
m.new_global_import('env', 'glble', .i32_t, false)
|
||||||
|
|
||||||
|
m.new_function_import('wasi_unstable', 'proc_exit', [.i32_t], [])
|
||||||
|
m.new_function_import('wasi_unstable', 'fd_write', [.i32_t, .i32_t, .i32_t, .i32_t],
|
||||||
|
[.i32_t])
|
||||||
|
m.assign_memory('memory', true, 1, none)
|
||||||
|
|
||||||
|
m.new_data_segment('CIOVec.str', 0, [u8(8), 0, 0, 0]) // pointer to string
|
||||||
|
m.new_data_segment('CIOVec.len', 4, [u8(13), 0, 0, 0]) // length of string
|
||||||
|
m.new_data_segment(none, 8, 'Hello, WASI!\n'.bytes())
|
||||||
|
|
||||||
|
mut func := m.new_function('_start', [], [])
|
||||||
|
{
|
||||||
|
func.new_local_named(.i32_t, 'loc')
|
||||||
|
func.i32_const(1) // stdout
|
||||||
|
func.i32_const(0) // *iovs
|
||||||
|
func.i32_const(1) // 1 iov
|
||||||
|
func.i32_const(-1) // *retptrs
|
||||||
|
func.call_import('wasi_unstable', 'fd_write')
|
||||||
|
func.drop()
|
||||||
|
func.i32_const(0)
|
||||||
|
func.call_import('wasi_unstable', 'proc_exit')
|
||||||
|
}
|
||||||
|
m.commit(func, true)
|
||||||
|
|
||||||
|
print(m.compile().bytestr())
|
||||||
|
}
|
30
vlib/wasm/tests/patch_test.v
Normal file
30
vlib/wasm/tests/patch_test.v
Normal file
@ -0,0 +1,30 @@
|
|||||||
|
module main
|
||||||
|
|
||||||
|
import wasm
|
||||||
|
|
||||||
|
fn test_patch() {
|
||||||
|
mut m := wasm.Module{}
|
||||||
|
vsp := m.new_global('__vsp', false, .i32_t, true, wasm.constexpr_value(10))
|
||||||
|
mut at := m.new_function('add', [], [])
|
||||||
|
{
|
||||||
|
pp := at.patch_pos()
|
||||||
|
at.drop()
|
||||||
|
at.i32_const(10)
|
||||||
|
at.i32_const(10)
|
||||||
|
at.i32_const(10)
|
||||||
|
at.drop()
|
||||||
|
at.drop()
|
||||||
|
at.drop()
|
||||||
|
pps := at.patch_pos()
|
||||||
|
{
|
||||||
|
at.i32_const(10)
|
||||||
|
at.i32_const(10)
|
||||||
|
at.global_set(vsp)
|
||||||
|
at.i32_const(10)
|
||||||
|
at.global_set(vsp)
|
||||||
|
}
|
||||||
|
at.patch(pp, pps) // move to start
|
||||||
|
}
|
||||||
|
m.commit(at, true)
|
||||||
|
validate(m.compile()) or { panic(err) }
|
||||||
|
}
|
@ -4,8 +4,9 @@ import wasm
|
|||||||
|
|
||||||
fn test_globals() {
|
fn test_globals() {
|
||||||
mut m := wasm.Module{}
|
mut m := wasm.Module{}
|
||||||
|
m.enable_debug('vlang')
|
||||||
|
|
||||||
vsp := m.new_global('__vsp', .i32_t, true, wasm.constexpr_value(10))
|
vsp := m.new_global('__vsp', false, .i32_t, true, wasm.constexpr_value(10))
|
||||||
mut func := m.new_function('vsp', [], [.i32_t])
|
mut func := m.new_function('vsp', [], [.i32_t])
|
||||||
{
|
{
|
||||||
func.global_get(vsp)
|
func.global_get(vsp)
|
||||||
@ -16,7 +17,7 @@ fn test_globals() {
|
|||||||
}
|
}
|
||||||
m.commit(func, true)
|
m.commit(func, true)
|
||||||
|
|
||||||
fref := m.new_global('__ref', .funcref_t, true, wasm.constexpr_ref_null(.funcref_t))
|
fref := m.new_global('__ref', true, .funcref_t, true, wasm.constexpr_ref_null(.funcref_t))
|
||||||
mut func1 := m.new_function('ref', [], [])
|
mut func1 := m.new_function('ref', [], [])
|
||||||
{
|
{
|
||||||
func1.ref_func('vsp')
|
func1.ref_func('vsp')
|
||||||
|
Loading…
Reference in New Issue
Block a user