Skip to content

Commit faff486

Browse files
committed
Add wasm table copy.
1 parent 17685d2 commit faff486

7 files changed

Lines changed: 110 additions & 39 deletions

File tree

ppci/wasm/execution/_base_instance.py

Lines changed: 54 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -8,8 +8,6 @@
88
class ModuleInstance(abc.ABC):
99
"""Web assembly module instance"""
1010

11-
""" Instantiated module """
12-
1311
def __init__(self):
1412
self.exports = Exports()
1513
self._tables = []
@@ -23,6 +21,9 @@ def invoke(self, name, *args):
2321

2422
def table_grow(self, table_idx: int, val: int, size: int) -> int:
2523
logger.debug(f"table_grow({table_idx=}, {size=}, {val=})")
24+
if size < 0:
25+
# When i32 is large, it's signed value is negative.
26+
return -1
2627
table = self._tables[table_idx]
2728
return table.grow(val, size)
2829

@@ -31,17 +32,45 @@ def table_size(self, table_idx: int) -> int:
3132
table = self._tables[table_idx]
3233
return table.size()
3334

34-
def table_init(self, table_idx: int) -> None:
35-
logger.debug(f"table_init({table_idx=})")
36-
# _table = self._tables[table_idx]
37-
raise NotImplementedError()
38-
return 0
39-
40-
def table_copy(self, table_idx: int) -> None:
41-
logger.debug(f"table_copy({table_idx=})")
42-
# _table = self._tables[table_idx]
43-
raise NotImplementedError()
44-
return 0
35+
def table_init(
36+
self, table_idx: int, elem_idx: int, d: int, s: int, n: int
37+
) -> None:
38+
logger.debug(
39+
f"table_init({table_idx=}, {elem_idx=}, {d=}, {s=}, {n=})"
40+
)
41+
table = self._tables[table_idx]
42+
elem = self._elems[elem_idx]
43+
if s + n > elem.size():
44+
raise ValueError("s + n > elem.size")
45+
if d + n > table.size():
46+
raise ValueError("d + n > table.size")
47+
for index in range(n):
48+
obj = elem.get_item(s + index)
49+
table.set_item(d + index, obj)
50+
51+
def table_copy(
52+
self, x_table_idx: int, y_table_idx: int, d: int, s: int, n: int
53+
) -> None:
54+
logger.debug(
55+
f"table_copy({x_table_idx=}, {y_table_idx=}, {d=}, {s=}, {n=})"
56+
)
57+
x_table = self._tables[x_table_idx]
58+
y_table = self._tables[y_table_idx]
59+
if d + n > x_table.size():
60+
raise ValueError("d + n > x_table.size")
61+
if s + n > y_table.size():
62+
raise ValueError("s + n > y_table.size")
63+
64+
# Regions may overlap, so check dest and source:
65+
if d <= s:
66+
for index in range(n):
67+
obj = y_table.get_item(s + index)
68+
x_table.set_item(d + index, obj)
69+
else:
70+
for index2 in range(n):
71+
index = n - 1 - index2
72+
obj = y_table.get_item(s + index)
73+
x_table.set_item(d + index, obj)
4574

4675
def table_fill(self, table_idx: int, i: int, val: int, n: int) -> None:
4776
logger.debug(f"table_fill({table_idx=}, {i=}, {val=}, {n=})")
@@ -50,6 +79,9 @@ def table_fill(self, table_idx: int, i: int, val: int, n: int) -> None:
5079
index = i + x
5180
table.set_item(index, val)
5281

82+
def elem_drop(self, elem_idx: int) -> None:
83+
pass
84+
5385
def memory_grow(self, memory_idx: int, amount: int) -> int:
5486
"""Grow memory and return the old size"""
5587
memory = self._memories[memory_idx]
@@ -99,7 +131,7 @@ def set_table_ptr(self, index, table):
99131
raise NotImplementedError()
100132

101133
@abc.abstractmethod
102-
def create_elem(self):
134+
def create_elem(self, index: int, size: int):
103135
raise NotImplementedError()
104136

105137
def eval_expression(self, expr):
@@ -145,7 +177,9 @@ def load_tables(self, wasm_module):
145177
self.set_table_ptr(index, table)
146178
elif isinstance(definition, components.Elem):
147179
elems.append(definition)
148-
self.create_elem()
180+
index = len(self._elems)
181+
size = len(definition.refs)
182+
self._elems.append(self.create_elem(index, size))
149183

150184
for index, elem in enumerate(elems):
151185
if elem.mode:
@@ -191,8 +225,11 @@ def populate_exports(self, module):
191225
class ElemInstance(abc.ABC):
192226
"""Runtime element instance"""
193227

