Started on extracting values. Works for most, not for tuples yet.

This commit is contained in:
Johan B.W. de Vries 2023-11-11 15:53:43 +01:00
parent dff5feed86
commit 769eaaf243
6 changed files with 151 additions and 59 deletions

View File

@ -12,6 +12,7 @@ from . import wasm
from .stdlib import alloc as stdlib_alloc
from .stdlib import types as stdlib_types
from .runtime import calculate_alloc_size, calculate_member_offset
from .wasmgenerator import Generator as WasmGenerator
LOAD_STORE_TYPE_MAP = {
@ -173,7 +174,7 @@ def tuple_instantiation(wgn: WasmGenerator, inp: ourlang.TupleInstantiation) ->
wgn.add_statement('nop', comment=f'{tmp_var.name} := ({comment_elements})')
# Allocated the required amounts of bytes in memory
wgn.i32.const(_calculate_alloc_size(inp.type3, is_member=False))
wgn.i32.const(calculate_alloc_size(inp.type3, is_member=False))
wgn.call(stdlib_alloc.__alloc__)
wgn.local.set(tmp_var)
@ -198,7 +199,7 @@ def tuple_instantiation(wgn: WasmGenerator, inp: ourlang.TupleInstantiation) ->
wgn.add_statement(f'{mtyp}.store', 'offset=' + str(offset))
wgn.add_statement('nop', comment='POST')
offset += _calculate_alloc_size(exp_type3, is_member=True)
offset += calculate_alloc_size(exp_type3, is_member=True)
# Return the allocated address
wgn.local.get(tmp_var)
@ -435,7 +436,7 @@ def expression(wgn: WasmGenerator, inp: ourlang.Expression) -> None:
wgn.unreachable(comment='Out of bounds')
wgn.local.get(tmp_var)
wgn.i32.const(_calculate_alloc_size(el_type))
wgn.i32.const(calculate_alloc_size(el_type))
wgn.i32.mul()
wgn.i32.add()
@ -452,7 +453,7 @@ def expression(wgn: WasmGenerator, inp: ourlang.Expression) -> None:
offset = 0
for el_type in inp.varref.type3.args[0:inp.index.value]:
assert isinstance(el_type, type3types.Type3), type3types.TYPE3_ASSERTION_ERROR
offset += _calculate_alloc_size(el_type)
offset += calculate_alloc_size(el_type)
# This doubles as the out of bounds check
el_type = inp.varref.type3.args[inp.index.value]
@ -478,7 +479,7 @@ def expression(wgn: WasmGenerator, inp: ourlang.Expression) -> None:
mtyp = LOAD_STORE_TYPE_MAP[inp.struct_type3.members[inp.member].name]
expression(wgn, inp.varref)
wgn.add_statement(f'{mtyp}.load', 'offset=' + str(_calculate_member_offset(
wgn.add_statement(f'{mtyp}.load', 'offset=' + str(calculate_member_offset(
inp.struct_type3, inp.member
)))
return
@ -830,7 +831,7 @@ def _generate_struct_constructor(wgn: WasmGenerator, inp: ourlang.StructConstruc
tmp_var = wgn.temp_var_i32('struct_adr')
# Allocated the required amounts of bytes in memory
wgn.i32.const(_calculate_alloc_size(inp.struct_type3))
wgn.i32.const(calculate_alloc_size(inp.struct_type3))
wgn.call(stdlib_alloc.__alloc__)
wgn.local.set(tmp_var)
@ -844,60 +845,9 @@ def _generate_struct_constructor(wgn: WasmGenerator, inp: ourlang.StructConstruc
wgn.local.get(tmp_var)
wgn.add_statement('local.get', f'${memname}')
wgn.add_statement(f'{mtyp}.store', 'offset=' + str(_calculate_member_offset(
wgn.add_statement(f'{mtyp}.store', 'offset=' + str(calculate_member_offset(
inp.struct_type3, memname
)))
# Return the allocated address
wgn.local.get(tmp_var)
def _calculate_alloc_size(typ: Union[type3types.StructType3, type3types.Type3], is_member: bool = False) -> int:
if typ == type3types.u8:
return 4 # FIXME: We allocate 4 bytes for every u8 since you load them into an i32
if typ in (type3types.u32, type3types.i32, type3types.f32, ):
return 4
if typ in (type3types.u64, type3types.i64, type3types.f64, ):
return 8
if isinstance(typ, type3types.StructType3):
if is_member:
# Structs referred to by other structs or tuples are pointers
return 4
return sum(
_calculate_alloc_size(x)
for x in typ.members.values()
)
if isinstance(typ, type3types.AppliedType3):
if typ.base is type3types.tuple:
if is_member:
# tuples referred to by other structs or tuples are pointers
return 4
size = 0
for arg in typ.args:
assert not isinstance(arg, type3types.IntType3)
if isinstance(arg, type3types.PlaceholderForType):
assert not arg.resolve_as is None
arg = arg.resolve_as
size += _calculate_alloc_size(arg, is_member=True)
return size
raise NotImplementedError(_calculate_alloc_size, typ)
def _calculate_member_offset(struct_type3: type3types.StructType3, member: str) -> int:
result = 0
for mem, memtyp in struct_type3.members.items():
if member == mem:
return result
result += _calculate_alloc_size(memtyp, is_member=True)
raise Exception(f'{member} not in {struct_type3}')

64
phasm/runtime.py Normal file
View File

@ -0,0 +1,64 @@
from typing import Union
from .type3 import types as type3types
def calculate_alloc_size(typ: Union[type3types.StructType3, type3types.Type3], is_member: bool = False) -> int:
if typ == type3types.u8:
return 4 # FIXME: We allocate 4 bytes for every u8 since you load them into an i32
if typ in (type3types.u32, type3types.i32, type3types.f32, ):
return 4
if typ in (type3types.u64, type3types.i64, type3types.f64, ):
return 8
if isinstance(typ, type3types.StructType3):
if is_member:
# Structs referred to by other structs or tuples are pointers
return 4
return sum(
calculate_alloc_size(x)
for x in typ.members.values()
)
if isinstance(typ, type3types.AppliedType3):
if typ.base is type3types.static_array:
if is_member:
# tuples referred to by other structs or tuples are pointers
return 4
assert isinstance(typ.args[0], type3types.Type3)
assert isinstance(typ.args[1], type3types.IntType3)
return typ.args[1].value * calculate_alloc_size(typ.args[0], is_member=True)
if typ.base is type3types.tuple:
if is_member:
# tuples referred to by other structs or tuples are pointers
return 4
size = 0
for arg in typ.args:
assert not isinstance(arg, type3types.IntType3)
if isinstance(arg, type3types.PlaceholderForType):
assert not arg.resolve_as is None
arg = arg.resolve_as
size += calculate_alloc_size(arg, is_member=True)
return size
raise NotImplementedError(calculate_alloc_size, typ)
def calculate_member_offset(struct_type3: type3types.StructType3, member: str) -> int:
result = 0
for mem, memtyp in struct_type3.members.items():
if member == mem:
return result
result += calculate_alloc_size(memtyp, is_member=True)
raise Exception(f'{member} not in {struct_type3}')

View File

@ -1,6 +1,11 @@
from typing import Any
import struct
import sys
from phasm.codestyle import phasm_render
from phasm.type3 import types as type3types
from phasm.runtime import calculate_alloc_size
from . import runners
@ -76,6 +81,12 @@ class Suite:
result = SuiteResult()
result.returned_value = runner.call(func_name, *wasm_args)
result.returned_value = _load_memory_stored_returned_value(
runner,
func_name,
result.returned_value,
)
write_header(sys.stderr, 'Memory (post run)')
runner.interpreter_dump_memory(sys.stderr)
@ -83,3 +94,53 @@ class Suite:
def write_header(textio, msg: str) -> None:
textio.write(f'{DASHES} {msg.ljust(16)} {DASHES}\n')
def _load_memory_stored_returned_value(
runner: runners.RunnerBase,
func_name: str,
wasm_value: Any,
) -> Any:
ret_type3 = runner.phasm_ast.functions[func_name].returns_type3
if ret_type3 in (type3types.i32, type3types.i64):
assert isinstance(wasm_value, int), wasm_value
return wasm_value
if ret_type3 in (type3types.u32, type3types.u64):
assert isinstance(wasm_value, int), wasm_value
assert 0 <= wasm_value, 'TODO: Extract negative values'
return wasm_value
if ret_type3 in (type3types.f32, type3types.f64, ):
assert isinstance(wasm_value, float), wasm_value
return wasm_value
if ret_type3 is type3types.bytes:
assert isinstance(wasm_value, int), wasm_value
adr = wasm_value
sys.stderr.write(f'Reading 0x{adr:08x}\n')
read_bytes = runner.interpreter_read_memory(adr, 4)
bytes_len, = struct.unpack('<I', read_bytes)
adr += 4
sys.stderr.write(f'Reading 0x{adr:08x}\n')
return runner.interpreter_read_memory(adr, bytes_len)
if isinstance(ret_type3, type3types.AppliedType3):
if ret_type3.base is type3types.static_array:
assert isinstance(wasm_value, int), wasm_value
adr = wasm_value
assert ret_type3.args[0] is type3types.u64, 'Not Implemented yet'
assert isinstance(ret_type3.args[1], type3types.IntType3)
sa_len = ret_type3.args[1].value
alloc_size = calculate_alloc_size(ret_type3)
read_bytes = runner.interpreter_read_memory(adr, alloc_size)
print('read_bytes', read_bytes)
return struct.unpack(f'<{sa_len}q', read_bytes)
raise NotImplementedError(ret_type3, wasm_value)

View File

@ -166,7 +166,7 @@ class RunnerPywasm3(RunnerBase):
def interpreter_read_memory(self, offset: int, length: int) -> bytes:
memory = self.rtime.get_memory(0)
return memory[offset:length].tobytes()
return memory[offset:offset + length].tobytes()
def interpreter_dump_memory(self, textio: TextIO) -> None:
_dump_memory(textio, self.rtime.get_memory(0))

View File

@ -1,3 +1,19 @@
# runtime_extract_value
As a developer
I want to extract a $TYPE value
In order get the result I calculated with my phasm code
```py
@exported
def testEntry() -> $TYPE:
return $VAL0
```
```py
expect(VAL0)
```
# module_constant_def_ok
As a developer

View File

@ -44,6 +44,7 @@ def generate_assertions(settings, result_code):
result = []
locals_ = {
'VAL0': eval(settings['VAL0']),
'TYPE_NAME': settings['TYPE_NAME'],
'expect': functools.partial(generate_assertion_expect, result),
'expect_type_error': functools.partial(generate_assertion_expect_type_error, result),