Now runs on new code

This commit is contained in:
Johan B.W. de Vries 2022-06-19 16:54:14 +02:00
parent a480e60698
commit ac0c49a92c
5 changed files with 189 additions and 42 deletions

6
TODO.md Normal file
View File

@ -0,0 +1,6 @@
# TODO
- Rename ourlang.Struct to OurTypeStruct
- Maybe rename this to a types module?
- Remove references to log_int32_list
- Fix naming in Suite() calls

View File

@ -21,16 +21,19 @@ def type_(inp: ourlang.OurType) -> wasm.OurType:
if isinstance(inp, ourlang.OurTypeFloat64): if isinstance(inp, ourlang.OurTypeFloat64):
return wasm.OurTypeFloat64() return wasm.OurTypeFloat64()
if isinstance(inp, ourlang.Struct): if isinstance(inp, (ourlang.Struct, ourlang.OurTypeTuple, )):
# Structs are passed as pointer # Structs and tuples are passed as pointer
# And pointers are i32 # And pointers are i32
return wasm.OurTypeInt32() return wasm.OurTypeInt32()
raise NotImplementedError(type_, inp) raise NotImplementedError(type_, inp)
# Operators that work for i32, i64, f32, f64
OPERATOR_MAP = { OPERATOR_MAP = {
'+': 'add', '+': 'add',
'-': 'sub', '-': 'sub',
'*': 'mul',
'==': 'eq',
} }
I32_OPERATOR_MAP = { # TODO: Introduce UInt32 type I32_OPERATOR_MAP = { # TODO: Introduce UInt32 type
@ -121,6 +124,19 @@ def expression(inp: ourlang.Expression) -> Statements:
yield wasm.Statement(inp.type.render() + '.load', 'offset=' + str(inp.member.offset)) yield wasm.Statement(inp.type.render() + '.load', 'offset=' + str(inp.member.offset))
return return
if isinstance(inp, ourlang.AccessTupleMember):
# FIXME: Properly implement this
# inp.type.render() is also a hack that doesn't really work consistently
if not isinstance(inp.type, (
ourlang.OurTypeInt32, ourlang.OurTypeFloat32,
ourlang.OurTypeInt64, ourlang.OurTypeFloat64,
)):
raise NotImplementedError(inp, inp.type)
yield from expression(inp.varref)
yield wasm.Statement(inp.type.render() + '.load', 'offset=' + str(inp.member.offset))
return
raise NotImplementedError(expression, inp) raise NotImplementedError(expression, inp)
def statement_return(inp: ourlang.StatementReturn) -> Statements: def statement_return(inp: ourlang.StatementReturn) -> Statements:
@ -157,7 +173,14 @@ def function_argument(inp: Tuple[str, ourlang.OurType]) -> wasm.Param:
return (inp[0], type_(inp[1]), ) return (inp[0], type_(inp[1]), )
def function(inp: ourlang.Function) -> wasm.Function: def function(inp: ourlang.Function) -> wasm.Function:
if isinstance(inp, ourlang.StructConstructor): if isinstance(inp, ourlang.TupleConstructor):
statements = [
*_generate_tuple_constructor(inp)
]
locals_ = [
('___new_reference___addr', wasm.OurTypeInt32(), ),
]
elif isinstance(inp, ourlang.StructConstructor):
statements = [ statements = [
*_generate_struct_constructor(inp) *_generate_struct_constructor(inp)
] ]
@ -219,6 +242,27 @@ def _generate_allocator(mod: ourlang.Module) -> wasm.Function:
], ],
) )
def _generate_tuple_constructor(inp: ourlang.TupleConstructor) -> Statements:
yield wasm.Statement('i32.const', str(inp.tuple.alloc_size()))
yield wasm.Statement('call', '$___new_reference___')
yield wasm.Statement('local.set', '$___new_reference___addr')
for member in inp.tuple.members:
# FIXME: Properly implement this
# inp.type.render() is also a hack that doesn't really work consistently
if not isinstance(member.type, (
ourlang.OurTypeInt32, ourlang.OurTypeFloat32,
ourlang.OurTypeInt64, ourlang.OurTypeFloat64,
)):
raise NotImplementedError
yield wasm.Statement('local.get', '$___new_reference___addr')
yield wasm.Statement('local.get', f'$arg{member.idx}')
yield wasm.Statement(f'{member.type.render()}.store', 'offset=' + str(member.offset))
yield wasm.Statement('local.get', '$___new_reference___addr')
def _generate_struct_constructor(inp: ourlang.StructConstructor) -> Statements: def _generate_struct_constructor(inp: ourlang.StructConstructor) -> Statements:
yield wasm.Statement('i32.const', str(inp.struct.alloc_size())) yield wasm.Statement('i32.const', str(inp.struct.alloc_size()))
yield wasm.Statement('call', '$___new_reference___') yield wasm.Statement('call', '$___new_reference___')