194-
def __init__(self):
195-
pass
228+
def __init__(self, size: int):
229+
self._size = size
230+
231+
def size(self) -> int:
232+
return self._size
196233

197234
@abc.abstractmethod
198235
def get_item(self, index: int):

ppci/wasm/execution/_native_instance.py

Lines changed: 4 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -113,13 +113,10 @@ def set_table_ptr(self, index, table):
113113
table_addr = table._meta_page.addr
114114
self._code_module._data_page.write_fmt(table_ptr, "Q", table_addr)
115115

116-
def create_elem(self):
117-
index = len(self._elems)
116+
def create_elem(self, index: int, size: int) -> "NativeElemInstance":
118117
name = self._wasm_info.elem_names[index]
119118
offset = self._code_module.get_symbol_offset(name)
120-
self._elems.append(
121-
NativeElemInstance(offset, self._code_module._data_page)
122-
)
119+
return NativeElemInstance(offset, self._code_module._data_page, size)
123120

124121
def set_mem_base_ptr(self, base_addr):
125122
"""Set memory base address"""
@@ -256,8 +253,8 @@ def get_item(self, index: int):
256253

257254

258255
class NativeElemInstance(ElemInstance):
259-
def __init__(self, offset, page):
260-
super().__init__()
256+
def __init__(self, offset, page, size):
257+
super().__init__(size)
261258
self._offset = offset
262259
self._page = page
263260

ppci/wasm/execution/_python_instance.py

Lines changed: 8 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -76,6 +76,7 @@ def __init__(self, py_module, imports):
7676
imports["wasm_rt_table_init"] = self.table_init
7777
imports["wasm_rt_table_copy"] = self.table_copy
7878
imports["wasm_rt_table_fill"] = self.table_fill
79+
imports["wasm_rt_elem_drop"] = self.elem_drop
7980

8081
imports["wasm_rt_memory_grow"] = self.memory_grow
8182
imports["wasm_rt_memory_size"] = self.memory_size
@@ -157,11 +158,10 @@ def set_table_ptr(self, index, table):
157158
addr = getattr(self._py_module, name)
158159
self.store_ptr(addr, table._get_ptr())
159160

160-
def create_elem(self) -> "PythonElemInstance":
161-
index = len(self._elems)
161+
def create_elem(self, index: int, size: int) -> "PythonElemInstance":
162162
name = self._wasm_info.elem_names[index]
163163
addr = getattr(self._py_module, name)
164-
self._elems.append(PythonElemInstance(addr, self))
164+
return PythonElemInstance(addr, self, size)
165165

166166
def get_func_by_index(self, index: int):
167167
exported_name = self._wasm_info.function_names[index]
@@ -304,6 +304,8 @@ def grow(self, val, count: int) -> int:
304304
ptr_size = 4
305305
old_size = self._instance.load_i32(self._addr + ptr_size)
306306
new_size = old_size + count
307+
if new_size > 0xFFFF_FFFF:
308+
return -1
307309
if self._max_size is not None and new_size > self._max_size:
308310
return -1
309311
old_addr = self._instance.load_ptr(self._addr)
@@ -327,12 +329,12 @@ def set_item(self, index: int, value):
327329
def get_item(self, index: int):
328330
base_ptr = self._get_ptr()
329331
data_ptr = self._instance.load_ptr(base_ptr) + 4 * index
330-
self._instance.load_ptr(data_ptr)
332+
return self._instance.load_ptr(data_ptr)
331333

332334

333335
class PythonElemInstance(ElemInstance):
334-
def __init__(self, addr: int, instance):
335-
super().__init__()
336+
def __init__(self, addr: int, instance, size):
337+
super().__init__(size)
336338
self._addr = addr
337339
self._instance = instance
338340

ppci/wasm/opcodes.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -74,7 +74,7 @@ class Space(enum.Enum):
7474
(
7575
"table.init",
7676
(0xFC, 12),
77-
(ArgType.ELEMIDX, ArgType.TABLEIDX),
77+
(ArgType.TABLEIDX, ArgType.ELEMIDX),
7878
("i32", "i32", "i32"),
7979
(),
8080
),

ppci/wasm/text/parser.py

Lines changed: 36 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,7 @@ def load_tuple(module, t):
3232
if not isinstance(t, tuple):
3333
raise TypeError(f"t must be tuple, not {type(t)}")
3434

35-
loader = WatTupleLoader(module)
35+
loader = WatParser(module)
3636

3737
if any(isinstance(e, components.Definition) for e in t):
3838
if not all(isinstance(e, components.Definition) for e in t):
@@ -105,18 +105,27 @@ def load_s_expr(module, s):
105105

106106

