MVP #1
6
TODO.md
Normal file
6
TODO.md
Normal 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
|
||||||
@ -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___')
|
||||||
|
|||||||
@ -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')
|
||||||
|
|
||||||
|
|||||||
@ -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,
|
||||||
|
))
|
||||||
|
|||||||
@ -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():
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user