mirror of
https://github.com/vlang/v.git
synced 2025-09-13 22:42:26 +03:00
wasm: implement inline assembly (#19686)
This commit is contained in:
parent
705eea8dcc
commit
c32b04d5be
17 changed files with 1419 additions and 105 deletions
|
@ -4,7 +4,7 @@ module builtin
|
||||||
// Shitty `sbrk` basic `malloc` and `free` impl
|
// Shitty `sbrk` basic `malloc` and `free` impl
|
||||||
// TODO: implement pure V `walloc` later
|
// TODO: implement pure V `walloc` later
|
||||||
|
|
||||||
__global g_heap_base = usize(__heap_base())
|
__global g_heap_base = usize(vwasm_heap_base())
|
||||||
|
|
||||||
// malloc dynamically allocates a `n` bytes block of memory on the heap.
|
// malloc dynamically allocates a `n` bytes block of memory on the heap.
|
||||||
// malloc returns a `byteptr` pointing to the memory address of the allocated space.
|
// malloc returns a `byteptr` pointing to the memory address of the allocated space.
|
||||||
|
|
|
@ -1,24 +1,41 @@
|
||||||
module builtin
|
module builtin
|
||||||
|
|
||||||
fn __heap_base() voidptr
|
// vwasm_heap_base returns the base address of the heap.
|
||||||
fn __memory_size() usize
|
pub fn vwasm_heap_base() voidptr {
|
||||||
fn __memory_grow(size usize) usize
|
mut rval := unsafe { nil }
|
||||||
fn __memory_fill(dest &u8, value isize, size isize)
|
asm wasm {
|
||||||
fn __memory_copy(dest &u8, src &u8, size isize)
|
global.get __heap_base
|
||||||
|
local.set rval
|
||||||
|
; =r (rval)
|
||||||
|
}
|
||||||
|
return rval
|
||||||
|
}
|
||||||
|
|
||||||
// add doc comments for the below functions
|
// vwasm_heap_size returns the size of the main wasm memory in pages.
|
||||||
|
// Analagous to the `memory.size` instruction.
|
||||||
|
pub fn vwasm_memory_size() int {
|
||||||
|
mut rval := 0
|
||||||
|
asm wasm {
|
||||||
|
memory.size
|
||||||
|
local.set rval
|
||||||
|
; =r (rval)
|
||||||
|
}
|
||||||
|
return rval
|
||||||
|
}
|
||||||
|
|
||||||
// __reinterpret_f32_u32 converts a `u32` to a `f32` without changing the bit pattern.
|
// vwasm_memory_grow grows the main wasm memory by `size` pages.
|
||||||
pub fn __reinterpret_f32_u32(v f32) u32
|
// Analagous to the `memory.grow` instruction.
|
||||||
|
pub fn vwasm_memory_grow(size int) int {
|
||||||
// __reinterpret_u32_f32 converts a `f32` to a `u32` without changing the bit pattern.
|
mut rval := 0
|
||||||
pub fn __reinterpret_u32_f32(v u32) f32
|
asm wasm {
|
||||||
|
local.get size
|
||||||
// __reinterpret_f64_u64 converts a `u64` to a `f64` without changing the bit pattern.
|
memory.grow
|
||||||
pub fn __reinterpret_f64_u64(v f64) u64
|
local.set rval
|
||||||
|
; =r (rval)
|
||||||
// __reinterpret_u64_f64 converts a `f64` to a `u64` without changing the bit pattern.
|
; r (size)
|
||||||
pub fn __reinterpret_u64_f64(v u64) f64
|
}
|
||||||
|
return rval
|
||||||
|
}
|
||||||
|
|
||||||
// vcalloc dynamically allocates a zeroed `n` bytes block of memory on the heap.
|
// vcalloc dynamically allocates a zeroed `n` bytes block of memory on the heap.
|
||||||
// vcalloc returns a `byteptr` pointing to the memory address of the allocated space.
|
// vcalloc returns a `byteptr` pointing to the memory address of the allocated space.
|
||||||
|
@ -33,7 +50,14 @@ pub fn vcalloc(n isize) &u8 {
|
||||||
|
|
||||||
res := unsafe { malloc(n) }
|
res := unsafe { malloc(n) }
|
||||||
|
|
||||||
__memory_fill(res, 0, n)
|
asm wasm {
|
||||||
|
local.get res
|
||||||
|
i32.const 0x0
|
||||||
|
local.get n
|
||||||
|
memory.fill
|
||||||
|
; ; r (res)
|
||||||
|
r (n)
|
||||||
|
}
|
||||||
|
|
||||||
return res
|
return res
|
||||||
}
|
}
|
||||||
|
@ -48,7 +72,15 @@ pub fn isnil(v voidptr) bool {
|
||||||
// The memory areas **CAN** overlap. vmemcpy returns a pointer to `dest`.
|
// The memory areas **CAN** overlap. vmemcpy returns a pointer to `dest`.
|
||||||
[unsafe]
|
[unsafe]
|
||||||
pub fn vmemcpy(dest voidptr, const_src voidptr, n isize) voidptr {
|
pub fn vmemcpy(dest voidptr, const_src voidptr, n isize) voidptr {
|
||||||
__memory_copy(dest, const_src, n)
|
asm wasm {
|
||||||
|
local.get dest
|
||||||
|
local.get const_src
|
||||||
|
local.get n
|
||||||
|
memory.copy
|
||||||
|
; ; r (dest)
|
||||||
|
r (const_src)
|
||||||
|
r (n)
|
||||||
|
}
|
||||||
return dest
|
return dest
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -56,7 +88,15 @@ pub fn vmemcpy(dest voidptr, const_src voidptr, n isize) voidptr {
|
||||||
// The memory areas **CAN** overlap. vmemmove returns a pointer to `dest`.
|
// The memory areas **CAN** overlap. vmemmove returns a pointer to `dest`.
|
||||||
[unsafe]
|
[unsafe]
|
||||||
pub fn vmemmove(dest voidptr, const_src voidptr, n isize) voidptr {
|
pub fn vmemmove(dest voidptr, const_src voidptr, n isize) voidptr {
|
||||||
__memory_copy(dest, const_src, n)
|
asm wasm {
|
||||||
|
local.get dest
|
||||||
|
local.get const_src
|
||||||
|
local.get n
|
||||||
|
memory.copy
|
||||||
|
; ; r (dest)
|
||||||
|
r (const_src)
|
||||||
|
r (n)
|
||||||
|
}
|
||||||
return dest
|
return dest
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -64,6 +104,14 @@ pub fn vmemmove(dest voidptr, const_src voidptr, n isize) voidptr {
|
||||||
// with the constant byte `c`. It returns a pointer to the memory area `s`.
|
// with the constant byte `c`. It returns a pointer to the memory area `s`.
|
||||||
[unsafe]
|
[unsafe]
|
||||||
pub fn vmemset(s voidptr, c int, n isize) voidptr {
|
pub fn vmemset(s voidptr, c int, n isize) voidptr {
|
||||||
__memory_fill(s, c, n)
|
asm wasm {
|
||||||
|
local.get s
|
||||||
|
local.get c
|
||||||
|
local.get n
|
||||||
|
memory.fill
|
||||||
|
; ; r (s)
|
||||||
|
r (c)
|
||||||
|
r (n)
|
||||||
|
}
|
||||||
return s
|
return s
|
||||||
}
|
}
|
||||||
|
|
|
@ -2411,6 +2411,9 @@ pub fn all_registers(mut t Table, arch pref.Arch) map[string]ScopeObject {
|
||||||
res[k] = v
|
res[k] = v
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
.wasm32 {
|
||||||
|
// no registers
|
||||||
|
}
|
||||||
else { // TODO
|
else { // TODO
|
||||||
panic('all_registers: unhandled arch')
|
panic('all_registers: unhandled arch')
|
||||||
}
|
}
|
||||||
|
|
|
@ -12,7 +12,12 @@ fn (mut f Fmt) asm_stmt(stmt ast.AsmStmt) {
|
||||||
} else if stmt.is_goto {
|
} else if stmt.is_goto {
|
||||||
f.write('goto ')
|
f.write('goto ')
|
||||||
}
|
}
|
||||||
f.writeln('${stmt.arch} {')
|
lit_arch := if stmt.arch == .wasm32 {
|
||||||
|
'wasm'
|
||||||
|
} else {
|
||||||
|
stmt.arch.str()
|
||||||
|
}
|
||||||
|
f.writeln('${lit_arch} {')
|
||||||
f.indent++
|
f.indent++
|
||||||
|
|
||||||
f.asm_templates(stmt.templates)
|
f.asm_templates(stmt.templates)
|
||||||
|
@ -54,7 +59,7 @@ fn (mut f Fmt) asm_arg(arg ast.AsmArg) {
|
||||||
f.write(arg.val.str())
|
f.write(arg.val.str())
|
||||||
}
|
}
|
||||||
string {
|
string {
|
||||||
f.write(arg)
|
f.string_literal(ast.StringLiteral{ val: arg })
|
||||||
}
|
}
|
||||||
ast.AsmAddressing {
|
ast.AsmAddressing {
|
||||||
if arg.segment != '' {
|
if arg.segment != '' {
|
||||||
|
|
889
vlib/v/gen/wasm/asm.v
Normal file
889
vlib/v/gen/wasm/asm.v
Normal file
|
@ -0,0 +1,889 @@
|
||||||
|
// 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)
|
||||||
|
}
|
||||||
|
}
|
132
vlib/v/gen/wasm/comptime.v
Normal file
132
vlib/v/gen/wasm/comptime.v
Normal file
|
@ -0,0 +1,132 @@
|
||||||
|
// 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) comptime_cond(cond ast.Expr, pkg_exists bool) bool {
|
||||||
|
match cond {
|
||||||
|
ast.BoolLiteral {
|
||||||
|
return cond.val
|
||||||
|
}
|
||||||
|
ast.ParExpr {
|
||||||
|
g.comptime_cond(cond.expr, pkg_exists)
|
||||||
|
}
|
||||||
|
ast.PrefixExpr {
|
||||||
|
if cond.op == .not {
|
||||||
|
return !g.comptime_cond(cond.right, pkg_exists)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
ast.InfixExpr {
|
||||||
|
match cond.op {
|
||||||
|
.and {
|
||||||
|
return g.comptime_cond(cond.left, pkg_exists)
|
||||||
|
&& g.comptime_cond(cond.right, pkg_exists)
|
||||||
|
}
|
||||||
|
.logical_or {
|
||||||
|
return g.comptime_cond(cond.left, pkg_exists)
|
||||||
|
|| g.comptime_cond(cond.right, pkg_exists)
|
||||||
|
}
|
||||||
|
.eq {
|
||||||
|
return g.comptime_cond(cond.left, pkg_exists) == g.comptime_cond(cond.right,
|
||||||
|
pkg_exists)
|
||||||
|
}
|
||||||
|
.ne {
|
||||||
|
return g.comptime_cond(cond.left, pkg_exists) != g.comptime_cond(cond.right,
|
||||||
|
pkg_exists)
|
||||||
|
}
|
||||||
|
// wasm doesn't support generics
|
||||||
|
// .key_is, .not_is
|
||||||
|
else {}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
ast.Ident {
|
||||||
|
return g.comptime_if_to_ifdef(cond.name, false)
|
||||||
|
}
|
||||||
|
ast.ComptimeCall {
|
||||||
|
return pkg_exists // more documentation needed here...
|
||||||
|
}
|
||||||
|
else {}
|
||||||
|
}
|
||||||
|
g.w_error('wasm.comptime_cond(): unhandled node: ' + cond.type_name())
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn (mut g Gen) comptime_if_expr(node ast.IfExpr, expected ast.Type, existing_rvars []Var) {
|
||||||
|
if !node.is_expr && !node.has_else && node.branches.len == 1 {
|
||||||
|
if node.branches[0].stmts.len == 0 {
|
||||||
|
// empty ifdef; result of target OS != conditional => skip
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for i, branch in node.branches {
|
||||||
|
has_expr := !(node.has_else && i + 1 >= node.branches.len)
|
||||||
|
|
||||||
|
if has_expr && !g.comptime_cond(branch.cond, branch.pkg_exist) {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
// !node.is_expr || cond
|
||||||
|
// handles else case, and if cond is true
|
||||||
|
g.rvar_expr_stmts(branch.stmts, expected, existing_rvars)
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn (mut g Gen) comptime_if_to_ifdef(name string, is_comptime_option bool) bool {
|
||||||
|
match name {
|
||||||
|
// platforms/os-es/compilers:
|
||||||
|
'windows', 'ios', 'macos', 'mach', 'darwin', 'linux', 'freebsd', 'openbsd', 'bsd',
|
||||||
|
'android', 'solaris', 'js_node', 'js_freestanding', 'js_browser', 'es5', 'js', 'native',
|
||||||
|
'glibc', 'gcc', 'tinyc', 'clang', 'mingw', 'msvc', 'cplusplus', 'gcboehm', 'prealloc',
|
||||||
|
'freestanding', 'amd64', 'aarch64', 'arm64' {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
'wasm' {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
//
|
||||||
|
'debug' {
|
||||||
|
return g.pref.is_debug
|
||||||
|
}
|
||||||
|
'prod' {
|
||||||
|
return g.pref.is_prod
|
||||||
|
}
|
||||||
|
// wasm doesn't support testing
|
||||||
|
'test' {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
// wasm doesn't support threads
|
||||||
|
'threads' {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
'no_bounds_checking' {
|
||||||
|
return g.pref.no_bounds_checking
|
||||||
|
}
|
||||||
|
// bitness:
|
||||||
|
'x64' {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
'x32' {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
// endianness:
|
||||||
|
'little_endian' {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
'big_endian' {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
// taken from JS: what does it do??
|
||||||
|
/*
|
||||||
|
if is_comptime_option
|
||||||
|
|| (g.pref.compile_defines_all.len > 0 && name in g.pref.compile_defines_all) {
|
||||||
|
return 'checkDefine("CUSTOM_DEFINE_${name}")'
|
||||||
|
}
|
||||||
|
return error('bad os ifdef name "${name}"')
|
||||||
|
*/
|
||||||
|
}
|
||||||
|
}
|
||||||
|
g.w_error('wasm.comptime_if_to_ifdef(): unhandled `${name}`, is_comptime_option: ${is_comptime_option}')
|
||||||
|
}
|
|
@ -62,7 +62,11 @@ pub struct LoopBreakpoint {
|
||||||
name string
|
name string
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[noreturn]
|
||||||
pub fn (mut g Gen) v_error(s string, pos token.Pos) {
|
pub fn (mut g Gen) v_error(s string, pos token.Pos) {
|
||||||
|
util.show_compiler_message('error:', pos: pos, file_path: g.file_path, message: s)
|
||||||
|
exit(1)
|
||||||
|
/*
|
||||||
if g.pref.output_mode == .stdout {
|
if g.pref.output_mode == .stdout {
|
||||||
util.show_compiler_message('error:', pos: pos, file_path: g.file_path, message: s)
|
util.show_compiler_message('error:', pos: pos, file_path: g.file_path, message: s)
|
||||||
exit(1)
|
exit(1)
|
||||||
|
@ -74,6 +78,7 @@ pub fn (mut g Gen) v_error(s string, pos token.Pos) {
|
||||||
message: s
|
message: s
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
*/
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn (mut g Gen) warning(s string, pos token.Pos) {
|
pub fn (mut g Gen) warning(s string, pos token.Pos) {
|
||||||
|
@ -444,50 +449,6 @@ pub fn (mut g Gen) infix_expr(node ast.InfixExpr, expected ast.Type) {
|
||||||
g.func.cast(g.as_numtype(g.get_wasm_type(res_typ)), res_typ.is_signed(), g.as_numtype(g.get_wasm_type(expected)))
|
g.func.cast(g.as_numtype(g.get_wasm_type(res_typ)), res_typ.is_signed(), g.as_numtype(g.get_wasm_type(expected)))
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn (mut g Gen) wasm_builtin(name string, node ast.CallExpr) {
|
|
||||||
for idx, arg in node.args {
|
|
||||||
g.expr(arg.expr, node.expected_arg_types[idx])
|
|
||||||
}
|
|
||||||
|
|
||||||
match name {
|
|
||||||
'__memory_grow' {
|
|
||||||
g.func.memory_grow()
|
|
||||||
}
|
|
||||||
'__memory_fill' {
|
|
||||||
g.func.memory_fill()
|
|
||||||
}
|
|
||||||
'__memory_copy' {
|
|
||||||
g.func.memory_copy()
|
|
||||||
}
|
|
||||||
'__memory_size' {
|
|
||||||
g.func.memory_size()
|
|
||||||
}
|
|
||||||
'__heap_base' {
|
|
||||||
if hp := g.heap_base {
|
|
||||||
g.func.global_get(hp)
|
|
||||||
}
|
|
||||||
hp := g.mod.new_global('__heap_base', false, .i32_t, false, wasm.constexpr_value(0))
|
|
||||||
g.func.global_get(hp)
|
|
||||||
g.heap_base = hp
|
|
||||||
}
|
|
||||||
'__reinterpret_f32_u32' {
|
|
||||||
g.func.reinterpret(.f32_t)
|
|
||||||
}
|
|
||||||
'__reinterpret_u32_f32' {
|
|
||||||
g.func.reinterpret(.i32_t)
|
|
||||||
}
|
|
||||||
'__reinterpret_f64_u64' {
|
|
||||||
g.func.reinterpret(.f64_t)
|
|
||||||
}
|
|
||||||
'__reinterpret_u64_f64' {
|
|
||||||
g.func.reinterpret(.i64_t)
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
panic('unreachable')
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn (mut g Gen) prefix_expr(node ast.PrefixExpr, expected ast.Type) {
|
pub fn (mut g Gen) prefix_expr(node ast.PrefixExpr, expected ast.Type) {
|
||||||
match node.op {
|
match node.op {
|
||||||
.minus {
|
.minus {
|
||||||
|
@ -571,6 +532,11 @@ pub fn (mut g Gen) if_branch(ifexpr ast.IfExpr, expected ast.Type, unpacked_para
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn (mut g Gen) if_expr(ifexpr ast.IfExpr, expected ast.Type, existing_rvars []Var) {
|
pub fn (mut g Gen) if_expr(ifexpr ast.IfExpr, expected ast.Type, existing_rvars []Var) {
|
||||||
|
if ifexpr.is_comptime {
|
||||||
|
g.comptime_if_expr(ifexpr, expected, existing_rvars)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
params := if expected == ast.void_type {
|
params := if expected == ast.void_type {
|
||||||
[]wasm.ValType{}
|
[]wasm.ValType{}
|
||||||
} else if existing_rvars.len == 0 {
|
} else if existing_rvars.len == 0 {
|
||||||
|
@ -587,13 +553,6 @@ pub fn (mut g Gen) call_expr(node ast.CallExpr, expected ast.Type, existing_rvar
|
||||||
|
|
||||||
is_print := name in ['panic', 'println', 'print', 'eprintln', 'eprint']
|
is_print := name in ['panic', 'println', 'print', 'eprintln', 'eprint']
|
||||||
|
|
||||||
if name in ['__memory_grow', '__memory_fill', '__memory_copy', '__memory_size', '__heap_base',
|
|
||||||
'__reinterpret_f32_u32', '__reinterpret_u32_f32', '__reinterpret_f64_u64',
|
|
||||||
'__reinterpret_u64_f64'] {
|
|
||||||
g.wasm_builtin(node.name, node)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
if node.is_method {
|
if node.is_method {
|
||||||
name = '${g.table.get_type_name(node.receiver_type)}.${node.name}'
|
name = '${g.table.get_type_name(node.receiver_type)}.${node.name}'
|
||||||
}
|
}
|
||||||
|
@ -1247,6 +1206,10 @@ pub fn (mut g Gen) expr_stmt(node ast.Stmt, expected ast.Type) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
ast.AsmStmt {
|
||||||
|
// assumed expected == void
|
||||||
|
g.asm_stmt(node)
|
||||||
|
}
|
||||||
else {
|
else {
|
||||||
g.w_error('wasm.expr_stmt(): unhandled node: ' + node.type_name())
|
g.w_error('wasm.expr_stmt(): unhandled node: ' + node.type_name())
|
||||||
}
|
}
|
||||||
|
|
|
@ -128,6 +128,15 @@ pub fn (mut g Gen) sp() wasm.GlobalIndex {
|
||||||
return g.sp()
|
return g.sp()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn (mut g Gen) hp() wasm.GlobalIndex {
|
||||||
|
if hp := g.heap_base {
|
||||||
|
return hp
|
||||||
|
}
|
||||||
|
hp := g.mod.new_global('__heap_base', false, .i32_t, false, wasm.constexpr_value(0))
|
||||||
|
g.heap_base = hp
|
||||||
|
return hp
|
||||||
|
}
|
||||||
|
|
||||||
pub fn (mut g Gen) new_local(name string, typ_ ast.Type) Var {
|
pub fn (mut g Gen) new_local(name string, typ_ ast.Type) Var {
|
||||||
mut typ := typ_
|
mut typ := typ_
|
||||||
ts := g.table.sym(typ)
|
ts := g.table.sym(typ)
|
||||||
|
@ -210,7 +219,7 @@ pub fn (mut g Gen) literal_to_constant_expression(typ_ ast.Type, init ast.Expr)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn (mut g Gen) new_global(name string, typ_ ast.Type, init ast.Expr, is_global_mut bool) Global {
|
pub fn (mut g Gen) new_global(name string, typ_ ast.Type, init ast.Expr, is_global_mut bool) Global {
|
||||||
mut typ := typ_
|
mut typ := ast.mktyp(typ_)
|
||||||
ts := g.table.sym(typ)
|
ts := g.table.sym(typ)
|
||||||
|
|
||||||
match ts.info {
|
match ts.info {
|
||||||
|
@ -461,6 +470,36 @@ pub fn (mut g Gen) set(v Var) {
|
||||||
g.mov(v, from)
|
g.mov(v, from)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// to satisfy inline assembly needs
|
||||||
|
// never used by actual codegen
|
||||||
|
pub fn (mut g Gen) tee(v Var) {
|
||||||
|
assert !v.is_global
|
||||||
|
|
||||||
|
if !v.is_address {
|
||||||
|
g.func.local_tee(v.idx)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if g.is_pure_type(v.typ) {
|
||||||
|
l := g.new_local('__tmp', v.typ)
|
||||||
|
g.func.local_tee(l.idx) // tee here, leave on stack
|
||||||
|
|
||||||
|
g.func.local_get(v.idx)
|
||||||
|
g.func.local_get(l.idx)
|
||||||
|
g.store(v.typ, v.offset)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
from := Var{
|
||||||
|
typ: v.typ
|
||||||
|
idx: g.func.new_local_named(.i32_t, '__tmp<voidptr>')
|
||||||
|
is_address: v.is_address
|
||||||
|
}
|
||||||
|
|
||||||
|
g.func.local_tee(from.idx) // tee here, leave on stack
|
||||||
|
g.mov(v, from)
|
||||||
|
}
|
||||||
|
|
||||||
pub fn (mut g Gen) ref(v Var) {
|
pub fn (mut g Gen) ref(v Var) {
|
||||||
g.ref_ignore_offset(v)
|
g.ref_ignore_offset(v)
|
||||||
|
|
||||||
|
|
150
vlib/v/gen/wasm/tests/asm.vv
Normal file
150
vlib/v/gen/wasm/tests/asm.vv
Normal file
|
@ -0,0 +1,150 @@
|
||||||
|
fn add_int(init f64, val int) f64 {
|
||||||
|
mut ret := 0.0
|
||||||
|
asm wasm {
|
||||||
|
local.get init
|
||||||
|
local.get val
|
||||||
|
f64.convert_i32_s
|
||||||
|
f64.add
|
||||||
|
local.set ret
|
||||||
|
; =r (ret)
|
||||||
|
; r (init)
|
||||||
|
r (val)
|
||||||
|
}
|
||||||
|
return ret
|
||||||
|
}
|
||||||
|
|
||||||
|
fn add_f64(init int, val f64) int {
|
||||||
|
mut ret := 0
|
||||||
|
asm wasm {
|
||||||
|
local.get init
|
||||||
|
local.get val
|
||||||
|
i32.trunc_sat_f64_s
|
||||||
|
i32.add
|
||||||
|
local.set ret
|
||||||
|
; =r (ret)
|
||||||
|
; r (init)
|
||||||
|
r (val)
|
||||||
|
}
|
||||||
|
return ret
|
||||||
|
}
|
||||||
|
|
||||||
|
fn memory_size() int {
|
||||||
|
mut ret := 0
|
||||||
|
asm wasm {
|
||||||
|
memory.size
|
||||||
|
local.set ret
|
||||||
|
; =r (ret)
|
||||||
|
}
|
||||||
|
return ret
|
||||||
|
}
|
||||||
|
|
||||||
|
fn memset() {
|
||||||
|
mut memory := [16]u8{}
|
||||||
|
|
||||||
|
asm wasm {
|
||||||
|
local.get memory
|
||||||
|
i32.const 0x33
|
||||||
|
i32.const 16
|
||||||
|
memory.fill
|
||||||
|
; =r (memory)
|
||||||
|
}
|
||||||
|
|
||||||
|
for i := 0; i < 16; i++ {
|
||||||
|
print(memory[i])
|
||||||
|
if i + 1 < 16 {
|
||||||
|
print(' ')
|
||||||
|
}
|
||||||
|
}
|
||||||
|
println('')
|
||||||
|
}
|
||||||
|
|
||||||
|
fn literals() (int, i64, f32, f64) {
|
||||||
|
mut l1 := 0
|
||||||
|
mut l2 := i64(0)
|
||||||
|
mut l3 := f32(0.0)
|
||||||
|
mut l4 := 0.0
|
||||||
|
|
||||||
|
asm wasm {
|
||||||
|
i32.const 999
|
||||||
|
i64.const 999
|
||||||
|
f32.const 999
|
||||||
|
f64.const 999
|
||||||
|
local.set l4
|
||||||
|
local.set l3
|
||||||
|
local.set l2
|
||||||
|
local.set l1
|
||||||
|
; =r (l1)
|
||||||
|
=r (l2)
|
||||||
|
=r (l3)
|
||||||
|
=r (l4)
|
||||||
|
}
|
||||||
|
|
||||||
|
return l1, l2, l3, l4
|
||||||
|
}
|
||||||
|
|
||||||
|
fn reinterpret_asm() string {
|
||||||
|
mut reinterp := 0
|
||||||
|
|
||||||
|
// reinterpret int value into f32, convert back
|
||||||
|
asm wasm {
|
||||||
|
i32.const 0x44424000 // 777.0
|
||||||
|
f32.reinterpret_i32
|
||||||
|
i32.trunc_sat_f32_s
|
||||||
|
local.set reinterp
|
||||||
|
; =r (reinterp)
|
||||||
|
}
|
||||||
|
|
||||||
|
mut lit := ''
|
||||||
|
|
||||||
|
// call "int.str" to convert int to string
|
||||||
|
asm wasm {
|
||||||
|
local.get lit
|
||||||
|
local.get reinterp
|
||||||
|
call 'int.str'
|
||||||
|
; =r (lit)
|
||||||
|
; r (reinterp)
|
||||||
|
}
|
||||||
|
|
||||||
|
return lit
|
||||||
|
}
|
||||||
|
|
||||||
|
fn malloc_ptr() {
|
||||||
|
mut v := unsafe { &int(malloc(sizeof(int))) }
|
||||||
|
|
||||||
|
asm wasm {
|
||||||
|
local.get v
|
||||||
|
i32.const 42
|
||||||
|
i32.store 2, 0 // log2(sizeof(int)) == 2
|
||||||
|
; =r (v)
|
||||||
|
}
|
||||||
|
|
||||||
|
println(*v)
|
||||||
|
|
||||||
|
mut readp := 0
|
||||||
|
asm wasm {
|
||||||
|
local.get v
|
||||||
|
i32.load 2, 0 // log2(sizeof(int)) == 2
|
||||||
|
local.set readp
|
||||||
|
; =r (readp)
|
||||||
|
; r (v)
|
||||||
|
}
|
||||||
|
|
||||||
|
println(readp)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn main() {
|
||||||
|
println(int(add_int(1.0, 2))) // no f64.str()
|
||||||
|
println(add_f64(1, 2.0))
|
||||||
|
println(memory_size())
|
||||||
|
memset()
|
||||||
|
|
||||||
|
a, b, c, d := literals()
|
||||||
|
println(a)
|
||||||
|
println(b)
|
||||||
|
println(int(c)) // no f32.str()
|
||||||
|
println(int(d)) // no f64.str()
|
||||||
|
|
||||||
|
seven_seven_seven := reinterpret_asm()
|
||||||
|
println(seven_seven_seven)
|
||||||
|
malloc_ptr()
|
||||||
|
}
|
11
vlib/v/gen/wasm/tests/asm.vv.out
Normal file
11
vlib/v/gen/wasm/tests/asm.vv.out
Normal file
|
@ -0,0 +1,11 @@
|
||||||
|
3
|
||||||
|
3
|
||||||
|
1
|
||||||
|
51 51 51 51 51 51 51 51 51 51 51 51 51 51 51 51
|
||||||
|
999
|
||||||
|
999
|
||||||
|
999
|
||||||
|
999
|
||||||
|
777
|
||||||
|
42
|
||||||
|
42
|
|
@ -30,4 +30,8 @@ fn main() {
|
||||||
assertions()
|
assertions()
|
||||||
|
|
||||||
// panic('nooo!')
|
// panic('nooo!')
|
||||||
|
|
||||||
|
println('wasm builtins')
|
||||||
|
println(vwasm_memory_size())
|
||||||
|
println(vwasm_memory_grow(0))
|
||||||
}
|
}
|
||||||
|
|
|
@ -4,3 +4,6 @@ false
|
||||||
false
|
false
|
||||||
true
|
true
|
||||||
110
|
110
|
||||||
|
wasm builtins
|
||||||
|
1
|
||||||
|
1
|
13
vlib/v/gen/wasm/tests/comptime.vv
Normal file
13
vlib/v/gen/wasm/tests/comptime.vv
Normal file
|
@ -0,0 +1,13 @@
|
||||||
|
fn main() {
|
||||||
|
$if wasm {
|
||||||
|
println('wasm')
|
||||||
|
} $else {
|
||||||
|
println('not wasm')
|
||||||
|
}
|
||||||
|
|
||||||
|
$if !wasm {
|
||||||
|
println('not wasm')
|
||||||
|
} $else {
|
||||||
|
println('wasm')
|
||||||
|
}
|
||||||
|
}
|
2
vlib/v/gen/wasm/tests/comptime.vv.out
Normal file
2
vlib/v/gen/wasm/tests/comptime.vv.out
Normal file
|
@ -0,0 +1,2 @@
|
||||||
|
wasm
|
||||||
|
wasm
|
|
@ -1162,6 +1162,11 @@ fn (mut p Parser) asm_stmt(is_top_level bool) ast.AsmStmt {
|
||||||
|
|
||||||
p.check(.key_asm)
|
p.check(.key_asm)
|
||||||
mut arch := pref.arch_from_string(p.tok.lit) or { pref.Arch._auto }
|
mut arch := pref.arch_from_string(p.tok.lit) or { pref.Arch._auto }
|
||||||
|
|
||||||
|
if is_top_level && arch == .wasm32 {
|
||||||
|
p.error("wasm doesn't support toplevel assembly")
|
||||||
|
}
|
||||||
|
|
||||||
mut is_volatile := false
|
mut is_volatile := false
|
||||||
mut is_goto := false
|
mut is_goto := false
|
||||||
if p.tok.kind == .key_volatile {
|
if p.tok.kind == .key_volatile {
|
||||||
|
@ -1175,7 +1180,7 @@ fn (mut p Parser) asm_stmt(is_top_level bool) ast.AsmStmt {
|
||||||
}
|
}
|
||||||
if arch == ._auto && !p.pref.is_fmt {
|
if arch == ._auto && !p.pref.is_fmt {
|
||||||
if p.tok.lit == '' {
|
if p.tok.lit == '' {
|
||||||
p.error('missing assembly architecture. Try i386, amd64 or arm64.')
|
p.error('missing assembly architecture. Try i386, amd64, arm64, or wasm.')
|
||||||
}
|
}
|
||||||
p.error('unknown assembly architecture')
|
p.error('unknown assembly architecture')
|
||||||
}
|
}
|
||||||
|
@ -1217,7 +1222,7 @@ fn (mut p Parser) asm_stmt(is_top_level bool) ast.AsmStmt {
|
||||||
if is_directive {
|
if is_directive {
|
||||||
p.next()
|
p.next()
|
||||||
}
|
}
|
||||||
if p.tok.kind in [.key_in, .key_lock, .key_orelse] { // `in`, `lock`, `or` are v keywords that are also x86/arm/riscv instructions.
|
if p.tok.kind in [.key_in, .key_lock, .key_orelse, .key_select, .key_return] { // `in`, `lock`, `or`, `select`, `return` are v keywords that are also x86/arm/riscv/wasm instructions.
|
||||||
name += p.tok.kind.str()
|
name += p.tok.kind.str()
|
||||||
p.next()
|
p.next()
|
||||||
} else if p.tok.kind == .number {
|
} else if p.tok.kind == .number {
|
||||||
|
@ -1227,13 +1232,19 @@ fn (mut p Parser) asm_stmt(is_top_level bool) ast.AsmStmt {
|
||||||
name += p.tok.lit
|
name += p.tok.lit
|
||||||
p.check(.name)
|
p.check(.name)
|
||||||
}
|
}
|
||||||
// dots are part of instructions for some riscv extensions
|
// dots are part of instructions for some riscv extensions and webassembly
|
||||||
if arch in [.rv32, .rv64] {
|
if arch in [.rv32, .rv64, .wasm32] {
|
||||||
for p.tok.kind == .dot {
|
for p.tok.kind == .dot {
|
||||||
name += '.'
|
name += '.'
|
||||||
p.next()
|
p.next()
|
||||||
name += p.tok.lit
|
// wasm: i32.const
|
||||||
p.check(.name)
|
if arch == .wasm32 && p.tok.kind == .key_const {
|
||||||
|
name += 'const'
|
||||||
|
p.next()
|
||||||
|
} else {
|
||||||
|
name += p.tok.lit
|
||||||
|
p.check(.name)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
mut is_label := false
|
mut is_label := false
|
||||||
|
@ -1254,6 +1265,11 @@ fn (mut p Parser) asm_stmt(is_top_level bool) ast.AsmStmt {
|
||||||
.name {
|
.name {
|
||||||
args << p.reg_or_alias()
|
args << p.reg_or_alias()
|
||||||
}
|
}
|
||||||
|
.string {
|
||||||
|
// wasm: call 'wasi_unstable' 'proc_exit'
|
||||||
|
args << p.tok.lit
|
||||||
|
p.next()
|
||||||
|
}
|
||||||
.number {
|
.number {
|
||||||
number_lit := p.parse_number_literal()
|
number_lit := p.parse_number_literal()
|
||||||
match number_lit {
|
match number_lit {
|
||||||
|
@ -1293,6 +1309,9 @@ fn (mut p Parser) asm_stmt(is_top_level bool) ast.AsmStmt {
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
.lsbr {
|
.lsbr {
|
||||||
|
if arch == .wasm32 {
|
||||||
|
p.error("wasm doesn't have addressing operands")
|
||||||
|
}
|
||||||
mut addressing := p.asm_addressing()
|
mut addressing := p.asm_addressing()
|
||||||
addressing.segment = segment
|
addressing.segment = segment
|
||||||
args << addressing
|
args << addressing
|
||||||
|
|
|
@ -835,6 +835,11 @@ pub fn parse_args_and_show_errors(known_external_commands []string, args []strin
|
||||||
if b.is_js() {
|
if b.is_js() {
|
||||||
res.output_cross_c = true
|
res.output_cross_c = true
|
||||||
}
|
}
|
||||||
|
if b == .wasm {
|
||||||
|
res.compile_defines << 'wasm'
|
||||||
|
res.compile_defines_all << 'wasm'
|
||||||
|
res.arch = .wasm32
|
||||||
|
}
|
||||||
res.backend = b
|
res.backend = b
|
||||||
i++
|
i++
|
||||||
}
|
}
|
||||||
|
@ -1096,7 +1101,7 @@ pub fn arch_from_string(arch_str string) !Arch {
|
||||||
'js_freestanding' {
|
'js_freestanding' {
|
||||||
return .js_freestanding
|
return .js_freestanding
|
||||||
}
|
}
|
||||||
'wasm32' {
|
'wasm32', 'wasm' {
|
||||||
return .wasm32
|
return .wasm32
|
||||||
}
|
}
|
||||||
'' {
|
'' {
|
||||||
|
|
|
@ -707,9 +707,9 @@ pub fn (mut func Function) sign_extend16(typ ValType) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// sign_extend32_i64 extends the value of a 32-bit integer of type i64.
|
// sign_extend32_i64 extends the value of a 32-bit integer of type i64.
|
||||||
// WebAssembly instruction: `i64.extend64_s`.
|
// WebAssembly instruction: `i64.extend32_s`.
|
||||||
pub fn (mut func Function) sign_extend32_i64() {
|
pub fn (mut func Function) sign_extend32() {
|
||||||
func.code << 0xC4 // i64.extend64_s
|
func.code << 0xC4 // i64.extend32_s
|
||||||
}
|
}
|
||||||
|
|
||||||
// cast casts a value of type `a` with respect to `is_signed`, to type `b`.
|
// cast casts a value of type `a` with respect to `is_signed`, to type `b`.
|
||||||
|
@ -831,29 +831,57 @@ pub fn (mut func Function) cast(a NumType, is_signed bool, b NumType) {
|
||||||
// - See function `cast` for the rest.
|
// - See function `cast` for the rest.
|
||||||
pub fn (mut func Function) cast_trapping(a NumType, is_signed bool, b NumType) {
|
pub fn (mut func Function) cast_trapping(a NumType, is_signed bool, b NumType) {
|
||||||
if a in [.f32_t, .f64_t] {
|
if a in [.f32_t, .f64_t] {
|
||||||
if a == .f32_t {
|
if is_signed {
|
||||||
match b {
|
if a == .f32_t {
|
||||||
.i32_t {
|
match b {
|
||||||
func.code << 0xA8 // i32.trunc_f32_s
|
.i32_t {
|
||||||
return
|
func.code << 0xA8 // i32.trunc_f32_s
|
||||||
|
return
|
||||||
|
}
|
||||||
|
.i64_t {
|
||||||
|
func.code << 0xAE // i64.trunc_f32_s
|
||||||
|
return
|
||||||
|
}
|
||||||
|
else {}
|
||||||
}
|
}
|
||||||
.i64_t {
|
} else {
|
||||||
func.code << 0xAE // i64.trunc_f32_s
|
match b {
|
||||||
return
|
.i32_t {
|
||||||
|
func.code << 0xAA // i32.trunc_f64_s
|
||||||
|
return
|
||||||
|
}
|
||||||
|
.i64_t {
|
||||||
|
func.code << 0xB0 // i64.trunc_f64_s
|
||||||
|
return
|
||||||
|
}
|
||||||
|
else {}
|
||||||
}
|
}
|
||||||
else {}
|
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
match b {
|
if a == .f32_t {
|
||||||
.i32_t {
|
match b {
|
||||||
func.code << 0xAA // i32.trunc_f64_s
|
.i32_t {
|
||||||
return
|
func.code << 0xA9 // i32.trunc_f32_u
|
||||||
|
return
|
||||||
|
}
|
||||||
|
.i64_t {
|
||||||
|
func.code << 0xAF // i64.trunc_f32_u
|
||||||
|
return
|
||||||
|
}
|
||||||
|
else {}
|
||||||
}
|
}
|
||||||
.i64_t {
|
} else {
|
||||||
func.code << 0xB0 // i64.trunc_f64_s
|
match b {
|
||||||
return
|
.i32_t {
|
||||||
|
func.code << 0xAB // i32.trunc_f64_u
|
||||||
|
return
|
||||||
|
}
|
||||||
|
.i64_t {
|
||||||
|
func.code << 0xB1 // i64.trunc_f64_u
|
||||||
|
return
|
||||||
|
}
|
||||||
|
else {}
|
||||||
}
|
}
|
||||||
else {}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue