MVP #1

Merged
jbwdevries merged 73 commits from idea_crc32 into master 2022-08-21 12:59:21 +00:00
5 changed files with 117 additions and 16 deletions
Showing only changes of commit b468ffa780 - Show all commits

View File

@ -29,6 +29,18 @@ class Visitor:
module = wasm.Module()
module.functions.append(wasm.Function(
'___new_reference___',
False,
[
('alloc_size', 'i32'),
],
'i32',
[
wasm.Statement('i32.const', '4', comment='Stub')
]
))
# Do a check first for all function definitions
# to get their types. Otherwise you cannot call
# a method that you haven't defined just yet,
@ -165,6 +177,7 @@ class Visitor:
members: List[wasm.ClassMember] = []
offset = 0
for stmt in node.body:
if not isinstance(stmt, ast.AnnAssign):
raise NotImplementedError
@ -189,9 +202,12 @@ class Visitor:
default = wasm.Constant(stmt.value.value)
members.append(wasm.ClassMember(
stmt.target.id, stmt.annotation.id, default
))
member = wasm.ClassMember(
stmt.target.id, stmt.annotation.id, offset, default
)
members.append(member)
offset += member.alloc_size()
return wasm.Class(node.name, members)
@ -294,7 +310,7 @@ class Visitor:
return self.visit_Name(wlocals, exp_type, node)
if isinstance(node, ast.Attribute):
return self.visit_Attribute(wlocals, exp_type, node)
return self.visit_Attribute(module, wlocals, exp_type, node)
raise NotImplementedError(node)
@ -419,7 +435,7 @@ class Visitor:
wlocals: WLocals,
exp_type: str,
node: ast.Call,
func: wasm.Class,
cls: wasm.Class,
) -> StatementGenerator:
"""
Visits a Call node as (part of) an expression
@ -429,9 +445,20 @@ class Visitor:
# TODO: malloc call
yield wasm.Statement('i32.const 0')
yield wasm.Statement('i32.load')
yield wasm.Statement('i32.const', str(cls.alloc_size()))
yield wasm.Statement('call', '$___new_reference___')
yield wasm.Statement('local.set', '$___new_reference___addr')
for member, arg in zip(cls.members, node.args):
if not isinstance(arg, ast.Constant):
raise NotImplementedError
yield wasm.Statement('local.get', '$___new_reference___addr')
yield wasm.Statement(f'{member.type}.const', str(arg.value))
yield wasm.Statement(f'{member.type}.store', 'offset=' + str(member.offset))
yield wasm.Statement('local.get', '$___new_reference___addr')
def visit_Call_func(
self,
@ -500,7 +527,13 @@ class Visitor:
raise NotImplementedError(exp_type)
def visit_Attribute(self, wlocals: WLocals, exp_type: str, node: ast.Attribute) -> StatementGenerator:
def visit_Attribute(
self,
module: wasm.Module,
wlocals: WLocals,
exp_type: str,
node: ast.Attribute,
) -> StatementGenerator:
"""
Visits an Attribute node as (part of) an expression
"""
@ -511,8 +544,26 @@ class Visitor:
if not isinstance(node.ctx, ast.Load):
raise NotImplementedError
cls_list = [
x
for x in module.classes
if x.name == 'Rectangle' # TODO: STUB, since we can't acces the type properly
]
assert len(cls_list) == 1
cls = cls_list[0]
member_list = [
x
for x in cls.members
if x.name == node.attr
]
assert len(member_list) == 1
member = member_list[0]
yield wasm.Statement('local.get', '$' + node.value.id)
yield wasm.Statement(exp_type + '.load', 'offset=0') # TODO: Calculate offset based on set struct
yield wasm.Statement(exp_type + '.load', 'offset=' + str(member.offset))
def visit_Name(self, wlocals: WLocals, exp_type: str, node: ast.Name) -> StatementGenerator:
"""

View File

@ -38,15 +38,19 @@ class Statement:
"""
Represents a Web Assembly statement
"""
def __init__(self, name: str, *args: str):
def __init__(self, name: str, *args: str, comment: Optional[str] = None):
self.name = name
self.args = args
self.comment = comment
def generate(self) -> str:
"""
Generates the text version
"""
return '{} {}'.format(self.name, ' '.join(self.args))
args = ' '.join(self.args)
comment = f' ;; {self.comment}' if self.comment else ''
return f'{self.name} {args}{comment}'
class Function:
"""
@ -78,7 +82,9 @@ class Function:
if self.result:
header += ' (result {})'.format(self.result)
return '(func {}\n {})'.format(
header += ' (local $___new_reference___addr i32)'
return '(func {}\n {}\n )'.format(
header,
'\n '.join(x.generate() for x in self.statements),
)
@ -94,11 +100,20 @@ class ClassMember:
"""
Represents a Web Assembly class member
"""
def __init__(self, name: str, type_: str, default: Optional[Constant]) -> None:
def __init__(self, name: str, type_: str, offset: int, default: Optional[Constant]) -> None:
self.name = name
self.type = type_
self.offset = offset
self.default = default
def alloc_size(self) -> int:
SIZE_MAP = {
'i32': 4,
'i64': 4,
}
return SIZE_MAP[self.type]
class Class:
"""
Represents a Web Assembly class
@ -107,6 +122,9 @@ class Class:
self.name = name
self.members = members
def alloc_size(self) -> int:
return sum(x.alloc_size() for x in self.members)
class Module:
"""
Represents a Web Assembly module
@ -121,7 +139,7 @@ class Module:
Generates the text version
"""
return '(module\n (memory 1)\n (data (memory 0) (i32.const 0) {})\n {}\n {})\n'.format(
'"\\\\04\\\\00\\\\00\\\\00"',
'"\\04\\00\\00\\00"',
'\n '.join(x.generate() for x in self.imports),
'\n '.join(x.generate() for x in self.functions),
)

View File

@ -31,6 +31,7 @@ def wat2wasm(code_wat):
class SuiteResult:
def __init__(self):
self.log_int32_list = []
self.returned_value = None
def callback_log_int32(self, store, value):
del store # auto passed by pywasm
@ -58,6 +59,10 @@ class Suite:
"""
code_wat = process(self.code_py, self.test_name)
dashes = '-' * 16
sys.stderr.write(f'{dashes} Assembly {dashes}\n')
line_list = code_wat.split('\n')
line_no_width = len(str(len(line_list)))
for line_no, line_txt in enumerate(line_list):
@ -72,6 +77,33 @@ class Suite:
result = SuiteResult()
runtime = Runtime(module, result.make_imports(), {})
sys.stderr.write(f'{dashes} Memory (pre run) {dashes}\n')
_dump_memory(runtime.store.mems[0].data)
result.returned_value = runtime.exec('testEntry', args)
sys.stderr.write(f'{dashes} Memory (post run) {dashes}\n')
_dump_memory(runtime.store.mems[0].data)
return result
def _dump_memory(mem):
line_width = 16
prev_line = None
skip = False
for idx in range(0, len(mem), line_width):
line = ''
for idx2 in range(0, line_width):
line += f'{mem[idx + idx2]:02X}'
if idx2 % 2 == 1:
line += ' '
if prev_line == line:
if not skip:
sys.stderr.write('**\n')
skip = True
else:
sys.stderr.write(f'{idx:08x} {line}\n')
prev_line = line

View File

@ -28,4 +28,3 @@ def testEntry() -> i32:
result = Suite(code_py, 'test_fib').run_code()
assert 102334155 == result.returned_value
assert False

View File

@ -236,6 +236,7 @@ def helper(left: i32, right: i32) -> i32:
assert [] == result.log_int32_list
@pytest.mark.integration_test
@pytest.mark.skip('Not yet implemented')
def test_assign():
code_py = """
@ -269,5 +270,5 @@ def helper(shape: Rectangle) -> i32:
result = Suite(code_py, 'test_call').run_code()
assert 100 == result.returned_value
assert 252 == result.returned_value
assert [] == result.log_int32_list