Added more robust and easy way to generate WASM
This commit is contained in:
parent
e03f038cf9
commit
fea817ca00
@ -6,6 +6,9 @@ from typing import Generator
|
|||||||
from . import ourlang
|
from . import ourlang
|
||||||
from . import typing
|
from . import typing
|
||||||
from . import wasm
|
from . import wasm
|
||||||
|
from . import wasmeasy
|
||||||
|
|
||||||
|
from .wasmeasy import i32, i64
|
||||||
|
|
||||||
Statements = Generator[wasm.Statement, None, None]
|
Statements = Generator[wasm.Statement, None, None]
|
||||||
|
|
||||||
@ -107,23 +110,23 @@ def expression(inp: ourlang.Expression) -> Statements:
|
|||||||
Compile: Any expression
|
Compile: Any expression
|
||||||
"""
|
"""
|
||||||
if isinstance(inp, ourlang.ConstantUInt8):
|
if isinstance(inp, ourlang.ConstantUInt8):
|
||||||
yield wasm.Statement('i32.const', str(inp.value))
|
yield i32.const(inp.value)
|
||||||
return
|
return
|
||||||
|
|
||||||
if isinstance(inp, ourlang.ConstantUInt32):
|
if isinstance(inp, ourlang.ConstantUInt32):
|
||||||
yield wasm.Statement('i32.const', str(inp.value))
|
yield i32.const(inp.value)
|
||||||
return
|
return
|
||||||
|
|
||||||
if isinstance(inp, ourlang.ConstantUInt64):
|
if isinstance(inp, ourlang.ConstantUInt64):
|
||||||
yield wasm.Statement('i64.const', str(inp.value))
|
yield i64.const(inp.value)
|
||||||
return
|
return
|
||||||
|
|
||||||
if isinstance(inp, ourlang.ConstantInt32):
|
if isinstance(inp, ourlang.ConstantInt32):
|
||||||
yield wasm.Statement('i32.const', str(inp.value))
|
yield i32.const(inp.value)
|
||||||
return
|
return
|
||||||
|
|
||||||
if isinstance(inp, ourlang.ConstantInt64):
|
if isinstance(inp, ourlang.ConstantInt64):
|
||||||
yield wasm.Statement('i64.const', str(inp.value))
|
yield i64.const(inp.value)
|
||||||
return
|
return
|
||||||
|
|
||||||
if isinstance(inp, ourlang.ConstantFloat32):
|
if isinstance(inp, ourlang.ConstantFloat32):
|
||||||
@ -196,7 +199,7 @@ def expression(inp: ourlang.Expression) -> Statements:
|
|||||||
if isinstance(inp.type, typing.TypeInt32):
|
if isinstance(inp.type, typing.TypeInt32):
|
||||||
if inp.operator == 'len':
|
if inp.operator == 'len':
|
||||||
if isinstance(inp.right.type, typing.TypeBytes):
|
if isinstance(inp.right.type, typing.TypeBytes):
|
||||||
yield wasm.Statement('i32.load')
|
yield i32.load()
|
||||||
return
|
return
|
||||||
|
|
||||||
raise NotImplementedError(expression, inp.type, inp.operator)
|
raise NotImplementedError(expression, inp.type, inp.operator)
|
||||||
@ -384,19 +387,21 @@ def _generate____new_reference___(mod: ourlang.Module) -> wasm.Function:
|
|||||||
],
|
],
|
||||||
type_(mod.types['i32']),
|
type_(mod.types['i32']),
|
||||||
[
|
[
|
||||||
wasm.Statement('i32.const', '0'),
|
i32.const(0),
|
||||||
wasm.Statement('i32.const', '0'),
|
i32.const(0),
|
||||||
wasm.Statement('i32.load'),
|
i32.load(),
|
||||||
wasm.Statement('local.tee', '$result', comment='Address for this call'),
|
wasm.Statement('local.tee', '$result', comment='Address for this call'),
|
||||||
wasm.Statement('local.get', '$alloc_size'),
|
wasm.Statement('local.get', '$alloc_size'),
|
||||||
wasm.Statement('i32.add'),
|
i32.add(),
|
||||||
wasm.Statement('i32.store', comment='Address for the next call'),
|
i32.store(comment='Address for the next call'),
|
||||||
wasm.Statement('local.get', '$result'),
|
wasm.Statement('local.get', '$result'),
|
||||||
],
|
],
|
||||||
)
|
)
|
||||||
|
|
||||||
STDLIB_ALLOC__FREE_BLOCK = '0x08'
|
STDLIB_ALLOC__IDENTIFIER = 0xA1C0
|
||||||
STDLIB_ALLOC__UNALLOC_ADDR = '0x0C'
|
STDLIB_ALLOC__RESERVED0 = 0x04
|
||||||
|
STDLIB_ALLOC__FREE_BLOCK = 0x08
|
||||||
|
STDLIB_ALLOC__UNALLOC_ADDR = 0x0C
|
||||||
|
|
||||||
def _generate_stdlib_alloc___init__(mod: ourlang.Module) -> wasm.Function:
|
def _generate_stdlib_alloc___init__(mod: ourlang.Module) -> wasm.Function:
|
||||||
return wasm.Function(
|
return wasm.Function(
|
||||||
@ -406,31 +411,31 @@ def _generate_stdlib_alloc___init__(mod: ourlang.Module) -> wasm.Function:
|
|||||||
[],
|
[],
|
||||||
wasm.WasmTypeNone(),
|
wasm.WasmTypeNone(),
|
||||||
[
|
[
|
||||||
wasm.Statement('i32.const', '0x00'),
|
i32.const(0),
|
||||||
wasm.Statement('i32.load'),
|
i32.load(),
|
||||||
wasm.Statement('i32.const', '0xA1C0'),
|
i32.const(STDLIB_ALLOC__IDENTIFIER),
|
||||||
wasm.Statement('i32.eq'),
|
i32.eq(),
|
||||||
wasm.Statement('if', comment='Already set up'),
|
*wasmeasy.if_(
|
||||||
wasm.Statement('return'),
|
wasm.Statement('return', comment='Already set up'),
|
||||||
wasm.Statement('end'),
|
),
|
||||||
|
|
||||||
wasm.Statement('i32.const', '0x04', comment='Reserved'),
|
i32.const(STDLIB_ALLOC__RESERVED0, comment='Reserved'),
|
||||||
wasm.Statement('i32.const', '0x00000000'),
|
i32.const(0),
|
||||||
wasm.Statement('i32.store'),
|
i32.store(),
|
||||||
|
|
||||||
wasm.Statement('i32.const', STDLIB_ALLOC__FREE_BLOCK,
|
i32.const(STDLIB_ALLOC__FREE_BLOCK,
|
||||||
comment='Address of next free block'),
|
comment='Address of next free block'),
|
||||||
wasm.Statement('i32.const', '0x00000000'),
|
i32.const(0, comment='None to start with'),
|
||||||
wasm.Statement('i32.store'),
|
i32.store(),
|
||||||
|
|
||||||
wasm.Statement('i32.const', STDLIB_ALLOC__UNALLOC_ADDR,
|
i32.const(STDLIB_ALLOC__UNALLOC_ADDR,
|
||||||
comment='Address of first unallocated byte'),
|
comment='Address of first unallocated byte'),
|
||||||
wasm.Statement('i32.const', '0x10'),
|
i32.const(0x10),
|
||||||
wasm.Statement('i32.store'),
|
i32.store(),
|
||||||
|
|
||||||
wasm.Statement('i32.const', '0x00', comment='Done setting up'),
|
i32.const(0, comment='Done setting up'),
|
||||||
wasm.Statement('i32.const', '0xA1C0'),
|
i32.const(STDLIB_ALLOC__IDENTIFIER),
|
||||||
wasm.Statement('i32.store'),
|
i32.store(),
|
||||||
],
|
],
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -446,14 +451,14 @@ def _generate_stdlib_alloc___find_free_block__(mod: ourlang.Module) -> wasm.Func
|
|||||||
],
|
],
|
||||||
type_(mod.types['i32']),
|
type_(mod.types['i32']),
|
||||||
[
|
[
|
||||||
wasm.Statement('i32.const', STDLIB_ALLOC__FREE_BLOCK),
|
i32.const(STDLIB_ALLOC__FREE_BLOCK),
|
||||||
wasm.Statement('i32.load'),
|
i32.load(),
|
||||||
wasm.Statement('i32.const', '0x00000000'),
|
i32.const(0),
|
||||||
wasm.Statement('i32.eq'),
|
i32.eq(),
|
||||||
wasm.Statement('if'),
|
*wasmeasy.if_(
|
||||||
wasm.Statement('i32.const', '0x00000000'),
|
i32.const(0),
|
||||||
wasm.Statement('return'),
|
wasm.Statement('return'),
|
||||||
wasm.Statement('end'),
|
),
|
||||||
wasm.Statement('unreachable'),
|
wasm.Statement('unreachable'),
|
||||||
],
|
],
|
||||||
)
|
)
|
||||||
@ -470,13 +475,13 @@ def _generate_stdlib_alloc___alloc__(mod: ourlang.Module) -> wasm.Function:
|
|||||||
],
|
],
|
||||||
type_(mod.types['i32']),
|
type_(mod.types['i32']),
|
||||||
[
|
[
|
||||||
wasm.Statement('i32.const', '0'),
|
i32.const(0),
|
||||||
wasm.Statement('i32.load'),
|
i32.load(),
|
||||||
wasm.Statement('i32.const', '0xA1C0'),
|
i32.const(STDLIB_ALLOC__IDENTIFIER),
|
||||||
wasm.Statement('i32.ne'),
|
i32.ne(),
|
||||||
wasm.Statement('if', comment='Failed to set up or memory corruption'),
|
*wasmeasy.if_(
|
||||||
wasm.Statement('unreachable'),
|
wasm.Statement('unreachable'),
|
||||||
wasm.Statement('end'),
|
),
|
||||||
|
|
||||||
wasm.Statement('local.get', '$alloc_size'),
|
wasm.Statement('local.get', '$alloc_size'),
|
||||||
wasm.Statement('call', '$stdlib.alloc.__find_free_block__'),
|
wasm.Statement('call', '$stdlib.alloc.__find_free_block__'),
|
||||||
@ -484,35 +489,33 @@ def _generate_stdlib_alloc___alloc__(mod: ourlang.Module) -> wasm.Function:
|
|||||||
|
|
||||||
# Check if there was a free block
|
# Check if there was a free block
|
||||||
wasm.Statement('local.get', '$result'),
|
wasm.Statement('local.get', '$result'),
|
||||||
wasm.Statement('i32.const', '0x00000000'),
|
i32.const(0),
|
||||||
wasm.Statement('i32.eq'),
|
i32.eq(),
|
||||||
wasm.Statement('if', comment='No free block found'),
|
*wasmeasy.if_(
|
||||||
|
|
||||||
# Use unallocated space
|
# Use unallocated space
|
||||||
wasm.Statement('i32.const', STDLIB_ALLOC__UNALLOC_ADDR),
|
i32.const(STDLIB_ALLOC__UNALLOC_ADDR),
|
||||||
|
|
||||||
wasm.Statement('i32.const', STDLIB_ALLOC__UNALLOC_ADDR),
|
i32.const(STDLIB_ALLOC__UNALLOC_ADDR),
|
||||||
wasm.Statement('i32.load'),
|
i32.load(),
|
||||||
wasm.Statement('local.tee', '$result'),
|
wasm.Statement('local.tee', '$result'),
|
||||||
|
|
||||||
# Updated unalloc pointer (address already set on stack)
|
# Updated unalloc pointer (address already set on stack)
|
||||||
wasm.Statement('i32.const', '0x04'), # Struct size
|
i32.const(4), # Header size
|
||||||
wasm.Statement('i32.add'),
|
i32.add(),
|
||||||
wasm.Statement('local.get', '$alloc_size'),
|
wasm.Statement('local.get', '$alloc_size'),
|
||||||
wasm.Statement('i32.add'),
|
i32.add(),
|
||||||
wasm.Statement('i32.store', 'offset=0'),
|
i32.store('offset=0'),
|
||||||
|
),
|
||||||
wasm.Statement('end'),
|
|
||||||
|
|
||||||
# Store block size
|
# Store block size
|
||||||
wasm.Statement('local.get', '$result'),
|
wasm.Statement('local.get', '$result'),
|
||||||
wasm.Statement('local.get', '$alloc_size'),
|
wasm.Statement('local.get', '$alloc_size'),
|
||||||
wasm.Statement('i32.store', 'offset=0'),
|
i32.store('offset=0'),
|
||||||
|
|
||||||
# Return address of the allocated bytes
|
# Return address of the allocated bytes
|
||||||
wasm.Statement('local.get', '$result'),
|
wasm.Statement('local.get', '$result'),
|
||||||
wasm.Statement('i32.const', '0x04'),
|
i32.const(4), # Header size
|
||||||
wasm.Statement('i32.add'),
|
i32.add(),
|
||||||
],
|
],
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -530,7 +533,7 @@ def _generate____access_bytes_index___(mod: ourlang.Module) -> wasm.Function:
|
|||||||
[
|
[
|
||||||
wasm.Statement('local.get', '$ofs'),
|
wasm.Statement('local.get', '$ofs'),
|
||||||
wasm.Statement('local.get', '$byt'),
|
wasm.Statement('local.get', '$byt'),
|
||||||
wasm.Statement('i32.load'),
|
i32.load(),
|
||||||
wasm.Statement('i32.lt_u'),
|
wasm.Statement('i32.lt_u'),
|
||||||
|
|
||||||
wasm.Statement('if', comment='$ofs < len($byt)'),
|
wasm.Statement('if', comment='$ofs < len($byt)'),
|
||||||
|
|||||||
69
phasm/wasmeasy.py
Normal file
69
phasm/wasmeasy.py
Normal file
@ -0,0 +1,69 @@
|
|||||||
|
"""
|
||||||
|
Helper functions to quickly generate WASM code
|
||||||
|
"""
|
||||||
|
from typing import List, Optional
|
||||||
|
|
||||||
|
import functools
|
||||||
|
|
||||||
|
from . import wasm
|
||||||
|
|
||||||
|
#pylint: disable=C0103,C0115,C0116,R0201,R0902
|
||||||
|
|
||||||
|
class Prefix_inn_fnn:
|
||||||
|
def __init__(self, prefix: str) -> None:
|
||||||
|
self.prefix = prefix
|
||||||
|
|
||||||
|
# 6.5.5. Memory Instructions
|
||||||
|
self.load = functools.partial(wasm.Statement, f'{self.prefix}.load')
|
||||||
|
self.store = functools.partial(wasm.Statement, f'{self.prefix}.store')
|
||||||
|
|
||||||
|
# 6.5.6. Numeric Instructions
|
||||||
|
self.clz = functools.partial(wasm.Statement, f'{self.prefix}.clz')
|
||||||
|
self.ctz = functools.partial(wasm.Statement, f'{self.prefix}.ctz')
|
||||||
|
self.popcnt = functools.partial(wasm.Statement, f'{self.prefix}.popcnt')
|
||||||
|
self.add = functools.partial(wasm.Statement, f'{self.prefix}.add')
|
||||||
|
self.sub = functools.partial(wasm.Statement, f'{self.prefix}.sub')
|
||||||
|
self.mul = functools.partial(wasm.Statement, f'{self.prefix}.mul')
|
||||||
|
self.div_s = functools.partial(wasm.Statement, f'{self.prefix}.div_s')
|
||||||
|
self.div_u = functools.partial(wasm.Statement, f'{self.prefix}.div_u')
|
||||||
|
self.rem_s = functools.partial(wasm.Statement, f'{self.prefix}.rem_s')
|
||||||
|
self.rem_u = functools.partial(wasm.Statement, f'{self.prefix}.rem_u')
|
||||||
|
self.and_ = functools.partial(wasm.Statement, f'{self.prefix}.and')
|
||||||
|
self.or_ = functools.partial(wasm.Statement, f'{self.prefix}.or')
|
||||||
|
self.xor = functools.partial(wasm.Statement, f'{self.prefix}.xor')
|
||||||
|
self.shl = functools.partial(wasm.Statement, f'{self.prefix}.shl')
|
||||||
|
self.shr_s = functools.partial(wasm.Statement, f'{self.prefix}.shr_s')
|
||||||
|
self.shr_u = functools.partial(wasm.Statement, f'{self.prefix}.shr_u')
|
||||||
|
self.rotl = functools.partial(wasm.Statement, f'{self.prefix}.rotl')
|
||||||
|
self.rotr = functools.partial(wasm.Statement, f'{self.prefix}.rotr')
|
||||||
|
|
||||||
|
self.eqz = functools.partial(wasm.Statement, f'{self.prefix}.eqz')
|
||||||
|
self.eq = functools.partial(wasm.Statement, f'{self.prefix}.eq')
|
||||||
|
self.ne = functools.partial(wasm.Statement, f'{self.prefix}.ne')
|
||||||
|
self.lt_s = functools.partial(wasm.Statement, f'{self.prefix}.lt_s')
|
||||||
|
self.lt_u = functools.partial(wasm.Statement, f'{self.prefix}.lt_u')
|
||||||
|
self.gt_s = functools.partial(wasm.Statement, f'{self.prefix}.gt_s')
|
||||||
|
self.gt_u = functools.partial(wasm.Statement, f'{self.prefix}.gt_u')
|
||||||
|
self.le_s = functools.partial(wasm.Statement, f'{self.prefix}.le_s')
|
||||||
|
self.le_u = functools.partial(wasm.Statement, f'{self.prefix}.le_u')
|
||||||
|
self.ge_s = functools.partial(wasm.Statement, f'{self.prefix}.ge_s')
|
||||||
|
self.ge_u = functools.partial(wasm.Statement, f'{self.prefix}.ge_u')
|
||||||
|
|
||||||
|
def const(self, value: int, comment: Optional[str] = None) -> wasm.Statement:
|
||||||
|
return wasm.Statement(f'{self.prefix}.const', f'0x{value:08x}', comment=comment)
|
||||||
|
|
||||||
|
i32 = Prefix_inn_fnn('i32')
|
||||||
|
i64 = Prefix_inn_fnn('i64')
|
||||||
|
|
||||||
|
class Block:
|
||||||
|
def __init__(self, start: str) -> None:
|
||||||
|
self.start = start
|
||||||
|
|
||||||
|
def __call__(self, *statements: wasm.Statement) -> List[wasm.Statement]:
|
||||||
|
return [
|
||||||
|
wasm.Statement('if'),
|
||||||
|
*statements,
|
||||||
|
wasm.Statement('end'),
|
||||||
|
]
|
||||||
|
|
||||||
|
if_ = Block('if')
|
||||||
@ -108,5 +108,3 @@ def testEntry() -> u8:
|
|||||||
assert 0x14 == offset0
|
assert 0x14 == offset0
|
||||||
assert 0x38 == offset1
|
assert 0x38 == offset1
|
||||||
assert 0x5C == offset2
|
assert 0x5C == offset2
|
||||||
|
|
||||||
assert False
|
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user