diff --git a/phasm/compiler.py b/phasm/compiler.py index 778d11a..0b456a2 100644 --- a/phasm/compiler.py +++ b/phasm/compiler.py @@ -360,6 +360,8 @@ def module(inp: ourlang.Module) -> wasm.Module: result.functions = [ _generate____new_reference___(inp), + _generate_stdlib_alloc___init__(inp), + _generate_stdlib_alloc___alloc__(inp), _generate____access_bytes_index___(inp), ] + [ function(x) @@ -392,6 +394,77 @@ def _generate____new_reference___(mod: ourlang.Module) -> wasm.Function: ], ) +def _generate_stdlib_alloc___init__(mod: ourlang.Module) -> wasm.Function: + return wasm.Function( + 'stdlib.alloc.__init__', + 'stdlib.alloc.__init__', + [], + [], + wasm.WasmTypeNone(), + [ + wasm.Statement('i32.const', '0x00'), + wasm.Statement('i32.load'), + wasm.Statement('i32.const', '0xA1C0'), + wasm.Statement('i32.eq'), + wasm.Statement('if', comment='Already set up'), + wasm.Statement('return'), + wasm.Statement('end'), + + wasm.Statement('i32.const', '0x04', comment='Reserved'), + wasm.Statement('i32.const', '0x000000'), + wasm.Statement('i32.store'), + + wasm.Statement('i32.const', '0x08', comment='Address of next free block'), + wasm.Statement('i32.const', '0x000000'), + wasm.Statement('i32.store'), + + wasm.Statement('i32.const', '0x0C', comment='Reserved'), + wasm.Statement('i32.const', '0x000000'), + wasm.Statement('i32.store'), + + wasm.Statement('i32.const', '0x00', comment='Done setting up'), + wasm.Statement('i32.const', '0xA1C0'), + wasm.Statement('i32.store'), + ], + ) + +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']), + [ + wasm.Statement('i32.const', '0'), + wasm.Statement('i32.load'), + wasm.Statement('i32.const', '0xA1C0'), + wasm.Statement('i32.ne'), + wasm.Statement('if', comment='Failed to set up'), + wasm.Statement('unreachable'), + wasm.Statement('end'), + + # wasm.Statement('local.get', '$alloc_size'), + # wasm.Statement('call', '$stdlib.alloc.__find_space__'), + wasm.Statement('i32.const', '0x16', comment='STUB'), + wasm.Statement('local.set', '$result'), + + # Store block size + wasm.Statement('local.get', '$result'), + wasm.Statement('local.get', '$alloc_size'), + wasm.Statement('i32.store', 'offset=0'), + + # Return address of the allocated bytes + wasm.Statement('local.get', '$result'), + wasm.Statement('i32.const', '0x04'), + wasm.Statement('i32.add'), + ], + ) + def _generate____access_bytes_index___(mod: ourlang.Module) -> wasm.Function: return wasm.Function( '___access_bytes_index___', diff --git a/tests/integration/test_stdlib_alloc.py b/tests/integration/test_stdlib_alloc.py new file mode 100644 index 0000000..52ccfa4 --- /dev/null +++ b/tests/integration/test_stdlib_alloc.py @@ -0,0 +1,106 @@ +import sys + +import pytest + +import wasm3 + +from phasm.compiler import phasm_compile +from phasm.parser import phasm_parse + +from .helpers import DASHES, wat2wasm, _dump_memory, _write_numbered_lines + +def setup_interpreter(code_phasm): + phasm_module = phasm_parse(code_phasm) + wasm_module = phasm_compile(phasm_module) + code_wat = wasm_module.to_wat() + + sys.stderr.write(f'{DASHES} Assembly {DASHES}\n') + _write_numbered_lines(code_wat) + + code_wasm = wat2wasm(code_wat) + + env = wasm3.Environment() + mod = env.parse_module(code_wasm) + + rtime = env.new_runtime(1024 * 1024) + rtime.load(mod) + + return rtime + +@pytest.mark.integration_test +def test___init__(): + code_py = """ +@exported +def testEntry() -> u8: + return 13 +""" + + rtime = setup_interpreter(code_py) + + for idx in range(128): + rtime.get_memory(0)[idx] = idx + + sys.stderr.write(f'{DASHES} Memory (pre run) {DASHES}\n') + _dump_memory(rtime.get_memory(0)) + + rtime.find_function('stdlib.alloc.__init__')() + + sys.stderr.write(f'{DASHES} Memory (pre run) {DASHES}\n') + _dump_memory(rtime.get_memory(0)) + + memory = rtime.get_memory(0).tobytes() + + assert ( + b'\xC0\xA1\x00\x00' + b'\x00\x00\x00\x00' + b'\x00\x00\x00\x00' + b'\x00\x00\x00\x00' + b'\x10\x11\x12\x13' # Untouched because unused + ) == memory[0:20] + +@pytest.mark.integration_test +def test___alloc___no_init(): + code_py = """ +@exported +def testEntry() -> u8: + return 13 +""" + + rtime = setup_interpreter(code_py) + + for idx in range(128): + rtime.get_memory(0)[idx] = idx + + sys.stderr.write(f'{DASHES} Memory (pre run) {DASHES}\n') + _dump_memory(rtime.get_memory(0)) + + with pytest.raises(RuntimeError, match='unreachable executed'): + rtime.find_function('stdlib.alloc.__alloc__')(32) + +@pytest.mark.integration_test +def test___alloc___ok(): + code_py = """ +@exported +def testEntry() -> u8: + return 13 +""" + + rtime = setup_interpreter(code_py) + + for idx in range(128): + rtime.get_memory(0)[idx] = idx + + sys.stderr.write(f'{DASHES} Memory (pre run) {DASHES}\n') + _dump_memory(rtime.get_memory(0)) + + offset = rtime.find_function('stdlib.alloc.__init__')() + offset = rtime.find_function('stdlib.alloc.__alloc__')(32) + + sys.stderr.write(f'{DASHES} Memory (pre run) {DASHES}\n') + _dump_memory(rtime.get_memory(0)) + + memory = rtime.get_memory(0).tobytes() + + assert b'\xC0\xA1\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00' == memory[0:12] + + assert 0 == offset