From 453c2865a8a74bdc717e2233f05dc24c5a06113a Mon Sep 17 00:00:00 2001 From: "Johan B.W. de Vries" Date: Sun, 19 Jun 2022 16:09:06 +0200 Subject: [PATCH] Structs --- py2wasm/compiler.py | 94 ++++++++++++++++++++++++++++++++++++++++----- py2wasm/ourlang.py | 22 +++++++++-- py2wasm/python.py | 2 +- py2wasm/wasm.py | 17 +++++++- 4 files changed, 120 insertions(+), 15 deletions(-) diff --git a/py2wasm/compiler.py b/py2wasm/compiler.py index 355e4b4..eba158c 100644 --- a/py2wasm/compiler.py +++ b/py2wasm/compiler.py @@ -21,6 +21,11 @@ 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 + # And pointers are i32 + return wasm.OurTypeInt32() + raise NotImplementedError(type_, inp) OPERATOR_MAP = { @@ -89,6 +94,19 @@ def expression(inp: ourlang.Expression) -> Statements: yield wasm.Statement('call', '${}'.format(inp.function.name)) return + if isinstance(inp, ourlang.AccessStructMember): + # 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: @@ -125,6 +143,21 @@ 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): + statements = [ + *_generate_struct_constructor(inp) + ] + locals_ = [ + ('___new_reference___addr', wasm.OurTypeInt32(), ), + ] + else: + statements = [ + x + for y in inp.statements + for x in statement(y) + ] + locals_ = [] # TODO + return wasm.Function( inp.name, inp.exported, @@ -132,20 +165,63 @@ def function(inp: ourlang.Function) -> wasm.Function: function_argument(x) for x in inp.posonlyargs ], - [], # TODO + locals_, type_(inp.returns), - [ - x - for y in inp.statements - for x in statement(y) - ] + statements ) def module(inp: ourlang.Module) -> wasm.Module: result = wasm.Module() - result.functions = [*map( - function, inp.functions.values(), - )] + result.functions = [ + _generate_allocator(inp), + ] + [ + function(x) + for x in inp.functions.values() + ] return result + +def _generate_allocator(mod: ourlang.Module) -> wasm.Function: + return wasm.Function( + '___new_reference___', + False, + [ + ('alloc_size', type_(mod.types['i32']), ), + ], + [ + ('result', type_(mod.types['i32']), ), + ], + type_(mod.types['i32']), + [ + wasm.Statement('i32.const', '0'), + wasm.Statement('i32.const', '0'), + wasm.Statement('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'), + wasm.Statement('local.get', '$result'), + ], + ) + +def _generate_struct_constructor(inp: ourlang.StructConstructor) -> Statements: + yield wasm.Statement('i32.const', str(inp.struct.alloc_size())) + yield wasm.Statement('call', '$___new_reference___') + + yield wasm.Statement('local.set', '$___new_reference___addr') + + for member in inp.struct.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'${member.name}') + yield wasm.Statement(f'{member.type.render()}.store', 'offset=' + str(member.offset)) + + yield wasm.Statement('local.get', '$___new_reference___addr') diff --git a/py2wasm/ourlang.py b/py2wasm/ourlang.py index 16661c8..0d53280 100644 --- a/py2wasm/ourlang.py +++ b/py2wasm/ourlang.py @@ -217,7 +217,9 @@ class UnaryOp(Expression): operator: str right: Expression - def __init__(self, operator: str, right: Expression) -> None: + def __init__(self, type_: OurType, operator: str, right: Expression) -> None: + super().__init__(type_) + self.operator = operator self.right = right @@ -234,6 +236,8 @@ class FunctionCall(Expression): arguments: List[Expression] def __init__(self, function: 'Function') -> None: + super().__init__(function.returns) + self.function = function self.arguments = [] @@ -258,6 +262,8 @@ class AccessStructMember(Expression): member: 'StructMember' def __init__(self, varref: VariableReference, member: 'StructMember') -> None: + super().__init__(member.type) + self.varref = varref self.member = member @@ -275,6 +281,8 @@ class AccessTupleMember(Expression): member_type: 'OurType' def __init__(self, varref: VariableReference, member_idx: int, member_type: 'OurType') -> None: + super().__init__(member_type) + self.varref = varref self.member_idx = member_idx self.member_type = member_type @@ -286,13 +294,14 @@ class TupleCreation(Expression): """ Create a tuple instance """ - __slots__ = ('type', 'members', ) + __slots__ = ('members', ) type: OurTypeTuple members: List[Expression] def __init__(self, type_: OurTypeTuple) -> None: - self.type = type_ + super().__init__(type_) + self.members = [] def render(self) -> str: @@ -488,6 +497,12 @@ class Struct(OurType): return result + def alloc_size(self) -> int: + return sum( + x.type.alloc_size() + for x in self.members + ) + class Module: """ A module is a file and consists of functions @@ -747,6 +762,7 @@ class OurVisitor: raise NotImplementedError(f'Operator {node.op}') return UnaryOp( + exp_type, operator, self.visit_Module_FunctionDef_expr(module, function, our_locals, exp_type, node.operand), ) diff --git a/py2wasm/python.py b/py2wasm/python.py index 91cef95..18d6869 100644 --- a/py2wasm/python.py +++ b/py2wasm/python.py @@ -199,7 +199,7 @@ class Visitor: params.append((arg.arg, self._get_type(arg.annotation), )) locals_: List[wasm.Param] = [ - ('___new_reference___addr', self._get_type('i32')), # For the ___new_reference__ method + ('___new_reference___addr', self._get_type('i32')), # For the ___new_reference___ method ] return wasm.Function(node.name, exported, params, locals_, result, []) diff --git a/py2wasm/wasm.py b/py2wasm/wasm.py index 09609e6..49612cd 100644 --- a/py2wasm/wasm.py +++ b/py2wasm/wasm.py @@ -232,6 +232,18 @@ class Function: '\n '.join(x.generate() for x in self.statements), ) +class ModuleMemory: + def __init__(self, data: bytes = b'') -> None: + self.data = data + + def generate(self) -> str: + return '(memory 1)\n (data (memory 0) (i32.const 0) "{}")\n'.format( + ''.join( + f'\\{x:02x}' + for x in self.data + ) + ) + class Module: """ Represents a Web Assembly module @@ -239,13 +251,14 @@ class Module: def __init__(self) -> None: self.imports: List[Import] = [] self.functions: List[Function] = [] + self.memory = ModuleMemory(b'\x04') # For ___new_reference___ def generate(self) -> str: """ Generates the text version """ - return '(module\n (memory 1)\n (data (memory 0) (i32.const 0) {})\n {}\n {})\n'.format( - '"\\04\\00\\00\\00"', + return '(module\n {}\n {}\n {})\n'.format( + self.memory.generate(), '\n '.join(x.generate() for x in self.imports), '\n '.join(x.generate() for x in self.functions), )