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