View File

@ -59,6 +59,9 @@ class OurTypeInt64(OurType):
def render(self) -> str: def render(self) -> str:
return 'i64' return 'i64'
def alloc_size(self) -> int:
return 8
class OurTypeFloat32(OurType): class OurTypeFloat32(OurType):
""" """
The Float type, 32 bits wide The Float type, 32 bits wide
@ -68,6 +71,9 @@ class OurTypeFloat32(OurType):
def render(self) -> str: def render(self) -> str:
return 'f32' return 'f32'
def alloc_size(self) -> int:
return 4
class OurTypeFloat64(OurType): class OurTypeFloat64(OurType):
""" """
The Float type, 64 bits wide The Float type, 64 bits wide
@ -77,21 +83,44 @@ class OurTypeFloat64(OurType):
def render(self) -> str: def render(self) -> str:
return 'f64' return 'f64'
def alloc_size(self) -> int:
return 8
class TupleMember:
"""
Represents a tuple member
"""
def __init__(self, idx: int, type_: OurType, offset: int) -> None:
self.idx = idx
self.type = type_
self.offset = offset
class OurTypeTuple(OurType): class OurTypeTuple(OurType):
""" """
The tuple type, 64 bits wide The tuple type, 64 bits wide
""" """
__slots__ = ('members', ) __slots__ = ('members', )
members: List[OurType] members: List[TupleMember]
def __init__(self) -> None: def __init__(self) -> None:
self.members = [] self.members = []
def render(self) -> str: def render(self) -> str:
mems = ', '.join(x.render() for x in self.members) mems = ', '.join(x.type.render() for x in self.members)
return f'({mems}, )' return f'({mems}, )'
def render_internal_name(self) -> str:
mems = '@'.join(x.type.render() for x in self.members)
assert ' ' not in mems, 'Not implement yet: subtuples'
return f'tuple@{mems}'
def alloc_size(self) -> int:
return sum(
x.type.alloc_size()
for x in self.members
)
class Expression: class Expression:
""" """
An expression within a statement An expression within a statement
@ -257,6 +286,9 @@ class FunctionCall(Expression):
if isinstance(self.function, StructConstructor): if isinstance(self.function, StructConstructor):
return f'{self.function.struct.name}({args})' return f'{self.function.struct.name}({args})'
if isinstance(self.function, TupleConstructor):
return f'({args}, )'
return f'{self.function.name}({args})' return f'{self.function.name}({args})'
class AccessStructMember(Expression): class AccessStructMember(Expression):
@ -281,21 +313,19 @@ class AccessTupleMember(Expression):
""" """
Access a tuple member for reading of writing Access a tuple member for reading of writing
""" """
__slots__ = ('varref', 'member_idx', 'member_type', ) __slots__ = ('varref', 'member', )
varref: VariableReference varref: VariableReference
member_idx: int member: TupleMember
member_type: 'OurType'
def __init__(self, varref: VariableReference, member_idx: int, member_type: 'OurType') -> None: def __init__(self, varref: VariableReference, member: TupleMember, ) -> None:
super().__init__(member_type) super().__init__(member.type)
self.varref = varref self.varref = varref
self.member_idx = member_idx self.member = member
self.member_type = member_type
def render(self) -> str: def render(self) -> str:
return f'{self.varref.render()}[{self.member_idx}]' return f'{self.varref.render()}[{self.member.idx}]'
class TupleCreation(Expression): class TupleCreation(Expression):
""" """
@ -450,6 +480,26 @@ class StructConstructor(Function):
self.struct = struct self.struct = struct
class TupleConstructor(Function):
"""
The constructor method for a tuple
"""
__slots__ = ('tuple', )
tuple: OurTypeTuple
def __init__(self, tuple_: OurTypeTuple) -> None:
name = tuple_.render_internal_name()
super().__init__(f'@{name}@__init___@', -1)
self.returns = tuple_
for mem in tuple_.members:
self.posonlyargs.append((f'arg{mem.idx}', mem.type, ))
self.tuple = tuple_
class StructMember: class StructMember:
""" """
Represents a struct member Represents a struct member
@ -823,10 +873,14 @@ class OurVisitor:
if len(exp_type.members) != len(node.elts): if len(exp_type.members) != len(node.elts):
_raise_static_error(node, f'Expression is expecting a tuple of size {len(exp_type.members)}, but {len(node.elts)} are given') _raise_static_error(node, f'Expression is expecting a tuple of size {len(exp_type.members)}, but {len(node.elts)} are given')
result = TupleCreation(exp_type) tuple_constructor = TupleConstructor(exp_type)
result.members = [
self.visit_Module_FunctionDef_expr(module, function, our_locals, arg_type, arg_node) func = module.functions[tuple_constructor.name]
for arg_node, arg_type in zip(node.elts, exp_type.members)
result = FunctionCall(func)
result.arguments = [
self.visit_Module_FunctionDef_expr(module, function, our_locals, mem.type, arg_node)
for arg_node, mem in zip(node.elts, exp_type.members)
] ]
return result return result
@ -930,12 +984,11 @@ class OurVisitor:
_raise_static_error(node, f'Index {node.slice.value.value} out of bounds for tuple {node.value.id}') _raise_static_error(node, f'Index {node.slice.value.value} out of bounds for tuple {node.value.id}')
member = node_typ.members[node.slice.value.value] member = node_typ.members[node.slice.value.value]
if exp_type != member: if exp_type != member.type:
_raise_static_error(node, f'Expected {exp_type.render()}, got {member.render()} instead') _raise_static_error(node, f'Expected {exp_type.render()}, got {member.type.render()} instead')
return AccessTupleMember( return AccessTupleMember(
VariableReference(node_typ, node.value.id), VariableReference(node_typ, node.value.id),
node.slice.value.value,
member, member,
) )
@ -997,11 +1050,23 @@ class OurVisitor:
_raise_static_error(node, 'Must be load context') _raise_static_error(node, 'Must be load context')
result = OurTypeTuple() result = OurTypeTuple()
result.members = [
self.visit_type(module, elt) offset = 0
for elt in node.elts
] for idx, elt in enumerate(node.elts):
return result member = TupleMember(idx, self.visit_type(module, elt), offset)
result.members.append(member)
offset += member.type.alloc_size()
key = result.render_internal_name()
if key not in module.types:
module.types[key] = result
constructor = TupleConstructor(result)
module.functions[constructor.name] = constructor
return module.types[key]
raise NotImplementedError(f'{node} as type') raise NotImplementedError(f'{node} as type')

