From a5c68065d7d3619d261cd0aefefa6cf0cb2c5890 Mon Sep 17 00:00:00 2001 From: "Johan B.W. de Vries" Date: Sat, 6 Aug 2022 13:44:11 +0200 Subject: [PATCH] More ideas about easy code generation --- phasm/stdlib/__init__.py | 0 phasm/stdlib/alloc.py | 129 +++++++++++++++++++++++++++++++++++++++ phasm/wasmeasy.py | 75 ++++++++++++++++++++++- pylintrc | 3 + 4 files changed, 206 insertions(+), 1 deletion(-) create mode 100644 phasm/stdlib/__init__.py create mode 100644 phasm/stdlib/alloc.py diff --git a/phasm/stdlib/__init__.py b/phasm/stdlib/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/phasm/stdlib/alloc.py b/phasm/stdlib/alloc.py new file mode 100644 index 0000000..02c2d48 --- /dev/null +++ b/phasm/stdlib/alloc.py @@ -0,0 +1,129 @@ +""" +stdlib: Memory allocation +""" +from phasm.wasmeasy import Generator, func_wrapper + +IDENTIFIER = 0xA1C0 + +ADR_IDENTIFIER = 0 +ADR_RESERVED0 = ADR_IDENTIFIER + 4 +ADR_FREE_BLOCK_PTR = ADR_RESERVED0 + 4 +ADR_UNALLOC_PTR = ADR_FREE_BLOCK_PTR + 4 + +@func_wrapper +def __init__(g: Generator) -> None: + """ + Initializes the memory so we can allocate it + """ + + # Check if the memory is already initialized + g.i32.const(0) + g.i32.load() + g.i32.const(IDENTIFIER) + g.i32.eq() + + with g.if_(): + # Already initialized, return without any changes + g.return_() + + # Store the first reserved i32 + g.i32.const(ADR_RESERVED0) + g.i32.const(0) + g.i32.store() + + # Store the pointer towards the first free block + # In this case, 0 since we haven't freed any blocks yet + g.i32.const(ADR_FREE_BLOCK_PTR) + g.i32.const(0) + g.i32.store() + + # Store the pointer towards the first unallocated block + # In this case the end of the stdlib.alloc header at the start + g.i32.const(ADR_UNALLOC_PTR) + g.i32.const(0x10) + g.i32.store() + + # Store that we've initialized the memory + g.i32.const(0) + g.i32.const(IDENTIFIER) + g.i32.store() + + +@func_wrapper +def __find_free_block__(g: Generator) -> None: + # Find out if we've freed any blocks at all so far + g.i32.const(ADR_FREE_BLOCK_PTR) + g.i32.load() + g.i32.const(0) + g.i32.eq() + + with g.if_(): + g.i32.const(0) + g.return_() + + g.unreachable() + +@func_wrapper +def __alloc__(g: Generator) -> None: + g.i32.const(ADR_IDENTIFIER) + g.i32.load() + g.i32.const(IDENTIFIER) + g.i32.ne() + with g.if_(): + g.unreachable() + +# def _generate_stdlib_alloc___alloc__(mod: ourlang.Module) -> wasm.Function: +# return wasm.Function( +# 'stdlib.alloc.__alloc__', +# 'stdlib.alloc.__alloc__', +# [ +# ('alloc_size', type_(mod.types['i32']), ), +# ], +# [ +# ('result', type_(mod.types['i32']), ), +# ], +# type_(mod.types['i32']), +# [ +# i32.const(0), +# i32.load(), +# i32.const(STDLIB_ALLOC__IDENTIFIER), +# i32.ne(), +# *wasmeasy.if_( +# wasm.Statement('unreachable'), +# ), +# +# wasm.Statement('local.get', '$alloc_size'), +# wasm.Statement('call', '$stdlib.alloc.__find_free_block__'), +# wasm.Statement('local.set', '$result'), +# +# # Check if there was a free block +# wasm.Statement('local.get', '$result'), +# i32.const(0), +# i32.eq(), +# *wasmeasy.if_( +# # Use unallocated space +# i32.const(STDLIB_ALLOC__ADR_UNALLOC_PTR), +# +# i32.const(STDLIB_ALLOC__ADR_UNALLOC_PTR), +# i32.load(), +# wasm.Statement('local.tee', '$result'), +# +# # Updated unalloc pointer (address already set on stack) +# i32.const(4), # Header size +# i32.add(), +# wasm.Statement('local.get', '$alloc_size'), +# i32.add(), +# i32.store('offset=0'), +# ), +# +# # Store block size +# wasm.Statement('local.get', '$result'), +# wasm.Statement('local.get', '$alloc_size'), +# i32.store('offset=0'), +# +# # Return address of the allocated bytes +# wasm.Statement('local.get', '$result'), +# i32.const(4), # Header size +# i32.add(), +# ], +# ) diff --git a/phasm/wasmeasy.py b/phasm/wasmeasy.py index 01c5d0c..3cf8ae1 100644 --- a/phasm/wasmeasy.py +++ b/phasm/wasmeasy.py @@ -1,7 +1,7 @@ """ Helper functions to quickly generate WASM code """ -from typing import List, Optional +from typing import Any, List, Optional import functools @@ -67,3 +67,76 @@ class Block: ] if_ = Block('if') + +class Generator_i32: + def __init__(self, generator: 'Generator') -> None: + self.generator = generator + + # 2.4.1. Numeric Instructions + self.eq = functools.partial(self.generator.add, 'i32.eq') + self.ne = functools.partial(self.generator.add, 'i32.ne') + + # 2.4.4. Memory Instructions + self.load = functools.partial(self.generator.add, 'i32.load') + self.store = functools.partial(self.generator.add, 'i32.store') + + def const(self, value: int, comment: Optional[str] = None) -> None: + self.generator.add('i32.const', f'0x{value:08x}', comment=comment) + +class GeneratorBlock: + def __init__(self, generator: 'Generator', name: str) -> None: + self.generator = generator + self.name = name + + def __enter__(self) -> None: + self.generator.add(self.name) + + def __exit__(self, exc_type: Any, exc_value: Any, traceback: Any) -> None: + if not exc_type: + self.generator.add('end') + +class Generator: + def __init__(self) -> None: + self.statements: List[wasm.Statement] = [] + + self.i32 = Generator_i32(self) + + # 2.4.5 Control Instructions + self.nop = functools.partial(self.add, 'nop') + self.unreachable = functools.partial(self.add, 'unreachable') + # block + # loop + self.if_ = functools.partial(GeneratorBlock, self, 'if') + # br + # br_if + # br_table + self.return_ = functools.partial(self.add, 'return') + # call + # call_indirect + + def add(self, name: str, *args: str, comment: Optional[str] = None) -> None: + self.statements.append(wasm.Statement(name, *args, comment=comment)) + +def func_wrapper(func: Any) -> wasm.Function: + assert func.__module__.startswith('phasm.') + + assert Generator is func.__annotations__['g'] + + params: Any = [] + + locals_: Any = [] + + assert None is func.__annotations__['return'] + return_type = wasm.WasmTypeNone() + + generator = Generator() + func(g=generator) + + return wasm.Function( + func.__module__[6:] + '.' + func.__name__, + func.__module__[6:] + '.' + func.__name__, + params, + locals_, + return_type, + generator.statements, + ) diff --git a/pylintrc b/pylintrc index bfa8c51..b38e5c0 100644 --- a/pylintrc +++ b/pylintrc @@ -1,2 +1,5 @@ [MASTER] disable=C0122,R0903,R0911,R0912,R0913,R0915,R1710,W0223 + +[stdlib] +good-names=g