107107
def load_from_s_tokens(module, tokens):
108-
loader = WatTupleLoader(module)
108+
loader = WatParser(module)
109109
loader.parse_module(tokens)
110110

111111

112-
class WatTupleLoader(RecursiveDescentParser):
112+
class WatParser(RecursiveDescentParser):
113113
"""WebAssembly text format parser."""
114114

115115
def __init__(self, module):
116116
super().__init__()
117117
self.module = module
118118
self.definitions = defaultdict(list)
119119
self._type_hash = {} # (params, results) -> ref
120+
self.counters = {
121+
"type": 0,
122+
"func": 0,
123+
"table": 0,
124+
"memory": 0,
125+
"global": 0,
126+
"elem": 0,
127+
"data": 0,
128+
}
120129

121130
self.resolve_backlog = []
122131
self.func_backlog = []
@@ -174,12 +183,23 @@ def resolve_references(self):
174183
"elem": {},
175184
"data": {},
176185
}
186+
counters = {
187+
"type": 0,
188+
"func": 0,
189+
"table": 0,
190+
"memory": 0,
191+
"global": 0,
192+
"elem": 0,
193+
"data": 0,
194+
}
177195

178196
# TODO: maybe this is not needed at this point?
179197
# Fill imports and other objects:
180198
for d in self.definitions["import"]:
181199
id_map = id_maps[d.kind]
182-
id_map[d.id] = len(id_map)
200+
assert d.id not in id_map
201+
id_map[d.id] = counters[d.kind]
202+
counters[d.kind] += 1
183203

184204
for space in [
185205
"type",
@@ -192,7 +212,9 @@ def resolve_references(self):
192212
]:
193213
id_map = id_maps[space]
194214
for d in self.definitions[space]:
195-
id_map[d.id] = len(id_map)
215+
assert d.id not in id_map
216+
id_map[d.id] = counters[space]
217+
counters[space] += 1
196218

197219
# resolve any unresolved items:
198220
for item in self.resolve_backlog:
@@ -231,7 +253,8 @@ def add_definition(self, definition):
231253
logger.debug(f"Parsed {definition} {nr}")
232254

233255
def gen_id(self, kind):
234-
id = len(self.definitions[kind])
256+
id = self.counters[kind]
257+
self.counters[kind] += 1
235258
return f"${id}"
236259

237260
# Section types:
@@ -790,6 +813,13 @@ def _gather_opcode_arguments(self, opcode):
790813
type_ref = self._parse_type_use()
791814
args = (type_ref, table_ref)
792815
# TODO: compare unbound func signature with type?
816+
elif opcode == "table.init":
817+
if is_ref(self.look_ahead(1).val):
818+
table_ref = self._parse_ref("table")
819+
else:
820+
table_ref = self._make_ref("table", 0)
821+
elem_ref = self._parse_ref("elem")
822+
args = (table_ref, elem_ref)
793823
else:
794824
operands = OPERANDS[opcode]
795825
args = self._parse_operands(operands)

ppci/wasm/wasm2ppci.py

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -666,7 +666,7 @@ def generate_function(self, ppci_function, signature, wasm_function):
666666
local_id, local_typ = local
667667
local_id = i if local_id is None else local_id
668668
ir_typ = self.get_ir_type(local_typ)
669-
size = ir_typ.size
669+
size = self.get_type_size(local_typ)
670670
alignment = size
671671
alloc = self.emit(ir.Alloc("alloc{}".format(i), size, alignment))
672672
addr = self.emit(ir.AddressOf(alloc, "local{}".format(i)))
@@ -1623,6 +1623,11 @@ def _runtime_call(self, opcode, args=()):
16231623
assert a.space == "table"
16241624
arg = self.emit(ir.Const(a.index, "idx", ir.i32))
16251625
arg_types0.append(ir.i32)
1626+
elif ai == ArgType.ELEMIDX:
1627+
assert isinstance(a, components.Ref)
1628+
assert a.space == "elem"
1629+
arg = self.emit(ir.Const(a.index, "idx", ir.i32))
1630+
arg_types0.append(ir.i32)
16261631
else:
16271632
raise NotImplementedError(str(ai))
16281633
args0.append(arg)

test/wasm/test_suite_full.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -355,7 +355,7 @@ def invoke(self, target: SExpression):
355355
elif any(nan_or_inf(a) for a in args):
356356
self.logger.warning(f"Not invoking method {func_name}({args})")
357357
else:
358-
self.logger.debug(f"Invoking {func_name} at line {target.loc}")
358+
self.logger.debug(f"Invoking '{func_name}' at line {target.loc}")
359359
return instance.exports[func_name](*args)
360360

361361
def register_instance(self, s_expr):

0 commit comments

Comments
 (0)