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):
|
||||
return wasm.OurTypeFloat64()
|
||||
|
||||
if isinstance(inp, ourlang.Struct):
|
||||
# Structs are passed as pointer
|
||||
if isinstance(inp, (ourlang.Struct, ourlang.OurTypeTuple, )):
|
||||
# Structs and tuples are passed as pointer
|
||||
# And pointers are i32
|
||||
return wasm.OurTypeInt32()
|
||||
|
||||
raise NotImplementedError(type_, inp)
|
||||
|
||||
# Operators that work for i32, i64, f32, f64
|
||||
OPERATOR_MAP = {
|
||||
'+': 'add',
|
||||
'-': 'sub',
|
||||
'*': 'mul',
|
||||
'==': 'eq',
|
||||
}
|
||||
|
||||
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))
|
||||
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)
|
||||
|
||||
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]), )
|
||||
|
||||
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 = [
|
||||
*_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:
|
||||
yield wasm.Statement('i32.const', str(inp.struct.alloc_size()))
|
||||
yield wasm.Statement('call', '$___new_reference___')
|
||||
|
||||
@ -59,6 +59,9 @@ class OurTypeInt64(OurType):
|
||||
def render(self) -> str:
|
||||
return 'i64'
|
||||
|
||||
def alloc_size(self) -> int:
|
||||
return 8
|
||||
|
||||
class OurTypeFloat32(OurType):
|
||||
"""
|
||||
The Float type, 32 bits wide
|
||||
@ -68,6 +71,9 @@ class OurTypeFloat32(OurType):
|
||||
def render(self) -> str:
|
||||
return 'f32'
|
||||
|
||||
def alloc_size(self) -> int:
|
||||
return 4
|
||||
|
||||
class OurTypeFloat64(OurType):
|
||||
"""
|
||||
The Float type, 64 bits wide
|
||||
@ -77,21 +83,44 @@ class OurTypeFloat64(OurType):
|
||||
def render(self) -> str:
|
||||
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):
|
||||
"""
|
||||
The tuple type, 64 bits wide
|
||||
"""
|
||||
__slots__ = ('members', )
|
||||
|
||||
members: List[OurType]
|
||||
members: List[TupleMember]
|
||||
|
||||
def __init__(self) -> None:
|
||||
self.members = []
|
||||
|
||||
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}, )'
|
||||
|
||||
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:
|
||||
"""
|
||||
An expression within a statement
|
||||
@ -257,6 +286,9 @@ class FunctionCall(Expression):
|
||||
if isinstance(self.function, StructConstructor):
|
||||
return f'{self.function.struct.name}({args})'
|
||||
|
||||
if isinstance(self.function, TupleConstructor):
|
||||
return f'({args}, )'
|
||||
|
||||
return f'{self.function.name}({args})'
|
||||
|
||||
class AccessStructMember(Expression):
|
||||
@ -281,21 +313,19 @@ class AccessTupleMember(Expression):
|
||||
"""
|
||||
Access a tuple member for reading of writing
|
||||
"""
|
||||
__slots__ = ('varref', 'member_idx', 'member_type', )
|
||||
__slots__ = ('varref', 'member', )
|
||||
|
||||
varref: VariableReference
|
||||
member_idx: int
|
||||
member_type: 'OurType'
|
||||
member: TupleMember
|
||||
|
||||
def __init__(self, varref: VariableReference, member_idx: int, member_type: 'OurType') -> None:
|
||||
super().__init__(member_type)
|
||||
def __init__(self, varref: VariableReference, member: TupleMember, ) -> None:
|
||||
super().__init__(member.type)
|
||||
|
||||
self.varref = varref
|
||||
self.member_idx = member_idx
|
||||
self.member_type = member_type
|
||||
self.member = member
|
||||
|
||||
def render(self) -> str:
|
||||
return f'{self.varref.render()}[{self.member_idx}]'
|
||||
return f'{self.varref.render()}[{self.member.idx}]'
|
||||
|
||||
class TupleCreation(Expression):
|
||||
"""
|
||||
@ -450,6 +480,26 @@ class StructConstructor(Function):
|
||||
|
||||
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:
|
||||
"""
|
||||
Represents a struct member
|
||||
@ -823,10 +873,14 @@ class OurVisitor:
|
||||
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')
|
||||
|
||||
result = TupleCreation(exp_type)
|
||||
result.members = [
|
||||
self.visit_Module_FunctionDef_expr(module, function, our_locals, arg_type, arg_node)
|
||||
for arg_node, arg_type in zip(node.elts, exp_type.members)
|
||||
tuple_constructor = TupleConstructor(exp_type)
|
||||
|
||||
func = module.functions[tuple_constructor.name]
|
||||
|
||||
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
|
||||
|
||||
@ -930,12 +984,11 @@ class OurVisitor:
|
||||
_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]
|
||||
if exp_type != member:
|
||||
_raise_static_error(node, f'Expected {exp_type.render()}, got {member.render()} instead')
|
||||
if exp_type != member.type:
|
||||
_raise_static_error(node, f'Expected {exp_type.render()}, got {member.type.render()} instead')
|
||||
|
||||
return AccessTupleMember(
|
||||
VariableReference(node_typ, node.value.id),
|
||||
node.slice.value.value,
|
||||
member,
|
||||
)
|
||||
|
||||
@ -997,11 +1050,23 @@ class OurVisitor:
|
||||
_raise_static_error(node, 'Must be load context')
|
||||
|
||||
result = OurTypeTuple()
|
||||
result.members = [
|
||||
self.visit_type(module, elt)
|
||||
for elt in node.elts
|
||||
]
|
||||
return result
|
||||
|
||||
offset = 0
|
||||
|
||||
for idx, elt in enumerate(node.elts):
|
||||
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')
|
||||
|
||||
|
||||
@ -15,6 +15,7 @@ import wasmer_compiler_cranelift
|
||||
import wasmtime
|
||||
|
||||
from py2wasm.utils import our_process, process
|
||||
from py2wasm.compiler import module
|
||||
|
||||
DASHES = '-' * 16
|
||||
|
||||
@ -69,23 +70,30 @@ class Suite:
|
||||
|
||||
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)
|
||||
|
||||
# Check if code formatting works
|
||||
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')
|
||||
|
||||
line_list = code_wat.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,
|
||||
))
|
||||
_write_numbered_lines(code_wat)
|
||||
|
||||
# Compile to assembly code
|
||||
code_wasm = wat2wasm(code_wat)
|
||||
|
||||
# Run assembly code
|
||||
return _run_pywasm3(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.load(mod)
|
||||
|
||||
sys.stderr.write(f'{DASHES} Memory (pre run) {DASHES}\n')
|
||||
_dump_memory(rtime.get_memory(0).tobytes())
|
||||
# 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())
|
||||
# sys.stderr.write(f'{DASHES} Memory (post run) {DASHES}\n')
|
||||
# _dump_memory(rtime.get_memory(0).tobytes())
|
||||
|
||||
return result
|
||||
|
||||
@ -190,3 +198,12 @@ def _dump_memory(mem):
|
||||
sys.stderr.write(f'{idx:08x} {line}\n')
|
||||
|
||||
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 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.parametrize('type_', ['i32', 'i64', 'f32', 'f64'])
|
||||
def test_arg(type_):
|
||||
@ -320,20 +334,21 @@ def helper(shape1: Rectangle, shape2: Rectangle) -> i32:
|
||||
assert [] == result.log_int32_list
|
||||
|
||||
@pytest.mark.integration_test
|
||||
def test_tuple_int():
|
||||
code_py = """
|
||||
@pytest.mark.parametrize('type_', ['i32', 'i64', 'f32', 'f64'])
|
||||
def test_tuple_simple(type_):
|
||||
code_py = f"""
|
||||
@exported
|
||||
def testEntry() -> i32:
|
||||
def testEntry() -> {type_}:
|
||||
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]
|
||||
"""
|
||||
|
||||
result = Suite(code_py, 'test_call').run_code()
|
||||
|
||||
assert 161 == result.returned_value
|
||||
assert [] == result.log_int32_list
|
||||
assert TYPE_MAP[type_] == type(result.returned_value)
|
||||
|
||||
@pytest.mark.integration_test
|
||||
def test_tuple_float():
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user