View File

@ -15,6 +15,7 @@ import wasmer_compiler_cranelift
import wasmtime import wasmtime
from py2wasm.utils import our_process, process from py2wasm.utils import our_process, process
from py2wasm.compiler import module
DASHES = '-' * 16 DASHES = '-' * 16
@ -69,23 +70,30 @@ class Suite:
Returned is an object with the results set Returned is an object with the results set
""" """
# sys.stderr.write(f'{DASHES} Old Assembly {DASHES}\n')
#
# code_wat_old = process(self.code_py, self.test_name)
# _write_numbered_lines(code_wat_old)
our_module = our_process(self.code_py, self.test_name) our_module = our_process(self.code_py, self.test_name)
# Check if code formatting works
assert self.code_py == '\n' + our_module.render() # \n for formatting in tests assert self.code_py == '\n' + our_module.render() # \n for formatting in tests
code_wat = process(self.code_py, self.test_name) # Compile
wat_module = module(our_module)
# Render as text
code_wat = wat_module.generate()
sys.stderr.write(f'{DASHES} Assembly {DASHES}\n') sys.stderr.write(f'{DASHES} Assembly {DASHES}\n')
line_list = code_wat.split('\n') _write_numbered_lines(code_wat)
line_no_width = len(str(len(line_list)))
for line_no, line_txt in enumerate(line_list):
sys.stderr.write('{} {}\n'.format(
str(line_no + 1).zfill(line_no_width),
line_txt,
))
# Compile to assembly code
code_wasm = wat2wasm(code_wat) code_wasm = wat2wasm(code_wat)
# Run assembly code
return _run_pywasm3(code_wasm, args) return _run_pywasm3(code_wasm, args)
def _run_pywasm(code_wasm, args): def _run_pywasm(code_wasm, args):
@ -117,13 +125,13 @@ def _run_pywasm3(code_wasm, args):
rtime = env.new_runtime(1024 * 1024) rtime = env.new_runtime(1024 * 1024)
rtime.load(mod) rtime.load(mod)
sys.stderr.write(f'{DASHES} Memory (pre run) {DASHES}\n') # sys.stderr.write(f'{DASHES} Memory (pre run) {DASHES}\n')
_dump_memory(rtime.get_memory(0).tobytes()) # _dump_memory(rtime.get_memory(0).tobytes())
result.returned_value = rtime.find_function('testEntry')(*args) result.returned_value = rtime.find_function('testEntry')(*args)
sys.stderr.write(f'{DASHES} Memory (post run) {DASHES}\n') # sys.stderr.write(f'{DASHES} Memory (post run) {DASHES}\n')
_dump_memory(rtime.get_memory(0).tobytes()) # _dump_memory(rtime.get_memory(0).tobytes())
return result return result
@ -190,3 +198,12 @@ def _dump_memory(mem):
sys.stderr.write(f'{idx:08x} {line}\n') sys.stderr.write(f'{idx:08x} {line}\n')
prev_line = line prev_line = line
def _write_numbered_lines(text: str) -> None:
line_list = text.split('\n')
line_no_width = len(str(len(line_list)))
for line_no, line_txt in enumerate(line_list):
sys.stderr.write('{} {}\n'.format(
str(line_no + 1).zfill(line_no_width),
line_txt,
))

