v/vlib/v/gen/wasm/asm.v

889 lines
15 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 v.ast
pub fn (mut g Gen) asm_call(node ast.AsmTemplate) {
// call 'main.test'
// call 'main.Struct.+'
//
// call 'wasi_unstable' 'proc_exit'
// call 'console' 'log'
if node.args.len !in [1, 2] {
g.v_error('incorrect number of arguments to `${node.name}`', node.pos)
}
arg0 := node.args[0]
sarg0 := if arg0 is string {
arg0
} else if node.args.len == 1 {
g.v_error('`${node.name}` must accept a string to call', node.pos)
} else {
g.v_error('`${node.name}` must accept a namespace for call', node.pos)
}
if node.args.len == 1 {
g.func.call(sarg0)
return
}
arg1 := node.args[1]
sarg1 := if arg1 is string {
arg1
} else {
g.v_error('`${node.name}` must accept a string for call', node.pos)
}
g.func.call_import(sarg0, sarg1)
}
pub fn (mut g Gen) asm_local_get_set_or_tee(node ast.AsmTemplate, vars AsmVars) {
if node.args.len != 1 {
g.v_error('incorrect number of arguments to `${node.name}`', node.pos)
}
arg0 := node.args[0]
alias := match arg0 {
ast.AsmAlias {
arg0
}
else {
g.v_error('must reference local by identifier', node.pos)
}
}
target_var := if alias.name == '__vbp' {
Var{
idx: g.bp()
}
} else {
var := vars[alias.name] or { g.v_error('unknown identifier', alias.pos) }
var
}
// -- doesn't work, cgen error
// else if var := vars[alias.name] {
// var
// }
if target_var.is_global {
g.v_error('`${alias.name}` is global, cannot use with this instruction', alias.pos)
}
match node.name {
'local.get' {
g.get(target_var)
}
'local.set' {
g.set(target_var)
}
'local.tee' {
g.tee(target_var)
}
else {
panic('unreachable')
}
}
}
pub fn (mut g Gen) asm_global_get_or_set(node ast.AsmTemplate, vars AsmVars) {
if node.args.len != 1 {
g.v_error('incorrect number of arguments to `${node.name}`', node.pos)
}
arg0 := node.args[0]
alias := match arg0 {
ast.AsmAlias {
arg0
}
else {
g.v_error('must reference global by identifier', node.pos)
}
}
target_var := if alias.name == '__vsp' {
Var{
g_idx: g.sp()
is_global: true
}
} else if alias.name == '__heap_base' {
Var{
g_idx: g.hp()
is_global: true
}
} else {
var := vars[alias.name] or { g.v_error('unknown identifier', alias.pos) }
var
}
if !target_var.is_global {
g.v_error('`${alias.name}` is a local, cannot use with this instruction', alias.pos)
}
match node.name {
'global.get' {
g.get(target_var)
}
'global.set' {
g.set(target_var)
}
else {
panic('unreachable')
}
}
}
pub fn (mut g Gen) asm_literal_arg(node ast.AsmTemplate) {
// i32.const
// i64.const
// f32.const
// f64.const
if node.args.len != 1 {
g.v_error('incorrect number of arguments to `${node.name}`', node.pos)
}
is_float := node.name[0] == `f`
arg := node.args[0]
if is_float {
literal := match arg {
ast.FloatLiteral {
arg.val
}
ast.IntegerLiteral {
arg.val
}
else {
g.v_error('must supply float value to `${node.name}`', node.pos)
}
}
match node.name {
'f32.const' {
g.func.f32_const(literal.f32())
}
'f64.const' {
g.func.f64_const(literal.f64())
}
else {
panic('unreachable')
}
}
return
}
literal := match arg {
ast.BoolLiteral {
if arg.val {
'1'
} else {
'0'
}
}
ast.CharLiteral {
u32(arg.val.runes()[0]).str() // there is a better way.
}
ast.IntegerLiteral {
arg.val
}
else {
g.v_error('must supply integer-like value to `${node.name}`', node.pos)
}
}
match node.name {
'i32.const' {
g.func.i32_const(i32(literal.int()))
}
'i64.const' {
g.func.i64_const(literal.i64())
}
else {
panic('unreachable')
}
}
}
pub fn (mut g Gen) asm_parse_align_offset(node ast.AsmTemplate) (int, int) {
if node.args.len != 2 {
g.v_error('incorrect number of arguments to `${node.name}`', node.pos)
}
arg0 := node.args[0]
arg1 := node.args[1]
align := match arg0 {
ast.IntegerLiteral {
arg0.val.int()
}
else {
g.v_error('must supply integer value to align', node.pos)
}
}
offset := match arg1 {
ast.IntegerLiteral {
arg1.val.int()
}
else {
g.v_error('must supply integer value to offset', node.pos)
}
}
return align, offset
}
pub fn (mut g Gen) asm_load_or_store(node ast.AsmTemplate) {
align, offset := g.asm_parse_align_offset(node)
match node.name {
'i32.load' {
g.func.load(.i32_t, align, offset)
}
'i64.load' {
g.func.load(.i64_t, align, offset)
}
'f32.load' {
g.func.load(.f32_t, align, offset)
}
'f64.load' {
g.func.load(.f64_t, align, offset)
}
'i32.store' {
g.func.store(.i32_t, align, offset)
}
'i64.store' {
g.func.store(.i64_t, align, offset)
}
'f32.store' {
g.func.store(.f32_t, align, offset)
}
'f64.store' {
g.func.store(.f64_t, align, offset)
}
'i32.load8_s' {
g.func.load8(.i32_t, true, align, offset)
}
'i64.load8_s' {
g.func.load8(.i64_t, true, align, offset)
}
'i32.load8_u' {
g.func.load8(.i32_t, false, align, offset)
}
'i64.load8_u' {
g.func.load8(.i64_t, false, align, offset)
}
'i32.load16_s' {
g.func.load16(.i32_t, true, align, offset)
}
'i64.load16_s' {
g.func.load16(.i64_t, true, align, offset)
}
'i32.load16_u' {
g.func.load16(.i32_t, false, align, offset)
}
'i64.load16_u' {
g.func.load16(.i64_t, false, align, offset)
}
'i64.load32_s' {
g.func.load32_i64(true, align, offset)
}
'i64.load32_u' {
g.func.load32_i64(false, align, offset)
}
'i32.store8' {
g.func.store8(.i32_t, align, offset)
}
'i64.store8' {
g.func.store8(.i64_t, align, offset)
}
'i32.store16' {
g.func.store16(.i32_t, align, offset)
}
'i64.store16' {
g.func.store16(.i64_t, align, offset)
}
'i64.store32' {
g.func.store32_i64(align, offset)
}
else {
panic('unreachable')
}
}
}
pub fn (mut g Gen) asm_template(parent ast.AsmStmt, node ast.AsmTemplate, vars AsmVars) {
if node.is_label || node.is_directive {
g.v_error("`asm wasm` doesn't support labels or directives", node.pos)
}
match node.name {
'unreachable' {
g.func.unreachable()
}
'nop' {
g.func.nop()
}
'drop' {
g.func.drop()
}
'return' {
g.func.c_return()
}
'select' {
g.func.c_select()
}
'i32.const' {
g.asm_literal_arg(node)
}
'i64.const' {
g.asm_literal_arg(node)
}
'f32.const' {
g.asm_literal_arg(node)
}
'f64.const' {
g.asm_literal_arg(node)
}
'local.get' {
g.asm_local_get_set_or_tee(node, vars)
}
'local.set' {
g.asm_local_get_set_or_tee(node, vars)
}
'local.tee' {
g.asm_local_get_set_or_tee(node, vars)
}
'global.get' {
g.asm_global_get_or_set(node, vars)
}
'global.set' {
g.asm_global_get_or_set(node, vars)
}
'i32.load' {
g.asm_load_or_store(node)
}
'i64.load' {
g.asm_load_or_store(node)
}
'f32.load' {
g.asm_load_or_store(node)
}
'f64.load' {
g.asm_load_or_store(node)
}
'i32.store' {
g.asm_load_or_store(node)
}
'i64.store' {
g.asm_load_or_store(node)
}
'f32.store' {
g.asm_load_or_store(node)
}
'f64.store' {
g.asm_load_or_store(node)
}
'i32.load8_s' {
g.asm_load_or_store(node)
}
'i64.load8_s' {
g.asm_load_or_store(node)
}
'i32.load8_u' {
g.asm_load_or_store(node)
}
'i64.load8_u' {
g.asm_load_or_store(node)
}
'i32.load16_s' {
g.asm_load_or_store(node)
}
'i64.load16_s' {
g.asm_load_or_store(node)
}
'i32.load16_u' {
g.asm_load_or_store(node)
}
'i64.load16_u' {
g.asm_load_or_store(node)
}
'i64.load32_s' {
g.asm_load_or_store(node)
}
'i64.load32_u' {
g.asm_load_or_store(node)
}
'i32.store8' {
g.asm_load_or_store(node)
}
'i64.store8' {
g.asm_load_or_store(node)
}
'i32.store16' {
g.asm_load_or_store(node)
}
'i64.store16' {
g.asm_load_or_store(node)
}
'i64.store32' {
g.asm_load_or_store(node)
}
'call' {
g.asm_call(node)
}
'i32.add' {
g.func.add(.i32_t)
}
'i32.sub' {
g.func.sub(.i32_t)
}
'i32.mul' {
g.func.mul(.i32_t)
}
'i32.div_s' {
g.func.div(.i32_t, true)
}
'i32.div_u' {
g.func.div(.i32_t, false)
}
'i32.rem_s' {
g.func.rem(.i32_t, true)
}
'i32.rem_u' {
g.func.rem(.i32_t, false)
}
'i64.add' {
g.func.add(.i64_t)
}
'i64.sub' {
g.func.sub(.i64_t)
}
'i64.mul' {
g.func.mul(.i64_t)
}
'i64.div_s' {
g.func.div(.i64_t, true)
}
'i64.div_u' {
g.func.div(.i64_t, false)
}
'i64.rem_s' {
g.func.rem(.i64_t, true)
}
'i64.rem_u' {
g.func.rem(.i64_t, false)
}
'f32.add' {
g.func.add(.f32_t)
}
'f32.sub' {
g.func.sub(.f32_t)
}
'f32.mul' {
g.func.mul(.f32_t)
}
'f32.div' {
g.func.div(.f32_t, true)
}
'f64.add' {
g.func.add(.f64_t)
}
'f64.sub' {
g.func.sub(.f64_t)
}
'f64.mul' {
g.func.mul(.f64_t)
}
'f64.div' {
g.func.div(.f64_t, true)
}
'i32.eqz' {
g.func.eqz(.i32_t)
}
'i32.eq' {
g.func.eq(.i32_t)
}
'i32.ne' {
g.func.ne(.i32_t)
}
'i32.lt_s' {
g.func.lt(.i32_t, true)
}
'i32.lt_u' {
g.func.lt(.i32_t, false)
}
'i32.gt_s' {
g.func.gt(.i32_t, true)
}
'i32.gt_u' {
g.func.gt(.i32_t, false)
}
'i32.le_s' {
g.func.le(.i32_t, true)
}
'i32.le_u' {
g.func.le(.i32_t, false)
}
'i32.ge_s' {
g.func.ge(.i32_t, true)
}
'i32.ge_u' {
g.func.ge(.i32_t, false)
}
'i64.eqz' {
g.func.eqz(.i64_t)
}
'i64.eq' {
g.func.eq(.i64_t)
}
'i64.ne' {
g.func.ne(.i64_t)
}
'i64.lt_s' {
g.func.lt(.i64_t, true)
}
'i64.lt_u' {
g.func.lt(.i64_t, false)
}
'i64.gt_s' {
g.func.gt(.i64_t, true)
}
'i64.gt_u' {
g.func.gt(.i64_t, false)
}
'i64.le_s' {
g.func.le(.i64_t, true)
}
'i64.le_u' {
g.func.le(.i64_t, false)
}
'i64.ge_s' {
g.func.ge(.i64_t, true)
}
'i64.ge_u' {
g.func.ge(.i64_t, false)
}
'f32.eq' {
g.func.eq(.f32_t)
}
'f32.ne' {
g.func.ne(.f32_t)
}
'f32.lt' {
g.func.lt(.f32_t, true)
}
'f32.gt' {
g.func.gt(.f32_t, true)
}
'f32.le' {
g.func.le(.f32_t, true)
}
'f32.ge' {
g.func.ge(.f32_t, true)
}
'f64.eq' {
g.func.eq(.f64_t)
}
'f64.ne' {
g.func.ne(.f64_t)
}
'f64.lt' {
g.func.lt(.f64_t, true)
}
'f64.gt' {
g.func.gt(.f64_t, true)
}
'f64.le' {
g.func.le(.f64_t, true)
}
'f64.ge' {
g.func.ge(.f64_t, true)
}
'i32.and' {
g.func.b_and(.i32_t)
}
'i32.or' {
g.func.b_or(.i32_t)
}
'i32.xor' {
g.func.b_xor(.i32_t)
}
'i32.shl' {
g.func.b_shl(.i32_t)
}
'i32.shr_s' {
g.func.b_shr(.i32_t, true)
}
'i32.shr_u' {
g.func.b_shr(.i32_t, true)
}
'i32.rotl' {
g.func.rotl(.i32_t)
}
'i32.rotr' {
g.func.rotr(.i32_t)
}
'i32.clz' {
g.func.clz(.i32_t)
}
'i32.ctz' {
g.func.ctz(.i32_t)
}
'i32.popcnt' {
g.func.popcnt(.i32_t)
}
'i64.and' {
g.func.b_and(.i64_t)
}
'i64.or' {
g.func.b_or(.i64_t)
}
'i64.xor' {
g.func.b_xor(.i64_t)
}
'i64.shl' {
g.func.b_shl(.i64_t)
}
'i64.shr_s' {
g.func.b_shr(.i64_t, true)
}
'i64.shr_u' {
g.func.b_shr(.i64_t, false)
}
'i64.rotl' {
g.func.rotl(.i64_t)
}
'i64.rotr' {
g.func.rotr(.i64_t)
}
'i64.clz' {
g.func.clz(.i64_t)
}
'i64.ctz' {
g.func.ctz(.i64_t)
}
'i64.popcnt' {
g.func.popcnt(.i64_t)
}
'f32.neg' {
g.func.neg(.f32_t)
}
'f32.ceil' {
g.func.ceil(.f32_t)
}
'f32.floor' {
g.func.floor(.f32_t)
}
'f32.trunc' {
g.func.trunc(.f32_t)
}
'f32.nearest' {
g.func.nearest(.f32_t)
}
'f32.sqrt' {
g.func.sqrt(.f32_t)
}
'f32.min' {
g.func.min(.f32_t)
}
'f32.max' {
g.func.max(.f32_t)
}
'f32.copysign' {
g.func.copysign(.f32_t)
}
'f64.abs' {
g.func.abs(.f64_t)
}
'f64.neg' {
g.func.neg(.f64_t)
}
'f64.ceil' {
g.func.ceil(.f64_t)
}
'f64.floor' {
g.func.floor(.f64_t)
}
'f64.trunc' {
g.func.trunc(.f64_t)
}
'f64.nearest' {
g.func.nearest(.f64_t)
}
'f64.sqrt' {
g.func.sqrt(.f64_t)
}
'f64.min' {
g.func.min(.f64_t)
}
'f64.max' {
g.func.max(.f64_t)
}
'f64.copysign' {
g.func.copysign(.f64_t)
}
'i32.wrap_i64' {
g.func.cast(.i64_t, false, .i32_t)
}
'i32.trunc_f32_s' {
g.func.cast_trapping(.f32_t, true, .i32_t)
}
'i32.trunc_f32_u' {
g.func.cast_trapping(.f32_t, false, .i32_t)
}
'i32.trunc_f64_s' {
g.func.cast_trapping(.f64_t, true, .i32_t)
}
'i32.trunc_f64_u' {
g.func.cast_trapping(.f64_t, false, .i32_t)
}
'i64.extend_i32_s' {
g.func.cast(.i32_t, true, .i64_t)
}
'i64.extend_i32_u' {
g.func.cast(.i32_t, false, .i64_t)
}
'i64.trunc_f32_s' {
g.func.cast_trapping(.f32_t, true, .i64_t)
}
'i64.trunc_f32_u' {
g.func.cast_trapping(.f32_t, false, .i64_t)
}
'i64.trunc_f64_s' {
g.func.cast_trapping(.f64_t, true, .i64_t)
}
'i64.trunc_f64_u' {
g.func.cast_trapping(.f64_t, false, .i64_t)
}
'f32.convert_i32_s' {
g.func.cast(.i32_t, true, .f32_t)
}
'f32.convert_i32_u' {
g.func.cast(.i32_t, false, .f32_t)
}
'f32.convert_i64_s' {
g.func.cast(.i64_t, true, .f32_t)
}
'f32.convert_i64_u' {
g.func.cast(.i64_t, false, .f32_t)
}
'f32.demote_f64' {
g.func.cast(.f64_t, true, .f32_t)
}
'f64.convert_i32_s' {
g.func.cast(.i32_t, true, .f64_t)
}
'f64.convert_i32_u' {
g.func.cast(.i32_t, false, .f64_t)
}
'f64.convert_i64_s' {
g.func.cast(.i64_t, true, .f64_t)
}
'f64.convert_i64_u' {
g.func.cast(.i64_t, false, .f64_t)
}
'f64.promote_f32' {
g.func.cast(.f32_t, true, .f64_t)
}
'i32.reinterpret_f32' {
g.func.reinterpret(.f32_t)
}
'i64.reinterpret_f64' {
g.func.reinterpret(.f64_t)
}
'f32.reinterpret_i32' {
g.func.reinterpret(.i32_t)
}
'f64.reinterpret_i64' {
g.func.reinterpret(.i64_t)
}
'i32.extend8_s' {
g.func.sign_extend8(.i32_t)
}
'i32.extend16_s' {
g.func.sign_extend16(.i32_t)
}
'i64.extend8_s' {
g.func.sign_extend8(.i64_t)
}
'i64.extend16_s' {
g.func.sign_extend16(.i64_t)
}
'i64.extend32_s' {
g.func.sign_extend32()
}
'i32.trunc_sat_f32_s' {
g.func.cast(.f32_t, true, .i32_t)
}
'i32.trunc_sat_f32_u' {
g.func.cast(.f32_t, false, .i32_t)
}
'i32.trunc_sat_f64_s' {
g.func.cast(.f64_t, true, .i32_t)
}
'i32.trunc_sat_f64_u' {
g.func.cast(.f64_t, false, .i32_t)
}
'i64.trunc_sat_f32_s' {
g.func.cast(.f32_t, true, .i64_t)
}
'i64.trunc_sat_f32_u' {
g.func.cast(.f32_t, false, .i64_t)
}
'i64.trunc_sat_f64_s' {
g.func.cast(.f64_t, true, .i64_t)
}
'i64.trunc_sat_f64_u' {
g.func.cast(.f64_t, false, .i64_t)
}
'memory.size' {
g.func.memory_size()
}
'memory.grow' {
g.func.memory_grow()
}
'memory.copy' {
g.func.memory_copy()
}
'memory.fill' {
g.func.memory_fill()
}
// TODO: impl later
/*
'ref.null' {
g.func.ref_null()
}
*/
else {
g.v_error('unknown opcode', node.pos)
}
}
}
type AsmVars = map[string]Var
pub fn (mut g Gen) asm_stmt(node ast.AsmStmt) {
mut vars := AsmVars(map[string]Var{})
for var_expr in node.output {
vars[var_expr.alias] = g.get_var_or_make_from_expr(var_expr.expr, var_expr.typ)
}
for var_expr in node.input {
vars[var_expr.alias] = g.get_var_or_make_from_expr(var_expr.expr, var_expr.typ)
}
if node.clobbered.len != 0 {
g.v_error('wasm does not support clobber lists', node.pos)
}
if node.global_labels.len != 0 || node.local_labels.len != 0 {
g.v_error('wasm does not support labels', node.pos)
}
for tmpl in node.templates {
g.asm_template(node, tmpl, vars)
}
}