Started on extracting values. Works for most, not for tuples yet.
This commit is contained in:
parent
dff5feed86
commit
769eaaf243
@ -12,6 +12,7 @@ from . import wasm
|
|||||||
|
|
||||||
from .stdlib import alloc as stdlib_alloc
|
from .stdlib import alloc as stdlib_alloc
|
||||||
from .stdlib import types as stdlib_types
|
from .stdlib import types as stdlib_types
|
||||||
|
from .runtime import calculate_alloc_size, calculate_member_offset
|
||||||
from .wasmgenerator import Generator as WasmGenerator
|
from .wasmgenerator import Generator as WasmGenerator
|
||||||
|
|
||||||
LOAD_STORE_TYPE_MAP = {
|
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})')
|
wgn.add_statement('nop', comment=f'{tmp_var.name} := ({comment_elements})')
|
||||||
|
|
||||||
# Allocated the required amounts of bytes in memory
|
# 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.call(stdlib_alloc.__alloc__)
|
||||||
wgn.local.set(tmp_var)
|
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(f'{mtyp}.store', 'offset=' + str(offset))
|
||||||
wgn.add_statement('nop', comment='POST')
|
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
|
# Return the allocated address
|
||||||
wgn.local.get(tmp_var)
|
wgn.local.get(tmp_var)
|
||||||
@ -435,7 +436,7 @@ def expression(wgn: WasmGenerator, inp: ourlang.Expression) -> None:
|
|||||||
wgn.unreachable(comment='Out of bounds')
|
wgn.unreachable(comment='Out of bounds')
|
||||||
|
|
||||||
wgn.local.get(tmp_var)
|
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.mul()
|
||||||
wgn.i32.add()
|
wgn.i32.add()
|
||||||
|
|
||||||
@ -452,7 +453,7 @@ def expression(wgn: WasmGenerator, inp: ourlang.Expression) -> None:
|
|||||||
offset = 0
|
offset = 0
|
||||||
for el_type in inp.varref.type3.args[0:inp.index.value]:
|
for el_type in inp.varref.type3.args[0:inp.index.value]:
|
||||||
assert isinstance(el_type, type3types.Type3), type3types.TYPE3_ASSERTION_ERROR
|
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
|
# This doubles as the out of bounds check
|
||||||
el_type = inp.varref.type3.args[inp.index.value]
|
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]
|
mtyp = LOAD_STORE_TYPE_MAP[inp.struct_type3.members[inp.member].name]
|
||||||
|
|
||||||
expression(wgn, inp.varref)
|
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
|
inp.struct_type3, inp.member
|
||||||
)))
|
)))
|
||||||
return
|
return
|
||||||
@ -830,7 +831,7 @@ def _generate_struct_constructor(wgn: WasmGenerator, inp: ourlang.StructConstruc
|
|||||||
tmp_var = wgn.temp_var_i32('struct_adr')
|
tmp_var = wgn.temp_var_i32('struct_adr')
|
||||||
|
|
||||||
# Allocated the required amounts of bytes in memory
|
# 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.call(stdlib_alloc.__alloc__)
|
||||||
wgn.local.set(tmp_var)
|
wgn.local.set(tmp_var)
|
||||||
|
|
||||||
@ -844,60 +845,9 @@ def _generate_struct_constructor(wgn: WasmGenerator, inp: ourlang.StructConstruc
|
|||||||
|
|
||||||
wgn.local.get(tmp_var)
|
wgn.local.get(tmp_var)
|
||||||
wgn.add_statement('local.get', f'${memname}')
|
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
|
inp.struct_type3, memname
|
||||||
)))
|
)))
|
||||||
|
|
||||||
# Return the allocated address
|
# Return the allocated address
|
||||||
wgn.local.get(tmp_var)
|
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
64
phasm/runtime.py
Normal 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}')
|
||||||
@ -1,6 +1,11 @@
|
|||||||
|
from typing import Any
|
||||||
|
|
||||||
|
import struct
|
||||||
import sys
|
import sys
|
||||||
|
|
||||||
from phasm.codestyle import phasm_render
|
from phasm.codestyle import phasm_render
|
||||||
|
from phasm.type3 import types as type3types
|
||||||
|
from phasm.runtime import calculate_alloc_size
|
||||||
|
|
||||||
from . import runners
|
from . import runners
|
||||||
|
|
||||||
@ -76,6 +81,12 @@ class Suite:
|
|||||||
result = SuiteResult()
|
result = SuiteResult()
|
||||||
result.returned_value = runner.call(func_name, *wasm_args)
|
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)')
|
write_header(sys.stderr, 'Memory (post run)')
|
||||||
runner.interpreter_dump_memory(sys.stderr)
|
runner.interpreter_dump_memory(sys.stderr)
|
||||||
|
|
||||||
@ -83,3 +94,53 @@ class Suite:
|
|||||||
|
|
||||||
def write_header(textio, msg: str) -> None:
|
def write_header(textio, msg: str) -> None:
|
||||||
textio.write(f'{DASHES} {msg.ljust(16)} {DASHES}\n')
|
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)
|
||||||
|
|||||||
@ -166,7 +166,7 @@ class RunnerPywasm3(RunnerBase):
|
|||||||
|
|
||||||
def interpreter_read_memory(self, offset: int, length: int) -> bytes:
|
def interpreter_read_memory(self, offset: int, length: int) -> bytes:
|
||||||
memory = self.rtime.get_memory(0)
|
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:
|
def interpreter_dump_memory(self, textio: TextIO) -> None:
|
||||||
_dump_memory(textio, self.rtime.get_memory(0))
|
_dump_memory(textio, self.rtime.get_memory(0))
|
||||||
|
|||||||
@ -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
|
# module_constant_def_ok
|
||||||
|
|
||||||
As a developer
|
As a developer
|
||||||
|
|||||||
@ -44,6 +44,7 @@ def generate_assertions(settings, result_code):
|
|||||||
result = []
|
result = []
|
||||||
|
|
||||||
locals_ = {
|
locals_ = {
|
||||||
|
'VAL0': eval(settings['VAL0']),
|
||||||
'TYPE_NAME': settings['TYPE_NAME'],
|
'TYPE_NAME': settings['TYPE_NAME'],
|
||||||
'expect': functools.partial(generate_assertion_expect, result),
|
'expect': functools.partial(generate_assertion_expect, result),
|
||||||
'expect_type_error': functools.partial(generate_assertion_expect_type_error, result),
|
'expect_type_error': functools.partial(generate_assertion_expect_type_error, result),
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user