Added more robust and easy way to generate WASM

This commit is contained in:
Johan B.W. de Vries 2022-08-04 21:18:53 +02:00
parent e03f038cf9
commit fea817ca00
3 changed files with 140 additions and 70 deletions

View File

@ -6,6 +6,9 @@ from typing import Generator
from . import ourlang
from . import typing
from . import wasm
from . import wasmeasy
from .wasmeasy import i32, i64
Statements = Generator[wasm.Statement, None, None]
@ -107,23 +110,23 @@ def expression(inp: ourlang.Expression) -> Statements:
Compile: Any expression
"""
if isinstance(inp, ourlang.ConstantUInt8):
yield wasm.Statement('i32.const', str(inp.value))
yield i32.const(inp.value)
return
if isinstance(inp, ourlang.ConstantUInt32):
yield wasm.Statement('i32.const', str(inp.value))
yield i32.const(inp.value)
return
if isinstance(inp, ourlang.ConstantUInt64):
yield wasm.Statement('i64.const', str(inp.value))
yield i64.const(inp.value)
return
if isinstance(inp, ourlang.ConstantInt32):
yield wasm.Statement('i32.const', str(inp.value))
yield i32.const(inp.value)
return
if isinstance(inp, ourlang.ConstantInt64):
yield wasm.Statement('i64.const', str(inp.value))
yield i64.const(inp.value)
return
if isinstance(inp, ourlang.ConstantFloat32):
@ -196,7 +199,7 @@ def expression(inp: ourlang.Expression) -> Statements:
if isinstance(inp.type, typing.TypeInt32):
if inp.operator == 'len':
if isinstance(inp.right.type, typing.TypeBytes):
yield wasm.Statement('i32.load')
yield i32.load()
return
raise NotImplementedError(expression, inp.type, inp.operator)
@ -384,19 +387,21 @@ def _generate____new_reference___(mod: ourlang.Module) -> wasm.Function:
],
type_(mod.types['i32']),
[
wasm.Statement('i32.const', '0'),
wasm.Statement('i32.const', '0'),
wasm.Statement('i32.load'),
i32.const(0),
i32.const(0),
i32.load(),
wasm.Statement('local.tee', '$result', comment='Address for this call'),
wasm.Statement('local.get', '$alloc_size'),
wasm.Statement('i32.add'),
wasm.Statement('i32.store', comment='Address for the next call'),
i32.add(),
i32.store(comment='Address for the next call'),
wasm.Statement('local.get', '$result'),
],
)
STDLIB_ALLOC__FREE_BLOCK = '0x08'
STDLIB_ALLOC__UNALLOC_ADDR = '0x0C'
STDLIB_ALLOC__IDENTIFIER = 0xA1C0
STDLIB_ALLOC__RESERVED0 = 0x04
STDLIB_ALLOC__FREE_BLOCK = 0x08
STDLIB_ALLOC__UNALLOC_ADDR = 0x0C
def _generate_stdlib_alloc___init__(mod: ourlang.Module) -> wasm.Function:
return wasm.Function(
@ -406,31 +411,31 @@ def _generate_stdlib_alloc___init__(mod: ourlang.Module) -> wasm.Function:
[],
wasm.WasmTypeNone(),
[
wasm.Statement('i32.const', '0x00'),
wasm.Statement('i32.load'),
wasm.Statement('i32.const', '0xA1C0'),
wasm.Statement('i32.eq'),
wasm.Statement('if', comment='Already set up'),
wasm.Statement('return'),
wasm.Statement('end'),
i32.const(0),
i32.load(),
i32.const(STDLIB_ALLOC__IDENTIFIER),
i32.eq(),
*wasmeasy.if_(
wasm.Statement('return', comment='Already set up'),
),
wasm.Statement('i32.const', '0x04', comment='Reserved'),
wasm.Statement('i32.const', '0x00000000'),
wasm.Statement('i32.store'),
i32.const(STDLIB_ALLOC__RESERVED0, comment='Reserved'),
i32.const(0),
i32.store(),
wasm.Statement('i32.const', STDLIB_ALLOC__FREE_BLOCK,
i32.const(STDLIB_ALLOC__FREE_BLOCK,
comment='Address of next free block'),
wasm.Statement('i32.const', '0x00000000'),
wasm.Statement('i32.store'),
i32.const(0, comment='None to start with'),
i32.store(),
wasm.Statement('i32.const', STDLIB_ALLOC__UNALLOC_ADDR,
i32.const(STDLIB_ALLOC__UNALLOC_ADDR,
comment='Address of first unallocated byte'),
wasm.Statement('i32.const', '0x10'),
wasm.Statement('i32.store'),
i32.const(0x10),
i32.store(),
wasm.Statement('i32.const', '0x00', comment='Done setting up'),
wasm.Statement('i32.const', '0xA1C0'),
wasm.Statement('i32.store'),
i32.const(0, comment='Done setting up'),
i32.const(STDLIB_ALLOC__IDENTIFIER),
i32.store(),
],
)
@ -446,14 +451,14 @@ def _generate_stdlib_alloc___find_free_block__(mod: ourlang.Module) -> wasm.Func
],
type_(mod.types['i32']),
[
wasm.Statement('i32.const', STDLIB_ALLOC__FREE_BLOCK),
wasm.Statement('i32.load'),
wasm.Statement('i32.const', '0x00000000'),
wasm.Statement('i32.eq'),
wasm.Statement('if'),
wasm.Statement('i32.const', '0x00000000'),
i32.const(STDLIB_ALLOC__FREE_BLOCK),
i32.load(),
i32.const(0),
i32.eq(),
*wasmeasy.if_(
i32.const(0),
wasm.Statement('return'),
wasm.Statement('end'),
),
wasm.Statement('unreachable'),
],
)
@ -470,13 +475,13 @@ def _generate_stdlib_alloc___alloc__(mod: ourlang.Module) -> wasm.Function:
],
type_(mod.types['i32']),
[
wasm.Statement('i32.const', '0'),
wasm.Statement('i32.load'),
wasm.Statement('i32.const', '0xA1C0'),
wasm.Statement('i32.ne'),
wasm.Statement('if', comment='Failed to set up or memory corruption'),
i32.const(0),
i32.load(),
i32.const(STDLIB_ALLOC__IDENTIFIER),
i32.ne(),
*wasmeasy.if_(
wasm.Statement('unreachable'),
wasm.Statement('end'),
),
wasm.Statement('local.get', '$alloc_size'),
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
wasm.Statement('local.get', '$result'),
wasm.Statement('i32.const', '0x00000000'),
wasm.Statement('i32.eq'),
wasm.Statement('if', comment='No free block found'),
i32.const(0),
i32.eq(),
*wasmeasy.if_(
# 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),
wasm.Statement('i32.load'),
i32.const(STDLIB_ALLOC__UNALLOC_ADDR),
i32.load(),
wasm.Statement('local.tee', '$result'),
# Updated unalloc pointer (address already set on stack)
wasm.Statement('i32.const', '0x04'), # Struct size
wasm.Statement('i32.add'),
i32.const(4), # Header size
i32.add(),
wasm.Statement('local.get', '$alloc_size'),
wasm.Statement('i32.add'),
wasm.Statement('i32.store', 'offset=0'),
wasm.Statement('end'),
i32.add(),
i32.store('offset=0'),
),
# Store block size
wasm.Statement('local.get', '$result'),
wasm.Statement('local.get', '$alloc_size'),
wasm.Statement('i32.store', 'offset=0'),
i32.store('offset=0'),
# Return address of the allocated bytes
wasm.Statement('local.get', '$result'),
wasm.Statement('i32.const', '0x04'),
wasm.Statement('i32.add'),
i32.const(4), # Header size
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', '$byt'),
wasm.Statement('i32.load'),
i32.load(),
wasm.Statement('i32.lt_u'),
wasm.Statement('if', comment='$ofs < len($byt)'),

69
phasm/wasmeasy.py Normal file
View 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')

View File

@ -108,5 +108,3 @@ def testEntry() -> u8:
assert 0x14 == offset0
assert 0x38 == offset1
assert 0x5C == offset2
assert False