View File

@ -51,6 +51,20 @@ def testEntry() -> {type_}:
assert 7 == result.returned_value assert 7 == result.returned_value
assert TYPE_MAP[type_] == type(result.returned_value) assert TYPE_MAP[type_] == type(result.returned_value)
@pytest.mark.integration_test
@pytest.mark.parametrize('type_', ['f32', 'f64'])
def test_buildins_sqrt(type_):
code_py = f"""
@exported
def testEntry() -> {type_}:
return sqrt(25)
"""
result = Suite(code_py, 'test_addition').run_code()
assert 5 == result.returned_value
assert TYPE_MAP[type_] == type(result.returned_value)
@pytest.mark.integration_test @pytest.mark.integration_test
@pytest.mark.parametrize('type_', ['i32', 'i64', 'f32', 'f64']) @pytest.mark.parametrize('type_', ['i32', 'i64', 'f32', 'f64'])
def test_arg(type_): def test_arg(type_):
@ -320,20 +334,21 @@ def helper(shape1: Rectangle, shape2: Rectangle) -> i32:
assert [] == result.log_int32_list assert [] == result.log_int32_list
@pytest.mark.integration_test @pytest.mark.integration_test
def test_tuple_int(): @pytest.mark.parametrize('type_', ['i32', 'i64', 'f32', 'f64'])
code_py = """ def test_tuple_simple(type_):
code_py = f"""
@exported @exported
def testEntry() -> i32: def testEntry() -> {type_}:
return helper((24, 57, 80, )) return helper((24, 57, 80, ))
def helper(vector: (i32, i32, i32, )) -> i32: def helper(vector: ({type_}, {type_}, {type_}, )) -> {type_}:
return vector[0] + vector[1] + vector[2] return vector[0] + vector[1] + vector[2]
""" """
result = Suite(code_py, 'test_call').run_code() result = Suite(code_py, 'test_call').run_code()
assert 161 == result.returned_value assert 161 == result.returned_value
assert [] == result.log_int32_list assert TYPE_MAP[type_] == type(result.returned_value)
@pytest.mark.integration_test @pytest.mark.integration_test
def test_tuple_float(): def test_tuple_float():