def test_coremark_crc16(v1, v2): coremark_wasm_snippet = r""" (module (func $crc8 (param i32 i32) (result i32) local.get 0 local.get 1 i32.add) (func (export "crc16") (param i32 i32) (result i32) local.get 0 i32.const 16 i32.shr_u local.get 0 i32.const 65535 i32.and local.get 1 call $crc8 call $crc8) ) """ m = Module(coremark_wasm_snippet) py_inst = instantiate(m, target='python') native_inst = instantiate(m, target='native') res1 = py_inst.exports['crc16'](v1, v2) res2 = native_inst.exports['crc16'](v1, v2) print('results', res1, 'should be equal to', res2) assert res1 == res2
def assert_equal_behavior(m): """ Instantiate the module in both python and native. Test that both operate the same. """ print(m.to_string()) # Compile / instantiate: py_inst = instantiate(m, target='python') native_inst = instantiate(m, target='native') # Run both python and x86 variant and compare outputs. res1 = py_inst.exports['my_func']() res2 = native_inst.exports['my_func']() print('results', res1, 'should be equal to', res2) assert res1 == res2
def _instantiate(self, m1): """ Instantiate a module. """ # Next step: Instantiate: if self.target: def my_print() -> None: pass def print_i32(x: int) -> None: pass imports = { 'spectest': { 'print_i32': print_i32, 'print': my_print, # 'global_i32': 777, 'table': components.Table("$table", 'funcref', 10, 20) } } for reg_name, reg_instance in self._registered_instances.items(): imports[reg_name] = {} # TODO: use reg_instance.exports mod_instance = instantiate(m1, imports=imports, target=self.target, reporter=self.reporter) self.logger.debug('Instantiated wasm module %s', mod_instance) if m1.id: self.named_module_instances[m1.id] = mod_instance else: mod_instance = None return mod_instance
def take_root(self, bits, target): # Function parameters and result: a = 4.0 b = 9.0 expected_result = 6.0 # Python cross check: python_result = math.sqrt(a) * math.sqrt(b) assert math.isclose(python_result, expected_result, rel_tol=0.0001, abs_tol=0.0000001) # Now via wasm instantiation: module = Module(src) # report_filename = 'root_{}_{}.html'.format(bits, target) # with html_reporter(report_filename) as reporter: #, reporter=reporter) instance = instantiate(module, target=target) # print('root ', bits, 'instance', inst, 'for target', target) funcname = 'f{}.mul_sqrts'.format(bits) res = instance.exports[funcname](a, b) # print('Result:', res, 'expected=', expected_result, 'python says', python_result) assert math.isclose(res, expected_result, rel_tol=0.0001, abs_tol=0.0000001)
def test_benchmark_nbody_with_ppci(benchmark): instance = wasm.instantiate(wasm.read_wasm(TEST_BYTES), {}, 'native') nbody = instance.exports.main def bench(): return nbody(N) assert benchmark(bench)
def test_python_to_wasm(): m = python_to_wasm(find_prime) imports = {'env': {'f64_print': f64_print}} # TODO: for some reason, target='native' ends up in an infinite loop? ob = wasm.instantiate(m, imports, target='python') result = ob.exports.find_prime(10) assert result == 29
def __init__(self, game_module=game_module): # Create import dict from methods env = {} for name in dir(self): if name.startswith('wasm_'): env[name[5:]] = getattr(self, name) self.imports = dict(env=env) # Instantiate game module self.game = wasm.instantiate(game_module, self.imports, target='python')
def assert_equal_memory(m): """ Instantiate the module in both python and native. Test that both operate the same. """ print(m.to_string()) # Compile / instantiate: py_inst = instantiate(m, target='python') native_inst = instantiate(m, target='native') # Run both python and x86 variant py_inst.exports['my_func']() native_inst.exports['my_func']() # Compare memory contents: py_mem = py_inst.exports['mem'] native_mem = native_inst.exports['mem'] assert py_mem.read(0, 100) == native_mem.read(0, 100)
def test_callbacks(self): """ Test various stuff around wasm instantiation. See examples/wasm/callbacks.py """ module = Module( ('import', 'py', 'add', ('func', '$add', ('param', 'i64', 'i64'), ('result', 'i64'))), ('global', '$g1', ('export', 'var1'), ('mut', 'i64'), ('i64.const', 42)), ('func', ('export', 'main'), ('param', 'i64'), ('result', 'i64'), ('local.get', 0), ('global.get', '$g1'), ('call', '$add'), ), ('func', ('export', 'add'), ('param', 'i64', 'i64'), ('result', 'i64'), ('local.get', 0), ('local.get', 1), ('call', '$add'), ), ('memory', ('export', 'mem0ry'), ('data', 'abcd'), ), ) def my_add(x: int, y: int) -> int: print('my add called', x, y) return x + y + 1 imports = { 'py_add': my_add } instance = instantiate( module, imports={ 'py': { 'add': my_add, } }) self.assertEqual(1380, instance.exports.main(1337)) self.assertEqual(85, instance.exports.add(42, 42)) self.assertEqual(3, instance.exports['add'](1, 1)) self.assertEqual(42, instance.exports.var1.read()) instance.exports.var1.write(7) self.assertEqual(7, instance.exports.var1.read()) self.assertEqual(1345, instance.exports.main(1337)) self.assertEqual(b"abcd", instance.exports.mem0ry[0:4]) instance.exports.mem0ry[1:3] = bytes([1,2]) self.assertEqual(b'a\x01\x02d', instance.exports.mem0ry[0:4])
def __init__(self, ai_module=ai_module1): super().__init__() self.ai = wasm.instantiate(ai_module, self.imports, target='native')
(func $fac-f32 (export "fac-f32") (type $over-f32) (if (result f32) (f32.eq (local.get 0) (f32.const 0.0)) (then (f32.const 1.0)) (else (f32.mul (local.get 0) (call $fac-f32 (f32.sub (local.get 0) (f32.const 1.0)) ) ) ) ) ) ) """ m = Module(src) obj = api.wasmcompile(src, 'x86_64', opt_level=0) api.objcopy(obj, None, 'elf', 'fac_f32.o') print(m.to_string()) inst = instantiate(m, {}) inst2 = instantiate(m, {}, target='python') for number in [1.0, 5.0, 10.0]: print('number', number, 'fac', inst.exports['fac-f32'](number), 'fac_python', inst2.exports['fac-f32'](number))
def test_memory1(): # The canonical form CODE0 = dedent(r""" (module (type $print (func (param i32))) (type $2 (func)) (import "js" "print_ln" (func $print (type $print))) (memory $0 1 1) (start $main) (func $main (type $2) i32.const 0 i32.load8_u call $print i32.const 1 i32.load8_u call $print) (data i32.const 0 "\04\03\02") ) """) # Test main code m0 = Module(CODE0) assert m0.to_string() == CODE0 b0 = m0.to_bytes() assert Module(b0).to_bytes() == b0 printed_numbers = [] def print_ln(x: int) -> None: printed_numbers.append(x) imports = { 'js': { 'print_ln': print_ln, } } instantiate(m0, imports=imports, target='python') assert [4, 3] == printed_numbers if has_node(): assert run_wasm_in_node(m0, True) == '4\n3' # Abbreviation: imported memory m3 = Module('(module (memory $m1 (import "foo" "bar_mem1") 1) )') assert m3.to_string() == dedent(""" (module (import "foo" "bar_mem1" (memory $m1 1)) ) """) m3 = Module('(module (memory (import "foo" "bar_mem1") 2 3) )') assert m3.to_string() == dedent(""" (module (import "foo" "bar_mem1" (memory 2 3)) ) """) # Abbeviation: inline data and unspecified (default) alignment CODE1 = r""" (module (type $print (func (param i32))) (type $2 (func)) (import "js" "print_ln" (func $print (type $print))) (memory (data "\04\03\02")) (start $main) (func $main (type $2) i32.const 0 i32.load8_u call $print i32.const 1 i32.load8_u call $print ) ) """ m1 = Module(CODE1) assert m1.to_string() == CODE0 assert m1.to_bytes() == b0
from ppci.wasm import instantiate, read_wasm import demo with open('demo.wasm', 'rb') as f: module = read_wasm(f) inst = instantiate(module, {}) print(inst) print('myAdd(0, 55)', inst.exports['myAdd'](0, 55), 'should be:', demo.myAdd(0, 55)) print('myAdd(7, 55)', inst.exports['myAdd'](7, 55), 'should be:', demo.myAdd(7, 55)) print('mySub(7, 2)', inst.exports['mySub'](7, 2), 'should be:', demo.mySub(7, 2)) print('mySub(17, 2)', inst.exports['mySub'](17, 2), 'should be:', demo.mySub(17, 2)) print('myFoo(2.2)', inst.exports['myFoo'](2.2), 'should be:', demo.myFoo(2.2)) print('myBar(7)', inst.exports['myBar'](7), 'should be:', demo.myBar(7))
from ppci.wasm import instantiate, read_wasm import mandel import x import time with open('mandel2.wasm', 'rb') as f: module = read_wasm(f) inst = instantiate(module, {'x': {'putc': x.putc, 'put_float': x.put_float}}) print(inst) print('mandel():') inst.exports['mandel']() print() print('mandel2():') inst.exports['mandel2']() print()
(func $fac-f32 (export "fac-f32") (type $over-f32) (if (result f32) (f32.eq (local.get 0) (f32.const 0.0)) (then (f32.const 1.0)) (else (f32.mul (local.get 0) (call $fac-f32 (f32.sub (local.get 0) (f32.const 1.0)) ) ) ) ) ) ) """ m = Module(src) obj = api.wasmcompile(src, 'x86_64', opt_level=0) api.objcopy(obj, None, 'elf', 'fac_f32.o') print(m.to_string()) inst = instantiate(m, target='native') inst2 = instantiate(m, target='python') for number in [1.0, 5.0, 10.0]: print('number', number, 'fac', inst.exports['fac-f32'](number), 'fac_python', inst2.exports['fac-f32'](number))
from ppci.wasm import instantiate, read_wasm import mandel import x import time with open('mandel.wasm', 'rb') as f: module = read_wasm(f) inst = instantiate(module, {'x': {'putc': x.putc}}) print(inst) print('python -> wasm -> native code mandel:') t1 = time.time() inst.exports['mandel']() t2 = time.time() print('Python mandel:') t3 = time.time() mandel.mandel() t4 = time.time() dt_native = t2 - t1 dt_python = t4 - t3 print('native took:', dt_native, 'python took:', dt_python, 'speedup', dt_python / dt_native)
def test_func1(): # The canonical form CODE0 = dedent(""" (module (type $0 (func (param i32))) (type $1 (func (param i32 i32) (result i32))) (type $2 (func)) (import "js" "print_ln" (func $print (type $0))) (start $main) (func $add (type $1) (get_local 0) (get_local 1) (i32.add) ) (func $main (type $2) (local $foo i32) (i32.const 4) (i32.const 3) (call $add) (set_local $foo) (get_local $foo) (call $print) ) ) """) # Test main code m0 = Module(CODE0) assert m0.to_string() == CODE0 b0 = m0.to_bytes() assert Module(b0).to_bytes() == b0 # TODO: figure out what is wrong below: if False: printed_numbers = [] def print_ln(x: int) -> None: printed_numbers.append(x) imports = { 'js': { 'print_ln': print_ln, }, } instantiate(m0, imports, target='python') assert [7] == printed_numbers if has_node(): assert run_wasm_in_node(m0, True) == '7' # Abbreviation: inline typedefs CODE1 = """ (module (import "js" "print_ln" (func $print (param i32))) (start $main) (func $add (param i32 i32) (result i32) (get_local 0) (get_local 1) (i32.add) ) (func $main (local $foo i32) (set_local $foo (call $add (i32.const 4) (i32.const 3) ) ) (call $print (get_local $foo) ) ) ) """ m1 = Module(CODE1) assert m1.to_string() == CODE0 # look at the indentation! assert m1.to_bytes() == b0
print(f.getvalue()) def my_add(x: int, y: int) -> int: print('my add called', x, y) return x + y + 1 # Run in memory imports = {'py_add': my_add} native_module = load_obj(obj, imports=imports) print(dir(native_module)) result = getattr(native_module, 'add')(42, 42) print(result, '(should be 85)') # This way a wasm module can be loaded analog to javascript: instance = instantiate(wasm_module, imports={'py': { 'add': my_add, }}) print(instance.exports.main(1337), '(should be 1380)') print(instance.exports.add(1, 1), '(should be 3)') print('global var', instance.exports.var1.read()) instance.exports.var1.write(7) print('global var', instance.exports.var1.read()) print(instance.exports.main(1337), '(should be 1345)') print('mem[0:4]=', instance.exports.mem0ry[0:4]) instance.exports.mem0ry[1:3] = bytes([1, 2]) print('mem[0:4]=', instance.exports.mem0ry[0:4])
def test_table1(): # The canonical form CODE0 = dedent(r""" (module (type $print (func (param i32))) (type $2 (func)) (import "js" "print_ln" (func $print (type $print))) (table $0 2 2 funcref) (start $main) (elem i32.const 0 $f1 $f2) (func $f1 (type $2) i32.const 101 call $print) (func $f2 (type $2) i32.const 102 call $print) (func $main (type $2) i32.const 0 call_indirect (type $2) i32.const 1 call_indirect (type $2)) ) """) # Test main code m0 = Module(CODE0) assert m0.to_string() == CODE0 b0 = m0.to_bytes() assert Module(b0).to_bytes() == b0 html_report = 'table_and_element_compilation_report.html' with open(html_report, 'w') as f, HtmlReportGenerator(f) as reporter: printed_numbers = [] def print_ln(x: int) -> None: printed_numbers.append(x) imports = { 'js': { 'print_ln': print_ln, }, } instantiate(m0, imports, target='python', reporter=reporter) assert [101, 102] == printed_numbers if is_platform_supported(): printed_numbers = [] def print_ln(x: int) -> None: printed_numbers.append(x) imports = { 'js': { 'print_ln': print_ln, }, } instantiate(m0, imports, target='native', reporter=reporter) assert [101, 102] == printed_numbers if has_node(): assert run_wasm_in_node(m0, True) == '101\n102' # Abbreviation: imported table m3 = Module('(module (table $t1 (import "foo" "bar_table1") funcref) )') assert m3.to_string() == dedent(""" (module (import "foo" "bar_table1" (table $t1 funcref)) ) """) m3 = Module('(module (table (import "foo" "bar_table1") 2 3 funcref) )') assert m3.to_string() == dedent(""" (module (import "foo" "bar_table1" (table 2 3 funcref)) ) """) # Abbeviation: inline data and unspecified (default) alignment CODE1 = r""" (module (type $print (func (param i32))) (type $2 (func)) (import "js" "print_ln" (func $print (type $print))) (table funcref (elem $f1 $f2)) (start $main) (func $f1 (type $2) (i32.const 101) (call $print) ) (func $f2 (type $2) (i32.const 102) (call $print) ) (func $main (type $2) (i32.const 0) (call_indirect (type $2)) (i32.const 1) (call_indirect (type $2)) ) ) """ m1 = Module(CODE1) assert m1.to_string() == CODE0 assert m1.to_bytes() == b0
def load_module(self, s_expr): if 'binary' in s_expr: # We have (module binary "") # Iterate: elems = iter(s_expr) # Skip to binary tag: while next(elems) != 'binary': pass # fetch data from last tuple elements: parts = [] for elem in elems: data = datastring2bytes(elem) parts.append(data) data = reduce(add, parts) # Load module from binary data: m1 = Module(data) # Go back data2 = m1.to_bytes() # Wont always be the same, e.g. some tests use non-minimal LEB ints # assert data == data2 data3 = Module(data2).to_bytes() # Check that reading it in result in the same module ... assert data2 == data3 else: # Load module from tuples: m1 = Module(s_expr) # # Convert module to text form and parse again # # This should yield the same binary form: # m2 = Module(m1.to_string()) # assert m1.to_bytes() == m2.to_bytes() # NOTE: going to string format and back does not # guarantee that parsing was correct. self.reporter.dump_wasm(m1) self.logger.debug('loaded wasm module %s', m1) # Next step: Instantiate: if self.target: def my_print() -> None: pass def print_i32(x: int) -> None: pass imports = { 'spectest': { 'print_i32': print_i32, 'print': my_print, } } self.mod_instance = instantiate( m1, imports, target=self.target, reporter=self.reporter) self.logger.debug('Instantiated wasm module %s', self.mod_instance) else: self.mod_instance = None
logging.basicConfig(level=logging.INFO) with open('wasmboy.wasm', 'rb') as f: wasm_module = Module(f) def log(a: int, b: int, c: int, d: int, e: int, f: int, g: int) -> None: print('Log:', a, b, c, d, e, f, g) this_dir = os.path.dirname(os.path.abspath(__file__)) html_report = os.path.join(this_dir, 'wasmboy_report.html') with reporting.html_reporter(html_report) as reporter: wasm_boy = instantiate(wasm_module, imports={'env': { 'log': log }}, target='native', reporter=reporter) # Following this explanation: # https://github.com/torch2424/wasmBoy/wiki/%5BWIP%5D-Core-API rom_filename = args.rom logging.info('Loading %s', rom_filename) # Load in a game to CARTRIDGE_ROM_LOCATION rom_location = wasm_boy.exports.CARTRIDGE_ROM_LOCATION.read() with open(rom_filename, 'rb') as f: rom_data = f.read() rom_size = len(rom_data) wasm_boy.exports.memory[rom_location:rom_location + rom_size] = rom_data
print(f.getvalue()) def my_add(x: int, y: int) -> int: print('my add called', x, y) return x + y + 1 # Run in memory imports = {'py_add': my_add} native_module = load_obj(obj, imports=imports) print(dir(native_module)) result = getattr(native_module, 'add')(42, 42) print(result, '(should be 85)') # This way a wasm module can be loaded analog to javascript: instance = instantiate(wasm_module, {'py': { 'add': my_add, }}) print(instance.exports.main(1337), '(should be 1380)') print(instance.exports.add(1, 1), '(should be 3)') print('global var', instance.exports.var1.read()) instance.exports.var1.write(7) print('global var', instance.exports.var1.read()) print(instance.exports.main(1337), '(should be 1345)') print('mem[0:4]=', instance.exports.mem0ry[0:4]) instance.exports.mem0ry[1:3] = bytes([1, 2]) print('mem[0:4]=', instance.exports.mem0ry[0:4])