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

169 lines
4.7 KiB
V

module wasm
import v.ast
import v.gen.wasm.binaryen
import encoding.binary as bin
import math.bits
fn (mut g Gen) bake_constants_plus_initialisers() []GlobalData {
mut initialisers := []GlobalData{}
for _, global in g.globals {
match global.init {
/*
ast.ArrayInit {
// TODO: call a seraliser recursively over all elements
if !global.init.is_fixed {
g.w_error('wasm backend does not support non fixed arrays yet')
}
for global.init
}*/
ast.BoolLiteral {
g.constant_data << ConstantData{
offset: global.abs_address
data: [u8(global.init.val)]
}
}
ast.FloatLiteral {
mut buf := []u8{len: 8}
wtyp := g.get_wasm_type(global.ast_typ)
match wtyp {
type_f32 {
bin.little_endian_put_u32(mut buf, bits.f32_bits(global.init.val.f32()))
}
type_f64 {
bin.little_endian_put_u64(mut buf, bits.f64_bits(global.init.val.f64()))
unsafe {
buf.len = 4
}
}
else {}
}
g.constant_data << ConstantData{
offset: global.abs_address
data: buf
}
}
ast.StringLiteral {
offset, len := g.allocate_string(global.init)
if g.table.sym(global.ast_typ).info !is ast.Struct {
mut buf := []u8{len: 4}
bin.little_endian_put_u32(mut buf, u32(offset))
g.constant_data << ConstantData{
offset: global.abs_address
data: buf
}
} else {
mut buf := []u8{len: 8}
bin.little_endian_put_u32(mut buf, u32(offset))
bin.little_endian_put_u32_at(mut buf, u32(len), 4)
g.constant_data << ConstantData{
offset: global.abs_address
data: buf
}
}
}
ast.IntegerLiteral {
mut buf := []u8{len: 8}
wtyp := g.get_wasm_type(global.ast_typ)
match wtyp {
type_i32 {
bin.little_endian_put_u32(mut buf, u32(global.init.val.int()))
}
type_i64 {
bin.little_endian_put_u64(mut buf, u64(global.init.val.i64()))
unsafe {
buf.len = 4
}
}
else {}
}
g.constant_data << ConstantData{
offset: global.abs_address
data: buf
}
}
else {
initialisers << global
}
}
}
return initialisers
}
fn round_up_to_multiple(val int, multiple int) int {
return val + (multiple - val % multiple) % multiple
}
fn (mut g Gen) make_vinit() binaryen.Function {
runtime_inits := g.bake_constants_plus_initialisers()
g.bare_function_start()
mut body := runtime_inits.map(g.set_var_v(it.to_var(''), g.expr(it.init, it.ast_typ)))
for mod_name in g.table.modules {
if mod_name == 'v.reflection' {
g.w_error('the wasm backend does not implement `v.reflection` yet')
}
init_fn_name := if mod_name != 'builtin' { '${mod_name}.init' } else { 'init' }
if _ := g.table.find_fn(init_fn_name) {
body << binaryen.call(g.mod, init_fn_name.str, unsafe { nil }, 0, type_none)
}
cleanup_fn_name := if mod_name != 'builtin' { '${mod_name}.cleanup' } else { 'cleanup' }
if _ := g.table.find_fn(cleanup_fn_name) {
body << binaryen.call(g.mod, cleanup_fn_name.str, unsafe { nil }, 0, type_none)
}
}
return g.bare_function('_vinit', g.mkblock(body))
}
fn (mut g Gen) housekeeping() {
// `_vinit` should be used to initialise the WASM module,
// then `main.main` can be called safely.
vinit := g.make_vinit()
stack_base := round_up_to_multiple(g.constant_data_offset, 1024)
heap_base := if g.needs_stack {
stack_base + 1024 * 16 // 16KiB of stack
} else {
stack_base
}
pages_needed := heap_base / (1024 * 64) + 1
if g.needs_stack || g.constant_data.len != 0 {
data := g.constant_data.map(it.data.data)
data_len := g.constant_data.map(it.data.len)
data_offsets := g.constant_data.map(binaryen.constant(g.mod, binaryen.literalint32(it.offset)))
passive := []bool{len: g.constant_data.len, init: false}
binaryen.setmemory(g.mod, pages_needed, pages_needed + 4, c'memory', data.data,
passive.data, data_offsets.data, data_len.data, data.len, false, false, c'memory')
binaryen.addglobal(g.mod, c'__heap_base', type_i32, false, g.literalint(heap_base,
ast.int_type))
}
if g.needs_stack {
// `g.constant_data_offset` rounded up to a multiple of 1024
binaryen.addglobal(g.mod, c'__vsp', type_i32, true, g.literalint(stack_base, ast.int_type))
}
if g.pref.os == .wasi {
main_expr := g.mkblock([binaryen.call(g.mod, c'_vinit', unsafe { nil }, 0, type_none),
binaryen.call(g.mod, c'main.main', unsafe { nil }, 0, type_none)])
binaryen.addfunction(g.mod, c'_start', type_none, type_none, unsafe { nil }, 0,
main_expr)
binaryen.addfunctionexport(g.mod, c'_start', c'_start')
} else {
// In `browser` mode, and function can be exported and called regardless.
// To avoid uninitialised data, `_vinit` is set to be ran immediately on
// WASM module creation.
binaryen.setstart(g.mod, vinit)
}
}