MVP #1

Merged
jbwdevries merged 73 commits from idea_crc32 into master 2022-08-21 12:59:21 +00:00
6 changed files with 177 additions and 28 deletions
Showing only changes of commit 865eccd719 - Show all commits

View File

@ -29,5 +29,6 @@ typecheck: venv/.done
venv/.done: requirements.txt
python3.8 -m venv venv
venv/bin/python3 -m pip install wheel
venv/bin/python3 -m pip install -r $^
touch $@

View File

@ -30,6 +30,8 @@ class Visitor:
'i64': wasm.OurTypeInt64(),
'f32': wasm.OurTypeFloat32(),
'f64': wasm.OurTypeFloat64(),
'i32x4': wasm.OurTypeVectorInt32x4(),
}
def _get_type(self, name: Union[None, str, ast.expr]) -> wasm.OurType:
@ -186,7 +188,7 @@ class Visitor:
else:
raise NotImplementedError
result = None if node.returns is None else self._get_type(node.returns)
result = self._get_type(node.returns)
assert not node.args.vararg
assert not node.args.kwonlyargs
@ -377,7 +379,7 @@ class Visitor:
return self.visit_Name(wlocals, exp_type, node)
if isinstance(node, ast.Tuple):
return self.visit_Tuple(wlocals, exp_type, node)
return self.visit_Tuple(module, wlocals, exp_type, node)
raise NotImplementedError(node)
@ -549,7 +551,8 @@ class Visitor:
called_params = func.params
called_result = func.result
assert exp_type == called_result, 'Function does not match expected type'
assert exp_type == called_result, \
f'Function does not match expected type; {called_name} returns {called_result.to_python()} but {exp_type.to_python()} is expected'
assert len(called_params) == len(node.args), \
'{}:{} Function {} requires {} arguments, but {} are supplied'.format(
@ -584,7 +587,8 @@ class Visitor:
return
if isinstance(exp_type, wasm.OurTypeFloat32):
assert isinstance(node.value, float)
assert isinstance(node.value, float), \
f'Expression expected a float, but received {node.value}'
# TODO: Size check?
yield wasm.Statement('f32.const', node.value.hex())
@ -672,6 +676,7 @@ class Visitor:
def visit_Tuple(
self,
module: wasm.Module,
wlocals: WLocals,
exp_type: wasm.OurType,
node: ast.Tuple,
@ -679,7 +684,21 @@ class Visitor:
"""
Visits an Tuple node as (part of) an expression
"""
assert isinstance(exp_type, wasm.OurTypeTuple), 'Expression is not expecting a tuple'
if isinstance(exp_type, wasm.OurTypeVector):
assert isinstance(exp_type, wasm.OurTypeVectorInt32x4), 'Not implemented yet'
assert len(node.elts) == 4, 'Not implemented yet'
values: List[str] = []
for arg in node.elts:
assert isinstance(arg, ast.Constant)
assert isinstance(arg.value, int)
values.append(str(arg.value))
yield wasm.Statement('v128.const', 'i32x4', *values)
return
assert isinstance(exp_type, wasm.OurTypeTuple), \
f'Expression is expecting {exp_type}, not a tuple'
# TODO: free call
@ -692,11 +711,10 @@ class Visitor:
yield wasm.Statement('local.set', '$___new_reference___addr', comment='Allocate for tuple')
for member, arg in zip(tpl.members, node.elts):
if not isinstance(arg, ast.Constant):
raise NotImplementedError('TODO: Non-const tuple members')
yield wasm.Statement('local.get', '$___new_reference___addr')
yield wasm.Statement(f'{member.type.to_wasm()}.const', str(arg.value))
yield from self.visit_expr(module, wlocals, member.type, arg)
yield wasm.Statement(f'{member.type.to_wasm()}.store', 'offset=' + str(member.offset),
comment='Write tuple value to memory')

View File

@ -9,11 +9,14 @@ from typing import Any, Iterable, List, Optional, Tuple, Union
###
class OurType:
def to_python(self) -> str:
raise NotImplementedError(self, 'to_python')
def to_wasm(self) -> str:
raise NotImplementedError
raise NotImplementedError(self, 'to_wasm')
def alloc_size(self) -> int:
raise NotImplementedError
raise NotImplementedError(self, 'alloc_size')
class OurTypeNone(OurType):
pass
@ -22,6 +25,9 @@ class OurTypeBool(OurType):
pass
class OurTypeInt32(OurType):
def to_python(self) -> str:
return 'i32'
def to_wasm(self) -> str:
return 'i32'
@ -49,6 +55,21 @@ class OurTypeFloat64(OurType):
def alloc_size(self) -> int:
return 8
class OurTypeVector(OurType):
"""
A vector is a 128-bit value
"""
def to_wasm(self) -> str:
return 'v128'
def alloc_size(self) -> int:
return 16
class OurTypeVectorInt32x4(OurTypeVector):
"""
4 Int32 values in a single vector
"""
class Constant:
"""
TODO
@ -95,12 +116,28 @@ class OurTypeTuple(OurType):
def __init__(self, members: List[TupleMember]) -> None:
self.members = members
def to_python(self) -> str:
return 'Tuple[' + (
', '.join(x.type.to_python() for x in self.members)
) + ']'
def to_wasm(self) -> str:
return 'i32' # WASM uses 32 bit pointers
def alloc_size(self) -> int:
return sum(x.type.alloc_size() for x in self.members)
def __eq__(self, other: Any) -> bool:
if not isinstance(other, OurTypeTuple):
raise NotImplementedError
return (
len(self.members) == len(other.members)
and all(
self.members[x].type == other.members[x].type
for x in range(len(self.members))
)
)
Param = Tuple[str, OurType]
@ -123,7 +160,7 @@ class Import:
self.name = name
self.intname = intname
self.params = [*params]
self.result: str = 'None'
self.result: OurType = OurTypeNone()
def generate(self) -> str:
"""
@ -164,7 +201,7 @@ class Function:
exported: bool,
params: Iterable[Param],
locals_: Iterable[Param],
result: Optional[OurType],
result: OurType,
statements: Iterable[Statement],
) -> None:
self.name = name

View File

@ -3,3 +3,7 @@ pylint==2.7.4
pytest==6.2.2
pytest-integration==0.2.2
pywasm==1.0.7
pywasm3==0.5.0
wasmer==1.0.0
wasmer_compiler_cranelift==1.0.0
wasmtime==0.36.0

View File

@ -5,11 +5,19 @@ import sys
from tempfile import NamedTemporaryFile
from pywasm import binary
from pywasm import Runtime
import pywasm
import wasm3
import wasmer
import wasmer_compiler_cranelift
import wasmtime
from py2wasm.utils import process
DASHES = '-' * 16
def wat2wasm(code_wat):
path = os.environ.get('WAT2WASM', 'wat2wasm')
@ -63,9 +71,7 @@ class Suite:
"""
code_wat = process(self.code_py, self.test_name)
dashes = '-' * 16
sys.stderr.write(f'{dashes} Assembly {dashes}\n')
sys.stderr.write(f'{DASHES} Assembly {DASHES}\n')
line_list = code_wat.split('\n')
line_no_width = len(str(len(line_list)))
@ -76,21 +82,91 @@ class Suite:
))
code_wasm = wat2wasm(code_wat)
module = binary.Module.from_reader(io.BytesIO(code_wasm))
return _run_pywasm3(code_wasm, args)
def _run_pywasm(code_wasm, args):
# https://pypi.org/project/pywasm/
result = SuiteResult()
runtime = Runtime(module, result.make_imports(), {})
sys.stderr.write(f'{dashes} Memory (pre run) {dashes}\n')
module = pywasm.binary.Module.from_reader(io.BytesIO(code_wasm))
runtime = pywasm.Runtime(module, result.make_imports(), {})
sys.stderr.write(f'{DASHES} Memory (pre run) {DASHES}\n')
_dump_memory(runtime.store.mems[0].data)
result.returned_value = runtime.exec('testEntry', args)
sys.stderr.write(f'{dashes} Memory (post run) {dashes}\n')
sys.stderr.write(f'{DASHES} Memory (post run) {DASHES}\n')
_dump_memory(runtime.store.mems[0].data)
return result
def _run_pywasm3(code_wasm, args):
# https://pypi.org/project/pywasm3/
result = SuiteResult()
env = wasm3.Environment()
mod = env.parse_module(code_wasm)
rtime = env.new_runtime(1024 * 1024)
rtime.load(mod)
sys.stderr.write(f'{DASHES} Memory (pre run) {DASHES}\n')
_dump_memory(rtime.get_memory(0).tobytes())
result.returned_value = rtime.find_function('testEntry')(*args)
sys.stderr.write(f'{DASHES} Memory (post run) {DASHES}\n')
_dump_memory(rtime.get_memory(0).tobytes())
return result
def _run_wasmtime(code_wasm, args):
# https://pypi.org/project/wasmtime/
result = SuiteResult()
store = wasmtime.Store()
module = wasmtime.Module(store.engine, code_wasm)
instance = wasmtime.Instance(store, module, [])
sys.stderr.write(f'{DASHES} Memory (pre run) {DASHES}\n')
sys.stderr.write('<Not available on wasmtime>\n')
result.returned_value = instance.exports(store)['testEntry'](store, *args)
sys.stderr.write(f'{DASHES} Memory (post run) {DASHES}\n')
sys.stderr.write('<Not available on wasmtime>\n')
return result
def _run_wasmer(code_wasm, args):
# https://pypi.org/project/wasmer/
result = SuiteResult()
store = wasmer.Store(wasmer.engine.JIT(wasmer_compiler_cranelift.Compiler))
# Let's compile the module to be able to execute it!
module = wasmer.Module(store, code_wasm)
# Now the module is compiled, we can instantiate it.
instance = wasmer.Instance(module)
sys.stderr.write(f'{DASHES} Memory (pre run) {DASHES}\n')
sys.stderr.write('<Not available on wasmer>\n')
# Call the exported `sum` function.
result.returned_value = instance.exports.testEntry(*args)
sys.stderr.write(f'{DASHES} Memory (post run) {DASHES}\n')
sys.stderr.write('<Not available on wasmer>\n')
return result
def _dump_memory(mem):
line_width = 16

View File

@ -341,7 +341,7 @@ def test_tuple_float():
@exported
def testEntry() -> f32:
return helper((1, 2, 3, ))
return helper((1.0, 2.0, 3.0, ))
def helper(v: Tuple[f32, f32, f32]) -> f32:
# sqrt is guaranteed by wasm, so we can use it
@ -353,3 +353,16 @@ def helper(v: Tuple[f32, f32, f32]) -> f32:
assert 3.74 < result.returned_value < 3.75
assert [] == result.log_int32_list
@pytest.mark.integration_test
@pytest.mark.skip('SIMD support is but a dream')
def test_tuple_i32x4():
code_py = """
@exported
def testEntry() -> i32x4:
return (51, 153, 204, 0, )
"""
result = Suite(code_py, 'test_rgb2hsl').run_code()
assert (1, 2, 3, 0) == result.returned_value